4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / main.go GO
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 := `
<!DOCTYPE html>
<html>
<head>
    <title>Zip Slip Vulnerability Demo</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
        .warning { background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 5px; margin: 20px 0; }
        .danger { background: #f8d7da; border: 1px solid #f5c6cb; padding: 15px; border-radius: 5px; margin: 20px 0; }
        .info { background: #d1ecf1; border: 1px solid #bee5eb; padding: 15px; border-radius: 5px; margin: 20px 0; }
        form { border: 1px solid #ddd; padding: 20px; border-radius: 5px; }
        input[type="file"] { margin: 10px 0; }
        button { background: #dc3545; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; }
        button:hover { background: #c82333; }
    </style>
</head>
<body>
    <h1>Zip Slip Vulnerability Demo</h1>
    
    <div class="danger">
        <h3>WARNING: This is a vulnerable application!</h3>
        <p>This server demonstrates the Zip Slip vulnerability (CVE-2019-10743) using the vulnerable version of mholt/archiver v3.1.1.</p>
        <p><strong>DO NOT use this in production!</strong></p>
    </div>

    <div class="info">
        <h3>How the vulnerability works:</h3>
        <ul>
            <li>The server accepts ZIP files via HTTP POST</li>
            <li>It extracts files using the vulnerable mholt/archiver package</li>
            <li>Malicious ZIP files can contain path traversal filenames (e.g., "../../../etc/passwd")</li>
            <li>These paths can escape the intended extraction directory</li>
            <li>This can lead to arbitrary file overwrites and potential code execution</li>
        </ul>
    </div>

    <div class="warning">
        <h3>To test the vulnerability:</h3>
        <ol>
            <li>Create a ZIP file with a file named "../../../test.txt"</li>
            <li>Upload it using the form below</li>
            <li>Check if the file was extracted outside the intended directory</li>
        </ol>
    </div>

    <form action="/upload" method="post" enctype="multipart/form-data">
        <h3>Upload ZIP File:</h3>
        <input type="file" name="file" accept=".zip" required>
        <br>
        <button type="submit">Upload and Extract</button>
    </form>

    <div class="info">
        <h3>Current directories:</h3>
        <p><strong>Upload directory:</strong> ./uploads</p>
        <p><strong>Extract directory:</strong> ./extracted</p>
    </div>
</body>
</html>`

	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(`
<!DOCTYPE html>
<html>
<head>
    <title>Extraction Complete</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
        .success { background: #d4edda; border: 1px solid #c3e6cb; padding: 15px; border-radius: 5px; margin: 20px 0; }
        .warning { background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 5px; margin: 20px 0; }
        .file-list { background: #f8f9fa; border: 1px solid #dee2e6; padding: 15px; border-radius: 5px; margin: 20px 0; }
        a { color: #007bff; text-decoration: none; }
        a:hover { text-decoration: underline; }
    </style>
</head>
<body>
    <h1>Extraction Complete</h1>
    
    <div class="success">
        <h3>File extracted successfully!</h3>
        <p><strong>Uploaded file:</strong> %s</p>
        <p><strong>Extracted to:</strong> %s</p>
    </div>

    <div class="warning">
        <h3>VULNERABILITY DEMONSTRATION</h3>
        <p>This extraction used the vulnerable mholt/archiver v3.1.1 package.</p>
        <p>If the ZIP contained path traversal filenames (like "../../../file.txt"), they could have been extracted outside the intended directory!</p>
    </div>

    <div class="file-list">
        <h3>Extracted Files:</h3>
        <ul>
`, header.Filename, extractDir)

	for _, file := range files {
		response += fmt.Sprintf("            <li>%s</li>\n", file)
	}

	response += `
        </ul>
    </div>

    <p><a href="/">Back to upload form</a></p>
</body>
</html>`

	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
}