0-Day Vulnerability In "Linux Grub 2" Allow Attacker To "Bypass Authentication"

A vulnerability in Grub2 has been found. The vulnerability exploited under certain circumstances, allowing local attackers to bypass any kind of authentication .so, the attacker may take control of the computer.

Grub2 is the bootloader used by most Linux systems including some embedded systems. This results in an incalculable number of affected devices.Vulnerability in grub2 allow the attacker Elevation of privilege,Information disclosure and Denial of service attack.


How To Check if your system is vulnerable ?


To quickly check if your system is vulnerable, when the Grub ask you the username, press the Backspace 28 times. If your machine reboots or you get a rescue shell then your Grub is affected.

Researchers Found The Vulnerability in the grub2 commit which introduced the fault wasb391bdb2f2c5ccf29da66cecdbfb7566656a704d,affecting the grub_password_get() function.

There are two functions which suffer the same integer underflow fault. The grub_username_get() and grub_password_get() located in grub-core/normal/auth.c and lib/crypto.c respectively. Both functions are equal except for a call to printf() in the grub_username_get(). The PoC described here is based only on exploiting the grub_username_get() to obtain a Grub rescue shell.

Below is the vulnerable grub_username_get() function:
static int
grub_username_get (char buf[], unsigned buf_size)
{

unsigned
cur_len = 0;
int
key;

while
(1)
{

key = grub_getkey ();
if
(key == '\n' || key == '\r')
break
;

if
(key == '\e')
{

cur_len = 0;
break
;
}


if (key == '\b') // Does not checks underflows !!
{
cur_len--; // Integer underflow !!
grub_printf ("\b");
continue
;
}


if
(!grub_isprint (key))
continue
;

if
(cur_len + 2 < buf_size)
{

buf[cur_len++] = key; // Off-by-two !!
grub_printf ("%c", key);
}
}


grub_memset( buf + cur_len, 0, buf_size - cur_len); // Out of bounds overwrite

grub_xputs ("\n");
grub_refresh ();

return
(key != '\e');
}

Function grub_username_get() at grub-core/normal/auth.c

The fault is caused by decrementing the cur_len variable without checking the range. 

Proof-Of-Concept


Exploiting the integer underflow can be used to cause an Off-by-two or an Out of bounds overwrite memory errors. The former error, overwrites up to two bytes right under the username buffer (local variable called login at function grub_auth_check_authentication()), but this area does not contain any usable information to build an attack; actually, it is padding.

The latter error, the Out of bounds overwrite, is more interesting because it allows to overwrite with zeros the zone below to the username buffer. This is because the grub_memset() function tries to set to zero all unused bytes of the username buffer. To do that, the code calculates the address of the first unused byte and how many bytes have to be zeroed in the buffer. The results of these calculations are passed as arguments to thegrub_memset() function:

    grub_memset (buf + cur_len, 0, buf_size - cur_len);

For example, typing "root" as usermane, cur_len is 5, and the grub_memset() function will clear (set to zero) bytes form 5 to 1024-5 (the username and password buffers are 1024 bytes) of the username buffer. This way of programming is quite robust. For example, if the typed username is stored in a clean 1024-byte array, then we can compare the whole 1024-bytes with the valid username, rather than comparing both strings. This protects against some short of side-channels attacks, like timing attacks.
To abuse the out of bound overwrite, the attacker can press the backspace key to underflow the cur_len variable, producing a very high value. This value is later used to calculate the starting address to clear.
    memset destination address = buf + cur_len
At this point, a second overflow occurs because the addition of this big value with the base address where the username buffer resides can not be hold in a 32-bit variable. Hence, we need to manage the first underflow and this second overflow to calculate the destination address where the grub_memset() function will start to set to zeros the buffer:
    cur_len--; // Integer Underflow
    grub_memset (buf + cur_len, 0, buf_size - cur_len); // Integer Overflow

The following example helps to understand how we can exploit this. Assuming that the username buffer resides in the address 0x7f674 and the attacker press the backspace key only once (producing an underflow to 0xFFFFFFFF) the resulting memset will be:
    grub_memset (0x7f673, 0, 1025);
The first argument is: (buf+cur_len) = (0x7f674+0xFFFFFFFF) = (0x7f674-1) = 0x7f673, the second argument: is the constant value used to overwrite the area, in this case 0, and the third argument is the number of bytes to overwrite: (buf_size-cur_len) = (1024-(-1)) = 1025. Therefore, the whole username buffer (1024) plus the very first byte under the buffer will be set to zero.
Therefore, the number backspace keystrokes (without introducing any username), is the number of bytes below the username that are zeroed.
Now, we are able to overwrite an arbitrary number of bytes below the username, we need to find out an interesting memory address that we can overwrite with zeros. A quick look to the current stack frame reveals that we were able to overwrite the return address of the grub_memset() function. The following picture sketches the stack memory layout:


Grub2: Redirecting the control flow.

As shown in the above picture, the return address of the grub_memset() function is at a distance of 16-bytes from the username buffer. In other words, if we press the backspace key 17 times, we will overwrite the highest byte of the return address. So, instead of returning to the caller function at 0x07eb53e8 we will jump to 0x00eb53e8. When the grub_memset() ends, control flow is redirected to the the 0x00eb53e8 address causing a reboot. The same occurs if we press the backspace key 18, 19 or 20 times, in all cases the system reboots.

How to Patch the vulnerability?


The bug can be easily fixed just by preventing that cur_len overflows. The main vendors are already aware of this vulnerability. By the way, we have created the following "emergency patch" from the main GRUB2 git repository:

From 88c9657960a6c5d3673a25c266781e876c181add Mon Sep 17 00:00:00 2001
From: Hector Marco-Gisbert <hecmargi@upv.es>
Date: Fri, 13 Nov 2015 16:21:09 +0100
Subject: [PATCH] Fix security issue when reading username and password

This patch fixes two integer underflows at:
* grub-core/lib/crypto.c
* grub-core/normal/auth.c

Signed-off-by: Hector Marco-Gisbert <hecmargi@upv.es>
Signed-off-by: Ismael Ripoll-Ripoll <iripoll@disca.upv.es>
---
grub-core/lib/crypto.c | 2 +-
grub-core/normal/auth.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c
index 010e550..524a3d8 100644
--- a/grub-core/lib/crypto.c
+++ b/grub-core/lib/crypto.c
@@ -468,7 +468,7 @@ grub_password_get (char buf[], unsigned buf_size)
break;
}

- if (key == '\b')
+ if (key == '\b' && cur_len)
{
cur_len--;
continue;
diff --git a/grub-core/normal/auth.c b/grub-core/normal/auth.c
index c6bd96e..5782ec5 100644
--- a/grub-core/normal/auth.c
+++ b/grub-core/normal/auth.c
@@ -172,7 +172,7 @@ grub_username_get (char buf[], unsigned buf_size)
break;
}

- if (key == '\b')
+ if (key == '\b' && cur_len)
{
cur_len--;
grub_printf ("\b");
--
1.9.1

Follow the link to patch the vulnerability[ 0001-Fix-CVE-2015-8370-Grub2-user-pass-vulnerability.patch ]

Patching GRUB 2.02:

$ git clone git://git.savannah.gnu.org/grub.git grub.git
$ cd grub.git
$ wget http://hmarco.org/bugs/patches/0001-Fix-CVE-2015-8370-Grub2-user-pass-vulnerability.patch
$ git apply 0001-Fix-CVE-2015-8370-Grub2-user-pass-vulnerability.patch