May 13, 2026·6 min read·25 visits
Anchor versions prior to 1.0.0 skip structural discriminator checks for the `InterfaceAccount` type, allowing attackers to supply improperly typed accounts that bypass framework-level validation.
A critical vulnerability in the Anchor framework's `anchor-lang` crate allows account substitution attacks. The `InterfaceAccount` type fails to validate the 8-byte account discriminator during deserialization, permitting an attacker to supply a mismatched account type and subvert program logic.
The Anchor framework provides the foundational infrastructure for developing smart contracts on the Solana blockchain. Within this ecosystem, the anchor-lang crate manages core account parsing and validation routines. The InterfaceAccount type facilitates polymorphic account handling, allowing developers to accept accounts owned by multiple defined programs, such as distinguishing between SPL Token and Token-2022 programs.
A critical vulnerability exists in Anchor versions prior to 1.0.0 where the InterfaceAccount type fails to perform standard structural validation. Specifically, the framework omits the 8-byte discriminator check during the deserialization of InterfaceAccount structures. This check serves as the primary mechanism in Anchor to verify that a provided account matches the expected struct definition.
Without this discriminator validation, the framework blindly maps the provided account data to the expected structural layout. Attackers exploit this by passing structurally dissimilar accounts that share the same program owner. The framework processes the anomalous account, leading to account substitution and the execution of unauthorized state transitions.
The core issue stems from the improper delegation of deserialization routines within the InterfaceAccount::try_from method. Anchor employs an 8-byte discriminator, derived from the SHA256 hash of the account type name, to uniquely identify program state types. Standard Anchor accounts invoke T::try_deserialize, which explicitly reads this discriminator and validates it against the expected type definition.
In vulnerable versions, InterfaceAccount overrides this behavior by invoking the try_from_unchecked method. This function delegates processing to T::try_deserialize_unchecked, which deliberately skips the discriminator verification step. The framework relies entirely on the outer program ownership check, assuming that any account owned by the specified program is valid for the operation.
This assumption breaks the core security model of Anchor programs. Solana programs frequently manage multiple distinct account types under a single program ID. By bypassing the discriminator verification, the deserializer interprets the raw byte array of an arbitrary account as the expected struct type. This results in type confusion, where malicious or unintended data structures are treated as trusted state components.
A review of the anchor-lang source code illustrates the exact mechanism of the bypass. Prior to the fix, the try_from method simply wrapped the unchecked variant, returning Self::try_from_unchecked(info). This unchecked variant inherently lacked the necessary data validation logic to ensure structural integrity.
The patch introduced in commit 26ef36968a62e28a1f028e7adae4806af30c747d fundamentally restructures this method. The updated code introduces explicit initialization checks, verifying that the account is not an uninitialized system program account with zero lamports. Most importantly, it replaces the unchecked deserialization call with strict, discriminator-aware logic.
The revised implementation requires T::try_deserialize(&mut data) to process the account bytes. This exact function enforces the 8-byte discriminator check prior to reading the state variables. The code diff below highlights the transition from the insecure bypass to the secure, validated deserialization flow.
// Vulnerable Implementation (lang/src/accounts/interface_account.rs)
- pub fn try_from(info: &'a AccountInfo<'a>) -> Result<Self> {
- Self::try_from_unchecked(info)
- }
// Patched Implementation (lang/src/accounts/interface_account.rs)
+ pub fn try_from(info: &'a AccountInfo<'a>) -> Result<Self> {
+ if info.owner == &system_program::ID && info.lamports() == 0 {
+ return Err(ErrorCode::AccountNotInitialized.into());
+ }
+ T::check_owner(info.owner)?;
+ let mut data: &[u8] = &info.try_borrow_data()?;
+ Ok(Self::new(info, T::try_deserialize(&mut data)?))
+ }Exploiting this vulnerability requires the attacker to identify a target instruction that utilizes the InterfaceAccount type. The attacker then analyzes the target program to locate secondary account types managed by the same program owner. The goal is to find an unexpected account type that contains a compatible byte layout or exploitable data values when parsed as the expected type.
The attacker constructs a malicious transaction, substituting the legitimate expected account with the identified alternative account. When the transaction reaches the Solana validator, the vulnerable anchor-lang deserializer skips the discriminator check. The framework parses the bytes of the alternative account according to the layout of the expected struct, mapping fields sequentially based on their defined sizes.
This byte mapping creates an artificial state where the application logic operates on attacker-controlled or misaligned data. The proof-of-concept provided in the patch commit demonstrates this exact behavior. An RPC call passing anotherAccount to an instruction expecting expectedAccount completes successfully in vulnerable versions, rather than throwing the standard AccountDiscriminatorMismatch error.
it("Doesn't allow unexpected accounts owned by the expected programs", async () => {
try {
await program.methods
.test()
.accounts({ expectedAccount: anotherAccount.publicKey })
.rpc();
assert.fail("Allowed unexpected account substitution!");
} catch (e) {
assert(e instanceof anchor.AnchorError);
assert.strictEqual(
e.error.errorCode.number,
anchor.LangErrorCode.AccountDiscriminatorMismatch
);
}
});The consequences of this account substitution vulnerability vary depending on the specific program logic. At a minimum, it enables type confusion across any application relying on InterfaceAccount for state management. Attackers leverage this confusion to bypass access controls, manipulate financial logic, or steal locked assets.
In decentralized finance protocols, passing a forged or alternative token account structure into a swap or withdrawal instruction leads to direct financial loss. If an attacker substitutes an administrative configuration account with a standard user account, they gain unauthorized execution capabilities over the entire protocol. The framework-level nature of this flaw amplifies the risk, as it inherently affects any downstream application utilizing the vulnerable component.
This vulnerability highlights the critical importance of cryptographic discriminators in Solana account models. Relying purely on program ownership checks leaves applications fundamentally exposed to internal state manipulation. Programs that utilize complex, multi-type data architectures are at the highest risk of exploitation.
The official remediation requires an immediate upgrade to the Anchor framework version 1.0.0 or later. This release incorporates the patch from pull request #4139, restoring the discriminator validation to the InterfaceAccount type. Development teams must update their Cargo.toml dependencies and recompile their smart contracts using the updated tooling.
After updating the framework, security teams must audit all existing implementations of InterfaceAccount. It is necessary to confirm that the underlying types defined within the application possess proper discriminators. Applications failing to define strong structural boundaries remain at risk even with the patched framework.
Developers must completely avoid the manual usage of try_from_unchecked or InterfaceAccount::try_from_unchecked methods. These functions bypass the safety mechanisms restored by the 1.0.0 patch and reintroduce the vulnerability. Standard deserialization practices must be strictly enforced throughout the codebase.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
anchor-lang Solana Foundation | < 1.0.0 | 1.0.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-20 |
| Attack Vector | Network |
| Vulnerability Class | Account Substitution / Type Confusion |
| Exploit Status | Proof-of-Concept Available |
| CVSS v3.1 Score | 9.1 |
| Patch Version | 1.0.0 |
Improper Input Validation leading to Account Substitution.
SiYuan Note versions prior to v3.7.4 contain an information disclosure vulnerability in the `/api/asset/resolveAssetPath` endpoint. This endpoint returns absolute backend filesystem paths unmodified to CheckAuth-only requests. Low-privileged users or unauthenticated readers under publish mode can exploit this to leak the local directory layout, operating system username, and overall host deployment structure.
An access control vulnerability in the SiYuan personal knowledge management platform before version v3.7.4 exposes notebook encryption parameters to unauthenticated remote attackers. When the platform is configured in Publish Mode, specific API endpoints fail to enforce authorization checks. This access failure leaks key-derivation materials, password verifiers, and wrapped database keys to anonymous network clients.
A security vulnerability in the SiYuan local-first personal knowledge management system allows unauthenticated remote attackers to bypass logical boundary controls in publish (read-only) mode. By interacting with endpoints that lack proper publish-access validation, an attacker can disclose the application's internal database schemas and harvest block IDs across both public and private notebooks. This metadata leakage compromises the confidentiality of restricted documents and provides foundational information for targeted extraction.
An information disclosure vulnerability exists in the SiYuan personal knowledge management system versions prior to v3.7.4. The application fails to enforce publish-access filters on block attribute retrieval endpoints. Consequently, unauthenticated remote attackers can bypass document-level protection rules (such as password protection or disabled-publish flags) to retrieve sensitive block-level attributes, including aliases, memos, block names, and custom metadata fields, by querying the API using guessed or known block IDs.
An authentication bypass vulnerability (classified as CWE-288) exists in the publish-mode component of SiYuan, a Go-based note-taking application. This security flaw allows unauthenticated remote attackers to bypass password-protected note boundaries by leveraging auxiliary block endpoints that fail to enforce document access checks. Attackers can exploit this issue by first harvesting document metadata via a public search endpoint and subsequently fetching full rendered document contents using vulnerable block endpoints. This technical analysis explores the root cause, exploitation methodology, and remediation path.
An uncontrolled recursion vulnerability (CWE-674) in the toml-node NPM package (published as toml) prior to version 4.2.0 allows unauthenticated remote attackers to trigger process-wide Denial of Service (DoS) crashes. By submitting TOML payloads with deep bracket or brace nesting, attackers exhaust the V8 runtime stack limit.