安全中级2:nginx的中间件漏洞

目录

一、nginx解析php的流程

1.原理   

2.CGI、FastCGI、PHP-FPM、PHP-CG、WrapperI的定义

二、Fastcgi协议

1.Fastecgi Record

2.Fastcgi Type

3.PHP-FPM(FastCGI进程管理器)

4.总结FastCGI解析的流程

三、nginx配置错误导致的漏洞

1.CRLF注入漏洞($uri解码漏洞,换行符导致的注入漏洞)

(1)原理

(2)利用CRLF修改头部信息

(3)CRLF+Bottle HTTP造成的反射性xss漏洞

(4)防御

2.目录穿越漏洞

(1)原理

(2)漏洞复现

(3)防御

3.Http add_header被覆盖的问题

(1)原理

(2)CSP(Content-Security-Policy)

(3)漏洞的复现

(4)防御

四、nginx文件名逻辑漏洞(Nginx 0.8.41 ~ 1.4.3 /1.5.0 ~ 1.5.7 )

1.原理

2.文件名逻辑漏洞的复现

(1)首先创建一个文本,里面输入php代码

(2)修改文件后缀名为1.gif

(3)打开浏览器输入

(4)选择我们1.gif文件

(5)打开抓包工具,之后上传文件抓包

(6)找到文件名给他加成1.gifaa.php

(7)进入到hex也就是编码数字的部分,修改文件名,将aa改成20和00,主要是让他生成 \0(也就是一个空格和阶段符号)。

(8)将修改的数据包放行提交到端

(9)查看我们上传图片的目录

 (10)打开抓包工具抓取该网页,出现以下问题,是因为我们禁止了抓包工具抓取图片,我们点击开启就可以了。

 (11)抓包

 (13)修改我们的文件名为1.gifaa.php

 (14)进入到hex下,修改我们的aa编码为20和00,就是空格和截断符,这里主要是为了和我们上传的文件名一样

 (15)将我们修改过的数据包发送就会看到我们注意的php页面

 3.防御

五、nginx的解析漏洞

1.原理

2.nginx解析漏洞复现

(1)先看我们php-fpm配置文件,发现我们的后缀名安全策略为空的

(2)访问http://192.168.191.129/uplocalfiles/nginx.png

(3)在上面路径后面随意添加一个以.php结尾的文件

 (4)触发我们的php恶意代码

(5)还有一种方法和上面nginx逻辑名漏洞一样上传php恶意文件,修改名称,第二次直接访问就会触发


一、nginx解析php的流程

1.原理   

        首先浏览器对nginx进行传输数据,nginx接收到浏览器传过来的数据之后,通过fastcgi协议将数据格式化成fastcgi规定的形式,格式完之后通过php-fpm进程管理工具,格式成n多个的phpcgi,从而处理成fastcgi规范的数据,处理完的数据交给我们的php,然后PHP将数据传递给我们的nginx。

2.CGI、FastCGI、PHP-FPM、PHP-CG、WrapperI的定义

  • CGI:CGI是一种协议,它定义了Nginx或者其他Web Server传递过来的数据格式,全称是(Common Gateway Interface,CGI),CGI是一个独立的程序,独立与WebServer之外,任何语言都可以写CGI程序,例如C、Perl、Python等。

  • FastCGI:FastCGI是一种协议,它的前身是CGI,可以简单的理解为是优化版的CGI,拥有更够的稳定性和性能。

  • PHP-CGI:只是一个PHP的解释器,本身只能解析请求,返回结果,不会做进程管理。

  • PHP-FPM:全称FastCGI Process Manager,看名称就可以知道,PHP-FPM是FastCGI进程的管理器,但前面讲到FastCGI是协议并不是程序,所以它管理的是PHP-CGI,形成了一个类似PHP-CGI进程池的概念。

  • Wrapper:字母意思是包装的意思,包装的是谁呢?包装的是FastCGI,通过FastCGI接口,Wrapper接收到请求后,会生成一个新的线程调用PHP解释器来处理数据。

二、Fastcgi协议

1.Fastecgi Record

        Fastcgi其实是一个通信协议,fastcgi协议则是服务器中间件和某个语言后端进行数据交换的协议。Fastcgi协议由多个record组成,record也有header和body一说,服务器中间件将这二者按照fastcgi的规则封装好发送给语言后端,语言后端解码以后拿到具体数据,进行指定操作,并将结果再按照该协议封装好后返回给服务器中间件。其结构如下

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;

        头由8个uchar类型的变量组成,每个变量1字节。其中,requestId占两个字节,一个唯一的标志id,以避免多个请求之间的影响;contentLength占两个字节,表示body的大小。

        语言端解析了fastcgi头以后,拿到contentLength,然后再在TCP流里读取大小等于contentLength的数据,这就是body体。

我们需要注意的是typre类型

2.Fastcgi Type

        type是用来编辑fastcgi的主要作用,最主要的是1、4、6

        值得注意的是,当我们的type是4的时候,就会按照对应的结构解析成我们的key-value,结构如下

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;

        我们需要注意的是上述我标红的部分,当为44的部分时候,会将我们的key-value,传递到我们的php-fpm解析成我们真正的数据。

3.PHP-FPM(FastCGI进程管理器)

        FPM其实是一个fastcgi协议解析器,Nginx等服务器中间件将用户请求按照fastcgi的规则打包好通过TCP传给FPM,FPM按照fastcgi的协议将key-value解析成真正的数据。结构如下

{
    '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'
}

最后执行SCRIPT_FILENAM的指向的PHP文件,将这个数据返回给我们的浏览器。

4.总结FastCGI解析的流程

        首先nginx接收到数据后,会被fastcgi处理,会按照type为4,构造出我们的key-value,然后传递给我们的php-fpm,php-fpm会将key-value对解析成我们真正的数据,这个数据中包含我们的版本,传参方式,请求文件根目录路径,请求的文件,请求参数等。然后根据数据请求我们的php,完成我们的nginx+php的请求。

三、nginx配置错误导致的漏洞

注意:1.先下载好文件(漏洞文件下载地址

        2.进入到vulhub/nginx/insecure-configuration/这个目录下

        3.启用docker加载环境(docker-compose up -d)

1.CRLF注入漏洞($uri解码漏洞,换行符导致的注入漏洞)

(1)原理

        在我们访问http://example.com/abc时,我们的网页会自动的跳转到http://www.example.com/abc,但是在跳转的过程之中需要保持我们的uri(也就是网址后面的部分,比如abc)不变,在nginx配置中有三种配置uri的方法$uri、$document_uri、$request_uri,在这里面$uri和$document_uri是解码后的请求路径,而$request_uri并没进行解码,正是因为uri解码,我们可以利用%0d%0a(换行符),在我们的uri后面写入我们恶意代码,从而插入到我们的referer的头部,从而修改我们的头部信息。

(2)利用CRLF修改头部信息

a.nginx运维的错误配置

b.注入一个set-cookie

curl -I http://127.0.0.1:8080%0d%0aset-cookie:%20jiege666

(3)CRLF+Bottle HTTP造成的反射性xss漏洞

a.bottle搭建起来的环境,利用python运行下面代码

import bottle
from bottle import route, run, template, request, response

@route('/')
def index():
    server = request.query.get('server')
    response.add_header('Server',server)
    return response

if __name__ == '__main__':
    bottle.debug(True)
    run(host='localhost', port=8081)

b.在浏览器输入以下的请求

http://localhost:8081/server=jiege%0d%0aX-XSS-Protection:0%0d%0a%0d%0a<script>alert(location.href)</script>

c.结果

d.原理

        我们分析代码,发现server接收我们的内容,我们随意赋值,加入一个%0d%0a(换行符)先给他换行,然后在他的referer头部插入X-XSS-Protection:0,这个是浏览用来防止我们进行xss注入的,所以我们拆入头部将他关闭,然后%0d%0a%0d%0a之所以是两个换行符,是为了让后面的内容插入到我们的body也就是我们的html里面的,后面跟上我们<script>alert(location.href)</script>,就可以造成我们的xss反射性漏洞弹窗,从而获取我们的路径,也可以是其他恶意代码。以上就完成我们的CRLF+Bottle HTTP的xss漏洞

e.防御CRLF+Bottle HTTP

        第一种就是使用CSP禁止iframe跳转,第二种是将跳转的url端口设置<80

(4)防御

        我们可以在配置nginx的uri的跳转的时候,配置成$request_uri这样就可以防止浏览器自己解码了,从而防御住了我们的CRLF(换行符漏洞)

2.目录穿越漏洞

(1)原理

        nginx在配置别名(alias)的时候忘记加了/,让原本访问某个目录下的文件,攻击者多加../就会返回到根目录,从而造成了目录穿越漏洞。

(2)漏洞复现

a.出现错误的配置文件

b.在浏览器输入

http://192.168.191.129:8081/files../

c.结果

d.漏洞利用

        我们可以进入到/usr/local/nginx/html/下,下载我们的config.php也就是我们的数据库连接文件,获得数据库的用户名和密码,登陆数据库阻止其他人的连接,或者获取数据。

(3)防御

        给location /files 和alias  /home的后面同时加/,或者都不加

3.Http add_header被覆盖的问题

(1)原理

        nginx在配置子模块(server location if)的add_header的时候,将会覆盖父模块的add_header头部,从而造成的隐患。

(2)CSP(Content-Security-Policy)

        主要是为了防止XSS的存在,阻止入侵者插入存在第三方跳转的链接

(3)漏洞的复现

a.后端出现配置错误的文件

        我们不难发现在我们的子模块中出现了add_header,而我们的父模块是只允许我们内部引用js,而我们的字模块却可以引用外部js,又因为我们的子模块会覆盖父模块,这就导致了漏洞的存在。

b.我们正常访问页面test1,发现页面存在csp只允许加载本地js

c.我们在访问test2,发现csp被覆盖了可以访问外部js,我们利用这一点进行攻击

d.在浏览器插入以下的请求

http://192.168.191.129:8082/test2#<img src=1 οnerrοr=alert(jiege666)>

e.我们查看页面弹出弹窗就可以进行xss漏洞的注入

         一般情况下会出现一个弹窗的,可能由于我们的浏览器版本可能太高了,可以降低浏览器版本就可以实现弹窗了

(4)防御

        在配置子模块的时候,不要重复去添加父模块所含有的add_header头部

四、nginx文件名逻辑漏洞(Nginx 0.8.41 ~ 1.4.3 /1.5.0 ~ 1.5.7 )

1.原理

        因为我们上传了一个文件名为1.gif \0.php文件,首先我们的nginx解析到他是php结尾的文件,之后将他发送给我们的fastcgi,我们的fastcgi将这个数据解析完成后,又由于1.gif \0.php,\0也就是c语言的结束符的意思,之后我们的通过我们的\0将这个文件名截断成我们的fastcgi认为他到1.gif就结束了,之后对上传的文件进行检查,发现他没有以php结尾,就将这个文件上传上去,然后我们的php-fpm.conf的安全后缀策略(security.limit_extensions = )是空的,之后他就会执行我们上传的1.gif \0.php文件。

 

2.文件名逻辑漏洞的复现

(1)首先创建一个文本,里面输入php代码

<?php phpinfo(); ?>

(2)修改文件后缀名为1.gif

(3)打开浏览器输入

http://192.168.191.129:8080

(4)选择我们1.gif文件

(5)打开抓包工具,之后上传文件抓包

(6)找到文件名给他加成1.gifaa.php

(7)进入到hex也就是编码数字的部分,修改文件名,将aa改成20和00,主要是让他生成 \0(也就是一个空格和阶段符号)。

(8)将修改的数据包放行提交到端

(9)查看我们上传图片的目录

http://192.168.191.129:8080/uploadfiles/1.gif

 (10)打开抓包工具抓取该网页,出现以下问题,是因为我们禁止了抓包工具抓取图片,我们点击开启就可以了。

 (11)抓包

 (13)修改我们的文件名为1.gifaa.php

 (14)进入到hex下,修改我们的aa编码为20和00,就是空格和截断符,这里主要是为了和我们上传的文件名一样

 (15)将我们修改过的数据包发送就会看到我们注意的php页面

 3.防御

        我们在配置php-fpm.conf的安全后缀策略(security.limit_extensions = jpg gif)尽量不要设置为空,这样就可以防御我们的nginx文件名逻辑漏洞。

五、nginx的解析漏洞

1.原理

        我们在配置php.ini的时候,开启了我们的自动修复路径(cgi.fix_pathinfo=1),也就是我们访问http://127.0.0.1/test.jpg/test.php,由于没有test.php这个文件我们服务器会自动到上一个路径,发现有test.jpg于是就自己执行了这个文件,但是由于我们在配置php-fpm.conf的安全后缀策略(security.limit_extensions = jpg php )为空或者就只有jpg和php,我们的服务就会执行jpg和php,我们的test.jpg里面就是我们的恶意的php代码,之后通过解析为php文件就导致了我们执行该文件,从而产生了我们的漏洞。

2.nginx解析漏洞复现

(1)先看我们php-fpm配置文件,发现我们的后缀名安全策略为空的

(2)访问http://192.168.191.129/uplocalfiles/nginx.png

http://192.168.191.129/uplocalfiles/nginx.png

(3)在上面路径后面随意添加一个以.php结尾的文件

http://192.168.191.129/uplocalfiles/nginx.png/nginx.php

 (4)触发我们的php恶意代码

(5)还有一种方法和上面nginx逻辑名漏洞一样上传php恶意文件,修改名称,第二次直接访问就会触发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值