CVE-2022-25850: Server-Side Request Forgery (SSRF) (CWE-918)
value: The package github.com/hoppscotch/proxyscotch before 1.0.0 are vulnerable to Server-side Request Forgery (SSRF) when interceptor mode is set to proxy. It occurs when an HTTP request is made by a backend server to an untrusted URL submitted by a user. It leads to a leakage of sensitive information from the server.
CWE description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
CVSS v3 base score: 7.5
Published: 2022-05-01T16:15Z
Repository: https://github.com/hoppscotch/proxyscotch
Language: Go
Files changed: libproxy/proxy.go, server/server.go
Commit date: 2022-01-24 15:52:33 +0530
Commit message: feat: ability to have a blacklist of target urls for proxy to make calls to

Diff: diff --git a/libproxy/proxy.go b/libproxy/proxy.go
index 7dab8c4..c9fb0fb 100644
--- a/libproxy/proxy.go
+++ b/libproxy/proxy.go
@@ -22,10 +22,11 @@ type statusChangeFunction func(status string, isListening bool)
 var (
 	accessToken        string
 	sessionFingerprint string
 	allowedOrigins     []string
 	bannedOutputs      []string
+	bannedDests        []string
 )
 
 type Request struct {
 	AccessToken string
 	WantsBinary bool
@@ -47,10 +48,20 @@ type Response struct {
 	Data       string            `json:"data"`
 	StatusText string            `json:"statusText"`
 	Headers    map[string]string `json:"headers"`
 }
 
+func isAllowedDest(dest string) bool {
+	for _, b := range bannedDests {
+		if b == dest {
+			return false
+		}
+	}
+
+	return true
+}
+
 func isAllowedOrigin(origin string) bool {
 	if allowedOrigins[0] == "*" {
 		return true
 	}
 
@@ -66,17 +77,23 @@ func isAllowedOrigin(origin string) bool {
 func Initialize(
 	initialAccessToken string,
 	proxyURL string,
 	initialAllowedOrigins string,
 	initialBannedOutputs string,
+	initialBannedDests string,
 	onStatusChange statusChangeFunction,
 	withSSL bool,
 	finished chan bool,
 ) {
 	if initialBannedOutputs != "" {
 		bannedOutputs = strings.Split(initialBannedOutputs, ",")
 	}
+	if initialBannedDests != "" {
+		bannedDests = strings.Split(initialBannedDests, ",")
+	} else {
+		bannedDests = []string{}
+	}
 	allowedOrigins = strings.Split(initialAllowedOrigins, ",")
 	accessToken = initialAccessToken
 	sessionFingerprint = uuid.New().String()
 	log.Println("Starting proxy server...")
 
@@ -207,10 +224,17 @@ func proxyHandler(response http.ResponseWriter, request *http.Request) {
 	var proxyRequest http.Request
 	proxyRequest.Header = make(http.Header)
 	proxyRequest.Method = requestData.Method
 	proxyRequest.URL, _ = url.Parse(requestData.Url)
 
+	// Block requests to illegal destinations
+	if !isAllowedDest(proxyRequest.URL.Hostname()) {
+		log.Print("A request to a banned destination was made.")
+		_, _ = fmt.Fprintln(response, "{\"success\": false, \"data\":{\"message\":\"(Proxy Error) Request cannot be to this destination.\"}}")
+		return
+	}
+
 	var params = proxyRequest.URL.Query()
 
 	for k, v := range requestData.Params {
 		params.Set(k, v)
 	}
diff --git a/server/server.go b/server/server.go
index 2109e3c..80b66e1 100644
--- a/server/server.go
+++ b/server/server.go
@@ -10,14 +10,16 @@ import (
 func main() {
 	hostPtr := flag.String("host", "localhost:9159", "the hostname that the server should listen on.")
 	tokenPtr := flag.String("token", "", "the Proxy Access Token used to restrict access to the server.")
 	allowedOriginsPtr := flag.String("allowed-origins", "*", "a comma separated list of allowed origins.")
 	bannedOutputsPtr := flag.String("banned-outputs", "", "a comma separated list of banned outputs.")
+	bannedDestsPtr := flag.String("banned-dests", "", "a comma separated list of banned proxy destinations.")
+
 	flag.Parse()
 
 	finished := make(chan bool)
-	libproxy.Initialize(*tokenPtr, *hostPtr, *allowedOriginsPtr, *bannedOutputsPtr, onProxyStateChangeServer, false, finished)
+	libproxy.Initialize(*tokenPtr, *hostPtr, *allowedOriginsPtr, *bannedOutputsPtr, *bannedDestsPtr, onProxyStateChangeServer, false, finished)
 
 	<-finished
 }
 
 func onProxyStateChangeServer(status string, isListening bool) {

Vulnerable code: 	libproxy.Initialize(*tokenPtr, *hostPtr, *allowedOriginsPtr, *bannedOutputsPtr, onProxyStateChangeServer, false, finished)
Fixed code: 	bannedDests        []string
func isAllowedDest(dest string) bool {
	for _, b := range bannedDests {
		if b == dest {
			return false
		}
	}

	return true
}

	initialBannedDests string,
	if initialBannedDests != "" {
		bannedDests = strings.Split(initialBannedDests, ",")
	} else {
		bannedDests = []string{}
	}
	// Block requests to illegal destinations
	if !isAllowedDest(proxyRequest.URL.Hostname()) {
		log.Print("A request to a banned destination was made.")
		_, _ = fmt.Fprintln(response, "{\"success\": false, \"data\":{\"message\":\"(Proxy Error) Request cannot be to this destination.\"}}")
		return
	}

	bannedDestsPtr := flag.String("banned-dests", "", "a comma separated list of banned proxy destinations.")

	libproxy.Initialize(*tokenPtr, *hostPtr, *allowedOriginsPtr, *bannedOutputsPtr, *bannedDestsPtr, onProxyStateChangeServer, false, finished)