Hacking With Android Rich Hoggan Introduction I was watching the Yahoo/Symantec internet movie Cybergeddon when it came out and was intrigued by the main character Chloe Jocelyn, a special agent a gent with the FBI’s Cyber Cybe r Division. Hollywood has a history of hacker movies, yet they also have a history of developing scenes that focus on “cool factor” rather than realism. As a result, it’s hard to believe that Chloe Jocelyn would be able to actually hack into anything with a cell phone. And while this might be the case for Hollywood, I felt inspired to find the tangible in being able to hack with a cell phone. With that said, this article will focus on the development of a tool for scanning local WIFI access points on an Android. Getting Started In order to write software for the Android platform, we have a couple of choices. First, we could write a full blown Android application using Java and Android’s development kit. Or for faster results, we could write scripts using Python and SL4A or Scripting Layer 4 Android. In doing so, we are able to use the SL4A API which includes phone level access to basic features that could be of interest to those of us interested in hacking with our phones. For example, the API gives us access to the phone’s BlueTooth sensor, the WIFI antenna, GPS, and most other common sensors and operating system components. Another reason for using SL4A is the fact that it includes multiple scripting layer interpreters which means we can pick the best language based on any number of given factors. Currently, SL4A supports HTML, JavaScript, Python, Perl, and PHP just to name a few. With this ability, we can pick languages for their strengths without having to be confined to one language. We will be using Python P ython due to the fact that it’s a powerful scripting language but is also easy to get started with. (It also seemed to be the language with the most support). Lastly, it will be helpful to understand Python fairly well, because while the API does give enough access that most of the underlying code can be ignored, it’s still an API meant for developers. This essentially means that what’s returned from an API call might not be completely useable and might require further code in order to make sense of the output. On a final note before we get started, installing SL4A can be accomplished by going to the associated Google Code website which can be found at http://code.google.com/ p/android-scripting/. Simply click Downloads while in your Android’s browser and download the SL4A application to your phone. Once downloaded and installed, download the desired interpreters and install
those as well; these can be found on the Downloads page as well. With these downloaded, the interpreter’s installers will appear in your applications menu. At this point, tap on them and tap Install to ensure that the interpreters are installed and ready for use within SL4A. Start the SL4A application, and if everything was installed successfully, the demo scripts will appear in the main window. Try any of these to determine if everything is running as it should. If not, double back through your installation process. Finally, figure 1 is an example of what SL4A looks like when running. [Figure 1 - SL4A] Developing A WIFI Scanner
Looking at the SL4A API (http://code.google.com/p/android-scripting/wiki/ApiReference) we come to a section entitled “WifiFacade.” When I stumbled upon this section of the API, I immediately realized that a WIFI scanner could be developed with very little code. And to my amazement, I was right because most of the underlying code is handled h andled in the API calls. Moreover, Moreove r, the scan results return the level of encryption deployed on the access point which makes it easy to see which are using WEP and, better yet, y et, which have no encryption at all. Unfortunately though, this is one of those cases where whe re the output returned is not extremely useful mainly because it’s a blob of text that is not easily read.
However, in order to develop this tool we have to use a few API calls including checkWifiState(), wifiStartScan(), and wifiGetScanResults(). [Figure 2 - WIFI Scanner Source Code - See Apendix] Figure 2 is a listing of the source code for WifiFinder. The tool is comprised of a few basic functions, scanning for access points, displaying the closest access point to the screen, and writing all the returned access points to an XML file for later analysis. The application first checks whether or not the WIFI antenna is active. This check is performed by the checkWifiState() function, and if the antenna isn’t active then scanning won’t be started and an error message is displayed to the user. If, however, the antenna is active, scanning starts by calling the wifiStartScan() function which has a similar check in place to make sure the scan returned without errors. If the scan returns successfully, the results are then returned from wifiGetScanResults() and stored in appropriate data structures before being pushed to the screen and written to the XML file. [Figure 3 - wifiGetScanResults() default output]
[Figure 4 - Data Structures] for current in accessPointTokensString: #Store each access point in the accessPointsArray for #further processing accessPointsArray.append(current) totalAccessPoints = totalAccessPoints + 1
Figure 3 shows what the default output looks like when returned by the wifiGetScanResults() API call. As can be seen, it would take far too long to understand the output and also the output is displayed in a Dictionary format. Essentially, this means that the returned output is accessible through a key/value pair relationship. For example, we could access each of the access point’s components by referencing the appropriate key such as ssid, capabilities, or frequency. [Figure 5 - Closest Access Point] for current in accessPointsArray: #Iterate through accessPointsArray and determine which level reading #was the closest currentLevel = current['level'] levels.append(currentLevel) #Determine if the levels list has any values if len(levels) > 0: lowestLevel = max(levels) else: printError("noValues") #Iterate through accessPointsArray and match lowestLevel #to current['level'] for current in accessPointsArray: if current['level'] == lowestLevel: #Print access point's data to the screen print "['ssid']:", current['ssid'] print "['capabilities']: ", current['capabilities'] print "['frequency']", current['frequency'] print "['bssid']:", current['bssid'] print "['level']:", current['level'] With the ability to access each component of the access point separately, we are able to isolate certain pieces of information for further processing. For example, we can iterate through the level component of each access point and calculate which access point was closest at the time of scanning, or we could iterate through the capabilities of each access point and determine which is using WEP authentication. Figure 5 illustrates how we can go about calculating the closest access point. The first thing the code does is copy each signal level value into an array. At this point, we check to determine if there are any levels stored in the array, and if so, we can set the lowestLevel variable to the value returned by the max() function. Finally, we can iterate over the array list and print out the
access point data associated with the signal level which in turn is the closest access point. Now this would seem to be counter intuitive, but when dealing with signal levels such as those associated with routers, the closer the number is to 0, in theory, the closer it is to the phone’s WIFI antenna. It should be noted -- however ho wever -- that this is not a sure fire way of finding the closest access acc ess point in that it’s merely providing a rough estimate and is not intended for precision.
[Figure 6 - XML Output Format] #Write access points to a file on the sdcard and determine which #authentication measures are being implemented for current in accessPointsArray: #Iterate through accessPointsArray, access each dictionary #and print out each access point component """ Generate XML strings using the following format:
""" currentSSID = "\t\t
" + str(current['ssid']) + "\n" currentBSSID = "\t\t
" + str(current['bssid']) + "\n" currentCapabilities = "\t\t
" +str(current['capabilities']) + " capabilities>\n" currentFrequency = "\t\t" + str(current['frequency']) + " frequency>\n" currentLevel = "\t\t" + str(current['level']) + "\n" #Write xml strings to the file fileWriter.write("\t\n") file Writer.write(currentSSID) fileWriter.write(currentBSSID) fileWriter.write(currentCapabilities) fileWriter.write(currentCapabilities) fileWriter.write(currentFrequency) fileWriter.write(currentFrequency) fileWriter.write(currentLevel) fileWriter.write(currentLevel) fileWriter.write("\t\n") fileWriter.write("\t\n") #Write the end of the xml file file Writer.write("\n") The last functionality we will talk about is writing the returned access points to an XML file. There is nothing special about writing the XML file as we are merely developing XML tags for describing each access point’s components. And while this can be useful for analyzing captured data using other tools, we could write to a text file as well which makes for easier viewing of all the access points. But because we are writing files, it’s important to take a moment and discuss how files are stored in SL4A. A typical file path to where scripts are stored is “sl4a/scripts/.” This will usually be established when SL4A is
installed, yet another option is to install SL4A onto an SD card which will keep everything portable especially because it’s possible to write Python code in a standard IDE or text editor and have pure Python code run for testing purposes on the native machine (this is not so for any SL4A API calls, however, in that they have to run in SL4A unless emulated). In this case, the file path will be “sdcard/sl4a/scripts.” There are no hard and fast rules about where to keep scripts aside from the base file path as previously described. Keeping your scripts organized will be helpful when determining where to store output files though. Figure 6 shows an example of XML output. [Figure 7 - XML output]
[Figure 8 - WIFIFinder Running]
Finally, in figure 7 we see an example of our newly minted tool up and running. As you can see there is no fancy user interface, and there doesn’t need to be. The only thing on the screen is information. In it’s current state, the application doesn’t constantly return the closest access point, nor constantly scan for access points which could be considered a setback seeing as how you’ll have hav e to keep running the application to refresh scan results. Which brings us to our last point -- usability. Currently, the application has bugs in it such as inconsistent results from the WIFI antenna in that it would scan and sometimes it wouldn’t. On top of that, we are running a development platform that itself, never left Alpha. And from what I’ve seen, there hasn’t been that much forward movement on furthering SL4A. What does this all mean? This means that the tools being developed run and have an actual purpose, but they are being developed and run on an experimental platform. Similarly, no guarantees can be given that they will run on every Android phone that could be encountered. But there is a light at the end of the tunnel because they were tested on newer phones and seemed to run without incident. Conclusions As we have seen in this article, it’s possible to develop tools using SL4A and Python that can run on Andriod devices. Similarly, it’s possible to have access to standard Python programming functionality within SL4A which allows for more powerful mobile applications. More so, we have developed a fully mobile application. This means that we can run the app while riding on a bus. It’s also quite easy to see the potential in developing tools on this platform because at the end of the day, it looks like we are
simply playing on our phones. And detection is the name of the game when attempting to effectively defensively or offensively assess security. It doesn’t look like much right now, and obviously more commercially available options are still the most effective, but the mobile platform is surely becoming the primary means of staying productive in society and this means advancing all aspects of technology including security tools especially where they run. In other words, this isn’t the end inasmuch as it’s the beginning. Apendix A - Full Source Code #Application: WIFIFinder #Developer: Rich Hoggan #Creation Date: 10-03-2012 """ Description: WIFIFinder scans for WIFI access points and prints data about the closest access point as well as scan statistics. Last Modification Date: 10-27-2012 Modification Descriptions: 10052012 - Added code for reading GPS sensor when closest access point is found. 10272012 - Output now writes to an XML file. Also optimized file handling. """ #Compatibility: #Operating System: Android 2.2+ (W/SL4A Installed) #Python Version: 2.6 #Python Issues: N/A ###Import Directives### import sys import platform import android """ Function Code """ #Function Name: printProgramInformation() #Function Description: Prints the program's initial information def printProgramInformation(): print "WifiFinder" print "Developed By: Rich Hoggan C2012" """ Function Name: printError() Function Description: Prints an error message based on the error flag
Allowed arugments: inputError checkStateFailed connectionInfoFailed scanFailed """ def printError(errorFlag): if errorFlag == "inputError": print "Invalid input. Double check your input and try again." elif errorFlag == "checkStateFailed": print "WIFI state check failed. Try again later." elif errorFlag == "connectionInfoFailed": print "Could not retreive connection information." elif errorFlag == "scanFailed": print "WIFI scan could not be completed. Try again later." elif errorFlag == "noValues": print "No access point levels were found." else: print "Invalid argument was passed to printError() function." """ Function Name: printStatus() Function Description: Prints a status message based on the status flag Allowed arugments: checkingWifiState startingScan accessPointsFound """ def printStatus(statusFlag): if statusFlag == "checkingWifiState": print "" print "Checking phone's WIFI state..." elif statusFlag == "startingScan": print "Starting access point scan..." elif statusFlag == "accessPointsFound": print "Access points were found..." print "Displaying possible access points" else: print "Invalid argument was passed to printStatus() function." """ Application Code """ #Variable and object declarations getWifiState = "" getScanResults = "" scanStart = "" errorFlag = ""
accessPointTokensString = "" accessPointsArray = [] split = "" current = "" totalAccessPoints = 0 counter = 0 closestAccessPoint = 0 accessPointGeoLocation = "" lowestLevel = 0 levels = [] testing = "" #File variables currentSSID = "" currentCapabilities = "" currentFrequency = "" currentBSSID = "" currentLevel = "" #Android object variables droid = android.Android() printProgramInformation() #Create file object fileWriter = open("/sdcard/sl4a/scripts/development/MobileDevelopment/WIFIFinder/access_poi nts.xml", "w") #Determine the phone's wifi state printStatus("checkingWifiState") getWifiState = droid.checkWifiState() if getWifiState: #If state check passes, print message and start #scanning for wifi networks printStatus("startingScan") scanStart = droid.wifiStartScan() if scanStart: #If wifi scan passed, print message and format #output for viewing printStatus("accessPointsFound") #Pass getScanResults to accessPointTokensString array getScanResults = droid.wifiGetScanResults() accessPointTokensString = getScanResults[1] #Build data structure for storing access point information print "All Access Points Returned From Scan:"
for current in accessPointTokensString: #Store each access point in the accessPointsArray for #further processing accessPointsArray.append(current) totalAccessPoints = totalAccessPoints + 1 print "" #Begin XML file fileWriter.write("\n") fileWriter.write("\n") #Write access points to a file on the sdcard and determine which #authentication measures are being implemented for current in accessPointsArray: #Iterate through accessPointsArray, access each dictionary #and print out each access point component """ Generate XML strings using the following format: """ currentSSID = "\t\t" + str(current['ssid']) + "\n" currentBSSID = "\t\t" + str(current['bssid']) + "\n" currentCapabilities = "\t\t" + str(current['capabilities']) + "\n" currentFrequency = "\t\t" + str(current['frequency']) + "\n" currentLevel = "\t\t" + str(current['level']) + "\n" #Write xml strings to the file fileWriter.write("\t\n") fileWriter.write(currentSSID) fileWriter.write(currentBSSID) fileWriter.write(currentCapabilities) fileWriter.write(currentFrequency) fileWriter.write(currentLevel) fileWriter.write("\t\n") #Write the end of the xml file
fileWriter.write("\n") #Determine which access point is the closest print "Closest Access Point:" print "---------------------" for current in accessPointsArray: #Iterate through accessPointsArray and determine which level reading #was the closest currentLevel = current['level'] levels.append(currentLevel) #Determine if the levels list has any values if len(levels) > 0: lowestLevel = max(levels) else: printError("noValues") #Iterate through accessPointsArray and match lowestLevel #to current['level'] for current in accessPointsArray: if current['level'] == lowestLevel: #When the closet access point is found, read the GPS sensor's #current location #accessPointGeoLocation = droid.readLocation() #Print access point's data to the screen print "['ssid']:", current['ssid'] print "['capabilities']: ", current['capabilities'] print "['frequency']", current['frequency'] print "['bssid']:", current['bssid'] print "['level']:", current['level'] #print "Last known location: ",accessPointGeoLocation #Close file objects fileWriter.close() else: printError("scanFailed") else: printError("checkStateFailed")