webfreemedium

Dark Runes

HackTheBox

"Survivors find a battered laptop in the rubble. Powering it up, they discover a cryptic software interface from an ancient architecture firm, hinting at vital blueprints. They must crack its security protocols. Undeterred, they race against time."

$ ls tags/ techniques/
admin_registration_bypassaccess_code_brute_forcephantomjs_file_readssrf_via_pdf_generationiframe_file_protocol

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

Dark Runes — HackTheBox

Description

"Survivors find a battered laptop in the rubble. Powering it up, they discover a cryptic software interface from an ancient architecture firm, hinting at vital blueprints. They must crack its security protocols. Undeterred, they race against time."

Target: http://154.57.164.69:31117

Technology Stack

  • Node.js 16.5.0 (Express) — web application
  • SQLite — database with users and documents tables
  • markdown-pdf v11.0.0 — markdown to PDF conversion via PhantomJS
  • sanitize-html — HTML sanitization (only on regular document routes)
  • HMAC-like cookie auth — custom cookie generation/validation with random SECRET
  • PhantomJS — headless browser used by markdown-pdf for rendering

Architecture

Single Express application with the following route structure:

  1. Auth routes (/login, /register) — user registration and login with cookie-based auth
  2. Document routes (/document/*) — CRUD for markdown documents, content sanitized via sanitize-html
  3. Export routes (/document/export/:id, /document/debug/export) — PDF generation from markdown/HTML

Key security middleware:

  • isAuthenticated — validates cookie signature using HMAC with random SECRET
  • isAdmin — checks req.user.username === "admin"

Access code system:

  • rotatePass() generates a 4-digit code (0000-9999) via crypto.randomBytes(2).readUInt16BE() % 10000
  • Writes the code to a file named with that code
  • verifyPass() checks the pass; on failure, calls rotatePass() to generate a new code
  • Only rotatePass() is called on startup — no admin user is pre-created

Analysis

Vulnerability 1: Missing Admin User — Registration Bypass

The isAdmin middleware checks req.user.username === "admin", but the application never creates an admin user at startup. Only rotatePass() is called in src/index.js. The /register endpoint has no restriction on the username "admin", so anyone can register as admin.

...

$ grep --similar

Similar writeups