In this third installation of my blog series about wireless presentation devices, I’ll focus on how to discover exposed network services and how to reverse engineer proprietary network protocols. We’ll rely on information gained during the two previous posts to do so. You can find those posts there:
- Man-in-the-conference room - Part I (Introduction)
- Man-in-the-conference room - Part II (Hardware Hacking)
1. Network Scan
One of the easiest way to perform a network scan without any interference is to get your own machine to act as a gateway and connect the device under test to it.
I’ve been using the following bash script for a while now, it follows those 4 simple steps:
- Create a DHCP server config with a /24 subnet
- Bring up the interface to which the device is connected
- Apply iptables rules to NAT traffic to the outside world
- Launch DHCP server
gw_up.sh
This one simply brings everything down:
gw_down.sh
1.1 IPv6 support ?
One point that is often overlooked is whether the device supports IPv6 or not. This is important because some devices will support IPv6 but only use iptables for firewalling and leave things opened given that they forgot about ip6tables. One of the easiest way to check for IPv6 support externally is to try to reach the device’s IPv6 link-local address.
Link-local addresses in IPv6 are derived from the device’s MAC address, so let’s find it using arp:
Once you have it you can easily derive the link-local address using a bash script like this one:
$ mac_to_ipv6.sh 00:12:5f:16:30:9f
fe80::0212:5fff:fe16:309f
Now that we have the address, we can try to ping it with ping6. Note that you need to mention the interface to which the device is connected at the end of the address if you didn’t set explicit routing with ‘ip -6 route’.
$ ping6 -c 5 fe80::0212:5fff:fe16:309f%eth0
PING fe80::0212:5fff:fe16:309f%eth0(fe80::212:5fff:fe16:309f) 56 data bytes
From fe80::36e6:d7ff:fe01:3471 icmp_seq=1 Destination unreachable: Address unreachable
From fe80::36e6:d7ff:fe01:3471 icmp_seq=2 Destination unreachable: Address unreachable
From fe80::36e6:d7ff:fe01:3471 icmp_seq=3 Destination unreachable: Address unreachable
From fe80::36e6:d7ff:fe01:3471 icmp_seq=4 Destination unreachable: Address unreachable
From fe80::36e6:d7ff:fe01:3471 icmp_seq=5 Destination unreachable: Address unreachable
--- fe80::0212:5fff:fe16:309f%eth0 ping statistics ---
5 packets transmitted, 0 received, +5 errors, 100% packet loss, time 3999ms
We see the address is unreachable, which likely means the device does not support IPv6. Note: this hypothesis was later confirmed in my tests.
1.2 TCP Scan (IPv4)
We know the device only supports IPv4 so let’s scan the address it got from our DHCP server with Nmap and see what’s open. We’ll start with a full TCP scan with service fingerprinting enabled, no ping, no DNS resolution:
$ nmap -sV -p- -Pn -n -T4 192.168.100.2 Nmap scan report for 192.168.100.2 Host is up, received arp-response (0.00050s latency). Not shown: 65526 closed ports Reason: 65526 resets PORT STATE SERVICE REASON VERSION80/tcp open http syn-ack ttl 64 lighttpd 1.4.37
389/tcp open ldap? syn-ack ttl 64 → scdecapp (association)
443/tcp open ssl/http syn-ack ttl 64 lighttpd 1.4.37
515/tcp open printer? syn-ack ttl 64 → scdecapp (streaming)
7000/tcp open afs3-fileserver? syn-ack ttl 64 → AirplayService
8080/tcp open http-proxy? syn-ack ttl 64 → scdecapp (association) 19996/tcp open unknown syn-ack ttl 64 → scdecapp (association)
31865/tcp open unknown syn-ack ttl 64 → scdecapp (streaming)
49153/tcp open rtsp syn-ack ttl 64 → AirplayService
Service description beginning with an arrow are manual addition as they are not recognized by Nmap.
The device is exposing three main kind services:
- a lighttpd server hosting the web GUI
- an Airplay service
- custom services listening on multiple ports, named “scdecapp” based on initial firmware analysis.
HTTP Web GUI (lighttpd)
The web GUI is not unusual. A lighttpd server with CGI scripts behind. This is what the interface looks like:
Note that two users with default credentials are set: admin/admin and moderator/moderator.
AirplayService
The exposure of this service breaks the Airmedia protocol purpose. The whole idea behind that proprietary protocol is that a user must enter a PIN code to be able to stream content. Given that by default Airplay does not force users to authenticate, anyone can stream arbitrary content to the device, thus bypassing Airmedia custom protocol.
This can be demonstrated using any open implementation of Airplay such as open-airplay:
$ git clone https://github.com/jamesdlow/open-airplay.git
$ cd open-airplay/Java && ant
$ java -jar build/airplay.jar -h 192.168.100.2 -p /tmp/this_is_fine.jpg
Awind Protocol (scdecapp)
This is a proprietary protocol developped by Awind (OEM provider of Crestron). It takes care of discovery, association, and streaming of content. We will reverse engineer it in the next section.
1.3 UDP Scan (IPv4)
Now that we’ve covered TCP, let’s move to UDP ! As we can see in the excerpt below, the device exposes three services: NetBIOS, SNMP, and mDNS:
$ nmap -sUV -p- -T4 -Pn -n 192.168.100.2
Nmap scan report for 192.168.100.2
Host is up, received arp-response (0.00054s latency).
Reason: 981 port-unreaches
PORT STATE SERVICE REASON VERSION
137/udp open netbios-ns udp-response ttl 64 Microsoft Windows XP netbios-ssn
161/udp open snmp udp-response ttl 64 SNMPv1 server; Crestron Electronics, Inc. SNMPv3 server (public)
5353/udp open mdns udp-response ttl 255 DNS-based service discovery
NetBIOS
You can confirm NetBIOS exposure using nbtscan. I still can’t wrap my head around why they would need to expose such service, but yet it is there.
$ nbtscan 192.168.100.2
Doing NBT name scan for addresses from 192.168.100.2
IP address NetBIOS Name Server User MAC address
------------------------------------------------------------------------------
192.168.100.2 AIRMEDIA-16309F server AIRMEDIA-16309F 00:00:00:00:00:00
SNMP
Out of the box, the device exposes SNMP version 1 and version 2c using default read and write communities (public, private). The best way to interact with it is to use snmpget, snmpset, and snmpwalk utilities:
$ snmpwalk -c public -v1 192.168.100.2
SNMPv2-MIB::sysDescr.0 = STRING: Crestron Electronics AM-100 (Version 2.4.1.19)
--snip--
mDNS
To check services advertised over multicast DNS, nothing better than Metasploit auxiliary/scanner/mdns/query
module:
msf5 > use auxiliary/scanner/mdns/query
msf5 auxiliary(<span style="color:#F44336">scanner/mdns/query</span>) > run
[*] Sending mDNS PTR IN queries for _services._dns-sd._udp.local to 192.168.100.2->192.168.100.2 port 5353 (1 hosts)
[+] 192.168.100.2 responded with _services._dns-sd._udp.local: (PTR _raop._tcp.local, PTR _airplay._tcp.local)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
Looking on the wire we can see the device advertising as an “Apple TV version 3.2”:
11:22:19.640419 IP 192.168.100.2.mdns > 224.0.0.251.mdns: 0*- [0q] 7/0/0 PTR 00125F1799DF@AirMedia-16309f._raop._tcp.local., (Cache flush) TXT "txtvers=1" "cn=0,1,2,3" "da=true" "et=0,3,5" "ft=0x5A7FFFF7,0xE" "md=0,1,2" "sv=false" "sr=44100" "ss=16" "pw=1" "vn=65537" "tp=UDP" "vs=220.68" <span style="background-color:#FFEB3B;color:black">"am=AppleTV3,2"</span> "pk=7af87b1bda1782678d48ca3494defe037a7da5a2e358c74dda9f04706694e88d" "sf=0x44" "vv=2", (Cache flush) SRV Crestron.local.:49153 0 0, (Cache flush) A 192.168.100.2, PTR AirMedia-16309f._airplay._tcp.local., (Cache flush) TXT "deviceid=00:12:5f:16:30:9f" "srcvers=220.68" "features=0x5A7FFFF7,0xE" "pw=1" "flags=0x44" "model=AppleTV3,2" "pk=7af87b1bda1782678d48ca3494defe037a7da5a2e358c74dda9f04706694e88d" "vv=2", (Cache flush) SRV Crestron.local.:7000 0 0 (586)
Now that we have a pretty good understanding of what’s running on the device network-wise, it’s time to reverse engineer this unknown protocol we’ve come accross during our TCP scan. Time to perform some traffic analysis !
2. Trafic Analysis & Protocol Reverse Engineering
There are many ways to capture traffic for analysis while testing embedded devices. You could use a switch with a SPAN port, connect it directly like I did in the previous section, wait to get a shell and use tcpdump locally, …
We’ll use a more straightforward method that does not involve buying a switch or getting a shell: transparent bridges with brctl. The bash script below should help you get started with transparent bridges on Linux:
Connect the tested device on one interface and the second interface to your switch/router. Once your bridge interface is up you can start capturing traffic flowing through it with Wireshark.
2.1 Discovery Protocol
To discover Airmedia devices connected to the same subnet, client applications (Windows, iOS, Android) send a UDP packet on port 1047 to the broadcast address of their subnet with a payload set to WPPS
. Upon reception, the Airmedia device reply with a UDP packet to port 1047 on the client.
You can see the request/response in tcpdump output below (I removed IP layer from the output so it’s easier to understand). The response packet always contains AWPP, the device’s name, make, model, firmware version, and some fixed values. The firmware version is made of four hex bytes that needs to be interpreted as integer (i.e. firmware version is 2.4.1.13 here).
tcpdump: listening on eth14, link-type EN10MB (Ethernet), capture size 262144 bytes 192.168.100.1.1047 > 192.168.100.255.1047: [udp sum ok] UDP, length 4 0x0000: 4500 0020 0001 0000 4011 307b c0a8 6401 E.......@.0{..d. 0x0010: c0a8 64ff 0417 0417 000c 05b3 5750 5053 ..d.........WPPS 192.168.100.2.1047 > 192.168.100.1.1047: [udp sum ok] UDP, length 128 0x0000: 4500 009c 0000 4000 4011 f0cc c0a8 6432 E.....@.@.....d2 0x0010: c0a8 6401 0417 0417 0088 acf0 4157 5050 ..d.........AWPP 0x0020: c0a8 6402 01bb 7c79 0411 270c 4169 724d ..d...|y..'.AirM 0x0030: 6564 6961 2d31 3633 3039 6600 0000 0000 edia-16309f.....' 0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x0050: 0000 0000 4372 6573 7472 6f31 3030 3131 ....Crestro10011 0x0060: 3131 3031 3131 3030 3031 0221 0000 0000 1101110001.!.... 0x0070: 0000 0000 012d 0000 0000 0000 0000 5769 .....-........Wi 0x0080: 5047 314b 3573 0000 0000 0000 0000 0204 PG1K5s.......... 0x0090: 0113 0405 3000 0000 0000 0000 ....0.......
We can emulate that exchange using a Lua script and Nmap. The script simply sends discovery packets and monitor the interface for replies. If valid replies are observed, device information is displayed.
broadcast-awind-discover.nse
Running the script will give you this:
# nmap --script broadcast-awind-discover -e eth0
Starting Nmap 7.70SVN ( https://nmap.org ) at 2018-09-29 21:49 CEST
Pre-scan script results:
| broadcast-awind-discover:
| 192.168.100.2:
| Hostname: AirMedia-16309f
| Make: Crestro100111101110001\x02a
| Model: WiPG1K5s
|_ Version: 2.6.0.6
WARNING: No targets were specified, so 0 hosts scanned.
Nmap done: 0 IP addresses (0 hosts up) scanned in 1.63 seconds
Of course, we can also simulate an Airmedia device by replying to discovery requests sent by legitimate clients:
This could be used by an attacker to force a client to execute the association with its own machine instead of the legitimate Airmedia device and therefore steal the PIN code 😈.
2.2 Association/Authentication Protocol
The association between a client application and the Airmedia device is performed over TCP/389 with what seems to be a proprietary protocol.
Discovery
The first step is a “ping pong” request to verify availability of the remote device. Client sends wppaliveROCK
to which the server replies wppaliveROLL
. It’s super easy to check with netcat:
$ echo "wppaliveROCK" | nc 192.168.100.2 389
wppaliveROLL
This behavior can be exploited with Nmap to reliably fingerprint that service. The rule below can be appended to nmap-service-probes file. It defines a TCP probe that will send wppaliveROCK
to the target port. If Nmap receives a response from the service that match wppaliveROLL
, this means we successfully identified an Awind association port.
Probe TCP awindAssociat q|wppaliveROCK\n|
# rarity 8
ports 389,3268
match awind-associate m|^wppaliveROLL$|s p/Awind scdecapp association/ d/specialized/ cpe:/h:awind/
Association (0x90)
The second step is similar to what we observed during the broadcast discovery of devices but instead of a broadcast UDP packet, a TCP packet is sent to port 389. That packet holds the command wppcmd
followed by two null bytes and an opcode: 0x90
.
The device reply to this by sending back the information we already analyzed in the broadcast discovery section: hostname, model, make, and firmware version. That exchange can be triggered with a bit of Python and netcat:
$ python -c 'print "wppcmd\x00\x00\x90"' | nc 192.168.100.2 389 | hexdump -C 00000000 77 70 70 63 6d 64 00 00 91 41 57 50 50 c0 a8 01 |wppcmd...AWPP...| 00000010 13 01 bb 7c 79 04 11 27 0c 41 69 72 4d 65 64 69 |...|y..'.AirMedi| 00000020 61 2d 31 36 33 30 39 66 00 00 00 00 00 00 00 00 |a-16309f........| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000040 00 43 72 65 73 74 72 6f 31 30 30 31 31 31 31 30 |.Crestro10011110| 00000050 31 31 31 30 30 30 31 02 61 00 00 00 00 00 00 00 |1110001.a.......| 00000060 00 01 2d 00 00 00 00 00 00 00 00 57 69 50 47 31 |..-........WiPG1| 00000070 4b 35 73 00 00 00 00 00 00 00 00 02 06 00 06 04 |K5s.............| 00000080 05 30 00 00 00 00 00 00 00 |.0.......| 00000089
We see that the device answers with wppcmd
followed by two null bytes and the response opcode: 0x91
.
Authentication (0x92, 0x93)
The third step is PIN-based authentication. The opcode for authentication is 0x92
and the packet contains the PIN code. In the example below we see a client attempting to login with the wrong PIN (1234), to which the device reply with an authentication response opcode (0x93
) and a value set to 0x00
meaning authentication failed.
00000015 77 70 70 63 6d 64 00 00 92 47 47 47 47 47 47 47 wppcmd.. .GGGGGGG 00000025 47 27 73 20 69 50 61 64 00 00 00 00 00 00 00 00 G's iPad ........ 00000035 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 00000045 00 00 00 00 00 00 00 00 00 c0 a8 0c e4 31 32 33 ........ .....123 00000055 34 00 00 00 00 1e 0a 0a 00 01 00 00 02 4a 6e 4d 4....... .....JnM 00000065 4f 50 53 44 4b 00 00 00 00 00 00 00 00 00 00 00 OPSDK... ........ 00000075 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 00000085 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 00000095 00 00 00 00 00 00 00 00 00 ........ . 00000095 77 70 70 63 6d 64 00 00 93 00 wppcmd.. ..
In the example below we see a client attempting to login with the right PIN 4160, to which the device reply with an authentication response opcode (0x93
) and a value set to 0x01
meaning authentication successful.
00000015 77 70 70 63 6d 64 00 00 92 47 47 47 47 47 47 47 wppcmd.. .GGGGGGG 00000025 47 27 73 20 69 50 61 64 00 00 00 00 00 00 00 00 G's iPad ........ 00000035 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 00000045 00 00 00 00 00 00 00 00 00 c0 a8 0c e4 34 31 36 ........ .....416 00000055 30 00 00 00 00 1e 0a 0a 00 01 00 00 02 4a 6e 4d 0....... .....JnM 00000065 4f 50 53 44 4b 00 00 00 00 00 00 00 00 00 00 00 OPSDK... ........ 00000075 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 00000085 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 00000095 00 00 00 00 00 00 00 00 00 ........ . 00000095 77 70 70 63 6d 64 00 00 93 01 wppcmd.. ..
Now that we know how the association protocol works, let’s write a PIN bruteforcer !
bruteforce_pin.py
This is what the script running looks like. It’s a little bit slow, but it’s a single threaded proof-of-concept :)
2.3 Streaming Protocol
Streaming is performed over TCP port 31865 by default, but it seems that it can also be performed over other ports such as TCP/515 and TCP/8080. All those ports reply to NULL probes with wppib
, this can be observed with netcat:
$ echo "" | nc 192.168.100.2 31865 | hexdump -C
00000000 77 70 70 69 62 00 00 10 00 00 00 00 |wppib.......|
0000000c
This means that the service can be reliably fingerprinted with Nmap in the same way that we did for the association protocol. This time we edit nmap-service-probes in the section following the NULL probe definition. A NULL probe is simply Nmap connecting to the service and sending an empty payload.
If the response returned by the server matches “wppib”, we know it’s an Awind streaming receiver:
match awind-wppib m|^wppib\0\0\x10\0\0\0\0$| p/Awind scdecapp stream/ d/specialized/ cpe:/h:awind/
To see how streaming is performed, I captured multiple streams performed from an Android phone. This is what the exchange looks like:
First, the server answers the client with this wppib:
77 70 70 69 62 00 00 10 00 00 00 00 wppib.......
Then the client sends this packet:
53 65 6e 64 65 72 49 64 02 00 00 00 00 00 SenderId......
00 00 00 00 00 00 00 00 00 00 00 00 00 00 ..............
00 00 00 00 ....
Immediately followed by this:
41 57 49 4e 44 49 42 20 04 00 00 00 00 00 00 00 AWINDIB ........
00 00 04 ff 02 cf 00 38 40 00 00 38 47 ce 00 38 .......8@..8G..8
40 00 00 01 55 de 56 53 4d 4b 01 32 41 9a ff d8 @...U.VSMK.2A...
ff e0 00 10 4a 46 49 46 00 01 01 00 00 01 00 01 ....JFIF........
--snip--
00 01 80 00 01 00 01 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 49 42 54 41 49 4c ..IBTAIL
As I didn’t want to stare for hours at hexadecimal, I simply dumped all TCP payload from an identified TCP stream with tshark:
$ for l in `tshark -r "traffic_capture_201706161332.pcapng" -Y usb -z follow,tcp,raw,9`; do echo $l | xxd -r -p >> /tmp/android.bin; done
I ran binwalk on the extracted binary payloads to identify the kind of data being transmitted:
$ binwalk android.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
104 0x68 JPEG image data, JFIF standard 1.01
Easy win, it seems they simply sends JPEG files in cleartext. Let’s use dd to carve out the JPEG file:
$ dd if=android.bin of=9.jpg skip=104 bs=1
87560+0 records in
87560+0 records out
87560 bytes (88 kB, 86 KiB) copied, 0,0983665 s, 890 kB/s
Opening the file confirmed my assumption as I was looking at my Android device screen.
Wrap-up
If we combine the broadcast discovery script with our custom fingerprinting rules and the association script, we can identify all devices running in the same subnet than us and reliably identify proprietary services running on them.
A quick demo with Nmap below:
3. Conclusion
Over the course of this post we learned how to identify network ports exposed by a target device. We then successfully reverse engineered proprietary protocols implemented by Airmedia AM-101 by capturing traffic between legitimate clients and our target device.
We identified the following issues affecting the device:
- arbitrary streaming of content (via Airplay or by bruteforcing the PIN protection)
- weak default credentials (SNMP community, admin and moderator passwords on web interface)
- plaintext transmission of PIN code and streamed content
Some recommendations if you deploy this kind of device in your network:
- disable Airplay service
- disable SNMP service or move to SNMP version 3 and set strong credentials
- set strong credentials for admin and moderator users
- disable auto-discovery in software clients
- disable remote view if not required
- update to the latest firmware version of your device
- put them in a dedicated audio/video VLAN with proper firewalling and segregation rules
Hope you learned something along the way :) Nmap service probe rules, Nmap scripts, and custom Python scripts are now available on my Github at https://github.com/qkaiser/awind-research/.
The next step will be even more fun as we’ll dig into vulnerability research and development! Keep an eye on this blog, I’ll release it on March 27th.
You can find it at Man-in-the-conference-room - Part IV (Vulnerability Research & Development).