[Security Discussion] cernekee:
Over the past year or so, a number of news stories have discussed the possibility of tracking smartphone users in public via wifi probes. The recent announcement of Pry-Fi inspired me to do a little research to see exactly what information was being revealed, and what could be done about it.

The easiest way to observe the information leak in question (and later, to find out if it has been successfully plugged) is to follow part 4 of Vivek Ramachandran's WLAN Security Megaprimer. He describes the process of setting up a virtual machine running Backtrack Linux (now replaced by Kali Linux), and using aircrack-ng and wireshark to observe WLAN activity.

The whole series is worth watching, but here are a few basic survival commands:

# enable RF monitor mode on wlan0 (your interface might have a different name)
airmon-ng start wlan0

# compile a list of known networks and their BSSIDs; hit ^C to quit
airodump-ng mon0

# tune the WLAN card to the channel of interest (seen in the above list)
iwconfig wlan0 channel 1

# start wireshark and then begin sniffing
wireshark

Android uses the standard wpa_supplicant infrastructure found on Linux PCs to manage the WLAN interface and its database of known access points (APs). The configuration is stored in /data/misc/wifi/wpa_supplicant.conf. I was not able to find this mirrored anywhere else (e.g. in a SQLite database).

The wpa_supplicant daemon is controlled through commands sent over its standard socket interface by $AOSP/frameworks/base/wifi/java/android/net/wifi/WifiInfo.java, its JNI helpers, and libhardware. If your ROM supports it, you can observe what wpa_supplicant is doing by running wpa_cli from a shell, then in another window, running 'adb logcat -s "wpa_supplicant:*"'. Some useful wpa_cli commands include:

# show each outbound message (other levels include: info, debug, excessive)
log_level msgdump

# initiate a scan and dump results (note that the results also show up in
# logcat if the log_level is sufficiently high)
scan
scan_results

# show current status
status

# show known networks
list_networks

# disable scan_ssid on entry #2 from list_networks
set_network 2 scan_ssid 0
save

# reread the wpa_supplicant.conf file
reconfigure

On my N5 running KK 4.4 I needed to preface most commands with "IFNAME=wlan0", but on my N7 running JB 4.2.2 only the bare commands were accepted. YMMV so try both if in doubt.

Here are some observations gathered from experimenting with Nexus 7 (2012) running JB 4.2.2:

There is an entry in wpa_supplicant.conf for every known network.

Any network added manually with the '+' icon will have scan_ssid=1. Any network picked from the list of scan results will not have a scan_ssid property.

When wifi is enabled and the user is in the Settings->Wifi activity, Android will perform a network scan about every 8 seconds.

When wifi is enabled and the user is doing something else, and the interface is not currently associated with an AP, Android will perform a network scan about every 15 seconds.

On each scan, wpa_supplicant will send out probe requests. Every probe request contains the interface's MAC address. :(

wpa_supplicant will send probe requests containing an explicit ESSID name for each entry that has scan_ssid=1. If there are no such entries, the SSID parameter in the probe request will always be empty (signifying a broadcast probe request).

If wpa_supplicant.conf contains a very common ESSID name (like "linksys" or "netgear") then your device will try to associate with any other AP that uses that name. i.e. as expected, it matches the ASCII ESSID rather than the BSSID MAC address

If your device associates with a network, it leaks a whole bunch more data (and could be victimized by a malicious network). So if privacy/security are important, you'll want to carefully control the conditions under which your device associates with wifi networks.

N.B. The baseband side might not be any more trustworthy, but that is beyond the scope of this discussion.

The latter two items potentially leak sensitive data: your MAC address uniquely identifies your smartphone, and an ESSID list can reveal networks to which you have connected in the past (home networks, work networks, etc). In some cases the network names may be unique enough to translate back to a specific geographical location. The ESSID is just an ASCII string, not the AP's MAC address, so it may introduce some degree of ambiguity.

If you're monitoring wpa_supplicant via wpa_cli and logcat, and you used "log_level msgdump", you can see which ESSID names your device is broadcasting:

D/wpa_supplicant(19967): wlan0: Control interface command 'SCAN'
D/wpa_supplicant(19967): wlan0: Setting scan request: 0 sec 0 usec
[color=red]D/wpa_supplicant(19967): Scan SSID - hexdump(len=7): 61 74 74 77 69 66 69[/color]
D/wpa_supplicant(19967): wlan0: Starting AP scan for wildcard SSID
D/wpa_supplicant(19967): WPS: Building WPS IE for Probe Request
D/wpa_supplicant(19967): WPS: * Version (hardcoded 0x10)
D/wpa_supplicant(19967): WPS: * Request Type
D/wpa_supplicant(19967): WPS: * Config Methods (4388)
D/wpa_supplicant(19967): WPS: * UUID-E
D/wpa_supplicant(19967): WPS: * Primary Device Type
D/wpa_supplicant(19967): WPS: * RF Bands (1)
D/wpa_supplicant(19967): WPS: * Association State
D/wpa_supplicant(19967): WPS: * Configuration Error (0)
D/wpa_supplicant(19967): WPS: * Device Password ID (0)
D/wpa_supplicant(19967): WPS: * Manufacturer
D/wpa_supplicant(19967): WPS: * Model Name
D/wpa_supplicant(19967): WPS: * Model Number
D/wpa_supplicant(19967): WPS: * Device Name
D/wpa_supplicant(19967): WPS: * Version2 (0x20)
D/wpa_supplicant(19967): P2P: * P2P IE header
D/wpa_supplicant(19967): P2P: * Capability dev=24 group=00
D/wpa_supplicant(19967): P2P: * Listen Channel: Regulatory Class 81 Channel 6
[color=red]D/wpa_supplicant(19967): nl80211: Scan SSID - hexdump(len=7): 61 74 74 77 69 66 69[/color]
[color=blue]D/wpa_supplicant(19967): nl80211: Scan SSID - hexdump(len=0): [NULL][/color]

The red lines show probe requests that leak the "attwifi" ESSID and your MAC address. The blue line shows a probe request with an empty (broadcast) ESSID, which still leaks your MAC address.

So, what can we do about these leaks? I'll post a list of suggestions and see what else the XDA community comes up with:

Check your wpa_supplicant.conf for any entries with scan_ssid=1. If the network has SSID broadcasts disabled, you need this property; if you are the administrator you can re-enable SSID broadcasts on the AP and then eliminate scan_ssid=1. Vivek's videos explain why disabling SSID broadcast on the AP does not actually provide any security benefits.

Disable "Keep Wi-Fi on during sleep." This option is found under Settings -> Wi-Fi -> (menu) -> Advanced. If wifi is disabled while the phone is in your pocket sleeping, it will not broadcast its MAC address every 15 seconds. This isn't a comprehensive solution but it reduces the scope of the problem.

Run Smarter Wi-Fi Manager. SWM uses your coarse (cellular/GPS?) location to decide whether or not to enable the WLAN radio. Pros: open source; eliminates many possible information leaks. Cons: I believe this requires location services enabled, which may supply location data to other apps and/or impact battery life.

Run Pry-Fi. Pros: I have verified through Wireshark that this indeed randomizes the MAC addresses in the Probe Request packets. Cons: Not open source (and it's a "security" app that runs as root); causes strange interactions with the standard Android UI. Other potential side effects are unknown as the code has not yet been analyzed.

Modify the ROM. It may be possible to tweak wpa_supplicant or the kernel WLAN driver to send random or generic (ff:ff:ff:ff:ff:ff?) MAC addresses in their probe requests. For wpa_supplicant this may be doable through Cydia Substrate. It is likely that a well-done ROM modification would have few or no side effects on usability. However, it may require each individual wpa_supplicant driver or kernel WLAN driver to be modified.

Modify the UI. It would be nice if the Settings -> Wi-Fi interface clearly distinguished entries with scan_ssid=1. Maybe these could be shown in red with a simple Xposed module or ROM tweak.

Any other ideas?