|
| 1 | +/* |
| 2 | + * This fuzz processor script will raise alerts based on HTTP response codes. |
| 3 | + * It expects two special parameters: |
| 4 | + * - A regular expression used to match the response code |
| 5 | + * - A string that is either "pass" or "fail", indicating whether or not a |
| 6 | + * matching response is expected ("pass") or unexpected ("fail"). |
| 7 | + * Unexpected responses will cause an alert to be raised. |
| 8 | + */ |
| 9 | + |
| 10 | +// See https://github.com/zaproxy/community-scripts/tree/main/httpfuzzerprocessor |
| 11 | + |
| 12 | +// See https://github.com/zaproxy/community-scripts/blob/main/httpfuzzerprocessor/showDifferences.js |
| 13 | +// for inspiration for differencing logic used to document detected defects. |
| 14 | + |
| 15 | +// This script needs Diff add-on |
| 16 | + |
| 17 | +var DiffTool = Java.type("org.zaproxy.zap.extension.diff.diff_match_patch"); |
| 18 | + |
| 19 | +/* |
| 20 | + * Declare parameters |
| 21 | + */ |
| 22 | +function getRequiredParamsNames() { |
| 23 | + return ["pattern", "sense"]; |
| 24 | +} |
| 25 | + |
| 26 | +function getOptionalParamsNames() { |
| 27 | + return []; |
| 28 | +} |
| 29 | + |
| 30 | +/* |
| 31 | + * Processes the fuzzed message (payloads already injected). |
| 32 | + * |
| 33 | + * Called before forwarding the message to the server. |
| 34 | + * |
| 35 | + * @param {HttpFuzzerTaskProcessorUtils} utils - A utility object that contains functions that ease common tasks. |
| 36 | + * @param {HttpMessage} message - The fuzzed message, that will be forward to the server. |
| 37 | + */ |
| 38 | +function processMessage(utils, message) { |
| 39 | + // Take no action |
| 40 | +} |
| 41 | + |
| 42 | +/* |
| 43 | + * Processes the fuzz result. |
| 44 | + * |
| 45 | + * Called after receiving the fuzzed message from the server. |
| 46 | + * |
| 47 | + * @param {HttpFuzzerTaskProcessorUtils} utils - A utility object that contains functions that ease common tasks. |
| 48 | + * @param {HttpFuzzResult} fuzzResult - The result of sending the fuzzed message. |
| 49 | + * @return {boolean} Whether the result should be accepted, or discarded and not shown. |
| 50 | + */ |
| 51 | +function processResult(utils, fuzzResult) { |
| 52 | + // All the above 'utils' functions are available plus: |
| 53 | + // To raise an alert: |
| 54 | + // utils.raiseAlert(risk, confidence, name, description) |
| 55 | + // To obtain the fuzzed message, received from the server: |
| 56 | + // fuzzResult.getHttpMessage() |
| 57 | + |
| 58 | + // Retrieve (string) parameters and convert to required types |
| 59 | + var params = utils.getParameters(); |
| 60 | + var pattern = new RegExp(params.pattern); // response regex |
| 61 | + var sense = params.sense == "pass"; // true if regex is expected response |
| 62 | + |
| 63 | + // Retrieve response code and test it against supplied pattern |
| 64 | + var fuzzed = fuzzResult.getHttpMessage(); |
| 65 | + var actual = fuzzed.getResponseHeader().getStatusCode().toString(); |
| 66 | + var found = actual.search(pattern) != -1; |
| 67 | + var expected = found == sense; |
| 68 | + var why = ""; |
| 69 | + |
| 70 | + // If unexpected, raise an alert |
| 71 | + if (!expected) { |
| 72 | + // Convert (inverse) of sense to English |
| 73 | + if (sense) { // expected a match but did not get it |
| 74 | + why = " did not match "; |
| 75 | + } else { // expected no match but got one anyway |
| 76 | + why = " matched "; |
| 77 | + } |
| 78 | + |
| 79 | + // "The original message" |
| 80 | + var original = utils.getOriginalMessage(); |
| 81 | + |
| 82 | + // Compare the content of the original and fuzzed requests; this will |
| 83 | + // indicate what changed to cause the problem. This is very nice if you |
| 84 | + // are sending it somewhere that will render HTML, but looks like noise |
| 85 | + // anywhere else. |
| 86 | + //var diffHtml = createDiffHtml( |
| 87 | + // requestAsString(original), |
| 88 | + // requestAsString(fuzzed) |
| 89 | + //); |
| 90 | + |
| 91 | + // Generate a text difference of the two files |
| 92 | + var diffText = createDiffText( |
| 93 | + requestAsString(original), |
| 94 | + requestAsString(fuzzed) |
| 95 | + ); |
| 96 | + |
| 97 | + utils.raiseAlert( |
| 98 | + 3, // High Risk |
| 99 | + 2, // Medium Confidence |
| 100 | + "Unexpected Fuzzer Response", // name |
| 101 | + "The application is failing to handle unexpected input correctly.", // description (long) |
| 102 | + null, // what parameter was fuzzed? we have no idea... |
| 103 | + diffText, // attack |
| 104 | + null, // otherInfo (long) |
| 105 | + null, // solution (long) |
| 106 | + null, // reference (long) |
| 107 | + "The received response " + actual + why + params.pattern + ".", // evidence |
| 108 | + 684, // CWE-684: Incorrect Provision of Specified Functionality |
| 109 | + 20 // WASC-20: Improper Input Handling |
| 110 | + ); |
| 111 | + |
| 112 | + // We don't need any more examples; stop this fuzzer |
| 113 | + utils.stopFuzzer(); |
| 114 | + } |
| 115 | + |
| 116 | + // Always accept the result |
| 117 | + return true; |
| 118 | +} |
| 119 | + |
| 120 | +function requestAsString(httpMessage) { |
| 121 | + var requestHeader = httpMessage.getRequestHeader().toString(); |
| 122 | + var requestBody = httpMessage.getRequestBody().toString(); |
| 123 | + return requestHeader + "\r\n" + requestBody; |
| 124 | +} |
| 125 | + |
| 126 | +function createDiffHtml(original, fuzzed) { |
| 127 | + var diffTool = new DiffTool(); |
| 128 | + var diffList = diffTool.diff_main(original, fuzzed); |
| 129 | + return diffTool.diff_prettyHtml(diffList); |
| 130 | +} |
| 131 | + |
| 132 | +function createDiffText(original, fuzzed) { |
| 133 | + var diffTool = new DiffTool(); |
| 134 | + diffTool.Patch_Margin = 16; // bytes of context for patches |
| 135 | + var patchList = diffTool.patch_make(original, fuzzed); |
| 136 | + return diffTool.patch_toText(patchList); |
| 137 | +} |
0 commit comments