package org.owasp.webgoat.lessons; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import org.apache.ecs.Element; import org.apache.ecs.ElementContainer; import org.apache.ecs.StringElement; import org.apache.ecs.html.Input; import org.apache.ecs.html.P; import org.owasp.webgoat.session.DatabaseUtilities; import org.owasp.webgoat.session.ECSFactory; import org.owasp.webgoat.session.WebSession; /** * Copyright (c) 2005 Free Software Foundation developed under the custody of the Open Web * Application Security Project (http://www.owasp.org) This software package is published by OWASP * under the GPL. You should read and accept the LICENSE before you use, modify and/or redistribute * this software. * * @author Chuck Willis Chuck's web site (this lesson is heavily based on Jeff Williams' SQL Injection lesson * @created January 14, 2005 */ public class BlindSqlInjection extends LessonAdapter { private final static String ACCT_NUM = "account_number"; private final static int TARGET_ACCT_NUM = 15613; private static Connection connection = null; /** * Description of the Method * * @param s Description of the Parameter * @return Description of the Return Value */ protected Element createContent( WebSession s ) { ElementContainer ec = new ElementContainer(); try { if ( connection == null ) { connection = DatabaseUtilities.makeConnection( s ); } ec.addElement( new P().addElement( "Enter your Account Number: " ) ); String accountNumber = s.getParser().getRawParameter( ACCT_NUM, "101" ); Input input = new Input( Input.TEXT, ACCT_NUM, accountNumber.toString() ); ec.addElement( input ); Element b = ECSFactory.makeButton( "Go!" ); ec.addElement( b ); String query = "SELECT * FROM user_data WHERE userid = " + accountNumber ; String answer_query; if(runningOnWindows()) { answer_query = "SELECT TOP 1 first_name FROM user_data WHERE userid = " + TARGET_ACCT_NUM; } else { answer_query = "SELECT first_name FROM user_data WHERE userid = " + TARGET_ACCT_NUM; } try { Statement answer_statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY ); ResultSet answer_results = answer_statement.executeQuery( answer_query ); answer_results.first(); if( accountNumber.toString().equals(answer_results.getString(1))) { makeSuccess( s ); } else { Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY ); ResultSet results = statement.executeQuery( query ); if ( ( results != null ) && ( results.first() == true ) ) { ec.addElement( new P().addElement("Account number is valid")); } else { ec.addElement( new P().addElement("Invalid account number")); } } } catch ( SQLException sqle ) { ec.addElement( new P().addElement("An error occurred, please try again.")); } } catch ( Exception e ) { s.setMessage( "Error generating " + this.getClass().getName() ); e.printStackTrace(); } return ( ec ); } /** * Gets the category attribute of the SqlInjection object * * @return The category value */ public Category getCategory() { return AbstractLesson.A6; } /** * Gets the credits attribute of the AbstractLesson object * * @return The credits value */ public Element getCredits() { return new StringElement("By Chuck Willis (edited 14 Dec 05 - Hints need to updated for non-Windows systems)"); } /** * * Determines the OS that WebGoat is running on. Needed because different DB backends * are used on the different OSes (Access on Windows, InstantDB on others) * * @return true if running on Windows, false otherwise */ private boolean runningOnWindows() { String os = System.getProperty("os.name","Windows"); if ( os.toLowerCase().indexOf("window") != -1 ) { return true; } else { return false; } } /** * Gets the hints attribute of the DatabaseFieldScreen object * * @return The hints value */ protected List getHints() { List hints = new ArrayList(); if (runningOnWindows()) { hints.add( "Compound SQL statements can be made by joining multiple tests with keywords like AND and OR. " + "Create a SQL statement that you can use as a true/false test and then " + "select the first character of the target element and do a start narrowing " + "down the character using > and <" + "

The backend database is Microsoft Access. Keep that in mind if you research SQL functions " + "on the Internet since different databases use some different functions and syntax."); hints.add( "This is the code for the query being built and issued by WebGoat:

" + "\"SELECT * FROM user_data WHERE userid = \" + accountNumber " ); hints.add( "The application is taking your input and inserting it at the end of a pre-formed SQL command. "+ "You will need to make use of the following SQL functions: " + "

SELECT - query for your target data and get a string "+ "

mid(string, start, length) - returns a " + "substring of string starting at the start character and going for length characters "+ "

asc(string) will return the ascii value of the first character in string " + "

> and < - once you have a character's value, compare it to a choosen one"); hints.add( "Example: is the first character of the first_name of userid " + TARGET_ACCT_NUM + " less than 'M' (ascii 77)? " + "

101 AND (asc( mid((SELECT first_name FROM user_data WHERE userid=" + TARGET_ACCT_NUM + ") , 1 , 1) ) < 77 ); " + "

If you get back that account number is valid, then yes. If get back that the number is" + "invalid then answer is no."); hints.add( "Another example: is the second character of the first_name of userid " + TARGET_ACCT_NUM + " greater than 'm' (ascii 109)? " + "

101 AND (asc( mid((SELECT first_name FROM user_data WHERE userid=" + TARGET_ACCT_NUM + ") , 2 , 1) ) > 109 ); " + "

If you get back that account number is valid, then yes. If get back that the number is " + "invalid then answer is no."); } else { hints.add("Compound SQL statements can be made by joining multiple tests with keywords like AND and OR. " + "Create a SQL statement that you can use as a true/false test and then " + "select the first character of the target element and do a start narrowing " + "down the character using > and <" ); hints.add("The database backend is InstantDB. Here is a reference guide : http://www.instantdb.com/doc/syntax.html"); hints.add( "This is the code for the query being built and issued by WebGoat:

" + "\"SELECT * FROM user_data WHERE userid = \" + accountNumber " ); hints.add( "THIS HINT IS FOR THE MS ACCESS DB. IT NEEDS TO BE ALTERED FOR THE INSTANTDB BACKEND.

The application is taking your input and inserting it at the end of a pre-formed SQL command. "+ "You will need to make use of the following SQL functions: " + "

SELECT - query for your target data and get a string "+ "

mid(string, start, length) - returns a " + "substring of string starting at the start character and going for length characters "+ "

asc(string) will return the ascii value of the first character in string " + "

> and < - once you have a character's value, compare it to a choosen one"); hints.add( "THIS HINT IS FOR THE MS ACCESS DB. IT NEEDS TO BE ALTERED FOR THE INSTANTDB BACKEND.

Example: is the first character of the first_name of userid " + TARGET_ACCT_NUM + " less than 'M' (ascii 77)? " + "

101 AND (asc( mid((SELECT first_name FROM user_data WHERE userid=" + TARGET_ACCT_NUM + ") , 1 , 1) ) < 77 ); " + "

If you get back that account number is valid, then yes. If get back that the number is" + "invalid then answer is no."); hints.add( "THIS HINT IS FOR THE MS ACCESS DB. IT NEEDS TO BE ALTERED FOR THE INSTANTDB BACKEND.

example: is the second character of the first_name of userid " + TARGET_ACCT_NUM + " greater than 'm' (ascii 109)? " + "

101 AND (asc( mid((SELECT first_name FROM user_data WHERE userid=" + TARGET_ACCT_NUM + ") , 2 , 1) ) > 109 ); " + "

If you get back that account number is valid, then yes. If get back that the number is " + "invalid then answer is no."); } return hints; } /** * Gets the instructions attribute of the SqlInjection object * * @return The instructions value */ public String getInstructions(WebSession s) { String instructions = "The form below allows a user to enter an account number and determine if "+ "it is valid or not. Use this form to develop a true / false test check other entries in the database. "+ "

Reference Ascii Values: 'A' = 65 'Z' = 90 'a' = 97 'z' = 122 " + "

The goal is to find the value of "+ "the first_name in table user_data for userid " + TARGET_ACCT_NUM + ". Put that name in the form to pass the lesson."; return ( instructions ); } private final static Integer DEFAULT_RANKING = new Integer(70); protected Integer getDefaultRanking() { return DEFAULT_RANKING; } /** * Gets the title attribute of the DatabaseFieldScreen object * * @return The title value */ public String getTitle() { return ( "How to Perform Blind SQL Injection" ); } /** * Constructor for the DatabaseFieldScreen object * * @param s Description of the Parameter */ public void handleRequest( WebSession s ) { try { super.handleRequest( s ); if ( connection == null ) { connection = DatabaseUtilities.makeConnection( s ); } } catch ( Exception e ) { System.out.println( "Exception caught: " + e ); e.printStackTrace( System.out ); } } }