Thought I’d publish this one, was going to try to use it as some kind of technology worth selling (as most in this sector seem to do with 50 lines of code and an idea nowadays) but in a moment of brilliance remembered what things were like back in the day and what we stood for. Call me a changed man, if you will. You will be happy to hear that I still love and adore using excessive smilies
anyways, enough of the bee-ess. on with the show.
With Samy.pl doing his talk at defcon about geolocation it took me back to my first java code back in February of this year. It was built on samys early idea of exploiting a Verizon FIOS router to get a location. I thought “well if what Samy is doing is exploiting a FIOS router to get its MAC address then sending it to google, surely there is a better way of getting the routers MAC without having to exploit a flaw in a router or browser?” The answer was simple – if you are reading this page then the MAC of your router is already on your local machine in the ARP cache.
I just needed some way to pull that info out and thought of a java applet. To quote one of my cohorts from textfiles days: “Boom. Paydirt !!” While its awesome Samy has expanded his exploits to more routers in the time that has passed since his original idea and expanded on ways to extract the information remotely, from a practicality and compatibility point of view, there was always one easy way to go with this and I picked up on that a while ago. I’m surprised that only three of my contacts wrote me and asked for the coedz to this when I posted http://iwtf.net/2010/02/05/where-the-hell-am-i/…. Read on
The idea is that many people run java applets when they visit websites. Sometimes, depending on the level of authorisation on the applet, they see a box pop up about a security warning and just click OK as they trust the site they are on. Not everyone does this, but then the applet they are trying to run wont load, will it? The method I use is signed java applications, which give the java VM access to system calls as they are allowed to run outside of the sandbox… see here. Whats more worrying is that any site you visit which runs a signed java applet can retrieve this information without you even knowing.
Even without a CA behind your signed applet I have noticed that a self signed applet seems to get accepted by most users, however I would imagine that an applet signed with a CA behind it would be more fruitful
This applet could be anything from an IRC applet, a speedtest applet, through to some game or stocks/shares applet – the list goes as far as any java applet you can think of (or ones you have not yet thought of). As anybody worth his salt knows the VM can exclude certain methods in your local properties, but the majority of users do not know this or edit their local properties, so I relied on that in this idea. My analysis of those that visited my original applet showed that 90% of users ran the self signed applet – id bet it would be higher if it were signed by a trusted CA
Using the system calls allowed by a signed applet, I determine the MAC address of the default gateway attached to the machines (first by querying the routing table, then by querying the ARP cache). I then use this address to poll google Geolocation, which will either return a hit on the MAC address and show a relatively accurate location, or it will not recognise the MAC and return an IP location based on its general location knowledge of the IP address polling it.
I personally do nothing with this data (as the code is only running locally on the users machine and not ‘talking back’ in my POC), but app writers could easily have the code return the google co-ordinates to them via XML/JSON/whatever before their applet does its real job – thus learning their users location just by the fact the user has ran a java applet which they have either self signed or had signed with CA backing.
So while Samys attack is great if you want to use practices such as cross site scripting and rely on certain routers being in use which are exploitable, using methodology similar to mine in your signed java applet seems like a ‘cleaner’ way of doing things which will return more fruit as the local ARP cache already has the MAC of the default GW in there (as someone has just visited your website). In addition you have cross compatibility with many platforms by using a Java VM without having to rely on XSS in specific browsers or exploitable routers. Dont quote me on that ‘cleaner’ bit though
This vector does work better on people who are using wireless, obviously, however many wifi/hardwire combo routers use the same MAC on their bridge component so the same MAC also shows up in the ARP cache when connected to ethernet ports the same as it does when connected to wireless. Of course, if the user in question does not have their MAC logged in googles DB, then you only get the general location that google return from their knowledge of the IP address polling them (which is also your target user as the applet is running on their machine – better to have a general idea than none at all, right?).
so without further ado, heres the code. Yep – put together like a POS but it was my first java program after all – no points for spotting my perl experience. While I did try to adapt it for the Macs out there, I dont own one to test it so kind of cut it out of the initial routing discovery phase but left some of the concept in there for Mac’s. The POC should work on most Win or Linux systems, and im sure you Mac guys out there can figure out making it work for Mac, just as im sure you java coders can make it work accross more releases of certain O/s’s. Also should add that google seems to have changed their URL structure on maps, so that will need updating in the code – ill update it when I have time, but you get the concept and the code will still return co-ordinates. You could even use JSON parsers and import gears if you wanted to be uber clever, but I was going for lightweight myself. Also, pretty please, ignore my excessive imports. Again – first java code
If you do use this idea in your code, it would be most cool of you to stick a greet to me and samy in there, like we all did back in the day. Hope to see some of you at the next Defcon – while I dont speak there much I have been known to provide some of the background soundtrack.
Cheers,
Simon
chmod 744 onwiththecode.sh ; ./onwiththecode.sh
[code]
import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.awt.event.*;
import javax.swing.*;
public class locate extends Applet implements ActionListener{
public void init() {
JOptionPane.showMessageDialog(null, "Trying to track you down now, a button should appear once the process is completed. Copyright 2010 iwtf.net. Visit www.iwtf.net for more info.");
String link_Text = "Show me on a map";
String[] defTable;
String[] arpTable;
String routerIP = "0.0.0.0";
String routerHWMAC = "00:00:00:00:00:00";
String line = null;
Process result = null;
try {
result = Runtime.getRuntime().exec("netstat -rn");
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader output = new BufferedReader(new InputStreamReader(result.getInputStream()));
try {
line = output.readLine();
} catch (IOException e) {
e.printStackTrace();
}
String info = "0";
while(line != null){
if (isWindowsXP()){
info = line.toLowerCase();
if ( info.startsWith("default") == true ){
defTable = info.split("\\s+");
routerIP = defTable[2];
}
}else if (isWindowsVista()){
info = line.toLowerCase();
if ( info.contains("0.0.0.0 0.0.0.0") == true ){
defTable = info.split("\\s+");
routerIP = defTable[3];
}
}else if (isWindowsSeven()){
info = line.toLowerCase();
if ( info.contains("0.0.0.0 0.0.0.0") == true ){
defTable = info.split("\\s+");
routerIP = defTable[3];
}
}else if(isUnix()){
if ( line.contains("UG") == true ) {
defTable = line.split("\\s+");
routerIP = defTable[1];
}
}else{
System.out.printf("Sorry your operating system is not supported\n");
System.exit(0);
}
try {
line = output.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
if (routerIP.contains("0.0.0.0") == true){
System.out.printf("There was a problem. Aborted\n");
System.exit(0);
}
Process arpResult = null;
try {
arpResult = Runtime.getRuntime().exec("arp -a");
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader defOutput = new BufferedReader(new InputStreamReader(arpResult.getInputStream()));
String arpLine = null;
try {
arpLine = defOutput.readLine();
} catch (IOException e) {
e.printStackTrace();
}
while(arpLine != null){
if (isWindowsXP()){
if ( arpLine.contains(routerIP) == true ) {
arpTable = arpLine.split("\\s+");
routerHWMAC = arpTable[2];
}
}else if (isWindowsVista()){
if ( arpLine.contains(routerIP) == true ) {
arpTable = arpLine.split("\\s+");
routerHWMAC = arpTable[2];
}
}else if (isWindowsSeven()){
if ( arpLine.contains(routerIP) == true ) {
arpTable = arpLine.split("\\s+");
routerHWMAC = arpTable[2];
}
}else if((isMac()) || (isUnix())){
if ( arpLine.contains(routerIP) == true ) {
arpTable = arpLine.split("\\s+");
routerHWMAC = arpTable[3];
}
}else{
System.out.printf("Sorry your operating system is not supported\n");
System.exit(0);
}
try {
arpLine = defOutput.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
if (routerHWMAC.contains("00:00:00:00:00:00") == true){
System.out.printf("There was a problem. Aborted\n");
System.exit(0);
}
String jsonString = "{\"version\": \"1.1.0\",\"host\": \"maps.google.com\",\"home_mobile_country_code\": 310,\"home_mobile_network_code\": 410,\"radio_type\": \"gsm\", \"carrier\": \"Vodafone\",
\"request_address\": true,\"address_language\": \"en_GB\",\"wifi_towers\": [ { \"mac_address\": \"" + routerHWMAC + "\", \"signal_strength\": 100, \"age\": 0 } ]}";
int port = 80;
String hostname = "www.google.com";
InetAddress addr = null;
try {
addr = InetAddress.getByName(hostname);
} catch (UnknownHostException e) {
e.printStackTrace();
}
Socket socket = null;
try {
socket = new Socket(addr, port);
} catch (IOException e) {
e.printStackTrace();
}
String path = "/loc/json";
BufferedWriter wr = null;
try {
wr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
wr.write("POST "+path+" HTTP/1.0\r\nContent-Length: "+jsonString.length()+"\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n");
} catch (IOException e) {
e.printStackTrace();
}
try {
wr.write(jsonString);
} catch (IOException e) {
e.printStackTrace();
}
try {
wr.flush();
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader rd = null;
try {
rd = new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
String linez;
String response = "0";
try {
while ((linez = rd.readLine()) != null) {
response = response + linez;
}
} catch (IOException e) {
e.printStackTrace();
}
try {
wr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
rd.close();
} catch (IOException e) {
e.printStackTrace();
}
if ( response.contains("location") == true ) {
System.out.printf("Got a location : ");
}else{
System.out.printf("Error communicating with location database\n");
System.exit(0);
}
String split1[] = response.split("latitude\":");
String split3[] = split1[1].split(",");
String split4[] = split3[1].split(":");
Global.mapURL = "http://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=" + split3[0] + "," + split4[1] + "&sll=" + split3[0] +"," + split4[1];
System.out.printf("%s\n", Global.mapURL);
Button b = new Button(link_Text);
b.addActionListener(this);
add(b);
}
public void actionPerformed(ActionEvent ae){
Button source = (Button)ae.getSource();
try
{
AppletContext a = getAppletContext();
URL u = new URL(Global.mapURL);
a.showDocument(u,"_blank");
}
catch (MalformedURLException e){
System.out.println(e.getMessage());
}
}
public static boolean isWindowsXP(){
String os = System.getProperty("os.name").toLowerCase();
return (os.indexOf( "windows xp" ) >= 0);
}
public static boolean isWindowsVista(){
String os = System.getProperty("os.name").toLowerCase();
return (os.indexOf( "windows vista" ) >= 0);
}
public static boolean isWindowsSeven(){
String os = System.getProperty("os.name").toLowerCase();
return (os.indexOf( "windows 7" ) >= 0);
}
public static boolean isMac(){
String os = System.getProperty("os.name").toLowerCase();
return (os.indexOf( "mac" ) >= 0);
}
public static boolean isUnix(){
String os = System.getProperty("os.name").toLowerCase();
return (os.indexOf( "nix") >=0 || os.indexOf( "nux") >=0);
}
}
public class Global {
public static String mapURL = null;
}
[code]
Admiring the commitment you put into your blog and detailed information you present. It’s good to come across a blog every once in a while that isn’t the same unwanted rehashed material. Wonderful read! I’ve bookmarked your site and I’m adding your RSS feeds to my Google account.
Quite a beautiful website. I recently built mine and i was looking for some ideas and your website gave me some. May i ask you whether you developed the website by youself?
Cheers
I find myself coming back to your web-site only because you have lots of awesome insights and also you happen to be at this a while, which is very impressive and tells me you know your stuff.
[...] See http://iwtf.net/2010/08/04/accurate-geolocation-of-your-users/ for [...]