<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>SANS Top 25 on guy@secdev.uk</title>
    <link>https://www.secdev.uk/blog/tags/sans-top-25/</link>
    <description>Recent content in SANS Top 25 on guy@secdev.uk</description>
    <generator>Hugo</generator>
    <language>en-gb</language>
    <copyright>Guy Dixon | guy@secdev.uk</copyright>
    <lastBuildDate>Sat, 16 Aug 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.secdev.uk/blog/tags/sans-top-25/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Race Conditions</title>
      <link>https://www.secdev.uk/blog/technology/2025-08-16-race-conditions/</link>
      <pubDate>Sat, 16 Aug 2025 00:00:00 +0000</pubDate>
      <guid>https://www.secdev.uk/blog/technology/2025-08-16-race-conditions/</guid>
      <description>&lt;p&gt;Race conditions (CWE-362) are, in my opinion, the most insidious class of security bugs you&amp;rsquo;ll encounter. They occur when the behaviour of a program depends on the relative timing of concurrent operations, and at least one of those operations modifies shared state. The window between a check and a subsequent use of the checked value, the classic time-of-check to time-of-use (TOCTOU) pattern, is the most exploited form, but races also show up in counter increments, balance updates, session management, and file operations. What makes race conditions uniquely dangerous is their non-determinism: the bug may not manifest in thousands of test runs, then appear under production load when two requests arrive within microseconds of each other. I want to walk through race conditions in Python, Go, Java, and Rust, from the obvious unprotected counter to the subtle channel-based ordering assumption that passes every test but fails under contention.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Integer Overflow</title>
      <link>https://www.secdev.uk/blog/technology/2025-08-02-integer-overflow/</link>
      <pubDate>Sat, 02 Aug 2025 00:00:00 +0000</pubDate>
      <guid>https://www.secdev.uk/blog/technology/2025-08-02-integer-overflow/</guid>
      <description>&lt;p&gt;Integer overflow (CWE-190) is one of those bugs that I find endlessly fascinating because of how quietly destructive it is. It happens when an arithmetic operation produces a value that exceeds the maximum (or falls below the minimum) representable value for the integer type. In C and C++, signed integer overflow is undefined behaviour, the compiler is free to assume it never happens, and optimizations built on that assumption can eliminate bounds checks entirely. Unsigned overflow wraps around silently. Go and Java define overflow as wrapping (two&amp;rsquo;s complement), which prevents undefined behaviour but still produces incorrect results that lead to security vulnerabilities: undersized allocations, bypassed length checks, and negative indices into arrays. Rust panics on overflow in debug mode but wraps in release mode by default, creating a gap between testing and production behaviour that caught me off guard when I first started digging into Rust&amp;rsquo;s safety guarantees. I want to walk through integer overflow across C, C++, Rust, Go, and Java, from the textbook multiplication overflow to the subtle cast truncation that can survive expert review.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Null Pointer Dereference</title>
      <link>https://www.secdev.uk/blog/technology/2025-07-19-null-pointer-dereference/</link>
      <pubDate>Sat, 19 Jul 2025 00:00:00 +0000</pubDate>
      <guid>https://www.secdev.uk/blog/technology/2025-07-19-null-pointer-dereference/</guid>
      <description>&lt;p&gt;Null pointer dereference (CWE-476) is one of those bugs that shows up across every language, and the more I researched it for this post, the more I was struck by how much damage it can do depending on context. The consequences vary dramatically: C programs crash with a segfault (or worse, the kernel maps page zero and an attacker gets code execution), C++ invokes undefined behaviour that the compiler may optimise into literally anything, Go panics with a nil pointer dereference that kills the goroutine or the whole program, and Java throws a &lt;code&gt;NullPointerException&lt;/code&gt; that can crash the app or leak stack traces to an attacker. MITRE ranks CWE-476 consistently in the top 25 most dangerous software weaknesses, and digging into the CVE data, that ranking is well deserved. I want to walk through C, C++, Go, and Java here, from the obvious unchecked &lt;code&gt;malloc&lt;/code&gt; return to the subtle nil interface trap in Go and the conditional path where null silently propagates through multiple function calls.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Use After Free</title>
      <link>https://www.secdev.uk/blog/technology/2025-07-05-use-after-free/</link>
      <pubDate>Sat, 05 Jul 2025 00:00:00 +0000</pubDate>
      <guid>https://www.secdev.uk/blog/technology/2025-07-05-use-after-free/</guid>
      <description>&lt;p&gt;Use-after-free (CWE-416) is one of those bug classes that I wanted to understand deeply because it keeps showing up at the root of high-profile exploits. It occurs when a program continues to use a pointer after the memory it references has been freed. The freed memory may be reallocated for a different purpose, and the dangling pointer now reads or writes data that belongs to a completely different object. Attackers exploit this by controlling what gets allocated into the freed slot, replacing a data buffer with a crafted object that contains a function pointer, then triggering the dangling pointer to call through it. Reading through CVE databases, use-after-free is at the root of hundreds of browser exploits, kernel privilege escalations, and server compromises. This post covers C and C++, from the obvious free-then-use to the subtle shared-pointer aliasing and callback registration patterns that can evade expert review.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Out-of-Bounds Writes</title>
      <link>https://www.secdev.uk/blog/technology/2025-06-21-out-of-bounds-writes/</link>
      <pubDate>Sat, 21 Jun 2025 00:00:00 +0000</pubDate>
      <guid>https://www.secdev.uk/blog/technology/2025-06-21-out-of-bounds-writes/</guid>
      <description>&lt;p&gt;Out-of-bounds writes (CWE-787) are the single most dangerous class of memory corruption vulnerabilities on the SANS/CWE Top 25, and they&amp;rsquo;ve held that position for years. The reason is clear once you dig into the mechanics: writing past the end of a buffer can overwrite return addresses, function pointers, vtable entries, and adjacent heap metadata, giving attackers arbitrary code execution. Unlike higher-level languages where the runtime catches array index violations, C and C++ silently corrupt memory, and the consequences may not manifest until thousands of instructions later. Even Rust, with its ownership model, is vulnerable when &lt;code&gt;unsafe&lt;/code&gt; blocks bypass the borrow checker. In this post I&amp;rsquo;ll dissect out-of-bounds writes in C, C++, and Rust, from the classic &lt;code&gt;strcpy&lt;/code&gt; overflow to the subtle off-by-one in pointer arithmetic that can survive expert review.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
