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 Aspect Security * @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: " + ( helpFile.toString().length() == 0 ? "<select file from list below>" : helpFile.toString() ) + "") ); 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"),"
") .replaceAll("(?s)","") .replaceAll("

","
") .replaceAll("
\\s
","
"))); } } 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 hints = new ArrayList(); 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"; } }