1024programmer PHP The PHP version of the source code is complete and runnable_[Dry goods] PHP kernel analysis of FPM and disable_function security issues

The PHP version of the source code is complete and runnable_[Dry goods] PHP kernel analysis of FPM and disable_function security issues

Maybe I’ve been busy at work lately,Maybe I’m lazy,The efficiency is not very high,In recent months, I have read some PHP kernel source code intermittently.

In this year’s TCTF,there was a scene of attacking FPM to bypass the sandbox. Decided to explore the FPM life cycle and disable_function source code implementation , phpinfo cannot be displayed accurately. There are still many places that I am not familiar with,I will slowly add later,Worship RR and President P,ORZ.

【tips: Please swipe left and right to view all the code lines in this article】Debugging environment

Install the debugging tool gdb

apt install gdb

Download php source code :

wget https://www.php .net/distributions/php-7.1.0.tar.gz

Then configure ./configure as follows

./configure –prefix=/root/php7.1.0 –enable-phpdbg-debug –enable-debug –enable-fpm CFLAGS=”-g3 -gdwarf-4″

View the Makefile as follows:

CC = gcc

CFLAGS = $(CFLAGS_CLEAN) – prefer-non-pic -static

CFLAGS_CLEAN = -I/usr/include -g3 -gdwarf-4 -fvisibility=hidden -O0 -Wall -DZEND_SIGNALS $(PROF_FLAGS)

CPP = gcc -E

CPPFLAGS =

CXX =

CXXFLAGS = -g -O0 -prefer-non-pic -static $(PROF_FLAGS)

CXXFLAGS_CLEAN = -g -O0

DEBUG_CFLAGS = -Wall

Only the necessary debug module & # 43; fpm module & # xff0c; other modules are installed as required.

CFLAGS=”-g3 -gdwarf-4″ is an additional configuration of compilation parameters , turn off all compilation optimization mechanisms , the symbol information necessary to generate gdb (symbol table ), and set the dwarf debug information format. Many macros are defined in the PHP kernel , In gdb debugging, the macro expand xxxx command can be used to expand macros more conveniently.

Compile and install php

as follows:

make && make install

bin directory contains commonly used php command line interpreters

3ffed6911331b50d8a371a00943843b2.  png

The sbin directory contains configuration files that fpm, still needs to run.

  • Specify the fpm configuration file , copy php-fpm.conf.default from the compiled directory and rename it to php-fpm.conf

  • Specify php configuration file , Copy php.ini-development from the source code directory and rename it to php.ini

50ceb067acbd700c4c472ac4e2ae9eba.png

Configure php.ini by yourself, mainly configure php-fpm here .conf

php-fpm is a multi-process model , a master process , multiple worker processes.

The master process is responsible for managing the scheduling , The worker process is responsible for processing the client (nginx) request.

The master process has three modes of managing the work process :

  • ondemand,on-demand mode,when there is a request The worker will be started

  • static,static mode,Start a fixed number of workers

  • dynamic& #xff0c;Dynamic Mode,Initialize some workers,Dynamically adjust the number of workers during operation

Let the working mode of fpm be static, and the work process There is only one & # xff0c; convenient for debugging & # xff0c; set the configuration file as follows & # xff1a;

pm = static

; The number of child processes to be created when pm is set to 'static' and the

; maximum number of child processes when pm is set to &#39 ;dynamic' or 'ondemand'.

; This value sets the limit on the number of simultaneous requests that will be

; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.

; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP

; CGI. The below defaults are based on a server without much resources. Don't

; forget to tweak pm.* to fit your needs.

; Note: Used when pm is set to 'static', 'dynamic' 39; or 'ondemand'

; Note: This value is mandatory.

pm.max_children = 1

; The number of child processes created on startup.

; Note: Used only when pm is set to 'dynamic'

; Default Value: min_spare_servers + ( max_spare_servers – min_spare_servers) / 2

pm.start_servers = 1

; The desired minimum number of idle server processes.

; Note: Used only when pm is set to 'dynamic'

; Note: Mandatory when pm is set to 'dynamic'

pm.min_spare_servers &#61 ; 1

; The desired maximum number of idle server processes.

; Note: Used only when pm is set to 'dynamic'

; Note: Mandatory when pm is set to 'dynamic'

pm.max_spare_servers = 1

Run fpm

./php-fpm -c php.ini -y php-fpm.conf

ps can find that the work process only starts one as scheduled:

<img src="https://img8.php1.cn/3cdc5/y *ini_entry = (zend_ini_entry*)Z_PTR_P(el);

int module_number = *(int *)arg;

if (ini_entry->module_number ! = module_number) {

return 0;

}

if (!sapi_module.phpinfo_as_text) {

PUTS(”

“);

PUTS(“

“);

PHPWRITE(ZSTR_VAL(ini_entry->name), ZSTR_LEN(ini_entry->name));

PUTS (“

“);

php_ini_displayer_cb (ini_entry, zend_ini_display_active);

puts (“

“); Ini_display_orig);

PUTS(“

\n”);

} else {

PHPWRITE(ZSTR_VAL(ini_entry->name), ZSTR_LEN(ini_entry->name));

PUTS(” => “);

php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE);

PUTS(” => “);

php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG);

PUTS(“\n”);

}

return 0;

}

After entering the if (!sapi_module.phpinfo_as_text) branch, it is the output of each configuration , php_ini_displayer_cb is the output function of the package , the general order is like this , the specific call stack As follows :

4b6be7a9107c06d7eb757d8dc4414c45.png

phpinfo obtains and prints information according to EG (ini_directives).

Attack PHP-FPM analysis

Trace PHP-FPM to accept malicious FastCgi protocol and parse according to PHP_VALUE to set disable_functions=.

The fcgi_accept_request function accepts the socket connection from the client through the accept function, and assigns it to req->fd.

213ce176188dc5c49b21c629cc57e69d.png

will pass The outer while loop keeps accepting connections

3530d5816aec9ab5f7ed56b032ef968c.png

At the same time, store the request variable containing the request handle in the global variable ,SG(server_context),The macro definition is as follows:

# define SG(v) (sapi_globals.v)

extern SAPI_API sapi_globals_struct sapi_globals;

Then enter the init_request_info function :

p>

static void init_request_info(void)

{

fcgi_request *request = (fcgi_request* ) SG(server_context);

char *env_script_filename = FCGI_GETENV(request, “SCRIPT_FILENAME”);

char *env_path_translated = FCGI_GETENV(request, “PATH_TRANSLATED” );

char *script_path_translated = env_script_filename;

char *ini;

int apache_was_here = 0;

//…………..

//…..

//…………. .

/* INI stuff */

ini = FCGI_GETENV(request, “PHP_VALUE”);

if (ini) {

int mode = ZEND_INI_USER;

char *tmp;

spprintf(&tmp, 0, “%s\n”, ini);

zend_parse_ini_string(tmp, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fastcgi_ini_parser, &mode);

efree(tmp);

}

ini &#61 ; FCGI_GETENV(request, “PHP_ADMIN_VALUE”);

if (ini) {

int mode = ZEND_INI_SYSTEM;

char *tmp;

p>

spprintf(&tmp, 0, “%s\n”, ini);

zend_parse_ini_string(tmp, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fastcgi_ini_parser, &mode);

efree(tmp);

}

}

Get the PHP_VALUE in the FastCgi request through the FCGI_GETENV macro:

fcgi_quick_getenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1))

Then enter the zend_parse_ini_string function &# xff1a;

ZEND_API int zend_parse_ini_string(char *str, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg)

{

int retval;

zend_ini_parser_param ini_parser_param;

ini_parser_param.ini_parser_cb = ini_parser_cb;

ini_parser_param.arg = 61; arg;

CG(ini_parser_param) = &ini_parser_param;

if (zend_ini_prepare_string_for_scanning(str, scanner_mode) == FAILURE) {

return FAILURE;

}

CG(ini_parser_unbuffered_errors) = unbuffered_errors;

retval = ini_parse();

p>

shutdown_ini_scanner();

if (retval == 0) {

return SUCCESS;

} else {

return FAILURE;

}

}

ini_parser_param.ini_parser_cb = ini_parser_cb real assigned fastcgi_ini_parser function, Then enter ini_parse for parsing , and then use the ZEND_INI_PARSER_CB macro

  1891aab33f9338525abdccb83da540df.png

View definition :

#define ZEND_INI_PARSER_CB (CG(ini_parser_param))- >ini_parser_cb

Actually call the fastcgi_ini_parser function :

c1c3f48e33292d248bd37de059f9b251.png

Continue to enter the fpm_php_apply_defines_ex function :

0118875db6edb67702994c25d9ed3e62.png

The call information at this time is as follows :

5db2a9a3da25c3649abceace29e26ce5.png

Continue to enter the fpm_php_zend_ini_alter_master function

8ce7f6b2a2b394e2ddeb98d149c995d7.png

From EG (ini_directives), find the ini_entry for disable_functions, Then modify the value to us The incoming content , and the value displayed by phpinfo comes from here.

The function string to be disabled will also be passed to the fpm_php_disable function :

7564037df88ea90d6a0d93dd100df6a8.png

Call the zend_disable_function function to modify func->handler to complete the disable.

Summary

  • The essence of disable_function is to modify func->handler to disable the function.

  • When the malicious FastCgi containing PHP_VALUE==disable_function= attacks FPM , can only modify the `EG( ini_directives) `, that is, surface modification , invalid for already disabled functions, but new functions can be disabled via FPM.

  • The more common and effective options for attacking FPM are extension_dir + extension, open_basedir, allow_url_include = On + auto_prepend_file = php:/ /input.

Reference

CGI、FastCGI和PHP-FPM关系图解

https://blog.csdn.net/st091zsc/article/ details/83822122

https://segmentfault.com/a/1190000013321594

Pay attention to the pulse , more security information and technical articles are waiting for you, xff01;

2110a5e89c7c46f96044406ca17763a5.png

8525abdccb83da540df.png” />

View definition :

#define ZEND_INI_PARSER_CB (CG(ini_parser_param))- >ini_parser_cb

Actually call the fastcgi_ini_parser function :

c1c3f48e33292d248bd37de059f9b251.png

Continue to enter the fpm_php_apply_defines_ex function :

0118875db6edb67702994c25d9ed3e62.png

The call information at this time is as follows :

5db2a9a3da25c3649abceace29e26ce5.png

Continue to enter the fpm_php_zend_ini_alter_master function

8ce7f6b2a2b394e2ddeb98d149c995d7.png

From EG (ini_directives), find the ini_entry for disable_functions, Then modify the value to us The incoming content , and the value displayed by phpinfo comes from here.

The function string to be disabled will also be passed to the fpm_php_disable function :

7564037df88ea90d6a0d93dd100df6a8.png

Call the zend_disable_function function to modify func->handler to complete the disable .

Summary

  • The essence of disable_function is to modify func->handler to disable the function.

  • When the malicious FastCgi containing PHP_VALUE==disable_function= attacks FPM , can only modify the `EG( ini_directives) `, that is, surface modification , invalid for already disabled functions, but new functions can be disabled via FPM.

  • The more common and effective options for attacking FPM are extension_dir + extension, open_basedir, allow_url_include = On + auto_prepend_file = php:/ /input.

Reference

CGI、FastCGI和PHP-FPM关系图解

https://blog.csdn.net/st091zsc/article/ details/83822122

https://segmentfault.com/a/1190000013321594

Pay attention to the pulse , more security information and technical articles are waiting for you, xff01;

2110a5e89c7c46f96044406ca17763a5.png

This article is from the internet and does not represent1024programmerPosition, please indicate the source when reprinting:https://www.1024programmer.com/34283

author: admin

Previous article
Next article

Leave a Reply

Your email address will not be published. Required fields are marked *

Contact Us

Contact us

181-3619-1160

Online consultation: QQ交谈

E-mail: [email protected]

Working hours: Monday to Friday, 9:00-17:30, holidays off

Follow wechat
Scan wechat and follow us

Scan wechat and follow us

Follow Weibo
Back to top
首页
微信
电话
搜索