zer0pts CTF
forensics
We're given a core dump.
Let's extract the original ELF file by binwalk or something.
$ binwalk -e core
And you'll get the binary: 2000.elf
.
Although analysing the ELF may seem hard as every symbol is gone, recovering symbols is pretty easy.
For example, assume you want to know what function sub_8F0
is.
Tracing deeper by IDA, we find the function address:
You can get the symbol in gdb. (Open core
in gdb)
$ gdb -q ./_core.extracted/2000.elf core
pwndbg> x/4xg 0x7FFBF0AB9DA0
0x7ffbf0ab9da0 <__bsd_signal>: 0x06773ff883ff478d 0x8b481a75fffe8348
0x7ffbf0ab9db0 <__bsd_signal+16>: 0x00c764003ac0b305 0xffc0c74800000016
So, you can understand sub_8F0
is signal
.
In this way, you'll find the remove process.
int sqsum(int a, int b) {
return a * a + b * b;
}
int shred(char *filename) {
int fd, i;
char *buffer;
struct stat st;
struct timeval tv;
struct timezone tz;
if (stat(filename, &st))
return 1;
fd = open(filename, O_RDWR);
if (fd < 0)
return 1;
gettimeofday(&tv, &tz);
srand(sqsum(tv.tv_sec, tv.tv_usec));
buffer = malloc(st.st_size);
lseek(fd, 0, SEEK_SET);
read(fd, buffer, st.st_size);
for(i = 0; i < st.st_size; i++) {
buffer[i] ^= rand() & 0xff;
}
lseek(fd, 0, SEEK_SET);
write(fd, buffer, st.st_size);
free(buffer);
close(fd);
if (unlink(filename) != 0)
return 1;
write(1, "Press ENTER to quit...", 0x16);
read(0, NULL, 1);
return 0;
}
We need to recover the seed of srand
, which may remain on the stack.
One way to find this is look around the main
stack. Since main will return to __libc_start_main
, we can search for the return address. We know the address of __libc_start_main_ret
:
pwndbg> x/16i 0x7ffbf0a9cb5c <-- near __libc_start_main
0x7ffbf0a9cb5c <__libc_start_main+172>: mov QWORD PTR [rsp+0x68],rax
0x7ffbf0a9cb61 <__libc_start_main+177>: mov rax,QWORD PTR fs:0x2f8
0x7ffbf0a9cb6a <__libc_start_main+186>: mov QWORD PTR [rsp+0x70],rax
0x7ffbf0a9cb6f <__libc_start_main+191>: lea rax,[rsp+0x20]
0x7ffbf0a9cb74 <__libc_start_main+196>: mov QWORD PTR fs:0x300,rax
0x7ffbf0a9cb7d <__libc_start_main+205>: mov rax,QWORD PTR [rip+0x3c9324] # 0x7ffbf0e65ea8
0x7ffbf0a9cb84 <__libc_start_main+212>: mov rsi,QWORD PTR [rsp+0x8]
0x7ffbf0a9cb89 <__libc_start_main+217>: mov edi,DWORD PTR [rsp+0x14]
0x7ffbf0a9cb8d <__libc_start_main+221>: mov rdx,QWORD PTR [rax]
0x7ffbf0a9cb90 <__libc_start_main+224>: mov rax,QWORD PTR [rsp+0x18]
0x7ffbf0a9cb95 <__libc_start_main+229>: call rax
0x7ffbf0a9cb97 <__libc_start_main+231>: mov edi,eax
0x7ffbf0a9cb99 <__libc_start_main+233>: call 0x7ffbf0abe120 <__GI_exit>
0x7ffbf0a9cb9e <__libc_start_main+238>: mov rax,QWORD PTR [rip+0x3ced23] # 0x7ffbf0e6b8c8 <__libc_pthread_functions+392>
0x7ffbf0a9cba5 <__libc_start_main+245>: ror rax,0x11
0x7ffbf0a9cba9 <__libc_start_main+249>: xor rax,QWORD PTR fs:0x30
pwndbg> search -p 0x7ffbf0a9cb97
warning: Unable to access 16000 bytes of target memory at 0x7ffbf0a92707, halting search.
warning: Unable to access 15737 bytes of target memory at 0x7ffbf0e8f287, halting search.
warning: Unable to access 15737 bytes of target memory at 0x7ffbf0e8f287, halting search.
[stack] 0x7ffd8fdadfa8 0x7ffbf0a9cb97
pwndbg> x/24xg 0x7ffd8fdadf00
0x7ffd8fdadf00: 0x0000000000000001 0x000003e8000081b4
0x7ffd8fdadf10: 0x00000000000003e8 0x0000000000000000
0x7ffd8fdadf20: 0x0000000000003ca7 0x0000000000001000
0x7ffd8fdadf30: 0x0000000000000020 0x000000005e23f5d2
0x7ffd8fdadf40: 0x0000000003400ca0 0x000000005e23f5d2
0x7ffd8fdadf50: 0x0000000003400ca0 0x000000005e23f5d2
0x7ffd8fdadf60: 0x0000000003400ca0 0x0000000000000000
0x7ffd8fdadf70: 0x0000000000000000 0x0000000000000000
0x7ffd8fdadf80: 0x00007ffd8fdadfa0 0x00005635b10a3cb4 <-- return adddress of shred
0x7ffd8fdadf90: 0x00007ffd8fdae088 0x0000000200000000
0x7ffd8fdadfa0: 0x00005635b10a3cd0 0x00007ffbf0a9cb97 <-- return address of main
0x7ffd8fdadfb0: 0x0000000000000002 0x00007ffd8fdae088
pwndbg> x/32xg 0x7ffd8fdadf00 - 0x100
0x7ffd8fdade00: 0x0000000046505845 0x00000122000003cb
0x7ffd8fdade10: 0x000000000000001e 0x00007ffbf0e66c40
0x7ffd8fdade20: 0x0000000000000001 0x00005635b211a260
0x7ffd8fdade30: 0x00005635b211a250 0x00007ffbf0b13335
0x7ffd8fdade40: 0xffffffffffffffb0 0x0000000000003ca7
0x7ffd8fdade50: 0xffffffffffffffb0 0x00005635b10a3960
0x7ffd8fdade60: 0x00007ffd8fdae080 0x9a7e4d02ae2ede00
0x7ffd8fdade70: 0x0000000000000000 0x0000000000000000
0x7ffd8fdade80: 0x00007ffd8fdadf80 0x00005635b10a3960
0x7ffd8fdade90: 0x00007ffd8fdae080 0x0000000000000000
0x7ffd8fdadea0: 0x0000000000000000 0x00005635b10a3c67
0x7ffd8fdadeb0: 0x0000000000000000 0x00007ffd8fdaef38
0x7ffd8fdadec0: 0x0000000000000000 0x0000000300003ca7
0x7ffd8fdaded0: 0x00005635b211a260 0x0000000000000000 <-- address of shredded file
0x7ffd8fdadee0: 0x000000005e23f5f6 0x00000000000f17d8
0x7ffd8fdadef0: 0x0000000000000803 0x0000000000de0ce6-
pwndbg> x/16xg 0x00005635b211a260
0x5635b211a260: 0x1ea4e01452991394 0x5f88f11aa7768cb6
0x5635b211a270: 0x76cf90fcba38d09d 0xd755583ddf177936
0x5635b211a280: 0xb43e63e89bb291d4 0x1bc8bf4995a781d9
0x5635b211a290: 0xa67d82ef9241c150 0x6d82e1c96eaa566c
0x5635b211a2a0: 0xb52815b2f828f7a6 0x93dc80f226fa0fd7
0x5635b211a2b0: 0xe61cf6de28313ef7 0x42c1d707b89859ce
0x5635b211a2c0: 0xc351ea537844bd31 0x5e9fbed589a1cf44
0x5635b211a2d0: 0xb3fc3c26d4dda2db 0xb92db878af3d2e82
We have everything necessary to restore the file.
Let's restore the PDF file.
from ptrlib import *
import ctypes
glibc = ctypes.cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc-2.27.so')
with open("../distfiles/core", "rb") as f:
buf = f.read()
ofs_stat = buf.index(p64(0x803))
ofs_tv = ofs_stat - 0x10
ofs_enc = buf.index(p64(0x20db1)) + 8
st_size = u64(buf[ofs_stat + 0x30:ofs_stat + 0x38])
tv_sec = u64(buf[ofs_tv:ofs_tv+8])
tv_usec = u64(buf[ofs_tv+8:ofs_tv+16])
enc = buf[ofs_enc:ofs_enc + st_size]
glibc.srand(tv_sec**2 + tv_usec**2)
plain = b""
for c in enc:
plain += bytes([c ^ (glibc.rand() & 0xff)])
with open("restored.pdf", "wb") as f:
f.write(plain)
You'll find the flag in the PDF.