SSRF Loopback IP filter bypass in directus
Description
Directus is a real-time API and App dashboard for managing SQL database content. When relying on blocking access to localhost using the default 0.0.0.0 filter a user may bypass this block by using other registered loopback devices (like 127.0.0.2 - 127.127.127.127). This issue has been addressed in release versions 10.13.3 and 11.1.0. Users are advised to upgrade. Users unable to upgrade may block this bypass by manually adding the 127.0.0.0/8 CIDR range which will block access to any 127.X.X.X ip instead of just 127.0.0.1.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
directusnpm | < 10.13.3 | 10.13.3 |
directusnpm | >= 11.0.0, < 11.1.0 | 11.1.0 |
@directus/apinpm | < 21.0.0 | 21.0.0 |
@directus/apinpm | >= 22.0.0, < 22.1.1 | 22.1.1 |
Affected products
1Patches
4769fa22797bfMerge commit from fork
2 files changed · +6 −0
api/src/middleware/respond.ts+1 −0 modified@@ -27,6 +27,7 @@ export const respond: RequestHandler = asyncHandler(async (req, res) => { if ( (req.method.toLowerCase() === 'get' || req.originalUrl?.startsWith('/graphql')) && + req.originalUrl?.startsWith('/auth') === false && env['CACHE_ENABLED'] === true && cache && !req.sanitizedQuery.export &&
.changeset/orange-suns-fry.md+5 −0 added@@ -0,0 +1,5 @@ +--- +'@directus/api': patch +--- + +Fixed an issue that could cause the response cache to misbehave
4aace0bbe572Merge commit from fork
2 files changed · +6 −0
api/src/middleware/respond.ts+1 −0 modified@@ -27,6 +27,7 @@ export const respond: RequestHandler = asyncHandler(async (req, res) => { if ( (req.method.toLowerCase() === 'get' || req.originalUrl?.startsWith('/graphql')) && + req.originalUrl?.startsWith('/auth') === false && env['CACHE_ENABLED'] === true && cache && !req.sanitizedQuery.export &&
.changeset/orange-suns-fry.md+5 −0 added@@ -0,0 +1,5 @@ +--- +'@directus/api': patch +--- + +Fixed an issue that could cause the response cache to misbehave
c1f3ccc68159Merge commit from fork
3 files changed · +34 −2
api/src/request/is-denied-ip.test.ts+23 −1 modified@@ -108,7 +108,7 @@ test(`Returns true if IP matches resolved local network interface address`, asyn netmask: '255.0.0.0', family: 'IPv4', mac: '00:00:00:00:00:00', - internal: true, + internal: false, cidr: '127.0.0.1/8', }, ], @@ -118,3 +118,25 @@ test(`Returns true if IP matches resolved local network interface address`, asyn expect(result).toBe(true); }); + +test(`Returns true if IP matches resolved to local loopback devices`, async () => { + vi.mocked(useEnv).mockReturnValue({ IMPORT_IP_DENY_LIST: ['0.0.0.0'] }); + + vi.mocked(os.networkInterfaces).mockReturnValue({ + fa0: undefined, + lo0: [ + { + address: '127.0.0.1', + netmask: '255.0.0.0', + family: 'IPv4', + mac: '00:00:00:00:00:00', + internal: true, + cidr: '127.0.0.1/8', + }, + ], + }); + + expect(isDeniedIp('127.0.0.1')).toBe(true); + expect(isDeniedIp('127.8.16.32')).toBe(true); + expect(isDeniedIp('127.127.127.127')).toBe(true); +});
api/src/request/is-denied-ip.ts+6 −1 modified@@ -1,4 +1,5 @@ import { useEnv } from '@directus/env'; +import { matches } from 'ip-matching'; import os from 'node:os'; import { useLogger } from '../logger/index.js'; import { ipInNetworks } from '../utils/ip-in-networks.js'; @@ -29,7 +30,11 @@ export function isDeniedIp(ip: string): boolean { if (!networkInfo) continue; for (const info of networkInfo) { - if (info.address === ip) return true; + if (info.internal && info.cidr) { + if (matches(ip, info.cidr)) return true; + } else if (info.address === ip) { + return true; + } } } }
.changeset/silly-months-protect.md+5 −0 added@@ -0,0 +1,5 @@ +--- +'@directus/env': patch +--- + +Expanded `0.0.0.0` matching of loopback ranges
3 files changed · +34 −2
api/src/request/is-denied-ip.test.ts+23 −1 modified@@ -108,7 +108,7 @@ test(`Returns true if IP matches resolved local network interface address`, asyn netmask: '255.0.0.0', family: 'IPv4', mac: '00:00:00:00:00:00', - internal: true, + internal: false, cidr: '127.0.0.1/8', }, ], @@ -118,3 +118,25 @@ test(`Returns true if IP matches resolved local network interface address`, asyn expect(result).toBe(true); }); + +test(`Returns true if IP matches resolved to local loopback devices`, async () => { + vi.mocked(useEnv).mockReturnValue({ IMPORT_IP_DENY_LIST: ['0.0.0.0'] }); + + vi.mocked(os.networkInterfaces).mockReturnValue({ + fa0: undefined, + lo0: [ + { + address: '127.0.0.1', + netmask: '255.0.0.0', + family: 'IPv4', + mac: '00:00:00:00:00:00', + internal: true, + cidr: '127.0.0.1/8', + }, + ], + }); + + expect(isDeniedIp('127.0.0.1')).toBe(true); + expect(isDeniedIp('127.8.16.32')).toBe(true); + expect(isDeniedIp('127.127.127.127')).toBe(true); +});
api/src/request/is-denied-ip.ts+6 −1 modified@@ -1,5 +1,6 @@ import { useEnv } from '@directus/env'; import os from 'node:os'; +import { matches } from 'ip-matching'; import { useLogger } from '../logger/index.js'; import { ipInNetworks } from '../utils/ip-in-networks.js'; @@ -29,7 +30,11 @@ export function isDeniedIp(ip: string): boolean { if (!networkInfo) continue; for (const info of networkInfo) { - if (info.address === ip) return true; + if (info.internal && info.cidr) { + if (matches(ip, info.cidr)) return true; + } else if (info.address === ip) { + return true; + } } } }
.changeset/silly-months-protect.md+5 −0 added@@ -0,0 +1,5 @@ +--- +'@directus/env': patch +--- + +Expanded `0.0.0.0` matching of loopback ranges
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-68g8-c275-xf2mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-46990ghsaADVISORY
- github.com/directus/directus/commit/4aace0bbe57232e38cd6a287ee475293e46dc91bghsax_refsource_MISCWEB
- github.com/directus/directus/commit/769fa22797bff5a9231599883b391e013f122e52ghsax_refsource_MISCWEB
- github.com/directus/directus/commit/8cbf943b65fd4a763d09a5fdbba8996b1e7797ffghsax_refsource_MISCWEB
- github.com/directus/directus/commit/c1f3ccc681595038d094ce110ddeee38cb38f431ghsax_refsource_MISCWEB
- github.com/directus/directus/security/advisories/GHSA-68g8-c275-xf2mghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.