//import javax.annotation.PostConstruct; import burp.IBurpExtender; import burp.IBurpExtenderCallbacks; import burp.IScanIssue; import burp.IHttpRequestResponse; import org.jruby.*; import org.jruby.javasupport.JavaUtil; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; /** * This is an implementation of the BurpExtender/IBurpExtender interface * for Burp Suite which provides glue between a Ruby runtime and Burp. * * This is a complete implementation of the Burp Extender interfaces available * as of Burp Suite 1.2/1.2.05 */ public class BurpExtender implements IBurpExtender { public final static String INIT_METH = "evt_extender_init"; public final static String PROXYMSG_METH = "evt_proxy_message_raw"; public final static String HTTPMSG_METH = "evt_http_message"; public final static String SCANISSUE_METH = "evt_scan_issue"; public final static String MAINARGS_METH = "evt_commandline_args"; public final static String REG_METH = "evt_register_callbacks"; public final static String CLOSE_METH = "evt_application_closing"; // Internal reference to hold the ruby Burp handler private static IRubyObject r_obj = null; /** * Sets an internal reference to the ruby handler class or module to use * for proxied BurpExtender events into a ruby runtime. * * Generally, this should probably be called before burp.StartBurp.main. * However, it is also possible to set this afterwards and even swap in * new objects during runtime. */ public static void set_handler(IRubyObject hnd) { r_obj = hnd; } /** * Returns the internal Ruby handler reference. * * The handler is the ruby class or module used for proxying BurpExtender * events into a ruby runtime. */ public static IRubyObject get_handler() { return r_obj; } /** * This constructor is invoked from Burp's extender framework. * * This implementation invokes the INIT_METH method * from the Ruby handler object if one is defined passing it Ruby * usable reference to the instance. * */ public BurpExtender() { if (r_obj !=null && r_obj.respondsTo(INIT_METH)) r_obj.callMethod(ctx(r_obj), INIT_METH, to_ruby(rt(r_obj), this)); } /** * This method is invoked immediately after the implementation's constructor * to pass any command-line arguments that were passed to Burp Suite on * startup. * * This implementation invokes the method defined by * MAINARGS_METH in the Ruby handler if both the handler * and its ruby method are defined. * * It allows Ruby implementations to control aspects of their behaviour at * runtime by defining their own command-line arguments. * * WARNING: Burp appears to have a bug (as of 1.2 and 1.2.05) which causes * Burp to exit immediately if arguments are supplied regardless whether * this handler is used. * * @param args The command-line arguments passed to Burp Suite on startup. */ public void setCommandLineArgs(String[] args) { if(r_obj != null && r_obj.respondsTo(MAINARGS_METH)) r_obj.callMethod(ctx(r_obj), MAINARGS_METH, to_ruby(rt(r_obj), args)); } /** * This method is invoked on startup. It registers an instance of the * burp.IBurpExtenderCallbacks interface, providing methods * that may be invoked by the implementation to perform various actions. * * The call to registerExtenderCallbacks need not return, and * implementations may use the invoking thread for any purpose.

* * This implementation simply passes a ruby-usable "callbacks" instance to * the Ruby handler using the method defined by REG_METH if * both the handler and its ruby method are defined. * * @param callbacks An implementation of the * IBurpExtenderCallbacks interface. */ public void registerExtenderCallbacks(IBurpExtenderCallbacks cb) { if(r_obj != null && r_obj.respondsTo(REG_METH)) { cb.issueAlert("[BurpExtender] registering JRuby handler callbacks"); r_obj.callMethod(ctx(r_obj), REG_METH, to_ruby(rt(r_obj), cb)); } } /** * This method is invoked by Burp Proxy whenever a client request or server * response is received. * * This implementation simply passes all arguments to the Ruby handler's * method defined by PROXYMSG_METH if both the handler and * its ruby method are defined. * * This allows Ruby implementations to perform logging functions, modify * the message, specify an action (intercept, drop, etc.) and perform any * other arbitrary processing. * * @param messageReference An identifier which is unique to a single * request/response pair. This can be used to correlate details of requests * and responses and perform processing on the response message accordingly. * @param messageIsRequest Flags whether the message is a client request or * a server response. * @param remoteHost The hostname of the remote HTTP server. * @param remotePort The port of the remote HTTP server. * @param serviceIsHttps Flags whether the protocol is HTTPS or HTTP. * @param httpMethod The method verb used in the client request. * @param url The requested URL. * @param resourceType The filetype of the requested resource, or a * zero-length string if the resource has no filetype. * @param statusCode The HTTP status code returned by the server. This value * is null for request messages. * @param responseContentType The content-type string returned by the * server. This value is null for request messages. * @param message The full HTTP message. * @param action An array containing a single integer, allowing the * implementation to communicate back to Burp Proxy a non-default * interception action for the message. The default value is * ACTION_FOLLOW_RULES. Set action[0] to one of * the other possible values to perform a different action. * @return Implementations should return either (a) the same object received * in the message paramater, or (b) a different object * containing a modified message. */ public byte[] processProxyMessage( int messageReference, boolean messageIsRequest, String remoteHost, int remotePort, boolean serviceIsHttps, String httpMethod, String url, String resourceType, String statusCode, String responseContentType, byte[] message, int[] action ) { if (r_obj != null && r_obj.respondsTo(PROXYMSG_METH)) { Ruby rt = rt(r_obj); // prepare an alternate action value to present to ruby IRubyObject r_action = to_ruby(rt, action); // prepare an alternate String message value to present to ruby //String message_str = new String(message); IRubyObject r_msg = to_ruby(rt, message); IRubyObject pxy_msg[] = { to_ruby(rt, messageReference), to_ruby(rt, messageIsRequest), to_ruby(rt, remoteHost), to_ruby(rt, remotePort), to_ruby(rt, serviceIsHttps), to_ruby(rt, httpMethod), to_ruby(rt, url), to_ruby(rt, resourceType), to_ruby(rt, statusCode), to_ruby(rt, responseContentType), r_msg, r_action }; // slurp back in the action value in-case it's been changed action[0] = ((int[]) JavaUtil.convertRubyToJava(r_action))[0]; IRubyObject ret = r_obj.callMethod(ctx(r_obj), PROXYMSG_METH, pxy_msg); if(ret != r_msg) { return (byte[]) JavaUtil.convertRubyToJava(ret); } } return message; } /** * Added in Burp 1.2.09 * No javadoc yet but here's what the PortSwigger dev blog has to say: * * The processHttpMessage method is invoked whenever any of Burp's tools * makes an HTTP request or receives a response. This is effectively a * generalised version of the existing processProxyMessage method, and * can be used to intercept and modify the HTTP traffic of all Burp * tools. */ public void processHttpMessage( String toolName, boolean messageIsRequest, IHttpRequestResponse messageInfo ) { if (r_obj != null && r_obj.respondsTo(HTTPMSG_METH)) { Ruby rt = rt(r_obj); IRubyObject http_msg[] = { to_ruby(rt, toolName), to_ruby(rt, messageIsRequest), to_ruby(rt, messageInfo) }; r_obj.callMethod(ctx(r_obj), HTTPMSG_METH, http_msg); } } /** * Added in Burp 1.2.09 * * The newScanIssue method is invoked whenever Burp Scanner discovers a * new, unique issue, and can be used to perform customised reporting or * logging of issues. */ public void newScanIssue(IScanIssue issue) { if (r_obj != null && r_obj.respondsTo(SCANISSUE_METH)) r_obj.callMethod(ctx(r_obj), SCANISSUE_METH, to_ruby(rt(r_obj), issue)); } /** * This method is invoked immediately before Burp Suite exits. * This implementation simply invokes the Ruby handler's method defined * by CLOSE_METH if both the handler and its ruby method are * defined. * * This allows implementations to carry out any clean-up actions necessary * (e.g. flushing log files or closing database resources, etc.). */ public void applicationClosing() { if (r_obj != null && r_obj.respondsTo(CLOSE_METH)) r_obj.callMethod(ctx(r_obj), CLOSE_METH); } // Private method to return the ThreadContext for a given ruby object. // This is used in the various event proxies private ThreadContext ctx(IRubyObject obj) { return rt(obj).getThreadService().getCurrentContext(); } // Private method to return the ruby runtime for a given ruby object. // This is used in the various event proxies private Ruby rt(IRubyObject obj) { return obj.getRuntime(); } // private method to transfer arbitrary java objects into a ruby runtime. // This is used in the various event proxies to pass arguments to the // ruby handler object. private IRubyObject to_ruby(Ruby rt, Object obj) { return JavaUtil.convertJavaToUsableRubyObject(rt, obj); } /** * Causes Burp Proxy to follow the current interception rules to determine * the appropriate action to take for the message. */ public final static int ACTION_FOLLOW_RULES = 0; /** * Causes Burp Proxy to present the message to the user for manual * review or modification. */ public final static int ACTION_DO_INTERCEPT = 1; /** * Causes Burp Proxy to forward the message to the remote server or client. */ public final static int ACTION_DONT_INTERCEPT = 2; /** * Causes Burp Proxy to drop the message and close the client connection. */ public final static int ACTION_DROP = 3; /** * Causes Burp Proxy to follow the current interception rules to determine * the appropriate action to take for the message, and then make a second * call to processProxyMessage. */ public final static int ACTION_FOLLOW_RULES_AND_REHOOK = 0x10; /** * Causes Burp Proxy to present the message to the user for manual * review or modification, and then make a second call to * processProxyMessage. */ public final static int ACTION_DO_INTERCEPT_AND_REHOOK = 0x11; /** * Causes Burp Proxy to skip user interception, and then make a second call * to processProxyMessage. */ public final static int ACTION_DONT_INTERCEPT_AND_REHOOK = 0x12; }