August 7, 2026
Finding SQL Injection in a Government Website
Finding SQL Injection

By Ahmad Farel Pratama
2 min read
While testing a government website, I came across an interesting behavior on a page that looked pretty ordinary.
There was an id parameter used to load a gallery detail page. I started testing it with a few basic SQLi techniques, and eventually confirmed that the parameter was vulnerable to UNION-based SQL Injection.
The target has been redacted in this write-up.
The Target
The vulnerable endpoint looked like this:
https://[REDACTED]/index.php?p=example-example&id=9&example=examplehttps://[REDACTED]/index.php?p=example-example&id=9&example=exampleThe parameter I focused on was:
id=9id=91. Initial Test
The normal request returned the expected page:
https://[REDACTED]/index.php?p=example-example&id=9&example=examplehttps://[REDACTED]/index.php?p=example-example&id=9&example=exampleI then appended a single quote to the id parameter:
https://[REDACTED]/index.php?p=example-example&id=9’&example=examplehttps://[REDACTED]/index.php?p=example-example&id=9’&example=exampleThe response changed and the page no l. Testing SQL Comment Syntaxonger behaved normally. This was my first indication that the parameter might be reaching an SQL query without being handled properly.
2. Testing SQL Comment Syntax
Next, I tried:
https://[REDACTED]/index.php?p=example-example&id=9’ — +-&example=examplehttps://[REDACTED]/index.php?p=example-example&id=9’ — +-&example=exampleThe page returned to its previous behavior. The idea here was to use the SQL comment syntax to terminate the injected portion and comment out the remainder of the original query.
The different responses between these requests gave me enough confidence to continue testing for SQL Injection.
3. Determining the Column Count
I used ORDER BY to investigate the number of columns returned by the underlying query.
First:
id=9’ORDER+BY+15--+-id=9’ORDER+BY+15--+-This resulted in an error.
I then tried:
id=9’ORDER+BY+10--+-id=9’ORDER+BY+10--+-This returned normally.
At this point, I knew that ORDER BY 15 was exceeding the usable column range, while ORDER BY 10 was accepted.
So rather than claiming that the query definitely had exactly 10 columns, the safer conclusion is that the query appeared to expose 10 or fewer usable columns based on these tests.
4. UNION SELECT
Now I wanted to see whether I could inject values into the query result.
I tested:
id=9’UNION+SELECT+,1,2,3,4,5,6,7,8,9,10--+-id=9’UNION+SELECT+,1,2,3,4,5,6,7,8,9,10--+-The interesting part was that, number 3 appeared in the response. This meant that values supplied through the UNION SELECT were reaching the application's output.
In other words, the application was taking part of the attacker-controlled UNION result and rendering it in the page. This confirmed that the injection was UNION-based, and that columns 3 was usable as reflected output positions.
5. Viewing database tables
After identifying a reflected column, I continued testing to determine how far the SQL Injection could interact with the database.
I then performed limited database enumeration to identify what information was accessible from the backend.
The testing showed that I was able to retrieve information about the database structure, including table and column names.
Potential Impact
The SQL Injection allows an unauthenticated attacker to access sensitive data from the backend database, including identity card information and other personal information belonging to residents of [REDACTED].
Alright
I reported this vulnerability to the relevant CSIRT team on August 12, 2025.
On August 13, 2025, I received a response that the report would be reviewed internally.
On September 9, 2025, I received an appreciation certificate for the finding, recognizing my contribution to improving the security of their systems.
I reported this vulnerability through responsible disclosure and intentionally redacted the target and sensitive database information from this write-up.
Keep learning. Keep hacking ethically.