A recent vulnerability discovery, identified as CVE-2022-41725, has been found in the popular Go programming language. The vulnerability makes it possible to trigger a denial of service (DoS) attack by consuming excessive system resources in net/http and mime/multipart packages.

This post is aimed at providing an easy-to-understand explanation of the vulnerability, its impact, and potential remediation strategies. We'll also include links to original references and potential fixes.

Vulnerability Details

The vulnerability exists in the handling of multipart form parsing with mime/multipart.Reader.ReadForm. It can lead to unbounded memory and disk consumption, since the function doesn't properly account for various forms of memory overhead, allowing a carefully crafted form to consume much more memory than intended.

Additionally, this vulnerability affects form parsing in the net/http package through the Request methods FormFile, FormValue, ParseMultipartForm, and PostFormValue.

As per the documentation, ReadForm accepts a maxMemory parameter, and stores "up to maxMemory bytes +10MB (reserved for non-file parts) in memory". The issue lies in the fact that the unconfigurable 10MB reservation can potentially cause a DoS attack vector by itself.

Moreover, ReadForm didn't have any limitations on the number of disk files created, which could cause a relatively small request to create a significant number of temporary files.

The Fix

The fix for this vulnerability involves proper accounting for memory overhead and enforcing strict memory usage limits. With the fix, ReadForm should now stay within its documented limit of 10MB + maxMemory bytes of memory consumption.

Another change brought by the fix is that ReadForm now only creates one on-disk temporary file, merging multiple form parts into a single file. This behavior can be changed back to using distinct files for each form part by setting the environment variable GODEBUG=multipartfiles=distinct.

Note: Users should be aware that multipart.ReadForm and the http.Request methods that call it do not limit the disk space used by temporary files. It is advised to limit the size of form data using http.MaxBytesReader.

Code Snippet

Here's a code snippet showing the correct way of handling form file uploads using the fixed net/http package:

package main

import (
	"io"
	"net/http"
	"os"
)

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	r.Body = http.MaxBytesReader(w, r.Body, 10<<20) // Limit the request body size
	err := r.ParseMultipartForm(32 << 20)           // Set maxMemory to 32MB
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	file, _, err := r.FormFile("file")
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	defer file.Close()

	outFile, err := os.Create("uploaded_file")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer outFile.Close()

	_, err = io.Copy(outFile, file)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func main() {
	http.HandleFunc("/upload", uploadHandler)
	http.ListenAndServe(":808", nil)
}

References

- Original Go Issue: golang/go#49707
- Go Change (Fix): golang/go#45729
- CVE-2022-41725: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-41725

Conclusion

CVE-2022-41725 is a denial of service vulnerability caused by excessive resource consumption in Go's net/http and mime/multipart packages. The fix provided by the Go team stays within the documented memory limit and ensures proper memory accounting, making it essential to apply the patch to mitigate potential DoS attacks.

It is also recommended to limit the size of form data with http.MaxBytesReader to avoid excessive disk usage by temporary files.

Timeline

Published on: 02/28/2023 18:15:00 UTC
Last modified on: 03/10/2023 04:58:00 UTC