Convert complete result set into String within SQL (HSQLDB) - java

As part of a Secure Code course I am given a vulnerable Java system, where queries are built up using string concatintion, and so on. Following is the respective code line that I have to SQL inject to retrieve log-in data:
String comment;
int articleID;
//...
stmt.executeUpdate("INSERT INTO comment (text, article_id) VALUES ('" + comment + "', " + articleId + ")");
comment can be set to any text, it is just read plain-text from a textbox. Therefore, I thought about setting the textbox text to an SELECT statement, reading the complete login data (table user has columns login and password):
' || (SELECT login || password FROM user) || '
which in overall should result in the query
INSERT INTO comment (text, article_id) VALUES ('' || (SELECT login || password FROM user) || '', 4)
yet, this results in
org.hsqldb.HsqlException: cardinality violation
Question:
I assume this is because (SELECT login || password FROM user) is not a single string, but a result set, and may therefore not be concatenated using ||.
Is it possible to convert the complete result set to one string in a way it can be used in this SQL injection scenario (in standard SQL / SQL that works on HSQLDB)?

You are correct, as there is obviously more than one user and multiple rows are returned from the SELECT.
You can add LIMIT 1 to the SELECT to get the first row. You can also create a string array from the inner SELECT. See the guide.
In HSQLDB each database user has separate access rights to the tables. In a real deployment the user that inserts comments will not even see the existence of the table that contains the password, let alone select from it.

Related

BLOCKER - Change this code to not construct SQL queries directly from user-controlled data

Sonarqube is giving me this error:
[BLOCKER] Change this code to not construct SQL queries directly from user-controlled data
Here is my code:
String countSQL;
countSQL = (String.format("SELECT count(*) as total FROM ltid_owner.enty %s",additionalWhereClauses));
jdbcTemplateTMI.queryForObject(countSQL, Integer.class);
In the above code additionalWhereClauses could be something like this shown below which I am building on the fly when the user clicks on the grid to perform filtering on different columns:
additionalWhereClauses = where UPPER(enty_num) like '003%'
Can you please let me know how to resolve this issue?
Your code combines strings into SQL statements. If any of these strings contains user provided input, an attacker can sneak in code to trigger an SQL injection attack and possibly run arbitrary code on your computer (obligatory Bobby Tables reference).
Simple example:
String sql = "SELECT * FROM users WHERE name = '" + name + "' AND password = '" + password + "'";
If I enter ' OR 1=1 -- for the name (and "..." for the password, but that doesn't really matter anymore) the code becomes a valid SQL statement:
SELECT *
FROM users
WHERE name = '' OR 1=1 -- ' AND password = '...'
but the user name / password check is completely disabled.
To avoid this, use prepared statements. They build the SQL command in a way that SQL injection is impossible.
Maybe this never happens in your code as you don't accept user input, but Sonar doesn't know this (and human reviewers won't either). I'd always use prepared statements. Just because your code only passed column headers from a frontend, doesn't mean an attacker cannot manually call your web service endpoints and pass whatever they want, it your code runs as an HTTP endpoint.

How to replace a string in a string with integer type in java?

I have a requirement. The technology is quite old doesn't support spring at all . It is pure java application with jdbc connection.
Requirement is :
Suppose
select * from employee where empid = <<empid>> and designation = 'Doctor'
I am trying to replace <> with actual int value in java . How I can do it ?
String query = "select * from employee where empid = <<empid>> and designation = 'Doctor'";
if(query.contains("<<empid>>"))
/// Here I want to replace <<empid>> with actual int value in java
Any leads will be helpful
The code you didn't paste, that actually executes the SQL is either [A] a massive security leak that needs serious rewrites, or [B] is using PreparedStatement.
Here's the problem: SQL injection. Creating the SQL string by mixing a template or a bunch of string constants together with a bunch of user input is a security leak. For example, if you try to make SELECT * FROM users WHERE email = 'foo#bar.com' by e.g. String sql = "SELECT * FROM users WHERE email = '" + email + "'";, the problem is, what if the user puts in the web form, in the 'email' field: whatever#foo.com'; DROP TABLE users CASCADE; EXEC 'FORMAT C: /y /force'; --? Then the SQL becomes:
SELECT * FROM users WHERE email = 'whatever#foo.com'; DROP TABLE users CASCADE; EXEC 'FORMAT C: /y /force'; --';
That is legal SQL and you really, really, really don't want your DB engine to execute it.
Each DB engine has its own ideas on what's actually legal, and may do crazy things such as treating curly quotes as real quotes, etc. So, there is no feasible blacklist or whitelist technology you can think of that will properly cover all the bases: You need to ask your DB engine to do this for you, you can't fix this hole yourself.
Java supports this, via java.sql.PreparedStatement. You instead always pass a fully constant SQL string to the engine, and then fill in the blanks, so to speak:
PreparedStatement ps = con.prepareStatement("SELECT * FROM users WHERE email = ?");
ps.setString(1, "foo#whatever.com");
ps.query();
That's how you do it (and add try-with-resources just like you should already be doing here; statements and resultsets are resources you must always close). Even if you call .setString(1, "foo#whatever.com'; DROP TABLE users CASCADE; --"), then it'll simply look for a row in the database that has that mouthful in the email field. It will not delete the entire users table. Security hole eliminated (and this is the only feasible way to eliminate it).
So, check out that code. Is it using preparedstatement? In that case, well, one way or another that code needs to be calling:
ps.setInt(1, 999);
Where ps is the PreparedStatement object created with connection.prepareStatement(...) where ... is either an SQL constant or at least your input string where the <<empid>> was replaced with a question mark and never with any string input from an untrusted source. The 1 in ps.setInt(1, 999) is the position of the question mark (1 = the first question becomes 999), and the 999 is your actual number. It may look like:
if (input instanceof String) {
ps.setString(idx++, (String) input);
} else if (input instanceof Integer) {
ps.setInt(idx++, ((Integer) input).intValue());
} ...
etcetera. If you don't see that, find the setInt invoke and figure out how to get there. If you don't see any setInt, then what you want is not possible without making some updates to this code.
If you don't even see PreparedStatement anywhere in the code, oh dear! Take that server offline right now, research if a security leak has occurred, if this server stored european data you have 72 hours to notify all users if it has or you can't reasonably figure out e.g. by inspecting logs that it hasn't, or you're in breach of the GDPR. Then rewrite that part using PreparedStatement to solve the problem.

Hibernate HQL injection example

Yesterday searching through some repositories on Github I found some interesting stuff: one Java project (I won't mention the name of the repository but I've already notified the owner of it) contained a bad handling of HQL queries which could lead to SQL/HQL injections. The code was the following: (note that username and password come from the user)
Query query = session.createQuery("from Client where username = '" + username + "'");
List clients = query.list();
Client client = (Client) clients.get(0);
if (!validPassword(client.getPassword(), password)) {
return false;
}
//client is authenticated....
I think it is obvious that this query is injectable. I don't really know how this vulnerable query could be exploited because even if we inject the username, the
password is still checked. The database used was MySql (if it helps).
So my question is: how could this be exploited?
Even though HQL is more restrictive than SQL for injections, it can still be exploited.
Some example injections are explained at https://blog.h3xstream.com/2014/02/hql-for-pentesters.html
A similar question to this one has been asked already before at https://security.stackexchange.com/questions/24265/hql-injection-example
The answer to this question explains how characters of a password (hash) could be scanned. e.g. if for an Oracle database the value of username is:
admin' AND SUBSTR(password, 0, 1) = 'A
Then if
the first character of the password (hash) is not 'A' -> the clients List is empty and the clients.get(0) method call throws an IndexOutOfBoundsException
the first character of the password (hash) is 'A', but the provided password is false -> the user is not authenticated
the first character of the password (hash) is 'A' and the provided password is correct -> the user is authenticated
A hacker can repeat the query for each x and z in
SUBSTR(password, x, x + 1) = z
in the query above until the outcome is always case 2. where the user is not authenticated. This way he can find out the password hash for the user admin and may be able to crack his password.
Other exploits are possible, I am not going to list all of them...
Yes so...once you had start hibernate session, You can fetch data using query. Now you have written query for Client table.
For ex,
username = "ABC"
1) Your query from Client where username = 'ABC' will fetch data from Client whoes username is exact ABC.
If it found multiple same Username, it also return all.
2) It is going to store in list. 0 or more record will store in list.
3) Then whatever records came, it fetch only first record using
Client client = (Client) clients.get(0);
4) it check with client object record password with your expected password that may be suppose to save in some variable via method calling.
5) if it won't match then it return with false boolean flag otherwise code will go ahead with authenticated client execution.
Hope you got your answer.

Sql query error in java program

I have developed a java program and I need to update and insert the login details of users. I have two textfields created and two buttons name add user and edit the user. when I type the username and password in the two textfields the user added to the database successfully, the error is in the edit user, I want to update the password of the user based on username,
I'm getting SQL error when trying to update the user,
here is my SQL query for updating the password of a user based on his username,
String sql = "UPDATE Admin SET password='"+JT_pass1.getText()+"' WHERE
username = "+JT_username1.getText();
when i execute im getting this error,
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column
'sss' in 'where clause'
"sss" is what I entered to username field,
Mysql database I have admin table which consists of two columns admin and username,
I cannot figure out where am I getting wrong, please any help would be highly appreciated.
Your immediate problem is that you forgot to place single quotes around the username in your query. Hence, the database is interpreting sss as a column. But you should really be using prepared statements:
String query = "UPDATE Admin SET password=? WHERE username = ?";
PreparedStatement update = con.prepareStatement(query);
update.setString(JT_pass1.getText());
update.setString(JT_username1.getText());
update.executeUpdate();
There are many advantages to using prepared statements. First, it will automatically take care of proper escaping of strings and other types of data. In addition, it will prevent SQL injection from happening.
To get this to work, you need to add quotes around the username like so:
String sql = "UPDATE Admin SET password='"+JT_pass1.getText()+"' WHERE
username = '"+JT_username1.getText()+"'";
However, updating the database this way is vulnerable to SQL injection, so it would be much better to use Prepared Statements.
To consider "JT_username1.getText()" as a part of you query string, you have to enclose it under proper quotation.
Same like added "JT_pass1.getText()" between single and double quote, you have to add "JT_username1.getText()" as well.
String sql = "UPDATE Admin SET password='" + JT_pass1.getText() + "' WHERE username = '"+JT_username1.getText()+"'";

Why does this SQL-Statement lead to an error?

I am getting quite angry with this, so I seek help from the crowd ;)
What I want to do: We have a Unity learning game which shall implement a login window. The entered credentials are then hashed (the pw is) and sent to the server, who then should check this against a database.
I have the following table:
xy.users_confirms with the following colums:
id username email password hashcode created
Why does my code
String sql = "SELECT " + "xy.users_confirms.password as pwhash, "
+"FROM xy.users_confirms " +"WHERE xy.users_confirms.username = " +"\"userNameToGetHashFor\"";
lead me to the SQLException "Parameter index out of range (1 > number of parameters, which is 0)"
?
Thanks, any input is much appreciated!
Try this:
String parameter = "'"+ strNameToGetHashFor + "'";
String sql = "SELECT " + "xy.users_confirms.password as pwhash, "
+"FROM xy.users_confirms "
+"WHERE xy.users_confirms.username ="+ parameter;
You are using varchar value as a parameter so it's need to be quot like this.'username'. or you can use Stored Procedure.
Personally, I would try getting a working query using the custom query box directly in phpmyadmin. Once you have a working query you can re-write it in java.
And I would try writing the syntax like this into the phpmyadmin query box:
SELECT password as pwhash
FROM xy.users_confirms
WHERE username ='userNameToGetHashFor'
Using the above syntax I don't see anyway your error could persist.
Phpmyadmin screen cap showing custom query box: http://screencast.com/t/9h8anH0Aj
(the 2 empty text boxes in screen cap are just me hiding my database info)
The comma after pwhash is one potential cause:
+ "xy.users_confirms.password as pwhash*!*,*!* "
Depending on the DBMS, you may also need to use single quotes instead of double quotes like this:
+ "'userNameToGetHashFor'";
Also this code is potentially vulnerable to a SQL Injection attack so you may want to make the userNameToGetHashFor a parameter rather than concatenating the string into the SQL statement.

Categories