Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2022-29464 | Kitploit
Herramientas/GitHubGitHub/devengpk/cve-2022-29464
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónRed TeamingDesarrollo de Payloads
GitHubdevengpk/cve-2022-29464

CVE-2022-29464

Ver Repositorio
1hace 3 añosAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Exploit y writeup de WSO2 RCE (CVE-2022-29464).

Detalles

CVE-2022-29464 es una falla grave en WSO2. La falla es una carga arbitraria de archivos no autorizada y sin restricciones que permite a usuarios no autorizados subir archivos JSP maliciosos a los servidores WSO2 y obtener ejecución remota de código (RCE).

La ruta de carga vulnerable es /fileupload, manejada por el FileUploadServlet servlet. Es una ruta no protegida por IAM, como se puede ver en el archivo de configuración indentity.xml:```xml

root@kitploit:~
La función 'handleSecurity()' se encarga de proteger las distintas rutas proporcionadas por WSO2 y ofrece un mecanismo para realizar comprobaciones de seguridad en las solicitudes HTTP recibidas. 'handleSecurity()' llamará a 'CarbonUILoginUtil.handleLoginPageRequest(),' y, en función de su valor de retorno, se decidirá si se concede o se deniega el acceso a la URI solicitada:```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() devuelve CarbonUILoginUtil.RETURN_TRUE cuando la ruta es /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()` devolviendo `CarbonUILoginUtil.RETURN_TRUE`, `handleSecurity()` devolverá `true`, y el acceso a `/fileupload` será entonces concedido sin autenticación.

el servlet `FileUploadServlet` y, tras [`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) y a través de una serie de llamadas a métodos, carga finalmente del archivo de configuración `carbon.xml` múltiples formatos/acciones de subida de archivos junto con el objeto que maneja cada 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);
        }
    }

el constructor de la clase FileUploadExecutorManager es el siguiente:```java public FileUploadExecutorManager(BundleContext bundleContext, ConfigurationContext configCtx, String webContext) throws CarbonException { this.bundleContext = bundleContext; this.configContext = configCtx; this.webContext = webContext; this.loadExecutorMap(); }

root@kitploit:~
el constructor llama al método privado [`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) que es donde se realiza la carga de la configuración:```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]

las configuraciones de formatos de carga de archivos se encuentran dentro del namespace FileUploadConfig en el archivo de configuración XML, esta es la configuración predeterminada:```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:~
El método `loadExecutorMap()` crea y rellena un `HashMap` de `<Action, Class>` con las acciones y las clases extraídas del archivo de configuración. Este se utilizará más tarde para elegir qué clase usar para manejar adecuadamente un formato/acción determinado.

Luego, cuando la ruta `/fileupload` recibe una solicitud POST, se llamará al método [`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. El método simplemente reenvía el objeto de solicitud y respuesta al método `execute()` de `fileUploadExecutorManager`, que fue inicializado en `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);
        }
    }

el método execute(), divide la URL de la solicitud justo después de la cadena fileupload/, lo que significa que extrae todo lo que esté después de /fileupload/ en la URL de la solicitud y lo asigna 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:~
el `actionString` se pasa al constructor de la clase `CarbonXmlFileUploadExecHandler` junto con `request` y `response`:```java
        private CarbonXmlFileUploadExecHandler(HttpServletRequest request,
                                               HttpServletResponse response,
                                               String actionString) {
            this.request = request;
            this.response = response;
            this.actionString = actionString;
        }

el constructor los guardará en sus propiedades.

después de eso, el objeto carbonXmlExecHandler junto con otros objetos se añadirá a execHandlerManager mediante el método addExecHandler().```java public void addExecHandler(FileUploadExecutionHandler handler) { if (prevHandler != null) { prevHandler.setNext(handler); } else { firstHandler = handler; } prevHandler = handler; }

root@kitploit:~
entonces se llama a `execHandlerManager.startExec()`:```java
        public void startExec() throws IOException {
            firstHandler.execute();
        }

startExec() llama a execute() del primer objeto añadido, que es 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) recorre el `HashMap` de `<Action, Class>` creado anteriormente y encuentra la Action (clave) que es igual a `actionString`; si la encuentra, se llamará al método `executeGeneric()` del objeto asociado con esa Action.

para revisar la configuración predeterminada tiene 7 acciones, que son:
* `keystore`, `certificate`, `*` gestionadas por `org.wso2.carbon.ui.transports.fileupload.AnyFileUploadExecutor`
* `jarZip` gestionada por `org.wso2.carbon.ui.transports.fileupload.JarZipUploadExecutor`
* `dbs` gestionada por `org.wso2.carbon.ui.transports.fileupload.DBSFileUploadExecutor`
* `tools` gestionada por `org.wso2.carbon.ui.transports.fileupload.ToolsFileUploadExecutor`
* `toolsAny` gestionada por `org.wso2.carbon.ui.transports.fileupload.ToolsAnyFileUploadExecutor`

cada uno de estos objetos maneja la subida de manera diferente; algunos de ellos aceptan extensiones específicas.

el primero que encontré vulnerable a escritura arbitraria de archivos fue `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` no tiene un método `executeGeneric()`, pero extiende [`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), que sí tiene un método [`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() llama primero a parseRequest() con el objeto de solicitud como parámetro:```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:~
primero se asegura de que la solicitud POST sea una solicitud POST multiparte, y luego extrae los archivos subidos, se asegura de que la solicitud POST contenga al menos un archivo subido y lo valida contra el tamaño máximo de archivo.

después de regresar de `parseRequest()`, `executeGeneric()` llamará ahora al método `execute()` que está [anulado](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) por `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;
	}

Aquí es donde radica el bug, el método execute() es vulnerable a una vulnerabilidad de path traversal, ya que confía en el nombre de archivo dado por el usuario en la solicitud POST. Sin que el path traversal escape del directorio tmp, el archivo se guarda realmente en:``` ./tmp/work/extra/$uuid/$filename

root@kitploit:~
con `uuid` devuelto en la respuesta:

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

el archivo se puede encontrar en:

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

Ahora solo necesitamos salir del directorio `tmp` y añadir nuestro shell JSP a alguna ubicación servida por el WSO2.

busquemos el directorio `appBase` de tomcat:

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

este directorio es la ubicación de las aplicaciones que están desplegadas en tomcat, contiene múltiples aplicaciones WAR ya desplegadas y también sus archivos WAR originales:```
./repository/deployment/server/webapps

image

una de esas aplicaciones es authenticationendpoint (//host/authenticationendpoint) que maneja la autenticación hacia WSO2 y su ubicación es:``` ./repository/deployment/server/webapps/authenticationendpoint

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

**NOTA:** también podemos usar la vulnerabilidad para crear nuestro propio directorio nuevo (ruta de contexto) en el directorio `appBase` y se desplegará automáticamente, pero solo llevaré uno y usaré `authenticationendpoint`.
# PoC
* Usando Burpsuite:

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

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

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

* Usando exploiy.py:

> Uso: 
> ```
> python3 exploit.py https://host:9443/ ArbitraryShellName.jsp
> ```
![poc](https://assets.kitploit.com/production/public/readmes/24643/01a463e5871c7a232eca86d8d7afafecfc687621989f4033c7204965fb933a47.gif)
Descargar herramienta