USTC Hackergame 2022 Write Up
签到
日常改参数。
猫咪问答喵
中国科学技术大学 NEBULA 战队(USTC NEBULA)是于何时成立的喵?
在这个页面中有这样一段介绍:
星云战队(Nebula)
中国科学技术大学“星云战队(Nebula)”成立于 2017 年 3 月,“星云”一词来自中国科学技术大学 BBS“瀚海星云”,代表同学们对科学技术的无限向往和追求。战队现领队为网络空间安全学院吴文涛老师,现任队长为网络空间安全学院李蔚林、童蒙和武汉。战队核心成员包括了来自网络空间安全学院、少年班学院、物理学院、计算机学院等各个院系的同学,充分体现了我校多学院共建网络空间安全一级学科的特点。战队以赛代练,以赛促学,在诸多赛事中获得佳绩。
可知答案为
2017-03
。
22 年坚持,小 C 仍然使用着一台他从小用到大的 Windows 2000 计算机。那么,在不变更系统配置和程序代码的前提下,Firefox 浏览器能在 Windows 2000 下运行的最后一个大版本号是多少?
Google 搜索
windows 2000 firefox
可以搜索到一个帖子:Last version of fireFox to work on Windows 2000?,可知答案为 12。你知道 PwnKit(CVE-2021-4034)喵?据可靠谣传,出题组的某位同学本来想出这样一道类似的题,但是发现 Linux 内核更新之后居然不再允许 argc 为 0 了喵!那么,请找出在 Linux 内核 master 分支(torvalds/linux.git)下,首个变动此行为的 commit 的 hash 吧喵!
关于这部分的限制在
fs/exec.c
文件下,那么git blame
可知这部分是在dcd46d8
中被修改的。
家目录里的秘密
VS Code 里的 flag
全局搜索 flag{
可得:
rclone 里的 flag
在 rclone.conf
里可以找到一个密码,通过在 Google 上搜索可以找到 一份现成的解密代码:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"log"
)
// crypt internals
var (
cryptKey = []byte{
0x9c, 0x93, 0x5b, 0x48, 0x73, 0x0a, 0x55, 0x4d,
0x6b, 0xfd, 0x7c, 0x63, 0xc8, 0x86, 0xa9, 0x2b,
0xd3, 0x90, 0x19, 0x8e, 0xb8, 0x12, 0x8a, 0xfb,
0xf4, 0xde, 0x16, 0x2b, 0x8b, 0x95, 0xf6, 0x38,
}
cryptBlock cipher.Block
cryptRand = rand.Reader
)
// crypt transforms in to out using iv under AES-CTR.
//
// in and out may be the same buffer.
//
// Note encryption and decryption are the same operation
func crypt(out, in, iv []byte) error {
if cryptBlock == nil {
var err error
cryptBlock, err = aes.NewCipher(cryptKey)
if err != nil {
return err
}
}
stream := cipher.NewCTR(cryptBlock, iv)
stream.XORKeyStream(out, in)
return nil
}
// Reveal an obscured value
func Reveal(x string) (string, error) {
ciphertext, err := base64.RawURLEncoding.DecodeString(x)
if err != nil {
return "", fmt.Errorf("base64 decode failed when revealing password - is it obscured? %w", err)
}
if len(ciphertext) < aes.BlockSize {
return "", errors.New("input too short when revealing password - is it obscured?")
}
buf := ciphertext[aes.BlockSize:]
iv := ciphertext[:aes.BlockSize]
if err := crypt(buf, buf, iv); err != nil {
return "", fmt.Errorf("decrypt failed when revealing password - is it obscured? %w", err)
}
return string(buf), nil
}
// MustReveal reveals an obscured value, exiting with a fatal error if it failed
func MustReveal(x string) string {
out, err := Reveal(x)
if err != nil {
log.Fatalf("Reveal failed: %v", err)
}
return out
}
func main() {
fmt.Println(MustReveal("tqqTq4tmQRDZ0sT_leJr7-WtCiHVXSMrVN49dWELPH1uce-5DPiuDtjBUN3EI38zvewgN5JaZqAirNnLlsQ"))
}
跑一遍就出来了:
HeiLang
打开 VSCode,将 \| ([\d]+)\] = ([\d]+)
递归替换为 ] = $2\na[$1] = $2
,然后执行脚本即可得到 flag:
Xcaptcha
在打开验证码页面的一瞬间将这段 JS 脚本塞进控制台里即可。
for (let i = 1; i <= 3; i++) {
let raw_str = document.querySelector(`label[for="captcha${i}"]`).innerHTML;
let match = /([\d]+)\+([\d]+)/.exec(raw_str);
let sum = BigInt(match[1]) + BigInt(match[2]);
document.getElementById('captcha' + i).value = sum.toString();
}
document.getElementById('submit').click();
旅行照片 2.0
照片分析
exiftool 一把梭。
LaTeX 机器人
纯文本
众所周知,LaTeX 有一个 \input
指令:
Flag 到手:flag{becAr3fu11dUd3c5a1b17ffa}
。
安全的在线测评
无法 AC 的题目
最开始我打算在程序里读文件,结果不知道为什么写挂了……
于是就立马想到了用汇编读文件,代码和动态数据差不多。
动态数据
查看评测机的源码可以发现 n = 5,那么可以用汇编在编译的时候读文件:
#include <stdio.h>
#include <string.h>
asm("staticInput: .incbin \"data/static.in\"");
asm("staticAnswer: .incbin \"data/static.out\"");
asm("dynamicInput0: .incbin \"data/dynamic0.in\"");
asm("dynamicInput1: .incbin \"data/dynamic1.in\"");
asm("dynamicInput2: .incbin \"data/dynamic2.in\"");
asm("dynamicInput3: .incbin \"data/dynamic3.in\"");
asm("dynamicInput4: .incbin \"data/dynamic4.in\"");
asm("dynamicAnswer0: .incbin \"data/dynamic0.out\"");
asm("dynamicAnswer1: .incbin \"data/dynamic1.out\"");
asm("dynamicAnswer2: .incbin \"data/dynamic2.out\"");
asm("dynamicAnswer3: .incbin \"data/dynamic3.out\"");
asm("dynamicAnswer4: .incbin \"data/dynamic4.out\"");
extern char staticInput[];
extern char staticAnswer[];
extern char dynamicInput0[];
extern char dynamicInput1[];
extern char dynamicInput2[];
extern char dynamicInput3[];
extern char dynamicInput4[];
extern char dynamicAnswer0[];
extern char dynamicAnswer1[];
extern char dynamicAnswer2[];
extern char dynamicAnswer3[];
extern char dynamicAnswer4[];
char n[1000000], a[6][1000000], p[1000000], q[1000000];
int main(int argc, char **argv[]) {
scanf("%s\n", &n);
sscanf(staticInput, "%s", a[0]);
sscanf(dynamicInput0, "%s", a[1]);
sscanf(dynamicInput1, "%s", a[2]);
sscanf(dynamicInput2, "%s", a[3]);
sscanf(dynamicInput3, "%s", a[4]);
sscanf(dynamicInput4, "%s", a[5]);
if (!strcmp(n, a[0])) {
sscanf(staticAnswer, "%s\n%s", p, q);
printf("%s\n%s\n", p, q);
} else if (!strcmp(n, a[1])) {
sscanf(dynamicAnswer0, "%s\n%s", p, q);
printf("%s\n%s\n", p, q);
} else if (!strcmp(n, a[2])) {
sscanf(dynamicAnswer1, "%s\n%s", p, q);
printf("%s\n%s\n", p, q);
} else if (!strcmp(n, a[3])) {
sscanf(dynamicAnswer2, "%s\n%s", p, q);
printf("%s\n%s\n", p, q);
} else if (!strcmp(n, a[4])) {
sscanf(dynamicAnswer3, "%s\n%s", p, q);
printf("%s\n%s\n", p, q);
} else if (!strcmp(n, a[5])) {
sscanf(dynamicAnswer4, "%s\n%s", p, q);
printf("%s\n%s\n", p, q);
}
return 0;
}
企鹅拼盘
这么简单我闭眼都可以!
手动一个一个试就好啦~
后记
今年是我打 Hackergame 的第三年了,由于近期学业繁忙(甚至我刚从 CSP-S 考场出来就回来写 Write Up),所以并没有能抽出足够的时间来享受这场比赛了,只能用两天的零碎时间水一点签到题了事,binary 还是一如既往地稀烂……