0ctf 2019中Wallbreaker Easy中的出题点就是采用了imagick在处理一些特定后缀文件时,会调用ffmpeg,也就是会开启子进程,从而达成加载共享库执行系统命令bypass disable。
Apache Mod CGI前面的两种利用都需要putenv,如果putenv被ban了那么就需要这种方式,简单介绍一下原理。
原理利用htaccess覆盖apache配置,增加cgi程序达成执行系统命令,事实上同上传htaccess解析png文件为php程序的利用方式大同小异。
mod cgi:
任何具有MIME类型application/x-httpd-cgi或者被cgi-script处理器处理的文件都将被作为CGI脚本对待并由服务器运行,它的输出将被返回给客户端。可以通过两种途径使文件成为CGI脚本,一种是文件具有已由AddType指令定义的扩展名,另一种是文件位于ScriptAlias目录中。
因此我们只需上传一个.htaccess:
Options +ExecCGI //使运行cgi程序的执行 AddHandler cgi-script .test //将test后缀的文件解析为cgi程序 利用利用就很简单了:
上传htaccess,内容为上文所给出的内容
上传a.test,内容为:
#!/bin/bash echo&&ls
给a.test权限,访问即可得到执行结果。
PHP-FPMphp-fpm相信有读者在配置php环境时会遇到,如使用nginx+php时会在配置文件中配置如下:
location ~ .php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }那么看看百度百科中关于php-fpm的介绍:
PHP-FPM(FastCGI Process Manager:FastCGI进程管理器)是一个PHPFastCGI管理器,对于PHP 5.3.3之前的php来说,是一个补丁包 [1] ,旨在将FastCGI进程管理整合进PHP包中。如果你使用的是PHP5.3.3之前的PHP的话,就必须将它patch到你的PHP源代码中,在编译安装PHP后才可以使用。
那么fastcgi又是什么?Fastcgi 是一种通讯协议,用于Web服务器与后端语言的数据交换。
原理那么我们在配置了php-fpm后如访问?test=1,那么会被解析为如下键值对:
{ 'GATEWAY_INTERFACE': 'FastCGI/1.0', 'REQUEST_METHOD': 'GET', 'SCRIPT_FILENAME': '/var/www/html/test.php', 'SCRIPT_NAME': '/test.php', 'QUERY_STRING': '?test=1', 'REQUEST_URI': '/test.php?test=1', 'DOCUMENT_ROOT': '/var/www/html', 'SERVER_SOFTWARE': 'php/fcgiclient', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '12304', 'SERVER_ADDR': '127.0.0.1', 'SERVER_PORT': '80', 'SERVER_NAME': "localhost", 'SERVER_PROTOCOL': 'HTTP/1.1' }这个数组很眼熟,会发现其实就是$_SERVER里面的一部分,那么php-fpm拿到这一个数组后会去找到SCRIPT_FILENAME的值,对于这里的/var/www/html/test.php,然后去执行它。
前面笔者留了一个配置,在配置中可以看到fastcgi的端口是9000,监听地址是127.0.0.1,那么如果地址为0.0.0.0,也即是将其暴露到公网中,倘若我们伪造与fastcgi通信,这样就会导致远程代码执行。
那么事实上php-fpm通信方式有tcp也就是9000端口的那个,以及socket的通信,因此也存在着两种攻击方式。
socket方式的话配置文件会有如下:
fastcgi_pass unix:/var/run/phpfpm.sock;那么我们可以稍微了解一下fastcgi的协议组成,其由多个record组成,这里摘抄一下p神原文中的一段结构体:
typedef struct { /* Header */ unsigned char version; // 版本 unsigned char type; // 本次record的类型 unsigned char requestIdB1; // 本次record对应的请求id unsigned char requestIdB0; unsigned char contentLengthB1; // body体的大小 unsigned char contentLengthB0; unsigned char paddingLength; // 额外块大小 unsigned char reserved; /* Body */ unsigned char contentData[contentLength]; unsigned char paddingData[paddingLength]; } FCGI_Record;可以看到record分为header以及body,其中header固定为8字节,而body由其contentLength决定,而paddingData为保留段,不需要时长度置为0。