Recently, the researchers found a GRUB2 vulnerability, version 1.98 (released in 2009) to 2.02 (released in 2015) are affected. This vulnerability allows local users to bypass any form of authentication (plaintext password or hashed password), allowing an attacker to gain control of the computer. Most Linux systems use GRUB2 as the boot loader, including some embedded systems. As a result, there will be countless devices that are at risk from this vulnerability.
As shown in the following figure, we successfully exploited this vulnerability under Debian 7.5 QEMU to obtain the grub rescue shell.
Quickly determine if your system has the vulnerability.
To quickly determine if your system has this vulnerability, you just have to press 28 times BACKSPACE (BACKSPACE) When you enter the user name interface in grub, and if the system restarts or returns to the rescue shell, your grub will be affected by this vulnerability.
Effect
An attacker who successfully exploited this vulnerability could get Grub Rescue Shell,grub Rescue is a very high-privileged shell that can do the following:
1. Elevation of privilege: An attacker can obtain all the privileges of the Grub console without a valid user name password.
2. Information disclosure: Attackers can get a more convenient environment by loading a custom kernel and INITRAMFS (such as from USB), copying data that steals an entire hard drive, or installing a rootkit into the system.
3. Denial of service: The attacker is able to destroy any data including grub itself, even if the hard disk is encrypted data can be overwritten, resulting in DOS (the system cannot be used).
Details
This vulnerability exists in Grub code from version 1.98 (2009), B391BDB2F2C5CCF29DA66CECDBFB7566656A704D is the commit number of the vulnerability, and the problem exists in the Grub_password_get () function.
There are two of these functions that have integer underflow problems grub_username_get () and Grub_password_get (). They exist in the grub-core/normal/auth.c and lib/crypto.c files, respectively. These two functions, in addition to the call to printf (), are the same in other places, as shown in Grub_username_get () below. The POC in this article gets the grub rescue shell by exploiting the vulnerability in grub_username_get ().
The following is the vulnerability detail code in the Grub_username_get () function:
Grub_username_get () function code
This problem is due to the fact that the self-decrement variable Cur_len does not do range checking.
Utilization (POC)
In this code, there are two questions about the use of the Cur_len variable off-by-two and out of bounds overwrite (two comments later in the code). The previous error annotation point, in the buffer used to store the user name, will have a length of two bytes, but there is no way to use, the overwritten memory is used for padding.
The next error comment point (//out of bounds overwrite here) is interesting because this code allows us to overwrite the memory used to store the user name cache with 0x00. This is because the Grub_memset () function attempts to set the unused byte of the user name buffer to 0x00. To achieve this, the code calculates the first unused byte address and the size of the buffer that needs to be populated with 0x00. The results of these two calculations are passed as parameters to the Grub_memset () function:
Grub_memset (buf + cur_len, 0, Buf_size-cur_len);
For example, when you enter "root" in the user name, the value of Cur_len is the 5,grub_memset () function to empty the 5th to 1024th byte of the user name buffer (the buffer length of the user name and password is 1024 bytes) (set to 0x00). The robustness of writing code is good. For example, if you enter a user name that is stored in a clean 1024-byte array, you can compare the entire 1024-byte memory directly to a valid user name instead of comparing two strings. This protects against some short side-channels attacks, such as timing attacks (such as the first input username for aaaaaaaa and then the BBBB, so that the encoding avoids the second result as bbbb[0x00 ]aaa of the case).
The simplest and fastest way to verify this memory overlay is to keep pressing BACKSPACE (BACKSPACE) to let the Cur_len variable overflow to a very large value, which is immediately used to calculate the starting address of the space to be emptied.
memset Destination Address = buf + Cur_len
Through this point, the second buffer overflow is triggered because the value of the user name buffer address exceeds the range that the 32-bit variable can store. Therefore, we will calculate the destination address that the Grub_memset () function will use by carefully structuring the first underflow and the second buffer overflow:
cur_len--; Integer underflow
grub_memset (buf + cur_len, 0, Buf_size-cur_len);//Integer Overflow
The following example can help you understand how we are exploiting this vulnerability. Assuming the user name buffer has a starting address of 0x7f674, and then the attacker presses the backspace bar (the underflow value is 0xFFFFFFFF), then memset is the following:
Grub_memset (0x7f673, 0, 1025);
The first parameter: (buf+cur_len) = (0X7F674+0XFFFFFFFF) = (0x7f674-1) = 0x7f673; The second parameter: a constant to overwrite the memory, here is 0; The third parameter is the size to overwrite: (buf_size-cur_ Len) = (1024-(-1)) = 1025. As a result, the buffer space for the entire user name (1024) plus the previous byte are all overwritten by 0x00.
The number of times the backspace is pressed is the amount of 0x00 that was populated before the user name buffer.
Now, we have been able to overwrite any number of bytes in the user name buffer. Next you need to find the memory address that 0x00 overwrites and can be used to implement malicious code execution. Looking in the stack frame, you can quickly see that the return address of the Grub_memset () function can be overwritten. The following diagram clearly shows the memory layout of the stack:
GRUB2: Redirect Control flow
As shown in the figure, the distance between the return address of the Grub_memset () function and the user name buffer is 16 bytes. In other words, if we press the BACKSPACE key 17 times, we will be able to overwrite the highest byte of the return address. Therefore, the function return address 0x07eb53e8 will be replaced and will eventually jump to 0x00eb53e8. When the Grub_memset () execution finishes, the control flow is redirected to 0x00eb53e8, causing the system to restart. Similarly, pressing the BACKSPACE bar 18,19,20 times will cause the system to restart.
Here we are able to redirect the control flow.
We skipped the code analysis on the 0x00eb53e8,0x000053e8, 0x000000e8 addresses, because jumping to these addresses only causes the system to restart and there is no way to control the execution flow.
Although it is difficult to jump to 0x0 for a successful construction attack, we will show how we do it in the end.
The system can continue to survive after jumping to 0x0.
The 0x0 address is the portal of the processor's IVT (interrupt vector table). This contains a large number of pointers to the segment offset table.
IVT interrupts the code at the lowest address
In the early stages of startup, neither the processor nor the execution framework has all the functionality. The following are some of the key factors that can be successfully exploited, which may vary from one system to another:
1. The processor is in "protected mode", and the GRUB2 will turn on this mode at the very beginning
2. Virtual memory is not enabled
3. No memory protection, memory is readable/writable/executable, and there is no NX/DEP protection
4. The processor executes a 32-bit instruction set, Even under 64-bit Architecture
5. The processor automatically handles dynamically self-modifying code: If the write affects a prefetch instruction, the command queue will be invalid.
6. No stack protection mechanism (SSP)
7. No open address space layout randomization (ASLR)
Therefore, jumping to the 0x0 address does not cause the system to crash itself, but we need to control the execution flow so that the code goes to the target function Grub_rescue_run () that contains the GRUB2 Rescue Shell function.
To jump to 0x0, you need to control which things.
When the user presses [Enter] or [ESC], the main loop of the Grub_username_get () function ends. At this point, the value of the last key is saved in the%EBX register (the ASCII code of Enter is 0x8 for ASCII 0xd,esc). The%esi register will hold the value of the Cur_len variable.
As shown in the figure above, the instruction pointer (EIP) points to the 0x0 address, the value of the%esi register is-28 (using the program to double-press the BACKSPACE bar 28 times), and then press [Enter] (%ebx=0xd).
IVT Reverse
If the state of the processor is as described above (which automatically handles dynamically self-modifying code), the code in IVT has the same functionality as memcpy (), which copies the code from the address pointed to by the%esi Register to 0x0 (IVT itself). So IVT is a self-modifying code, and we can select the chunks of code we want to copy.
The following steps show the actual order in which the code is executed, at which point the value of the%esi register is -28 (0XFFFFFFE4):
In the third loop, the RETW instruction is inserted toward 0x0007, at which point the%esp points to the address 0xe00c (the top of the stack return address).
Therefore, when the RETW instruction executes, the execution stream jumps to 0xe00c. This address belongs to the Grub_rescue_run () function:
Using this step, GRUB2 into the Grub rescue function, we get a high-privileged shell.
Fortunately, the memory was slightly modified, but it was able to use all of the grub's features. The IVT interrupt vector has been modified, but the IVT will no longer be used because the processor is now in protected mode.
One Step Closer
Although we went into the GRUB2 rescue function, but did not really pass the certification. If you want to enter normal mode (this mode provides the Grub menu and the full editing function), Grub asks you to enter the correct username password. We can enter the GRUB2 command directly, or even introduce a new module to add a new grub feature, and eventually launch the full bash shell to get a more convenient environment by deploying malware into the system. To run the Linux bash, we can use GRUB2 commands such as Linux, INITRD or Insmod.
While it is perfectly possible to run the Linux kernel using the GRUB2 command to deploy malware, we have found a simpler solution to write code patches in GRUB2 's RAM to bypass authentication and then back to "normal" mode. The idea of this method is to modify the user authentication check condition, and its related code in the is_authenticated () function in the grub-core/normal/auth.c file.
This modification was done using the GRUB2 Rescue command Write_word. In this way, all the conditions that return the "normal" mode of GRUB2 have been reached. In other words, we do not need a username and password to enter the GRUB2 "edit mode".
Apt attacks how to use this 0day.
Physical contact is an "advanced" feature of APT attacks. One of the main goals of apt attacks is to steal sensitive information. The following is a very simple example of how an apt affects the system and continuously acquires user data. The following is an overview of the target system configuration:
1.bios/uefi is protected with a password;
2.GRUB2 edit mode is protected with a password;
3. Extended boot mode is disabled: Cdrom,dvd,usb,netboot,pxe ...
4. User data is encrypted.
As mentioned earlier, our goal is to steal user data. Since the data is encrypted, our strategy is to infect the system and then wait for the user to decrypt the data (by logging into the system), and then we get the plaintext information directly.
Preparing your environment for deploying malware
Through our analysis and demonstration of the exploits of GRUB2, we can easily modify the Linux portal to load a Linux kernel to get the root-privileged shell. This is a very old but still effective deception method, only need to add Init=/bin/bash to the Linux entrance, we can get root access to the Linux shell, this environment allows us to deploy malicious software more convenient.
Since/bin/bash is the first process to start, syslog monitoring is not running and logs are not logged. As a result, this intrusion will not be detected by common Linux monitoring.
Deploy malware for continuous control
To show how many things can be done by exploiting this Grub2 0day vulnerability, we have developed a simple POC. This POC can tamper with Firefox's link library, be able to create a new thread and launch a bounce shell connected to the control server on port 53rd. This is, of course, a simple example where malware gets a lot of privacy in real-world scenarios.
Upload the modified link library to VirusTotal detection, none of the 55 antivirus engines can detect this as a malicious software. Firefox is a web browser that sends requests to HTTP and DNS ports, so the use of these ports by our modified link libraries does not seem to be a suspicious behavior.
In order to infect the system we put the modified libplc4.so into USB and then replace the source file. We must attach the USB device to the system and give writable permissions, as shown in the following figure:
Infection System
When any user runs Firefox, a bounce shell initiates a connection. At this point, all the user data has been decrypted, which allows us to steal all of the user's information. The following figure shows that user Bob (the attacked user) is using Firefox to browse the Web, while Alice (the attacker) has fully obtained Bob's data.
For a more sustainable control system, you can modify a simple kernel into an unencrypted/boot partition, Laiti deploy a more persistent malware, so we can do whatever we want.
Remediation Scenarios
This vulnerability is easy to fix, as long as it prevents cur_len overflow. Mainstream vendors are now aware of the vulnerability, so we've also written an "emergency patch" in GRUB2 git: