By:Simone Margaritelli
c
Follow Simone Margaritelli (@evilsocket) Zimperium zLabs
Follow Zimperium zLabs (@zLabsProeject)
During the last weeks we’ve been investigating multiple aspects of GSM security such as protocol vulnerabilities as well as source auditing the world’s most common open source software products that run GSM networks. In this post we’ll share the details about multiple vulnerabilities in such software which allow an attacker to compromise a BTS station, crash it, or takeover its transceiver module remotely.
A BTS (base transceiver station) is composed of software and radio equipment that allows mobile stations (cellular phones) to connect to the GSM, UMTS, and LTE networks. They are the equivalent of wireless access points for Wi-Fi networks and handle the “Um”[1] layer/interface as shown in Figure 1.
Figure 1: A mobile station connecting to a BTS ( GNURadio OpenBTS documentation ).
The lower level software behind any BTS is the transceiver, which is the direct interface to the radio hardware. It is responsible for frequency tuning and handling GMSK (Gaussian Minimum Shift Keying) data modulation/demodulation. In short, it digitizes the radio waves. All the communication and synchronization with the rest of the logical units of the BTS is handled through three UDP sockets as shown in Figure 2.
Figure 2: The transceiver module and the three UDP sockets used to communicate with the rest of the BTS.
The clock socket is used for timing synchronization. The command socket is used by the BTS to send commands to the transceiver module. Finally, the data socket is used to transmit GSM “bursts” (data packets) from the BTS to the radio and receive responses back.
The UDPSocket
class is used by the transceiver to handle all the three channels in Figure 2.
Our research shows that all of the most commonly available BTS software shares the same (or a very similar) transceiver code base. Thus, all of them are affected by the same vulnerabilities. The following reports are valid for all products listed below.
Such vulnerabilities would allow a malicious party to remotely control the transceiver module, thus compromising the BTS functionalities, impersonating a parallel BTS communicating with it.
Moreover, it is possible for the attacker to send GSM data bursts to the transceiver itself and perform a wide range of attacks such as IMSI detaching, encryption downgrading, denial of service, etc against mobile subscribers.
In order to be accepted by the transceiver module, UDP packets sent to the data channel socket must respect the following format:
Once the transceiver receives these packets it will decode and modulate them using GMSK (Gaussian Minimum Shift Keying). Eventually, the bursts will be transmitted to the connected mobile stations accordingly to their contents.
Even if the following products are GSM and UMTS only, the transceiver (which is a standalone component) itself is universal. Chances are that other (proprietary) BTS software uses the very same code base in order to serve LTE connectivity as well.
There’s a bug in the network library of the aforementioned products which makes the transceiver UDP sockets bind to INADDR_ANY
instead of the user configured value (127.0.0.1
by default). This allows any attacker with IP connectivity to the BTS system to receive and send packets from/to the transceiver. Moreover, access to the services exposed on these UDP network sockets is not protected by any authentication mechanism whatsoever.
Figure 3: The three transceiver sockets bound to 0.0.0.0
An attacker with IP connectivity could send UDP traffic to exercise any functionality provided. This could allow remote control takeover, GSM traffic hijacking, various information disclosure, DoS, or worse.
The root cause of this vulnerability (and the reason why the following vulnerabilities can be reached remotely) can be found in the UDPSocket
constructor and the UDPSocket::open
method in the source file CommonLibs/Sockets.cpp
. This source file is present in all affected products. The following excerpt shows the vulnerable code.
256 UDPSocket::UDPSocket(unsigned short wSrcPort, 257 const char * wDestIP, unsigned short wDestPort ) 258 :DatagramSocket() 259 { 260 open(wSrcPort); 261 destination(wDestPort, wDestIP); 262 } ... 266 void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP ) 267 { 268 resolveAddress((sockaddr_in*)mDestination, wDestIP, wDestPort ); 269 }
In the above snippet we can see that the desired bind address is saved into the mDestination
class member variable, but here’s how the UDPSocket::open
method is implemented:
271 address.sin_family = AF_INET; 272 address.sin_addr.s_addr = INADDR_ANY; 273 address.sin_port = htons(localPort); 274 if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
Despite the fact that the UDPSocket
class provides a constructor argument to specify the address to which to bind the server, this information is ignored. As show on line 272
, the socket is bound to INADDR_ANY
instead of using the mDestination
address variable.
An attacker can overflow a stack buffer by sending an oversized UDP packet to the control channel.
An attacker may be able to achieve remote code execution (RCE) or cause a denial of service (DoS) condition.
The control channel is handled by the Transceiver::driveControl
method in the Transceiver.cpp
source file. The first lines follow.
694 void Transceiver::driveControl(size_t chan) 695 { 696 int MAX_PACKET_LENGTH = 100; 697 698 // check control socket 699 char buffer[MAX_PACKET_LENGTH]; 700 int msgLen = -1; 701 buffer[0] = ''; 702 703 msgLen = mCtrlSockets[chan]->read(buffer);
Note that the packet buffer, which resides on the method’s stack, is defined to be 100 bytes (from MAX_PACKET_LENGTH
).
If we analyze the DatagramSocket::read
method (the DatagramSocket
class is the parent class of UDPSocket
) declared in the Sockets.cpp
source file, we see the following.
194 int DatagramSocket::read(char* buffer) 195 { 196 socklen_t temp_len = sizeof(mSource); 197 int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0,
Here we see that MAX_UDP_LENGTH
bytes are read instead of MAX_PACKET_LENGTH
. This value is defined in the Sockets.h
file as follows.
#define MAX_UDP_LENGTH 1500 /* (or 8000 for OpenBTS-UMTS) */
Therefore, it’s possible to cause a stack overflow in the transceiver process simply by sending a UDP packet larger than 100 bytes. Figure 4 shows the result of a debugging session when this occurs.
Figure 4: Segmentation fault caused by a large UDP packet.
The control channel does not implement any type of authentication. Since it is exposed to the outer network due to Issue 1, this fact can be used by any malicious party to control the transceiver module remotely.
An attacker could…
SETBSIC
command to change the BTS identity to another one.The control channel implements a simple text over UDP protocol handled by the Transceiver::driveControl
method in the Transceiver.cpp
source file. Some of the features this protocol exposes include (remember, there is no authentication):
CMD POWERON / CMD POWEROFF
CMD RXTUNE frequency / CMD TXTUNE frequency
CMD SETBSIC value
An attacker can execute such commands (and others) by simply sending UDP packets to port 5701
of the server. The full protocol specification can be found inside the TRXManager/README.TRXManager
file.
We’ve demonstrated how the complete lack of any form of authentication and code bugs make the aforementioned BTS products vulnerable to a wide range of attack.
We highly recommend vendors to apply the following mitigations in order to make their products safer:
For security and mobility professionals concerned about implications of similar attacks to corporate or BYOD devices, consider leveraging a Mobile Threat Prevention solution such as Zimperium zIPS to detect active manipulation by an unauthorized third party.