package org.owasp.webgoat; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.TimeZone; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.owasp.webgoat.lessons.AbstractLesson; import org.owasp.webgoat.lessons.WelcomeScreen; import org.owasp.webgoat.lessons.admin.WelcomeAdminScreen; import org.owasp.webgoat.session.Course; import org.owasp.webgoat.session.ErrorScreen; import org.owasp.webgoat.session.Screen; import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.WebSession; /** * Copyright (c) 2002 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 Jeff Williams Aspect Security * @created October 28, 2003 */ public class HammerHead extends HttpServlet { /** * Description of the Field */ protected static SimpleDateFormat httpDateFormat; /** * Description of the Field */ protected WebSession mySession; /** * Set the session timeout to be 2 days */ private final static int sessionTimeoutSeconds = 60 * 60 * 24 * 2; //private final static int sessionTimeoutSeconds = 1; /** * Properties file path */ public static String propertiesPath = null; /** * Description of the Method * * @param request Description of the Parameter * @param response Description of the Parameter * @exception IOException Description of the Exception * @exception ServletException Description of the Exception */ public void doGet( HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException { doPost( request, response ); } /** * Description of the Method * * @param request Description of the Parameter * @param response Description of the Parameter * @exception IOException Description of the Exception * @exception ServletException Description of the Exception */ public void doPost( HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException { Screen screen = null; try { //System.out.println( "HH Entering doPost: " ); //System.out.println( " - HH request " + request); //System.out.println( " - HH principle: " + request.getUserPrincipal() ); //setCacheHeaders(response, 0); ServletContext context = getServletContext(); // FIXME: If a response is written by updateSession(), do not call makeScreen() and writeScreen() mySession = updateSession( request, response, context ); if (response.isCommitted()) return; // Note: For the lesson to track the status, we need to update the lesson tracker object // from the screen.createContent() method. The create content is the only point // where the lesson "knows" what has happened. To track it at a latter point would // require the lesson to have memory. screen = makeScreen( mySession ); // This calls the lesson's handleRequest() if (response.isCommitted()) return; // if the screen parameter exists, the screen was visited via the menu categories, // we won't count these as visits. The user may be able to manipulate the counts // by specifying the screen parameter using a proxy. Good for them! String fromMenus = mySession.getParser().getRawParameter( WebSession.SCREEN, null ); if ( fromMenus == null ) { // if the show source parameter exists, don't add the visit fromMenus = mySession.getParser().getRawParameter( WebSession.SHOW, null ); if ( fromMenus == null ) { screen.getLessonTracker( mySession ).incrementNumVisits(); } } // log the access to this screen for this user UserTracker userTracker = UserTracker.instance(); userTracker.update( mySession, screen ); log( request, screen.getClass().getName() + " | " + mySession.getParser().toString() ); // Redirect the request to our View servlet String userAgent = request.getHeader("user-agent"); String clientBrowser = "Not known!"; if (userAgent != null) { clientBrowser = userAgent; } request.setAttribute("client.browser", clientBrowser); request.getSession().setAttribute("websession", mySession); request.getSession().setAttribute("course", mySession.getCourse()); request.getRequestDispatcher(getViewPage(mySession)).forward(request, response); } catch ( Throwable t ) { t.printStackTrace(); log( "ERROR: " + t ); screen = new ErrorScreen( mySession, t ); } finally { try { this.writeScreen( screen, response ); } catch ( Throwable thr ) { thr.printStackTrace(); log( request, "Could not write error screen: " + thr.getMessage() ); } //System.out.println( "HH Leaving doPost: " ); } } private String getViewPage(WebSession webSession) { String page; // If this session has not seen the landing page yet, go there instead. HttpSession session = webSession.getRequest().getSession(); if (session.getAttribute("welcomed") == null) { session.setAttribute("welcomed", "true"); page = "/webgoat.jsp"; } else page = "/main.jsp"; return page; } /** * Description of the Method * * @param session Description of the Parameter */ private void dumpSession( HttpSession session ) { Enumeration enumerator = session.getAttributeNames(); while ( enumerator.hasMoreElements() ) { String name = (String) enumerator.nextElement(); Object value = session.getAttribute( name ); System.out.println( "Name: " + name ); System.out.println( "Value: " + value ); } } /** * Description of the Method * * @param date Description of the Parameter * @return RFC 1123 http date format */ protected static String formatHttpDate( Date date ) { synchronized ( httpDateFormat ) { return httpDateFormat.format( date ); } } /** * Return information about this servlet * * @return The servletInfo value */ public String getServletInfo() { return "WebGoat is sponsored by Aspect Security."; } /** * Return properties path * * @return servlet context path + WEB_INF */ public void init() throws ServletException { httpDateFormat = new SimpleDateFormat( "EEE, dd MMM yyyyy HH:mm:ss z", Locale.US ); httpDateFormat.setTimeZone( TimeZone.getTimeZone( "GMT" ) ); propertiesPath = getServletContext().getRealPath( "." + System.getProperty("file.separator")+ "WEB-INF" + "/webgoat.properties"); } /** * Description of the Method * * @param request Description of the Parameter * @param message Description of the Parameter */ public void log( HttpServletRequest request, String message ) { String output = new Date() + " | " + request.getRemoteHost() + ":" + request.getRemoteAddr() + " | " + message; log( output ); System.out.println( output ); } public List getCategories() { Course course = mySession.getCourse(); // May need to clone the List before returning it. //return new ArrayList(course.getCategories()); return course.getCategories(); } /* public List getLessons(Category category, String role) { Course course = mySession.getCourse(); // May need to clone the List before returning it. //return new ArrayList(course.getLessons(category, role)); return course.getLessons(category, role); } */ /** * Description of the Method * * @param s Description of the Parameter * @return Description of the Return Value */ protected Screen makeScreen( WebSession s ) { Screen screen = null; int scr = s.getCurrentScreen(); Course course = s.getCourse(); if ( s.isUser() || s.isChallenge() ) { if ( scr == WebSession.WELCOME ) { screen = new WelcomeScreen( s ); } else { AbstractLesson lesson = course.getLesson( s, scr, AbstractLesson.USER_ROLE ); if ( lesson == null && s.isHackedAdmin() ) { // If admin was hacked, let the user see some of the admin screens lesson = course.getLesson( s, scr, AbstractLesson.HACKED_ADMIN_ROLE ); } if ( lesson != null ) { screen = lesson; // We need to do some bookkeeping for the hackable admin interface. // This is the only place we can tell if the user successfully hacked the hackable // admin and has actually accessed an admin screen. You need BOTH pieces of information // in order to satisfy the remote admin lesson. s.setHasHackableAdmin( screen.getRole() ); lesson.handleRequest( s ); s.setCurrentMenu( lesson.getCategory().getRanking() ); } else { screen = new ErrorScreen( s, "Invalid screen requested. Try: http://localhost/WebGoat/attack" ); } } } else if ( s.isAdmin() ) { if ( scr == WebSession.WELCOME ) { screen = new WelcomeAdminScreen( s ); } else { // Admin can see all roles. // FIXME: should be able to pass a list of roles. AbstractLesson lesson = course.getLesson( s, scr, AbstractLesson.ADMIN_ROLE ); if ( lesson == null ) { lesson = course.getLesson( s, scr, AbstractLesson.HACKED_ADMIN_ROLE ); } if ( lesson == null ) { lesson = course.getLesson( s, scr, AbstractLesson.USER_ROLE ); } if ( lesson != null ) { screen = lesson; // We need to do some bookkeeping for the hackable admin interface. // This is the only place we can tell if the user successfully hacked the hackable // admin and has actually accessed an admin screen. You need BOTH pieces of information // in order to satisfy the remote admin lesson. s.setHasHackableAdmin( screen.getRole() ); lesson.handleRequest( s ); s.setCurrentMenu( lesson.getCategory().getRanking() ); } else { screen = new ErrorScreen( s, "Invalid screen requested. Try Setting Admin to false or Try: http://localhost/WebGoat/attack" ); } } } return ( screen ); } /** * This method sets the required expiration headers in the response for a given RunData object. * This method attempts to set all relevant headers, both for HTTP 1.0 and HTTP 1.1. * * @param response The new cacheHeaders value * @param expiry The new cacheHeaders value */ protected static void setCacheHeaders( HttpServletResponse response, int expiry ) { if ( expiry == 0 ) { response.setHeader( "Pragma", "no-cache" ); response.setHeader( "Cache-Control", "no-cache" ); response.setHeader( "Expires", formatHttpDate( new Date() ) ); } else { Date expiryDate = new Date( System.currentTimeMillis() + expiry ); response.setHeader( "Expires", formatHttpDate( expiryDate ) ); } } /** * Description of the Method * * @param request Description of the Parameter * @param response Description of the Parameter * @param context Description of the Parameter * @return Description of the Return Value */ protected WebSession updateSession( HttpServletRequest request, HttpServletResponse response, ServletContext context ) throws IOException { HttpSession hs; hs = request.getSession( true ); //System.out.println( "HH Entering Session_id: " + hs.getId() ); // dumpSession( hs ); // Get our session object out of the HTTP session WebSession session = null; Object o = hs.getAttribute( WebSession.SESSION ); if ( ( o != null ) && o instanceof WebSession ) { session = (WebSession) o; } else { // Create new custom session and save it in the HTTP session //System.out.println( "HH Creating new WebSession: " ); session = new WebSession( this, context ); hs.setAttribute( WebSession.SESSION, session ); // reset timeout hs.setMaxInactiveInterval( sessionTimeoutSeconds ); } session.update( request, response, this.getServletName() ); // to authenticate //System.out.println( "HH Leaving Session_id: " + hs.getId() ); //dumpSession( hs ); return ( session ); } /** * Description of the Method * * @param s Description of the Parameter * @param response Description of the Parameter * @exception IOException Description of the Exception */ protected void writeScreen( Screen s, HttpServletResponse response ) throws IOException { response.setContentType( "text/html" ); PrintWriter out = response.getWriter(); if ( s == null ) { s = new ErrorScreen( mySession, "Page to display was null" ); } // set the content-length of the response. // Trying to avoid chunked-encoding. (Aspect required) response.setContentLength( s.getContentLength() ); response.setHeader("Content-Length",s.getContentLength()+""); s.output( out ); out.close(); } }