深入研究PHP中的preg_replace和代码执行


Posted in PHP onAugust 15, 2018

前言

本文将深入研究 preg_replace /e 模式下的代码执行问题,其中包括 preg_replace 函数的执行过程分析、正则表达式分析、漏洞触发分析,当中的坑非常多,相信看完本文,你一定会有所收获。下面是 七月火 和 l1nk3r 的分析结果。

案例

下面先看一个案例,思考如何利用此处的 preg_replace /e 模式,执行代码(可以先不看下文分析,自己思考出 payload 试试)。

深入研究PHP中的preg_replace和代码执行

这个案例实际上很简单,就是 preg_replace 使用了 /e 模式,导致可以代码执行,而且该函数的第一个和第三个参数都是我们可以控制的。我们都知道, preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(也就是上图 preg_replace 函数的第二个参数)当做代码来执行,然而这里的第二个参数却固定为 'strtolower("\\1")' 字符串,那这样要如何执行代码呢?

爬坑1

上面的命令执行,相当于 eval('strtolower("\\1");') 结果,当中的 \\1 实际上就是 \1 ,而 \1 在正则表达式中有自己的含义。我们来看看 W3Cschool 中对其的描述:

反向引用

对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 '\n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

所以这里的 \1 实际上指定的是第一个子匹配项,我们拿 ripstech 官方给的 payload 进行分析,方便大家理解。官方 payload 为: /?.*={${phpinfo()}} ,即 GET 方式传入的参数名为 /?.* ,值为 {${phpinfo()}}  。

原先的语句: preg_replace('/(' . $regex . ')/ei', 'strtolower("\\1")', $value);
变成了语句: preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});

深入研究PHP中的preg_replace和代码执行

爬坑2

上面的 preg_replace 语句如果直接写在程序里面,当然可以成功执行 phpinfo() ,然而我们的 .* 是通过 GET 方式传入,你会发现无法执行 phpinfo 函数,如下图:

深入研究PHP中的preg_replace和代码执行

我们 var_dump 一下 $_GET 数组,会发现我们传上去的 .* 变成了 _* ,如下图所示:

深入研究PHP中的preg_replace和代码执行

这是由于在PHP中,对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,这就导致我们正则匹配失效。我们可以 fuzz 一下PHP会将哪些符号替换成下划线,发现有:(这是非法字符不为首字母的情况)

深入研究PHP中的preg_replace和代码执行

当非法字符为首字母时,只有点号会被替换成下划线:

深入研究PHP中的preg_replace和代码执行

所以我们要做的就是换一个正则表达式,让其匹配到 {${phpinfo()}} 即可执行 phpinfo 函数。这里我提供一个 payload : \S*=${phpinfo()} 执行结果如下:

深入研究PHP中的preg_replace和代码执行

爬坑3

下面再说说我们为什么要匹配到 {${phpinfo()}} 或者 ${phpinfo()} ,才能执行 phpinfo 函数,这是一个小坑。这实际上是PHP可变变量 的原因。在PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。 ${phpinfo()} 中的 phpinfo() 会被当做变量先执行,执行后,即变成 ${1} (phpinfo()成功执行返回true)。如果这个理解了,你就能明白下面这个问题:

var_dump(phpinfo()); // 结果:布尔 true
var_dump(strtolower(phpinfo()));// 结果:字符串 '1'
var_dump(preg_replace('/(.*)/ie','1','{${phpinfo()}}'));// 结果:字符串'11'

var_dump(preg_replace('/(.*)/ie','strtolower("\\1")','{${phpinfo()}}'));// 结果:空字符串''
var_dump(preg_replace('/(.*)/ie','strtolower("{${phpinfo()}}")','{${phpinfo()}}'));// 结果:空字符串''
这里的'strtolower("{${phpinfo()}}")'执行后相当于 strtolower("{${1}}") 又相当于 strtolower("{null}") 又相当于 '' 空字符串

总结

这个问题是我们在做 PHP-Audit-Labs 项目的时候发现的,我们尽可能地将每一篇文章的漏洞理解透彻,分析清楚,这对自身也是一种提高。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
xajax写的留言本
Nov 25 PHP
不重新编译PHP为php增加openssl模块的方法
Jun 14 PHP
一个PHP的QRcode类与大家分享
Nov 13 PHP
php递归方法实现无限分类实例代码
Feb 28 PHP
ThinkPHP验证码使用简明教程
Mar 05 PHP
Chrome Web App开发小结
Sep 04 PHP
Zend Framework教程之Autoloading用法详解
Mar 08 PHP
PHP 二维数组和三维数组的过滤
Mar 16 PHP
php+jquery+html实现点击不刷新加载更多的实例代码
Aug 12 PHP
php中使用websocket详解
Sep 23 PHP
详谈PHP中public,private,protected,abstract等关键字的用法
Dec 31 PHP
浅析PHP中的闭包和匿名函数
Dec 25 PHP
PHP中一个有趣的preg_replace函数详解
Aug 15 #PHP
PHP使用curl_multi_select解决curl_multi网页假死问题的方法
Aug 15 #PHP
php+croppic.js实现剪切上传图片功能
Aug 14 #PHP
PHP设计模式之委托模式定义与用法简单示例
Aug 13 #PHP
PHP设计模式之建造者模式定义与用法简单示例
Aug 13 #PHP
PHP设计模式之装饰器模式定义与用法简单示例
Aug 13 #PHP
PHP实现的ID混淆算法类与用法示例
Aug 10 #PHP
You might like
php判断访问IP的方法
2015/06/19 PHP
PHP判断表达式中括号是否匹配的简单实例
2016/10/22 PHP
PHP实现十进制数字与二十六进制字母串相互转换操作示例
2018/08/10 PHP
CSS中一些@规则的用法小结
2021/03/09 HTML / CSS
javascript中的变量是传值还是传址的?
2010/04/19 Javascript
页面只有一个text的时候,回车自动submit的解决方法
2010/08/12 Javascript
jquery.fileEveryWhere.js 一个跨浏览器的file显示插件
2011/10/24 Javascript
js+css实现增加表单可用性之提示文字
2013/06/03 Javascript
CheckBoxList多选样式jquery、C#获取选择项
2013/09/06 Javascript
浅析javascript中function 的 length 属性
2014/05/27 Javascript
node.js中的fs.appendFile方法使用说明
2014/12/17 Javascript
Jquery 实现图片轮换
2015/01/28 Javascript
微信小程序 省市区选择器实例详解(附源码下载)
2017/01/05 Javascript
jQuery实现获取h1-h6标题元素值的方法
2017/03/06 Javascript
react-native-fs实现文件下载、文本存储的示例代码
2017/09/22 Javascript
JavaScript 自定义事件之我见
2017/09/25 Javascript
Echarts之悬浮框中的数据排序问题
2018/11/08 Javascript
[05:49]2014DOTA2TI4正赛第二日综述 昔日冠军纷纷落马 VG LGD占尽先机
2014/07/20 DOTA
Python学习笔记(二)基础语法
2014/06/06 Python
跟老齐学Python之有容乃大的list(1)
2014/09/14 Python
使用Python抓取豆瓣影评数据的方法
2018/10/17 Python
Python数据类型之Dict字典实例详解
2019/05/07 Python
Python&&GDAL实现NDVI的计算方式
2020/01/09 Python
Python tkinter实现日期选择器
2021/02/22 Python
法国美发器材和产品购物网站:Beauty Coiffure
2016/12/05 全球购物
XD健身器材:Kevlar球、Crossfit健身球
2019/03/26 全球购物
Notino匈牙利:购买香水和化妆品
2019/04/12 全球购物
俄罗斯花园种植材料批发和零售网上商店:Беккер
2019/07/22 全球购物
C语言怎样定义和声明全局变量和函数最好
2013/11/26 面试题
2014校长四风问题对照检查材料思想汇报
2014/09/16 职场文书
群众路线自我剖析范文
2014/11/04 职场文书
大学生入党群众意见书
2015/06/02 职场文书
小学生读书笔记
2015/07/01 职场文书
Python 中数组和数字相乘时的注意事项说明
2021/05/10 Python
判断Python中的Nonetype类型
2021/05/25 Python