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 );
}
}
}