Simple NodeJS Web Proxy

I was on a project recently where we were performing a network penetration test against an internal network, remotely. To facilitate this, we shipped a server to the client so they could install it into their datacenter. We then log in to this host, and perform our testing from it, rather than sending consultants onsite. This is super efficient, and cost-effective. The host we send out in these cases is referred to as a ‘jump box’ — because it offers a jumping-off point into the network.

For whatever reason, this time the jump box did not have the full set of tools installed. In many cases, this wouldn’t be a terrible problem, we’d just install the tools we need and move on. However, in this case, the client was performing egress filtering — meaning we had no way to get out to the internet from our jump box. This led to a dilemma: how can I access the remote services (like web sites) easily? Usually I’d set up tinyproxy or something similar, and set up a tunnel over SSH to access it. Unfortunately, I didn’t have tinyproxy or any other web proxy servers installed, and had no easy way to get one.

However, I did have nodejs installed on the box: which is quite useful for this type of thing! I did a quick search for nodejs proxies, and found a suitable base code over here. (Remember, I can’t get out to the internet from the jump box, so I need to limit my proxy code to only core nodejs modules, I can’t ‘npm install’ anything.)

That post is quite old (6 years!) and some things have changed since it was written. For one thing, the http.createServer() method has been replaced by http.request(). The sample code there also contained some things I didn’t need, like a blacklist of sites to prevent access to, but it had some things I definitely did want (like a whitelist of IPs that are allowed to talk to the proxy. That’s an important factor when doing this sort of test, if you are going to open up services on a client network, you need to take steps to minimize security risks they may cause. Restricting access to this proxy to only the localhost of the jump box helps me do that.)

You can check out the final results at my pentools github repo.

Once I had the code in place, I simply opened up an SSH tunnel to the jump box, and set up port 8080 on my laptop to tunnel to port 8080 on the jump box, like this:

ssh -L 8080:localhost:8080 user@jumpbox

Once I was logged in, I configured Firefox on my laptop to use localhost port 8080 for a web proxy, and I could now point my browser to the client’s internal network addresses and browse their websites from my browser, through the jump box proxy.

Kivlad – Initial Thoughts

Recently, the folks over at Matasano Security released a tool to decompile Android Dalvik binaries. The tool is named Kivlad and it can be found on their site.

Having a need to dissassemble Android binaries on a fairly frequent basis, I’m always looking for a new tool to help out, so I took this one for a spin.

The first thing to point out is the disclaimer on the Matasano site, which reads:

This is very much an alpha release and while it will be production-quality in the near future, we wanted to give the community a taste of what’s to come.

Experimenting with Kivlad shows that disclaimer to be very warranted. Kivlad is a cool concept, and is somewhat unique in the field because the output format is HTML. At the core, Kivlad offers a tree view of the Android elements contained within the Dex. I like this idea, and it will be very cool to see it get developed further.

That said, the tool is pretty near non-functional (for me at least) – I’m unable to get it to parse any APK other than the included HelloWorld.apk that comes with the download.

Further, because the background of the HTML output is an image loaded via CSS, if the output file is not in the source code directory, it is completely unreadable (because the font color is set to white, and the default browser background is also white.) This is easily fixed, either open the file in the source code directory, or change the CSS to use whatever color scheme you wish.

I should note that I find both of these shortcoming to be perfectly acceptable, (Matasano did say the tool was pre-alpha afterall), and as I said, I like the concept and where this seems headed quite a lot.

The process to getting Kivlad working is pretty straightforward IMO, but I’ve included my notes here in case someone finds them useful. (These are for the Ubuntu Linux distribution, tested on versions 10.10 and 11.04)

  1. Install the required software and libraries:
    sudo apt-get install ruby1.9.1 libzip-ruby1.9.1 graphviz
    sudo ln -s /usr/bin/ruby1.9.1 /usr/bin/ruby
    sudo ln -s /usr/bin/gem1.9.1 usr/bin/gem
    sudo gem install metasm
  2. Download Kivlad and extract it:
    wget http://www.matasano.com/research/kivlad/kivlad-0.1.tar.gz
    tar zxf kivlad-0.1.tar.gz
    cd kivlad-0.1
  3. Run the tool:
    ruby ./reflect.rb HelloWorld.apk HelloWorld.html

This will spit out a bunch of stuff, and you’ll end up with a HelloWorld.html file in the directory:

Lcom/daeken/helloworld/HelloWorld;/
Succeed

Lcom/daeken/helloworld/HelloWorld;/bar
Succeed

...

Lcom/daeken/helloworld/R$string;/
Succeed

Lcom/daeken/helloworld/R;/
Succeed
[]

As I mentioned, the only APK I’ve been able to successfully use Kivlad on is the HelloWorld.apk. Every other APK I’ve tried has resulted in the following error (example below using OI_Safe_1.2.4 from freewarelovers.com):

ruby ./reflect.rb OI_Safe_1.2.4.apk OI_Safe_1.2.4.html

Lestreamj/ciphers/trivium/Trivium$Maker;/
Succeed

Lestreamj/ciphers/trivium/Trivium$Maker;/create
/home/rossja/Desktop/kivlad-0.1/instruction_form.rb:11:in `inspect': wrong number of arguments(1 for 0) (ArgumentError)
 from /home/rossja/Desktop/kivlad-0.1/instruction_form.rb:11:in `convert_insn'
 from /home/rossja/Desktop/kivlad-0.1/decompiler.rb:61:in `disassemble_all'
 from /home/rossja/Desktop/kivlad-0.1/decompiler.rb:85:in `disassemble_blocks'
 from /home/rossja/Desktop/kivlad-0.1/decompiler.rb:713:in `decompile'
 from /home/rossja/Desktop/kivlad-0.1/decompiler.rb:774:in `decompile'
 from /home/rossja/Desktop/kivlad-0.1/dex.rb:70:in `method'
 from /home/rossja/Desktop/kivlad-0.1/dex.rb:46:in `block (2 levels) in initialize'
 from /home/rossja/Desktop/kivlad-0.1/dex.rb:45:in `map'
 from /home/rossja/Desktop/kivlad-0.1/dex.rb:45:in `block in initialize'
 from /home/rossja/Desktop/kivlad-0.1/dex.rb:30:in `map'
 from /home/rossja/Desktop/kivlad-0.1/dex.rb:30:in `initialize'
 from ./reflect.rb:9:in `new'
 from ./reflect.rb:9:in `initialize'
 from ./reflect.rb:21:in `new'
 from ./reflect.rb:21:in `'

At some point I’ll try to chase down where the issue is and see if I can figure out whom to send a patch to =)

ASNCheck Script

While working on a project today I decided that it would be handy to have a script that could take an AS number (from stdin or from a list of them) and check the health status of it (via things like DNSBL for example), specifically gathering information that could lead one to determine the relative infection/compromise level.

Ideally, such a script would be able to alternatively take an IP address, determine the AS for it and then report on both the IP provided as well as the overall “health” of the AS associated with it.

Well, some of that I managed to whip out tonight, though not all.

I’ll keep working on this, but I think it’s useful enough now to warrant posting (I normally do *not* make code public in this raw a state, so take note that there are very likely bugs in this).

That said, here’s ‘asncheck.py’.
In its current state, it just returns a list of IP addresses from a given AS which are in the dShield current watchlist.

#! /usr/bin/env python
# ------------------------------------------------
# asncheck:
# retrieves the current dshield watchlist for
# a given AS, returning just the IP addresses. 
# sample url:
# https://secure.dshield.org/asdetailsascii.html?as=123
# ------------------------------------------------
# written by:
# jason ross (algorythm@gmail.com)
# ------------------------------------------------
import sys

def main():
   # here beginneth the script
   opts = parmsdealer()

   if (opts.verbose == 1):
      print "nRetrieving information for AS Number " + opts.asn + ":n"
    
   if (opts.infile):
      try:
         filedata = open(opts.infile, 'rU')
      except IOError:
         print "unable to open input file '" + opts.infile + "'n"
         sys.exit(1)
      except:
          print "Unexpected error:", sys.exc_info()[0]
          sys.exit(1)
      else:
         for line in filedata:
            print line
            asn = line.split(opts.delim, 3)[int(opts.col)]
    
   if (opts.asn):
      asn = opts.asn

   dshield(asn, opts.verbose)

   #print '{0}.{1}.{2}.{3}'.format(oct1.zfill(3),oct2.zfill(3),oct3.zfill(3),oct4.zfill(3))


def parmsdealer():
   import sys
   from optparse import OptionParser
   version="nasncheck: version 0.1nauthor: jason ross <algorythm@gmail.com>n"
   usage="nn%prog [OPTIONS]n"
   parser = OptionParser(usage=usage, version=version)
   
   # set up command line arguments
   parser.set_defaults(col=0)
   parser.set_defaults(delim="|")
   parser.set_defaults(verbose=0)
    
   parser.add_option("-v", "--verbose", dest="verbose",
                     action="store_true", help="turn on/off verbosity (default: off)")
   parser.add_option("-a", "--asn", dest="asn",
                     action="store", help="specify the AS to retrieve data for (just the number, or with 'AS' prepended)")
   parser.add_option("-f", "--infile", dest="infile",
                     action="store", help="get the AS from the specified file (can be a list)")
   parser.add_option("-c", "--col", dest="col",
                     action="store", help="[required with -f] specifies which column in an input file contains the AS (default is to use the first column: '0')")
   parser.add_option("-d", "--delim", dest="delim",
                     action="store", help="[required with -f] specifies the delimiter to use when parsing the input file (default is to use the ASCII pipe character (0x7c):  '|')")
                     
   # process command line arguments
   (options, args) = parser.parse_args()
   
   # exit if we're missing options
   if (not options.asn and not options.infile):
      print "n" + sys.argv[0] + ": missing parameter(s)n"
      parser.print_help()
      print "n"
      sys.exit(1)
    
   # exit if we've got conflicting options
   if (options.asn and options.infile):
      print "n" + sys.argv[0] + ": can't set both an asn and an input file (there can be only one!)n"
      parser.print_help()
      print "n"
      sys.exit(1)

   return options


def dshield(asn, verbose):
   import socket
   import urllib
   import urllib2
   import re
    
   # urllib2 calls socket, so we can set the timeout here
   timeout = 5
   socket.setdefaulttimeout(timeout)

   baseuri = 'https://secure.dshield.org/asdetailsascii.html'

   params = {}
   params['as'] = asn
   encparams = urllib.urlencode(params)

   requri = baseuri + '?' + encparams
   req = urllib2.Request(requri)

   if (verbose == 1):
      print "opening " + requri + "n"

   try:
      res = urllib2.urlopen(req)
   except urllib2.URLError, e:
      if hasattr(e, "code"):
         print "site borked! HTTP error: " 
         print e.code
      elif hasattr(e, "reason"):
         print "server borked! reason: "
         print e.reason
   else:
      data = res.readlines()
#      print data
      for line in data:
         if ( re.match(r"[0-9]", line) ):
            ip = line.split()
            print ip[0]
      

if __name__ == "__main__":
   main()