
Exploit pour CVE-2022-29464, une vulnérabilité de téléchargement de fichier arbitraire non authentifié dans les serveurs WSO2, permettant l'exécution de code à distance via le téléchargement d'un fichier JSP malveillant.
Exploit RCE WSO2 (CVE-2022-29464).
CVE-2022-29464 est une vulnérabilité critique sur WSO2 découverte par Orange Tsai. La vulnérabilité est un téléchargement de fichier arbitraire non authentifié et sans restriction qui permet aux attaquants non authentifiés d'obtenir une RCE sur les serveurs WSO2 en téléchargeant des fichiers JSP malveillants.
La route de téléchargement vulnérable est /fileupload qui est gérée par FileUploadServlet qui est une route non protégée comme on peut le voir dans le fichier de configuration indentity.xml :```xml
le `FileUploadServlet` et lors de [`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) et à travers une série d'appels de méthodes charge finalement à partir du fichier de configuration `carbon.xml` plusieurs formats/actions de téléversement ainsi que l'objet qui gère chaque format.```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);
}
}
le constructeur de la classe FileUploadExecutorManager est le suivant :```java
public FileUploadExecutorManager(BundleContext bundleContext,
ConfigurationContext configCtx,
String webContext) throws CarbonException {
this.bundleContext = bundleContext;
this.configContext = configCtx;
this.webContext = webContext;
this.loadExecutorMap();
}
le constructeur appelle la méthode privée [`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) qui est l'endroit où le chargement de la configuration est effectué :```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]
Les configurations des formats de téléchargement de fichiers se trouvent dans l'espace de noms FileUploadConfig dans le fichier de configuration XML, voici la configuration par défaut :```xml
100
<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>
la méthode `loadExecutorMap()` crée et remplit une `HashMap` de `<Action, Class>` avec les Actions et les Classes extraites du fichier de configuration. ce qui sera utilisé plus tard pour choisir la classe à utiliser pour gérer correctement un format/action donné.
Plus tard, lorsque la route `/fileupload` reçoit une requête POST, la méthode [`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) du servlet sera appelée. la méthode transfère simplement les objets requête et réponse à la méthode `execute()` de `fileUploadExecutorManager` qui a été initialisée dans `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);
}
}
la méthode execute(), divise l'URL de la requête juste après la chaîne fileupload/, ce qui signifie qu'elle extrait tout ce qui se trouve après le /fileupload/ dans l'URL de la requête et l'assigne à actionString.```java
public boolean execute(HttpServletRequest request,
HttpServletResponse response) throws IOException {
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);