0x00 前言

重拾半年多没更新的博客。毕竟此前写博客很大程度上是为了排解学业上每天机械性重复学习那点少到可怜的知识带来的烦闷,近来一则生活充实,于是便无甚更新博客之动力;二则生活充实,入了许多新坑,甚少有闲暇时间;三则因找到了稳定的内容输出渠道(私下的、公开的),且常能收到反馈,感觉确实已经无欲无求。

但是,但是,但是,但是这个逼站的 CTF 这两天属实是把我恶心到了,令人不吐不快,直欲将出题人的脑袋剖开了研究下其脑回路的卷绕数 [1] 究竟是多少。话不多说,直接开喷。

0x01 弱智 UA 题

页面存档:https://archive.ph/GOm5b

提示 需要使用bilibili Security Browser浏览器访问,遂直接用 Chrome 插件 ModHeader 修改请求头中的 User-Agentbilibili Security Browser,拿到了 flag2。又读了下网页源码,在一个弱智的 display: none 的元素里拿到了 flag1

后来发现忘记把 UA 改回去了

0x02 在 0x01 里不小心做完了

略(让人无语。。。)

0x03 弱智弱密码猜解题

页面存档:https://archive.ph/OdIuz

用户名 admin,密码 bilibili,没有理由。反正就一个弱密码组合扔你脸上,几次以内试出来是你欧气足,试不出来是你非酋。离谱。

0x04 弱智 Cookie 校验题

页面存档:https://archive.ph/6oayl

页面提示为 有些秘密只有超级管理员才能看见,挖了一下源码发现请求的 api 是 /api/ctf/4,得到了 "code": 403。那显然只能是 Cookie 鉴权,看眼 Cookie 发现除了通用的 session 之外还有个 role=ee11cbb19052e40b07aac0ca060c23e,也就是 role=md5("user")。解法是将 role 改成 md5("Administrator")。可见 Bilibili 程序员的英语是有多么的差,「超级」= super 都不会!super 应该是小学英语词汇吧?「超级管理员」如果翻译成 Administrator,那「管理员」应该翻译成啥?还是说贵站的程序员认为超级管理员和管理员是一个意思?那可能不仅仅是小学英语没学好,看上去小学语文也没学好。不禁为十几年前的义务教育普及程度哀叹一声。当浮一大白。

什么你问我为啥 Administrator 的 A 要大写,但是上一题的用户名 admin 就不用大写?我怎么知道,可能这两道题不是一个人出的吧。

什么你问我为啥 md5("user") 的 u 就是小写?因为,因为出题人可能正好在那个地方键盘的 Shift 坏了。别问我,问他去

0x05 弱智暴力题

页面存档:https://archive.ph/hIgMD

页面提示为 这里没有你想要的答案,挖了一下源码发现请求了 api /api/ctf/5?uid=100336889,得到 "code": 403。于是立刻开始暴力枚举 uid,开 30 线程从 uid=0 开始跑,跑到两三万的时候把服务器跑崩了。crash ++ 之后坐等服务器恢复,在此期间看了看这个 uid 的人的 bilibili 空间,发现就是个可怜的无辜路人。后来想到从这个 uid 附近开始筛查,最终在这个 uid 加上一个不大于 100 的偏移量处拿到了 flag5(这个偏移量每个人还不一样,就很弱智)

我最开始以为权限不够就得找高权限的人的 uid,但是手动试了 bishi 等人(还有陈睿)的 uid 发现同样 403 之后感觉这波是贵站程序员揭竿而起,立了一个普通路人(uid=100336889)当皇帝,拒绝承认 bishi 等的历史地位和现任 CEO 陈睿的领导地位。看不懂了,不知道陈睿得知自己被一个 uid 在一亿多的人给 ntr 了会有什么感想。

0x06 我们仍未知道题干在哪

页面存档:https://archive.ph/UgYJc

首先看看这个弱智页面的初次加载的资源:

status type size method url
200 text/html 1487 GET http://45.113.201.36/blog/single.php?id=1
200 text/css 18754 GET http://45.113.201.36/blog/css/bootstrap.min.css
200 text/css 8313 GET http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css
404 text/css 0 GET http://45.113.201.36/blog/css/pace.css
200 text/css 3134 GET http://45.113.201.36/blog/css/custom.css
404 application/javascript 0 GET http://45.113.201.36/blog/js/jquery-2.1.3.min.js
200 application/javascript 9223 GET http://45.113.201.36/blog/js/bootstrap.min.js
404 application/javascript 0 GET http://45.113.201.36/blog/js/pace.min.js
404 application/javascript 0 GET http://45.113.201.36/blog/js/modernizr.custom.js
404 application/javascript 0 GET http://45.113.201.36/blog/js/script.js
200 application/javascript 922 GET http://45.113.201.36/blog/js/canvas-nest.min.js
404 application/javascript 0 GET http://45.113.201.36/blog/js/pace.min.js
404 application/javascript 0 GET http://45.113.201.36/blog/js/modernizr.custom.js

是的,你没看错,这个丑得一比没几个字还是抄的模板(gh: TheFifthMan/simple-blog)的网页,有一堆 404。先纵观整个 ctf 活动,出现的框架和库就已经有 Vue.js、jQuery、Boostrap 和 Element UI 四种。我们再看看后端的架构:

1
2
3
4
5
Service Unavailable

The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.
---------------------
Apache/2.4.7 (Ubuntu) Server at 10.34.12.219
1
2
3
4
HTTP/1.0 500 Internal Server Error
Date: Sun, 25 Oct 2020 14:44:12 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.14

(笑)所以后端是跑在 Ubuntu 上的 Apache 2.4.7 后面反代的一个 PHP 5.5.9。都是古早版本,于是想看看有没有 CVE,搜了一圈发现不太行。惯例跑了一个 gh: maurosoria/dirsearch,跑出来两个 PHP 文件:/blog/test.php/blog/end.php。打开 test.php 得到了一大长串 JSFuck,放进 Console 执行,得到了如下代码:

1
2
3
var str1 = "程序员最多的地方";
var str2 = "bilibili1024havefun";
console.log()

不知道 console.log() 有啥用,反正先去 github 上搜了 bilibili1024havefun,果然搜到了一个 repo:gh: interesting-1024/end。白盒审计呗。放出来的 PHP 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$str = intval($_GET['id']);
$reg = preg_match('/\d/is', $_GET['id']);

if(!is_numeric($_GET['id']) and $reg !== 1 and $str === 1){
$content = file_get_contents($_GET['url']);

//文件路径猜解
if (false){
echo "还差一点点啦~";
}else{
echo $flag;
}
}else{
echo "你想要的不在这儿~";
}

不难看出一个可行的攻击方法是 end.php?id[]=1。至于文件路径猜解,经过一些尝试之后发现只要是包含 flag.txt(但又不只有 flag.txt)的 url 都是能拿到一张图片的,把图片扔进 StegSolve 或者 StegOnline,或者直接 tail 一下都能拿到位于 jpg 文件末尾的 flag10(你没看错,甚至没有用到 LSB 隐写,而且拿到的是 flag10)。

世界第八大未解之谜:flag6 在哪?

我们尝试了 SQL 注入 single.php?id=1 中的 id 参数(毕竟不加这个参数就显示不出页面),但似乎后端只是判断了 isset($_GET["id"]),而没有真正用到这个 id 的值。从 -4000 到 120000 的暴力枚举结果也佐证了这个猜测。sqlmap 跑完之后,众人发现似乎有个 Referer 头的基于时间的盲注,但是此时因为同时有几十个人在跑 sqlmap,另外几十个人在跑 dirsearch,贵站这台配置比学生机高不了多少的服务器很快就卡到让人无从判断 timeout 是因为注入的 SLEEP 起效了还是因为服务器没空闲资源来处理这个请求了。于是就卡住了。

我们还尝试了 nmap 扫服务器的开放端口,扫出来了这样的结果:

1
2
3
4
5
6
7
8
25/tcp open smtp
80/tcp open http
110/tcp open pop3
443/tcp closed https
1194/tcp closed openvpn
6379/tcp closed redis
8069/tcp closed unknown
8091/tcp closed jamlink

接下来就是喜闻乐见的 redis 未授权登录,直接用 redis-cli 连到 :6379 上,搞事情:

1
2
3
> keys *
1) flag8
> get flag8

就得到了 flag8。之后当然要想办法删库跑路了,结果 set flag8 "" 虽然返回了 OK 但是 flag8 的值丝毫没变。百思不得其解,直到我发现这个 redis 是假的:

1
2
3
4
5
6
7
8
> set flag8 ""
OK
> wdnmd
OK
> ???
OK
> shit cnm
OK

所以这本质上就是个带有 tab 自动补全的、只支持少量特定 redis 命令的 OK 复读机。离谱。用 ncat 连到 6379 端口,甚至可以得到一个 output=input[1:] 的奇怪东西。比如你输入 ping,就会收到 ing。

于是乎,少年在打败恶龙解救公主的路途中,经商入仕不小心成了百万富翁和达官贵人,可是连半片龙鳞都还没看到。此情此景让我们不得不发问:nmd,你们究竟把恶龙藏到哪里去了?

0x0? 疑点

  1. 在首页和题目列表页使用了相同的背景图片名(83b92f73637ab8056346bb6b8a3af6d9840e8bb0.(jpg|png)),但只有一张图的 sha1 哈希值能对上。
  2. 两张图片对比发现 22 娘和 33 娘的位置发生了位移,背景图中一些像素改变,且 33 娘从一只左手一只右手变成了有两只右手??
  3. 既然从模板阉割成 single.php 都已经删了那么多东西了,为什么不把引入了 404 掉的资源的那些代码给删了?留着又没有用。
  4. end.php 的 url 参数包含 flag.txt 就能得到 flag10(如 url=cnmflag.txt),但是 url=flag.txt 却不会有 flag。且构造了特殊的输入 url=https://jiejiss.com/test 表明,end.php 真的通过 file_get_contents 去读了文件。猜测是如果文件不存在且路径包含 flag.txt 才会返回 flag10,那么这就意味着当前目录下真的有一个 flag.txt,只不过根据 end.php,它回显不出来。大胆猜测榜首 90 分的队伍差的就是这 10 分。。。可惜没法构造任意文件上传的洞,否则可以传一个 phar 包上去,直接 getshell。
    1. 中提到的两张图,仔细看会发现背景的颜色分成不同颜色的条带,且并不均匀。看了 LSB 发现隐隐能看到字母和数字,但实在是太模糊太抽象了,只能看到 _2c 几个字符。

0x0! 游戏时间

接下来是找不到恶龙而无所事事的挑战者们玩的一些游戏。

  1. 发现 end.php 真的会去读 url= 后面的文件路径,于是用 url=/dev/urandom 把 PHP 搞崩了,PHP 带走了 Apache
  • 大家惊讶地发现这个服务器竟然还运行了 nginx v1.10,实际的处理途径是 nginx - Apache - PHP
  1. 发现 single.php 里面似乎有一些和邮件相关的代码,于是对着服务器上开放的 25 和 110 端口(分别对应 SMTP 和 POP3)使劲搞事情。不过无论怎么搞都收不到邮件。
  2. 大家用十几门语言写了 flag 生成器,每秒提交 5 个 flag。有人在多台服务器上使用多线程提交,根据他的计算,只需要 27 年就能知道 flag6 了。
  3. 既然 redis-fake 里面的 flag8 删不掉,那我们总可以往里塞假 flag 吧?于是来得晚的人发现 redis-fake 里面有 flag1flag10,看上去跟真的一样。
  4. 私信轰炸贵站的客服,问客服「结束亦是开始」(第六题提示词)是什么意思。客服:您是在用户转正答题中遇到了困难吗?答曰:非也,我问你的这句话来自贵站「1024 大型脑筋急转弯」活动。客服:啥?
  5. 后来有大佬找到了额外的一两个 flag,在 QQ 群内恼怒发言:「这 flag 真的藏在 sb 地方!」我们于是开始思考什么是 sb 地方。想了想,以这弱智猜谜的套路,恐怕这个 sb 的地方我们根本就想不到。
  6. 很多人在后期魔怔了,看到啥都说「是线索」。后来 Gmail 换 logo 了他们都觉得这是线索,肯定是贵站和 Google 联动了。获得称号:魔怔人。

现在就等一个官方 WriteUp。毕竟不知道 6 7 9 的标答思路,没法喷。


UPDATE

0x06 正常注入题 弱智服务器

TL;DR: 一个基于 HTTP Referer 头的简单注入题,有一些非常基础的过滤

这个题本身就是普通 ctf 题,没什么好说的。用 sqlmap 都能跑出来在 HTTP Referer 这里有注入。但让选手非常非常困惑的是,在盲注过程中由于服务器本身性能太差,无从判断 timeout 的成因。(详见分割线前的 0x06 大点)

Anyway,在从 MySQL 中拿到 flag6 之后还可以顺便构造一个文件路径爆破的攻击方法,通过二分实现目录猜解和文件内容读取。经过读取的 end.php 实际内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<?php

$str = intval($_GET['id']);
$reg = preg_match('/\d/is', $_GET['id']);

if(!is_numeric($_GET['id']) and $reg !== 1 and $str === 1){
$content = file_get_contents($_GET['url']);
//echo $content;
$filename = "./imgs/bilibili_224a634752448def6c0ec064e49fe797_havefun".".jpg";
if (strpos($_GET['url'],"flag.txt") == false){
echo "还差一点点啦~";
}else{
//file_put_contents($filename,$content);
echo "<img src=".$filename.">";
}
}else{
echo "你想要的不在这儿~";
}
?>

0x07 弱智中的弱智的路径猜解题

首先在第三题里,背景图的路径从 banner_bg.gif 切换成了 api/images?file=banner.png。在前文的「疑点」中我已经提到过这个问题,因为通常来讲 ctf 中不会有无用功。虽然 b 站这次的 ctf 有一堆弱智一样的操作,凭空创造出 n 多个疑点让人非常无力吐槽,但是这个 api 开头的路径确实还是非常可疑,首先要考虑的就是任意文件读。可是 api/images?file=./banner.png 却返回了 404,dirsearch 的扫描也认为该入口是硬编码了白名单。

后来大家在 QQ 群里讨论了一下,有师傅一语道破天机:这个地方确实是硬编码了白名单,flag7 就藏在 ?file=../../../flag7.txt 里!于是一口老血郁结于心,闷闷不乐,直欲取贵站出题人之项上人头,蒸煮烹炸而啖之。但后来又仔细思虑,放弃了这个想法:一则实在无法进入 b 站工作区,二则也担心自身智力遭到同化,陷入只会出弱智猜谜题的可怜境地!

所以最终的解法是访问 /api/images?file=../../../flag7.txt

0x08 弱智 redis 题

未授权访问,在前面讲过了。

0x09 弱智 aes 题

首先你要猜到 /api/images?file=../../../secret.txt 这个路径。大多数人肯定想不到这一个 images?file 的 api,他们用了两次!(其实后面又用了一次)所以很容易错过。打开文件之后发现是一串 base64:

1
SkRGWDZRZnJxelJQU21YME42MU04OWlwV1l0SlYvcEJRVEJPWWFYUXVHOGZBcnJ1bjNXS3hXRlpHd05uMjFjRw==

两次解码之后发现出现大量特殊字符,长度为 48 bytes,猜测是经过了 AES 加密。但迫于找不到 key,最后这 10 分也没拿到。复盘发现原来又要用到 /api/images?file= 这个 api,这次需要传入 file=md5sum(secret.txt).jpg,也就是 file=ae10c97f6de1129abb00b5c961394336.jpg。然后在文件中寻找字符串,可得:

this is the key of secret.txt:aes_key

遂以 "\x00".repeat(16) 为初始化向量,以 "aes_key" + "\x00".repeat(9) 为 key,以 CBC Mode 解码之,得到 flag9

本题全场无一人做出,复盘思路来自第一天就到了 90 分的大佬和官方有关弱智人士交流时得到的提示。

0x0a 弱智文件路径猜解题

用 dirsearch 跑出来一个 test.php 和一个 end.php,test.php 里的东西在 GitHub 上搜能搜到 end.php 的修改过的源码,然后随便构造下参数就过了。

总结

wcnm

来源:https://blog.jiejiss.com/