1025 lines
26 KiB
Java

package org.owasp.webgoat.lessons;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.apache.ecs.Element;
import org.apache.ecs.ElementContainer;
import org.apache.ecs.StringElement;
import org.apache.ecs.html.B;
import org.apache.ecs.html.Body;
import org.apache.ecs.html.Form;
import org.apache.ecs.html.HR;
import org.apache.ecs.html.Head;
import org.apache.ecs.html.Html;
import org.apache.ecs.html.IMG;
import org.apache.ecs.html.LI;
import org.apache.ecs.html.PRE;
import org.apache.ecs.html.TD;
import org.apache.ecs.html.TR;
import org.apache.ecs.html.Table;
import org.apache.ecs.html.Title;
import org.apache.ecs.html.UL;
import org.owasp.webgoat.session.ParameterNotFoundException;
import org.owasp.webgoat.session.Screen;
import org.owasp.webgoat.session.WebSession;
import org.owasp.webgoat.session.WebgoatProperties;
/**
* Copyright (c) 2002 Free Software Foundation developed under the custody of the Open Web
* Application Security Project (http://www.owasp.org) This software package org.owasp.webgoat.is
* published by OWASP under the GPL. You should read and accept the LICENSE before you use, modify
* and/or redistribute this software.
*
* @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
* @created October 28, 2003
*/
public abstract class AbstractLesson extends Screen implements Comparable
{
/**
* Description of the Field
*/
public final static Category A1 = new Category( "Unvalidated Parameters", new Integer( 110 ) );
/**
* Description of the Field
*/
public final static Category A10 = new Category( "Insecure Configuration Management", new Integer( 1010 ) );
/**
* Description of the Field
*/
public final static Category A2 = new Category( "Broken Access Control", new Integer( 210 ) );
/**
* Description of the Field
*/
public final static Category A3 = new Category( "Broken Authentication and Session Management", new Integer( 310 ) );
/**
* Description of the Field
*/
public final static Category A4 = new Category( "Cross-Site Scripting (XSS)", new Integer( 410 ) );
/**
* Description of the Field
*/
public final static Category A5 = new Category( "Buffer Overflows", new Integer( 510 ) );
/**
* Description of the Field
*/
public final static Category A6 = new Category( "Injection Flaws", new Integer( 610 ) );
/**
* Description of the Field
*/
public final static Category A7 = new Category( "Improper Error Handling", new Integer( 710 ) );
/**
* Description of the Field
*/
public final static Category A8 = new Category( "Insecure Storage", new Integer( 810 ) );
/**
* Description of the Field
*/
public final static Category A9 = new Category( "Denial of Service", new Integer( 910 ) );
/**
* Description of the Field
*/
public final static Category WEB_SERVICES = new Category( "Web Services", new Integer( 1110 ) );
public final static Category ADMIN_FUNCTIONS = new Category( "Admin Functions", new Integer( 10 ) );
public final static Category GENERAL = new Category( "General", new Integer( 50 ) );
public final static Category CODE_QUALITY = new Category( "Code Quality", new Integer( 70 ) );
public final static Category CHALLENGE = new Category( "Challenge", new Integer( 2000 ) );
/**
* Description of the Field
*/
public final static String ADMIN_ROLE = "admin";
public final static String CHALLENGE_ROLE = "challenge";
/**
* Description of the Field
*/
public final static String HACKED_ADMIN_ROLE = "hacked_admin";
/**
* Description of the Field
*/
public final static String USER_ROLE = "user";
private static int count = 1;
private Integer id = null;
final static IMG nextGrey = new IMG( "images/right16.gif" ).setAlt( "Next" ).setBorder( 0 ).setHspace( 0 )
.setVspace( 0 );
final static IMG previousGrey = new IMG( "images/left14.gif" ).setAlt( "Previous" ).setBorder( 0 ).setHspace( 0 )
.setVspace( 0 );
private static Vector categories = new Vector();
private Integer ranking;
private Category category;
private boolean hidden;
private String sourceFileName;
private String lessonPlanFileName;
static
{
categories.add(A1);
categories.add(A2);
categories.add(A3);
categories.add(A4);
categories.add(A5);
categories.add(A6);
categories.add(A7);
categories.add(A8);
categories.add(A9);
categories.add(A10);
categories.add(WEB_SERVICES);
categories.add(ADMIN_FUNCTIONS);
categories.add(GENERAL);
categories.add(CODE_QUALITY);
categories.add(CHALLENGE);
}
/**
* Constructor for the Lesson object
*/
public AbstractLesson()
{
id = new Integer( ++count );
}
public String getName()
{
String className = getClass().getName();
return className.substring(className.lastIndexOf('.') + 1);
}
public void setRanking(Integer ranking)
{
this.ranking = ranking;
}
public void setHidden(boolean hidden)
{
this.hidden = hidden;
}
public static Category getCategory(String myCategoryName)
{
Category myCategory = null;
Iterator i = categories.iterator();
boolean done = false;
while (i.hasNext() && !done)
{
Category category = (Category) i.next();
if (category.getName().equalsIgnoreCase(myCategoryName))
{
myCategory = category;
done = true;
}
}
return myCategory;
}
public void update(WebgoatProperties properties)
{
String className = getClass().getName();
className = className.substring(className.lastIndexOf(".") + 1);
setRanking(new Integer(properties.getIntProperty("lesson." + className + ".ranking", getDefaultRanking().intValue())));
String categoryRankingKey = "category." + getDefaultCategory().getName() + ".ranking";
//System.out.println("Category ranking key: " + categoryRankingKey);
Category tempCategory = AbstractLesson.getCategory(getDefaultCategory().getName());
tempCategory.setRanking(new Integer(properties.getIntProperty(categoryRankingKey, getDefaultCategory().getRanking().intValue())));
category = tempCategory;
setHidden(properties.getBooleanProperty("lesson." + className + ".hidden", getDefaultHidden()));
//System.out.println(className + " in " + tempCategory.getName() + " (Category Ranking: " + tempCategory.getRanking() + " Lesson ranking: " + getRanking() + ", hidden:" + hidden +")");
}
public boolean isCompleted(WebSession s)
{
return getLessonTracker( s, this ).getCompleted();
}
/**
* Gets the credits attribute of the AbstractLesson object
*
* @return The credits value
*/
public abstract Element getCredits();
/**
* Description of the Method
*
* @param obj Description of the Parameter
* @return Description of the Return Value
*/
public int compareTo( Object obj )
{
return this.getRanking().compareTo( ( (AbstractLesson) obj ).getRanking() );
}
/**
* Description of the Method
*
* @param obj Description of the Parameter
* @return Description of the Return Value
*/
public boolean equals( Object obj )
{
return this.getScreenId() == ( (AbstractLesson) obj ).getScreenId();
}
/**
* Gets the category attribute of the Lesson object
*
* @return The category value
*/
public Category getCategory()
{
return category;
}
protected abstract Integer getDefaultRanking();
protected abstract Category getDefaultCategory();
protected abstract boolean getDefaultHidden();
public void setCategory(String categoryName)
{
if (categoryName != null)
{
category = getCategory(categoryName);
}
else
{
category = getDefaultCategory();
}
}
/**
* Gets the fileMethod attribute of the Lesson class
*
* @param reader Description of the Parameter
* @param methodName Description of the Parameter
* @param numbers Description of the Parameter
* @return The fileMethod value
*/
public static String getFileMethod( BufferedReader reader, String methodName, boolean numbers )
{
int count = 0;
StringBuffer sb = new StringBuffer();
boolean echo = false;
boolean startCount = false;
int parenCount = 0;
try
{
String line;
while ( ( line = reader.readLine() ) != null )
{
if ( ( line.indexOf( methodName ) != -1 )
&& ( ( line.indexOf( "public" ) != -1 ) || ( line.indexOf( "protected" ) != -1 ) || ( line
.indexOf( "private" ) != -1 ) ) )
{
echo = true;
startCount = true;
}
if ( echo && startCount )
{
if ( numbers )
{
sb.append( pad( ++count ) + " " );
}
sb.append( line + "\n" );
}
if ( echo && ( line.indexOf( "{" ) != -1 ) )
{
parenCount++;
}
if ( echo && ( line.indexOf( "}" ) != -1 ) )
{
parenCount--;
if ( parenCount == 0 )
{
startCount = false;
echo = false;
}
}
}
reader.close();
}
catch ( Exception e )
{
System.out.println( e );
e.printStackTrace();
}
return ( sb.toString() );
}
/**
* Reads text from a file into an ElementContainer. Each line in the file is represented in the
* ElementContainer by a StringElement. Each StringElement is appended with a new-line
* character.
*
* @param reader Description of the Parameter
* @param numbers Description of the Parameter
* @return Description of the Return Value
*/
public static String readFromFile( BufferedReader reader, boolean numbers )
{
return ( getFileText( reader, numbers ) );
}
/**
* Gets the fileText attribute of the Screen class
*
* @param reader Description of the Parameter
* @param numbers Description of the Parameter
* @return The fileText value
*/
public static String getFileText( BufferedReader reader, boolean numbers )
{
int count = 0;
StringBuffer sb = new StringBuffer();
try
{
String line;
while ( ( line = reader.readLine() ) != null )
{
if ( numbers )
{
sb.append( pad( ++count ) + " " );
}
sb.append( line + System.getProperty( "line.separator" ) );
}
reader.close();
}
catch ( Exception e )
{
System.out.println( e );
e.printStackTrace();
}
return ( sb.toString() );
}
/**
* Will this screen be included in an enterprise edition.
*
* @return The ranking value
*/
public boolean isEnterprise()
{
return false;
}
/**
* Gets the hintCount attribute of the Lesson object
*
* @return The hintCount value
*/
public int getHintCount()
{
return getHints().size();
}
protected abstract List getHints();
/**
* Fill in a minor hint that will help people who basically get it, but are stuck on somthing
* silly.
*
* @return The hint1 value
*/
public String getHint(int hintNumber)
{
return (String) getHints().get(hintNumber);
}
/**
* Gets the instructions attribute of the AbstractLesson object
*
* @return The instructions value
*/
public abstract String getInstructions( WebSession s );
/**
* Gets the lessonPlan attribute of the Lesson object
*
* @return The lessonPlan value
*/
protected String getLessonName()
{
int index = this.getClass().getName().indexOf( "lessons." );
return this.getClass().getName().substring( index + "lessons.".length() );
}
/**
* Gets the title attribute of the HelloScreen object
*
* @return The title value
*/
public abstract String getTitle();
/**
* Gets the content of lessonPlanURL
* @param s TODO
*
* @return The HTML content of the current lesson plan
*/
public String getLessonPlan(WebSession s)
{
String src = null;
try
{
//System.out.println("Loading lesson plan file: " + getLessonPlanFileName());
src = readFromFile( new BufferedReader( new FileReader( s.getWebResource(getLessonPlanFileName()) ) ), false );
}
catch ( Exception e )
{
//s.setMessage( "Could not find lesson plan for " + getLessonName());
src = ( "Could not find lesson plan for: " + getLessonName() );
}
return src;
}
/**
* Gets the ranking attribute of the Lesson object
*
* @return The ranking value
*/
public Integer getRanking()
{
if (ranking != null)
{
return ranking;
}
else
{
return getDefaultRanking();
}
}
/**
* Gets the hidden value of the Lesson Object
*
* @return The hidden value
*/
public boolean getHidden()
{
return this.hidden;
}
/**
* Gets the role attribute of the AbstractLesson object
*
* @return The role value
*/
public String getRole()
{
// FIXME: Each lesson should have a role assigned to it. Each user/student
// should also have a role(s) assigned. The user would only be allowed
// to see lessons that correspond to their role. Eventually these roles
// will be stored in the internal database. The user will be able to hack
// into the database and change their role. This will allow the user to
// see the admin screens, once they figure out how to turn the admin
// switch on.
return USER_ROLE;
}
/**
* Gets the uniqueID attribute of the AbstractLesson object
*
* @return The uniqueID value
*/
public int getScreenId()
{
return id.intValue();
}
public String getHtml(WebSession s)
{
String html = null;
// FIXME: This doesn't work for the labs since they do not implement createContent().
String rawHtml = createContent(s).toString();
//System.out.println("Getting raw html content: " + rawHtml.substring(0, Math.min(rawHtml.length(), 100)));
html = convertMetachars(AbstractLesson.readFromFile( new BufferedReader( new StringReader( rawHtml ) ), true ) );
//System.out.println("Getting encoded html content: " + html.substring(0, Math.min(html.length(), 100)));
return html;
}
public String getSource(WebSession s)
{
String source = null;
String src = null;
try
{
//System.out.println("Loading source file: " + getSourceFileName());
src = convertMetacharsJavaCode( readFromFile( new BufferedReader( new FileReader( s.getWebResource(getSourceFileName()) ) ), true ) );
// TODO: For styled line numbers and better memory efficiency, use a custom FilterReader
// that performs the convertMetacharsJavaCode() transform plus optionally adds a styled
// line number. Wouldn't color syntax be great too?
}
catch ( IOException e )
{
s.setMessage( "Could not find source file");
src = ( "Could not find source file" );
}
Html html = new Html();
Head head = new Head();
head.addElement( new Title( getSourceFileName() ) );
head.addElement( new StringElement( "<meta name=\"Author\" content=\"Jeff Williams\">" ) );
head.addElement( new StringElement( "<link rev=\"made\" href=\"mailto:jeff.williams@aspectsecurity.com\">" ) );
Body body = new Body();
body.addElement( new StringElement( src ) );
html.addElement( head );
html.addElement( body );
source = html.toString();
return source;
}
/**
* Get the link that can be used to request this screen.
*
* @return
*/
public String getLink()
{
StringBuffer link = new StringBuffer();
link.append( "attack?" );
link.append( WebSession.SCREEN );
link.append( "=" );
link.append( getScreenId() );
return link.toString();
}
/**
* Get the link to the jsp page used to render this screen.
*
* @return
*/
public String getPage(WebSession s)
{
return null;
}
/**
* Get the link to the jsp template page used to render this screen.
*
* @return
*/
public String getTemplatePage(WebSession s)
{
return null;
}
public abstract String getCurrentAction(WebSession s);
public abstract void setCurrentAction(WebSession s, String lessonScreen);
public void setStage(WebSession s, int stage)
{
//System.out.println("Changed to stage " + stage);
getLessonTracker( s ).setStage(stage);
}
public int getStage(WebSession s)
{
int stage = getLessonTracker( s ).getStage();
//System.out.println("In stage " + stage);
return stage;
}
/**
* Override this method to implement accesss control in a lesson.
*
* @param s
* @param functionId
* @return
*/
public boolean isAuthorized(WebSession s, int employeeId, String functionId)
{
return false;
}
/**
* Override this method to implement accesss control in a lesson.
*
* @param s
* @param functionId
* @return
*/
public boolean isAuthorized(WebSession s, String role, String functionId)
{
boolean authorized = false;
try
{
String query = "SELECT * FROM auth WHERE role = '" + role + "' and functionid = '" + functionId + "'";
try
{
Statement answer_statement = WebSession.getConnection(s).createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY );
ResultSet answer_results = answer_statement.executeQuery( query );
authorized = answer_results.first();
}
catch ( SQLException sqle )
{
s.setMessage( "Error authorizing" );
sqle.printStackTrace();
}
}
catch ( Exception e )
{
s.setMessage( "Error authorizing" );
e.printStackTrace();
}
return authorized;
}
public int getUserId(WebSession s) throws ParameterNotFoundException
{
return -1;
}
public String getUserName(WebSession s) throws ParameterNotFoundException
{
return null;
}
/**
* Description of the Method
*
* @param s Description of the Parameter
* @return Description of the Return Value
*/
protected TD makeParamDump_DELETEME( WebSession s )
{
Vector v = new Vector();
if ( s.getParser() != null )
{
Enumeration e = s.getParser().getParameterNames();
while ( ( e != null ) && e.hasMoreElements() )
{
String name = (String) e.nextElement();
String[] values = s.getParser().getParameterValues( name );
for ( int loop = 0; ( values != null ) && ( loop < values.length ); loop++ )
{
v.add( name + " -> " + values[loop] );
}
}
Collections.sort( v );
}
UL list = new UL();
if ( v.size() == 0 )
{
list.addElement( new LI( "No parameters" ) );
}
Iterator i = v.iterator();
while ( i.hasNext() )
{
String str = (String) i.next();
list.addElement( new LI( str ) );
}
ElementContainer ec = new ElementContainer();
ec.addElement( new B( "Parameters from HTTP Request" ) );
ec.addElement( list );
return ( new TD().setVAlign( "TOP" ).addElement( ec ) );
}
// this doesn't work -- I think it's because getting parameters
// also causes the servlet container to read the request
// but I'm not sure.
/**
* Description of the Method
*
* @param s Description of the Parameter
* @return Description of the Return Value
*/
protected Element makeRequestDump_DELETEME( WebSession s )
{
Element el = null;
try
{
el = new StringElement( readFromFile( s.getRequest().getReader(), false ) );
}
catch ( Exception e )
{
s.setMessage( "Couldn't read HTTP request" );
}
ElementContainer ec = new ElementContainer();
ec.addElement( new B( "HTTP Request" ) );
ec.addElement( el );
Table t = new Table().setCellSpacing( 0 ).setCellPadding( 0 ).setBorder( 0 );
if ( s.isColor() )
{
t.setBorder( 1 );
}
t.addElement( new TR().addElement( new TD().setVAlign( "TOP" ).addElement( ec ) ) );
return ( t );
}
/**
* Description of the Method
*
* @param s Description of the Parameter
* @return Description of the Return Value
*/
protected Element makeSourceDump_DELETEME( WebSession s )
{
if ( !s.showSource() )
{
return new StringElement();
}
String filename = s.getWebResource( this.getClass().getName() );
Table t = new Table().setWidth( Screen.MAIN_SIZE );
/*
if ( s.isColor() )
{
t.setBorder( 1 );
t.setBgColor( HtmlColor.CORAL );
}
*/
t.addElement( new TR().addElement( new TD().addElement( new HR() ) ) );
try
{
t.addElement( new TR().addElement( new TD().addElement( convertMetachars( readFromFile( new BufferedReader(
new FileReader( filename ) ), true ) ) ) ) );
}
catch ( IOException e )
{
System.out.println( "reading file EXCEPTION: " + filename );
s.setMessage( "Could not find source file" );
}
return ( t );
}
/**
* Description of the Method
*
* @param windowName Description of the Parameter
* @return Description of the Return Value
*/
public static String makeWindowScript( String windowName )
{
// FIXME: make this string static
StringBuffer script = new StringBuffer();
script.append( "<script language=\"JavaScript\">\n" );
script.append( " <!--\n" );
script.append( " function makeWindow(url) {\n" );
script.append( "\n" );
script.append( " agent = navigator.userAgent;\n" );
script.append( "\n" );
script.append( " params = \"\";\n" );
script.append( " params += \"toolbar=0,\";\n" );
script.append( " params += \"location=0,\";\n" );
script.append( " params += \"directories=0,\";\n" );
script.append( " params += \"status=0,\";\n" );
script.append( " params += \"menubar=0,\";\n" );
script.append( " params += \"scrollbars=1,\";\n" );
script.append( " params += \"resizable=1,\";\n" );
script.append( " params += \"width=500,\";\n" );
script.append( " params += \"height=350\";\n" );
script.append( "\n" );
script.append( " // close the window to vary the window size\n" );
script.append( " if (typeof(win) == \"object\" && !win.closed){\n" );
script.append( " win.close();\n" );
script.append( " }\n" );
script.append( "\n" );
script.append( " win = window.open(url, '" + windowName + "' , params);\n" );
script.append( "\n" );
script.append( " // bring the window to the front\n" );
script.append( " win.focus();\n" );
script.append( " }\n" );
script.append( " //-->\n" );
script.append( " </script>\n" );
return script.toString();
}
/**
* Simply reads a url into an Element for display. CAUTION: you might want to tinker with any
* non-https links (href)
*
* @param url Description of the Parameter
* @return Description of the Return Value
*/
public static Element readFromURL( String url )
{
ElementContainer ec = new ElementContainer();
try
{
URL u = new URL( url );
HttpURLConnection huc = (HttpURLConnection) u.openConnection();
BufferedReader reader = new BufferedReader( new InputStreamReader( huc.getInputStream() ) );
String line;
while ( ( line = reader.readLine() ) != null )
{
ec.addElement( new StringElement( line ) );
}
reader.close();
}
catch ( Exception e )
{
System.out.println( e );
e.printStackTrace();
}
return ( ec );
}
/**
* Description of the Method
*
* @param reader Description of the Parameter
* @param numbers Description of the Parameter
* @param methodName Description of the Parameter
* @return Description of the Return Value
*/
public static Element readMethodFromFile( BufferedReader reader, String methodName, boolean numbers )
{
PRE pre = new PRE().addElement( getFileMethod( reader, methodName, numbers ) );
return ( pre );
}
/**
* Description of the Method
*
* @param s Description of the Parameter
*/
public void handleRequest( WebSession s )
{
// call createContent first so messages will go somewhere
Form form = new Form( getFormAction(), Form.POST ).setName( "form" ).setEncType( "" );
form.addElement( createContent( s ) );
setContent(form);
}
protected String getFormAction()
{
return "attack"+ "?menu="+ getCategory().getRanking();
}
/**
* Description of the Method
*
* @param s Description of the Parameter
* @return Description of the Return Value
*/
/*
public String makeSourceHTML( WebSession s )
{
String className = this.getClass().getName();
String lessonName = className.substring( className.indexOf( '.' ) + 1 );
String filename = s.getSourceFile( lessonName );
String src = null;
try
{
src = convertMetacharsJavaCode( readFromFile( new BufferedReader( new FileReader( filename ) ), true ) );
}
catch ( IOException e )
{
s.setMessage( "Could not find source file" );
src = ( "Could not find source file" );
}
Html html = new Html();
Head head = new Head();
head.addElement( new Title( lessonName + ".java" ) );
head.addElement( new StringElement( "<meta name=\"Author\" content=\"Jeff Williams\">" ) );
head.addElement( new StringElement( "<link rev=\"made\" href=\"mailto:jeff.williams@aspectsecurity.com\">" ) );
Body body = new Body();
body.addElement( new StringElement( src ) );
html.addElement( head );
html.addElement( body );
return html.toString();
}
*/
public String toString()
{
return getTitle();
}
public String getLessonPlanFileName()
{
return lessonPlanFileName;
}
public void setLessonPlanFileName(String lessonPlanFileName)
{
this.lessonPlanFileName = lessonPlanFileName;
}
public String getSourceFileName()
{
return sourceFileName;
}
public void setSourceFileName(String sourceFileName)
{
//System.out.println("Setting source file of lesson " + this + " to: " + sourceFileName);
this.sourceFileName = sourceFileName;
}
}