|
// spam the scammers until they're gone |
|
// dependencies: an-array-of-english-words request blessed |
|
const request = require('request'); |
|
const words = require('an-array-of-english-words'); |
|
const blessed = require('blessed'); |
|
const http = require('http'); |
|
const EventEmitter = require('events'); |
|
|
|
const UI_UPDATE_INTERVAL = 100; |
|
const EMAIL_DOMAINS = [ |
|
'inbox.com', 'yahoo.com', 'gmail.com', 'aol.com', 'icloud.com', |
|
'outlook.com', 'mail.com', 'hotmail.com', 'ymail.com' |
|
]; |
|
/* eslint-disable max-len */ |
|
const REQUEST_OPTS = { |
|
url: 'http://not-a-real-site.not-a-real-tld/thisguy/login.php', |
|
method: 'POST', |
|
headers: { |
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36', |
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', |
|
'Accept-Encoding': 'gzip, deflate', |
|
'Cache-Control': 'max-age=0', |
|
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,zh-TW;q=0.6,es;q=0.5', |
|
'Origin': 'http://not-a-real-site.not-a-real-tld', |
|
'Upgrade-Insecure-Requests': 1 |
|
}, |
|
timeout: 5000 |
|
}; |
|
/* eslint-enable max-len */ |
|
const ITER_OPTS = { |
|
count: 100, // how many requests to perform simultaneously |
|
reqOpts: REQUEST_OPTS, |
|
emailDomains: EMAIL_DOMAINS, |
|
wordList: words, |
|
acceptCode: 302, // status code to treat as success |
|
emailKey: 'email', |
|
passwordKey: 'password', |
|
additionalFormOpts: { |
|
// additional values which may be submitted with the form |
|
// submit: 'CONTINUE' |
|
} |
|
}; |
|
http.globalAgent.maxSockets = ITER_OPTS.count; |
|
|
|
/** |
|
* Generate a random integer |
|
* @param {Number} high Upper limit |
|
* @param {Number} [low = 0] Lower limit |
|
* @return {Number} The random integer |
|
*/ |
|
function randInt(high, low = 0) { |
|
return Math.floor(Math.random() * (high - low)) + low; |
|
} |
|
|
|
/** |
|
* Represents an iteration of requests |
|
* @extends EventEmitter |
|
*/ |
|
class Iteration extends EventEmitter { |
|
/** |
|
* Creates an Iteration |
|
* @param {Object} opts Options for this iteration |
|
* @param {Number} opts.count Number of requests in the iteration |
|
* @param {Object} opts.reqOpts Options to pass to request() |
|
* @param {Object} opts.emailDomains Acceptable email domains to be used |
|
* @param {String[]} opts.wordList Word list to use for generating credentials |
|
* @param {Number} [opts.acceptCode=200] Status code signifying success |
|
* @param {String} [opts.emailKey=email] Form key for email |
|
* @param {String} [opts.passwordKey=password] Form key for password |
|
* @param {Object} [opts.additionalFormOpts] Additional keys to add to form |
|
*/ |
|
constructor(opts) { |
|
super(); |
|
this.count = opts.count; |
|
this.reqOpts = opts.reqOpts; |
|
this.emailDomains = opts.emailDomains; |
|
this.wordList = opts.wordList; |
|
this.acceptCode = opts.acceptCode || 200; |
|
this.emailKey = opts.emailKey || 'email'; |
|
this.passwordKey = opts.passwordKey || 'password'; |
|
this.additionalFormOpts = opts.additionalFormOpts || {}; |
|
|
|
this.pending = 0; |
|
this.totalRequests = 0; |
|
this.lastEmail = null; |
|
this.lastPassword = null; |
|
this.lastError = null; |
|
} |
|
|
|
/** |
|
* Generate a random password. |
|
* @return {String} The generated password |
|
*/ |
|
randomPassword() { |
|
let word = this.wordList[randInt(this.wordList.length - 1)]; |
|
return word + randInt(1000); |
|
} |
|
|
|
/** |
|
* Generate a random email. |
|
* @return {String} The generated email |
|
*/ |
|
randomEmail() { |
|
let str = this.randomPassword(); |
|
let domain = this.emailDomains[randInt(this.emailDomains.length - 1)]; |
|
return `${str}@${domain}`; |
|
} |
|
|
|
/** |
|
* Perform a request |
|
* @return {Request} The request object |
|
*/ |
|
doRequest() { |
|
this.pending++; |
|
this.totalRequests++; |
|
this.emit('startRequest', this.totalRequests); |
|
let email = this.randomEmail(); |
|
let password = this.randomPassword(); |
|
let opts = { |
|
form: { |
|
[this.emailKey]: email, |
|
[this.passwordKey]: password, |
|
...this.additionalFormOpts |
|
}, |
|
...this.reqOpts |
|
}; |
|
let req = request.post(opts, (err, response) => { |
|
this.lastEmail = email; |
|
this.lastPassword = password; |
|
this.pending--; |
|
this.emit('endRequest', this.totalRequests); |
|
if (err || response.statusCode !== this.acceptCode) { |
|
this.lastError = err ? err.message : response.statusCode; |
|
this.doRequest(); |
|
} else if (this.pending <= 0) this.emit('done'); |
|
}); |
|
return req; |
|
} |
|
|
|
/** |
|
* Start performing requests |
|
*/ |
|
start() { |
|
for (let i = 0; i < this.count; i++) this.doRequest(); |
|
} |
|
} |
|
/* |
|
* Events: |
|
* startRequest, requestId |
|
* endRequest, requestId |
|
* done |
|
*/ |
|
|
|
const screen = blessed.screen({smartCSR: true}); |
|
screen.key('C-c', () => process.exit(0)); |
|
const infoBox = blessed.box({ |
|
top: 0, |
|
left: 0, |
|
width: '100%', |
|
height: '100%', |
|
content: '', |
|
border: { |
|
type: 'line', |
|
} |
|
}); |
|
screen.append(infoBox); |
|
infoBox.focus(); |
|
|
|
let iterCount = 0; |
|
let iter = null; |
|
|
|
/** |
|
* Update the display |
|
*/ |
|
function updateDisplay() { |
|
infoBox.setContent([ |
|
`Iteration #${iterCount}`, |
|
`Performing ${iter.count} requests`, |
|
`Requests performed in iteration: ${iter.totalRequests}`, |
|
`Requests pending in iteration: ${iter.pending}`, |
|
``, |
|
`Last email sent: ${iter.lastEmail}`, |
|
`Last password sent: ${iter.lastPassword}`, |
|
`Last error: ${iter.lastError || '(none)'}` |
|
].join('\n')); |
|
screen.render(); |
|
} |
|
|
|
// interval for update here |
|
setInterval(() => updateDisplay(), UI_UPDATE_INTERVAL); |
|
|
|
/** |
|
* Run a new iteration |
|
*/ |
|
function newIter() { |
|
iterCount++; |
|
iter = new Iteration(ITER_OPTS); |
|
iter.on('done', () => newIter()); |
|
iter.start(); |
|
} |
|
|
|
newIter(); |