CVE-2025-48888
Executive Summary
CVE-2025-48888 is a permission bypass vulnerability in the Deno JavaScript/TypeScript/WebAssembly runtime. When a user specifies both a global --allow-* and a corresponding --deny-* flag (e.g., deno run --allow-read --deny-read main.ts), the --deny-* flag is incorrectly ignored, and the permission is granted. This violates the principle that deny rules should take precedence over allow rules. The vulnerability affects Deno versions starting from 1.41.3 up to, but not including, 2.1.13, 2.2.13, and 2.3.2.
The impact is limited to scenarios where a user explicitly provides contradictory permission flags, which is considered a nonsensical configuration. However, in automated or scripted environments where such flags might be generated dynamically, this could lead to unintended privilege escalation. The CVSS score is 0.0 due to the low likelihood of exploitation in typical use cases, but the vulnerability is a logical flaw in permission enforcement.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Deno is a runtime for JavaScript, TypeScript, and WebAssembly. While this vulnerability does not directly affect package supply chains, it could be exploited in CI/CD pipelines or containerized environments where Deno scripts are executed with specific permission sets. If a pipeline script uses both --allow-read and --deny-read (e.g., due to misconfiguration or dynamic flag generation), the deny flag would be bypassed, potentially exposing sensitive files.
CVSS Breakdown & Threat Level
- CVSS Score: 0.0 (None)
- Vector: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
- Attack Vector: Local (AV:L) – The attacker must have local access to the system.
- Attack Complexity: Low (AC:L) – No special conditions required.
- Privileges Required: Low (PR:L) – The attacker needs to execute a Deno script.
- User Interaction: None (UI:N) – No user interaction required beyond running the script.
- Scope: Unchanged (S:U) – The vulnerability does not affect other components.
- Confidentiality Impact: Low (C:L) – Potential limited information disclosure.
- Integrity Impact: None (I:N) – No data modification.
- Availability Impact: None (A:N) – No denial of service.
Threat Level: Low. The vulnerability requires a specific, contradictory flag combination that is unlikely in normal usage. However, in automated environments, it could be triggered inadvertently.
Deep Dive & Mechanics
The vulnerability lies in Deno's permission resolution logic. When a user specifies both --allow-read and --deny-read, the runtime should enforce the deny rule, effectively blocking read access. However, due to a bug introduced in version 1.41.3, the deny flag is overridden by the allow flag, granting read access.
The root cause is in the permission parsing and enforcement code. The --deny-* flags are processed after --allow-* flags, but the logic incorrectly treats the allow flag as taking precedence. The intended behavior is that deny flags should always override allow flags, as they represent explicit restrictions.
The vulnerability affects all global unary permissions: --allow-read, --allow-write, --allow-net, --allow-env, --allow-run, --allow-ffi, --allow-hrtime, and their corresponding --deny-* counterparts.
Vulnerable vs. Patched Code
Vulnerable Code (Deno < 2.1.13, 2.2.13, 2.3.2):
// Simplified permission resolution logic
fn resolve_permission(allow: bool, deny: bool) -> bool {
if allow {
return true; // Bug: deny is ignored if allow is true
}
if deny {
return false;
}
false // Default deny
}
Patched Code (Deno >= 2.1.13, 2.2.13, 2.3.2):
// Corrected permission resolution logic
fn resolve_permission(allow: bool, deny: bool) -> bool {
if deny {
return false; // Deny takes precedence
}
if allow {
return true;
}
false // Default deny
}
The fix ensures that if a deny flag is set, it overrides any allow flag, enforcing the restriction.
Exploitation & Lab Setup
Red Team
To exploit this vulnerability, an attacker would need to craft a scenario where a Deno script is executed with both --allow-read and --deny-read flags. This could occur in a CI/CD pipeline where flags are dynamically generated based on configuration files.
Proof of Concept:
- Create a file
sensitive.txtwith sensitive content. - Create a Deno script
read.tsthat reads the file:const content = await Deno.readTextFile("sensitive.txt"); console.log(content); - Execute the script with contradictory flags:
In vulnerable versions, the script will successfully read and print the file content, bypassing the deny rule.deno run --allow-read --deny-read read.ts
Blue Team
To detect exploitation attempts:
- Monitor for Deno processes with both
--allow-*and--deny-*flags for the same permission. - Audit CI/CD pipeline configurations for contradictory permission flags.
- Use process monitoring tools to detect unexpected file access by Deno scripts.
Detection Command:
ps aux | grep "deno.*--allow-read.*--deny-read"
Indicators of Compromise (IOCs)
- Process: Deno runtime processes with both
--allow-*and--deny-*flags for the same permission. - Logs: Deno permission warnings or errors indicating contradictory flags.
- File Access: Unexpected access to files that should be denied by
--deny-reador similar flags.
Example IOC Log Entry:
[2025-03-15 10:00:00] WARN: Both --allow-read and --deny-read specified. Deny flag ignored.
Detection Engineering
YARA Rule
rule Detect_Deno_Contradictory_Permissions {
meta:
description = "Detects Deno processes with contradictory --allow-* and --deny-* flags"
author = "Valters IT"
date = "2025-03-15"
hash = "d41d8cd98f00b204e9800998ecf8427e"
strings:
$allow_read = "--allow-read"
$deny_read = "--deny-read"
$allow_write = "--allow-write"
$deny_write = "--deny-write"
$allow_net = "--allow-net"
$deny_net = "--deny-net"
$allow_env = "--allow-env"
$deny_env = "--deny-env"
$allow_run = "--allow-run"
$deny_run = "--deny-run"
$allow_ffi = "--allow-ffi"
$deny_ffi = "--deny-ffi"
$allow_hrtime = "--allow-hrtime"
$deny_hrtime = "--deny-hrtime"
condition:
(any of ($allow_*) and any of ($deny_*)) and
for any i in (0..#allow_read) : (
for any j in (0..#deny_read) : (
@allow_read[i] < @deny_read[j] or @deny_read[j] < @allow_read[i]
)
)
}
Sigma Rule
title: Deno Contradictory Permission Flags
id: d8c3b2a1-1234-5678-9abc-def012345678
status: experimental
description: Detects Deno processes with both --allow-* and --deny-* flags for the same permission
author: Valters IT
date: 2025-03-15
logsource:
category: process_creation
product: linux
detection:
selection:
Image|endswith: '/deno'
CommandLine|contains|all:
- '--allow-read'
- '--deny-read'
condition: selection
falsepositives:
- Intentional testing scenarios
level: low
tags:
- attack.privilege_escalation
- attack.t1548
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"Deno Contradictory Permission Flags Detected"; flow:to_server; content:"deno"; http_uri; content:"--allow-read"; http_uri; content:"--deny-read"; http_uri; classtype:attempted-recon; sid:1000001; rev:1;)
Remediation & Mitigation
Upgrade Deno: Update to version 2.1.13, 2.2.13, or 2.3.2 or later.
deno upgrade --version 2.3.2Audit Configurations: Review all scripts and CI/CD pipelines for contradictory
--allow-*and--deny-*flags.Implement Permission Validation: Add pre-execution checks to ensure that deny flags are not overridden by allow flags.
Monitor Logs: Enable Deno's permission logging to detect when contradictory flags are used.
Use Least Privilege: Avoid using global
--allow-*flags. Instead, use granular permissions with--allow-read=/specific/path.
Valters IT
Securing Digital Frontiers