Shellshock analysis CVE-2014-6271
Some time ago, the shell-breaking vulnerabilities made various companies very busy. The vulnerabilities have been around for a while, and the analysis of the Internet has also been transferred. When they stop, it's time for me to collect data to digest the vulnerability.
Vulnerability Overview
GNU Bash 4.3 and earlier versions have security vulnerabilities when evaluating some constructed environment variables. Adding additional strings to the function definitions in the environment variable values triggers this vulnerability, attackers can exploit this vulnerability to change or bypass environmental restrictions to execute Shell commands. Some services and applications allow unauthenticated remote attackers to provide environment variables to exploit this vulnerability. This vulnerability is caused by the creation of environment variables with constructed values before Bash Shell is called. These variables can contain code and will be executed immediately after the Shell is called. The severity of the Shell Cracking vulnerability is defined as 10 (highest). In April this year, the OpenSSL heartbleed vulnerability was only 5!
Why is this vulnerability so popular?
1. The vulnerability has a wide range of impact and has been present for a long time.
Bash, a Unix shell. The first official version was released in 1989. It was originally intended to be used on the GNU operating system, but can run on most Unix-like operating systems, both Linux and Mac OS X v10.4 use it as the default shell. It is also ported to Cygwin and MinGW on Microsoft Windows, or a DJGPP project that can be used on a MS-DOS. It is also transplanted on Novell NetWare and Android.
Therefore, this vulnerability exists in mainstream Linux and MacOSX operating systems. This vulnerability affects various applications that interact with bash, such as HTTP, FTP, and DHCP.
The problematic bash code has existed for more than 20 years.
Vulnerability Principle
To understand this vulnerability, you must first know what the bash environment variable is. The following is a reference to the article about the left ear mouse.
Everyone knows about environment variables. I don't need to popularize this. Environment variables are the variables in the shell Running in the operating system. Many programs change their execution behavior through environment variables. In bash, the syntax for defining an environment variable is very simple (Note: there cannot be spaces before and after the = sign ):
Then you can use this variable, such as echo $ var or something. However, we need to know that this variable is only a "local variable" of the Current shell and can only be accessed in the current shell process. The process fork of this shell process cannot be accessed.
You can perform the following tests:
1 2 3 4 5 |
$ var="hello coolshell" $echo$var hello coolshell $bash $echo$var |
In the above test, the third command executes a bash, that is, a bash sub-process, and you will find that var cannot be accessed.
In order for the shell Sub-process to be accessible, We need to execute the following export:
1 |
$exportvar="hello coolshell" |
In this way, the environment variable will be visible in its child processes.
You can useEnvCommand. However, the env command can also be used to define the environment variables of the export. As follows:
With these basic knowledge, we still need to know a basic knowledge-shell function.
Bash Functions
Defining a function in bash is simple, as shown below:
1 2 3 |
$ foo(){echo"hello coolshell"; } $ foo hello coolshell |
With the above basic environment variable knowledge, you will certainly want to try whether this function can be called in the sub-process, of course, the answer is not.
1 2 3 4 5 6 |
$ foo(){echo"hello coolshell"; } $ foo hello coolshell $bash $ foo bash: foo:commandnot found |
As you can see, it is the same as the environment variable. If you want to access it in a sub-process, it is still the same. export and export must have a parameter-f, it indicates an export function. For example:
1 2 3 4 5 6 7 |
$ foo(){echo"hello coolshell"; } $ foo hello coolshell $export-f foo $bash $ foo hello coolshell |
Bash vulnerability test code
Run the following code in Bash Shell:
Env x = '() {:;}; echo vulnerable' bash-c "echo this is a test"
If the output is:
Vulnerable
This is a test
Indicates a vulnerability exists. After a patch is installed, the following error is returned:
Bash: Warning: x: ignoring function definition attempt
Bash: 'X' Function Definition import Error
This is a test
Principle Analysis
Variables can be defined in Shell, and a variable named x is defined in POC. The content is a string:
() {:;}; Echo vulnerable
According to the vulnerability information, the vulnerability occurs when Shell executes the command after the function body when processing the function definition. But here the value of x is a string. How does it convert to a function.
In reality, this is related to Bash implementation. In Bash, a function is defined in the format:
Function function_name (){
Body;
}
When Bash initializes environment variables and the syntax parser finds parentheses and braces, it is considered as a function definition:
[Lu4nx @ lx-pc ~] $ Say_hello = '() {echo hello world ;}'
[Lu4nx @ lx-pc ~] $ Export say_hello
[Lu4nx @ lx-pc ~] $ Bash-c 'say _ hello'
Hello world
In the new Bash process, say_hello is a function in the new environment. Its evolution process is as follows:
1. When the new bash is started, the environment variable say_hello is scanned with parentheses and braces, which are determined to be a function definition.
2. bash uses say_hello as the function name and its value as the function body.
The typeset command can list all variables and function definitions in the current environment. Let's use typeset to see how this string becomes a function. Continue with the say_hello function defined above:
[Lu4nx @ lx-pc ~] $ Bash-c 'typeset' | fgrep-A 10 say_hello
Say_hello ()
{
Echo hello world
}
A Bash process is started and typeset is executed. typeset returns all definitions in the current environment (New Environment). It is clear that say_hello is converted into a function.
Cause of vulnerability
The vulnerability is that Bash executes the statement after the function definition after parsing the function body. Why.
By combining the patches, I analyzed the Bash source code. During Bash initialization, The parse_and_execute function in builtins/evalstring. c was called. Yes, that is, when Bash initializes the environment, it calls eval functions similar to other advanced languages, which are responsible for parsing string input and executing.
Continue to read the source code of parse_and_execute. The key points are as follows:
218 else if (command = global_command)
219 {
220 struct fd_bitmap * bitmap;
It determines whether the command is defined as global. After the new bash process is started, say_hello is not only parsed as a function, but also global:
[Lu4nx @ lx-pc data] $ bash-c 'typeset-F'
Say_hello ()
{
Echo hello world
}
Declare-fx say_hello
The declare command is embedded in Bash and used to limit the attributes of variables.-f indicates that say_hello is a function, and-x indicates that say_hello is converted into an environment variable by export, therefore, this statement makes say_hello a globally valid function.
In fact, Bash actually wants to initialize the environment variables and define some functions at startup. The initial method is to run the value assignment statement such as variable name = value with eval, if a function definition appears, it is converted into a function. In addition, it does not want to do anything else, but when it scans the function definition, when converting it into a function, the subsequent commands are accidentally executed. This is not an eval error. This is because the Syntax Parsing is not strictly considered, therefore, the patch adds such a sentence to determine the validity of the function body:
if ((flags & SEVAL_FUNCDEF) && command->type != cm_function_def)
After the above official patch is patched, it will be bypassed
Run the following command:
1 |
envX='() { (a)=>\' sh -c "echo date";cat echo |
When the code above runs, an error is reported, but what it requires is an error. After the error is reported, an echo file is generated in the current directory. The content of this file is a time text. The following figure shows how the preceding command is executed.
1 2 3 4 5 |
$env X='() { (a)=>\' sh -c "echo date";cat echo sh: X: line 1: syntax error near unexpected token `=' sh: X: line 1: `' sh: error importingfunction definition for `X' Sat Sep 27 22:06:29 CST 2014 |
Principle Analysis:
The first patch officially provided was changed:
1. The parameter type and quantity restrictions can be seen from the comments:
#define SEVAL_FUNCDEF 0x080 /* only allow function definitions */#define SEVAL_ONECMD 0x100 /* only allow a single command */
2. added the type judgment to parse_and_execute in the builtins/evalstring. c file:
If (flags & SEVAL_FUNCDEF) & command-> type! = Cm_function_def)
{
Invalid, not Function Definition
Break;
}
...
// If the logic is true, the parameter is invalid.
If (flags & SEVAL_ONECMD)
Break;
From the above, we can see the patch idea: if it is not a function definition or a command that exceeds one, it is deemed invalid. What is valid? The Bypass POC provides the answer:
Env X = '() {(x) => \'./bash-c 'my echo hello'
As long as the function body satisfies the () {header. In addition, this POC also meets a single command, because ";" is not displayed.
Bash Shell encountered a syntax problem (x) = ignored during eval. After a syntax error occurs, only the "> \" characters are left in the buffer.
Next we came to the point where the new bash process executed this command:
$> \
My echo hello
If you know bash, you will know that \ is used for line feed on the command line, so it is equivalent to executing
$> \ My echo hello
Then the my file is generated in the path with the content "hello.
Bash syntax is extremely weird. Let's analyze it one by one.
The character \ is a transfer character, and the text following it will be retained. \ my is actually equal to the string my. If \ is not present, the new bash process regards my as a command. If you enter \ in the terminal and press enter, the current bash process will block waiting for your input. In the POC, "input" is my.
Character> is the legendary redirection. Assume that the output of process A is written to file B as follows:
A> B
In fact, you can write it in the form of B A. If you don't believe it, try it:
[Lu4nx @ lx-pc/tmp] $> hi date
[Lu4nx @ lx-pc/tmp] $ cat hi
Saturday, September 27, 2014 01:06:06 CST
This prefix is also the first time I saw it. This analysis of the Shell Source Code showed that its design is very similar to a Lisp parser. I thought this method takes care of Lisper, because the Bash structure is basically an interactive (REPL) and eval, and the core of the Lisp parser is eval, until I read the Shell Yacc syntax analysis (parse. y. The redirection syntax is defined as follows:
Redirection: '> 'word
{
Redir. filename = $2;
$ = Make_redirection (1, r_output_direction, redir );
}
Here, the output file is taken from $2. $2 indicates the parameter WORD in this section. If the input statement is> a B, the real parameter of WORD is; if the input statement is A> B, the real parameter of WORD is B.
So the idea of POC is to define a function body with invalid syntax, bypass the detection code of the function definition, and then execute the following command, finally, Bash executes> \ my echo hello during initialization.
Reference adapted articles
Http://coolshell.cn/
Http://blog.knownsec.com/2014/09/bash_3-0-4-3-command-exec-analysis/
Http://blog.knownsec.com/2014/09/bash_3-0-4-3-command-exec-patch-bypass-analysis/
Http://www.antiy.com/response/CVE-2014-6271.html