4.7 KiB
Selective Security
Welcome to the write-up for Selective Security. This is an introductory "web" (Web Exploitation) challenge that demonstrates one of the most critical and pervasive vulnerabilities in web application history: SQL Injection (SQLi).
In this challenge, we are presented with a seemingly secure login portal that separates "standard" users from "administrators." Our mission is to bypass the authentication mechanism and gain access to the restricted Admin Dashboard to retrieve the flag.
1. Initial Reconnaissance
The challenge provides us with a link to an "Internal Blog Portal" and a downloadable archive: selective_security.tar.xz.
When we visit the portal, we are greeted by a login form. We can try logging in with random credentials (e.g., guest/guest), which grants us access as a "Standard User." We see a basic blog feed, but no flag. The challenge description tells us that the "actual administrative features are protected by a strict database verification check." To get the flag, we need to log in as the admin user.
2. Source Code Analysis
Since we are given the source code in selective_security.tar.xz, we can see exactly how the server handles our login attempt. After extracting the archive, we find a single file: main.go.
Looking at the loginHandler function, we see how the application distinguishes between users:
func loginHandler(w http.ResponseWriter, r *http.Request) {
// ...
username := r.FormValue("username")
password := r.FormValue("password")
if username == "admin" {
handleAdminLogin(w, password)
} else {
// Standard users get the fakeUserTmpl (no flag)
data := map[string]string{"Username": username}
renderTemplate(w, fakeUserTmpl, data)
}
}
If we provide the username admin, the application calls handleAdminLogin. This is where the "strict database verification" happens:
func handleAdminLogin(w http.ResponseWriter, password string) {
// Build query
query := fmt.Sprintf("SELECT id FROM users WHERE username = 'admin' AND password = '%s'", password)
log.Println("Executing Query:", query)
var id int
err := db.QueryRow(query).Scan(&id)
if err == nil {
// SUCCESS: The database found a matching record!
data := map[string]string{"Flag": globalFlag}
renderTemplate(w, successTmpl, data)
} else {
// ... handle error ...
}
}
3. The Vulnerability: SQL Injection
The vulnerability lies in how the SQL query is constructed. The application uses fmt.Sprintf to insert our password directly into the query string:
"SELECT id FROM users WHERE username = 'admin' AND password = '%s'"
This is a classic SQL Injection vulnerability. Because the application does not use parameterized queries (placeholders like ?), it treats our input as part of the SQL command itself rather than just data.
4. Developing the Exploit
We don't know the admin's password, but we can use SQL syntax to change the logic of the WHERE clause. Our goal is to make the entire condition evaluate to TRUE, so the database returns a result.
If we enter the following payload as the password:
' OR '1'='1
The final query executed by the database becomes:
SELECT id FROM users WHERE username = 'admin' AND password = '' OR '1'='1'
Breaking down the logic:
username = 'admin' AND password = '': This part is evaluated first (due to operator precedence) and is likely False.OR '1'='1': This part is always True.False OR Trueresults in True.
The database ignores the incorrect password check and returns the admin's ID. The Go code sees that a row was returned (err == nil) and grants us access to the dashboard.
5. Exploitation
- Navigate to the login page.
- Enter Username:
admin - Enter Password:
' OR '1'='1 - Click Login.
The "Administrator Access Granted" page appears, displaying the flag.
Flag: {flag:Sql_Inj3ct10n_Is_Ez_Pz_Read_From_File}
Lessons Learned
This challenge highlights why you should never trust user input when building database queries. Even a single vulnerability like this can give an attacker full access to sensitive data or administrative accounts.
To prevent this, always use parameterized queries (also known as prepared statements). In Go, the secure way to write this query would be:
// SECURE VERSION
db.QueryRow("SELECT id FROM users WHERE username = 'admin' AND password = ?", password)
By using the ? placeholder, the database driver ensures that the input is treated strictly as a string, making it impossible for the user to "break out" and inject SQL commands.
Happy Hacking!