300 lines
9.3 KiB
Java

package org.owasp.webgoat.lessons;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.ecs.Element;
import org.apache.ecs.ElementContainer;
import org.apache.ecs.StringElement;
import org.apache.ecs.html.BR;
import org.apache.ecs.html.HR;
import org.apache.ecs.html.P;
import org.apache.ecs.html.PRE;
import org.owasp.webgoat.session.ECSFactory;
import org.owasp.webgoat.session.WebSession;
import org.owasp.webgoat.util.Exec;
import org.owasp.webgoat.util.ExecResults;
/**
* 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 class CommandInjection extends LessonAdapter
{
private final static String HELP_FILE = "HelpFile";
private String osName = System.getProperty( "os.name" );
/**
* 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();
boolean illegalCommand = s.isDefuseOSCommands();
try
{
String helpFile = s.getParser().getRawParameter( HELP_FILE, "BasicAuthentication.help" );
String safeDirName;
if ( s.isDefuseOSCommands() && ( helpFile.indexOf('&') != -1 || helpFile.indexOf(';') != -1) )
{
int index = helpFile.indexOf('&');
if ( index == -1)
{
index = helpFile.indexOf(';');
}
if (( osName.indexOf( "Windows" ) != -1 &&
( helpFile.substring(index + 1).trim().toLowerCase().equals("netstat -a") ||
helpFile.substring(index + 1).trim().toLowerCase().equals("dir") ||
helpFile.substring(index + 1).trim().toLowerCase().equals("ls") ||
helpFile.substring(index + 1).trim().toLowerCase().equals("ifconfig") ||
helpFile.substring(index + 1).trim().toLowerCase().equals("ipconfig") )) ||
(helpFile.substring(index + 1).trim().toLowerCase().equals("netstat -a #") ||
helpFile.substring(index + 1).trim().toLowerCase().equals("dir #") ||
helpFile.substring(index + 1).trim().toLowerCase().equals("ls #") ||
helpFile.substring(index + 1).trim().toLowerCase().equals("ls -l #") ||
helpFile.substring(index + 1).trim().toLowerCase().equals("ifconfig #") ||
helpFile.substring(index + 1).trim().toLowerCase().equals("ipconfig #") ))
{
illegalCommand = false;
}
else
{
s.setMessage("It appears that you are on the right track. " +
"Commands that may compromise the operating system have been disabled. " +
"The following commands are allowed: netstat -a, dir, ls, ifconfig, and ipconfig");
}
}
if ( s.isDefuseOSCommands() && helpFile.indexOf('&') == -1 && helpFile.indexOf(';') == -1 )
{
if ( helpFile.length() > 0 )
{
if ( upDirCount( helpFile ) <= 3 )
{
// FIXME: This value isn't used. What is the goal here?
safeDirName = s.getContext().getRealPath("/") + helpFile;
illegalCommand = false;
}
else
{
s.setMessage("It appears that you are on the right track. " +
"Commands that may compromise the operating system have been disabled. " +
"This lesson is a command injection lesson, not access control.");
}
}
else
{
// No Command entered.
illegalCommand = false;
}
}
File safeDir = new File(s.getContext().getRealPath( "/lesson_plans" ));
ec.addElement( new StringElement( "You are currently viewing: <b>"
+ ( helpFile.toString().length() == 0 ? "&lt;select file from list below&gt;" : helpFile.toString() )
+ "</b>") );
if ( !illegalCommand ) {
String results;
String fileData = null;
helpFile = helpFile.replaceAll("\\.help","\\.html");
if ( osName.indexOf( "Windows" ) != -1 )
{
// Add quotes around the filename to avoid having special characters in DOS filenames
results = exec( s, "cmd.exe /c dir /b \"" + safeDir.getPath() + "\"");
fileData = exec(s, "cmd.exe /c type \"" + new File(safeDir, helpFile).getPath() + "\"");
}
else
{
String[] cmd1 = {"/bin/sh", "-c", "ls \"" + safeDir.getPath() + "\""};
results = exec( s, cmd1 );
String[] cmd2 = {"/bin/sh", "-c", "cat \"" + new File(safeDir, helpFile).getPath() + "\""};
fileData = exec( s, cmd2 );
}
ec.addElement( new P().addElement( "Select the lesson plan to view: " ) );
ec.addElement( ECSFactory.makePulldown( HELP_FILE, parseResults( results.replaceAll("(?s)\\.html", "\\.help") )) );
//ec.addElement( results );
Element b = ECSFactory.makeButton( "View" );
ec.addElement( b );
// Strip out some of the extra html from the "help" file
ec.addElement( new BR() );
ec.addElement( new BR() );
ec.addElement( new HR().setWidth("90%") );
ec.addElement( new StringElement( fileData.replaceAll(System.getProperty("line.separator"),"<br>")
.replaceAll("(?s)<!DOCTYPE.*/head>","")
.replaceAll("<br><br>","<br>")
.replaceAll("<br>\\s<br>","<br>")));
}
}
catch ( Exception e )
{
s.setMessage( "Error generating " + this.getClass().getName() );
e.printStackTrace();
}
return ( ec );
}
private String parseResults( String results )
{
return results.replaceAll("(?s).*Output...\\s","").replaceAll("(?s)Returncode.*","");
}
public static int upDirCount( String fileName )
{
int count = 0;
// check for "." = %2d
// we wouldn't want anyone bypassing the check by useing encoding :)
// FIXME: I don't think hex endoing will work here.
fileName = fileName.replaceAll("%2d",".");
int startIndex = fileName.indexOf("..");
while ( startIndex != -1 )
{
count++;
startIndex = fileName.indexOf("..", startIndex+1);
}
return count;
}
/**
* Description of the Method
*
* @param command Description of the Parameter
* @param s Description of the Parameter
* @return Description of the Return Value
*/
private String exec( WebSession s, String command )
{
System.out.println("Executing OS command: " + command);
ExecResults er = Exec.execSimple( command );
if (( command.indexOf("&" ) != -1 || command.indexOf(";") != -1 ) && !er.getError() )
{
makeSuccess( s );
}
return ( er.toString() );
}
/**
* Description of the Method
*
* @param command Description of the Parameter
* @param s Description of the Parameter
* @return Description of the Return Value
*/
private String exec( WebSession s, String[] command )
{
System.out.println("Executing OS command: " + Arrays.asList(command));
ExecResults er = Exec.execSimple( command );
if ( !er.getError() )
{
makeSuccess( s );
}
return ( er.toString() );
}
/**
* Description of the Method
*
* @param command Description of the Parameter
* @param args Description of the Parameter
* @param s Description of the Parameter
* @return Description of the Return Value
*/
private Element exec( WebSession s, String command, String args )
{
System.out.println("Executing OS command: '" + command + "' with args: '" + args + "'");
ExecResults er = Exec.execSimple( command, args );
if (( args.indexOf("&" ) != -1 || args.indexOf(";") != -1 ) && !er.getError() )
{
makeSuccess( s );
}
PRE p = new PRE().addElement( er.toString() );
return ( p );
}
/**
* Gets the category attribute of the CommandInjection object
*
* @return The category value
*/
protected Category getDefaultCategory()
{
return AbstractLesson.A6;
}
/**
* Gets the hints attribute of the DirectoryScreen object
*
* @return The hints value
*/
protected List getHints()
{
List<String> hints = new ArrayList<String>();
hints.add( "The application is using a system command to return the contents of a file." );
hints.add( "The ampersand(&) separates commands in the Windows 2000 command shell. In Unix the separator is typically a semi-colon(;)" );
hints.add( "Use a proxy to insert & netstat -a on Windows or ;netstat -a on Unix." );
hints.add( "Note that the server may enclose the submitted file name within quotes" );
return hints;
}
/**
* Gets the instructions attribute of the ParameterInjection object
*
* @return The instructions value
*/
public String getInstructions(WebSession s)
{
String instructions = "Choose the lesson plan you would like to view. " +
"Try to inject a command to the operating system.";
return ( instructions );
}
private final static Integer DEFAULT_RANKING = new Integer(40);
protected Integer getDefaultRanking()
{
return DEFAULT_RANKING;
}
/**
* Gets the title attribute of the DirectoryScreen object
*
* @return The title value
*/
public String getTitle()
{
return "How to Perform Command Injection";
}
}