记一次bypass某场景GD库及拓展分析
0x00 前言
gou楼兰师傅发来个站说是过不了gd库,问我有啥办法没有,给了他之前海贼师傅说的jpg_payload脚本,但是绕不过,问他拿了站点,写了个jpg_payload批量的fuzz脚本,fuzz了大半天,但都是没有成功的。
目标基础信息:
thinkphp3.2.3
php 5.4.45
使用gd库添加文字水印
0x01 转机
晚上吃饱喝足,闲来无事重新分析目标给出的报错信息,当上传了不是jpg文件的信息时候,返回如下内容。
可以知道目标使用了tp自带的图片处理类Think\image->open方法。
下个thinkphp 3.2.3源码,对比分析。
Think\image->open调用了驱动类的open方法,这里是Gd库。
跟到Gd类的open方法,可以看到这里用了getImageSize()方法来获取图像信息。
目标报错的“非法图像文件”也就是因为Gd类getImageSize()方法没有绕过导致的。
P神之前有一篇文章说到imagemagick绕过getImageSize()的方法,利用xbmp格式图像。
(参考文章:imagemagick邂逅getimagesize的那点事儿)
很简单,加上这两句话再试一下,payload如下:
#define xlogo_width 200 #define xlogo_height 200 <?php phpinfo();?>
很可惜没有直接过,
目标这一次报错为”没有指定图像资源”,结合报错点为size()方法
需要this->img为空的时候才会报这个错,也就是没有初始化到。追溯看$img什么时候初始化。
可以看到$img是在open方法由imagecreatefromxxx方法或者图片为gif的时候由imagecreatefromstring方法赋值的。
这里我们传入的格式是xbm,也就是会调用imagecreatefromxbm方法来创建图片,但这里没创建成功。
0x02 突破
猜想是因为xbm格式问题,google查找xbm文件的标准格式。在维基百科上,看到xbm的标准格式如下:
当即就是复制这串东西,插入php代码,如下payload。
#define test_width 16 #define test_height 7 <?php echo 'it works';?> static char test_bits[] = { 0x13, 0x00, 0x15, 0x00, 0x93, 0xcd, 0x55, 0xa5, 0x93, 0xc5, 0x00, 0x80, 0x00, 0x60 };
打一发,一发入魂,直接绕过。
成功getshell
简单粗暴之极,那么到底是怎么绕过的呢?
0x03 分析
后面拿到后端源码,发现源码大概长这样:
分析这段代码,通过base64编码获取到文件内容,从base64里面拿到的后缀,而后拼接后缀名写入文件,文件名是当前时间,注意后面的if判断,在写入之后添加水印,添加成功将覆盖掉原来的文件,最后返回文件存储的路径。
这段代码抛开Gd的问题,其实可以直接爆破文件名,因为文件其实已经写入了,后面报错不影响,只是没法返回路径罢了。
那么这里是怎么过的Gd的呢?
看到代码,这里先是open(),接着text()添加水印,最后save()保存图像。
open()里面需要绕过的点有getImageSize(),最后是createimagefromxxx,这两个都可以直接用xbm格式绕过。
至于text()加水印,这里怎么绕过的我没分析,啥图形处理太难了,放弃。确定一点就是在经过text()方法处理之后,xbm图片是没有变化的。
看到最后的save()方法,save方法里面调用了
imagexbm方法保存图像
分析imagexbm源码,看到gd库源码gdImageXbmCtx方法:
https://github.com/libgd/libgd/blob/gd-2.1.0/src/gd_xbm.c#L162
这段代码C写的,大概意思就是只允许特定的字符写出。
通过gdCtxPrintf方法来将内容输出到out,所以我们只需要关注这个方法里面可以输出什么就行了,通读之后可以知道内容被限制只能为标准格式,如下:
其中可以控的就是文件名了,代码会将name插入进去,name是文件名。
但是往上看一下:
做了限制,导致文件名只允许大小写字母和0-9数字,其他字符都会被转换为_
也就可以解释原本内容为
保存文件名为2.jpg
保存之后的内容是怎么来的
又经过深入分析,发现php版本不同,save方法表现也不一样。以phpstudy的几个版本为例
php版本 |
写入文件是否成功 |
5.3.29 |
成功 |
5.4.45 |
不成功 |
5.5.38 |
不成功 |
5.6.27 |
成功 |
7.0.12 |
成功 |
而且save()写入文件成功与否都不会报错。
至此,其实已经搞明白了,目标站能过的原因,就是因为php 5.4.45版本save保存不成功,但是又不报错,导致原本的内容不被覆盖,程序继续往下走,最后返回之前写入的文件路径。
倘若这里的版本换成其他的了,比如5.6或者7.0那么原文件就会被覆盖,php代码自然也写不进去。
至于为什么xbm格式可以允许php代码,imagecreatefromxbm能够创建成功呢?可以看
https://github.com/libgd/libgd/blob/gd-2.1.0/src/gd_xbm.c#L22
Gd源码gdImageCreateFromXbm 方法是怎么解析的。(代码就不截了)
我的C很烂,读了很久,大概应该是只检测标准的格式有没有,有就可以创建图像了,不管其他字符。
0x04 延伸
这只是一个特定的例子。比较常见的应该是通过$_FILES上传文件,添加水印的。如下场景:
直接open上传的临时文件,在添加了水印之后,最后save的时候才保存文件到可访问目录。
这种情况利用xbm格式就需要绕过imagexbm方法了,这个目前来看是不可能的,代码写死了,确实没办法引入php代码。
0x05 总结
算是完整记录了一下遇到的问题
总结起来就是利用xbm格式绕过getimagesize(),通过imagecreatfromxbm(),通过text()添加水印,最终在特定版本情况下,save()方法不报错,没有覆盖掉原来写入的shell,实现了伪绕过
关于gd库绕过的相关问题、场景之后可以继续分析研究,挺有意思的
原文地址:https://www.cnblogs.com/r00tuser/p/11312212.html
- apache开启openssl支持
- 微信小程序开发常见问题(二)
- PHP数据结构(二十一) ——希尔排序
- PHP数据结构(二十二) ——快速排序
- PHP数据结构(二十三) ——快速排序
- PHP数据结构(二十四) ——堆排序
- PHP数据结构(二十五) ——并归排序
- PHP数据结构(二十六) ——基数排序实现36进制数排序
- Apache配置
- jquery事件
- 设计模式专题(二)——策略模式
- ASP.NET AJAX(10)__Authentication ServiceAuthentication ServiceAuthentication Service属性Authentication
- 高效开发 MVVM 和 databinding 你需要使用的工具
- ASP.NET AJAX(9)__Profile Service什么是ASP.NET Profile如何使用ASP.NET ProfileProfile ServiceProfile Service预
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android图片识别应用详解
- Android 列表形式的切换的示例代码
- Android 拍照并对照片进行裁剪和压缩实例详解
- Android 多线程实现重复启动与停止的服务
- Android 简单的图片查看器源码实现
- jmeter在linux系统下运行及本地内存调优的方法详解
- Android实现录音功能实现实例(MediaRecorder)
- Linux下9种优秀的代码比对工具推荐小结
- Android开发实现在Wifi下获取本地IP地址的方法
- Android基于Glide v4.x的图片加载进度监听
- Android如何通过scheme跳转界面
- Android EditText实现输入金额类型详解
- ubuntu20.04中文输入法安装步骤
- 如何利用Android Studio将moudle变成jar示例详解
- Android自带的四种线程池使用总结