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

 更新时间:2018年08月15日 09:29:55   作者:红日安全  
这篇文章主要给大家介绍了关于PHP中preg_replace和代码执行的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

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

案例

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

这个案例实际上很简单,就是 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()}});


爬坑2

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

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

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

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

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

爬坑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解析字符串函数sscanf的实用方法

    php解析字符串函数sscanf的实用方法

    在PHP编程中,有时需要对字符串进行解析,而sscanf函数就是一种非常方便的解析工具,本文详细介绍了sscanf函数的用法,包括基本用法和高级用法,通过大量的示例代码,展示了如何使用sscanf函数解析各种不同格式的字符串
    2023-09-09
  • php curl 伪造IP来源的实例代码

    php curl 伪造IP来源的实例代码

    php curl 太强大了,它不但可以模仿用户登录,还可以模仿用户IP地址哦,为伪造IP来源,本实例仅供参考哦
    2012-11-11
  • PHP获取当前执行php文件名的代码

    PHP获取当前执行php文件名的代码

    这篇文章主要介绍了PHP获取当前执行php文件名的代码,需要的朋友可以参考下
    2017-03-03
  • PHP简洁函数(PHP简单明了函数语法)

    PHP简洁函数(PHP简单明了函数语法)

    笔者由于碰到用户多次修改页面,所以在麻烦的情况下,改用php完成技术开发,这篇文章主要记录PHP简洁函数,讲述PHP简单明了函数语法,适合人群为对开源社区感兴趣,对php感兴趣,有一点时间了解下php,希望对读者带来快乐
    2012-06-06
  • php生成0~1随机小数的方法(必看)

    php生成0~1随机小数的方法(必看)

    下面小编就为大家带来一篇php生成0~1随机小数的方法(必看)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • php文件上传的两种实现方法

    php文件上传的两种实现方法

    这篇文章主要为大家详细介绍了两种php文件上传的实现方法,感兴趣的朋友可以参考一下
    2016-04-04
  • 详解PHP序列化反序列化的方法

    详解PHP序列化反序列化的方法

    这篇文章主要介绍了PHP序列化反序列化的方法,在一些开源的php源代码中,我们经常看到一些配置文件里面存放的是一些类似带有格式的变量名称和值,想知道这是为什么,请阅读下文
    2015-10-10
  • php Smarty 字符比较代码

    php Smarty 字符比较代码

    php Smarty 字符比较代码,需要的朋友可以参考下。
    2011-02-02
  • php循环语句 for()与foreach()用法区别介绍

    php循环语句 for()与foreach()用法区别介绍

    下面我用两个实例来介绍一下关于在php中foreach与for语句用法区别介绍,有需要的朋友可参考一下
    2012-09-09
  • PHP strtotime函数用法、实现原理和源码分析

    PHP strtotime函数用法、实现原理和源码分析

    这篇文章主要介绍了PHP strtotime函数用法、实现原理和源码分析,本文讲解了strtotime函数的一些用法、strtotime函数的实现基本原理、strtotime(“-1 month”)求值失败的原因等内容,需要的朋友可以参考下
    2015-02-02

最新评论