From 3f6d4b206bce13765233de186da7e17e9756591d Mon Sep 17 00:00:00 2001 From: Ata Date: Sun, 5 Jul 2020 18:17:59 +0300 Subject: [PATCH 1/5] Added stdin support to fuzzer implementing #5 --- SingleUrlFuzzer.js | 19 +++++++++++----- package-lock.json | 7 +++++- package.json | 2 +- puff.js | 55 +++++++++++++++++++++++++++++++--------------- 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/SingleUrlFuzzer.js b/SingleUrlFuzzer.js index 8bbcf6c..7bd24ba 100644 --- a/SingleUrlFuzzer.js +++ b/SingleUrlFuzzer.js @@ -16,7 +16,7 @@ class SingleUrlFuzzer{ * Read url from the parameter, and read * */ - constructor(url,cbhandler, threadHandler, terminator, wordlist, verbose){ + constructor(url,cbhandler, threadHandler, terminator, wordlist, verbose, multi=false){ this.url = url this.wlistFpointer=0; this.cbHandler=cbhandler @@ -24,6 +24,7 @@ class SingleUrlFuzzer{ this.verbose = verbose this.threadHandler = threadHandler; this.wordlist = wordlist; + this.multi = multi; //read wordlist if(this.verbose) console.log(`${warn} Reading Wordlist`) @@ -42,11 +43,13 @@ class SingleUrlFuzzer{ /* * Acquire next url from wordlist */ + + var line = this.wlistContent[this.wlistFpointer]; this.wlistFpointer+=1 - return this.wlistContent[this.wlistFpointer]; + return line } - async checkFinished(){ + checkFinished(){ /* * Check if wordlist finished */ @@ -68,16 +71,22 @@ class SingleUrlFuzzer{ outputHandler.write('Last url checked, waiting for all threads to finish') } - this.terminator.terminate() + if(this.multi){ + console.log('') + }else{ + this.terminator.terminate() + } } + return true; } + return false; } async loadNextUrl(thread){ /* * Load next url from from the wordlist */ - this.checkFinished() + if(this.checkFinished()) return; var line = await this.acquire() thread.url = await replaceKeyword(this.url, line) thread.pld = line diff --git a/package-lock.json b/package-lock.json index 20f3dbf..ae6b961 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "puff-fuzz", - "version": "0.0.6", + "version": "0.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -263,6 +263,11 @@ "util-deprecate": "^1.0.1" } }, + "readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==" + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", diff --git a/package.json b/package.json index d276da7..379cb1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "puff-fuzz", - "version": "0.1.0", + "version": "0.1.1", "description": "Simple Clientside vulnerability/xss fuzzer", "main": "puff.js", "repository": "https://github.com/FlameOfIgnis/puff", diff --git a/puff.js b/puff.js index aa8f407..1dc14ca 100644 --- a/puff.js +++ b/puff.js @@ -6,7 +6,7 @@ const puppeteer = require('puppeteer'); const { program } = require('commander'); const fs = require('fs') const path = require('path') - +const readline = require('readline') //config wizard and io handlers const {setChromePath,resolveChromiumPath} = require('./configwiz.js') @@ -60,13 +60,6 @@ var config = require(path.join(__dirname,'/config.json')) //resolve ch var chromium_path = resolveChromiumPath(config); -//check if required parameters were given -if(!(program.wordlist || program.url)){ - console.log('Wordlist and url are required parameters.') - process.exit() -} - - //init tool (async () => { @@ -80,16 +73,42 @@ if(!(program.wordlist || program.url)){ terminator.browser = browser; threadHandler = new ThreadHandler(browser) - - const {SingleUrlFuzzer} = require('./SingleUrlFuzzer.js') - var suFuzzer = new SingleUrlFuzzer(program.url, cbHandler, threadHandler, terminator, wordlist, verbose); + if(program.wordlist && program.url){ + const {SingleUrlFuzzer} = require('./SingleUrlFuzzer.js') + var suFuzzer = new SingleUrlFuzzer(program.url, cbHandler, threadHandler, terminator, wordlist, verbose); + //initialize threads + for(var i=0;i { + if(url=='') return + var suFuzzer = new SingleUrlFuzzer(url, cbHandler, threadHandler, terminator, wordlist, verbose, true); + //initialize threads + for(var i=0;i{ + terminator.graceful() + }) + } + - //initialize threads - for(var i=0;i Date: Sun, 5 Jul 2020 18:39:38 +0300 Subject: [PATCH 2/5] Fix throwing exception at the end of wordlist and stop terminating when fuzz finishes on stdin mode --- SingleUrlFuzzer.js | 11 ++++++++--- callbacks.js | 2 +- puff.js | 2 ++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/SingleUrlFuzzer.js b/SingleUrlFuzzer.js index 7bd24ba..d897586 100644 --- a/SingleUrlFuzzer.js +++ b/SingleUrlFuzzer.js @@ -62,9 +62,9 @@ class SingleUrlFuzzer{ outputHandler.deleteLastLine() console.log("Thread finished") } - + this.threadHandler.workerCount-=1; //Only terminate program if all the threads have finished, so it doesn't lose the progress on those pending requests. - if(this.terminator.terminatedCount==threadHandler.workerCount){ + if(this.threadHandler.workerCount==0){ //TODO, timeout possible idle/stuck threads and terminate if(this.verbose){ outputHandler.deleteLastLine() @@ -86,7 +86,12 @@ class SingleUrlFuzzer{ /* * Load next url from from the wordlist */ - if(this.checkFinished()) return; + if(this.checkFinished()){ + try{ + await thread.close() + }catch(e){}; + return; + } var line = await this.acquire() thread.url = await replaceKeyword(this.url, line) thread.pld = line diff --git a/callbacks.js b/callbacks.js index 8a2ef83..c29f7c6 100644 --- a/callbacks.js +++ b/callbacks.js @@ -47,7 +47,7 @@ class TriggerHandler{ }) //xss windows tend to get load looped, but not sure if needed - //thread.evaluate(() => window.stop()); + thread.evaluate(() => window.stop()); } catchLoadFailure(thread){ diff --git a/puff.js b/puff.js index 1dc14ca..92d51c4 100644 --- a/puff.js +++ b/puff.js @@ -103,6 +103,8 @@ var chromium_path = resolveChromiumPath(config); rl.on('SIGINT', ()=>{ terminator.graceful() }) + }else{ + } From 507ae945990db5918ca5e4fa2fb6d0d5dffd2be5 Mon Sep 17 00:00:00 2001 From: Ata Date: Sun, 5 Jul 2020 19:20:23 +0300 Subject: [PATCH 3/5] Added single payload stdin mode further improvements for #5 --- README.md | 38 ++++- callbacks.js | 2 +- fuzzers/SinglePayloadFuzzer.js | 74 ++++++++++ .../SingleUrlFuzzer.js | 2 +- fuzzers/StdinFuzzer.js | 137 ++++++++++++++++++ puff.js | 40 ++++- 6 files changed, 284 insertions(+), 9 deletions(-) create mode 100644 fuzzers/SinglePayloadFuzzer.js rename SingleUrlFuzzer.js => fuzzers/SingleUrlFuzzer.js (99%) create mode 100644 fuzzers/StdinFuzzer.js diff --git a/README.md b/README.md index 679c988..7a7a1f1 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Don't worry, just modify your wordlist to use `puff()` instead of `alert()` in -Running from source: +**Running from source:** ``` node puff.js -w xss.txt -u "http://your.url?message=FUZZ" @@ -79,7 +79,7 @@ node puff.js -w xss.txt -u "http://your.url?message=FUZZ" -t 25 node puff.js -w xss.txt -u "http://your.url?message=FUZZ" -d ``` -installed via npm: +**installed via npm:** ``` puff -w xss.txt -u "http://your.url?message=FUZZ" @@ -88,6 +88,40 @@ puff -w xss.txt -u "http://your.url?message=FUZZ" -t 25 puff -w xss.txt -u "http://your.url?message=FUZZ" -d ``` + + +**Running with stdin fuzzing mode:** +``` +cat urls.txt | node puff.js -w .\wordlist-examples\events.txt + + +cat urls.txt | puff -w .\wordlist-examples\events.txt +``` +Where urls.txt is +``` +http://example.com?query=FUZZ +https://another.com/page/#FUZZ +``` + + +**Running with stdin single payload mode:** +cat urls.txt | node puff.js -w .\wordlist-examples\events.txt + + +cat urls.txt | puff -w .\wordlist-examples\events.txt +``` +Where urls.txt is +``` +http://example.com?query= +http://example.com?query=javascript:alert() +https://another.com/page/# +``` + + + + + + ---

diff --git a/callbacks.js b/callbacks.js index c29f7c6..d47d4a7 100644 --- a/callbacks.js +++ b/callbacks.js @@ -51,7 +51,7 @@ class TriggerHandler{ } catchLoadFailure(thread){ - this.outputHandler.write(`${bstart}[${thread.status}] [FAILURE] ${thread.pld} ${colstop}`, 5000) + this.outputHandler.write(`${bstart} [${thread.status}] [FAILURE] ${thread.pld} ${colstop}`, 5000) this.outputHandler.bLastOutputImportant=true } diff --git a/fuzzers/SinglePayloadFuzzer.js b/fuzzers/SinglePayloadFuzzer.js new file mode 100644 index 0000000..107c2ca --- /dev/null +++ b/fuzzers/SinglePayloadFuzzer.js @@ -0,0 +1,74 @@ + +const {fail,succ,warn,info,gstart,bstart,ystart,rstart,colstop} = require('../pretty.js') +const fs = require('fs') +function replaceKeyword(url,pld){ + pld = pld.replace(/ /g, '%20') + var t=url; + t = t.replace(/FUZZ/g,pld.replace(/\n|\r/g,'')) + return t; + +} + + +class SinglePayloadFuzzer{ + /* + * Fuzzer for when both wordlist and url parameter is supplied. + * Read url from the parameter, and read + * + */ + constructor(url,cbhandler, threadHandler, terminator, wordlist, verbose, multi=false){ + this.url = url + this.wlistFpointer=0; + this.cbHandler=cbhandler + this.terminator = terminator + this.verbose = verbose + this.threadHandler = threadHandler; + this.wordlist = wordlist; + this.multi = multi; + + } + + + async loadNextUrl(thread){ + /* + * Load next url from from the wordlist + */ + thread.url = this.url + await this.processURL(thread,thread.url) + thread.close() + } + + + async processURL(thread, url){ + /* + * Process url, visit, try to trigger events etc. + */ + try{ + thread.goto(thread.url) + + //capture window response + const response = await thread.waitForNavigation(); + + //acquire possible redirect chain + var chain = (response.request().redirectChain()) + + //get http response + thread.status = response.status(); + + //if there was a redirect chain, output it. If not, its a normal response + if(chain.length){ + thread.wasHTTPRedirect = true; + this.cbHandler.catchRedirect(thread, chain) + }else{ + this.cbHandler.catchNormal(thread) + } + }catch(e){ + //Not properly implemented yet, dom-errors, http timeouts + this.cbHandler.catchLoadFailure(thread) + } + } +} + +module.exports = { + SinglePayloadFuzzer:SinglePayloadFuzzer +} \ No newline at end of file diff --git a/SingleUrlFuzzer.js b/fuzzers/SingleUrlFuzzer.js similarity index 99% rename from SingleUrlFuzzer.js rename to fuzzers/SingleUrlFuzzer.js index d897586..6ec51c4 100644 --- a/SingleUrlFuzzer.js +++ b/fuzzers/SingleUrlFuzzer.js @@ -1,5 +1,5 @@ -const {fail,succ,warn,info,gstart,bstart,ystart,rstart,colstop} = require('./pretty.js') +const {fail,succ,warn,info,gstart,bstart,ystart,rstart,colstop} = require('../pretty.js') const fs = require('fs') function replaceKeyword(url,pld){ pld = pld.replace(/ /g, '%20') diff --git a/fuzzers/StdinFuzzer.js b/fuzzers/StdinFuzzer.js new file mode 100644 index 0000000..3f9520d --- /dev/null +++ b/fuzzers/StdinFuzzer.js @@ -0,0 +1,137 @@ + +const {fail,succ,warn,info,gstart,bstart,ystart,rstart,colstop} = require('../pretty.js') + +function replaceKeyword(url,pld){ + pld = pld.replace(/ /g, '%20') + var t=url; + t = t.replace(/FUZZ/g,pld.replace(/\n|\r/g,'')) + return t; + +} + + +class SingleUrlFuzzer{ + /* + * Fuzzer for when both wordlist and url parameter is supplied. + * Read url from the parameter, and read + * + */ + constructor(url,cbhandler, threadHandler, terminator, wordlist, verbose, multi=false){ + this.url = url + this.wlistFpointer=0; + this.cbHandler=cbhandler + this.terminator = terminator + this.verbose = verbose + this.threadHandler = threadHandler; + this.wordlist = wordlist; + this.multi = multi; + + //read wordlist + if(this.verbose) console.log(`${warn} Reading Wordlist`) + try{ + this.wlistContent = fs.readFileSync(wordlist).toString().split("\n") + }catch(e){ + console.log(`${fail} Wordlist file was not found`) + console.log(e) + process.exit(1) + } + if(this.verbose) console.log(`${succ} Wordlist loaded, ${this.wlistContent.length} lines.`) + + } + + async acquire(){ + /* + * Acquire next url from stdin + */ + + var line = this.wlistContent[this.wlistFpointer]; + this.wlistFpointer+=1 + return line + } + + checkFinished(){ + /* + * Check if wordlist finished + */ + + //if this thread is done + if(this.wlistFpointer>=this.wlistContent.length){ + this.terminator.terminatedCount+=1 + this.wlistFpointer+=1 + if(this.verbose){ + outputHandler.deleteLastLine() + console.log("Thread finished") + } + this.threadHandler.workerCount-=1; + //Only terminate program if all the threads have finished, so it doesn't lose the progress on those pending requests. + if(this.threadHandler.workerCount==0){ + //TODO, timeout possible idle/stuck threads and terminate + if(this.verbose){ + outputHandler.deleteLastLine() + outputHandler.write('Last url checked, waiting for all threads to finish') + } + + if(this.multi){ + console.log('') + }else{ + this.terminator.terminate() + } + } + return true; + } + return false; + } + + async loadNextUrl(thread){ + /* + * Load next url from from the wordlist + */ + if(this.checkFinished()){ + try{ + await thread.close() + }catch(e){}; + return; + } + var line = await this.acquire() + thread.url = await replaceKeyword(this.url, line) + thread.pld = line + this.processURL(thread,thread.url) + } + + + async processURL(thread, url){ + /* + * Process url, visit, try to trigger events etc. + */ + try{ + thread.goto(thread.url) + + //capture window response + const response = await thread.waitForNavigation(); + + //acquire possible redirect chain + var chain = (response.request().redirectChain()) + + //get http response + thread.status = response.status(); + + //if there was a redirect chain, output it. If not, its a normal response + if(chain.length){ + thread.wasHTTPRedirect = true; + this.cbHandler.catchRedirect(thread, chain) + }else{ + this.cbHandler.catchNormal(thread) + } + }catch(e){ + //Not properly implemented yet, dom-errors, http timeouts + this.cbHandler.catchLoadFailure(thread) + } + + //recurses + this.loadNextUrl(thread) + } +} + +module.exports = { + SingleUrlFuzzer:SingleUrlFuzzer +} \ No newline at end of file diff --git a/puff.js b/puff.js index 92d51c4..ee6d936 100644 --- a/puff.js +++ b/puff.js @@ -73,8 +73,10 @@ var chromium_path = resolveChromiumPath(config); terminator.browser = browser; threadHandler = new ThreadHandler(browser) + + //normal mode if(program.wordlist && program.url){ - const {SingleUrlFuzzer} = require('./SingleUrlFuzzer.js') + const {SingleUrlFuzzer} = require('./fuzzers/SingleUrlFuzzer.js') var suFuzzer = new SingleUrlFuzzer(program.url, cbHandler, threadHandler, terminator, wordlist, verbose); //initialize threads for(var i=0;i{ terminator.graceful() }) + + + //get payloads from stdin + }else if(program.url){ + console.log('Invalid arguements.')//This mode is not yet implemented + process.exit(1) + + + + //test single url }else{ + const {SinglePayloadFuzzer} = require('./fuzzers/SinglePayloadFuzzer.js') + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + rl.on('line', (url) => { + if(url=='') return + var singlePayloadFuzzer = new SinglePayloadFuzzer(url, cbHandler, threadHandler, terminator, verbose); + var newThread = threadHandler.newThread(browser, singlePayloadFuzzer, cbHandler); + }); + + rl.on('SIGINT', ()=>{ + terminator.graceful() + }) } - })(); - -//https://xss-game.appspot.com/level1/frame?query=FUZZ \ No newline at end of file From 7c8d4e237fa1525535be4e094f63d4d803084f68 Mon Sep 17 00:00:00 2001 From: Ata Date: Sun, 5 Jul 2020 19:21:12 +0300 Subject: [PATCH 4/5] Fix readme format error --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7a7a1f1..074b060 100644 --- a/README.md +++ b/README.md @@ -105,11 +105,13 @@ https://another.com/page/#FUZZ **Running with stdin single payload mode:** +``` cat urls.txt | node puff.js -w .\wordlist-examples\events.txt cat urls.txt | puff -w .\wordlist-examples\events.txt ``` + Where urls.txt is ``` http://example.com?query= From d309c09e4ad65f6a525cd48b609ddc9d50a1b42c Mon Sep 17 00:00:00 2001 From: Ata Date: Sun, 5 Jul 2020 19:28:05 +0300 Subject: [PATCH 5/5] Minor bugfix --- fuzzers/SinglePayloadFuzzer.js | 1 + puff.js | 1 + 2 files changed, 2 insertions(+) diff --git a/fuzzers/SinglePayloadFuzzer.js b/fuzzers/SinglePayloadFuzzer.js index 107c2ca..ee6197a 100644 --- a/fuzzers/SinglePayloadFuzzer.js +++ b/fuzzers/SinglePayloadFuzzer.js @@ -34,6 +34,7 @@ class SinglePayloadFuzzer{ * Load next url from from the wordlist */ thread.url = this.url + thread.pld = '' await this.processURL(thread,thread.url) thread.close() } diff --git a/puff.js b/puff.js index ee6d936..5127688 100644 --- a/puff.js +++ b/puff.js @@ -122,6 +122,7 @@ var chromium_path = resolveChromiumPath(config); //test single url }else{ + console.log('Running on stdin single payload mode..') const {SinglePayloadFuzzer} = require('./fuzzers/SinglePayloadFuzzer.js') const rl = readline.createInterface({