As part of the incoming admission procedure of my Erasmus destination institution, Barcelona School of Informatics, an application form had to be filled with information about me and the courses I planned on attending.
Therefore, there is a section of the form where students can choose from a drop-down list the courses they want to attend. When the "Add" button is pressed, an asynchronous request is sent to the server and the course gets added to the table.
Unfortunately for me, I noticed one of my courses was not listed because it was not being offered for exchange students. So I replaced the HTML of the drop-down in order to include that specific course that was missing and was able to add it to my list of subjects, since there was no further server-side validation taking place. Obviously, I ended up deleting it, because I did not want someone to later tell me I could not enroll in that course.
Finding the vulnerability
Eventually, I tested the system for SQL Injection by adding an apostrophe to the initials of the subject name:
Indeed, the system was vulnerable to SQL Injection attacks. The server receives the list of courses selected and replies with the HTML code of the new drop-down, without the already selected courses. The client sends the list of currently selected courses as a string made of their initials separated by commas (e.g.
AGT,AM,AMMM), but that string was not being properly escaped before being sent to the database.
Exploiting the vulnerability
I am not very experienced with this kind of attacks, but one way of performing them (after finding the vulnerability) is to use the UNION operator to join the already existing SELECT statement with a new SELECT statement written by the exploiter. The challenge is to craft a statement that is similar to the original one, since it must contain the same amount of columns and with the same type.
If the number of columns in my test string doesn’t match the number of columns in the original statement, the database will throw the following error:
ORA-01789: query block has incorrect number of result columns.
Therefore, one way of finding the right amount of columns and their types is by trial and error (but there are faster methods). Fortunately, in this specific case, the whole query was being output to the user, which meant that I could easily count the number of columns and find their types. The original query returned a total of 6 columns with types where the 1st and 4th were numbers and the rest were strings.
Now that I had information about the original query, I proceeded with my attempt to do an SQL Injection attack taking advantage of the UNION operator. This was my input string:
A' UNION SELECT a1.codi_assig, a1.sigles, a1.nom, a1.credits, a1.titulacio, a2.quadri FROM rriiassig--
My objective with this string was to replicate the original query, selecting the exact same columns, just to test if everything would work properly. The last two hyphens are used to start an inline comment, which means that the rest of the original query that follows them would be ignored. However, the database replied with the following error:
ORA-00923 FROM keyword not found where expected
After analyzing the query, I noticed that my string was being changed before being sent to the database:
A' UNION SELECT a1.codi_assig' AND sigles!=' a1.sigles' AND sigles!=' a1.nom' AND sigles!=' a1.credits' AND sigles!=' a1.titulacio' AND sigles!=' a2.quadri FROM rriiassig--
Wherever my input had a comma character, it would be replaced by the string
' AND sigles !='. As mentioned above, the normal user input would be a list of subjects separated by commas, so the idea of replacing the commas by such string was to select only the subjects with sigles (initials) different to the ones in the input list. The result of this selection would then be the content of the new drop-down list, where the user could continue adding other courses.
This meant that my string could not contain any commas, as these were being replaced by a different text.
But how to do a SQL SELECT query for 6 columns without using a comma to separate the different fields? This is possible by cross joining 6 different subqueries where each of those subqueries returns only one column, such as:
A' UNION SELECT * FROM (SELECT 1 FROM rriiassig) CROSS JOIN (SELECT 'aaa' FROM rriiassig) CROSS JOIN (SELECT 'bbb' FROM rriiassig) CROSS JOIN (SELECT 4 FROM rriiassig) CROSS JOIN (SELECT 'ccc' FROM rriiassig) CROSS JOIN (SELECT 'Q1' FROM rriiassig) --
I ran this query and… Oops, the server went down for a couple minutes due to the huge size of my query (cross joining 6 tables). I should have filtered my query by applying a LIMIT to one or more fields. That’s when I stopped my investigation and emailed the institution with information on the security exploit, so I still don’t know if the vulnerability gave me access to the database of the international page only or if that database was also used in different locations for different purposes. They answered the day after:
[…] We’ve reproduced the bug and looking at the source code, all the application form application uses parametrized SQL except the subject part! […]"
The issue was then fixed in less than a week.