sharkfest ‘10 | stanford university | june 14–17, 2010 scripting wifi security software...
TRANSCRIPT
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Scripting Wifi Security SoftwareSharkfest 10
Mike Kershaw / DragornAruba Networks / Kismet
SHARKFEST ‘10Stanford UniversityJune 14-17, 2010
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Where We're Going
Why we'd script stuffScripting KismetWriting new toolsScripting LORCONReal world tools
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
What's the point?
AutomationAlternate interfacesLoggingDynamic alertsExtremely fast prototype and tool developmentReal world security
tools
(Boring, but useful)
(Exciting and scary!)
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Kismet
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Talking to Kismet
Kismet is pretty easy to scriptBut no-one seems toActually 2 programs – kismet_server and
kismet_clientTalks over standard TCPAnd it's even a human-readable protocol, similar
to IMAP
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Where are you?
Kismet listens to port 2501 by defaultTalk to it with netcatOr telnetOr any other TCP socket tools
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Kismet says “Hi there”Trying 127.0.0.1...Connected to localhost.Escape character is '^]'.*KISMET: 0.0.0 1275891833 DRD1812 tuntap 1000 *PROTOCOLS: KISMET,ERROR,ACK,PROTOCOLS,CAPABILITY,TERMINATE,TIME,PACKET,STATUS,PLUGIN,SOURCE,ALERT,BTSCANDEV,D15D4DEV,WEPKEY,STRING,GPS,BSSID,SSID,CLIENT,BSSIDSRC,CLISRC,NETTAG,CLITAG,REMOVE,CHANNEL,SPECTRUM,INFO,BATTERY,CRITFAIL*TIME: 1276050955*TIME: 1276050956*TIME: 1276050957
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Kismet sentences
Consist of a *sentence type followed by space-delimited fields
Fields which contain free-form text are buffered with \001 bytes
*FOO f1 f2 f3 \001f4 with spaces\001 f5
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Commands
Commands are !ID command parametersThe ID may be incremented or repeatingKismet will include the ID in responsesUseful for figuring out if a queued command
completed
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Assumptions about you
Kismet assumes some sentences MUST be handled by the client.
*KISMET, *TIME, *ERROR, *ACK, *PROTOCOLS, *CAPABILITY, *TERMINATE
This doesn't mean you have to do something smart
Just that you have to not fail
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Umpteenth normal form
Kismet protocols are vaguely like a normalized database
When unknown numbers of dynamic records reference the same data, they are a separate sentence
F.E. networks are stored as BSSID (primary data) and SSID (multiple SSID records indexed by BSSID)
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
What can you do
Protocol fields are listed using the CAPABILITY command
Different versions of Kismet may support different fields, your client can examine this
Clients are expected to handle missing fields gracefully
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Getting data
Similar to SQL, ALL fields or only SPECIFIC fields can be requested
Fields may be requested in any order (and will be returned in that order)
Enabled via the ENABLE commandClient is responsible for handling de-mux of
multiple protocol requests – Kismet will only listen to the last req
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Initial burst
Kismet tracks things all the timeClients are only connected sometimesNon-realtime tracking records are sent in a burst
when a sentence is enabledF.E. Enabling BSSID will cause Kismet to send all
existing BSSID recordsSome protocols don't maintain history
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Down by the delta
Once Kismet has sent the initial burst of old dataNew data is sent once per second as it changesF.E. A BSSID record will be sent every second
while a network is in rangeThe client is expected to merge this cleanly with
existing known data
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Most useful
*BSSID – Networks seen*SSID – Network SSID records*CLIENT – Wireless client records*GPS – (Obviously) GPS records*ALERT – Alerts / IDS functions
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Cheap ways to talk
`netcat'BashSed
Incomprensible but easy
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Talking with bashecho -e '\n!0 enable channel channel,packets' | nc localhost 2501 | awk 'BEGIN { CHN = 0; }; /CHANNEL:/ { chnum[CHN]=$2; chval[CHN]=$3; CHN=CHN+1; }; /TIME/ { if (CHN != 0) { printf("["); for (x = 0; x < CHN; x++) { printf("{\"id\":%s,\"value\":%s}", chnum[x], chval[x]); if (x < (CHN-1)) printf(",") } printf("]\n"); CHN=0; fflush(""); } };'
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Breaking it down
echo -e '\n!0 enable channel channel,packets'
Send a command to enable the CHANNEL sentence, with the fields 'channel' and 'packets'
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Breaking it down
nc localhost 2501
Netcat is a great tool for talking to tcp (or UDP) network hosts from scripts
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Breaking it down
awk 'BEGIN { CHN = 0; }; /CHANNEL:/ { chnum[CHN]=$2; chval[CHN]=$3; CHN=CHN+1; }; /TIME/ { if (CHN != 0) …
Awk is a book in itself, but we begin by setting the # of channels to 0, then when we get the CHANNEL sentence recording it to an array
When we get the TIME sentence we know we've gotten all the channels, so we output it
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Breaking it down
printf("["); for (x = 0; x < CHN; x++) { printf("{\"id\":%s,\"value\":%s}", chnum[x], chval[x]); if (x < (CHN-1)) printf(",") } printf("]\n"); CHN=0; fflush(""); } };'
More awk nastiness, basically just iterates through our array of channels and prints them
At the end, flush the output
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
The result[{"id":1,"value":75202},{"id":2,"value":28589},
{"id":3,"value":8613},{"id":4,"value":6042},{"id":5,"value":9890},{"id":6,"value":27937},{"id":7,"value":19615},{"id":8,"value":8644},{"id":9,"value":761895},{"id":10,"value":27690},{"id":11,"value":47546},{"id":48,"value":15994},{"id":149,"value":1322071},{"id":165,"value":1},{"id":28928,"value":1617419}]
Kismet TCP socket to JSON for an AJAX channel display in 1 line of shell!
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
How it works
Kismet sends the TIME sentence once per second, so we can use it for timing
We know if we see a TIME sentence, we've gotten all the channels Kismet knows about
Normally we'd index by channel #, but this is hard in awk, so we cheat
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
More intuitive
Ruby interface to KismetPeople seem to like Ruby. I'm not sure I doIf you don't, it's easy to port this to perl, python,
etc – patches welcome!Committed to SVN already with examples
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Basic API setup
Require 'kismet'
Standard Ruby moduleKis = Kismet.new(host, port)
Defaults to localhost, 2501Kis.connect
Kis.run
Connect and run as thread
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Subscribing
Kismet.rb allows subscribing to sentences with callbacks
Callbacks called with a dictionary of fields returned
Secondary callbacks when a command completes (more on this soon)
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Subscription
kis.subscribe("bssid", ["bssid", "manuf", "channel"], Proc.new {|*args| bssidcb(*args)})
Subscribe to a sentence (“bssid”) with a list of fields, and a callback
Ruby doesn't do function passing per se, so we use Proc to make a passable block. Bssidcb is our callback function
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Callback
def bssidcb(proto, fields)
puts "Kismet saw network #{fields['bssid']} manuf #{fields['manuf']} on channel #{fields['channel']}"
end
Callback function with sentence and fieldsFields in hash indexed by name
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Ack callbacks
Called when command completesWhen requesting a sentence with historical
data, Kismet sends the historical data, then the ACK
We can use this to trigger that we've gotten the complete current state
It's a bit of a kluge but...
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Ack to die
def bssiddiecb(text)
$k.kill
exit
end
Ack-cb just calls “exit” - we only want to list the networks we've seen so far
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Keep on trucking
Kismet.rb runs the network code in a separate thread
To keep running with subscribed callbacks, call the 'wait' function
Will wait for the Kismet session to end (either naturally or via a kill command elsewhere)
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
What we've made easy
Kismet to Syslog bridge (subscribe to ALERT and use Ruby logger)
Kismet to JSONProgrammatic handling of rogue networksPretty much any arbitrary use of Kismet data
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
LORCON
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
LORCON
Loss Of Radio CONtrolWriting the same code for different drivers sucksWriting the same code for different platforms
sucksHopefully LORCON doesn't suck
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
LORCON2
Unfortunately, LORCON kind of suckedLORCON2 API much cleanerDesigned to match the libpcap APIReally easy to useC, Ruby API, Python under developmenthttp://802.11ninja.net
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Super simple
Automatically determines the type of cardAutomatically creates injection VAPsSupports sniff, inject, or sniff+inject where
possibleSend arbitrary bytes OR use the packet assembly
API
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
It's even easy in Clorcon_driver_t *dri;
lorcon_t *ctx;
uint8_t packet[...];
dri = lorcon_auto_driver(“wlan0”);
ctx = lorcon_create(“wlan0”, dri);
lorcon_open_injmon(ctx);
lorcon_set_channel(ctx, 6);
lorcon_send_bytes(ctx, sizeof(packet), packet);
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
And it comes with ruby
require "Lorcon2"
pp Lorcon.version
pp Lorcon.drivers
pp Lorcon.find_driver("mac80211")
pp Lorcon.auto_driver(“wlan0”)
tx = Lorcon::Device.new(intf)
tx.openinjmon()
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Goal: Simplicity
There's a lot of weird modes you can put a card in
Most of the time you just want inject+monitorMost of the time you just want to send bytesAnd it'd be nice if it worked like pcap
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Pcap + Lorcon
def safe_loop(wifi)
@q = Queue.new
reader = Thread.new do
wifi.each_packet {|pkt| @q << pkt }
end
Some TLC needed (see test.rb in Lorcon) but we integrate with each_packet
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Simple in C, too
void apitest_packet_hdlr(lorcon_t *context, lorcon_packet_t *packet,
u_char *user) { ... }
dri = lorcon_auto_driver(interface);
ctx = lorcon_create(interface, dri)
lorcon_open_injmon(ctx)
lorcon_loop(ctx, 0, apitest_packet_hdlr, NULL);
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Modeled on pcap_loop
Lorcon handles pcap internals (if you want it to)lorcon_loop calls the provided function for each
packetEasy access to dot3 via lorcon_packet_to_dot3
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Building packets
In Ruby, Racket handles most of the packet assembly duties
There are other packet builders tooBut a lot of them are REALLY REALLY slowOrders of magnitude slowerNo great dot11 generator, but Lorcon can
translate dot3 automatically
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Racket L2 – Looks like etherresponse = Racket.new
response.l2 = Ethernet.new("01234567890123")
response.l2.dst_mac = eth.src_mac
response.l2.src_mac = eth.dst_mac
response.l2.ethertype = 0x0800
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Racket L3response.l3 = IPv4.new
response.l3.src_ip = ip.dst_ip
response.l3.dst_ip = ip.src_ip
response.l3.protocol = ip.protocol
response.l3.ttl = ip.ttl
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
+ Lorcon
injpkt = Lorcon::Packet.new()
injpkt.dot3 = response.pack
injpkt.bssid = pkt.bssid
injpkt.direction = Lorcon::Packet::LORCON_FROM_DS;
tx.inject(injpkt) or puts "Failed to inject: " + tx.error
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Lorcon Packet Forge
Packet assembly made easy for 802.11Uses a linked list of temporary dataPackets can be manipulated/appended at willExported into an array for transmit
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
LCPF notes
Lcpa_foo – Lorcon Packet Assembly, basic functions for manipulating packets
Lcpf_foo – Lorcon Packet Forge, packet creation
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Building with LCPFmetapack = lcpa_init();
tx80211_initpacket(&txpack);
lcpf_randmac(sourcemac, 1);
lcpf_randmac(bssidmac, 1);
lcpf_80211headers(metapack, WLAN_FC_TYPE_DATA, WLAN_FC_SUBTYPE_DATANULL, 0x02, /* fcflags, FromDS */ 0x00, /* duration */, targetmac, bssidmac, sourcemac, NULL, /* addr4 */ 0, /* Fragment number */, 0); /* Sequence number */
lcpa_freeze(metapack, &txpack);
stuff();
lcpa_free(metapack);
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
What can we do now?
Kismet + Lorcon + RubySelective interaction with networks“Aggresssive” IDS attacking rogue networks in
your building“Renderman friendly network decloak” … Send a
probe req to a SSID w/ suspected names, let Kismet decloak response
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Putting it in the real world(AKA “the fun part of the talk”)
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Putting this in the real world
Not a bunch of teenagers on MTVAlready part of MetasploitLORCON + Ruby + MSF220 lines of codeAll scripting code (native Ruby)
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
The inspiration
Wifi session hijackingAbout 5 years ago, Airpwn was debuted by Toast
at DefconTCP stream hijacking on 802.11Everyone forgot about this...Not just for shock porn!
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Corps of Engineers
Rerouting TCP streams“Ye Olde” 1990 shared media attackTCP is only “secure” against hijacking because
the seq/ack numbers are randomI see your seq/ack over wireless
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Anatomy of a Session
Handshake (syn/synack/ack)Client-> Server
“GET /foo HTTP/1.0”Seq 123 Ack 0
Server-> Client“HTTP headers, content”Seq 10 ack 189
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
What it takes
Lets add this to MSFLORCON + Lorcon-Ruby wrapperRacket (Ruby packet creator)Ruby-PCAPA little TLC
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
An evil session
HandshakeClient->Server (GET)MSF <-Client (Hijack data)MSF <-Client (FIN!)Server <-Client (Real data, ignored)
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
In action
msf > use auxiliary/spoof/wifi/airpwn
msf auxiliary(airpwn) > set INTERFACE alfa0
INTERFACE => alfa0
msf auxiliary(airpwn) > set RESPONSE "Airpwn - MSF!"
RESPONSE => Airpwn – MSF!
msf auxiliary(airpwn) > run
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Results
msf auxiliary(airpwn) > run
[*] AIRPWN: Response packet has no HTTP headers, creating some.
[*] Auxiliary module execution completed
msf auxiliary(airpwn) >
[*] AIRPWN: 10.10.100.42 -> 208.127.144.14 HTTP GET [/files/racket/src/doc/] TCP SEQ 542050816
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
And because it's Ruby
Fine tuning in YAMLRegex matchingDynamic content generationFile injection or fragment in runtime
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Step 3: Profit
What does all this get us?
Arbitrary HTTP content replacement
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Or in other words
Full control of the DOMControl over formsControl over the browser environmentAccess to anything in the security context of the
hijacked website
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Obviously scripted
So we can replace contentBig deal, what now?Nearly every web-2.0-y site uses gobs of
background javascriptWhat happens if we replace one of those?
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
It's not news, it's JS
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Fragments of JS
Especially attractive as a targetTotally invisible to the userMultiple requests = multiple opportunies to land
attackRun in same privilege domain as web page
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
I'm in your browser
… Rewriting your DOMDOM – Document Object ModelProgrammatic representation of page contentOnce we're in the DOM we can anything
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
It's not stupid, it's advanced
var embeds = document.getElementsByTagName('div');
for(var i=0; i < embeds.length; i++){ if (embeds[i].getAttribute("class") == "cnnT1Img") { embeds[i].innerHTML = "..."; } else if (embeds[i].getAttribute("class") == "cnnT1Txt") { embeds[i].innerHTML = "..."; }}
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
DOM is tasty
What other mischief can we do?Rewrite all forms to proxy through a loggerRewrite all HTTPS to HTTPPoison content topical to a conference?
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
HTTP, not so S
var refs = document.getElementsByTagName('a');
for (var i = 0; i < refs.length; i++){
var rval = refs[i].getAttribute("href");
if (rval == null) { continue; }
refs[i].setAttribute("href", rval.replace(/^https:/, "http:");
}
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
This really matters
This matters
Like, a lot
No, seriously
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Persistence pays off
Who has read rsnakes VPN paper?
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Quick cache
Short version:Browsers have cacheCache sticks aroundUsers don't noticeWhen I own your TCP session I own your cache
control
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
In control
Client fed spiked JS fileMalicious contentCache headers say “keep for 10 years”Malicious file is re-used every time they revisit
the siteFrom inside their company network!
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Don't think it's a problem?
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
What can we do now?
User has spiked, cached fileBrowser will re-use itIframe attacks? Kaminsky socket/sucket? New
browser exploits?But a user would NEVER go to twitter at work,
right?
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Poison the well
How many sites use Analytics?Loading urchin.js from the same url?And what happens if we poison that URL?For every site loading it
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Calling home to Mom
Cache modified JS that loads content from an attacker-controlled server
Maybe no good browser vulns this week?Wait for a browser 0day then flip the switchEveryone w/ cached callbacks gets owned
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
There are no innocents
No website is “innocent”Websites that don't ask for logins are just as
capable as feeding the browser exploitsAny website can have browser-owning code
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Well aren't you clever
I'm smart!I use a VPN!
-or-I force my users to use a VPN via UACThis won't work against me!
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Yeah, but...
Yeah, it wouldn't work...Except your browser has no concept of security
domainsSomething cached in an insecure domain...Is still cached in a secure domain!
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
“Click OK to agree”
Many hotspots have a landing page to agree to EULA
Many landing pages are not encryptedUnencrypted page on an open network? Perfect
targetNow we can feed the user pre-VPN content
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Magic (h)8 ball
If the attacker controls your pre-vpn landing page
They control your browserThey control what gets loadedIframes? Pop-unders? AJAX?
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Americas top 40
Attacker hijacks VPN landing pageInjects code to load things over AJAXLoads the top 40 pages the victim may be likely
to visit in the backgroundCache-poison page requested in the background
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Smarter JS
Attacker can examine fetched contentIf poison code not present...Request it again!We will load your websiteAnd hit it with a brickWe will not run out of bricks
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Frequent landings
Take it one step furtherVPN can access internal pages too, right?We control L2, right?Soo....
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Dumb Network Stuff
Can we use LORCON to attack other protocols?Sure can!Racing DNS isn't hardCapture query, set QR bit, supply our own
response
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Your intranet is showing
We control DNSWe control page queryWe can be sure a request went throughWhat stops us guessing pages like
http://intranet/ ?
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Hint: Nothing
Nothing!How about some JS that loads the original
intranet content...Then crawls the DOM and ships it all off to the
attacker via POST?Or rewrites your form DOMs to proxy out?
SHARKFEST ‘10 | Stanford University | June 14–17, 2010
Summary
Kismet is easy to talk toLORCON is easy to write forOpen wifi is terrifying
http://www.kismetwireless.nethttp://802.11ninja.net