Moderate severityNVD Advisory· Published Jun 22, 2022· Updated Sep 17, 2024
directus - SSRF which leads to internal port scan
CVE-2022-23080
Description
In directus versions v9.0.0-beta.2 through 9.6.0 are vulnerable to server-side request forgery (SSRF) in the media upload functionality which allows a low privileged user to perform internal network port scans.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
directusnpm | >= 9.0.0-beta.2, < 9.7.0 | 9.7.0 |
Affected products
1Patches
16da3f1ed5034Add support for import ip deny list (#12025)
3 files changed · +60 −3
api/src/env.ts+3 −0 modified@@ -77,6 +77,8 @@ const defaults: Record<string, any> = { IP_TRUST_PROXY: true, IP_CUSTOM_HEADER: false, + IMPORT_IP_DENY_LIST: '0.0.0.0', + SERVE_APP: true, RELATIONAL_BATCH_SIZE: 25000, @@ -95,6 +97,7 @@ const typeMap: Record<string, string> = { DB_PORT: 'number', DB_EXCLUDE_TABLES: 'array', + IMPORT_IP_DENY_LIST: 'array', }; let env: Record<string, any> = {
api/src/services/files.ts+56 −3 modified@@ -5,7 +5,9 @@ import { clone } from 'lodash'; import { extension } from 'mime-types'; import path from 'path'; import sharp from 'sharp'; -import url from 'url'; +import url, { URL } from 'url'; +import { promisify } from 'util'; +import { lookup } from 'dns'; import emitter from '../emitter'; import env from '../env'; import { ForbiddenException, ServiceUnavailableException } from '../exceptions'; @@ -14,6 +16,10 @@ import storage from '../storage'; import { AbstractServiceOptions, File, PrimaryKey, MutationOptions } from '../types'; import { toArray } from '@directus/shared/utils'; import { ItemsService } from './items'; +import net from 'net'; +import os from 'os'; + +const lookupDNS = promisify(lookup); export class FilesService extends ItemsService { constructor(options: AbstractServiceOptions) { @@ -161,15 +167,62 @@ export class FilesService extends ItemsService { throw new ForbiddenException(); } + let resolvedUrl; + + try { + resolvedUrl = new URL(importURL); + } catch (err: any) { + logger.warn(err, `Requested URL ${importURL} isn't a valid URL`); + throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, { + service: 'external-file', + }); + } + + let ip = resolvedUrl.hostname; + + if (net.isIP(ip) === 0) { + try { + ip = (await lookupDNS(ip)).address; + } catch (err: any) { + logger.warn(err, `Couldn't lookup the DNS for url ${importURL}`); + throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, { + service: 'external-file', + }); + } + } + + if (env.IMPORT_IP_DENY_LIST.includes('0.0.0.0')) { + const networkInterfaces = os.networkInterfaces(); + + for (const networkInfo of Object.values(networkInterfaces)) { + if (!networkInfo) continue; + + for (const info of networkInfo) { + if (info.address === ip) { + logger.warn(`Requested URL ${importURL} resolves to localhost.`); + throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, { + service: 'external-file', + }); + } + } + } + } + + if (env.IMPORT_IP_DENY_LIST.includes(ip)) { + logger.warn(`Requested URL ${importURL} resolves to a denied IP address.`); + throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, { + service: 'external-file', + }); + } + let fileResponse: AxiosResponse<NodeJS.ReadableStream>; try { fileResponse = await axios.get<NodeJS.ReadableStream>(importURL, { responseType: 'stream', }); } catch (err: any) { - logger.warn(`Couldn't fetch file from url "${importURL}"`); - logger.warn(err); + logger.warn(err, `Couldn't fetch file from url "${importURL}"`); throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, { service: 'external-file', });
docs/configuration/config-options.md+1 −0 modified@@ -280,6 +280,7 @@ All the `DB_POOL_` prefixed options are passed to [`tarn.js`](https://github.com | `IP_CUSTOM_HEADER` | What custom request header to use for the IP address | false | | `CONTENT_SECURITY_POLICY` | Custom overrides for the Content-Security-Policy header. See [helmet's documentation](https://helmetjs.github.io) for more information. | -- | | `ASSETS_CONTENT_SECURITY_POLICY` | Custom overrides for the Content-Security-Policy header for the /assets endpoint. See [helmet's documentation](https://helmetjs.github.io) for more information. | -- | +| `IMPORT_IP_DENY_LIST` | Deny importing files from these IP addresses. Use `0.0.0.0` for any local IP address | `0.0.0.0` | ::: tip Cookie Strictness
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
4- github.com/advisories/GHSA-5h75-pvq4-82c9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-23080ghsaADVISORY
- github.com/directus/directus/commit/6da3f1ed5034115b1da00440008351bf0d808d83ghsax_refsource_MISCWEB
- www.mend.io/vulnerability-database/CVE-2022-23080ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.