lifedebugger commited on
Commit
12a70ac
·
verified ·
1 Parent(s): dc15350

Update main.go

Browse files
Files changed (1) hide show
  1. main.go +63 -33
main.go CHANGED
@@ -3,21 +3,21 @@ package main
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 == "" {
@@ -27,31 +27,47 @@ func connectDB() (*sql.DB, error) {
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>
@@ -60,7 +76,7 @@ func main() {
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)
@@ -70,6 +86,29 @@ func main() {
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)
@@ -78,51 +117,42 @@ func main() {
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
  }
 
3
  import (
4
  "database/sql"
5
  "fmt"
6
+ "io/ioutil"
7
  "log"
8
  "net/http"
9
  "os"
10
+ "strings"
11
 
12
  _ "github.com/lib/pq"
13
  )
14
 
 
15
  type User struct {
16
  ID int
17
  Username string
18
  Flag string
19
  }
20
 
 
21
  func connectDB() (*sql.DB, error) {
22
  connStr := os.Getenv("DATABASE_URL")
23
  if connStr == "" {
 
27
  }
28
 
29
  func main() {
30
+ // 1. Endpoint to View Source Code (Crucial for the CTF)
31
+ http.HandleFunc("/source", func(w http.ResponseWriter, r *http.Request) {
32
+ content, err := ioutil.ReadFile("main.go")
33
+ if err != nil {
34
+ http.Error(w, "Could not read source code.", http.StatusInternalServerError)
35
+ return
36
+ }
37
+ w.Header().Set("Content-Type", "text/plain")
38
+ w.Write(content)
39
+ })
40
+
41
+ // 2. Login Page
42
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
43
  html := `
44
  <!DOCTYPE html>
45
  <html>
46
  <head>
47
+ <title>Secure Login System v2.0</title>
48
  <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
49
  </head>
50
+ <body class="bg-gray-900 text-white flex flex-col items-center justify-center h-screen">
51
+ <div class="bg-gray-800 p-8 rounded shadow-lg w-96 border-t-4 border-blue-500">
52
+ <h1 class="text-2xl mb-2 text-center font-bold text-blue-400">SECURE GATEWAY</h1>
53
+ <p class="text-gray-400 mb-6 text-center text-xs">Protected by GoWAF™ technology</p>
54
+
55
  <form action="/login" method="POST" class="space-y-4">
56
  <div>
57
+ <label class="block text-sm font-medium text-gray-300">Username</label>
58
+ <input type="text" name="username" class="w-full p-2 bg-gray-700 rounded border border-gray-600 focus:border-blue-500 outline-none" placeholder="Enter username">
59
  </div>
60
  <div>
61
+ <label class="block text-sm font-medium text-gray-300">Password</label>
62
+ <input type="password" name="password" class="w-full p-2 bg-gray-700 rounded border border-gray-600 focus:border-blue-500 outline-none" placeholder="••••••">
63
  </div>
64
+ <button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition">AUTHENTICATE</button>
65
  </form>
66
+
67
+ <div class="mt-6 border-t border-gray-700 pt-4 text-center">
68
+ <p class="text-xs text-gray-500">Developers only:</p>
69
+ <a href="/source" class="text-sm text-blue-400 hover:underline">View Source Code</a>
70
+ </div>
71
  </div>
72
  </body>
73
  </html>
 
76
  w.Write([]byte(html))
77
  })
78
 
79
+ // 3. The Hardened (but still vulnerable) Login Endpoint
80
  http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
81
  if r.Method != http.MethodPost {
82
  http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
 
86
  username := r.FormValue("username")
87
  password := r.FormValue("password")
88
 
89
+ // --- CHALLENGE PART 1: HEADER CHECK ---
90
+ // The student must read the code to realize they need to change their User-Agent.
91
+ ua := r.Header.Get("User-Agent")
92
+ if ua != "Secure-CTF-Browser/1.0" {
93
+ http.Error(w, "Security Alert: Browser not authorized. Please use 'Secure-CTF-Browser/1.0'", http.StatusForbidden)
94
+ return
95
+ }
96
+
97
+ // --- CHALLENGE PART 2: THE TRAP ---
98
+ // We explicitly block "admin" to prevent simple brute forcing.
99
+ // They must use SQL injection to select the admin row *without* sending "admin" as the username string.
100
+ if username == "admin" {
101
+ http.Error(w, "Direct login as 'admin' is disabled for security reasons.", http.StatusForbidden)
102
+ return
103
+ }
104
+
105
+ // --- CHALLENGE PART 3: THE WAF (Input Filtering) ---
106
+ // We block spaces. The student must use SQL comments (/**/) or tabs to bypass this.
107
+ if strings.Contains(username, " ") || strings.Contains(password, " ") {
108
+ http.Error(w, "WAF Detection: Spaces are not allowed in input fields.", http.StatusBadRequest)
109
+ return
110
+ }
111
+
112
  db, err := connectDB()
113
  if err != nil {
114
  http.Error(w, "Database connection failed", http.StatusInternalServerError)
 
117
  }
118
  defer db.Close()
119
 
120
+ // --- THE VULNERABILITY ---
121
+ // Still using fmt.Sprintf, but now the input is heavily restricted by the checks above.
 
122
  query := fmt.Sprintf("SELECT id, username, flag FROM users WHERE username = '%s' AND password = '%s'", username, password)
123
 
 
124
  log.Printf("Executing Query: %s\n", query)
125
 
126
  var user User
127
+ // QueryRow returns the first matching row. If they inject ' OR '1'='1, it will return the first user in the DB (Admin).
128
  err = db.QueryRow(query).Scan(&user.ID, &user.Username, &user.Flag)
129
 
130
  if err != nil {
131
  if err == sql.ErrNoRows {
132
  w.WriteHeader(http.StatusUnauthorized)
133
+ w.Write([]byte("Invalid credentials."))
 
 
 
 
 
 
134
  } else {
135
  http.Error(w, "Query error: "+err.Error(), http.StatusInternalServerError)
136
  }
137
  return
138
  }
139
 
140
+ // Success
141
  w.WriteHeader(http.StatusOK)
142
  fmt.Fprintf(w, `
143
+ <div style="font-family: monospace; background: #111; color: #4ade80; padding: 20px; text-align: center;">
144
+ <h1>SYSTEM BREACHED</h1>
145
+ <p>User: %s</p>
146
  <p style="font-size: 24px; border: 1px dashed #4ade80; display: inline-block; padding: 10px;">FLAG: %s</p>
147
  </div>
148
  `, user.Username, user.Flag)
149
  })
150
 
 
151
  port := os.Getenv("PORT")
152
  if port == "" {
153
  port = "7860"
154
  }
155
 
156
+ log.Printf("CTF Hard Mode listening on port %s", port)
157
  log.Fatal(http.ListenAndServe(":"+port, nil))
158
  }