Jan 28, 2026·6 min read·24 visits
The `soroban-fixed-point-math` library for Stellar smart contracts failed to handle 'double negative' division correctly and lacked overflow checks when casting `i128` down to `i64`. This allows attackers to trigger incorrect rounding or wrap negative values into positive ones.
A critical logic error in the `soroban-fixed-point-math` library allows for incorrect rounding and integer overflows in signed arithmetic operations. This vulnerability affects Stellar Soroban smart contracts, potentially turning massive deficits into massive gains via narrowing cast errors.
In the unforgiving world of blockchain development, floating-point numbers are contraband. They are non-deterministic, messy, and banned from most virtual machines, including Stellar's Soroban. To perform financial math—calculating interest rates, slippage, or token shares—developers rely on fixed-point arithmetic libraries. These libraries are the bedrock of DeFi; if they crack, the whole building comes down.
Enter soroban-fixed-point-math, a popular Rust crate used to handle these precise calculations on the Stellar network. It promises safe, high-precision math for i64 and i128 types. But in versions 1.3.0 and 1.4.0, that promise was broken.
CVE-2026-24783 isn't just a rounding error; it's a fundamental misunderstanding of signed integers that could allow a smart contract to confuse a massive debt for a massive surplus. It represents the classic 'silent killer' of smart contracts: logic that looks right at a glance but falls apart under specific, mathematically valid edge cases.
The vulnerability stems from two distinct but equally embarrassing failures in the library's mulDiv logic. The first is a failure of basic algebra. When performing division, the library needed to determine if the result was negative to apply the correct 'floor' or 'ceil' rounding logic. The developers wrote a check that looked essentially like this: if product < 0.
Here is the problem: If the product (numerator) is negative and the divisor is also negative, the result should be positive. However, the code saw the negative product, panicked, and applied negative-number rounding logic to a positive result. This leads to off-by-one errors that, while annoying, are rarely catastrophic on their own.
The second flaw is the main event. When performing calculations on 64-bit signed integers (i64), the library rightfully promotes them to 128-bit (i128) to prevent intermediate overflows. However, when casting the result back down to i64, the library only checked if the value was too big (> i64::MAX). It completely forgot to check if the value was too small (< i64::MIN).
Rust's as keyword performs a truncating cast. If you take a massive negative number in i128 (one that exceeds the lower bounds of i64) and blindly cast it as i64, you don't get an error. You get a wrap-around. The sign bit gets chopped or misinterpreted, and suddenly, a deeply negative value acts like a large positive one.
Let's look at the Rust code responsible for the narrowing overflow. This is a simplified view of the i64 implementation before the patch. The goal is to return Option<i64>, where None indicates an overflow.
Vulnerable Code (Simplified):
fn mul_div(a: i64, b: i64, c: i64) -> Option<i64> {
// Promote to i128 to avoid intermediate overflow
let r = (a as i128) * (b as i128);
let res_i128 = r / (c as i128);
// THE BUG: Only checks upper bound!
if res_i128 > (i64::MAX as i128) {
return None;
}
// Blind cast. If res_i128 is smaller than i64::MIN,
// this wraps around!
Some(res_i128 as i64)
}If res_i128 is -9,223,372,036,854,775,809 (just one below i64::MIN), the upper bound check passes (it's certainly not greater than MAX). The cast then strips the high bits, and the binary representation is reinterpreted within the i64 bit space, resulting in a completely different, positive number.
The Fix (Commit c9233f7):
fn mul_div(a: i64, b: i64, c: i64) -> Option<i64> {
let r = (a as i128) * (b as i128);
let res_i128 = r / (c as i128);
// The Fix: Use TryFrom or check both bounds
i64::try_from(res_i128).ok()
}The fix is elegantly simple: stop doing manual bounds checks if you're bad at them. Rust's try_from handles all edge cases of narrowing casts automatically.
How do we weaponize this? Imagine a DeFi lending protocol on Soroban. The protocol tracks your account health using signed integers: positive values mean you are solvent, negative values mean you are in debt. The protocol uses soroban-fixed-point-math to calculate the "Adjusted Health Factor" after a market crash.
The Scenario:
(CurrentBalance * LeverageFactor) / MarketIndex.CurrentBalance * LeverageFactor results in a massive negative number, technically expressible in i128 but far below i64::MIN.mul_div. The intermediate result is, say, -2^64.-2^64 greater than i64::MAX? No. It proceeds.i64. Due to two's complement behavior, specific bit patterns of large negative numbers can wrap around to zero or positive values.Instead of the contract realizing you are hopelessly insolvent and liquidating you, the function returns a positive i64. Your dashboard suddenly shows you have a massive surplus. You withdraw the "excess" collateral and walk away, leaving the protocol with bad debt and a broken ledger.
The remediation for this vulnerability is straightforward but urgent. The developers of soroban-fixed-point-math released versions 1.3.1 and 1.4.1 to address the issue. The patch does two things:
(r < 0 && z > 0) || (r > 0 && z < 0) to determine if the result is truly negative before applying negative rounding.as i64 casting with i64::try_from(), which inherently checks both lower and upper bounds and returns an error if the value cannot fit in the target type.If you are a developer using this library, you cannot simply "hope for the best." Signed integer bugs are notoriously difficult to fuzz because they reside at the extreme edges of the number line. Check your Cargo.toml file immediately.
> [!NOTE] > If your contract is immutable and deployed with the vulnerable version, you are in a tight spot. You will need to migrate state to a new contract or execute an administrative pause if your governance model supports it.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
soroban-fixed-point-math script3 | 1.3.0 | 1.3.1 |
soroban-fixed-point-math script3 | 1.4.0 | 1.4.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-682 |
| Attack Vector | Network |
| CVSS Score | 7.5 (High) |
| Impact | Integrity Loss (Calculation Errors) |
| Exploit Status | PoC Available |
| Language | Rust |
The software performs a calculation that generates incorrect or inaccurate results that can affect the control flow or data integrity of the system.
An access control deficiency in the 9Router dashboard allows unauthenticated remote attackers to perform full CRUD operations on integrated AI providers, extract plaintext API keys, and access complete system conversation histories.
A DOM-based cross-site scripting (XSS) vulnerability exists in Craft CMS versions 4.0.0-RC1 through 4.17.15 and 5.0.0-RC1 through 5.9.22. The flaw resides within the CraftSupport widget's feedback search component, which fails to neutralize GitHub issue titles before rendering them into the administrator's control panel. An unauthenticated attacker can exploit this vulnerability by submitting a crafted issue to the public Craft CMS repository on GitHub.
CVE-2026-55793 is a DOM-based Stored Cross-Site Scripting (XSS) vulnerability affecting Craft CMS versions 5.0.0-RC1 through 5.9.22. An authenticated user with minimum Author privileges can store a malicious payload in an entry's title. When an administrator or high-privileged user performs a drag-and-drop operation under the modified entry in the structure table view, the unescaped payload is retrieved and concatenated into raw HTML, resulting in arbitrary JavaScript execution within the context of the administrative session.
Craft CMS versions 5.9.0 through 5.9.9 are vulnerable to authenticated Remote Code Execution (RCE). An attacker with control panel permissions to edit entries can inject malicious Twig templates into the client-side HTTP Referer header. During the post-save redirect sequence, the server evaluates this user-controlled header using an unsandboxed Twig rendering function, leading to arbitrary system command execution.
An authenticated SQL injection vulnerability exists in the datapoint crosstab export functionality of OpenRemote. The vulnerability is caused by insecure manual SQL string construction that concatenates user-controlled display data, specifically asset display names and attribute names, directly into raw SQL statements. These statements are processed by the PostgreSQL database engine using the crosstab function to structure dynamic CSV outputs.
An insecure redirect vulnerability in Coder allows an authenticated attacker who controls a workspace agent to perform unauthorized cross-agent file operations and achieve remote code execution in other workspaces. By exploiting default redirect-following behavior in the control-plane's HTTP client, a malicious agent can redirect legitimate requests to a victim's deterministic tailnet IP address.