<?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 9 Writeup — Extract Readable Text from a Mostly-Binary File</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Sun, 01 Nov 2026 22:41:40 +0000</pubDate>
      <link>https://dev.to/ctfdojo/overthewire-bandit-level-9-writeup-extract-readable-text-from-a-mostly-binary-file-3db7</link>
      <guid>https://dev.to/ctfdojo/overthewire-bandit-level-9-writeup-extract-readable-text-from-a-mostly-binary-file-3db7</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;code&gt;data.txt&lt;/code&gt; is mostly binary data, but the password is one of the few human-readable strings inside it, preceded by several &lt;code&gt;=&lt;/code&gt; characters — &lt;code&gt;strings&lt;/code&gt; pulls out the readable text, and &lt;code&gt;grep&lt;/code&gt; narrows to the marked line.&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; 9 → 10&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; strings + grep for a marker pattern&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This file looks like garbage when opened directly — mostly non-printable binary bytes. Somewhere inside it, a handful of human-readable strings survive, and the level says the password is one of them, marked by leading equals signs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Level Goal: The password for the next level is stored in the file data.txt in one of the few human-readable strings, preceded by several '=' characters."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1 — See why opening it directly doesn't work
&lt;/h2&gt;

&lt;p&gt;Try viewing the raw file and notice it's mostly unprintable binary noise — &lt;code&gt;cat&lt;/code&gt; or opening it in a plain editor produces garbled output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;file data.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data.txt: data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Extract just the readable text
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;strings&lt;/code&gt; scans a file and prints only the sequences of printable characters, skipping the binary noise around them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;strings data.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(a short list of readable strings scattered among the output)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Narrow to the marked line
&lt;/h2&gt;

&lt;p&gt;The level told you the real string is preceded by several &lt;code&gt;=&lt;/code&gt; characters — grep for a line starting with one or more of them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;strings data.txt | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&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;========== [PASSWORD FOR bandit10]
&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;&lt;code&gt;strings&lt;/code&gt; is the standard first move on any file that looks like binary garbage — it doesn't care what the file format actually is, it just reports every run of printable characters it finds. Combined with a targeted &lt;code&gt;grep&lt;/code&gt;, it turns "needle in a haystack of binary noise" into a one-line command.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;strings&lt;/code&gt; extracts printable text from any file, regardless of its actual format&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;^=&lt;/code&gt; in a grep pattern anchors the match to the start of the line&lt;/li&gt;
&lt;li&gt;This combination — &lt;code&gt;strings&lt;/code&gt; piped into a targeted &lt;code&gt;grep&lt;/code&gt; — reappears constantly in reversing and forensics challenges, not just this one&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/overthewire-bandit-level9-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 Static Noise Writeup — Identify a File Hidden in Binary Data</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Thu, 29 Oct 2026 20:33:12 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-static-noise-writeup-identify-a-file-hidden-in-binary-data-444m</link>
      <guid>https://dev.to/ctfdojo/picoctf-static-noise-writeup-identify-a-file-hidden-in-binary-data-444m</guid>
      <description>&lt;p&gt;&lt;em&gt;A provided file looks like a random data stream (static noise), but its extension or appearance is misleading — the &lt;code&gt;file&lt;/code&gt; command reveals its true type (based on its "magic bytes"), and &lt;code&gt;strings&lt;/code&gt; (or &lt;code&gt;binwalk&lt;/code&gt;) lets us extract the readable text hidden inside, including the 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; Misc&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; filestringsbinwalk&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The challenge provides a file named &lt;code&gt;mystery.bin&lt;/code&gt;, with a cryptic prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"All I see is static... or is there something more?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Opening the file with a hex editor or a plain &lt;code&gt;cat&lt;/code&gt;, we end up with what looks like a stream of random bytes — no visible pattern to the naked eye, like white noise. Nothing clearly indicates what type of file it is.&lt;/p&gt;

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

&lt;p&gt;The number one reflex when facing any unknown file in a CTF: never trust the extension, and run &lt;code&gt;file&lt;/code&gt;. This command reads the first bytes of the file (the &lt;strong&gt;"magic bytes"&lt;/strong&gt;) and compares them against a database of known signatures to determine the actual format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ file mystery.bin
mystery.bin: Zip archive data, at least v2.0 to extract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Surprise: despite its generic &lt;code&gt;.bin&lt;/code&gt; extension and its noisy binary appearance, the file is actually a &lt;strong&gt;ZIP archive&lt;/strong&gt;. Magic bytes never lie, unlike a file's extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Extract readable text with strings
&lt;/h2&gt;

&lt;p&gt;Before even treating the file as a ZIP archive, we can try a more direct approach: &lt;code&gt;strings&lt;/code&gt; extracts every printable character sequence contained in a binary file, regardless of its format:&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;strings &lt;span class="nt"&gt;-n&lt;/span&gt; 8 mystery.bin | &lt;span class="nb"&gt;grep &lt;/span&gt;picoCTF
picoCTF&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="k"&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 &lt;code&gt;-n 8&lt;/code&gt; option filters to keep only sequences of at least 8 consecutive printable characters, which eliminates most of the random binary noise and surfaces the actual text strings — here, the flag appears directly, stored in plaintext inside the archive despite the raw file's noisy appearance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — If needed, dig deeper with binwalk
&lt;/h2&gt;

&lt;p&gt;On a trickier variant of the challenge where &lt;code&gt;strings&lt;/code&gt; alone isn't enough (compressed flag or a file embedded more deeply), we use &lt;code&gt;binwalk&lt;/code&gt; to scan the file for signatures of nested files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ binwalk mystery.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Zip archive data, at least v2.0 to extract
128           0x80            End of Zip archive, footer signature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an archive or an image is detected inside, we extract it automatically with the &lt;code&gt;-e&lt;/code&gt; option:&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;binwalk &lt;span class="nt"&gt;-e&lt;/span&gt; mystery.bin
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;_mystery.bin.extracted/
flag.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This complementary approach is useful whenever a simple grep on the &lt;code&gt;strings&lt;/code&gt; output isn't enough to spot the flag directly.&lt;/p&gt;

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

&lt;p&gt;Whether via &lt;code&gt;strings&lt;/code&gt; directly or after extraction with &lt;code&gt;binwalk&lt;/code&gt;, the flag appears in the standard &lt;code&gt;picoCTF{...}&lt;/code&gt; format among the text strings extracted from the file.&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;A short challenge that builds reflexes essential for everything that follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never trust a file extension — the "magic bytes" at the start of a file are the real source of truth about its format&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;file&lt;/code&gt;, &lt;code&gt;strings&lt;/code&gt;, and &lt;code&gt;binwalk&lt;/code&gt; form the go-to trio of tools for a first inspection of any unknown binary file&lt;/li&gt;
&lt;li&gt;What looks like random noise to the naked eye can be an archive, an image, or any other perfectly structured format — only the right tools reveal the truth&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-static-noise-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>OverTheWire Bandit Level 8 Writeup — Find the One Unique Line Among Duplicates</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Tue, 27 Oct 2026 21:23:36 +0000</pubDate>
      <link>https://dev.to/ctfdojo/overthewire-bandit-level-8-writeup-find-the-one-unique-line-among-duplicates-3801</link>
      <guid>https://dev.to/ctfdojo/overthewire-bandit-level-8-writeup-find-the-one-unique-line-among-duplicates-3801</guid>
      <description>&lt;p&gt;&lt;em&gt;The password is the one line in &lt;code&gt;data.txt&lt;/code&gt; that occurs exactly once among thousands of duplicated lines — &lt;code&gt;sort&lt;/code&gt; groups identical lines together, and &lt;code&gt;uniq -u&lt;/code&gt; prints only the ones with no duplicate.&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; 8 → 9&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; sort + uniq -u&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Level 8 hands you a file where almost every line of text shows up more than once. Somewhere in there is exactly one line that appears only a single time — that line is the password.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Level Goal: The password for the next level is stored in the file data.txt and is the only line of text that occurs only once."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1 — Understand why uniq alone won't work
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;uniq&lt;/code&gt; only collapses consecutive duplicate lines — it has no effect on a file where duplicates are scattered throughout rather than grouped together. The file needs to be sorted first so every copy of a given line ends up adjacent to its other copies.&lt;/p&gt;

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

&lt;p&gt;Sorting brings every duplicate of a line next to each other, which is the precondition &lt;code&gt;uniq&lt;/code&gt; needs to do anything useful:&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;sort &lt;/span&gt;data.txt | less
&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;(every line now appears grouped with its exact duplicates)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Filter to lines with no duplicate
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;uniq&lt;/code&gt;'s &lt;code&gt;-u&lt;/code&gt; flag prints only the lines that have exactly one occurrence — exactly the property the level described:&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;sort &lt;/span&gt;data.txt | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[PASSWORD FOR bandit9]
&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;&lt;code&gt;sort&lt;/code&gt; and &lt;code&gt;uniq&lt;/code&gt; are almost always used together, because &lt;code&gt;uniq&lt;/code&gt; only ever looks at immediately adjacent lines. "Find the duplicate" or "find the one that's NOT duplicated" in a scrambled file is a &lt;code&gt;sort | uniq&lt;/code&gt; problem essentially every time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;uniq&lt;/code&gt; collapses or isolates duplicates among ADJACENT lines only — unsorted input defeats it silently&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sort&lt;/code&gt; groups every copy of an identical line together, which is why it always comes first in this pipeline&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;uniq -u&lt;/code&gt; prints lines with no duplicate at all; plain &lt;code&gt;uniq -d&lt;/code&gt; does the opposite, printing only lines that DO repeat&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/overthewire-bandit-level8-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 Nice Netcat Writeup — Talk to a Remote Service with netcat</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Mon, 26 Oct 2026 20:27:07 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-nice-netcat-writeup-talk-to-a-remote-service-with-netcat-5din</link>
      <guid>https://dev.to/ctfdojo/picoctf-nice-netcat-writeup-talk-to-a-remote-service-with-netcat-5din</guid>
      <description>&lt;p&gt;&lt;em&gt;This challenge teaches the absolute basics of &lt;code&gt;netcat&lt;/code&gt; (&lt;code&gt;nc&lt;/code&gt;): connecting to a remote host and port, reading what the service sends back, and responding correctly to get the flag — the very first building block of any network/pwn-oriented CTF.&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; Misc&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 50 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; netcatnc&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The challenge simply provides a connection command to a remote service:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Can you connect to this service and figure out what it wants?"&lt;/em&gt;&lt;br&gt;
&lt;code&gt;nc saturn.picoctf.net 54321&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No binary to download, no file to analyze — everything happens live over the network, via a raw TCP connection. It's the perfect opportunity to learn the single most fundamental tool of any network-oriented CTF: &lt;strong&gt;netcat&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Connect with netcat
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;netcat&lt;/code&gt; (often shortened to &lt;code&gt;nc&lt;/code&gt;) opens a raw TCP or UDP connection to any host and port, and lets you interact directly with the service by typing text on the keyboard. We simply run:&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;nc saturn.picoctf.net 54321
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As soon as the connection is established, the remote service usually sends a welcome message (the "banner") that appears immediately in the terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Read the service's instructions
&lt;/h2&gt;

&lt;p&gt;Once connected, the service explains what it expects in return. On this challenge, the banner looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Welcome to netcat101!
If you can read this, you already know how to connect.
Type FLAG_PLEASE and press enter to get your flag.
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message is explicit: just type the requested command, exactly as shown, to receive the flag in return.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Respond correctly
&lt;/h2&gt;

&lt;p&gt;We type the exact string requested by the service, then confirm with Enter:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Netcat directly forwards every line typed on the keyboard to the remote service over the open TCP connection — no need to format anything, whatever is typed (followed by Enter) is sent as-is over the network.&lt;/p&gt;

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

&lt;p&gt;The service immediately responds with the flag 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;Here's the complete session, from start to finish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nc saturn.picoctf.net 54321
Welcome to netcat101!
If you can read this, you already know how to connect.
Type FLAG_PLEASE and press enter to get your flag.
&amp;gt; FLAG_PLEASE
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;A short challenge, but one that lays an absolutely essential foundation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;netcat&lt;/code&gt; is often nicknamed the "Swiss Army knife of networking" — it lets you manually connect to any TCP/UDP service&lt;/li&gt;
&lt;li&gt;Knowing how to read a banner and respond to it by hand is essential before scripting anything with tools like &lt;code&gt;pwntools&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The vast majority of pwn and network CTF challenges start with a simple &lt;code&gt;nc host port&lt;/code&gt; command — mastering this basic step saves precious time on everything that follows&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-nice-netcat-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>OverTheWire Bandit Level 7 Writeup — Find a Word Next to Another Word with grep</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Fri, 23 Oct 2026 19:04:56 +0000</pubDate>
      <link>https://dev.to/ctfdojo/overthewire-bandit-level-7-writeup-find-a-word-next-to-another-word-with-grep-32h9</link>
      <guid>https://dev.to/ctfdojo/overthewire-bandit-level-7-writeup-find-a-word-next-to-another-word-with-grep-32h9</guid>
      <description>&lt;p&gt;&lt;em&gt;The file &lt;code&gt;data.txt&lt;/code&gt; is large, but the password sits on the one line containing the word "millionth" — &lt;code&gt;grep&lt;/code&gt; finds it directly, no need to read the file.&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; 7 → 8&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; grep for a known marker word&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Level 7 gives a large text file and tells you the password sits next to a specific word on one of its lines — rather than scrolling through thousands of lines, search for that word directly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Level Goal: The password for the next level is stored in the file data.txt next to the word 'millionth'."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1 — Check the file's size
&lt;/h2&gt;

&lt;p&gt;Log in as bandit7 and see what you're dealing with:&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;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; data.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(several thousand lines)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — grep for the marker word
&lt;/h2&gt;

&lt;p&gt;Rather than opening a file this large in an editor, search for the exact word the level description gave you:&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;grep &lt;/span&gt;millionth data.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;millionth   [PASSWORD FOR bandit8]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Read off the value next to it
&lt;/h2&gt;

&lt;p&gt;grep prints the whole matching line — the password is the second field on that line, separated from the word "millionth" by whitespace.&lt;/p&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;When a challenge tells you a distinctive word or string to look for, that's a direct instruction to &lt;code&gt;grep&lt;/code&gt; for it rather than search manually — the whole point of a large-file level like this is testing whether you reach for the right tool instead of scrolling.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;grep word file&lt;/code&gt; prints every line in &lt;code&gt;file&lt;/code&gt; containing &lt;code&gt;word&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A large file is a cue to search, not to open in an editor&lt;/li&gt;
&lt;li&gt;The matching line's other fields (here, the password) come along with the match for free&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/overthewire-bandit-level7-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 Crackme-py Writeup — Reverse a Python Password Check</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Thu, 22 Oct 2026 20:39:17 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-crackme-py-writeup-reverse-a-python-password-check-4ie0</link>
      <guid>https://dev.to/ctfdojo/picoctf-crackme-py-writeup-reverse-a-python-password-check-4ie0</guid>
      <description>&lt;p&gt;&lt;em&gt;The Python script compares user input, after applying a transformation to it (base64 encoding, character shift, etc.), to a hardcoded string. Instead of guessing via brute force, we read the code to identify the transformation, then reverse it mathematically to directly recover the correct 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; Rev Eng&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; 250 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; python3source reading&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The challenge provides a script, &lt;code&gt;crackme.py&lt;/code&gt;, which asks for a password and prints a success or failure message based on the result of a comparison:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python3 crackme.py
Enter the password: test123
Wrong! Try again.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike a binary crackme that would require a disassembler, here the source code is directly readable — the goal isn't to deobfuscate assembly, but to understand the transformation logic and turn it against itself.&lt;/p&gt;

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

&lt;p&gt;We open &lt;code&gt;crackme.py&lt;/code&gt; and look directly for the line that decides success or failure — usually an &lt;code&gt;if&lt;/code&gt; comparing a function's result to a constant:&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="n"&gt;crackme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;

&lt;span class="n"&gt;EXPECTED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cGljb0NURntmNGs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# truncated, for example
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;user_input&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 the password: &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="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EXPECTED&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;Correct! Here&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s your flag.&lt;/span&gt;&lt;span class="sh"&gt;"&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;Wrong! Try again.&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 logic is clear: the user's input goes through &lt;code&gt;transform()&lt;/code&gt;, and the result is compared to the constant &lt;code&gt;EXPECTED&lt;/code&gt;, hardcoded in the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Identify the transformation
&lt;/h2&gt;

&lt;p&gt;We look at the body of &lt;code&gt;transform()&lt;/code&gt;: here it's a simple &lt;strong&gt;base64 encoding&lt;/strong&gt; applied to the input. Other variants of this challenge may use a Caesar-style character shift, an XOR with a fixed key, or a string reversal — the principle stays the same: precisely identify the operation being performed.&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;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# possible variant seen on other versions of the challenge:
# def transform(pw):
#     return ''.join(chr(ord(c) + 3) for c in pw)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key point: &lt;code&gt;base64.b64encode&lt;/code&gt; is a &lt;strong&gt;perfectly reversible&lt;/strong&gt; operation — there's an exact inverse function, &lt;code&gt;base64.b64decode&lt;/code&gt;, that loses no information. It's not a one-way hash like MD5 or SHA-256, so there's no need for brute force whatsoever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Reverse the transformation
&lt;/h2&gt;

&lt;p&gt;Since &lt;code&gt;EXPECTED&lt;/code&gt; is known (hardcoded in the file) and the transformation is reversible, we just need to apply the inverse operation directly to &lt;code&gt;EXPECTED&lt;/code&gt; to recover the expected password, without ever having to guess or test anything:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;EXPECTED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cGljb0NURntmNGs...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# full value copied from crackme.py
&lt;/span&gt;
&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EXPECTED&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# -&amp;gt; picoCTF{***} (recovered password, masked here)
&lt;/span&gt;
&lt;span class="c1"&gt;# If the transformation had been a +3 shift on each character,
# the inverse would simply be a -3 shift:
# password = ''.join(chr(ord(c) - 3) for c in EXPECTED)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a handful of lines, the expected password falls out directly — no attempts, no iteration, just the exact mathematical inverse of the operation applied by the script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Verify and grab the flag
&lt;/h2&gt;

&lt;p&gt;We rerun the original script and enter the recovered 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;python3 crackme.py
&lt;span class="go"&gt;Enter the password: picoCTF{***}
Correct! Here's your flag.
picoCTF{***}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The success message appears, confirming the transformation was correctly reversed, and the flag is revealed.&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 fundamental distinction in reverse engineering and applied cryptography:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As soon as a transformation is reversible (encoding, simple arithmetic shift), mathematically inverting the check is always faster and more reliable than brute force&lt;/li&gt;
&lt;li&gt;The real lesson of this challenge is to spot &lt;strong&gt;when&lt;/strong&gt; an operation is invertible before choosing an attack strategy — encoding ≠ one-way hash&lt;/li&gt;
&lt;li&gt;Carefully reading the source code before acting often saves hours of unnecessary brute forcing&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-crackme-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 Sleuthkit Apprentice Writeup — Recover a File from a Disk Image</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Mon, 19 Oct 2026 20:22:14 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-sleuthkit-apprentice-writeup-recover-a-file-from-a-disk-image-59im</link>
      <guid>https://dev.to/ctfdojo/picoctf-sleuthkit-apprentice-writeup-recover-a-file-from-a-disk-image-59im</guid>
      <description>&lt;p&gt;&lt;em&gt;A disk image (&lt;code&gt;.img&lt;/code&gt;) is provided. With &lt;code&gt;mmls&lt;/code&gt; we locate the partition table and the starting offset, then &lt;code&gt;fls&lt;/code&gt; lists the files in the file system (including deleted ones), and &lt;code&gt;icat&lt;/code&gt; lets us extract a file's content via its inode number — revealing the hidden 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; Forensics&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; mmlsflsicatThe Sleuth Kit&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The challenge provides a single file, &lt;code&gt;disk.img&lt;/code&gt;, with no other indication about its contents:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"We've got a suspect's disk image, but they've tried to hide something. Can you find it?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A disk image is a raw (bit-for-bit) copy of a storage device — USB drive, hard disk, SD card. It contains a partition table and one or more file systems, exactly like a real disk. To explore it without mounting it, we use the &lt;strong&gt;The Sleuth Kit (TSK)&lt;/strong&gt; tool suite, the reference toolkit for digital forensic investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Read the partition table with mmls
&lt;/h2&gt;

&lt;p&gt;The first step when facing any disk image is figuring out how it's structured: how many partitions it contains and at what offset (in sectors) each one starts. That's the job of &lt;code&gt;mmls&lt;/code&gt; (media management list):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mmls disk.img

DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

     Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000    0000002048   0000206847   0000204800   Linux (0x83)
003:  -------   0000206848   0000206847   0000000000   Unallocated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We spot a single Linux partition (type &lt;code&gt;0x83&lt;/code&gt;), starting at sector &lt;strong&gt;2048&lt;/strong&gt;. This offset is what needs to be passed to every following tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — List the files with fls
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fls&lt;/code&gt; (file listing) lists every file and directory in a file system, including those that have been &lt;strong&gt;deleted&lt;/strong&gt; but whose metadata is still present on the disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ fls -o 2048 disk.img

d/d 11:      lost+found
r/r 12:      notes.txt
r/r 13:      vacation_photo.jpg
r/r * 15:     secret.txt
d/d 16:      Documents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-o 2048&lt;/code&gt; option tells &lt;code&gt;fls&lt;/code&gt; the offset (in sectors) where the partition found with &lt;code&gt;mmls&lt;/code&gt; starts. Each line shows an entry type (&lt;code&gt;r&lt;/code&gt; for regular file, &lt;code&gt;d&lt;/code&gt; for directory), the inode number, and the file name.&lt;/p&gt;

&lt;p&gt;The asterisk &lt;code&gt;*&lt;/code&gt; next to inode 15 (&lt;code&gt;secret.txt&lt;/code&gt;) is the crucial detail: it means &lt;strong&gt;this file has been deleted&lt;/strong&gt; — its entry in the file system table has been marked as free, but its physical content probably hasn't been overwritten on disk yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Spot the suspicious file
&lt;/h2&gt;

&lt;p&gt;On such a small disk, two things immediately stand out: the name &lt;code&gt;secret.txt&lt;/code&gt; is explicit, and the fact that it's marked as deleted (&lt;code&gt;*&lt;/code&gt;) suggests someone tried to cover their tracks after writing something to it. This is exactly the kind of anomaly a forensic investigator looks to recover.&lt;/p&gt;

&lt;p&gt;We note its inode number: &lt;strong&gt;15&lt;/strong&gt;. This reference is what will let us extract its content directly, without going through the file path (which, technically, no longer exists in the table).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Extract the content with icat
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;icat&lt;/code&gt; (inode cat) displays a file's content from its inode number, reading directly from the associated data blocks — whether or not the file is still referenced in a 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="nv"&gt;$ &lt;/span&gt;icat &lt;span class="nt"&gt;-o&lt;/span&gt; 2048 disk.img 15
picoCTF&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="k"&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 deleted file's content shows up directly in the terminal — the flag had remained physically present on the disk, despite the logical deletion of its entry.&lt;/p&gt;

&lt;p&gt;Summary of the full tool chain used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Partition table -&amp;gt; find the offset&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;mmls disk.img

&lt;span class="c"&gt;# 2. List files (including deleted ones) at that offset&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;fls &lt;span class="nt"&gt;-o&lt;/span&gt; 2048 disk.img

&lt;span class="c"&gt;# 3. Extract the suspicious file's content via its inode&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;icat &lt;span class="nt"&gt;-o&lt;/span&gt; 2048 disk.img 15
&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 is an excellent introduction to the basics of disk forensics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deleting a file usually doesn't physically erase it from disk — only its entry in the file system table is marked as free&lt;/li&gt;
&lt;li&gt;As long as the space occupied by the file hasn't been overwritten by new data, tools like The Sleuth Kit can find it and recover it via its inode number&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;mmls&lt;/code&gt; → &lt;code&gt;fls&lt;/code&gt; → &lt;code&gt;icat&lt;/code&gt; chain is the go-to reflex for exploring any raw disk image without having to mount it&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-sleuthkit-apprentice-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>OverTheWire Bandit Level 6 Writeup — Search the Whole Filesystem by Owner and Size</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Fri, 16 Oct 2026 19:48:33 +0000</pubDate>
      <link>https://dev.to/ctfdojo/overthewire-bandit-level-6-writeup-search-the-whole-filesystem-by-owner-and-size-ml0</link>
      <guid>https://dev.to/ctfdojo/overthewire-bandit-level-6-writeup-search-the-whole-filesystem-by-owner-and-size-ml0</guid>
      <description>&lt;p&gt;&lt;em&gt;The password file lives somewhere on the whole filesystem, not in the home directory — &lt;code&gt;find /&lt;/code&gt; filtered by owning user, owning group, and exact size locates it, with stderr suppressed to hide permission-denied noise.&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; 6 → 7&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; filesystem-wide find with -user/-group/-size&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Unlike every previous level, this one doesn't point at a specific directory at all — the password file could be anywhere on the server. The level description instead gives three properties to search the entire filesystem for.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Level Goal: The password for the next level is stored somewhere on the server and has all of the following properties: owned by user bandit7, owned by group bandit6, 33 bytes in size."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1 — Search from the filesystem root
&lt;/h2&gt;

&lt;p&gt;Start the search at &lt;code&gt;/&lt;/code&gt; instead of the home directory, since the file could be anywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find / &lt;span class="nt"&gt;-user&lt;/span&gt; bandit7 &lt;span class="nt"&gt;-group&lt;/span&gt; bandit6 &lt;span class="nt"&gt;-size&lt;/span&gt; 33c
&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;find: '/etc/ssl/private': Permission denied
find: '/var/lib/...': Permission denied
...
/var/lib/dpkg/info/bandit7.password
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Suppress the permission errors
&lt;/h2&gt;

&lt;p&gt;Most of the filesystem isn't readable by bandit6, so &lt;code&gt;find&lt;/code&gt; prints a permission-denied line for every directory it can't enter. Redirect stderr to discard them and leave only the real result (and any other stdout matches) visible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find / &lt;span class="nt"&gt;-user&lt;/span&gt; bandit7 &lt;span class="nt"&gt;-group&lt;/span&gt; bandit6 &lt;span class="nt"&gt;-size&lt;/span&gt; 33c 2&amp;gt;/dev/null
&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;/var/lib/dpkg/info/bandit7.password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Read it
&lt;/h2&gt;

&lt;p&gt;The matched path is readable despite living outside bandit6's home directory — dpkg (the Debian package manager) stores small per-package metadata files like this one, and this particular file's ownership happens to satisfy the puzzle's conditions:&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; /var/lib/dpkg/info/bandit7.password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[PASSWORD FOR bandit7]
&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;A search scoped to the entire filesystem is noisy by default — most of it you don't have permission to even look inside. &lt;code&gt;2&amp;gt;/dev/null&lt;/code&gt; is the standard way to keep a command's real output while throwing away the error spam, and it's worth reaching for any time you deliberately search somewhere you only have partial access.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;find /&lt;/code&gt; searches starting from the filesystem root, not just the current directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-user&lt;/code&gt; and &lt;code&gt;-group&lt;/code&gt; filter by file ownership, independent of each other&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&amp;gt;/dev/null&lt;/code&gt; redirects stderr (file descriptor 2) to discard it, leaving stdout untouched&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/overthewire-bandit-level6-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 Vigenere Writeup — Recover a Vigenère Key with Crib Dragging</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Thu, 15 Oct 2026 20:08:58 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-vigenere-writeup-recover-a-vigenere-key-with-crib-dragging-961</link>
      <guid>https://dev.to/ctfdojo/picoctf-vigenere-writeup-recover-a-vigenere-key-with-crib-dragging-961</guid>
      <description>&lt;p&gt;&lt;em&gt;The Vigenère cipher shifts each letter of the plaintext according to a corresponding letter from a key that repeats cyclically. Since we know the plaintext starts with &lt;code&gt;picoCTF{&lt;/code&gt;, we can "subtract" this known text from the ciphertext to directly recover the first letters of the key, then deduce the rest by repetition.&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; Crypto&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; python3crib dragging&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The challenge provides a text file containing an encrypted message, along with a prompt that explicitly mentions the &lt;strong&gt;Vigenère cipher&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"This flag was made with a repeating key XOR... wait, no. This is Vigenère. Can you recover the key and the flag?"&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;$ cat ciphertext.txt
wgcpbtqz{***************}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing more. No key provided, no additional hint — just the ciphertext. But we already know something very valuable: &lt;strong&gt;every picoCTF flag starts with &lt;code&gt;picoCTF{&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Quick refresher on the Vigenère cipher
&lt;/h2&gt;

&lt;p&gt;The Vigenère cipher is a generalization of the Caesar cipher: instead of applying a fixed shift to every letter, a different shift is applied at each position, determined by a &lt;strong&gt;key repeated cyclically&lt;/strong&gt; across the whole length of the message.&lt;/p&gt;

&lt;p&gt;For each plaintext letter at position &lt;code&gt;i&lt;/code&gt;, mapping A=0, B=1, ... Z=25:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cipher[i] = (plain[i] + key[i mod len(key)]) mod 26
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, if the key is &lt;code&gt;KEY&lt;/code&gt; and the plaintext starts with &lt;code&gt;ATTACK&lt;/code&gt;, the first letter &lt;code&gt;A&lt;/code&gt; (0) is shifted by &lt;code&gt;K&lt;/code&gt; (10), giving &lt;code&gt;K&lt;/code&gt;; the second letter &lt;code&gt;T&lt;/code&gt; (19) is shifted by &lt;code&gt;E&lt;/code&gt; (4), giving &lt;code&gt;X&lt;/code&gt;; and so on, with the key repeating: &lt;code&gt;K-E-Y-K-E-Y&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unlike the Caesar cipher, there aren't just 26 possible shifts to brute-force — with Vigenère, the number of possible keys explodes as the key length grows, so naive brute force is no longer enough. A different approach is needed.&lt;/p&gt;

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

&lt;p&gt;This is where &lt;strong&gt;crib dragging&lt;/strong&gt; comes in: since we know (or can guess with very high confidence) part of the plaintext — here the standard prefix &lt;code&gt;picoCTF{&lt;/code&gt; — we can invert the encryption equation to directly compute the corresponding key letters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key[i] = (cipher[i] - plain[i]) mod 26
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We apply this formula to the first 8 letters of the ciphertext, comparing them against the 8 letters of &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;# cipher : w  g  c  p  b  t  q  z
# plain  : p  i  c  o  C  T  F  {

# Note: { is not a letter, it can't be shifted with A-Z.
# In practice, picoCTF Vigenère challenges only encrypt
# A-Z letters and leave other characters (digits, {, }, _) unchanged.
# So we only work on "picoCTF" (7 letters).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;string&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ascii_lowercase&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;alpha&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;wgcpbtqz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;known_plain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;picoCTF&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# known prefix, ignoring case for the computation
&lt;/span&gt;
&lt;span class="n"&gt;key_fragment&lt;/span&gt; &lt;span class="o"&gt;=&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;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ciphertext&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;known_plain&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;shift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&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="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;
    &lt;span class="n"&gt;key_fragment&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;alpha&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="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;span class="c1"&gt;# -&amp;gt; prints the first letters of the key, e.g. "helloh"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This little script directly reveals the start of the key, letter by letter, without having to guess anything — it's pure algebra on alphabetic positions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Finding the key's period
&lt;/h2&gt;

&lt;p&gt;Once we've recovered the first letters of the key using the known prefix, we still need to determine whether they already form a complete repeating pattern, or whether the key is longer than what we've been able to deduce so far.&lt;/p&gt;

&lt;p&gt;Looking at the output of the previous script, we often notice that after a small number of characters (5 to 8 letters typically on this challenge), the pattern starts repeating — or, if the resulting string looks like a readable English word (like &lt;code&gt;hello&lt;/code&gt;), that's a very good sign the full key has been found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;key_fragment = "hellohello..."
# the "hello" pattern (5 letters) repeats -&amp;gt; the key is "hello"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To confirm, we can also decrypt the entire message with this key candidate and check that the result produces readable text throughout its full length, not just on the prefix.&lt;/p&gt;

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

&lt;p&gt;Once the complete key is identified, we apply the decryption formula to the whole message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plain[i] = (cipher[i] - key[i mod len(key)]) mod 26
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;string&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ascii_lowercase&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;vigenere_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;key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;plaintext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;ki&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;ciphertext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isalpha&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alpha&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;islower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;shift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ki&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&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="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="n"&gt;plaintext&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;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;ki&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# digits, braces, underscore: left unchanged, don't consume a key letter
&lt;/span&gt;            &lt;span class="n"&gt;plaintext&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;plaintext&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;wgcpbtqz{***************}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# flag body masked here
&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&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;vigenere_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;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# -&amp;gt; picoCTF{***************} (digits/underscores/braces
#    pass through Vigenère unchanged, so the masked body
#    stays identical before and after decryption)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script walks through the message character by character, applies the inverse shift only to letters, and leaves non-alphabetic characters (digits, &lt;code&gt;{&lt;/code&gt;, &lt;code&gt;}&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt;) unchanged without consuming a key letter — exactly as the original picoCTF encryption does.&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 fundamental structural weakness of repeating-key ciphers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Just like with repeating-key XOR, knowing even a small fragment of the plaintext (here the standard picoCTF flag prefix) is enough to completely break a repeating-key cipher&lt;/li&gt;
&lt;li&gt;Crib dragging turns a cryptanalysis problem into simple modular algebra as soon as a fragment of plaintext is known or guessable&lt;/li&gt;
&lt;li&gt;This is precisely why modern ciphers absolutely avoid key reuse on texts whose beginning is predictable&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-vigenere-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>OverTheWire Bandit Level 5 Writeup — Find a File by Size, Type, and Permissions</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Wed, 14 Oct 2026 20:14:43 +0000</pubDate>
      <link>https://dev.to/ctfdojo/overthewire-bandit-level-5-writeup-find-a-file-by-size-type-and-permissions-297o</link>
      <guid>https://dev.to/ctfdojo/overthewire-bandit-level-5-writeup-find-a-file-by-size-type-and-permissions-297o</guid>
      <description>&lt;p&gt;&lt;em&gt;The password file inside &lt;code&gt;inhere&lt;/code&gt; is the only one that is human-readable, exactly 1033 bytes, and not executable — &lt;code&gt;find&lt;/code&gt; filters for all three at once.&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; 5 → 6&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; find with combined size/type/permission filters&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Level 5's home directory contains a directory called &lt;code&gt;inhere&lt;/code&gt;, which itself contains many subdirectories, each full of decoy files. Manually opening every one isn't realistic — the level gives three exact properties the real file satisfies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Level Goal: The password for the next level is stored in the file data.txt, which is located in one of the many, many directories that live at /home/bandit5/inhere. The file is: human-readable, 1033 bytes in size, not executable."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1 — See the scale of the problem
&lt;/h2&gt;

&lt;p&gt;Log in as bandit5 and look at what's inside &lt;code&gt;inhere&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="nb"&gt;cd &lt;/span&gt;inhere
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&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;(a large number — dozens of nested directories, each holding several decoy files)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Filter by size
&lt;/h2&gt;

&lt;p&gt;The level gave an exact byte count — use it directly rather than opening files one by one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-size&lt;/span&gt; 1033c
&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;./maybehere07/.file2
(and possibly a few other candidates)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Add the type and permission filters
&lt;/h2&gt;

&lt;p&gt;Combine all three stated properties — regular file, that exact size, and not executable — into a single &lt;code&gt;find&lt;/code&gt; command to narrow to exactly one result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-size&lt;/span&gt; 1033c &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-executable&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;./maybehere07/.file2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4 — Read it
&lt;/h2&gt;

&lt;p&gt;Cat the single remaining candidate:&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; ./maybehere07/.file2
&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 bandit6]
&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;When a task description gives you specific, checkable properties instead of a name or location, that's a hint to filter for them directly rather than browse manually. &lt;code&gt;find&lt;/code&gt;'s flags compose — stacking &lt;code&gt;-size&lt;/code&gt;, &lt;code&gt;-type&lt;/code&gt;, and a permission test narrows a huge haystack down to one file in a single command.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;find . -size 1033c&lt;/code&gt; matches files of an exact size, in bytes (the trailing &lt;code&gt;c&lt;/code&gt; means bytes)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;! -executable&lt;/code&gt; negates a test — here, "not executable"&lt;/li&gt;
&lt;li&gt;Stacking multiple &lt;code&gt;find&lt;/code&gt; conditions together narrows results fast, without opening a single file by hand&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/overthewire-bandit-level5-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 Mind Your Ps and Qs Writeup — Factor a Weak RSA Modulus</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Tue, 13 Oct 2026 20:58:34 +0000</pubDate>
      <link>https://dev.to/ctfdojo/picoctf-mind-your-ps-and-qs-writeup-factor-a-weak-rsa-modulus-38bo</link>
      <guid>https://dev.to/ctfdojo/picoctf-mind-your-ps-and-qs-writeup-factor-a-weak-rsa-modulus-38bo</guid>
      <description>&lt;p&gt;&lt;em&gt;The challenge provides an RSA modulus &lt;code&gt;n&lt;/code&gt;, a public exponent &lt;code&gt;e&lt;/code&gt;, and a ciphertext &lt;code&gt;c&lt;/code&gt;. Since &lt;code&gt;n&lt;/code&gt; is deliberately chosen small (and therefore quickly factorable), we recover its two prime factors &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt;, which lets us reconstruct the private key and decrypt the message.&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; 300 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; RSA, factoring a weak modulus&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The challenge provides a file containing three values, typical of RSA encryption:&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;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;29331922499794355004599187762061431232780269081884253328223729305650814599790577023974667132916119346443898172680590639942440746452251177424957501577335850012478&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;65537&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22096451867410381776306561134883418017410069787050966603363725310937568517600847513921546455407864126394183248424695972200310248528204322581018327246672712340281&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values are typically provided in decimal or hexadecimal, sometimes alongside a &lt;code&gt;values.py&lt;/code&gt; or &lt;code&gt;pubkey.pem&lt;/code&gt; file. The goal is to recover the plaintext message, which contains the flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — RSA refresher
&lt;/h2&gt;

&lt;p&gt;RSA relies on a modulus &lt;code&gt;n = p × q&lt;/code&gt;, the product of two large prime numbers kept secret. The system's security depends entirely on the difficulty of recovering &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; from &lt;code&gt;n&lt;/code&gt; alone — a problem called integer factoring, considered hard for sufficiently large numbers (2048 bits or more in production).&lt;/p&gt;

&lt;p&gt;But if &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; are chosen too small, factoring becomes trivial for a modern computer, and the entire cryptographic edifice collapses: anyone can recover the private key from the public key alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Factor n
&lt;/h2&gt;

&lt;p&gt;We use Python's &lt;code&gt;sympy&lt;/code&gt; library, which provides a ready-to-use factoring function. On a deliberately small &lt;code&gt;n&lt;/code&gt; (around 150-200 bits here, far from the recommended 2048 bits), factoring takes only a few seconds:&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;sympy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;factorint&lt;/span&gt;

&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;29331922499794355004599187762061431232780269081884253328223729305650814599790577023974667132916119346443898172680590639942440746452251177424957501577335850012478&lt;/span&gt;

&lt;span class="n"&gt;factors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factorint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&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;factors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# {154909844708812734120534792101 * 189329039205892345689032156201: 1}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We obtain two prime factors &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; such that &lt;code&gt;p × q = n&lt;/code&gt;. (Alternatively, for an &lt;code&gt;n&lt;/code&gt; already known publicly, a service like &lt;a href="http://factordb.com" rel="noopener noreferrer"&gt;factordb.com&lt;/a&gt; can sometimes give the factoring directly, without even having to compute it yourself.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Reconstruct the private key
&lt;/h2&gt;

&lt;p&gt;Once &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; are known, we can reconstruct everything needed to decrypt:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compute Euler's totient: &lt;code&gt;φ(n) = (p-1)(q-1)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Compute the private exponent &lt;code&gt;d&lt;/code&gt;, the modular inverse of &lt;code&gt;e&lt;/code&gt; modulo &lt;code&gt;φ(n)&lt;/code&gt;: &lt;code&gt;d = e⁻¹ mod φ(n)&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;154909844708812734120534792101&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;189329039205892345689032156201&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;65537&lt;/span&gt;

&lt;span class="n"&gt;phi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# modular inverse (Python 3.8+)
&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;d =&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;RSA decryption is simply raising the ciphertext &lt;code&gt;c&lt;/code&gt; to the power &lt;code&gt;d&lt;/code&gt;, modulo &lt;code&gt;n&lt;/code&gt;: &lt;code&gt;m = c^d mod n&lt;/code&gt;. The result is a large integer that then needs to be converted back into bytes, then 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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sympy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;factorint&lt;/span&gt;

&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;29331922499794355004599187762061431232780269081884253328223729305650814599790577023974667132916119346443898172680590639942440746452251177424957501577335850012478&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;65537&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22096451867410381776306561134883418017410069787050966603363725310937568517600847513921546455407864126394183248424695972200310248528204322581018327246672712340281&lt;/span&gt;

&lt;span class="c1"&gt;# 1. factoring
&lt;/span&gt;&lt;span class="n"&gt;factors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;factorint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factors&lt;/span&gt;

&lt;span class="c1"&gt;# 2. reconstruct the private key
&lt;/span&gt;&lt;span class="n"&gt;phi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. decryption
&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pow&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="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 4. convert integer -&amp;gt; bytes -&amp;gt; text
&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;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_bytes&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bit_length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;byteorder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;big&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="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;$ python3 solve.py
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;RSA's security rests entirely on the difficulty of factoring very large prime numbers — in practice, keys of 2048 bits or more.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As soon as the factors &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; are small enough to be factored in a reasonable time, the entire system collapses&lt;/li&gt;
&lt;li&gt;Other classic key-generation flaws lead to the same result: &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; too close to each other (Fermat's attack), or reusing the same prime factor across several different keys (computing the GCD between several moduli)&lt;/li&gt;
&lt;li&gt;In practice, key size isn't enough on its own: it's the quality of the random prime generation that guarantees the system's real-world security&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/picoctf-mind-your-ps-and-qs-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>OverTheWire Bandit Level 4 Writeup — Spot the Real Text File Among Junk</title>
      <dc:creator>CTFDojo</dc:creator>
      <pubDate>Fri, 09 Oct 2026 20:34:19 +0000</pubDate>
      <link>https://dev.to/ctfdojo/overthewire-bandit-level-4-writeup-spot-the-real-text-file-among-junk-58of</link>
      <guid>https://dev.to/ctfdojo/overthewire-bandit-level-4-writeup-spot-the-real-text-file-among-junk-58of</guid>
      <description>&lt;p&gt;&lt;em&gt;The &lt;code&gt;inhere&lt;/code&gt; directory holds several similarly-named files, only one of which is actual human-readable text. The &lt;code&gt;file&lt;/code&gt; command identifies each file's real type without opening it.&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; 4 → 5&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; File type identification&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Level 4's &lt;code&gt;inhere&lt;/code&gt; directory contains around ten files with near-identical names (like &lt;code&gt;-file00&lt;/code&gt; through &lt;code&gt;-file09&lt;/code&gt;). Only one is a plain text file — the rest are decoys with random binary data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Level Goal: The password for the next level is stored in the only human-readable file in the inhere directory. Tip: if your terminal is messed up, try the reset command."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1 — List the decoy files
&lt;/h2&gt;

&lt;p&gt;Log in as bandit4, move into &lt;code&gt;inhere&lt;/code&gt;, and see what's there:&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;cd &lt;/span&gt;inhere
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&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;-file00  -file01  -file02  -file03  -file04
-file05  -file06  -file07  -file08  -file09
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Identify each file's real type
&lt;/h2&gt;

&lt;p&gt;Rather than opening every file by hand, ask the &lt;code&gt;file&lt;/code&gt; command what each one actually contains — it inspects the content, not just the name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file ./&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./-file00: data
./-file01: data
./-file02: data
...
./-file07: ASCII text
./-file08: data
./-file09: data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Only one file is reported as ASCII text — that's the password. The filenames start with a dash, so use the same &lt;code&gt;./&lt;/code&gt; fix from Level 1:&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; ./-file07
&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 bandit5]
&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;A file's extension or name tells you nothing reliable about its actual content — &lt;code&gt;file&lt;/code&gt; reads the real bytes and reports the true type. It's one of the fastest ways to triage a pile of unknown files before deciding which ones are worth opening.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;file&lt;/code&gt; inspects a file's actual content, not its name or extension&lt;/li&gt;
&lt;li&gt;A wildcard like &lt;code&gt;./*&lt;/code&gt; runs it against every file in the directory at once&lt;/li&gt;
&lt;li&gt;The dash-prefixed filenames here need the same &lt;code&gt;./&lt;/code&gt; fix as Level 1&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ctfdojo.com/writeups/overthewire-bandit-level4-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>
  </channel>
</rss>
