格式化字符串

format string基本介绍

此漏洞由printf类函数在使用时直接使用用户可控字符串作为格式化字符串使用所导致。如printf(s),在运行时,用户可以通过给予s特殊的值造成程序崩溃或泄露内存地址,乃至任意地址写控制程序流程。

利用原理

获取参数


在使用printf函数时,第一个参数为格式化字符串指针,之后根据格式符向下寻找需要的参数并根据格式符进行解释。但是在printf(s)过程中,若字符串s中存在格式符,仍会按照次序将相应位置的变量进行解析并输出。

特殊用法

在利用格式化字符串漏洞的过程中有几个至关重要的特殊用法

%5$d -> 5$表示指定使用第五个参数
%240c -> 表示输出240个字符
%n -> 不产生输出,将再次之前已经输出的字符个数写入对应参数所指向的内存
%7$hhn -> 将输出字符个数写入第七个参数所指向的区域,写入一个字节(x86)(half half)

利用%n向指定内存中写是改变程序流程的关键,而其他格式符则用于泄露内存信息或为配合%n写数据。

例题  

 echo  


一个可以循环利用的格式化字符串,可以通过%n将printf的地址改写成为system函数的地址,下一次输入”/bin/sh”后执行printf(“/bin/sh”)而实际上调用了system函数,获得shell。

根据调试中的栈结构,0xffdd69b0位置存储格式化字符串指针,其下有指向_IO_2_1_stdin_和_GLOBAL_OFFSET_TABLE_的指针。(如果有需要可以通过泄露这两个变量查询所使用的libc库,进而获得其他变量或函数的偏移。)
其下0xffdd69cc为输入的格式化串的起始地址,可以在格式化字符串中布局需要改写的内存地址,按照顺序目标地址所在位置分别为printf函数格式化字符串的第7, 8, 9 ,10个参数,即分别使用%7$hhn-%10$hhn逐字节覆写(一次输出太多字节容易导致链接断开,故使用hhn),除了起始16个字节为地址,其余使用%240c等进行占位.
使用pwntools的fmtstr_payload()函数自动生成利用字符串。

pwnlib.fmtstr.fmtstr_payload(offset, writes, numbwritten=0, write_size=’byte’) → str
Makes payload with given parameter. It can generate payload for 32 or 64 bits architectures. The size of the addr is taken from context.bits
Parameters:

  • offset (int) – the first formatter’s offset you control
  • writes (dict) – dict with addr, value {addr: value, addr2: value2}
  • numbwritten (int) – number of byte already written by the printf function
  • write_size (str) – must be byte, short or int. Tells if you want to write byte by byte, short by short or int by int (hhn, hn or n)

函数中offset即为栈中指向被改写区域指针相对格式化字符串指针的偏移(作为第几个参数),在上图中指向printf.got的指针位于格式化字符串指针下第七个的位置,offset即为7.
writes是一个*字典 *,为要改写的值和目标值,即用value的值替换掉内存中key指向的区域。
numbwritten即为在之前已经输出的字符数,write_size为mei每次改写的size,一般使用byte(hhn),以避免程序崩溃或连接断开。
所以本题payload可以直接使用函数生成
payload = fmtstr_payload(7, {printf_got: system_plt})
//好像是因为system函数未调用过,got中没有装载地址,同时printf非首次调用不再经过plt查询?我将下图中printf和system的got和plt任意调换均不能成功。


echo2

变成了64bit且开启了PIE保护。

可以看到在调用printf函数时,栈中存储着很多与全局变量和函数相关的地址,可以利用格式化字符串泄露两个函数地址,从而查询使用的libc版本(inndy网站中提供了题目使用的链接库,故不需要此步骤),继而确定elf基址和libc的偏移量。
在尝试中发现stdout函数地址输出为nil,setbuf和init无法从libc中获取地址,所以采用c08处的main和be8处的__libc_start_main.

//这里有一点是本地的时候be8处地址为__libc_start_main+241,但是远程时,需要令接收到的值-240,才能满足基址后三位为000.
0x7fffffffcbe8 —▸ 0x7ffff7a5a2b1 (__libc_start_main+241) ◂— mov edi, eax
排除掉随机地址的障碍之后就可以像echo一样覆盖函数地址,但是由于64位程序中地址为0x7fffffffxxxx的形式,前两个字节为\x00,不能向printf函数中写入,可以覆写exit函数,但是传递’/bin/sh’有障碍。
这里可以利用libc库中固有的一个执行execv(‘/bin/sh’)的gadget。可以在libc中通过查找字符串/bin/sh找到。
将此gadget的地址逐位写到exit_got,即可获得shell。有一点是fmtstr_payload生成的利用字符串会把地址放在前面,而地址中存在\x00会导致printf中断,所以不能正常使用,还是手动构造。

echo3

这道题的主要问题在于格式化字符串不在stack中,也就无法直接利用payload构造改写的地址。这类题目的策略在于利用栈中存在的指向特定区域的变量,不断构造跳板,最终实现对指定地址的改写。

一般改写时选用此类栈变量的值为栈中的地址,且该值指向另一个栈地址的位置构造跳板,进行改写。
一般步骤为:

1.leak栈地址和libc基址
2.将指向与目标地址接近的地址的栈变量连缀到跳板上
3.改写上一步相近的地址为目标地址
4.对目标地址内容进行改写

选择上图中0x1e和0x1f形式的位置作为跳板,因为要改写栈变量为got地址(0x0804****), 所以将0x13,0x15处等内容和目标相近的地址连缀到跳板处,随后改写0x13,0x15的内容,最终实现

1e:78│ 0x0458 —▸ 053c —▸ 0x042c(0x13) -> 0x804a020(exit_got)

1f :7c│ 0x045c —▸ 0534 —▸ 0x0434(0x15) -> 0x804a022(exit_got+2)

使用printf修改内存时,每次最多对一个地址修改两个字节(hn),因而构造两个跳板,分别指向目标地址的高低两个位置(x64大概要构造4个跳板),多重跳板进行改写时,每次都要注意指针的层次关系

echo3这道题,有幸学长提前进行了点播,指出了最大的一个坑:使用不同的libc运行时,栈结构有差异。所以在本地调试的时候就通过指定libc,产生与远程相同的栈结构.

io = process(‘./echo3’, env = {‘LD_PRELOAD’:’../libc-2.23.so.i386’})

另一个比较麻烦的点在于栈的位置由随机数决定,所以每次运行的栈结构不尽相同,但只有三十种情况。解决办法是挑选一个比较合适的栈结构(有丰富栈变量,指向libc全局变量等),对照固定的栈结构进行设计,运行时多次运行,进行栈结构碰撞。
在这个程序中要求输入五次然后exit,所以先改写了exit_got,使调用exit的时候再跳转到循环部分,从而实现无限输入(利用exit跳转回之后栈结构发生一定变化,需要适当调整),再覆写printf_got的内容为system_addr即可。
弄完之后发现,其实没必要改写exit,根据上面的分析,完全可以利用改写exit_got的机会直接改写printf_got,并在4次输入内完成,第五次直接输入’/bin/sh’,获得shell.

Ref:

利用小结(一)
利用小结(二)
CTAF Wiki
M4x的inndyWP
qazbnm456/ctf-course
pwnlib.fmtstr
安全技术精粹
m4x.fun

generated by haroopad

作者:辣鸡小谱尼
出处:http://www.cnblogs.com/ZHijack/
如有转载,荣幸之至!请随手标明出处;

转载于:https://www.cnblogs.com/ZHijack/p/8687182.html

在格式化字符串的边缘试探相关推荐

  1. 使用C++ ostringstream来格式化字符串输出

    在Linux C中,我们通常使用snprintf来格式化字符串输出,但是有一个问题,就是可能会频繁申请大的缓冲区,并且无法实现字符串的动态增加,比如你定义的字符缓存为100个字节,如果你格式化以后的内 ...

  2. 零基础入门学习Python(14)-格式化字符串

    什么是格式化字符串 格式化字符串就是按照统一的规格去输出一个字符串,如果规格不统一,就很可能造成误会 format()方法 接受位置参数和关键字参数两种参数 二者均传递到一个replacement的字 ...

  3. WPF中Binding使用StringFormat格式化字符串方法

    原文:WPF中Binding使用StringFormat格式化字符串方法 货币格式 <TextBlock Text="{Binding Price, StringFormat={}{0 ...

  4. python 为什么用%格式化_Python中应该使用%还是format来格式化字符串?

    原标题:Python中应该使用%还是format来格式化字符串? Python中格式化字符串目前有两种阵营:%和format,我们应该选择哪种呢? 自从Python2.6引入了format这个格式化字 ...

  5. Linux下的格式化字符串漏洞利用姿势

    [转]http://www.cnblogs.com/Ox9A82/p/5429099.html linux最早的漏洞防护机制nx-stack刚刚出现后就有人想出了突破方法.那就是只有栈是不可执行,而除 ...

  6. python转化为字符串格式_Python格式化字符串~转

    在编写程序的过程中,经常需要进行格式化输出,每次用每次查.干脆就在这里整理一下,以便索引. 格式化操作符(%) "%"是Python风格的字符串格式化操作符,非常类似C语言里的pr ...

  7. 变量与字符串的连接 - format、格式化字符串

    变量与字符串的连接 先当以如下三个变量: name='wwb' age='17' job='study' 方法一:利用+号进行连接 >>>info1='''Welcome to '' ...

  8. 淘气的页数 - 格式化字符串

    2019独角兽企业重金招聘Python工程师标准>>> 格式化字符串 [toc] 淘气的页数 大牛:"小鸟,发什么呆啊?" 小鸟:"牛哥,我这里要写一个 ...

  9. c语言printf相关函数 格式化字符串攻击 简介

    目录 一.类printf函数簇实现原理 二.格式化字符串攻击原理 三.一个实际的例子 一.类printf函数簇实现原理 类printf函数的最大的特点就是,在函数定义的时候无法知道函数实参的数目和类型 ...

最新文章

  1. Object-C 入门介绍
  2. python装饰器作用-Python装饰器用法实例总结
  3. Flutter Text或者RichText不换行的问题
  4. 机器人等级考试一级教具_全国青少年机器人技术等级考试(一级):基本结构...
  5. 【HDU - 5187】zhx's contest (快速幂+ 快速乘,模板)
  6. opencv计算图像亮度调节_OpenCV教程创建Trackbar图像对比度、亮度值调整
  7. 它打败了欧几里得空间,踹飞了数学怪物,成为全世界的焦点
  8. dbvis连接mysql_Dbvis连接Mysql驱动问题
  9. 哈工大密码学实验(CA证书认证系统)
  10. 会声会影x4素材_怎么给视频打马赛克?运用会声会影2019
  11. java毕业设计德云社票务系统Mybatis+系统+数据库+调试部署
  12. 第20节 信息化基础知识
  13. BLE - 连接时触发配对
  14. Python京东抢购
  15. 2018计算机中文期刊影响因子排名,2018期刊影响因子排名(1)
  16. python 查tensorflow版本_查看已安装tensorflow版本
  17. 【华为机试真题 JAVA】字符串子序列II-100
  18. 神经网络常用术语(Updating)
  19. python绘制花朵图案_Python实现平行坐标图的绘制(plotly)方式
  20. 面试官:为啥加了索引查询会变快?

热门文章

  1. adb检测不到模拟器的解决方法
  2. ice 的 Nonmutating 和 Idempotent
  3. 程序员与颈椎病(一) 我得了什么病
  4. Mac终端命令和连接服务器
  5. 【Codesys】-按钮启动外部.exe应用程序,按钮关闭HMI界面,桌面图标启动HMI界面。
  6. 从虚拟光驱启动计算机,电脑如何使用虚拟光驱装系统Windows
  7. python足球联赛赛程_足球联赛赛程表工作表
  8. 【广告计算】互联网控制舆论的三个方法
  9. mumu模拟器cpu设置_网易MuMu模拟器CPU虚拟化怎么设置?
  10. hbuilderx ios自定义基座真机测试