├── .gitignore ├── README.md ├── demo.gif ├── example └── certstreamcatcher.js ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Certstreamcatcher 2 | Catching phishing by observing [certificate transparency logs](https://www.certificate-transparency.org/known-logs). This tool is based on regex with effective standards for detecting phishing sites in real time using [certstream](https://github.com/CaliDog/certstream-js) and can also detect [punycode (IDNA)](https://en.wikipedia.org/wiki/Punycode) attacks such as https://www.ṁyetḣerwallet.com. 3 | 4 | 5 | ### Phishing 6 | 7 | [![Phishing](https://pbs.twimg.com/media/DY-k7YFWsAA_f8l.jpg)](https://twitter.com/6IX7ine/status/943229448614182912) 8 | 9 | 10 | ### Installation 11 | 12 | ``` 13 | $ cd /opt/ 14 | $ git clone https://github.com/6IX7ine/certstreamcatcher.git 15 | $ cd certstreamcatcher 16 | $ npm install 17 | ``` 18 | 19 | ### npm package 20 | 21 | To install certstreamcatcher using `npm` run: 22 | 23 | npm install --save certstreamcatcher 24 | 25 | ### Try on npm runkit 26 | 27 | This is a playground to test certstreamcatcher 28 | 29 | [https://npm.runkit.com/certstreamcatcher](https://npm.runkit.com/certstreamcatcher) 30 | 31 | ### Usage 32 | The certstreamcatcher is extremely simple, all you have to do is to import the library **certstreamcatcher** and certstream register the callback and call **certstreamClientPhishing** and pass the callback parameter to certstreamClientPhishing. 33 | 34 | ``` 35 | const certstreamcatcher = require('certstreamcatcher'); 36 | const certstream = require("certstream"); 37 | 38 | const regex = /(wellsfargo|paypal|login|sign-in|secure|update|money|sslsecure|amazon)/gi; # Keywords 39 | 40 | const tlds = ['.io','.gq','.ml','.cf','.tk','.xyz','.pw','.cc']; # tlds 41 | 42 | var client = new certstream(function(certstream) { 43 | certstreamcatcher.certstreamClientPhishing(certstream, regex, tlds, {tlds: true}); 44 | }); 45 | 46 | client.connect(); 47 | ``` 48 | To execute the program save the above code and execute with the command: 49 | ``` 50 | $ node certstreamcatcher.js 51 | ``` 52 | 53 | 54 | 55 | 56 | 57 | ## Donations 58 | * XMR: `49m12JEEC6HPCHkLMX5QL4SrDQdKwh6eb4Muu8Z9CwA9MwemhzFQ3VcgHwyuR73rC22WCymTUyep7DVrfN3GPt5JBCekPrR` 59 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimywork/certstreamcatcher/b0d8f9b7b236e5d9004eed045bc5277ccfae7924/demo.gif -------------------------------------------------------------------------------- /example/certstreamcatcher.js: -------------------------------------------------------------------------------- 1 | 2 | const catcherstream = require('../index'); 3 | const certstream = require("certstream"); 4 | 5 | const keywords = /(?:verification|webscr|credential|support|activity|security|update|authentication|authenticate|authorize|wallet|purchase|transaction|recover|unlock|confirm|live|service|manage|invoice|secure|customer|client|online|safe|priv8|santander|pagseguro|bitcoin|yobit|bitfinex|etherdelta|iqoption|localbitcoins|ethereum|wallet|mymonero|blockchain|bitflyer|coinbase|hitbtc|lakebtc|bitfinex|bitconnect|coinsbank|moneypaypal|moneygram|westernunion|bankofamerica|wellsfargo|itau|nubank|paypal|bittrex|blockchain|netflix|gmail|yahoo|google|apple|amazon|cryptopia|localbitcoins|gdax|changelly|binance|hitbtc|bithumb|luno|coinatmradar|poloniex|shapeshift|bitmex|gemini|bitbay|coinmama|bitflyer|bisq|btcmarkets|coincheck|zebpay|indacoin|coinhive)/gi; 6 | 7 | const tlds = ['.io','.gq','.ml','.cf','.tk','.xyz','.pw','.cc','.club','.work','.top','.support','.bank','.info','.study','.party','.click','.country','.stream','.gdn','.mom','.xin','.kim','.men', '.loan', '.download', '.racing', '.online', '.center', '.ren', '.gb', '.win', '.review', '.vip', '.party', '.tech', '.science', '.business', '.com']; 8 | 9 | var client = new certstream(function(certstream) { 10 | catcherstream.certstreamClientPhishing(certstream, keywords, tlds, {tlds: false}); 11 | }); 12 | 13 | client.connect(); 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Github 3 | * https://github.com/6IX7ine/certstreamcatcher 4 | * 5 | * Copyright (c) 2017 Fábio Castro 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict' 10 | 11 | const tld = require('tldjs'); 12 | const lodash = require('lodash'); 13 | const color = require('cli-color'); 14 | const status = require('node-status'); 15 | const punycode = require('punycode'); 16 | 17 | const yellow = color.yellow.underline; 18 | const danger = color.redBright.underline; 19 | const white = color.white.underline; 20 | 21 | const certificates = status.addItem('certificates'); 22 | 23 | const mapping = { 24 | 25 | "a": ["à", "á", "â", "ã", "ä", "å", "ā", "ă", "ą", "ǎ", "ȁ", "ȃ", "ȧ", "ḁ", "ẚ", "ạ"], 26 | "b": ["ḃ", "ḅ", "ḇ", "ƀ"], 27 | "c": ["ç", "ć", "ĉ", "ċ", "č"], 28 | "d": ["ḋ", "ḍ", "ḏ", "ḑ", "ḓ", "đ"], 29 | "e": ["è", "é", "ê", "ë", "ĕ", "ė", "ę", "ě", "ȅ", "ȇ", "ȩ", "ḙ", "ḛ", "ẹ", "ẽ"], 30 | "f": ["ḟ"], 31 | "g": ["ĝ", "ğ", "ġ", "ģ", "ǧ", "ǵ", "ḡ"], 32 | "h": ["ĥ", "ȟ", "ḣ", "ḥ", "ḧ", "ḩ", "ḫ", "ẖ"], 33 | "i": ["ì", "í", "î", "ï", "ĩ", "ī", "ĭ", "į", "ǐ", "ȉ", "ȋ", "ḭ", "ị"], 34 | "j": ["ĵ", "ǰ"], 35 | "k": ["ķ", "ǩ", "ḱ", "ḳ", "ḵ"], 36 | "l": ["ĺ", "ļ", "ľ", "ŀ", "ḷ", "ḻ", "ḽ", "ι", "ł"], 37 | "m": ["ḿ", "ṁ", "ṃ", "ϻ"], 38 | "n": ["ñ", "ń", "ņ", "ň", "ʼn", "ǹ", "ṅ", "ṇ", "ṉ", "ṋ"], 39 | "o": ["ò", "ó", "ô", "õ", "ö", "ō", "ŏ", "ő", "ơ", "ǒ", "ǫ", "ố", "ȍ", "ȏ", "ȯ", "ọ", "ø", "ộ"], 40 | "p": ["ṕ", "ṗ", "ϸ"], 41 | "r": ["ŕ", "ŗ", "ř", "ȑ", "ȓ", "ṙ", "ṛ", "ṟ", "ґ"], 42 | "s": ["ŝ", "ş", "š", "ș", "ṡ", "ṣ"], 43 | "t": ["ţ", "ť", "ț", "ṫ", "ṭ", "ṯ", "ṱ", "ẗ"], 44 | "u": ["ù", "ú", "û", "ü", "ũ", "ū", "ŭ", "ů", "ű", "ų", "ư", "ǔ", "ȕ", "ȗ", "ṳ", "ṵ", "ṷ", "ụ"], 45 | "v": ["ṽ", "ṿ"], 46 | "w": ["ŵ", "ẁ", "ẃ", "ẅ", "ẇ", "ẉ", "ẘ"], 47 | "x": ["ẋ", "ẍ"], 48 | "y": ["ý", "ÿ", "ŷ", "ȳ", "ẏ", "ẙ", "ỳ", "ỵ", "ỹ"], 49 | "z": ["ź", "ż", "ž", "ẑ", "ẓ", "ẕ"] 50 | } 51 | 52 | module.exports = { 53 | 54 | certstreamClientPhishing: function (certstream, regex, tlds, options) { 55 | 56 | this.certstream = certstream; 57 | this.regex = regex; 58 | this.tlds = tlds; 59 | 60 | 61 | let settings = { tlds: false }; 62 | 63 | if (!options) { options = {}; } 64 | 65 | for(var option in settings) { 66 | if (typeof options[option] !== 'undefined') { 67 | settings[option] = options[option]; 68 | } else { 69 | settings[option] = settings[option]; 70 | } 71 | } 72 | 73 | // Retorna vazio se não retornar uma messagem de atualização 74 | 75 | if (!lodash.includes(certstream, 'certificate_update')) { 76 | return; 77 | } 78 | 79 | 80 | let domains = certstream.data.leaf_cert.all_domains; 81 | let certs = certstream.data.chain; 82 | 83 | lodash.forEach(domains, function(domains) { 84 | 85 | 86 | if (lodash.startsWith(domains, '*.')) { 87 | domains = lodash.replace(domains, '*.', '', 0); 88 | } 89 | 90 | // Expressões regulares criadas com base no comportamento dos sites de phishing 91 | 92 | let keywords = (domains.match(regex) || []); // Keywords do dominio inteiro 93 | 94 | // Filter o dominio 95 | let domain = tld.parse(domains).domain; 96 | 97 | // Filter subdominio 98 | let subdomain = tld.parse(domains).subdomain; 99 | let tlddomain = tld.parse(domains).publicSuffix; 100 | 101 | 102 | if ((lodash.isNull(subdomain) && lodash.isEmpty(subdomain))) { return; } // Dominios com o subdomain vazio ou null 103 | if ((lodash.isNull(domain) && lodash.isEmpty(domain))) { return; } 104 | 105 | subdomain = (subdomain.match(regex) || []); // Subdomain Keywords 106 | domain = (domain.match(regex) || []); // Domain Keywords 107 | 108 | const dashed = (domains.match(/\-/g) || []).length; 109 | 110 | const points = (domains.match(/(\.)/g) || []).length; 111 | 112 | 113 | // Certificados gratuitos são mais propensos a serem utilizados para phishing 114 | 115 | let suspicious = false; 116 | 117 | lodash.forEach(certs, function(cert) { 118 | if (lodash.hasIn(cert,"subject")) { 119 | lodash.forOwn(cert, function(keyword) { 120 | if (lodash.includes(cert.subject.aggregated, "Let's Encrypt") || lodash.includes(cert.subject.aggregated, "CACert free certificate") 121 | || lodash.includes(cert.subject.aggregated, "StartSSL" 122 | || lodash.includes(cert.subject.aggregated, "Cloudflare") 123 | || lodash.includes(cert.subject.aggregated, "Free SSL"))) 124 | { 125 | suspicious = true; 126 | } 127 | }); 128 | } 129 | }); 130 | 131 | 132 | if (lodash.startsWith(domains, 'xn--', 0)) { 133 | 134 | let punyencode = punycode.toUnicode(domains); 135 | let punydecode = punycode.ucs2.decode(punyencode); 136 | 137 | lodash.forEach(punydecode, function(punycode) { 138 | 139 | if (punycode >= 128) { 140 | for(var keyword in mapping) { 141 | 142 | var maps = mapping[keyword]; 143 | 144 | for(var keywords in maps ) { 145 | punyencode = punyencode.replace(maps[keywords], keyword); 146 | } 147 | } 148 | } 149 | }); 150 | 151 | var punykeywords = (punyencode.match(regex) || []).length; 152 | 153 | if (punykeywords >= 1) { 154 | console.log(`[!] Punycode ${danger(`${domains}`)}`); 155 | } 156 | 157 | return; 158 | } 159 | 160 | 161 | 162 | if (settings.tlds) { 163 | 164 | // Retorn apenas os tlds passados como parametro 165 | lodash.forEach(tlds, function(tld) { 166 | if (lodash.includes(tld, tlddomain)) { 167 | 168 | lodash.forEach(keywords, function(keyword) { 169 | 170 | if (suspicious) { 171 | if (lodash.startsWith(subdomain, keyword, 0) && subdomain.length >= 1) { 172 | console.log(`[!] Suspicious ${danger(`${domains}`)}`); 173 | return; 174 | 175 | } 176 | if (lodash.startsWith(domain, keyword, 0) && domain.length >= 1) { 177 | console.log(`[!] Suspicious ${danger(`${domains}`)}`); 178 | return; 179 | } 180 | 181 | } else { 182 | if (lodash.startsWith(subdomain, keyword, 0) && subdomain.length >= 1) { 183 | console.log(`[!] Likely ${yellow(`${domains}`)}`); 184 | return; 185 | } 186 | if ((subdomain.length >= 1 || domain.length >= 1) || (dashed >= 3 || points >= 2)) { 187 | console.log(`[!] Potential ${white(`${domains}`)}`); 188 | return; 189 | } 190 | } 191 | 192 | }); 193 | } 194 | }); 195 | 196 | } else { 197 | 198 | lodash.forEach(keywords, function(keyword) { 199 | if (suspicious) { 200 | if (lodash.startsWith(subdomain, keyword, 0) && subdomain.length >= 1) { 201 | console.log(`[!] Suspicious ${danger(`${domains}`)}`); 202 | return; 203 | } 204 | if (lodash.startsWith(domain, keyword, 0) && domain.length >= 1) { 205 | console.log(`[!] Likely ${yellow(`${domains}`)}`); 206 | return; 207 | } 208 | 209 | } else { 210 | if (lodash.startsWith(subdomain, keyword, 0) && subdomain.length >= 1) { 211 | console.log(`[!] Likely ${yellow(`${domains}`)}`); 212 | return; 213 | } 214 | if ((subdomain.length >= 1 || domain.length >= 1) || (dashed >= 3 || points >= 2)) { 215 | console.log(`[!] Potential ${white(`${domains}`)}`); 216 | return; 217 | } 218 | } 219 | 220 | }); 221 | } 222 | 223 | }); 224 | 225 | certificates.inc(); 226 | } 227 | } 228 | 229 | status.start({ 230 | pattern: '{spinner.cyan} {certificates} Certs' 231 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "certstreamcatcher", 3 | "version": "1.0.4", 4 | "description": "Catching phishing by observing certificate transparency logs.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Fabio Castro @6IXN7ine", 10 | "license": "ISC", 11 | "dependencies": { 12 | "certstream": "^1.1.0", 13 | "cli-color": "^1.2.0", 14 | "lodash": "^4.17.4", 15 | "node-status": "^1.0.0", 16 | "tldjs": "^2.2.0" 17 | }, 18 | "directories": { 19 | "example": "example" 20 | }, 21 | "devDependencies": {}, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/6IX7ine/certstreamcatcher.git" 25 | }, 26 | "keywords": [ 27 | "certstream" 28 | ], 29 | "bugs": { 30 | "url": "https://github.com/6IX7ine/certstreamcatcher/issues" 31 | }, 32 | "homepage": "https://github.com/6IX7ine/certstreamcatcher#readme" 33 | } 34 | --------------------------------------------------------------------------------