fix: Success if only Smith earn most salary (#1744)

* Update labels

* Update Java

* Update Test

---------

Co-authored-by: René Zubcevic <rene@zubcevic.com>
This commit is contained in:
François Capon 2024-06-01 10:50:38 +02:00 committed by GitHub
parent e219887f14
commit 3134f18066
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 95 additions and 165 deletions

View File

@ -63,36 +63,42 @@ public class SqlInjectionLesson9 extends AssignmentEndpoint {
protected AttackResult injectableQueryIntegrity(String name, String auth_tan) { protected AttackResult injectableQueryIntegrity(String name, String auth_tan) {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
String query = String queryInjection =
"SELECT * FROM employees WHERE last_name = '" "SELECT * FROM employees WHERE last_name = '"
+ name + name
+ "' AND auth_tan = '" + "' AND auth_tan = '"
+ auth_tan + auth_tan
+ "'"; + "'";
try (Connection connection = dataSource.getConnection()) { try (Connection connection = dataSource.getConnection()) {
try { // V2019_09_26_7__employees.sql
Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); int oldMaxSalary = this.getMaxSalary(connection);
SqlInjectionLesson8.log(connection, query); int oldSumSalariesOfOtherEmployees = this.getSumSalariesOfOtherEmployees(connection);
ResultSet results = statement.executeQuery(query); // begin transaction
var test = results.getRow() != 0; connection.setAutoCommit(false);
if (results.getStatement() != null) { // do injectable query
if (results.first()) { Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE);
output.append(SqlInjectionLesson8.generateTable(results)); SqlInjectionLesson8.log(connection, queryInjection);
} else { statement.execute(queryInjection);
// no results // check new sum of salaries other employees and new salaries of John
return failed(this).feedback("sql-injection.8.no.results").build(); int newJohnSalary = this.getJohnSalary(connection);
} int newSumSalariesOfOtherEmployees = this.getSumSalariesOfOtherEmployees(connection);
} if (newJohnSalary > oldMaxSalary
} catch (SQLException e) { && newSumSalariesOfOtherEmployees == oldSumSalariesOfOtherEmployees) {
System.err.println(e.getMessage()); // success commit
return failed(this) connection.commit(); // need execute not executeQuery
.output("<br><span class='feedback-negative'>" + e.getMessage() + "</span>") connection.setAutoCommit(true);
.build(); output.append(
SqlInjectionLesson8.generateTable(this.getEmployeesDataOrderBySalaryDesc(connection)));
return success(this).feedback("sql-injection.9.success").output(output.toString()).build();
} }
// failed roolback
return checkSalaryRanking(connection, output); connection.rollback();
return failed(this)
} catch (Exception e) { .feedback("sql-injection.9.one")
.output(
SqlInjectionLesson8.generateTable(this.getEmployeesDataOrderBySalaryDesc(connection)))
.build();
} catch (SQLException e) {
System.err.println(e.getMessage()); System.err.println(e.getMessage());
return failed(this) return failed(this)
.output("<br><span class='feedback-negative'>" + e.getMessage() + "</span>") .output("<br><span class='feedback-negative'>" + e.getMessage() + "</span>")
@ -100,29 +106,31 @@ public class SqlInjectionLesson9 extends AssignmentEndpoint {
} }
} }
private AttackResult checkSalaryRanking(Connection connection, StringBuilder output) { private int getSqlInt(Connection connection, String query) throws SQLException {
try { Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE);
String query = "SELECT * FROM employees ORDER BY salary DESC"; ResultSet results = statement.executeQuery(query);
try (Statement statement = results.first();
connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); ) { return results.getInt(1);
ResultSet results = statement.executeQuery(query); }
results.first(); private int getMaxSalary(Connection connection) throws SQLException {
// user completes lesson if John Smith is the first in the list String query = "SELECT max(salary) FROM employees";
if ((results.getString(2).equals("John")) && (results.getString(3).equals("Smith"))) { return this.getSqlInt(connection, query);
output.append(SqlInjectionLesson8.generateTable(results)); }
return success(this)
.feedback("sql-injection.9.success") private int getSumSalariesOfOtherEmployees(Connection connection) throws SQLException {
.output(output.toString()) String query = "SELECT sum(salary) FROM employees WHERE auth_tan != '3SL99A'";
.build(); return this.getSqlInt(connection, query);
} else { }
return failed(this).feedback("sql-injection.9.one").output(output.toString()).build();
} private int getJohnSalary(Connection connection) throws SQLException {
} String query = "SELECT salary FROM employees WHERE auth_tan = '3SL99A'";
} catch (SQLException e) { return this.getSqlInt(connection, query);
return failed(this) }
.output("<br><span class='feedback-negative'>" + e.getMessage() + "</span>")
.build(); private ResultSet getEmployeesDataOrderBySalaryDesc(Connection connection) throws SQLException {
} String query = "SELECT * FROM employees ORDER BY salary DESC";
Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE);
return statement.executeQuery(query);
} }
} }

View File

@ -63,8 +63,8 @@ SqlStringInjectionHint.8.3=Try appending a SQL statement that always resolves to
SqlStringInjectionHint.8.4=Make sure all quotes (" ' ") are opened and closed properly so the resulting SQL query is syntactically correct. SqlStringInjectionHint.8.4=Make sure all quotes (" ' ") are opened and closed properly so the resulting SQL query is syntactically correct.
SqlStringInjectionHint.8.5=Try extending the WHERE clause of the statement by adding something like: ' OR '1' = '1. SqlStringInjectionHint.8.5=Try extending the WHERE clause of the statement by adding something like: ' OR '1' = '1.
sql-injection.9.success=Well done! Now you are earning the most money. And at the same time you successfully compromised the integrity of data by changing the salary! sql-injection.9.success=Well done! Now you are earning the most money. And at the same time you successfully compromised the integrity of data by changing your salary!
sql-injection.9.one=Still not earning enough! Better try again and change that. sql-injection.9.one=Still not earning enough or only your own salary must be changed! Better try again and change that.
SqlStringInjectionHint.9.1=Try to find a way, to chain another query to the end of the existing one. SqlStringInjectionHint.9.1=Try to find a way, to chain another query to the end of the existing one.
SqlStringInjectionHint.9.2=Use the ; metacharacter to do so. SqlStringInjectionHint.9.2=Use the ; metacharacter to do so.
SqlStringInjectionHint.9.3=Make use of DML to change your salary. SqlStringInjectionHint.9.3=Make use of DML to change your salary.

View File

@ -37,123 +37,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
*/ */
public class SqlInjectionLesson9Test extends SqlLessonTest { public class SqlInjectionLesson9Test extends SqlLessonTest {
private String completedError = "JSON path \"lessonCompleted\""; private final String completedError = "JSON path \"lessonCompleted\"";
@Test
public void oneAccount() throws Exception {
try {
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smith")
.param("auth_tan", "3SL99A"))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.9.one"))))
.andExpect(jsonPath("$.output", containsString("<table><tr><th>")));
} catch (AssertionError e) {
if (!e.getMessage().contains(completedError)) throw e;
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smith")
.param("auth_tan", "3SL99A"))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(true)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.9.success"))))
.andExpect(jsonPath("$.output", containsString("<table><tr><th>")));
}
}
@Test
public void multipleAccounts() throws Exception {
try {
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smith")
.param("auth_tan", "3SL99A' OR '1' = '1"))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.9.one"))))
.andExpect(
jsonPath(
"$.output",
containsString(
"<tr><td>96134<\\/td><td>Bob<\\/td><td>Franco<\\/td><td>Marketing<\\/td><td>83700<\\/td><td>LO9S2V<\\/td><\\/tr>")));
} catch (AssertionError e) {
if (!e.getMessage().contains(completedError)) throw e;
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smith")
.param("auth_tan", "3SL99A' OR '1' = '1"))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(true)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.9.success"))))
.andExpect(
jsonPath(
"$.output",
containsString(
"<tr><td>96134<\\/td><td>Bob<\\/td><td>Franco<\\/td><td>Marketing<\\/td><td>83700<\\/td><td>LO9S2V<\\/td><\\/tr>")));
}
}
@Test
public void wrongNameReturnsNoAccounts() throws Exception {
try {
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smithh")
.param("auth_tan", "3SL99A"))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.8.no.results"))))
.andExpect(jsonPath("$.output").doesNotExist());
} catch (AssertionError e) {
if (!e.getMessage().contains(completedError)) throw e;
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smithh")
.param("auth_tan", "3SL99A"))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(true)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.8.no.success"))))
.andExpect(jsonPath("$.output").doesNotExist());
}
}
@Test
public void wrongTANReturnsNoAccounts() throws Exception {
try {
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smithh")
.param("auth_tan", ""))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.8.no.results"))))
.andExpect(jsonPath("$.output").doesNotExist());
} catch (AssertionError e) {
if (!e.getMessage().contains(completedError)) throw e;
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smithh")
.param("auth_tan", ""))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(true)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.9.success"))))
.andExpect(jsonPath("$.output").doesNotExist());
}
}
@Test @Test
public void malformedQueryReturnsError() throws Exception { public void malformedQueryReturnsError() throws Exception {
@ -181,6 +65,44 @@ public class SqlInjectionLesson9Test extends SqlLessonTest {
} }
} }
@Test
public void SmithIsNotMostEarning() throws Exception {
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smith")
.param(
"auth_tan",
"3SL99A'; UPDATE employees SET salary = 9999 WHERE last_name = 'Smith"))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.9.one"))));
}
@Test
public void OnlySmithSalaryMustBeUpdated() throws Exception {
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "Smith")
.param("auth_tan", "3SL99A'; UPDATE employees SET salary = 9999 -- "))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.9.one"))));
}
@Test
public void OnlySmithMustMostEarning() throws Exception {
mockMvc
.perform(
MockMvcRequestBuilders.post("/SqlInjection/attack9")
.param("name", "'; UPDATE employees SET salary = 999999 -- ")
.param("auth_tan", ""))
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.9.one"))));
}
@Test @Test
public void SmithIsMostEarningCompletesAssignment() throws Exception { public void SmithIsMostEarningCompletesAssignment() throws Exception {
mockMvc mockMvc