Sunday, March 25, 2007

Querying Bank of America's SiteKey service

I've been meaning to pick up ruby again. It's such a cool language.

This evening, after returning from todays Shmoocon talks, I was inspired to write a bit of code. My ruby skills are still very rough, so I came up with a little project to help me to learn a few concepts.

The tool that I whipped up: a Ruby script that connects to Bank of America, and pulls down your SiteKey image and title.

Why would something like this be useful? Were such code to be taken, improved, and hacked up a bit, one could imagine some kind of Man in the middle proxying-phishing attack, which presented itself as a legitimate financial institution to the user, connected to Bank of America, and then passed the information back and forth between the user and the legitimate financial website that she thinks she is visiting.

The user would see her Sitekey, and thus would feel safe knowing that she was indeed connecting to BoA - while in fact, she was sending her information to someone in Nigeria.

Sitekey, of course, is the technology sold by Passmark/RSA to a number of financial institutions. It's the cheaper way to do 2 factor authentication. Sitekey has been mentioned in the press recently, after researchers found that users rarely noticed when the image was absent from their banking sessions.

I haven't developed anything like that though. This is just a simple Ruby script.

Sample output:

./boa-mechanize
Please enter your Bank Of America username: joeuser
Please enter your state 2 letter code (i.e. CA): NY
Please answer the following SiteKey Question.
What is the name of your best friend: jack valenti

Got it...
Sitekey phrase: She Sells Sea Shells On the Sea Shore
Sitekey image saved to: /tmp/sitekey.28317.0

---

And just to cover my freedom of speech bases, I'm including the text of the script here, instead of linking to a downloadable file.

----


#!/opt/local/bin/ruby

# Christopher Soghoian
# A fun little ruby script which'll login to your BoA account,
# and download your sitekey image and title, after prompting you
# for one of your sitekey questions

# Apologies for my horrible regex and ruby skills.
#I'm sure this can be done far cleaner.

# Copyleft GPL.

require 'rubygems'
require 'mechanize'
require 'tempfile'

agent = WWW::Mechanize.new
agent.user_agent_alias = 'Windows IE 6'

print "Please enter your Bank Of America username: "
username = gets.chomp

print "Please enter your state 2 letter code (i.e. CA): "
state = gets.chomp

# Make initial connection to BoA webserver. Send over our state of residence
page = agent.get("https://sitekey.bankofamerica.com/sas/signon.do?state=" + state)
boa_form = page.form('signonForm')
boa_form.onlineID = username
page = agent.submit(boa_form, boa_form.buttons.first)

# We've got a cookie that refers to our username and state. Keep going
page = agent.get('https://sitekey.bankofamerica.com/sas/signon.do?&detect=5')

# Extract the sitekey question (since our machine is unknown to BoA)
sitekey_question_html = page.search("//label[@for='sitekeyChallengeAnswer']").inner_html
sitekey_question = sitekey_question_html.scan(/\((.*)\)/)

# Prompt the user for it.
puts "Please answer the following SiteKey Question."
print sitekey_question, ": "
sitekey_answer = gets.chomp

# Submit the answer back to BoA
unknown_computer_form = page.form('challengeQandAForm')
unknown_computer_form.sitekeyChallengeAnswer = sitekey_answer
page = agent.submit(unknown_computer_form, unknown_computer_form.buttons.first)

# Neato. It worked. Grab the sitekey image, and the sitekey image title
sitekey_image_url = page.search("//html").inner_html.scan(/img src=\"(getMySiteKey.*)\" border/)

full_sitekey_image_url = "https://sitekey.bankofamerica.com/sas/#{sitekey_image_url}"
sitekey_image = agent.get(full_sitekey_image_url)

sitekey_image_title = page.search("//html").inner_html.scan(/Your SiteKey Image Title:.*nbsp;
(.*?)<\/td>/m)


tf = Tempfile.new("sitekey")
sitekey_image.save_as(tf.path)
print "\nGot it..\n"
print "Sitekey phrase: ", sitekey_image_title, "\n"
print "Sitekey image saved to: ", tf.path, "\n"
sleep 10

4 comments:

Greg Pfeil said...

Thanks, Chris. I've been complaining about this vulnerability since I first saw BoA put up their SiteKey login. It's insane that this passes for security.

It seems to me that it should be simple for a site to set up a PGP-based login system that tests your ability to decrypt and sign a message. That way I also wouldn't need to have a separate password for every site, and worry about how they're storing it. Just keep my public key and we're done.

Anonymous said...

It's a decent thought Greg, but I think there are too many people that would "back up" their private key onto flash drives, etc. and then lose them in a cab somewhere.

I think, some day, we need to make prolific use of those fancy little keychain devices (FLKD) that receive a password that dynamically changes every few minutes (to be used used in conjuction with a static, user-defined password, in case the user loses his/her FLKD).

The thing is, I don't want to have to carry 100 FLKDs, so there will need to be a few centralized FLKD providers, perhaps.

Anonymous said...

I'm getting the following error:

>ruby RubyScript.rb
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- mechanize (LoadError)
from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from RubyScript.rb:14
>Exit code: 1


this is the first time i've tried to use a ruby script so i have no idea what i'm doing

Anonymous said...

download mechanize and add it to ruby