Author：LoRexxar’@Knownsec 404 Team
Chinese version: https://paper.seebug.org/1063/
In Real World CTF 2019 Quals, Andrew Danau, a security researcher, found that when the %0a symbol was sent to the target server URL, the server returned an exception and it was very likely to be a vulnerability.
On October 23, 2019, someone posted the details of the vulnerability and exp on Github.. When nginx is not configured properly, it will cause php-fpm remote arbitrary code execution.
Let’s make an in-depth analysis of this vulnerability, and I also want to say thanks to my coworkers @Hcamael from Knownsec 404-Team.
In order to make the recurrence, we use vulhub to build the vulnerability environment.
git pull and
docker-compose up -d
download exp from github (under go environment)
go get -v github.com/neex/phuip-fpizdam
go install github.com/neex/phuip-fpizdam
use exp to attack
Before analyzing its principle, we can have a look at the patch.
We can found that the cause for the vulnerability is the controllable address of
path_info . Combined with the information given by the expert:
The regexp in `fastcgi_split_path_info` directive can be broken using the newline character (in encoded form, %0a). Broken regexp leads to empty PATH_INFO, which triggers the bug.
That is to say, when
path_info is truncated by %0a,
path_info will be set to null.
env_path_info is the address of the variable
path_info is 0 so
plien is 0.
slen is the length of requested url.
int ptlen = strlen(pt); int slen = len - ptlen;
int len = script_path_translated_len; len is the length of url path when url is http://127.0.0.1/index.php/123%0atest.php script_path_translated from nginx config，is /var/www/html/index.php/123\ntest.php ptlen is the length of content is which before first slash in url path when url is http://127.0.0.1/index.php/123%0atest.php pt is /var/www/html/index.php
The difference between these two variables is the length of the path behind. Since the path is controllable,
path_info can be controlled.
We can set the value of any address to zero in line 1222. According to the description of the vulnerability finder, by setting the value of the specified address to zero, we can control the
char* pos of
_fcgi_data_seg structure to zero.
script_name is also from request configuration.
But why is it that setting the
char* pos of
_fcgi_data_seg structure to zero can affect the result of
Here we go deeper to see the definition of
char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val);
The function directly modify
request->env, and this parameter is predefined.
Follow into the init function
request->env is the
fcgi_data_seg structure. And
request->env is a global variable when the nginx and fastcgi communicate.
Some global variable will be defined in nginx config.
The variable will be stored in the corresponding location on the heap.
When it comes to exploit, we could control
path_info to point to
request->env to set
request->env->pos to zero.
Back to the function
Here the lowest bit of
h->data->pos is set to 0 and the str is controllable. This means we can write data in front of it.
Now a new problem is that how could we write data into where we want?
Here we use the packet for example.
In the packet, the last two parts of the header are to complete its function. The
D-Gisos is used to shift and write data to specified location.
Ebut will be converted to a global variables in
fast_param ) .
First we need to understand how global variables in
fastcgi get data.
We can see when fastcgi want to get the global variables, it will read the length of the specified position for comparison, and then read a string as the value.
This means if the position is reasonable，the value of the var is the same，and the length is the same，fastcgi will read the corresponding data.
PHP_VALUE have exactly the same length, so we can confirm this from the changes in data on the heap。
Before covering, data of this address is,
And then execute
The data transfered to
We successfully write
PHP_VALUE and controlled its contents, which means we can control any global variables in PHP`
When we can control and global variables in PHP, we hava many ways to attack. Here we use the example in exp.
exp author sets up auto including, sets including path to
/tmp, and sets log file address to
/tmp/a and write the payload into the log file. Finally, the PHP file will automatically include log file to make a backdoor.
We suggest two ways to fix this vulnerability.
- Temporary ：
Modify nginx config file, and add this to relevant configuratoin
try_files $uri =404
In this way, Nginx will check if the file exist, and the request will not be transferred to php-fpm when it does not exist.
- Update PHP 7.1.X to 7.1.33
- Update PHP 7.2.X to 7.2.24
- Update PHP 7.3.X to 7.3.11
- Update PHP 7.1.X to 7.1.33
1.Nginx + php_fpm +
location ~ [^/]\.php(/|$) will transfer request to php-fpm.
fastcgi_split_path_info and start with
^ and end with
$. Only in this way can we use %0a to break.
ps: this will allow
index.php/321 -> index.php
PATH_INFO be set from
fastcgi_param PATH_INFO $fastcgi_path_info; . It will be defined by default in
4.There is no definition of file check in Nginx just like
try_files $uri =404，if it does, the request will not be transferred to php-fpm.
The impact of this vulnerability is limited since most Nginx configurations have file check, and the default Nginx not have this problem.
But also bacause of this, most sample online or environments that do not take this into consideration (the example configuration of Nginx and the default environment of NextCloud) got this problem, and this vulnerability has become a threat to other servers.
In this case, once a problematic configuration is spread, it may involve a huge number of servers, and timely updating is always the best protection:>
- vulnerability issue
- vulnerability finder environment
- vulnerability exp
- vulnerability code
- vulnerability repair commit