Intro
今年已经是第三次参赛了。说是不在乎排名,也挺违心的。不过每次参加比赛,那种一直惦记着要把题目解出来的感觉,是挺吸引人的。从解题倾向来看,还是 General 和 Web 的题目能拿下分数,其他的要么是数学不好,要么是没有接触。所以也很正常,玩得开心就好。
签到 (Web)
进入页面,会出现四个黑框,看上去似乎是要在规定时间内画出「2022」四个数字。但其实提交后,URL 会加上 result
参数,修改这个参数为「2022」即可获得 flag。
猫咪问答喵 (General)
今年的问答比去年难了不少,且按「答对一半」和「答对全部」两阶段给分。所以刚开始先挑了三道最为简单的题目完成,之后再找了个时间静下来解答剩余题目。
接下来将从解题顺序开始。
除了本地化的问题,否则很不推荐使用国内搜索引擎答题,效率会大打折扣。
-
第三题对于使用 Google 的选手来说,这道是送分题。直接搜索 'last firefox version in windows 2000' 即可。
-
第一题如果使用 Google 搜索,可能会受到其他内容的影响。较准确的是用
USTC NEBULA 成立
作为关键词搜索,可在 中国科学技术大学星云(Nebula)战队在第六届强网杯再创佳绩 这篇新闻稿中找到成立年月。 -
第六题可以在搜索引擎中找到文章 关于实行新的网络费用分担办法的通知,但要留意文中这句话:同时网字〔2003〕1号《关于实行新的网络费用分担办法的通知》终止实行。 说明这份文件上的实行日期,可能不是最早题中所涉及的服务实行时间。但又在这篇文章所属分类「新闻公告」中查找,最后一条的发布日期在 2010 年。
在网站里仔细查找,还是会发现在「网字文件」下有 2003 年的 同名文章,答案为文中所述的实行时间。
-
第四题开始就比较懵,刚开始想说直接去 Github 搜有关的 commit,但由于实在太多加上找不到搜索入口,遂暂时搁置。之后思路有所转变,虽然还是有些绕,后来还是找到了。
用 Google 搜索关键词
linux fix argc 0
,可以在结果里找到这篇文章 Handling argc==0 in the kernel - LWN.net,随后找到有人 started the linux-kernel discussion。虽然不是答案,但发现了关键词CVE-2021-4034
,也指明了修改的文件位于fs/exec.c
。这时可以去 linux 的 Github 仓库找到这个文件,并且查看文件的修改历史。在这个页面搜索
argv
,能找到这条 commit,点击查看提交详情,发现详情提及之前沟通的内容。完整 commit hash 在 url 中,复制即可。 -
其实第二题比上面的第四题简单,但由于思维被误导了,重新检查思路后解出了该题。
关于 LUG 的活动,可以先从官网入手找到相关的新闻页,不过由于新闻页的内容释出并不详细,所以要另寻方法。
这时候可以看到网站上有关活动的专门页面,而其中有关今年活动的链接,仍然指向之前找到的新闻。但可以下滑找到之前的相关「软件自由日」活动,链接指向了网站提供的 ftp,在 ftp 站中可以找到本次活动的与本题相关的 PPT。
在翻看 PPT 的时候,第一次答题容易把目光注意到标题为「Gnome + Wayland」的 page,但很难找到突破点。第二次再看的时候,注意到这段字:特别是 KDE 程序会有比较严重的问题,该页右侧截图的程序似乎出现了明显的 Bug。用搜索引擎寻找菜单中的
Configure Kdenlive
,确定它是一个 KDE 程序,到此解题结束。 -
第五题着实是把我卡住了,刚开始扫过题目知道这题应该不好解决,结果第二轮做下来,还是觉得它最难,主要在于摸不着头脑。
先从 SSH 连接域名的方式搜索,后来一无所获。再到用
key fingerprint
的内容搜索,也因为搜索结果模糊,没有实质进展。但仍在想,这一串输出并不是毫无意义,索性换一些搜索方式。搜索引擎的进阶搜索功能,可以使用精确搜索来排除与关键词相关但不准确匹配的内容。最后用这一方法搜索到一个工具为 Zeek 的 document page,访问代码块中的
id.resp_h
,在网页页脚里发现这个网站的域名 sdf.org,即为本题答案。
家目录里的秘密 (General)
下载题目包后解压,用 VS Code 打开文件夹,全局搜索关键字 flag{
,得到第一个 flag。
更换关键字为 flag
搜索后,可以看到 rclone.conf
文件内一个为 flag2 的 config setting,其中的 pass
似乎就是我们需要第二个 flag。先搜索引擎寻找 rclone config pass decode
,可以看到 fourm.rclone.org
的 论坛帖子How to retrieve a 'crypt' password from a config file,帖子主题描述的内容似乎就是我们想要的,帖子内的脚本运行后,可以获得第二个 flag。
HeiLang (General)
下载文件看源码,似乎是指定了一个长度为 10000 的数组(Python 的数据格式已经忘了,数组是 JS 的说法),然后给数组的指定位置赋值。本以为 Python 真有这样的赋值方法,结果确实是新创的。
既然这样,就回到了写 JavaScript 代码的老本行,读取每一行赋值代码为文本格式,把里面的内容切块执行。
const fs = require('fs')
let a = new Array(10000).join(0).split('')
function fillArray(string) {
let indexArr = null
string = string.replace('a[', '')
string = string.split('] =')
val = parseInt(string[1])
indexArr = string[0].split(' | ')
for (let i in indexArr) {
a[indexArr[i]] = val
}
}
fillArray('a[1225 | 2381 | 2956 | 3380 | 3441 | 4073 | 4090 | 4439 | 5883 | 6253 | 7683 | 8231 | 9933] = 978')
// 当时做的时候懒得读 .py 文件,于是就手动把那么长的赋值代码拷过来,批量加上函数,所以后面的省略…
let writerStream = fs.createWriteStream('output.txt')
writerStream.write(a.toString(),'UTF8')
writerStream.end()
writerStream.on('finish', function() {
console.log("write finished.")
})
最后回到 .py 文件,去掉之前写的数组生成和赋值代码,把结果文件拷贝回来加上赋值,就能解出 flag 了。
Xcaptcha (Web)
这题刚开始会摸不着头脑,但实际想想还是很好解决的,只是刚开始没想到用上脚本。
这里的脚本,指的是使用例如 Tempermonkey 或者 Violentmonkey,这类脚本插件在浏览器中有很好的实用性,在加载指定的 URL 时可以自执行。那就创建一个脚本,需要达到的目的有:
- 用 DOM 获取数字所在的元素和输入框所在的元素
- 切割得到两组数字
- 执行加法操作
- 把结果填入输入框
// ==UserScript==
// @name Hackergame 2022 Xcaptcha
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author null
// @match http://0.0.0.0/xcaptcha
// @grant none
// ==/UserScript==
(function() {
'use strict';
function calculate(labelDom) {
return new Promise((resolve, reject) => {
// extract number string from label
const arr = document.querySelector(labelDom).innerHTML.split(' ')[0].split('+');
// calculate result using BigInt()
const res = BigInt(arr[0]) + BigInt(arr[1]);
// convert to string and remove the 'n' end
resolve(res.toString().split('n')[0]);
});
};
calculate('[for="captcha1"]').then((res) => {
document.getElementById('captcha1').value = res;
});
calculate('[for="captcha2"]').then((res) => {
document.getElementById('captcha2').value = res;
});
calculate('[for="captcha3"]').then((res) => {
document.getElementById('captcha3').value = res;
});
})();
在做第三步的时候,需要注意整数溢出的问题,要使用 BigInt
而不是普通的数值格式。
旅行照片 2.0 (General)
去年题目的升级版,有两个 flag。其实都很简单,但要非常注意的是题目的设问,是拍摄地点,是拍摄地点,是拍摄地点!(重要的话说三次,本选手蠢的要死,卡了很久。)
第一个 flag 可以通过各种读取 EXIF 的软件和网站来快速获得,我这里直接使用的是 macOS 的 Preview.app,打开图片文件后找到窗口上部的信息 icon,就能弹出该照片的信息窗口,根据这些信息就可获得 flag。
第二个 flag 的思路,不再侧重于 EXIF。
-
手机屏幕像素的问题,还是可以通过查看 EXIF 上的设备型号
sm6115
,对应高通骁龙 662
处理器,搜寻与品牌和处理器匹配的手机型号,这样可以大大减少筛选范围。最后可以知道,拍摄者使用的手机型号是 Redmi Node 9 4G,屏幕像素为2340x1080
。 -
其余都与确定拍摄者的位置有关,但 EXIF 没有包含经纬度数据。所以通过放大拍摄的建筑物,可以看到建筑物上的文字 'WELCOME TO ZOZOMARINE STADIUM',再通过地图确定到日本的这座体育馆,根据拍摄方位确定拍摄者所处的酒店,进而知道该位置的邮政编码。要注意的是拍摄地点的邮政编码,而不是体育馆的邮政编码。虽然地理上很近,但却是不同的!
-
至于航班信息,为了保证准确,还是到 Flightradar24 上开了个七天体验会员(如果不开的话,只能查看过去七天范围内的航班动态图)。开通了会员,就可以把日期指定到拍摄时间,通过之前知道的拍摄者位置,来寻找航班信息了。
猜数字 (General)
先下载源码文件观察一番,主要的判断代码是:
var isLess = previous < this.number - 1e-6 / 2;
var isMore = previous > this.number + 1e-6 / 2;
var isPassed = !isLess && !isMore;
所以这个数字的值不重要,只是要让后面的 isPassed
为 true 即可。输入 NaN 提交即可得到 flag。
LaTeX 机器人 (Web)
同样是先下载题目附带的后端文件包,打开后厘清下图片生成的逻辑。在包内有个 base.tex 的情况下,准备一个空的 result.tex,依次插入 base.tex 的头部、用户输入内容、base.tex 的尾部。最后使用 pdflatex - pdfcrop - pdftoppm - pnmtopng,最后生成图片。
从代码结构可以看到,在用户输入的时候获取指定目录的文件输出就可解题。知道了这个,似乎解题就简单了不少。如果不太懂 LaTex 语言,就只要根据你要达到的目的去找命令。如果找的够准确,两题都没什么难度。
搜索关键词 'latex input text file',查到可以使用 \input{}
命令获取文本文件。因为代码中的用户 input.tex 在 /dev/shm
目录下,所以构建命令 /input{../../flag1}
即可获得第一个 flag。
第二个 flag 增加了输入文件会出现特殊字符的因素,所以上面的命令继续使用会报错。搜索关键词 latex input text file with special characters
,可以找到有篇帖子提供了三种方式能够不报错的获取内容。其中有种方法是通过引入包 \verbatiminput{}
,感觉可能会有包未安装的风险,所以没有深入研究下去,而是使用 \catcode
` 转义可能会出现的特殊字符,最后获得第二个 flag。
\catcode`\#=11
\catcode`\_=11
\input{../../flag2}
Flag 的痕迹 (Web)
打开网页后确定使用的是 Doku Wiki,那么得先了解如何查看编辑历史。以 dokuwiki history
作为关键词搜索后,可以知道在 URL 后加上参数 ?do=recent
即可。
但在网站上尝试后,页面提示的是 'Action disabled: recent'。留意到 Actions 可能不止一个,在 Doku Wiki 的官方文档中找到 Action Modes aka. do Modes,其中列出了各种操作模式。尝试几个后发现 ?do=diff
可以对比修改历史,回翻即可找到 flag。
线路板 (General)
下载题目提供的压缩包,解压后发现一些 .gbr 和 .gbrjob 文件。这时候需要寻找和安装可以打开这类文件的电路板开发工具,开源的 KiCad 似乎符合需求。
安装后打开 GerbView,可以拖动单个图层的 .gbr 或者包含所有图层的 .gbrjob 到程序内。加载成功的话,主界面会显示项目绘制的电路图,图中右侧偏下处会有被覆盖着的 flag{
露出,但被 D10 node 覆盖。
如果对界面用途不够了解,可以在 KiCad 网站上查找 操作文档。接下来先选择覆盖用的 D10 node 所在的图层,再尝试顺序选择高亮图层中不同 D node 的项目,这时就能看到所覆盖的 flag 内容了。
光与影 (General)
这道题虽然不懂 WebGL 的原理,但可通过修改代码尝试修改渲染效果。为了方便起见,建议把题目页面和有关的 js 文件下载下来到本地分析,几个文件大概的作用:
render-main.js
: 渲染 DOM,生成 webgl 实例,准备 webgl 渲染环境;webgl-utils.js
: 通过文件名推测是辅助 WebGL 运行的函数集;vertex-shader.js
: 定义顶点着色器;fragment-shader.js
: 定义片段着色器;
由于顶点着色器的定义内容不多,可以先从片段着色器开始看起。页面处理前的效果为 flag 的头部可见,其余被遮挡。在查看片段着色器定义时,可从代码上感知 sceneSDF()
函数中关于 t1SDF
~ t4SDF
都进行了相关转换或定位操作,可以认为是渲染 'flag' 四个字母。但 t5SDF
尤其特殊,不仅没有使用与前者差不多的 mk_trans()
,也引入了前者不曾使用的 p
和 vec3
参数,怀疑是生成遮罩的相关函数。遂修改 t5
变量中的各项数值后刷新查看效果,修改为 float t5 = t5SDF(p - vec3(0.1, 0.1, 0.1), vec3(0.1, 0.1, 0.1), 0.1);
得出结果。
大概是暴力法解题了,所有解题方法没有普适性,只有对代码的敏感性,仅参考。