前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Struts2 S2-045 漏洞触发流程不严谨推测

Struts2 S2-045 漏洞触发流程不严谨推测

作者头像
s1mba
发布2017-12-28 15:10:58
5280
发布2017-12-28 15:10:58
举报
文章被收录于专栏:开发与安全开发与安全
代码语言:javascript
复制
// 根据 已有的一些信息和修复版本的代码,推测应该是如下的触发流程
// 因为没有测试环境,也只是不严谨的代码触发流程推测,不保证正确性,欢迎大神交流分享。

//core\src\main\java\org\apache\struts2\dispatcher\multipart\JakartaMultiPartRequest.java
public void parse(HttpServletRequest request, String saveDir) throws IOException
{
    try
    {
        setLocale(request);
        processUpload(request, saveDir); // 调用 1
    }
    catch (FileUploadBase.SizeLimitExceededException e)
    {
        if (LOG.isWarnEnabled())
        {
            LOG.warn("Request exceeded size limit!", e);
        }
        String errorMessage = buildErrorMessage(e, new Object[] {e.getPermittedSize(), e.getActualSize()});
        if (!errors.contains(errorMessage))
        {
            errors.add(errorMessage);
        }
    }
    catch (Exception e)     // 调用 7 捕获异常
    {
        if (LOG.isWarnEnabled())
        {
            LOG.warn("Unable to parse request", e);
        }
        String errorMessage = buildErrorMessage(e, new Object[] {}); //调用 8
        if (!errors.contains(errorMessage))
        {
            errors.add(errorMessage);
        }
    }
}

protected String buildErrorMessage(Throwable e, Object[] args)
{
    String errorKey = "struts.messages.upload.error." + e.getClass().getSimpleName();
    if (LOG.isDebugEnabled())
    {
        LOG.debug("Preparing error message for key: [#0]", errorKey);
    }
    return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args);
    // 调用9,触发
   // 因为 args 为空,使用默认的 e.getMessage() 为 "the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is" + content-type
   // If a message is found, it will also be interpolated. Anything within ${...} will be treated as an OGNL expression and evaluated as such.
  // 故执行了http 请求头content-type 中的{}内部引入的指令
// reference:https://struts.apache.org/maven/struts2-core/apidocs/com/opensymphony/xwork2/util/LocalizedTextUtil.html#findText(java.lang.Class, java.lang.String, java.util.Locale, java.lang.String, java.lang.Object[])
}

protected void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException
{
    for (FileItem item : parseRequest(request, saveDir))   //  调用2,如下所示
    {
        if (LOG.isDebugEnabled())
        {
            LOG.debug("Found item " + item.getFieldName());
        }
        if (item.isFormField())
        {
            processNormalFormField(item, request.getCharacterEncoding());
        }
        else
        {
            processFileField(item);
        }
    }
}

protected List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException
{
    DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);
    ServletFileUpload upload = createServletFileUpload(fac);
    return upload.parseRequest(createRequestContext(servletRequest)); // 调用 2.1
}

protected ServletFileUpload createServletFileUpload(DiskFileItemFactory fac)
{
    ServletFileUpload upload = new ServletFileUpload(fac);
    upload.setSizeMax(maxSize);
    return upload;
}

// commons - fileupload - 1.2.1.jar org.apache.commons.fileupload.servlet ServletFileUpload.java
public class ServletFileUpload extends FileUpload
{
    public static final boolean isMultipartContent(final HttpServletRequest request)
    {
        if (!"post".equals(request.getMethod().toLowerCase()))
        {
            return false;
        }
        final String contentType = request.getContentType();
        return contentType != null && contentType.toLowerCase().startsWith("multipart/");
    }

    public ServletFileUpload()
    {
    }

    public ServletFileUpload(final FileItemFactory fileItemFactory)
    {
        super(fileItemFactory);
    }

    public List parseRequest(final HttpServletRequest request) throws FileUploadException
    {
        return this.parseRequest(new ServletRequestContext(request)); // 调用 2.2
    }

    public FileItemIterator getItemIterator(final HttpServletRequest request) throws FileUploadException, IOException
    {
        return super.getItemIterator(new ServletRequestContext(request));
    }
}



// commons - fileupload - 1.2.1.jar org.apache.commons.fileupload FileUploadBase.java
public List parseRequest(final RequestContext ctx) throws FileUploadException
{
    try
    {
        final FileItemIterator iter = this.getItemIterator(ctx); // 调用 3
        final List items = new ArrayList();
        final FileItemFactory fac = this.getFileItemFactory();
        if (fac == null)
        {
            throw new NullPointerException("No FileItemFactory has been set.");
        }
        while (iter.hasNext())
        {
            final FileItemStream item = iter.next();
            final FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(), item.isFormField(), item.getName());
            try
            {
                Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
            }
            catch (FileUploadIOException e)
            {
                throw (FileUploadException)e.getCause();
            }
            catch (IOException e2)
            {
                throw new IOFileUploadException("Processing of multipart/form-data request failed. " + e2.getMessage(), e2);
            }
            if (fileItem instanceof FileItemHeadersSupport)
            {
                final FileItemHeaders fih = item.getHeaders();
                ((FileItemHeadersSupport)fileItem).setHeaders(fih);
            }
            items.add(fileItem);
        }
        return items;
    }
    catch (FileUploadIOException e3)
    {
        throw (FileUploadException)e3.getCause();
    }
    catch (IOException e4)
    {
        throw new FileUploadException(e4.getMessage(), e4);
    }
}

public FileItemIterator getItemIterator(final RequestContext ctx) throws FileUploadException, IOException
{
    return new FileItemIteratorImpl(ctx); //调用 4
}

private class FileItemIteratorImpl implements FileItemIterator
{
    private final MultipartStream multi;
    private final MultipartStream.ProgressNotifier notifier;
    private final byte[] boundary;
    private FileItemStreamImpl currentItem;
    private String currentFieldName;
    private boolean skipPreamble;
    private boolean itemValid;
    private boolean eof;
    private final /* synthetic */ FileUploadBase this$0;

    // 构造函数调用 5
    FileItemIteratorImpl(final RequestContext ctx) throws FileUploadException, IOException
    {
        if (ctx == null)
        {
            throw new NullPointerException("ctx parameter");
        }
        final String contentType = ctx.getContentType();
        if (null == contentType || !contentType.toLowerCase().startsWith("multipart/"))
        {
            throw new InvalidContentTypeException("the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is " + contentType);
            // 调用6,抛出异常
        }
        InputStream input = ctx.getInputStream();
        if (FileUploadBase.this.sizeMax >= 0L)
        {
            final int requestSize = ctx.getContentLength();
            if (requestSize == -1)
            {
                input = new LimitedInputStream(input, FileUploadBase.this.sizeMax)
                {
                    protected void raiseError(final long pSizeMax, final long pCount) throws IOException
                    {
                        final FileUploadException ex = new SizeLimitExceededException("the request was rejected because its size (" + pCount + ") exceeds the configured maximum" + " (" + pSizeMax + ")", pCount, pSizeMax);
                        throw new FileUploadIOException(ex);
                    }
                };
            }
            else if (FileUploadBase.this.sizeMax >= 0L && requestSize > FileUploadBase.this.sizeMax)
            {
                throw new SizeLimitExceededException("the request was rejected because its size (" + requestSize + ") exceeds the configured maximum (" + FileUploadBase.this.sizeMax + ")", requestSize, FileUploadBase.this.sizeMax);
            }
        }
        String charEncoding = FileUploadBase.this.headerEncoding;
        if (charEncoding == null)
        {
            charEncoding = ctx.getCharacterEncoding();
        }
        this.boundary = FileUploadBase.this.getBoundary(contentType);
        if (this.boundary == null)
        {
            throw new FileUploadException("the request was rejected because no multipart boundary was found");
        }
        this.notifier = new MultipartStream.ProgressNotifier(FileUploadBase.this.listener, ctx.getContentLength());
        (this.multi = new MultipartStream(input, this.boundary, this.notifier)).setHeaderEncoding(charEncoding);
        this.skipPreamble = true;
        this.findNextItem();
    }

    //......
}


// commons - fileupload - 1.2.1.jar org.apache.commons.fileupload FileUploadException.java
public FileUploadException(final String msg, final Throwable cause)
{
    super(msg);
    this.cause = cause;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-03-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档