FCSC 2023 - ENISA Flag Store 1/2
Table of Contents
In this article, we will explore the step-by-step walkthrough of the ‘ENISA Flag Store 1/2’ challenge presented at FCSC 2023.
Challenge Overview
We have the following source code, which is the challenge’s web application. Due to the length of this code, you can download it here .
The goal of the challenge is to steal a flag from our dear Swiss friends.
Code Analysis
By analyzing the code, we can see several things:
Available Endpoints
We can observe the following endpoints:
mux.HandleFunc("/", endpoint_index)
mux.HandleFunc("/login", endpoint_login)
mux.HandleFunc("/signup", endpoint_signup)
mux.HandleFunc("/logout", endpoint_logout)
mux.HandleFunc("/flags", endpoint_flags)
Registration Error Handling
During account creation on the application, we can encounter several types of errors:
err := RegisterLoginPassword(username, password, country)
if err != nil {
if errors.Is(err, ErrUserAlreadyExist) {
http.Redirect(w, r, "/signup?error=3", http.StatusSeeOther)
} else if errors.Is(err, ErrFieldTooLong) {
http.Redirect(w, r, "/signup?error=4", http.StatusSeeOther)
} else {
http.Redirect(w, r, "/signup?error=5", http.StatusSeeOther)
}
return
}
Error 3: User already exists
Error 4: One of the fields has too many characters
Error 5: Other potential error
SQL Injection Vulnerability
In the getData() function that loads flags from the user’s country in the /flags endpoint:
func getData(user User) (
[]Flag,
error,
) {
var flags []Flag
req := fmt.Sprintf(`SELECT ctf, challenge, flag, points
FROM flags WHERE country = '%s';`, user.Country);
rows, err := db.Query(req);
if err != nil {
return flags, err
}
defer rows.Close()
for rows.Next() {
var flag Flag
err: rows.Scan(&flag.CTF, &flag.Challenge, &flag.Flag, &flag.Points)
if err != nil {
return flags, err
}
flags: append(flags, flag)
}
if err = rows.Err(); err != nil {
return flags, err
}
return flags, nil
}
We immediately recognize a SQL injection, because no data is filtered and is simply added to the query as is.
Exploitation Strategy
I remain focused on the registration part since apart from this place, we cannot really have control over the information we enter to attempt any exploitation.
SQL Injection Payload
We try to inject the country parameter to see the application’s behavior.
A plausible payload comes to mind and it is the following:
fr' OR country != 'fr
The executed query would be the following:
SELECT ctf, challenge, flag, points
FROM flags WHERE country = 'fr' OR country != 'fr';
The SQL query selects the ctf, challenge, flag, and points columns from the flags table for all records, without distinction of the country column value. Using the condition "country = 'fr' OR country != 'fr'" includes all possible rows in the table.
Exploitation with Burp Suite
I decide to open Burp Suite to capture the request we send during account registration:
Figure 0x1 – Registration request
I modify fr in country with our URL-encoded payload:
fr%27%20OR%20country%20!%3D%20%27fr
Result
The account has been successfully created. We just need to log in with the account to realize that the exploitation worked:
Figure 0x2 – Flag
Flag
The flag is FCSC{fad3a47d8ded28565aa2f68f6e2dbc37343881ba67fe39c5999a0102c387c34b}