java-How to perform SQL injection for testing purposes? - java

I have a web application that I am trying to "break".There's a login page that requires username and password input. Let's say I have a table Auser that stores username's info in MySQL.
When I hit Login after keying the credentials,it executes this line of code:
String sql = "select object(o) from Auser as o where ausername='" + username + "'";
Now, I know not using preparedStatement makes SQL query vulnerable to SQL injection and I want to perform such a stunt. I created a dummy table called test for the purpose of able to drop this table via the injection command.
I tried various ways like in my username input(root is the username):
root` DROP TABLE test;
And it didn't work. Is there a way to make my injection successful?
Update:
Just extra info, my username column is VARCHAR(255) and my method for getting the username is below:
public Auser get(String username, boolean moreInfo) {
try {
Auser u = null;
String sql = "select object(o) from Auser as o where ausername='" + username + "'";
List resList = em.createQuery(sql).getResultList();
if (resList == null) { // null check for sql query / library error
msg = CoreUtil.wrapMsg(CoreUtil.FUNC_ERROR,
this.getClass().getName(), "get[" + username + "]", "query error AUSER.");
} else if (resList.isEmpty()) {
msg = "User " + username + " not found.";
} else {
u = (Auser) resList.get(0);
}
return u;
} catch (Exception e) {
msg = CoreUtil.wrapMsg(CoreUtil.FUNC_ERROR,
this.getClass().getName(), "get[" + username + "]", e.getMessage());
return null;
}
}
Seems every solution, I tried keeps throwing IllegalArgumetnException and the table still remains.I just want to exploit the vulnerabilities of my program,it can be any kind of injection whether dropping a table, returning all users info,etc.

The EntityManager has some (very) basic protection built in that won't run more than one command in the same SQL statement.
This will protect you from Robert'); DROP TABLE Students; --, but it won't protect from attackers trying to expand/alter the one query that's being run.
For example, in your code an attacker could get the details of another user by entering the username ' OR 1 = 1 --; This would make the SQL string being executed
select object(o) from Auser as o where ausername='' OR 1 = 1 --'
which will select every user in the table (note that the -- at the end of the input will comment out everything after the injected code), and your method will return the first user in the result list This will potentially give the attacker details about another user that they should not have access to. If the first account is an administrator account then they may also have access they should not have.
An attacker can also learn the structure of the table this way - they can try strings like ' and IS_ADMIN = IS_ADMIN --, or ' OR ID = 0 --. If they try enough of these (and attacks like this can be easily automated) they will find valid column names when the query doesn't throw an error. They can potentially then make a more targeted injection attack to gain access to an admin account.
They might also learn things from the error message returned from a failed attempt, such as the DB platform, which can make attacks easier.

String sql = "select object(o) from Auser as o where ausername='" + username + "'";
If you want to delete the test table
username = "x'; DROP TABLE test AND '1'='1"
If you want to see all fields of all ausers entries
username = "x' OR '1'='1"

Related

sql-injection finding when using a prepared-statement in java [duplicate]

I am having code something like this.
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
Calculation of fullTableName is something like:
public String getFullTableName(final String table) {
if (this.schemaDB != null) {
return this.schemaDB + "." + table;
}
return table;
}
Here schemaDB is the name of the environment(which can be changed over time) and table is the table name(which will be fixed).
Value for schemaDB is coming from an XML file which makes the query vulnerable to SQL injection.
Query: I am not sure how the table name can be used as a prepared statement(like the name used in this example), which is the 100% security measure against SQL injection.
Could anyone please suggest me, what could be the possible approach to deal with this?
Note: We can be migrated to DB2 in future so the solution should compatible with both Oracle and DB2(and if possible database independent).
JDBC, sort of unfortunately, does not allow you to make the table name a bound variable inside statements. (It has its reasons for this).
So you can not write, or achieve this kind of functionnality :
connection.prepareStatement("SELECT * FROM ? where id=?", "TUSERS", 123);
And have TUSER be bound to the table name of the statement.
Therefore, your only safe way forward is to validate the user input. The safest way, though, is not to validate it and allow user-input go through the DB, because from a security point of view, you can always count on a user being smarter than your validation.
Never trust a dynamic, user generated String, concatenated inside your statement.
So what is a safe validation pattern ?
Pattern 1 : prebuild safe queries
1) Create all your valid statements once and for all, in code.
Map<String, String> statementByTableName = new HashMap<>();
statementByTableName.put("table_1", "DELETE FROM table_1 where name= ?");
statementByTableName.put("table_2", "DELETE FROM table_2 where name= ?");
If need be, this creation itself can be made dynamic, with a select * from ALL_TABLES; statement. ALL_TABLES will return all the tables your SQL user has access to, and you can also get the table name, and schema name from this.
2) Select the statement inside the map
String unsafeUserContent = ...
String safeStatement = statementByTableName.get(usafeUserContent);
conn.prepareStatement(safeStatement, name);
See how the unsafeUserContent variable never reaches the DB.
3) Make some kind of policy, or unit test, that checks that all you statementByTableName are valid against your schemas for future evolutions of it, and that no table is missing.
Pattern 2 : double check
You can 1) validate that the user input is indeed a table name, using an injection free query (I'm typing pseudo sql code here, you'd have to adapt it to make it work cause I have no Oracle instance to actually check it works) :
select * FROM
(select schema_name || '.' || table_name as fullName FROM all_tables)
WHERE fullName = ?
And bind your fullName as a prepared statement variable here. If you have a result, then it is a valid table name. Then you can use this result to build a safe query.
Pattern 3
It's sort of a mix between 1 and 2.
You create a table that is named, e.g., "TABLES_ALLOWED_FOR_DELETION", and you statically populate it with all tables that are fit for deletion.
Then you make your validation step be
conn.prepareStatement(SELECT safe_table_name FROM TABLES_ALLOWED_FOR_DELETION WHERE table_name = ?", unsafeDynamicString);
If this has a result, then you execute the safe_table_name. For extra safety, this table should not be writable by the standard application user.
I somehow feel the first pattern is better.
You can avoid attack by checking your table name using regular expression:
if (fullTableName.matches("[_a-zA-Z0-9\\.]+")) {
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
}
It's impossible to inject SQL using such a restricted set of characters.
Also, we can escape any quotes from table name, and safely add it to our query:
fullTableName = StringEscapeUtils.escapeSql(fullTableName);
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
StringEscapeUtils comes with Apache's commons-lang library.
I think that the best approach is to create a set of possible table names and check for existance in this set before creating query.
Set<String> validTables=.... // prepare this set yourself
if(validTables.contains(fullTableName))
{
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
//and so on
}else{
// ooooh you nasty haker!
}
create table MYTAB(n number);
insert into MYTAB values(10);
commit;
select * from mytab;
N
10
create table TABS2DEL(tname varchar2(32));
insert into TABS2DEL values('MYTAB');
commit;
select * from TABS2DEL;
TNAME
MYTAB
create or replace procedure deltab(v in varchar2)
is
LvSQL varchar2(32767);
LvChk number;
begin
LvChk := 0;
begin
select count(1)
into LvChk
from TABS2DEL
where tname = v;
if LvChk = 0 then
raise_application_error(-20001, 'Input table name '||v||' is not a valid table name');
end if;
exception when others
then raise;
end;
LvSQL := 'delete from '||v||' where n = 10';
execute immediate LvSQL;
commit;
end deltab;
begin
deltab('MYTAB');
end;
select * from mytab;
no rows found
begin
deltab('InvalidTableName');
end;
ORA-20001: Input table name InvalidTableName is not a valid table name ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 21
ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 16
ORA-06512: at line 2
ORA-06512: at "SYS.DBMS_SQL", line 1721

SQL Delete from two table in Oracle

I have to do remove the row (containing the userId) in the table "USERS". This is my query:
#SqlUpdate("delete from USERS where userId = :userId ")
void removeUser(#Bind("userId") String userId);
But first I want to remove that user from the table "USERS_DATA" (that is a daughter of USERS) which also contain the "userId". How can I do? I've tried this:
#SqlUpdate("delete from USERS_DATA where userId = :userId " +
" and delete from USERS where userId = :userId")
void removeUser(#Bind("userId") String userId);
but console tell me: java.sql.SQLSyntaxErrorException: ORA-00936: missing expression
Unlike some other RDBMS, Oracle does not allow you to pass two statements in the same SQL command (this helps to prevent SQL injection).
You can try using wrapping both queries in an anonymous PL/SLQ block:
BEGIN
delete from USERS_DATA where userId = :userId;
delete from USERS where userId = :userId;
END;
/
This will allow you to execute both DML statements together as they are part of the singular containing PL/SQL block.
Unfortunately, I am not familiar with that annotation syntax in Java so I cannot help you convert it to Java but I would guess at:
#SqlUpdate("BEGIN " +
"delete from USERS_DATA where userId = :userId; " +
"delete from USERS where userId = :userId; " +
"END;")
void removeUser(#Bind("userId") String userId);
Alternatively, you can create a procedure in Oracle:
CREATE OR REPLACE PROCEDURE delete_user(
in_userID USERS_DATA.USERID%TYPE
)
AS
BEGIN
DELETE FROM USERS_DATA WHERE userId = in_userId;
DELETE FROM USERS WHERE userId = in_userId;
END;
/
And you can then just call this procedure.

Sql query with optional fields for Search Web Application

Not an SQL expert:
I am currently implementing a Searching Web Application. The user will send a GET request to a particular Endpoint.
The Endpoint will accept a set of URL Params and the request can come with optional fields. Such as 1, 2 or 3 fields.
My Database table for USER looks like this.
+--------------+---------+------+-----+---------+----------------+
| id | firstName | lastName | email | zip | phone |
+--------------+---------+------+-----+---------+----------------+
Now the web application will get a GET Request with either Phone or Zip or Email.
I have two solutions for doing this but both look bad:
Solution 1:
Have multiple SQL Queries and Execute them according to the URL Params, I receive.
Select * from User where phone=1111111111 and zip=12345 and email=test#email.com;
Select * from User where phone=1111111111 and zip=12345;
...
...
And so on........I will end up having many queries and this will be a bad implementation. Also will be bad to maintain.
Solution 2:
Other solution that I am thinking of is to have a method, which will build an SQL query based on the URL Params I receive.
Example:
buildSQLQuery(phone,zip,email){
String sql = "Select * from User where "
if(phone!=null && email!=null && zip!=null ){
sql = sql + "phone = " + phone + " and zip = " + zip + " and email = " + email;
}else if (phone!=null && email!=null && zip==null){
sql = sql + "phone = " + phone + " and email = " + email;
}
.......
......
And so on have all conditions and build that particular query.
}
I don't like both these solutions.
Is there a way to write a Single SQL query and that will handle all the above conditions.
Something like if the URL Param value is NULL then that should not affect the query and I will get my expected results.
As in my case the optional values, which don't come in are set to NULL.
Can I implement something like this?
Select * from User where (if notNull (phone))(phone = phone ) and (if notNull (email))(email = email ) and (if notNull (zip))(zip = zip )
If any of the above one value is null then don't use that part in where Condition.
Also I will always have one field present, so there will be no case where all values are null.
I am implementing this web application in Java and Spring MVC. If anyone can guide me in the correct direction.
Thank you.
I think one more possible solution like:
String sql = "Select * from User where 1=1 "
if(phone!=null){
sql += " and phone = " + phone ;}
if(email!=null){
sql += " and email = " + email ;}
if(zip!=null){
sql += " and zip = " + zip ;}
OR You can try following single query:
select * from User
where (#phone is null OR phone = #phone) AND (#email is null OR email = #email) AND (#zip is null OR zip = #zip)
You can do like this:
SELECT * FROM User where(
IF($phone != 0, IF(phone = $phone,1,0), 0) AND
IF($email != 0, IF(email = $email,1,0),0) AND
IF($zip != 0, IF(zip = $zip,1,0),0)
)
assumed for PHP you can change the syntax, may be this query not do the job completely but if you provide fiddle i will modify this to give the proper result.

Error In JDBC Template Querying

I am trying to write this query in order to authenticate the username and password of my API but I am getting error column not found. Both username and passwords are strings and I am using MYSQL database. I think there is a error with quotations as username and password are strings. How can I rectify the below code or is there a better way to write the same.
P.S - I am using spring MVC and this is my first project.
#Override
public TypeInfo getRole(final TypeInfo typeinfo) {
String sql =
"select Role from registartion where UserName=\"" + typeinfo.getUserName() + "and Password=\"" + typeinfo.getPassword() + "/"";
return jdbcTemplate.query(sql, new ResultSetExtractor<TypeInfo>() {
#Override
public TypeInfo extractData(ResultSet rs)
throws SQLException, DataAccessException {
if (rs.next()) {
System.out.println("VALID USER");
TypeInfo typeinfo1 = new TypeInfo();
typeinfo1.setUserName(typeinfo.getUserName());
typeinfo1.setPassword(typeinfo.getPassword());
typeinfo1.setRole(rs.getString("Role"));
return typeinfo1;
}
System.out.println("Not A valid user");
return null;
}
});
}
I am getting a error that "select Role from registartion where UserName=******" column name ******* not found.
That's not the way you should write your query.
JdbcTemplate uses an Object[] array as parameters, to avoid SQL injection.
code it somewhere in the lines of this:
String user = "yourUser";
String password = "yourPassword";
final String sql = "SELECT * from FOO where username = ? and password = ?";
Object[] sqlParameters = new Object[]{user, password};
List<YourEntityClass> list = getJdbcTemplate.query(sql, new BeanPropertyRowMapper<Your Entity Class>(YourEntityClass.class), sqlParameters);
BeanPropertyRowMapper actually maps the values for you. just make sure your entity class has the same property names as the ones on your database
more info here:
jdbcTemplate examples
The proper solution would be to use a PreparedStatement, in order to avoid having to mess with quoting and enhance security.
If you really must construct the statement by string concatination, you should note that string literals in SQL are denoted by single quotes ('), not double quotes ("):
String sql =
"select Role from registartion where UserName='" + typeinfo.getUserName() + "' and Password='" + typeinfo.getPassword() + '";
Try this.
String sql = "select Role from registartion where UserName='" + typeinfo.getUserName() + "' and Password='" + typeinfo.getPassword() + "'";
Change the double quotes (including the escape characters) to single quotes.
Close the single quote enclosing the user name (typeinfo.getUserName()). You need to keep a space between the closing single quote and the subsequent string.
If it still does not work then check the table names and column names. Maybe it is 'registration' and not 'registartion'? Or may be it is 'user_name' and not 'username'?
Tips for beginners: Copy paste the sql string into any database browser, replace the variables with actual values and execute. Check for any errors. It is easier to fix errors this way.
And lastly, use parameterized sql queries to avoid sql injection. In my opinion parameterized queries reduces syntax errors too.
You have to use single qoutes around the column values:
"select Role from registartion where UserName='" + typeinfo.getUserName() + "' and Password='" + typeinfo.getPassword() + "'";
You should better use PreparedStatement. It is easier to read and safer (prevents sql injection).

SQL error Too few parameters

I'm having an sql problem. I writing a java application on top of a Access database.
It's a search query for several fields and I know the error is in the part where I need to calculate the age of a person when he or she went missing. I'm returning a tablemodel in my method so i need to do the calculations in my query. My latest atempt to make it work is this:
public TableModel UpdateTable(String dossiernr, String naam, String voornaam,
String startleeftijd, String eindleeftijd, String dossierjaar, String geslacht)
{
TableModel tb = null;
String sql= "SELECT [Nr dossier],[Annee],[Nom],[Prenom],[Disparu le],[Ne le],[Sexe], DATEDIFF('yyyy',[Ne le],[Disparu le]) - iif(DATEADD('yyyy', DATEDIFF('yyyy',[Ne le],[Disparu le]),"
+ "[Ne le])>[Disparu le],1,0) AS Age FROM TotalTable "
+ "WHERE [Nr dossier] LIKE ? AND [Nom] LIKE ? AND [Prenom] LIKE ? AND [Annee] LIKE ? AND Age >= ? AND Age <= ? AND [Sexe] LIKE ?;";
try
{
PreparedStatement pstatement;
Connection connection = PersistentieController.getInstance().getConnection();
pstatement = initStatement(connection,sql);
pstatement.setString(1, "%" + dossiernr + "%");
pstatement.setString(2, "%" + naam + "%");
pstatement.setString(3, "%" + voornaam + "%");
pstatement.setString(4, "%" + dossierjaar + "%");
pstatement.setString(5, startleeftijd);
pstatement.setString(6, eindleeftijd);
pstatement.setString(7, "%" + geslacht + "%");
rs=pstatement.executeQuery();
tb = DbUtils.resultSetToTableModel(rs);
pstatement.close();
}//einde try
catch (SQLException e)
{
e.printStackTrace();
} //einde catch
return tb;
}
When i run it, i get following error:
java.sql.SQLException: [Microsoft][ODBC Microsoft Access Driver] Too few parameters. Expected 8.
I only work with 7 parameters and don't get why he's asking for 8.
Thanks
You count 7 parameters in your WHERE clause. Unfortunately, the Access db engine treats Age as another parameter in that situation, so it thinks you have 8 parameters instead of only 7.
To understand why, start with this query which runs without error with my Access database:
SELECT some_text AS foo
FROM tblFoo
WHERE some_text Is Not Null;
However, when attempting to use the alias instead of the field name in the WHERE clause, Access prompts me to supply a value for foo because it treats it as a parameter:
SELECT some_text AS foo
FROM tblFoo
WHERE foo Is Not Null;
Access limits your ability to re-use alias names later in a query. In certain cases, it will accept the alias, but yours is not one of those cases.
You could define the alias in a subquery. Then the db engine will recognize it correctly when you reference the subquery's alias in the parent query.
If possible, test your SQL statements directly in Access. If they fail, that effort will give you the best chance to determine why.

Categories

Resources