package main import ( "fmt" "io" "log" "net/http" "os" "path/filepath" "github.com/gorilla/mux" "github.com/mholt/archiver" ) const ( uploadDir = "./uploads" extractDir = "./extracted" ) func main() { // Create directories if they don't exist os.MkdirAll(uploadDir, 0755) os.MkdirAll(extractDir, 0755) r := mux.NewRouter() // Serve static files (HTML form) r.HandleFunc("/", serveHomePage).Methods("GET") // Handle file upload and extraction r.HandleFunc("/upload", handleUpload).Methods("POST") fmt.Println("VULNERABLE Zip Slip Demo Server") fmt.Println("Upload directory:", uploadDir) fmt.Println("Extract directory:", extractDir) fmt.Println("Server running on http://localhost:8080") fmt.Println("This server is vulnerable to Zip Slip attacks!") fmt.Println("") log.Fatal(http.ListenAndServe(":8080", r)) } func serveHomePage(w http.ResponseWriter, r *http.Request) { html := ` Zip Slip Vulnerability Demo

Zip Slip Vulnerability Demo

WARNING: This is a vulnerable application!

This server demonstrates the Zip Slip vulnerability (CVE-2019-10743) using the vulnerable version of mholt/archiver v3.1.1.

DO NOT use this in production!

How the vulnerability works:

To test the vulnerability:

  1. Create a ZIP file with a file named "../../../test.txt"
  2. Upload it using the form below
  3. Check if the file was extracted outside the intended directory

Upload ZIP File:


Current directories:

Upload directory: ./uploads

Extract directory: ./extracted

` w.Header().Set("Content-Type", "text/html") w.Write([]byte(html)) } func handleUpload(w http.ResponseWriter, r *http.Request) { // Parse multipart form err := r.ParseMultipartForm(32 << 20) // 32MB max if err != nil { http.Error(w, "Failed to parse form: "+err.Error(), http.StatusBadRequest) return } // Get uploaded file file, header, err := r.FormFile("file") if err != nil { http.Error(w, "Failed to get uploaded file: "+err.Error(), http.StatusBadRequest) return } defer file.Close() // Check if it's a ZIP file if filepath.Ext(header.Filename) != ".zip" { http.Error(w, "Only ZIP files are allowed", http.StatusBadRequest) return } // Save uploaded file uploadPath := filepath.Join(uploadDir, header.Filename) uploadFile, err := os.Create(uploadPath) if err != nil { http.Error(w, "Failed to save uploaded file: "+err.Error(), http.StatusInternalServerError) return } defer uploadFile.Close() _, err = io.Copy(uploadFile, file) if err != nil { http.Error(w, "Failed to save uploaded file: "+err.Error(), http.StatusInternalServerError) return } // VULNERABLE: Extract the ZIP file using the vulnerable archiver package fmt.Printf("Extracting %s to %s\n", uploadPath, extractDir) // This is where the vulnerability occurs - the archiver doesn't validate paths err = archiver.Unarchive(uploadPath, extractDir) if err != nil { http.Error(w, "Failed to extract archive: "+err.Error(), http.StatusInternalServerError) return } // List extracted files files, err := listFiles(extractDir) if err != nil { http.Error(w, "Failed to list extracted files: "+err.Error(), http.StatusInternalServerError) return } // Create response response := fmt.Sprintf(` Extraction Complete

Extraction Complete

File extracted successfully!

Uploaded file: %s

Extracted to: %s

VULNERABILITY DEMONSTRATION

This extraction used the vulnerable mholt/archiver v3.1.1 package.

If the ZIP contained path traversal filenames (like "../../../file.txt"), they could have been extracted outside the intended directory!

Extracted Files:

Back to upload form

` w.Header().Set("Content-Type", "text/html") w.Write([]byte(response)) } func listFiles(dir string) ([]string, error) { var files []string err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // Skip the root directory itself if path == dir { return nil } // Get relative path relPath, err := filepath.Rel(dir, path) if err != nil { return err } if info.IsDir() { files = append(files, relPath+"/") } else { files = append(files, relPath) } return nil }) return files, err }