Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2022-29464 — 针对CVE-2022-29464的Python漏洞利用程序,该漏洞是WSO2服务器中的一个未经验证的任意文件上传漏洞,可通过恶意JSP文件上传实现远程代码执行。 | Kitploit
工具/GitHubGitHub/hakivvi/cve-2022-29464
Payload生成漏洞分析漏洞利用Web应用程序漏洞利用渗透测试红队
GitHubhakivvi/cve-2022-29464

CVE-2022-29464

针对CVE-2022-29464的Python漏洞利用程序,该漏洞是WSO2服务器中的一个未经验证的任意文件上传漏洞,可通过恶意JSP文件上传实现远程代码执行。

查看仓库
3788824年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2022-29464

WSO2 RCE(CVE-2022-29464)漏洞利用与分析文章。

详情

CVE-2022-29464 是WSO2中的严重漏洞,由 Orange Tsai 发现。该漏洞是未经身份验证的无限制任意文件上传,允许未经身份验证的攻击者通过上传恶意JSP文件在WSO2服务器上获得RCE。

存在漏洞的上传路径为 /fileupload,由 FileUploadServlet servlet 处理。并且从 identity.xml 配置文件中可以看出,该路径未受IAM保护:```xml

root@kitploit:~
并且同样不受默认登录措施的保护,`handleSecurity()` 是负责保护WSO2所服务的不同路由的函数,并提供了一种对接收到的HTTP请求执行安全检查的机制。`handleSecurity()` 将调用 `CarbonUILoginUtil.handleLoginPageRequest()`,并根据其返回值决定允许或拒绝对所请求URI的访问:```java
    public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
	[snipped]
        if ((val = CarbonUILoginUtil.handleLoginPageRequest(requestedURI, request, response,
                authenticated, context, indexPageURL)) != CarbonUILoginUtil.CONTINUE) {
            if (val == CarbonUILoginUtil.RETURN_TRUE) {
                return true;
            } else {
                return false;
            }
        }
	[snipped]
    }

当路径为 /fileupload 时,CarbonUILoginUtil.handleLoginPageRequest() 返回 CarbonUILoginUtil.RETURN_TRUE:```java protected static int handleLoginPageRequest(String requestedURI, HttpServletRequest request, HttpServletResponse response, boolean authenticated, String context, String indexPageURL) throws IOException { boolean isTryIt = requestedURI.indexOf("admin/jsp/WSRequestXSSproxy_ajaxprocessor.jsp") > -1; boolean isFileDownload = requestedURI.endsWith("/filedownload"); if ((requestedURI.indexOf("login.jsp") > -1 || requestedURI.indexOf("login_ajaxprocessor.jsp") > -1 || requestedURI.indexOf("admin/layout/template.jsp") > -1 || isFileDownload || requestedURI.endsWith("/fileupload") || requestedURI.indexOf("/fileupload/") > -1 || requestedURI.indexOf("login_action.jsp") > -1 || isTryIt || requestedURI.indexOf("tryit/JAXRSRequestXSSproxy_ajaxprocessor.jsp") > -1) && !requestedURI.contains(";")) {

root@kitploit:~
        if ((requestedURI.indexOf("login.jsp") > -1
                || requestedURI.indexOf("login_ajaxprocessor.jsp") > -1 || requestedURI
                .indexOf("login_action.jsp") > -1) && authenticated) {
            [snipped]
        } else if ((isTryIt || isFileDownload) && !authenticated) {
            [snipped]
        } else if (requestedURI.indexOf("login_action.jsp") > -1 && !authenticated) {
            [snipped]
        } else {
			if (log.isDebugEnabled()) {
				log.debug("Skipping security checks for " + requestedURI);
			}
            return RETURN_TRUE;
        }
    }

    return CONTINUE;
}
root@kitploit:~
当 `CarbonUILoginUtil.handleLoginPageRequest()` 返回 `CarbonUILoginUtil.RETURN_TRUE` 时,`handleSecurity()` 将返回 `true`,从而无需认证即可访问 `/fileupload`。

`FileUploadServlet` 这个 servlet 在 [`init()`](https://github.com/wso2/carbon-kernel/blob/d47232dfb2b26c0ef18a74e2ef4aa503caa59697/core/org.wso2.carbon.ui/src/main/java/org/wso2/carbon/ui/transports/FileUploadServlet.java#L71) 方法中,通过一系列方法调用,最终从 `carbon.xml` 配置文件加载多个上传文件格式/操作以及处理每种格式的对象。```java
    public void init(ServletConfig servletConfig) throws ServletException {
        this.servletConfig = servletConfig;
        try {
            fileUploadExecutorManager = new FileUploadExecutorManager(bundleContext, configContext, webContext);
            //Registering FileUploadExecutor Manager as an OSGi service
            bundleContext.registerService(FileUploadExecutorManager.class.getName(), fileUploadExecutorManager, null);
        } catch (CarbonException e) {
            log.error("Exception occurred while trying to initialize FileUploadServlet", e);
            throw new ServletException(e);
        }
    }

FileUploadExecutorManager类的构造函数如下:```java public FileUploadExecutorManager(BundleContext bundleContext, ConfigurationContext configCtx, String webContext) throws CarbonException { this.bundleContext = bundleContext; this.configContext = configCtx; this.webContext = webContext; this.loadExecutorMap(); }

root@kitploit:~
构造函数调用私有方法 [`loadExecutorMap()`](https://github.com/wso2/carbon-kernel/blob/d47232dfb2b26c0ef18a74e2ef4aa503caa59697/core/org.wso2.carbon.ui/src/main/java/org/wso2/carbon/ui/transports/fileupload/FileUploadExecutorManager.java#L131),该方法负责加载配置:```java
    private void loadExecutorMap() throws CarbonException {
        [snipped]
                try {
            documentElement = XMLUtils.toOM(serverConfiguration.getDocumentElement());
        } catch (Exception e) {
            String msg = "Unable to read Server Configuration.";
            log.error(msg);
            throw new CarbonException(msg, e);
        }
        [snipped]
        OMElement fileUploadConfigElement =
                documentElement.getFirstChildWithName(
                        new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "FileUploadConfig"));
        for (Iterator iterator = fileUploadConfigElement.getChildElements(); iterator.hasNext();) {
            OMElement mapppingElement = (OMElement) iterator.next();
            if (mapppingElement.getLocalName().equalsIgnoreCase("Mapping")) {
                OMElement actionsElement =
                        mapppingElement.getFirstChildWithName(
                                new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "Actions"));
                String confPath = System.getProperty(CarbonBaseConstants.CARBON_CONFIG_DIR_PATH);
        [snipped]

文件上传格式配置位于XML配置文件中的FileUploadConfig命名空间内,这是默认配置:```xml 100

root@kitploit:~
    <Mapping>
        <Actions>
            <Action>keystore</Action>
            <Action>certificate</Action>
            <Action>*</Action>
        </Actions>
        <Class>org.wso2.carbon.ui.transports.fileupload.AnyFileUploadExecutor</Class>
    </Mapping>

    <Mapping>
        <Actions>
            <Action>jarZip</Action>
        </Actions>
        <Class>org.wso2.carbon.ui.transports.fileupload.JarZipUploadExecutor</Class>
    </Mapping>
    <Mapping>
        <Actions>
            <Action>dbs</Action>
        </Actions>
        <Class>org.wso2.carbon.ui.transports.fileupload.DBSFileUploadExecutor</Class>
    </Mapping>
    <Mapping>
        <Actions>
            <Action>tools</Action>
        </Actions>
        <Class>org.wso2.carbon.ui.transports.fileupload.ToolsFileUploadExecutor</Class>
    </Mapping>
    <Mapping>
        <Actions>
            <Action>toolsAny</Action>
        </Actions>
        <Class>org.wso2.carbon.ui.transports.fileupload.ToolsAnyFileUploadExecutor</Class>
    </Mapping>
</FileUploadConfig>
root@kitploit:~
`loadExecutorMap()` 方法会创建并填充一个 `<Action, Class>` 类型的 `HashMap`,其中包含从配置文件中提取的 Actions 和 Classes。该映射将在后续用于选择适当的类来处理特定格式/动作。

之后,当 `/fileupload` 路由收到 POST 请求时,会调用 Servlet 的 [`doPost()`](https://github.com/wso2/carbon-kernel/blob/d47232dfb2b26c0ef18a74e2ef4aa503caa59697/core/org.wso2.carbon.ui/src/main/java/org/wso2/carbon/ui/transports/FileUploadServlet.java#L53) 方法。该方法仅将请求和响应对象转发给 `fileUploadExecutorManager` 的 `execute()` 方法,而 `fileUploadExecutorManager` 是在 `init()` 中初始化的。```java
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response) throws ServletException, IOException {

        try {
            fileUploadExecutorManager.execute(request, response);
        } catch (Exception e) {
            String msg = "File upload failed ";
            log.error(msg, e);
            throw new ServletException(e);
        }
    }

execute() 方法在请求 URL 中 fileupload/ 字符串之后进行分割,这意味着它提取请求 URL 中 /fileupload/ 之后的部分,并将其赋值给 actionString。```java public boolean execute(HttpServletRequest request, HttpServletResponse response) throws IOException {

root@kitploit:~
    HttpSession session = request.getSession();
    String cookie = (String) session.getAttribute(ServerConstants.ADMIN_SERVICE_COOKIE);
    request.setAttribute(CarbonConstants.ADMIN_SERVICE_COOKIE, cookie);
    request.setAttribute(CarbonConstants.WEB_CONTEXT, webContext);
    request.setAttribute(CarbonConstants.SERVER_URL,
                         CarbonUIUtil.getServerURL(request.getSession().getServletContext(),
                                                   request.getSession()));


    String requestURI = request.getRequestURI();

    //TODO - fileupload is hardcoded
    int indexToSplit = requestURI.indexOf("fileupload/") + "fileupload/".length();
    String actionString = requestURI.substring(indexToSplit);

    // Register execution handlers
    FileUploadExecutionHandlerManager execHandlerManager =
            new FileUploadExecutionHandlerManager();
    CarbonXmlFileUploadExecHandler carbonXmlExecHandler =
            new CarbonXmlFileUploadExecHandler(request, response, actionString);
    execHandlerManager.addExecHandler(carbonXmlExecHandler);
    OSGiFileUploadExecHandler osgiExecHandler =
            new OSGiFileUploadExecHandler(request, response);
    execHandlerManager.addExecHandler(osgiExecHandler);
    AnyFileUploadExecHandler anyFileExecHandler =
            new AnyFileUploadExecHandler(request, response);
    execHandlerManager.addExecHandler(anyFileExecHandler);
    execHandlerManager.startExec();
    return true;
}
root@kitploit:~
`actionString` 被传递给 `CarbonXmlFileUploadExecHandler` 类的构造函数,同时传入 `request` 和 `response`:```java
        private CarbonXmlFileUploadExecHandler(HttpServletRequest request,
                                               HttpServletResponse response,
                                               String actionString) {
            this.request = request;
            this.response = response;
            this.actionString = actionString;
        }

构造函数会将它们保存到其属性中。

之后,carbonXmlExecHandler 对象和其他对象将通过 addExecHandler() 方法添加到 execHandlerManager。```java public void addExecHandler(FileUploadExecutionHandler handler) { if (prevHandler != null) { prevHandler.setNext(handler); } else { firstHandler = handler; } prevHandler = handler; }

root@kitploit:~
然后调用 `execHandlerManager.startExec()`:```java
        public void startExec() throws IOException {
            firstHandler.execute();
        }

startExec() 调用第一个添加的对象(即 CarbonXmlFileUploadExecHandler)的 execute() 方法:```java public void execute() throws IOException { boolean foundExecutor = false; for (String key : executorMap.keySet()) { if (key.equals(actionString)) { AbstractFileUploadExecutor obj = executorMap.get(key); foundExecutor = true; obj.executeGeneric(request, response, configContext); break; } } if (!foundExecutor) { next(); } }

root@kitploit:~
[`execute()`](https://github.com/wso2/carbon-kernel/blob/d47232dfb2b26c0ef18a74e2ef4aa503caa59697/core/org.wso2.carbon.ui/src/main/java/org/wso2/carbon/ui/transports/fileupload/FileUploadExecutorManager.java#L430) 遍历先前创建的 `<Action, Class>` 的 `HashMap`,找到与 `actionString` 相等的 Action(键),如果找到,则调用与该 Action 关联的对象的 `executeGeneric()` 方法。

回顾一下,默认配置有 7 个动作,分别是:
* `keystore`、`certificate`、`*` 由 `org.wso2.carbon.ui.transports.fileupload.AnyFileUploadExecutor` 处理
* `jarZip` 由 `org.wso2.carbon.ui.transports.fileupload.JarZipUploadExecutor` 处理
* `dbs` 由 `org.wso2.carbon.ui.transports.fileupload.DBSFileUploadExecutor` 处理
* `tools` 由 `org.wso2.carbon.ui.transports.fileupload.ToolsFileUploadExecutor` 处理
* `toolsAny` 由 `org.wso2.carbon.ui.transports.fileupload.ToolsAnyFileUploadExecutor` 处理

每个对象处理上传的方式不同,有些接受特定扩展名。

我发现存在任意文件写入漏洞的第一个对象是 `toolsAny`([`ToolsAnyFileUploadExecutor`](https://github.com/wso2/carbon-kernel/blob/4.4.x/core/org.wso2.carbon.ui/src/main/java/org/wso2/carbon/ui/transports/fileupload/ToolsAnyFileUploadExecutor.java))。
`ToolsAnyFileUploadExecutor` 没有 `executeGeneric()` 方法,但它继承了 [`AbstractFileUploadExecutor`](https://github.com/wso2/carbon-kernel/blob/d47232dfb2b26c0ef18a74e2ef4aa503caa59697/core/org.wso2.carbon.ui/src/main/java/org/wso2/carbon/ui/transports/fileupload/AbstractFileUploadExecutor.java#L61),而该类确实有一个 [`executeGeneric()`](https://github.com/wso2/carbon-kernel/blob/d47232dfb2b26c0ef18a74e2ef4aa503caa59697/core/org.wso2.carbon.ui/src/main/java/org/wso2/carbon/ui/transports/fileupload/AbstractFileUploadExecutor.java#L97) 方法:```java
    boolean executeGeneric(HttpServletRequest request,
                           HttpServletResponse response,
                           ConfigurationContext configurationContext) throws IOException {//,
        //    CarbonException {
        this.configurationContext = configurationContext;
        try {
            parseRequest(request);
            return execute(request, response);
        } catch (FileUploadFailedException e) {
            sendErrorRedirect(request, response, e);
        } catch (FileSizeLimitExceededException e) {
            sendErrorRedirect(request, response, e);
        } catch (CarbonException e) {
            sendErrorRedirect(request, response, e);
        }
        return false;
    }

executeGeneric() 首先调用 parseRequest(),并将请求对象作为参数传入。```java protected void parseRequest(HttpServletRequest request) throws FileUploadFailedException, FileSizeLimitExceededException { fileItemsMap.set(new HashMap<String, ArrayList>()); formFieldsMap.set(new HashMap<String, ArrayList>());

root@kitploit:~
    ServletRequestContext servletRequestContext = new ServletRequestContext(request);
    boolean isMultipart = ServletFileUpload.isMultipartContent(servletRequestContext);
    Long totalFileSize = 0L;

    if (isMultipart) {

        List items;
        try {
            items = parseRequest(servletRequestContext);
        } catch (FileUploadException e) {
            String msg = "File upload failed";
            log.error(msg, e);
            throw new FileUploadFailedException(msg, e);
        }
        boolean multiItems = false;
        if (items.size() > 1) {
            multiItems = true;
        }

        // Add the uploaded items to the corresponding maps.
        for (Iterator iter = items.iterator(); iter.hasNext();) {
            FileItem item = (FileItem) iter.next();
            String fieldName = item.getFieldName().trim();
            if (item.isFormField()) {
                if (formFieldsMap.get().get(fieldName) == null) {
                    formFieldsMap.get().put(fieldName, new ArrayList<String>());
                }
                try {
                    formFieldsMap.get().get(fieldName).add(new String(item.get(), "UTF-8"));
                } catch (UnsupportedEncodingException ignore) {
                }
            } else {
                String fileName = item.getName();
                if ((fileName == null || fileName.length() == 0) && multiItems) {
                    continue;
                }
                if (fileItemsMap.get().get(fieldName) == null) {
                    fileItemsMap.get().put(fieldName, new ArrayList<FileItemData>());
                }
                totalFileSize += item.getSize();
                if (totalFileSize < totalFileUploadSizeLimit) {
                    fileItemsMap.get().get(fieldName).add(new FileItemData(item));
                } else {
                    throw new FileSizeLimitExceededException(getFileSizeLimit() / 1024 / 1024);
                }
            }
        }
    }
}
root@kitploit:~
它首先确保POST请求是一个多部分POST请求,然后提取上传的文件,确保POST请求至少包含一个上传文件,并验证其是否超过最大文件大小。

在从`parseRequest()`返回后,`executeGeneric()`现在将调用`execute()`方法,该方法被`ToolsAnyFileUploadExecutor`[覆盖](https://github.com/wso2/carbon-kernel/blob/d47232dfb2b26c0ef18a74e2ef4aa503caa59697/core/org.wso2.carbon.ui/src/main/java/org/wso2/carbon/ui/transports/fileupload/ToolsAnyFileUploadExecutor.java#L36):```java
	@Override
	public boolean execute(HttpServletRequest request,
			HttpServletResponse response) throws CarbonException, IOException {
		PrintWriter out = response.getWriter();
        try {
        	Map fileResourceMap =
                (Map) configurationContext
                        .getProperty(ServerConstants.FILE_RESOURCE_MAP);
        	if (fileResourceMap == null) {
        		fileResourceMap = new TreeBidiMap();
        		configurationContext.setProperty(ServerConstants.FILE_RESOURCE_MAP,
                                             fileResourceMap);
        	}
            List<FileItemData> fileItems = getAllFileItems();
            //String filePaths = "";

            for (FileItemData fileItem : fileItems) {
                String uuid = String.valueOf(
                        System.currentTimeMillis() + Math.random());
                String serviceUploadDir =
                        configurationContext
                                .getProperty(ServerConstants.WORK_DIR) +
                                File.separator +
                                "extra" + File
                                .separator +
                                uuid + File.separator;
                File dir = new File(serviceUploadDir);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                File uploadedFile = new File(dir, fileItem.getFileItem().getFieldName());
                try (FileOutputStream fileOutStream = new FileOutputStream(uploadedFile)) {
                    fileItem.getDataHandler().writeTo(fileOutStream);
                    fileOutStream.flush();
                }
                response.setContentType("text/plain; charset=utf-8");
                //filePaths = filePaths + uploadedFile.getAbsolutePath() + ",";
                fileResourceMap.put(uuid, uploadedFile.getAbsolutePath());
                out.write(uuid);
            }
            //filePaths = filePaths.substring(0, filePaths.length() - 1);
            //out.write(filePaths);
            out.flush();
        } catch (Exception e) {
            log.error("File upload FAILED", e);
            out.write("<script type=\"text/javascript\">" +
                    "top.wso2.wsf.Util.alertWarning('File upload FAILED. File may be non-existent or invalid.');" +
                    "</script>");
        } finally {
            out.close();
        }
        return true;
	}

这里正是漏洞所在:execute() 方法存在路径遍历漏洞,因为它信任 POST 请求中用户提供的文件名。如果不进行路径遍历转义,文件实际上会被保存到临时目录之外:``` ./tmp/work/extra/$uuid/$filename

root@kitploit:~
响应中返回了 `uuid`:

![image](https://assets.kitploit.com/production/public/readmes/26314/46ab2e31428a23ca42c93423c5c2a7508cb72c7e508d0a7e5405a35c27a4f619.png)

文件可以在以下位置找到:

![image](https://assets.kitploit.com/production/public/readmes/26314/49f94872497ef3ce08545f18bf26a84e60729c3ad0faef81819d2af8adf44f1d.png)

现在我们需要逃逸出 `tmp` 目录,并将我们的 JSP shell 添加到 WSO2 提供服务的某个位置。

让我们找到 tomcat 的 `appBase` 目录:

![image](https://assets.kitploit.com/production/public/readmes/26314/3e23eb3dd832cc9284d45d0a0339a5bf7ba1ec1bf2f26eaae67f23869488d04b.png)

该目录是部署在 tomcat 上的应用程序的位置,其中包含多个已部署的 WAR 应用程序以及它们的原始 WAR 文件:```
./repository/deployment/server/webapps

image

其中一个应用程序是 authenticationendpoint(//host/authenticationendpoint),它处理对 WSO2 的认证,其位置为:``` ./repository/deployment/server/webapps/authenticationendpoint

root@kitploit:~
![image](https://assets.kitploit.com/production/public/readmes/26314/bef03f1fdec5ff102076e81cf806e914bc733cadf836d8eb2fe7f80261ea70d7.png)

**注意:** 我们也可以利用该漏洞在 `appBase` 目录下创建自己的新目录(上下文路径),并且该目录会被自动部署,但我只使用一个并继续使用 `authenticationendpoint`。

# PoC
* 使用 Burpsuite:

![image](https://assets.kitploit.com/production/public/readmes/26314/f18951e9acbdfdbd282f6c83037d849069b2225774598bd70d1a135ff83bdc1c.png)

![image](https://assets.kitploit.com/production/public/readmes/26314/b513180803a0562bebfc66ea9dca8572ea6f9f8bc63a898c4e32cb811b0c2ebc.png)

![image](https://assets.kitploit.com/production/public/readmes/26314/0d02bf7c174a316d5acbdc73c245cefc1a9ee00d54887a3306f6a897708474f5.png)

* 使用 exploiy.py:

> 用法: 
> ```
> python3 exploit.py https://host:9443/ ArbitraryShellName.jsp
> ```
![poc](https://assets.kitploit.com/production/public/readmes/26314/01a463e5871c7a232eca86d8d7afafecfc687621989f4033c7204965fb933a47.gif)
下载工具