Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2022-29464 — WSO2 서버에서 악성 JSP 업로드를 통해 원격 코드 실행으로 이어지는 인증되지 않은 임의 파일 업로드인 CVE-2022-29464에 대한 익스플로잇 및 기술 문서입니다. | Kitploit
도구/GitHubGitHub/amit-pathak009/cve-2022-29464
Payload GenerationVulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingRed Teaming
GitHubamit-pathak009/cve-2022-29464

CVE-2022-29464

WSO2 서버에서 악성 JSP 업로드를 통해 원격 코드 실행으로 이어지는 인증되지 않은 임의 파일 업로드인 CVE-2022-29464에 대한 익스플로잇 및 기술 문서입니다.

저장소 보기
14년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2022-29464

WSO2 RCE (CVE-2022-29464) 익스플로잇 및 분석.

세부 사항

CVE-2022-29464는 Orange Tsai가 발견한 WSO2의 치명적인 취약점입니다. 이 취약점은 인증되지 않은 무제한 임의 파일 업로드로, 인증되지 않은 공격자가 악성 JSP 파일을 업로드하여 WSO2 서버에서 RCE를 얻을 수 있습니다.

취약한 업로드 경로는 /fileupload이며, 이는 FileUploadServlet 서블릿에 의해 처리됩니다. 그리고 이것은 indentity.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]
    }

CarbonUILoginUtil.handleLoginPageRequest()는 경로가 /fileupload일 때 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` 서블릿은 [`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:~
생성자는 구성 로딩이 수행되는 private 메서드 [`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`을 생성하고 채웁니다. 이 맵은 나중에 주어진 형식/액션을 적절히 처리하기 위해 어떤 클래스를 사용할지 선택하는 데 사용됩니다.

이후 `/fileupload` 경로가 POST 요청을 수신하면 서블릿의 [`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) 메서드가 호출됩니다. 이 메서드는 요청과 응답 객체를 `init()`에서 초기화된 `fileUploadExecutorManager`의 `execute()` 메서드로 전달하기만 합니다.```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() 메서드는 fileupload/ 문자열 바로 다음에서 요청 URL을 분할합니다. 즉, 요청 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`은 `request` 및 `response`와 함께 `CarbonXmlFileUploadExecHandler` 클래스 생성자에 전달됩니다:```java
        private CarbonXmlFileUploadExecHandler(HttpServletRequest request,
                                               HttpServletResponse response,
                                               String actionString) {
            this.request = request;
            this.response = response;
            this.actionString = actionString;
        }

생성자는 그것들을 자신의 속성에 저장합니다.

그 후 carbonXmlExecHandler 객체는 다른 객체들과 함께 execHandlerManager를 사용하여 addExecHandler() 메서드로 추가됩니다.```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`에 의해 처리됨

각각의 객체는 업로드를 다르게 처리하며, 일부는 특정 확장자를 허용합니다.

arbitrary file write에 취약한 첫 번째 객체는 `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()`은 이제 `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) `execute()` 메서드를 호출합니다:```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/22623/46ab2e31428a23ca42c93423c5c2a7508cb72c7e508d0a7e5405a35c27a4f619.png)

파일은 다음 위치에서 찾을 수 있습니다:

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

이제 `tmp` 디렉터리를 벗어나 WSO2에서 서비스되는 위치에 JSP 셸을 추가하기만 하면 됩니다.

tomcat의 `appBase` 디렉터리를 찾아봅시다:

![image](https://assets.kitploit.com/production/public/readmes/22623/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/22623/bef03f1fdec5ff102076e81cf806e914bc733cadf836d8eb2fe7f80261ea70d7.png)

**참고:** 취약점을 사용하여 `appBase` 디렉토리에 새로운 디렉토리(컨텍스트 경로)를 생성할 수도 있으며, 그러면 자동으로 배포됩니다. 하지만 여기서는 하나만 사용하겠습니다. `authenticationendpoint`를 사용합니다.
# PoC
* Burpsuite 사용:

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

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

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

* exploiy.py 사용:

> 사용법:
> ```
> python3 exploit.py https://host:9443/ ArbitraryShellName.jsp
> ```
![poc](https://assets.kitploit.com/production/public/readmes/22623/01a463e5871c7a232eca86d8d7afafecfc687621989f4033c7204965fb933a47.gif)
도구 다운로드