Having captured a few WiFi hot spots while driving around I wanted to export these hotspots to a KML file that I could then import into Google Maps. There are applications and scripts to do this but I decided to have a go a creating something myself. As I am currently learning Python and in my opinion the best way to learn a new language is by doing then Python was my first choice for this. I am by no means a Python expert and there will be better ways of doing this I am sure.
The first step is to open the Kismet XML file for parsing. The simplest way I found was to use an ElementTree.
import sys import xml.etree.ElementTree as Etree # Open the XML file tree = Etree.parse("test.netxml") root = tree.getroot()
Next we need to parse the child nodes looking specifically for the “wireless-network” tag. From there we can parse each sub element to extract the information we are interested in. Including the SSID, MAC address, location etc.
for child in root: # If new network entry if child.tag == "wireless-network": essid = "" bssid = "" encryption = [] gpslat = "" gpslng = "" # Iterate through child elements for element in child: # SSID element if element.tag == "SSID": # Interate through each sub element for subelement in element: # Found encryption type add it to the list if subelement.tag == "encryption": encryption.append(str(subelement.text)) # Found SSID save it elif subelement.tag == "essid": essid = str(subelement.text) # BSSID element contains MAC address save it if element.tag == "BSSID": bssid = str(element.text) # Location fix element if element.tag == "gps-info": for gps in element: # Extract latitude if gps.tag == "avg-lat": gpslat = str(gps.text) # Extract longitude if gps.tag == "avg-lon": gpslng = str(gps.text) # Sort encryption list encryption.sort()
With the input file parsed we move on to exporting the results to a KML file. For this I used the SimpleKML python library. SimpleKML makes working with KML files a breeze.
import simplekml # Create an instance if simplekml kml = simplekml.Kml() # Add a new point kml.newpoint(name=essid, description=bssid + " " + ' '.join(encryption), coords=[(gpslng, gpslat)]) # Now save it kml.save("output.kml")
That’s the basics covered. Time to add some features. One thing I wanted was to be able to filter hotspots found within a specific radius of a given point. For instance if you only wanted to save hotspots within a mile of your house you could enter you home location and set a search radius of one mile. The resulting KML file would then only contain hotspots that fell within that radius. For this we can use the Haversine formula. The Haversine formula determines the great-circle distance between two points on a sphere (in this case the earth) given their longitudes and latitudes. There is also happens to be a nice little Haversine python library.
from haversine import haversine home = (45.7597, 4.8422) dest = (48.8567, 2.3508) distance = haversine(home, dest, unit='mi') if distance > 1.0: print ("Greater than 1.0 miles!")
More features were then added including/excluding specific SSID’s, specifying certain authentication types, specifying the output file etc all of which are passed as arguments to the script.
My resulting netxml2kml script can be seen below:-
#!/usr/bin/env python3 import sys import xml.etree.ElementTree as Etree import os.path import simplekml import argparse from haversine import haversine # python netxml2kml.py test4.netxml output.kml -r 5.0 -x 45.7597 -y 4.8422 -s # python netxml2kml.py test4.netxml output.kml -i Raspberry -s # python netxml2kml.py test4.netxml output.kml -e Raspberry -s # python netxml2kml.py test4.netxml output.kml -a WEP -s def str2bool(v): if v.lower() in ('yes', 'true', 't', 'y', '1'): return True elif v.lower() in ('no', 'false', 'f', 'n', '0'): return False else: raise argparse.ArgumentTypeError('Boolean value expected.') parser = argparse.ArgumentParser(description='Convert Kismet Net XML to KML file.') parser.add_argument("input_file", help="the Net XML file to be converted.") parser.add_argument("output_file", help="the KML output file.") parser.add_argument("-e", "--exclude", default="", help="exclude SSID string.") parser.add_argument("-i", "--include", default="", help="include SSID string.") parser.add_argument("-a", "--auth", default="", help="include search authentication string.") parser.add_argument("-r", "--radius", type=float, default=0.0, help="include radius") parser.add_argument("-x", "--lat", type=float, default=45.7597, help="start latitude") parser.add_argument("-y", "--lon", type=float, default=4.8422, help="start longitude") parser.add_argument("-s", "--show", type=str2bool, nargs='?', const=True, default=False, help="Show output entries.") args = parser.parse_args() def do_extraction(filename: str, output: str, exclude: str, include: str, auth: str, show: bool, radius: int, lat: float, lon: float) -> int: if os.path.exists(filename) is False: print("Cannot find input file \"%s\"" % filename) sys.exit(1) print("Opening file \"%s\"" % filename) print("Saving to file \"%s\"" % output) tree = Etree.parse(filename) root = tree.getroot() kml = simplekml.Kml() print("Extracting entries...") found = 0 for child in root: if child.tag == "wireless-network": essid = "" bssid = "" encryption = [] gpslat = "" gpslng = "" for element in child: if element.tag == "SSID": for subelement in element: if subelement.tag == "encryption": encryption.append(str(subelement.text)) elif subelement.tag == "essid": essid = str(subelement.text) if element.tag == "BSSID": bssid = str(element.text) if element.tag == "gps-info": for gps in element: if gps.tag == "avg-lat": gpslat = str(gps.text) if gps.tag == "avg-lon": gpslng = str(gps.text) encryption.sort() valid = True if radius > 0.0: if gpslat and gpslng: home = (lat, lon) dest = (float(gpslat), float(gpslng)) dist = haversine(home, dest, unit='mi') if dist > radius: valid = False if include and essid.find(include) = 0: valid = False if auth and auth not in encryption: valid = False if valid: found += 1 kml.newpoint(name=essid, description=bssid + " " + ' '.join(encryption), coords=[(gpslng, gpslat)]) if show: print(essid + "," + bssid + "," + ' '.join(encryption) + "," + gpslat + "," + gpslng) kml.save(output) print("Extracted %d entries." % found) return found if __name__ == "__main__": ret = do_extraction(args.input_file, args.output_file, args.exclude, args.include, args.auth, args.show, args.radius, args.lat, args.lon)