Spaces:
Sleeping
Sleeping
Create main.go
Browse files
main.go
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package main
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"database/sql"
|
| 5 |
+
"fmt"
|
| 6 |
+
"log"
|
| 7 |
+
"net/http"
|
| 8 |
+
"os"
|
| 9 |
+
|
| 10 |
+
_ "github.com/lib/pq"
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
// User represents the database structure we are querying
|
| 14 |
+
type User struct {
|
| 15 |
+
ID int
|
| 16 |
+
Username string
|
| 17 |
+
Flag string
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
// connectDB handles the connection to Supabase/Postgres
|
| 21 |
+
func connectDB() (*sql.DB, error) {
|
| 22 |
+
connStr := os.Getenv("DATABASE_URL")
|
| 23 |
+
if connStr == "" {
|
| 24 |
+
return nil, fmt.Errorf("DATABASE_URL environment variable is not set")
|
| 25 |
+
}
|
| 26 |
+
return sql.Open("postgres", connStr)
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
func main() {
|
| 30 |
+
// 1. Serve static HTML for the login page
|
| 31 |
+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
| 32 |
+
html := `
|
| 33 |
+
<!DOCTYPE html>
|
| 34 |
+
<html>
|
| 35 |
+
<head>
|
| 36 |
+
<title>CTF Login Challenge</title>
|
| 37 |
+
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
| 38 |
+
</head>
|
| 39 |
+
<body class="bg-gray-900 text-white flex items-center justify-center h-screen">
|
| 40 |
+
<div class="bg-gray-800 p-8 rounded shadow-lg w-96">
|
| 41 |
+
<h1 class="text-2xl mb-4 text-center font-bold text-red-500">RESTRICTED AREA</h1>
|
| 42 |
+
<p class="text-gray-400 mb-6 text-center text-sm">Enter admin credentials to retrieve the flag.</p>
|
| 43 |
+
<form action="/login" method="POST" class="space-y-4">
|
| 44 |
+
<div>
|
| 45 |
+
<label class="block text-sm font-medium">Username</label>
|
| 46 |
+
<input type="text" name="username" class="w-full p-2 bg-gray-700 rounded border border-gray-600 focus:border-red-500 outline-none" placeholder="admin">
|
| 47 |
+
</div>
|
| 48 |
+
<div>
|
| 49 |
+
<label class="block text-sm font-medium">Password</label>
|
| 50 |
+
<input type="password" name="password" class="w-full p-2 bg-gray-700 rounded border border-gray-600 focus:border-red-500 outline-none" placeholder="••••••">
|
| 51 |
+
</div>
|
| 52 |
+
<button type="submit" class="w-full bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded transition">ACCESS SYSTEM</button>
|
| 53 |
+
</form>
|
| 54 |
+
<div class="mt-4 text-xs text-gray-600 text-center">System Version: 1.0.0-VULN</div>
|
| 55 |
+
</div>
|
| 56 |
+
</body>
|
| 57 |
+
</html>
|
| 58 |
+
`
|
| 59 |
+
w.Header().Set("Content-Type", "text/html")
|
| 60 |
+
w.Write([]byte(html))
|
| 61 |
+
})
|
| 62 |
+
|
| 63 |
+
// 2. The Vulnerable Login Endpoint
|
| 64 |
+
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
|
| 65 |
+
if r.Method != http.MethodPost {
|
| 66 |
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
| 67 |
+
return
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
username := r.FormValue("username")
|
| 71 |
+
password := r.FormValue("password")
|
| 72 |
+
|
| 73 |
+
db, err := connectDB()
|
| 74 |
+
if err != nil {
|
| 75 |
+
http.Error(w, "Database connection failed", http.StatusInternalServerError)
|
| 76 |
+
log.Println("DB Error:", err)
|
| 77 |
+
return
|
| 78 |
+
}
|
| 79 |
+
defer db.Close()
|
| 80 |
+
|
| 81 |
+
// --- THE VULNERABILITY IS HERE ---
|
| 82 |
+
// We are directly formatting the string with user input.
|
| 83 |
+
// A user can input "' OR '1'='1" to bypass the check.
|
| 84 |
+
query := fmt.Sprintf("SELECT id, username, flag FROM users WHERE username = '%s' AND password = '%s'", username, password)
|
| 85 |
+
|
| 86 |
+
// Log the query to the console (useful for debugging the CTF/seeing the injection)
|
| 87 |
+
log.Printf("Executing Query: %s\n", query)
|
| 88 |
+
|
| 89 |
+
var user User
|
| 90 |
+
// We use QueryRow because we expect one result for a login, but the injection might return the first row (admin)
|
| 91 |
+
err = db.QueryRow(query).Scan(&user.ID, &user.Username, &user.Flag)
|
| 92 |
+
|
| 93 |
+
if err != nil {
|
| 94 |
+
if err == sql.ErrNoRows {
|
| 95 |
+
w.WriteHeader(http.StatusUnauthorized)
|
| 96 |
+
w.Write([]byte(`
|
| 97 |
+
<div style="color: red; text-align: center; margin-top: 50px; font-family: monospace;">
|
| 98 |
+
<h1>ACCESS DENIED</h1>
|
| 99 |
+
<p>Invalid credentials.</p>
|
| 100 |
+
<a href="/" style="color: white;">Try Again</a>
|
| 101 |
+
</div>
|
| 102 |
+
`))
|
| 103 |
+
} else {
|
| 104 |
+
http.Error(w, "Query error: "+err.Error(), http.StatusInternalServerError)
|
| 105 |
+
}
|
| 106 |
+
return
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
// If successful (or injected successfully)
|
| 110 |
+
w.WriteHeader(http.StatusOK)
|
| 111 |
+
fmt.Fprintf(w, `
|
| 112 |
+
<div style="color: #4ade80; text-align: center; margin-top: 50px; font-family: monospace; background: #111; padding: 20px;">
|
| 113 |
+
<h1>ACCESS GRANTED</h1>
|
| 114 |
+
<p>Welcome, %s</p>
|
| 115 |
+
<p style="font-size: 24px; border: 1px dashed #4ade80; display: inline-block; padding: 10px;">FLAG: %s</p>
|
| 116 |
+
</div>
|
| 117 |
+
`, user.Username, user.Flag)
|
| 118 |
+
})
|
| 119 |
+
|
| 120 |
+
// Hugging Face Spaces usually serves on port 7860
|
| 121 |
+
port := os.Getenv("PORT")
|
| 122 |
+
if port == "" {
|
| 123 |
+
port = "7860"
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
log.Printf("CTF Challenge listening on port %s", port)
|
| 127 |
+
log.Fatal(http.ListenAndServe(":"+port, nil))
|
| 128 |
+
}
|