Try   HackMD

[zer0pts CTF 2020] shredder

tags: zer0pts CTF forensics

Overview

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.

Analysis

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.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Tracing deeper by IDA, we find the function address:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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

Solution

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.