Critical security issues in SevOne NMS
Hi, guys! Today I want to talk about security issues one of SevOne product — SevOne NMS (Network Management System). What is SevOne? “SevOne provides the most scalable network monitoring platform for the world’s most connected companies” — here’s what the official website says. “The SevOne Network Management System (NMS) software enables you to poll and monitor what happens in your network right now and to analyze what occurred historically.”
My first meeting with this management system was in Mach 2015 of last year while security auditing. During the audit I found a lot of vulnerabilities and misconfigurations. After that I was report about problems, but as of January 2016 it has not been fixed. I hope this post will help to draw the attention of developers to existing problems.
All information and material presented is intended to be used for educational or informational purposes only.
Discovery target
OK, let’s start!
You can download SevOne NMS as virtual machine packaging file here — https://www.sevone.com/download2/free/vimage/SevOne-Download.ova for free. Also, if you want, download user manual — https://www.sevone.com/download2/free/SevOne_vPAS_Guide.pdf. There are you can find some default passwords. SevOne is a commercial product. For security issues demonstration we don’t need license file, but for full use working you need a valid one.
After run imported virtual machine you can see main menu:
By default password for access to system configuration menu is ‘changeme‘. See user guide:
Also NMS has web interface and it’s more interesting for us.
Frontend written in PHP language. Default credentials is admin:SevOne. See user guide:
To our great disappointment, very often password is changed. But don’t worry and go on reading 🙂
Try to scan SevOne NMS with nmap:
Here we can see a lot of open ports. Have ssh and two mysql servers open for connection. Mysql servers is not allowed to connect from non local addresses.
That’s all — boring part is over and we move to some interesting things now 😉
Let the hacking begin
We can try connect to SSH with default credentials. Remember about password ‘changeme‘ for configuration menu from manual. Guess what? If administrator didn’t changed default password you can log in with it! Using username ‘configuser‘ try that:
Connection successfull. Home folder and shell did not configure for that user because of thet we cannot execute shell commands. But we can use connection as tunnel. Watch my hands:
ssh -N -L 31337:127.0.0.1:3306 configuser@192.168.1.104
With that command I am create tunnel from local 31337 port to mysql port 3306 on NMS server. I am also use ‘N’ switch. This guy helps us do not execute any remote command and connection will open. Remember about only local connection to mysql server? This trick can help you resolve this problem.
OK, now we can connect to mysql as root. Guess what password for root user? Correct! Password is empty.
Of course we have FILE_PRIV and can read some files from server:
Here we can see users, who have shell access.
Take notice on ‘cmcdr’ user. After I got access to the filesystem of NMS server I found some more default usernames and passwords. There was ‘cmcdr‘ and it’s password is *drum-roll* ‘cmcdr‘ (file: /usr/share/php5/soap3/examples/python/call_manager_verifier.py)
Now we have shell access to the server.
XXE, RCE and other disasters
Go back to web gui. There we have two XXE vulnerabilities in license file parse functionality. One working only for logged user. Other looks like the same, but working for unauthorized persons.
Let’s talk about authorized XXE. Take a look at this piece of code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | function getCertFile( $fromSession ) { $certFile = array(); if( $fromSession ) { $certFile["file"]["error"] = $_SESSION["file"]["error"]; $certFile["file"]["size"] = $_SESSION["file"]["size"]; $certFile["file"]["name"] = $_SESSION["file"]["name"]; $certFile["file"]["tmp_name"] = $_SESSION["file"]["tmp_name"]; } else { $certFile["file"]["error"] = $_FILES["file"]["error"]; $certFile["file"]["size"] = $_FILES["file"]["size"]; $certFile["file"]["name"] = $_FILES["file"]["name"]; $certFile["file"]["tmp_name"] = $_FILES["file"]["tmp_name"]; } return $certFile; } // Parse the import file {{{ try { $parsedCert = @(new SimpleXMLElement( $importCert )); --- $certFile = getCertFile( $fromSession ); --- $file = fopen( $certFile[ "file" ][ "tmp_name" ], "r" ); --- $importCert = fread( $file, MAX_IMPORT_SIZE ); --- // Parse the import file {{{ try { $parsedCert = @(new SimpleXMLElement( $importCert )); |
PHP version lower than 5.5.0 installed on NMS and class SimpleXMLElement uses uploaded file without any entity filtration. All of this makes XXE attack possible.
For successfilly exploitation demonstration we need pseudo-valid license file. I have one here. After authorization you need to send POST request to /doms/about/importCert.php:
Unauthorized XXE works similar, but without data output. This time post to /doms/login/index.php:
This attack possible because of incorrect user authorization check:
4 | Session::activeRequest(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static function activeRequest( $level = null, $write = 0, $xsrfToken = null ) { // Validate session and spit out json if need be self::jsonSessionCheck( $level, $xsrfToken ); --- private static function jsonSessionCheck( $level = null, $xsrfToken = null ) { --- $validation = self::validateSession( $level, $xsrfToken ); --- public static function validateSession( $level = null, $xsrfToken = null ) { --- $page = Gui::determinePage(); --- // If there is no session, we don't care about the rest of validation if( !isset( $_SESSION[ 'uid' ] ) && $page !== 'login' ) { return Gui::NO_LOGIN; } |
As you can see, if $page variable is equals to ‘login‘ string then condition FALSE and script will continue work.
You can try some SSRF attacks here by yourself but I’m moving on.
If we have valid user access we doesn’t need any XXE, SSRF bla bla bla, because RCE power in our hands. Take a look here:
1 2 3 4 5 6 7 8 9 10 11 12 | // Get our POST data $peerId = $_REQUEST[ 'peerId' ]; $pids = $_REQUEST[ 'pids' ]; --- // Note that we need a 'root' shell to make this command work. $shell = new SevOne\shell\Shell( 'root', $targetAddress ); --- // The kill commands (oh, and unprocess each thread as we see them $command = 'kill -9 ' . implode(' ', $pids); // Run our kill command and get ouput and return values $output = []; $returnValue = $shell->exec( $command, $output ); |
Hmm “// Note that we need a ‘root’ shell to make this command work.”, thanks a lot for that guys. But yet we need valid user access. Where can we take one of them? Piece of cake!
After I am get access to the SevOne NMS’s filesystem I’ve found some interesting thing — harcoded account with name ‘SevOneStats‘. I saw it at mysql table ” for the first time.
After that I was search files for ‘sevonestats‘ and found this /usr/local/scripts/utilities/plugins/selfmon/RAIDMon/poll.megacli.objects.php:
Yes, credentials is SevOneStats:n3v3rd13.
Finally, connect all information together and try to exploit.
Boom! It is a victory. At now we have root privileges on system.
Conclusion
In conclusion I’d like to say there are some more bugs left behind the scenes and I belive you will be able to find them for practice your skills.
Also, I commit a working reverse-shell exploit and XXE license file at the Github — https://github.com/allyshka/exploits/tree/master/sevone.
That’s all I want to tell you at this post. Good-bye for the moment, have a nice day and take care about yourself.
P.S. Sorry for my bad English :]
Anonymous
January 15, 2016 @ 8:19 pm
Wow. I can’t believe how someone could publish such vulnerable product. I’m learning security and I really enjoy your blog. I wish other posts would be in English.
russec
January 16, 2016 @ 8:43 am
Thanks. I’ll try to write in English at now.
Mike
May 7, 2016 @ 4:51 am
Your Post is great! and your english is fine.
check Sevone now and lets know if they fix it or made worse. thanks again