- All findings
- 5
- Critical
- 0
- High
- 0
- Medium
- 1
- Low
- 0
- Informational
- 4
Date of engagement: 6th February 2025 - 10th February 2025
About Us
Guvenkaya is a security research firm specializing in Rust security, Web3 security of Non-EVM protocols, and Web2 security. With our expertise, we provide both security auditing services and custom security solutions
About Aurora
Aurora is a Virtual Chain built on NEAR. It’s, at the same time, the sandbox and the proof of the robustness of the parent protocol. It’s a smart contract - probably the most complex that exists - that is also an Ethereum Virtual Machine, providing a turn-key solution for developers to operate their apps on an Ethereum-compatible, high-throughput, scalable and future-safe platform, with low transaction costs.
Audit Results
Guvenkaya conducted a security assessment of the NEAR Intents Passkeys Pull Request from 6th February 2025 to 10th February 2025. During this engagement, a total of 5 findings were reported. 1 of the findings was medium and the remaining were informational severity. All issues were either fixed, acknowledged, or scheduled per the remediation status.
Project Scope
| Files | Link |
|---|---|
| Passkeys Pull Request | https://github.com/near/intents/pull/43 |
Out of Scope
The audit will include reviewing the code for security vulnerabilities. The audit does not include a review of the tests and dependencies.
Timeline
- Start of the audit
6th February 2025
- Draft report
10th February 2025
- Final report
6th March 2025
Methodology
- RESEARCH INTO PROJECT ARCHITECTURE
- PREPARING ATTACK VECTORS
- SETTING UP AN ENVIRONMENT
- MANUAL CODE REVIEW OF THE CODE
- ASSESSMENT OF RUST SECURITY ISSUES
- ASSESSMENT OF NEAR SECURITY ISSUES
- ASSESSMENT OF ARITHMETIC ISSUES
- BUSINESS LOGIC VULNERABILITY ASSESSMENT
- ONCHAIN TESTING USING NEAR WORKSPACES
- BEST PRACTICES AND CODE QUALITY
- CHECKING FOR CODE REFACTORING/SIMPLIFICATION POSSIBILITIES
- ARCHITECTURE IMPROVEMENT SUGGESTIONS
- PREPARING POCS AND/OR TESTS FOR EACH CRITICAL/HIGH/MEDIUM ISSUES
Severity Breakdown
Findings Summary
| Finding | Impact | Likelihood | Severity | Remediation complexity | Remediation status |
|---|---|---|---|---|---|
| GUV-1: Potential DoS of The Main Functionality Through Malicious Solvers | Severe | Rare | Medium | Moderate | Scheduled |
| GUV-2: Anyone Can Fake The Passkey Signature For Their Own Account | Negligible | Rare | Informational | Complex | Acknowledged |
| GUV-3: Unsafe Callback URL Validation | Negligible | Rare | Informational | Simple | Acknowledged |
| GUV-4: Insufficient Nonce Entropy Validation | Negligible | Rare | Informational | Simple | Acknowledged |
| GUV-5: Different Signature Malleability For Each Curve | Negligible | Rare | Informational | Simple | Fixed |
Findings Details
GUV-1: Potential DoS of The Main Functionality Through Malicious Solvers
MediumMalicious solvers can cause service degradation or denial of service (DoS) to frontends and any systems relying on solver bus data.
Causes That Make This Attack Possible:
Frontends typically are optimizing to give the best quote to the user Solver bus simulates intents and aggregates quotes submitted via websockets A time gap exists between the solver bus's intent simulation and user signature submission Quote submission to the solver bus is unrestricted The failure of one intent causes all other intents to fail:
core:execute_signed_intents:core/src/engine/mod.rs
pub fn execute_signed_intents(
mut self,
signed: impl IntoIterator<Item = MultiPayload>,
) -> Result<Transfers> {
for signed in signed {
self.execute_signed_intent(signed)?;
}
self.finalize()
}An attacker controls multiple solvers with deposited funds for popular token pairs, consistently submitting the best prices. The solver's available funds aren't verified after intent simulation The solver bus returns quotes including the attacker's quote. Frontends likely select the attacker's quote due to its superior pricing The user signs the transaction and submits it to the smart contract A single malicious intent causes the entire transaction to fail, even if other intents were valid, due to the execution loop's error handling By repeating this process for each quote request, the attacker prevents reliable intent execution. This risk is heightened during early adoption when solver numbers are limited
Impact:
Reputation damage Inability for regular users to execute intents (griefing)
Recommendation
Implement a solver reputation system (possibly using a trusted solver list), economic penalties, and progressive timeout/backoff for unreliable solvers. This could be implemented with the addition of API keys.
Remediation - Scheduled
The Aurora team has acknowledged the issue and will fix it soon by introducing API keys and/or reputaion-based filteting for solvers on Solver Bus level.
GUV-2: Anyone Can Fake The Passkey Signature For Their Own Account
InformationalIt was observed that, anyone can generate a valid P256 key, add their public key through the add_public_key function, sign the required data, and successfully pass the smart contract verification. This effectively allows users to trick the smart contract into believing that an object was signed by a passkey when it wasn't. The significance of this issue depends on whether there's a need to differentiate between passkey and hardware key signatures, or if there are special benefits planned for passkey users.
Impact:
Users can fake signatures without utilizing passkeys
Recommendation
To ensure the passkey is genuine and from a verified source, you should:
Offchain - During Registration:
Obtain the attestation certificate (CA and device) from the blob at https://fidoalliance.org/metadata/ or other sources Verify the device's AAGUID, certificate chain, and newly generated public key If verification succeeds, call the smart contract to add the public key (consider mapping between AAGUID/credential_id &pubkey). Disable unverified public key additions for P256 signatures
Offchain - During Signing: Sign the intents object without the public_key field and send it to SC
Onchain:
Verify the flags and attestedData = true, then fetch the stored public key (using credential ID/AAGUID) Check the signature against the known public key Perform regular verifications (nonce, signer matches public key, etc.)
Remediation - Acknowledged
The Aurora team has acknowledged the issue but it won’t be fixed since it is allowed by design.
GUV-3: Unsafe Callback URL Validation
InformationalThe Nep413Payload structure and with_callback_url function in Nep413Payload implementation accepts callback URLs without any validation, potentially allowing malicious or unsafe URLs to be passed.
nep413:nep413/src/lib.rs
#[near(serializers = [borsh, json])]
#[serde(rename_all = "camelCase")]
#[derive(Debug, Clone)]
pub struct Nep413Payload {
pub message: String, #[serde_as(as = "Base64")]
pub nonce: [u8; 32], pub recipient: String, #[serde(default, skip_serializing_if = "Option::is_none")]
pub callback_url: Option<String>,
} // ... existing code ...
#[inline]
pub fn with_callback_url(mut self, callback_url: String) -> Self {
self.callback_url = Some(callback_url);
self
}Impact:
This allows arbitrary strings to be used as callback URLs, including:
Dangerous protocols (e.g., file://, data://) Malformed URLs URLs with null bytes or other control characters
If any off-chain component processes these callback URLs, it could be vulnerable to exploitation.
Recommendation
Implement proper URL validation in the with_callback_url and write a custom deserializer to verify Url on the parsing level in Nep413Payload.
Remediation - Acknowledged
The Aurora team has acknowledged the issue but it won’t be fixed since it is allowed by design.
GUV-4: Insufficient Nonce Entropy Validation
InformationalAny signed payload accepts any 32-byte array as a nonce without validating its entropy or randomness.
poc:test_zero_nonce_security
#[test]
fn test_zero_nonce_security() {
let zero_nonce_payload = Nep413Payload {
message: payload.message.clone(),
nonce: [0u8; 32], // Zero nonce accepted
recipient: payload.recipient.clone(),
callback_url: None,
};
}Impact:
If you’re only using the nonce as a once-per-user unique value there are no problems. However, It is still safer and more standard to generate a random nonce of sufficient length (128 bits+) so you avoid accidental collisions and stay in line with typical cryptographic best practices.
Recommendation
Implement proper nonce validation and generation
Remediation - Acknowledged
The Aurora team has acknowledged the issue but it won’t be fixed since it is allowed by design.
GUV-5: Different Signature Malleability For Each Curve
InformationalEach cryptographic curve implements signature malleability differently:
SeckP: Not Allowed. Uses ecrecover with a true flag to enable malleability checks Ed25519: Allowed. Uses env::ed25519_verify P256: Allowed. Implements passkey verification without signature malleability checking
The behavior of signature malleability should be unified across all curves. Having consistent security properties would simplify threat modeling, debugging, and tracking.
Impact:
While nonces currently protect against replay attacks, disallowing malleability for all curves would be more secure. Future code changes, such as adding functions without nonce validation, could introduce vulnerabilities if malleability remains inconsistent.
Recommendation
Enable signature malleability checks across all curves.
Remediation - Fixed
The Aurora team has fixed the issue by enabling signature malleability checks for all curves in these PRs:
Ed25519: PR-47 P256: PR-44
Source: published GitHub report · 17 pages. The original PDF includes the source formatting, figures, and linked references.
