Jan 28, 2026·6 min read·23 visits
The Soroban SDK used bare arithmetic to calculate slice indices. In Rust release builds (default for many), `u32::MAX + 1` wraps to `0`. This turns a request for 'all data' into a request for 'no data' silently. It affects data slicing and random number generation.
A critical integer overflow vulnerability in the Rust `soroban-sdk` allows smart contracts to silently process incorrect data ranges or generate wild random numbers. By exploiting standard arithmetic wrapping behaviors in Rust's release mode, attackers can force contracts to slice empty buffers instead of full datasets, or bypass PRNG bounds, leading to severe logic corruption without triggering a transaction revert.
We love Rust. We love it for the borrow checker, the type safety, and the smug sense of superiority we get when telling C++ developers that their memory leaks are "so 1990s." But Rust has a dirty little secret that bites hard when you leave the cozy embrace of debug builds: integer overflow.
In the world of blockchain, specifically Stellar's Soroban smart contract platform, the rs-soroban-sdk is the bridge between your contract logic and the host environment. It handles the nitty-gritty of memory, types, and host calls. But in CVE-2026-24889, that bridge turned out to be made of balsa wood painted to look like steel.
The vulnerability isn't some complex heap-spraying technique or a ROP chain. It's 3rd-grade math. The SDK was performing manual arithmetic on range bounds—calculating start and end indices for slices—using standard operators (+ and -). In the high-stakes, optimized world of Wasm smart contracts, this arithmetic didn't panic when it hit the integer limit. It rolled over like an odometer on a '95 Honda Civic, turning MAX into 0. And in a smart contract, 0 is rarely the number you want to see when you asked for EVERYTHING.
To understand this bug, you have to understand how Rust compiles. In dev (debug) profiles, if you try to add 1 to u32::MAX, the program panics. It screams, stops execution, and saves you from yourself. This is great. In Soroban terms, a panic aborts the transaction, reverting all state changes. No harm, no foul.
However, smart contracts need to be small and fast. They are compiled in release mode. By default, unless you explicitly set overflow-checks = true in your Cargo.toml, Rust disables overflow checks for performance. u32::MAX + 1 becomes 0. No panic. No abort. The code just keeps executing with the wrong number.
The SDK implements Bytes::slice and Vec::slice. These functions take a RangeBounds argument (like 0..=len). To communicate with the Soroban host environment (which expects exclusive end indices), the SDK has to normalize these bounds. If you pass an inclusive range like 0..=u32::MAX, the SDK calculates the end index as end + 1.
Do you see the train wreck coming? 4,294,967,295 + 1 wraps to 0. The SDK then tells the host: "Give me a slice starting at 0 and ending at 0." The host dutifully returns an empty byte array. The contract thinks it processed the data, but it processed nothing.
Let's look at the actual code responsible for this silent failure. The vulnerability existed in the slice method's bound calculation logic. It looked something like this:
// Vulnerable logic in SDK < 22.0.9
let end_bound = match range.end_bound() {
Bound::Included(s) => *s + 1, // <--- HERE IS THE BUG
Bound::Excluded(s) => *s,
Bound::Unbounded => len,
};It looks innocent. It's logically correct in a world with infinite integers. But we live in a 32-bit (or 64-bit) world. When s is u32::MAX, *s + 1 wraps.
The fix provided by the Stellar team is the textbook definition of "defensive programming." They replaced the bare arithmetic with checked operations and—crucially—an explicit trap.
// The Fix
Bound::Included(s) => s.checked_add(1)
.expect_optimized("attempt to add with overflow"),Note the use of .expect_optimized(). In the Soroban SDK, this isn't just a standard unwrap. It maps to wasm32::unreachable(), which is a specialized instruction that traps the Wasm execution immediately. This ensures that even if the developer still has overflow checks disabled in their project config, the SDK will force a crash (transaction abort) rather than allowing the wraparound.
How do we weaponize this? There are two primary vectors: Data Slicing and Random Number Generation (PRNG).
Vector 1: The Empty Slice Trick
Imagine a governance contract that iterates through a list of votes stored in a Bytes object to count "Yes" votes. The function takes a range to allow batch processing.
fn count_votes(votes: Bytes, range: RangeInclusive<u32>) -> u32 {
let batch = votes.slice(range); // Vulnerable slice call
// ... iterates over batch ...
}An attacker calls this with 0..=u32::MAX. The SDK wraps the length calculation to 0. The batch variable becomes an empty slice. The loop runs zero times. The function returns 0 "Yes" votes. If this logic was used to validate a quorum, you might have just bricked the proposal system or bypassed a check that requires "at least X votes checked."
Vector 2: The Chaos PRNG
The Prng::gen_range function had the opposite problem. To handle Bound::Excluded(end), it calculated end - 1.
If you call prng.gen_range(0..0), the code sees an excluded bound of 0. It calculates 0 - 1. In unsigned arithmetic, this wraps backwards to u64::MAX. Suddenly, a call that was supposed to generate a number in a tight range (or fail) is generating numbers across the entire 64-bit integer space. This is catastrophic for gaming contracts or lotteries relying on specific probability distributions.
The terrifying part of this vulnerability is the silence. In the blockchain world, we rely on the principle that "if it compiles and runs without error, the state transition is valid."
CVE-2026-24889 breaks that contract. It allows a transaction to complete successfully (SUCCESS status) while performing operations that are logically impossible (like slicing a 4GB array into 0 bytes when you asked for the whole thing). This leads to:
This affects any contract compiled with default release profiles using the affected SDK versions. It is a fundamental flaw in the foundational library of the ecosystem.
If you are a Soroban developer, stop what you are doing. Check your Cargo.toml. If you are using rs-soroban-sdk versions < 22.0.9, 23.0.x < 23.5.1, or 25.0.x < 25.0.2, you are sitting on a time bomb.
Remediation Steps:
cargo update -p soroban-sdk. The patched versions (22.0.9, 23.5.1, 25.0.2) contain the checked_add/sub logic that forces a panic.[profile.release]
overflow-checks = trueThis is like wearing a seatbelt and having airbags. The SDK patch is the airbag; the config change is the seatbelt. Use both.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
rs-soroban-sdk Stellar | < 22.0.9 | 22.0.9 |
rs-soroban-sdk Stellar | >= 23.0.0, < 23.5.1 | 23.5.1 |
rs-soroban-sdk Stellar | >= 25.0.0, < 25.0.2 | 25.0.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-190 |
| Attack Vector | Network |
| CVSS | 5.3 (Medium) |
| Impact | Integrity Loss / Logic Bypass |
| Exploit Status | PoC Available (Implicit) |
| Language | Rust / Wasm |
Integer Overflow or Wraparound
Januscape (CVE-2026-53359) is a critical Use-After-Free vulnerability in the x86 Shadow MMU component of the Linux Kernel's KVM subsystem. A logic error in shadow page tracking permits unauthorized page reuse without validating architectural execution roles, leading to dangling pointers in reverse mapping (rmap) tracking entries during guest memory teardown.
CVE-2026-48282 is a critical unauthenticated path traversal and arbitrary file write vulnerability in the Remote Development Services (RDS) component of Adobe ColdFusion. The vulnerability allows a remote, unauthenticated attacker to bypass directory boundaries and write arbitrary files, including CFML-based web shells, onto the host server. This flaw is actively exploited in the wild and enables full unauthenticated remote code execution under the privileges of the ColdFusion service account.
An information disclosure vulnerability exists in the web-auth/webauthn-lib PHP library when using the default SimpleFakeCredentialGenerator without a configured secret. This allows unauthenticated remote attackers to determine if a username exists on the target application.
A Stored and Reflected Cross-Site Scripting (XSS) vulnerability was identified in the Rust web service library 'rama' prior to version 0.3.0-rc.1. When serving directories using DirectoryServeMode::HtmlFileList, the library improperly escapes directory names, filenames, and request path components before injecting them into dynamically generated HTML files. This allows attackers to execute malicious scripts inside user browser sessions.
The ha-mcp add-on for Home Assistant exposes its settings and security policy routes without authentication at the bare root path of TCP port 9583. This exposure allows unauthorized adjacent network clients to reconfigure tools, alter policies, and bypass human-in-the-loop approval gates. The vulnerability has been addressed in development build 7.6.0.dev393 and subsequent releases by restricting access to root-mounted routes exclusively to the Supervisor Ingress IP.
An authentication freshness bypass vulnerability exists in the WebAuthn re-authentication path of Flask-Security-Too versions 5.8.0 and 5.8.1. The flaw allows an authenticated attacker to elevate the freshness status of a victim session using their own WebAuthn credential, bypassing re-authentication constraints.