前言

成功挤进校内前5,总榜前70

image-20231104123531580

image-20231104123607446

欣慰地看到这一年来的努力没有白费,自己的技术力有见长

也欣慰地看到今年的排行榜分数比去年更卷了,这是好事,说明网安圈高技术力的师傅们越来越多了,以后也能看到更多大佬们的技术分享文章

题解

Hackergame 启动

和去年的签到题一样,考点是GET请求传参

由于准确率是前端判断后通过GET传给similarity,所以直接手动修改url即可

https://cnhktrz3k5nc.hack-challenge.lug.ustc.edu.cn:13202/?similarity=100

image-20231028123120216

image-20231028123142362

猫咪小测

  1. 想要借阅世界图书出版公司出版的《A Classical Introduction To Modern Number Theory 2nd ed.》,应当前往中国科学技术大学西区图书馆的哪一层?(30 分)
    提示:是一个非负整数。

    来到中科大图书馆官方网站

    image-20231028123545301

    检索关键词,找到图书

    发现在西区外文书库

    image-20231028124425696

    搜索发现西区外文书库在12楼

    image-20231028125929373

  2. 今年 arXiv 网站的天体物理版块上有人发表了一篇关于「可观测宇宙中的鸡的密度上限」的论文,请问论文中作者计算出的鸡密度函数的上限为 10 的多少次方每立方秒差距?(30 分)
    提示:是一个非负整数。

    在arXiv网站中检索

    image-20231028130936924

    读论文这种事情就交给GPT了

    image-20231028131406013

  3. 为了支持 TCP BBR 拥塞控制算法,在编译 Linux 内核时应该配置好哪一条内核选项?(20 分)
    提示:输入格式为 CONFIG_XXXXX,如 CONFIG_SCHED_SMT。

    同样交给GPT

    image-20231028131600758

    1. 🥒🥒🥒:「我……从没觉得写类型标注有意思过」。在一篇论文中,作者给出了能够让 Python 的类型检查器 MyPY mypy 陷入死循环的代码,并证明 Python 的类型检查和停机问题一样困难。请问这篇论文发表在今年的哪个学术会议上?(20 分)

      提示:会议的大写英文简称,比如 ISCA、CCS、ICML。

      谷歌学术搜索关键词 mypy endless loop

      找到一篇2022年发布预印本的论文Python Type Hints Are Turing Complete

      image-20231028143124933

      搜索后找到会议名

      image-20231028143211765

      ECOOP

更深更暗

题目页面的文字提示flag在最深处

image-20231028143603368

用不着手动翻,直接查看源代码即可

image-20231028143844003

flag{T1t@n_f36a6c9528684bf2df12270a14d36927}

旅行照片 3.0

题目简介如下

你的学长去留学了,这一走短时间内怕是回不来了。于是,你在今年暑假来了一场计划已久的旅行,并顺路探望了这位久别的学长。翻阅当天拍下的照片, 种种回忆和感慨油然而生。

请观察照片并结合所有文字内容,正确回答题目以获取 flag。

首先要确定学校的位置,从之后的描述中,我们能确认他们在日本范围活动

信息量更大的是中午和下午的描述:

🌻 中午

离开校园后,你和学长走到了附近的一家拉面馆用餐。那家店里的拉面香气扑鼻,店内的装饰和氛围也充满了日式的风格。 学长(下图左一)与你分享了不少学校的趣事。饭后,你们决定在附近散步,享受这难得的闲暇时光。当你们走到一座博物馆前时, 马路对面的喷泉和它周围的景色引起了你的注意。下午,白色的帐篷里即将举办一场大型活动,人们忙碌的身影穿梭其中,充满了期待与热情。

🌻 下午和夜晚

在参观完博物馆后,学长陪你走到了上野站。你们都感到有些不舍,但知道每次的分别也是为了下次更好的相聚。 学长那天晚上将继续他的学术之旅,打算乘船欣赏东京的迷人夜景和闪耀的彩虹大桥(Rainbow Bridge)。 而你则搭乘了开往马里奥世界的电车,在那里度过了一段欢乐的时光。

我将其中的关键词都高亮了出来,我们现在知道该学校在上野站附近,同时周围有拉面店和博物馆

博物馆附近在举行大型活动,而且举办地有喷泉

那么出现了一个定位点,我们就需要搜寻它们的具体信息,进而推理出学校是哪一所

庆幸的是第二张图能很好的为已有信息给出补充

image-20231028213043320很显然,学长参加了statphys28,而他们吃饭的地方叫一信拉面馆

Statphys是国际统计物理大会的简称,是一个由国际统计物理学会(IUPAP)主办的会议。这个会议旨在为统计物理、计算机科学、数学等学科之间的交叉研究提供一个平台,让学者和学生们可以交流最新的研究成果和挑战性的重要问题。Statphys会议每两年举办一次,是统计物理领域最重要的国际会议之一。

那么这一届的会议举办地在日本东京

去官网查询一下

image-20231028213610257

时间是2023年8月7-11号,地点在东京大学本乡校区

我们在google map上找一下附近的拉面馆和博物馆

image-20231028214417989

东大附近有一个东京都美术馆和东京国立博物馆,既然题目里说的是博物馆,那么就暂且锁定为东京国立博物馆

搜索一信拉面,确实就在附近,我们的想法也得到验证

image-20231028215020743

寻找第3张图的位置

03

我们需要在地图上找到大片空地,然后在街景中验证

锁定上野公园大喷水

image-20231028223359990

image-20231028223257481

我们需要知道在这里举办的是什么活动,这样我们就能确定时间

用日语搜索上野公园大喷水活动

image-20231028224900804

其中梅酒祭statphys28的时间相对比较重合

我们先来做前面两题

1、你还记得与学长见面这天是哪一天吗?(格式:yyyy-mm-dd)

梅酒祭在2023年8月10日开始举办,所以答案为2023-08-10

2、在学校该展厅展示的所有同种金色奖牌的得主中,出生最晚者获奖时所在的研究所缩写是什么?

这里涉及到第一张图

01

图中为诺贝尔奖物理和化学奖牌,该奖牌得主为小柴昌俊

我们搜索一下东大诺贝尔奖展览Tokyo University Nobel Exhibits,发现校内的科学画廊确实有诺贝尔奖展览

image-20231028221318349

比对一下,其中出生最晚的是Takaaki Kajita教授,他所在研究所为东京大学宇宙辐射研究所(ICRR)

image-20231028221547761

得到flag

image-20231028224646710

来看3-4题

3、帐篷中活动招募志愿者时用于收集报名信息的在线问卷的编号(以字母 S 开头后接数字)是多少?

帐篷中活动即梅酒祭

在刚刚那个旅游资讯网站中我们发现了梅酒祭的官方网站

image-20231028225350937

进入活动官网

image-20231028225554832

找到志愿者招募网址

image-20231028225646224

得到申请表编号S495584522

image-20231028225736394

4、学长购买自己的博物馆门票时,花费了多少日元?

因为前面我们以经确认了博物馆为东京国立博物馆,我们去官网查找购票信息

image-20231028230305754

大学生门票500日元

但是这题没有这么简单,深入搜索后,我发现东大也在校园会员行列中

image-20231028231155123

所以学长应该可以免费观展

得到flag

image-20231028230457153

5、学长当天晚上需要在哪栋标志性建筑物的附近集合呢?(请用简体中文回答,四个汉字)

题目的描述说学长要继续学术之旅,而集合这种带有命令性质的词,说明学长晚上参加的是statphys28的活动

翻看官网的日程表

image-20231030012130605

那个时间段是宴会(Banquet)环节,说明我们的猜测正确

官网正好有发活动照片,我们浏览一下

image-20231030012313931

是在一条船上,也就是东京著名的水上巴士线路,之前有所耳闻,不过需要验证一下是否经过彩虹大桥,百度一下

image-20231028232104606

搜索该宴会的信息,依然是在官网

image-20231030012833776

image-20231030013248221

给出了集合地点:东京大学本乡校区 安田礼堂南侧

东大附近的标志性建筑就是东京巨蛋了,或者是安田讲堂本身

image-20231030013132061

image-20231030013955799

6、进站时,你在 JR 上野站中央检票口外看到「ボタン&カフリンクス」活动正在销售动物周边商品,该活动张贴的粉色背景海报上是什么动物(记作 A,两个汉字)? 在出站处附近建筑的屋顶广告牌上,每小时都会顽皮出现的那只 3D 动物是什么品种?(记作 B,三个汉字)?(格式:A-B)

这种活动的图片直接谷歌很难搜到

尝试在推特上搜索

image-20231028234605602

该动物为熊猫

任天堂主题乐园位于日本大阪环球影城,所以终点站为JR大阪环球影城站JR大阪站

附近的广告牌上可能有秋田犬金钱豹

排列组合一下

得到flag

image-20231030084151332

赛博井字棋

观察题目的站点

image-20231028144301564

小游戏的页面,一般需要通过修改前端JS中的规则

观察源码,发现判断胜负是在后端进行,通过前端将每一步下的位置传给后端

image-20231030085414755

一般来说井字棋永远不可能下赢AI,我们只能突破规则的限制

由于棋盘的OX是由数字存储,我们可以覆盖AI下过的地方

而前端对下同一个地方进行了过滤,那我们可以拦截请求并修改

由于该网站没有证书,bp抓不了,选择使用edge自带的网络功能

image-20231030093018402

我们可以看到下棋的位置为json格式

右键编辑并重新发送

image-20231030093102554

网络控制台将下棋位置改为AI下的地方(1,1)

image-20231030093156853

发包后我们下在(2,2)位置即可获胜

image-20231030092932846

奶奶的睡前 flag 故事

题目给的png如下

screenshot

再看看题目的提示

晴空万里的假期终于拍了拍翅膀飞来了。对于一心想扔掉教材、砸掉闹钟、跃向世界的 L 同学来说,期待了整整三年的跨国旅游大业终于是时候启动了,还能巧妙地顺带着做个美满的老友记。

可是,哎哟喂,他刚踩上波光粼粼的金沙海滩,那他最疼爱的华为手机就跟着海风一起去约会了大海,连他的钱包也在这场未知探索之旅中神秘失踪。

「这个地方怎么连个华为手机都不卖。若是买个苹果手机,心疼的是它连个实体 SIM 卡槽都藏起来了,回国肯定成了个大摆设。不如来个谷歌的『亲儿子』?」L 同学踌躇满志地嘀咕道。

那时,像是上天的安排,「咱这儿正好有个谷歌『亲儿子』的老手机,你拿去逍遥吧」。

L 同学满眼星光地接过,连系统都没心思升级,就开始疯狂安装那个久闻大名的 GPT 程序,甚至雀跃地在群里晒出一张跟 GPT 对话的精彩截图,一时间成为了群里的焦点人物。

谷歌亲儿子很显然是pixel,所以考虑去找它的截图漏洞

查阅新闻发现和win11的那个同属Acropalypse漏洞

那么直接用Acropalypse-Multi-Tool工具即可

由于不知道手机具体幸好,只能多试几次

image-20231028183821972

flag为flag{sh1nj1ru_k0k0r0_4nata_m4h0}

组委会模拟器

观察源代码,可以发现消息内容在span元素中

image-20231028195734693

编写油猴脚本,对span中的文字进行正则匹配,如果匹配flag则点击该span元素

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// ==UserScript==
// @name Hack Clicker
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Clicks on elements containing hack[...] in a span tag on the page
// @author R1ck
// @match http://202.38.93.111:10021/
// @grant none
// ==/UserScript==

(function() {
'use strict';

function clickElementIfContainsHack(element) {
let text = element.textContent;
let regex = /hack\[[a-z]+\]/g;

if (regex.test(text)) {
element.click();
}
}

function observeAndClickElements() {
let observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
let spans = node.querySelectorAll('span');
spans.forEach(span => {
clickElementIfContainsHack(span);
});
}
});
});
});

observer.observe(document.body, { childList: true, subtree: true });
}

observeAndClickElements();
})();

得到flag:flag{Web_pr0gra_mm1ng_8fed6bb815_15fun}

image-20231028202238559

Git?Git!

本题直接获得了带.get隐藏文件的git仓库

在目录下执行git log --stat

image-20231030113328136

发现最新一次的修改中并没有想要的信息

但是题目中提到马老师撤销了一次提交

被撤销的提交记录是可以在git reflog中看到

image-20231030113602712

我们可以观察到一个git log中没有的版本号,即被删除的commit

执行git reset --hard 505e1a3回退到该版本

再查看与之前版本的区别

image-20231030113840234

执行git diff 15fd0a1,得到flag

image-20231030114001151

HTTP集邮册

5种状态码

获取200状态只需发送正常请求即可

将请求方法换为一个不存在的,即可导致语法错误,此时服务端会返回400(Bad Request)

1
2
R1ck / HTTP/1.1\r\n
Host: example.com\r\n\r\n

image-20231028191449425

想要获取404,则需要请求一个不存在的页面

1
2
GET /nothing HTTP/1.1\r\n
Host: example.com\r\n\r\n

image-20231028190118345

由于服务端只接收GET请求,所以当我们将GET换成POST时,服务端可能会返回405(Method Not Allowed)

1
2
POST / HTTP/1.1\r\n
Host: example.com\r\n\r\n

image-20231028190148030

而如果我们传入的uri过长,则会导致410(Request-URI Too Large)状态码

1
2
GET /aaaaaaa(此处大约有4000个a,这里就不显示了) HTTP/1.1\r\n
Host: example.com\r\n\r\n

image-20231028190938623

没有状态……哈?

关于无状态码的判断逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
crlf = buf.find(b"\r\n")
if buf.strip() != b"":
try:
if crlf == -1:
raise ValueError("No CRLF found")
status_line = buf[:crlf]
http_version, status_code, reason_phrase = status_line.split(b" ", 2)
status_code = int(status_code)
except ValueError:
buf += "(无状态码)".encode()
status_code = None

很显然是CRLF注入,具体原理参考文章Nginx 配置错误导致漏洞(CRLF注入漏洞)——漏洞复现_配置错误漏洞-CSDN博客

在URL处填入CRLF,那么此时响应报文中就没有响应头存在

1
2
GET /\r\n HTTP/1.1\r\n
Host: example.com\r\n\r\n

image-20231029155120678

12种状态码

当我们输入一个服务器不支持的HTTP版本时,会返回505(HTTP Version not supported)

1
2
GET / HTTP/2.0\r\n
Host: example.com\r\n\r\n

image-20231029093358057

HTTP的请求头中有一种Range字段,可以控制请求的资源字节范围

语法为Range: bytes=start-end,用于请求部分资源

例如

1
2
Range: bytes=10- :第10个字节及最后个字节的数据
Range: bytes=40-100 :第40个字节到第100个字节之间的数据.

当我们设置的Range范围服务器能够返回,则会响应206(Partial Content)

1
2
3
GET / HTTP/1.1\r\n
Host: example.com\r\n
Range: bytes=0-1\r\n\r\n

image-20231029160434646

但是当客户端请求的 Range 头部格式不正确,或者客户端请求的 Range 超出了服务器支持的范围,那么服务器就会响应416(Range Not Satisfiable)

1
2
3
GET / HTTP/1.1\r\n
Host: example.com\r\n
Range: bytes=10000-10000\r\n\r\n

image-20231029160719033

当我们的请求头中存在一个服务器不支持的字段时,则会返回501(Not Implemented)

这里我们试出来了Transfer-Encoding

1
2
3
GET / HTTP/1.1\r\n
Host: example.com\r\n
Transfer-Encoding:\r\n\r\n

image-20231029161526225

在浏览器和服务端设置都允许缓存策略的前提下(服务端响应头的Cache-Control不为no-store,即禁止任何缓存),如果某个请求的响应头设置了Last-Modified:

第一次请求:浏览器会记住响应头的Last-Modified;
第二次及以后请求:浏览器会携带保存的Last-Modified分别作为If-Modified-Since放入请求头中携带过去,以此到服务端验证此次请求的资源是否过期或更新;服务端进行判断,若过期或更新,则返回新的资源;否则返回空以及状态码304,节省服务端消耗。

本题服务器的资源应该是静态的,所以我们可以主动发送If-Modified-Since字段,时间设置为响应头中Last-Modified的值,此时服务器会返回304(Not Modified)

1
2
3
GET / HTTP/1.1\r\n
Host: example.com\r\n
If-Modified-Since: Tue, 15 Aug 2023 17:03:04 GMT\r\n\r\n

image-20231029163653605

HTTP 协议中的 If-Unmodified-Since 消息头用于请求之中,使得当前请求成为条件式请求:只有当资源在指定的时间之后没有进行过修改的情况下,服务器才会返回请求的资源,或是接受 POST或其他 non-safe 方法的请求。如果所请求的资源在指定的时间之后发生了修改,那么会返回 412 (Precondition Failed) 错误。

此处我们将If-Unmodified-Since字段的时间设置在响应头中Last-Modified的时间之前,服务器就无法满足我们的要求,并会返回412

1
2
3
GET / HTTP/1.1\r\n
Host: example.com\r\n
If-Unmodified-Since: Tue, 14 Aug 2023 17:03:04 GMT\r\n\r\n

image-20231029164647337

Expect 是一个请求消息头,包含一个期望条件,表示服务器只有在满足此期望条件的情况下才能妥善地处理请求。

规范中定义的唯一期望是Expect: 100-continue,服务器应该以此作为回应:

  • 100 如果标题中包含的信息足以立即获得成功,

  • 417(期望失败),如果它不能达到预期; 或者其他任何其他 4xx 状态。

本题我们直接用Expect: 100-continue进行试验,发现服务器返回了响应码100(Continue)

1
2
3
GET / HTTP/1.1\r\n
Expect: 100-continue\r\n
Host: example.com\r\n\r\n

成功!

image-20231029170019231

题目提示通过无线信道传输图片,那么应该就是SSTV慢扫描电视,我们需要解码无线电

使用软件RX-SSTV

连上虚拟声卡后开始播放音频

image-20231029091252133

flag{SSssTV_y0u_W4NNa_HaV3_4_tr}

JSON ⊂ YAML?

这题主要是研究YAML和JSON的区别

题目需要给出一个字符串,使得其在解析为YAML1.1时和JSON时不同

下面这篇文章讲的非常详细:JSON is not a YAML subset (john-millikin.com)

JSON ⊄ YAML 1.1

在以指数表示法处理数字时,YAML 1.1 规范对数字的语法比 JSON 更严格:1e2 是有效的 JSON 数字,但 YAML 1.1 要求将其编写为 1.0e+2。此时YAML 解析器会将1e2视为字符串。

所以我们尝试向题目传入{"R1ck":1e2}

image-20231029100855804

JSON ⊄ YAML 1.2

本题需要在解析为YAML1.1时正常但YAML1.2时出现异常

这里需要用到json、yaml和ruamel.yaml这几个包的特性

json.loadsyaml.safe_load解析含有重复key的json数据时,后来的值会覆盖原来的值,导致最后只有一个key的值留下来。

但是YAML1.2更加严格,重复的键会导致ruamel.yaml.safe_load报错

所以我们尝试向题目传入{"R1ck":1,"R1ck":2}

image-20231030162007980

Docker for Everyone

详细原理参考文章docker用户组提权_docker命令提权-CSDN博客

在宿主机,执行ls -la /

image-20231029141240632

发现flag为软链接

执行docker run -v /dev/shm/:/flag -i -t alpine将该目录映射给docker的flag目录

由于我们在docker中有root权限,所以可以查看flag文件

image-20231029142940311

惜字如金 2.0

题目所给的惜字如金原则如下

  • 第一原则(又称 creat 原则):如单词最后一个字母为「e」或「E」,且该字母的上一个字母为辅音字母,则该字母予以删除。
  • 第二原则(又称 referer 原则):如单词中存在一串全部由完全相同(忽略大小写)的辅音字母组成的子串,则该子串仅保留第一个字母。

使用python调试所给源码,尝试补全删除掉的字母

首先是if __name__ == '__main__':中name被删掉的e

接着后面两句比较方法中的单词也需要补全来满足该方法的判断标准,即传进去的两个变量相等

最后就是get_cod_dict方法

其中会验证几个字符串是否满足24个字符的长度,源码如下

1
2
3
4
5
6
7
8
9
10
def get_cod_dict():
# prepar th cod dict
cod_dict = []
cod_dict += ['nymeh1niwemflcir}echaet']
cod_dict += ['a3g7}kidgojernoetlsup?h']
cod_dict += ['ulw!f5soadrhwnrsnstnoeq']
cod_dict += ['ct{l-findiehaai{oveatas']
cod_dict += ['ty9kxborszstguyd?!blm-p']
check_equals(set(len(s) for s in cod_dict), {24})
return ''.join(cod_dict)

这4个字符串都是23个字符,所以需要每个字符串需要补充一个字符,此处需要进行爆破被删除字符的位置以及原因(满足了哪个原则)

但是事实上本题的字符数量不算多,所以可以先通过肉眼观察一些特征来简化计算量

比如最终flag字符串的格式为flag{xxx-xxx}那么其中可以先寻找一下这几个字符目前在字典中的位置

f在字典的index为53,即第三个字符串的第6位,但此时f在第5位,说明删除的字符在f之前

l在字典的index为41,即第二个字符串的18位,但此时18位上也为l,说明第二个字符串被删除的字符在l之后

g的位置有问题,说明最后一个字符串删除位置在g之前

{位置有问题,说明第四个字符串删除位置在{之前

将被删除的位置按原则还原

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/python3

# Th size of the fill may reduce after XZRJification

def check_equals(left, right):
# check whether left == right or not
if left != right: exit(1)

def get_code_dict():
# prepar th code dict
code_dict = []
code_dict += ['nymeh1niwemflcir}echaete']
code_dict += ['a3g7}kidgojernoetlsupe?h']
code_dict += ['ulwe!f5soadrhwnrsnstnoeq']
code_dict += ['cte{l-findiehaai{oveatas']
code_dict += ['tye9kxborszstguyd?!blm-p']
check_equals(set(len(s) for s in code_dict), {24})
return ''.join(code_dict)

def decrypt_data(input_codes):
# retriev th decrypted data
code_dict = get_code_dict()
output_chars = [code_dict[c] for c in input_codes]
return ''.join(output_chars)

if __name__ == '__main__':
# check som obvious things
check_equals('creat', 'cre' + 'at')
check_equals('referrer', 'refer' + 'rer')
# check th flag
flag = decrypt_data([53, 41, 85, 109, 75, 1, 33, 48, 77, 90,
17, 118, 36, 25, 13, 89, 90, 3, 63, 25,
31, 77, 27, 60, 3, 118, 24, 62, 54, 61,
25, 63, 77, 36, 5, 32, 60, 67, 113, 28])
check_equals(flag.index('flag{'), 0)
check_equals(flag.index('}'), len(flag) - 1)
# print th flag
print(flag)

运行后输出flag:flag{you-ve-r3cover3d-7he-an5w3r-r1ght?}

高频率星球

在kali上安装asciinema

sudo apt-get install asciinema

执行asciinema cat asciinema_restore.rec > flag.txt

将所以shell的指令以及打印信息输出出来

image-20231029132504559

我们可以看到,高频率星人先验证了flag.js的哈希值

然后使用less将flag.js打印出来

最后需要执行node flag.js,而此时录制信息也结束了

所以我们可以从刚刚保存的flag.txt中将flag.js提取出来,然后自己用nodejs执行