diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionLessonTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionLessonTest.java index 680f80042..f5e3b6101 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionLessonTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionLessonTest.java @@ -11,7 +11,7 @@ public class SqlInjectionLessonTest extends IntegrationTest { public static final String sql_3 = "update employees set department='Sales' where last_name='Barnett'"; public static final String sql_4_drop = "alter table employees drop column phone"; public static final String sql_4_add = "alter table employees add column phone varchar(20)"; - public static final String sql_5 = "grant alter table to UnauthorizedUser"; + public static final String sql_5 = "grant select on grant_rights to unauthorized_user"; public static final String sql_9_account = " ' "; public static final String sql_9_operator = "or"; public static final String sql_9_injection = "'1'='1"; diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5.java b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5.java index 0cc1ce3af..0b8d064a5 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5.java +++ b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5.java @@ -30,11 +30,36 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import javax.annotation.PostConstruct; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + @RestController @AssignmentHints(value = {"SqlStringInjectionHint5-a"}) public class SqlInjectionLesson5 extends AssignmentEndpoint { + private final DataSource dataSource; + + public SqlInjectionLesson5(DataSource dataSource) { + this.dataSource = dataSource; + } + + @PostConstruct + public void createUser() { + // HSQLDB does not support CREATE USER with IF NOT EXISTS so we need to do it in code (DROP first will throw error if user does not exists) + try (Connection connection = dataSource.getConnection()) { + try (var statement = connection.prepareStatement("CREATE USER unauthorized_user PASSWORD test")) { + statement.execute(); + } + } catch (Exception e) { + //user already exists continue + } + } + @PostMapping("/SqlInjection/attack5") @ResponseBody public AttackResult completed(String query) { @@ -42,19 +67,29 @@ public class SqlInjectionLesson5 extends AssignmentEndpoint { } protected AttackResult injectableQuery(String query) { - try { - String regex = "(?i)^(grant alter table to [']?unauthorizedUser[']?)(?:[;]?)$"; - StringBuffer output = new StringBuffer(); - - // user completes lesson if the query is correct - if (query.matches(regex)) { - output.append("" + query + ""); - return success(this).output(output.toString()).build(); - } else { - return failed(this).output(output.toString()).build(); + try (Connection connection = dataSource.getConnection()) { + try (Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + statement.executeQuery(query); + if (checkSolution(connection)) { + return success(this).build(); + } + return failed(this).output("Your query was: " + query).build(); } } catch (Exception e) { - return failed(this).output(this.getClass().getName() + " : " + e.getMessage()).build(); + return failed(this).output(this.getClass().getName() + " : " + e.getMessage() + "
Your query was: " + query).build(); } } + + private boolean checkSolution(Connection connection) { + try { + var stmt = connection.prepareStatement("SELECT * FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE TABLE_NAME = ? AND GRANTEE = ?"); + stmt.setString(1, "GRANT_RIGHTS"); + stmt.setString(2, "UNAUTHORIZED_USER"); + var resultSet = stmt.executeQuery(); + return resultSet.next(); + } catch (SQLException throwables) { + return false; + } + + } } diff --git a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2021_03_13_8__grant.sql b/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2021_03_13_8__grant.sql new file mode 100644 index 000000000..b4577e4d1 --- /dev/null +++ b/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2021_03_13_8__grant.sql @@ -0,0 +1,14 @@ +CREATE TABLE grant_rights( + userid varchar(6) not null primary key, + first_name varchar(20), + last_name varchar(20), + department varchar(20), + salary int +); + +INSERT INTO grant_rights VALUES ('32147','Paulina', 'Travers', 'Accounting', 46000); +INSERT INTO grant_rights VALUES ('89762','Tobi', 'Barnett', 'Development', 77000); +INSERT INTO grant_rights VALUES ('96134','Bob', 'Franco', 'Marketing', 83700); +INSERT INTO grant_rights VALUES ('34477','Abraham ', 'Holman', 'Development', 50000); +INSERT INTO grant_rights VALUES ('37648','John', 'Smith', 'Marketing', 64350); + diff --git a/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties index 7c24f2fb8..cd611a432 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties @@ -25,7 +25,8 @@ SqlStringInjectionHint4-1=ALTER TABLE alters the structure of an existing databa SqlStringInjectionHint4-2=Do not forget the data type of the new column (e.g. varchar(size) or int(size)) SqlStringInjectionHint4-3=ALTER TABLE table name ADD column name data type(size); -SqlStringInjectionHint5-a=Look at the example. There is everything you will need. +SqlStringInjectionHint5-1=Take a look at how to use a grant statement. +SqlStringInjectionHint5-2=You are using 'tom' trying to grant access to tom sql-injection.5a.success=You have succeeded: {0} sql-injection.5a.no.results=No results matched. Try Again. diff --git a/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5Test.java b/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5Test.java index 6ea8bf178..ad0296714 100644 --- a/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5Test.java +++ b/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5Test.java @@ -23,16 +23,22 @@ package org.owasp.webgoat.sql_injection.introduction; import org.hamcrest.CoreMatchers; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import org.owasp.webgoat.assignments.AssignmentEndpointTest; import org.owasp.webgoat.sql_injection.SqlLessonTest; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import javax.sql.DataSource; + +import java.sql.SQLException; + import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -41,28 +47,34 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standal @RunWith(SpringJUnit4ClassRunner.class) public class SqlInjectionLesson5Test extends SqlLessonTest { + @Autowired + private DataSource dataSource; + + @After + public void removeGrant() throws SQLException { + dataSource.getConnection().prepareStatement("revoke select on grant_rights from unauthorized_user cascade").execute(); + } + @Test public void grantSolution() throws Exception { mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5") - .param("query","grant alter table to unauthorizedUser")) + .param("query", "grant select on grant_rights to unauthorized_user")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.output", CoreMatchers.containsString("grant"))) .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); } @Test - public void grantSolutionWithSingleQuotes() throws Exception { + public void differentTableShouldNotSolveIt() throws Exception { mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5") - .param("query","grant alter table to 'unauthorizedUser';")) + .param("query", "grant select on users to unauthorized_user")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.output", CoreMatchers.containsString("grant"))) - .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); } @Test - public void grantSolutionWrong() throws Exception { + public void noGrantShouldNotSolveIt() throws Exception { mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5") - .param("query","grant alter table to me")) + .param("query", "select * from grant_rights")) .andExpect(status().isOk()) .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); }