Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2022-29464 — WSO2 RCE (CVE-2022-29464) exploit e writeup. | Kitploit
Strumenti/GitHubGitHub/hakivvi/cve-2022-29464
Generazione di PayloadAnalisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebPenetration TestingRed Teaming
GitHubhakivvi/cve-2022-29464

CVE-2022-29464

WSO2 RCE (CVE-2022-29464) exploit e writeup.

Vedi Repository
378884 anni faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2022-29464

WSO2 RCE (CVE-2022-29464) exploit e writeup.

Dettagli

CVE-2022-29464 è una vulnerabilità critica su WSO2 scoperta da Orange Tsai. La vulnerabilità è un upload arbitrario di file non autenticato e senza restrizioni che consente a un attaccante non autenticato di ottenere RCE sui server WSO2 caricando file JSP malevoli.

Il percorso di upload vulnerabile è /fileupload, gestito dal servlet FileUploadServlet. È un percorso non protetto da IAM, come possiamo vedere nel file di configurazione indentity.xml:```xml

root@kitploit:~
E inoltre non protetta dalla misura di login predefinita, `handleSecurity()` è la funzione responsabile della protezione delle diverse route servite da WSO2 e fornisce un meccanismo per eseguire controlli di sicurezza sulle richieste HTTP ricevute; `handleSecurity()` chiamerà `CarbonUILoginUtil.handleLoginPageRequest()` e in base al suo valore di ritorno verrà deciso se consentire o negare l'accesso all'URI richiesto:```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() restituisce CarbonUILoginUtil.RETURN_TRUE quando la route è /fileupload:```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:~
con `CarbonUILoginUtil.handleLoginPageRequest()` che restituisce `CarbonUILoginUtil.RETURN_TRUE`, `handleSecurity()` restituirà `true`, quindi l'accesso a `/fileupload` sarà concesso senza autenticazione.

il servlet `FileUploadServlet` e, tramite [`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) e una serie di chiamate a metodi, carica infine dal file di configurazione `carbon.xml` più formati/azioni di upload di file insieme all'oggetto che gestisce ogni formato.```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);
        }
    }

il costruttore della classe FileUploadExecutorManager è il seguente:```java public FileUploadExecutorManager(BundleContext bundleContext, ConfigurationContext configCtx, String webContext) throws CarbonException { this.bundleContext = bundleContext; this.configContext = configCtx; this.webContext = webContext; this.loadExecutorMap(); }

root@kitploit:~
il costruttore chiama il metodo privato [`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) che è dove viene eseguito il caricamento della configurazione:```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]

le configurazioni dei formati di caricamento dei file si trovano all'interno dello spazio dei nomi FileUploadConfig nel file di configurazione XML, questa è la configurazione predefinita:```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:~
il metodo `loadExecutorMap()` crea e riempie una `HashMap` di `<Action, Class>` con le Action e le Classi estratte dal file di configurazione. che verrà poi utilizzata per scegliere quale classe usare per gestire correttamente un determinato formato/azione.

Successivamente, quando la rotta `/fileupload` riceve una richiesta POST, verrà chiamato il metodo [`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) del servlet. Il metodo si limita a inoltrare gli oggetti request e response al metodo `execute()` di `fileUploadExecutorManager`, che è stato inizializzato in `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);
        }
    }

il metodo execute(), divide l'URL della richiesta subito dopo la stringa fileupload/, il che significa che estrae tutto ciò che viene dopo /fileupload/ nell'URL della richiesta e lo assegna a 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:~
l'`actionString` viene passato al costruttore della classe `CarbonXmlFileUploadExecHandler` insieme a `request` e `response`:```java
        private CarbonXmlFileUploadExecHandler(HttpServletRequest request,
                                               HttpServletResponse response,
                                               String actionString) {
            this.request = request;
            this.response = response;
            this.actionString = actionString;
        }

il costruttore li salverà nelle sue proprietà.

dopo di che l'oggetto carbonXmlExecHandler insieme ad altri oggetti verrà aggiunto a execHandlerManager usando il metodo addExecHandler().```java public void addExecHandler(FileUploadExecutionHandler handler) { if (prevHandler != null) { prevHandler.setNext(handler); } else { firstHandler = handler; } prevHandler = handler; }

root@kitploit:~
quindi viene chiamato `execHandlerManager.startExec()`:```java
        public void startExec() throws IOException {
            firstHandler.execute();
        }

startExec() chiama execute() del primo oggetto aggiunto, che è CarbonXmlFileUploadExecHandler:```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) scorre la `HashMap` di `<Action, Class>` creata in precedenza e trova l'Action (chiave) uguale a `actionString`, se la trova viene chiamato il metodo `executeGeneric()` dell'oggetto associato a quell'Action.

Per ricapitolare, la configurazione predefinita ha 7 azioni, che sono:
* `keystore`, `certificate`, `*` gestiti da `org.wso2.carbon.ui.transports.fileupload.AnyFileUploadExecutor`
* `jarZip` gestito da `org.wso2.carbon.ui.transports.fileupload.JarZipUploadExecutor`
* `dbs` gestito da `org.wso2.carbon.ui.transports.fileupload.DBSFileUploadExecutor`
* `tools` gestito da `org.wso2.carbon.ui.transports.fileupload.ToolsFileUploadExecutor`
* `toolsAny` gestito da `org.wso2.carbon.ui.transports.fileupload.ToolsAnyFileUploadExecutor`

ciascuno di questi oggetti gestisce l'upload in modo diverso, alcuni di essi accettano estensioni specifiche.

il primo che ho trovato vulnerabile alla scrittura arbitraria di file è stato `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` non ha un metodo `executeGeneric()` ma estende [`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) che ha un metodo [`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() chiama prima parseRequest() con l'oggetto request come parametro:```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:~
prima assicura che la richiesta POST sia una richiesta POST multipart, e poi estrae i file caricati, assicura che la richiesta POST contenga almeno un file caricato e lo valida rispetto alla dimensione massima del file.

dopo il ritorno da `parseRequest()`, `executeGeneric()` chiamerà ora il metodo `execute()` che è [sovrascritto](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) da `ToolsAnyFileUploadExecutor`:```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;
	}

È qui che risiede il bug, il metodo execute() è vulnerabile a una vulnerabilità di path traversal poiché si fida del nome file fornito dall'utente nella richiesta POST. senza che la path traversal esca dalla directory tmp il file viene effettivamente salvato in:``` ./tmp/work/extra/$uuid/$filename

root@kitploit:~
con `uuid` restituito nella risposta:

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

il file può essere trovato in:

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

Ora dobbiamo solo uscire dalla directory `tmp` e aggiungere la nostra shell JSP in una posizione servita da WSO2.

Cerchiamo la directory `appBase` di tomcat:

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

questa directory è la posizione delle applicazioni distribuite su tomcat; contiene diverse applicazioni WAR già distribuite e anche i loro file WAR grezzi:```
./repository/deployment/server/webapps

image

una di queste applicazioni è authenticationendpoint (//host/authenticationendpoint) che gestisce l'autenticazione a WSO2 e la sua posizione è:``` ./repository/deployment/server/webapps/authenticationendpoint

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

**NOTA:** possiamo anche usare la vulnerabilità per creare una nostra nuova directory (percorso del contesto) nella directory `appBase` e verrà distribuita automaticamente, ma io ne prenderò semplicemente una e userò `authenticationendpoint`.
# PoC
* Utilizzo di 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)

* Utilizzo di exploiy.py:

> Utilizzo:
> ```
> python3 exploit.py https://host:9443/ ArbitraryShellName.jsp
> ```
![poc](https://assets.kitploit.com/production/public/readmes/26314/01a463e5871c7a232eca86d8d7afafecfc687621989f4033c7204965fb933a47.gif)
Scarica lo strumento