Land heyder#2, Refactor namespaces

This commit is contained in:
Spencer McIntyre 2023-02-14 16:44:29 -05:00
commit 5d254cc36b
16 changed files with 200 additions and 160 deletions

View File

@ -8,11 +8,13 @@ module Msf
module Gitlab module Gitlab
include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HTTP::Gitlab::AccessTokens include Msf::Exploit::Remote::HTTP::Gitlab::AccessTokens
include Msf::Exploit::Remote::HTTP::Gitlab::Authenticate
include Msf::Exploit::Remote::HTTP::Gitlab::Error include Msf::Exploit::Remote::HTTP::Gitlab::Error
include Msf::Exploit::Remote::HTTP::Gitlab::Form
include Msf::Exploit::Remote::HTTP::Gitlab::Groups include Msf::Exploit::Remote::HTTP::Gitlab::Groups
include Msf::Exploit::Remote::HTTP::Gitlab::Helpers include Msf::Exploit::Remote::HTTP::Gitlab::Helpers
include Msf::Exploit::Remote::HTTP::Gitlab::Import include Msf::Exploit::Remote::HTTP::Gitlab::Import
include Msf::Exploit::Remote::HTTP::Gitlab::Signin include Msf::Exploit::Remote::HTTP::Gitlab::Rest
include Msf::Exploit::Remote::HTTP::Gitlab::Version include Msf::Exploit::Remote::HTTP::Gitlab::Version
def initialize(info = {}) def initialize(info = {})

View File

@ -2,53 +2,6 @@
# GitLab Access Tokens mixin # GitLab Access Tokens mixin
module Msf::Exploit::Remote::HTTP::Gitlab::AccessTokens module Msf::Exploit::Remote::HTTP::Gitlab::AccessTokens
# Create Gitlab access access token include Msf::Exploit::Remote::HTTP::Gitlab::Form::AccessTokens
# include Msf::Exploit::Remote::HTTP::Gitlab::Rest::V4::AccessTokens
# @return [String,nil] Gitlab personal access token if created, nil otherwise
def gitlab_create_personal_access_token
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/-/profile/personal_access_tokens'),
'keep_cookies' => true,
'vars_post' => {
'personal_access_token[name]' => Rex::Text.rand_text_alphanumeric(8),
'personal_access_token[expires_at]' => '',
'personal_access_token[scopes][]' => 'api',
'commit' => 'Create personal access token'
},
'headers' => {
'X-CSRF-Token' => gitlab_helper_extract_csrf_token(path: '/-/profile/personal_access_tokens', regex: /name="csrf-token" content="(.*)"/)
}
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError, "Failed to create access token. Unexpected HTTP #{res.code} response." unless res.code == 200
token = JSON.parse(res.body)['new_token']
return token if token
nil
end
# Revoke Gitlab personal access token
#
# @return [nil,GitLabClientError] nil if revoke, Msf::Exploit::Remote::HTTP::Gitlab::GitLabClientError otherwise
def gitlab_revoke_personal_access_token(personal_access_token)
res = send_request_cgi({
'method' => 'DELETE',
'uri' => normalize_uri(target_uri.path, '/api/v4/personal_access_tokens/self'),
'ctype' => 'application/json',
'headers' => {
'PRIVATE-TOKEN' => personal_access_token
}
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError, "Failed to revoke access token. Unexpected HTTP #{res.code} response." unless res.code == 204
nil
end
end end

View File

@ -0,0 +1,5 @@
# -*- coding: binary -*-
module Msf::Exploit::Remote::HTTP::Gitlab::Authenticate
include Msf::Exploit::Remote::HTTP::Gitlab::Form::Authenticate
end

View File

@ -0,0 +1,2 @@
module Msf::Exploit::Remote::HTTP::Gitlab::Form
end

View File

@ -0,0 +1,34 @@
# -*- coding: binary -*-
# Create a Gitlab Access Token via form
module Msf::Exploit::Remote::HTTP::Gitlab::Form::AccessTokens
# Create Gitlab access access token
#
# @return [String,nil] Gitlab personal access token if created, nil otherwise
def gitlab_create_personal_access_token
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/-/profile/personal_access_tokens'),
'keep_cookies' => true,
'vars_post' => {
'personal_access_token[name]' => Rex::Text.rand_text_alphanumeric(8),
'personal_access_token[expires_at]' => '',
'personal_access_token[scopes][]' => 'api',
'commit' => 'Create personal access token'
},
'headers' => {
'X-CSRF-Token' => gitlab_helper_extract_csrf_token(path: '/-/profile/personal_access_tokens', regex: /name="csrf-token" content="(.*)"/)
}
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError, "Failed to create access token. Unexpected HTTP #{res.code} response." unless res.code == 200
token = JSON.parse(res.body)['new_token']
return token if token
nil
end
end

View File

@ -1,7 +1,7 @@
# -*- coding: binary -*- # -*- coding: binary -*-
# GitLab session mixin # GitLab session mixin
module Msf::Exploit::Remote::HTTP::Gitlab::Signin module Msf::Exploit::Remote::HTTP::Gitlab::Form::Authenticate
# performs a gitlab login # performs a gitlab login
# #
# @param user [String] Username # @param user [String] Username
@ -36,7 +36,7 @@ module Msf::Exploit::Remote::HTTP::Gitlab::Signin
# performs a gitlab logout # performs a gitlab logout
# #
# @return [Bolean,GitLabError] True if sign out, Msf::Exploit::Remote::HTTP::Gitlab::Error otherwise # @return [Boolean,GitLabError] True if sign out, Msf::Exploit::Remote::HTTP::Gitlab::Error otherwise
def gitlab_sign_out def gitlab_sign_out
csrf_token = gitlab_helper_extract_csrf_token( csrf_token = gitlab_helper_extract_csrf_token(
path: '/', path: '/',

View File

@ -2,50 +2,5 @@
# GitLab Groups mixin # GitLab Groups mixin
module Msf::Exploit::Remote::HTTP::Gitlab::Groups module Msf::Exploit::Remote::HTTP::Gitlab::Groups
# Create a new group include Msf::Exploit::Remote::HTTP::Gitlab::Rest::V4::Groups
#
# @return [String,nil] Group ID if successful create, nil otherwise
def gitlab_create_group(group_name, api_token)
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/api/v4/groups'),
'ctype' => 'application/json',
'headers' => {
'PRIVATE-TOKEN' => api_token
},
'data' => {
name: group_name, path: group_name, visibility: 'public'
}.to_json
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::GroupError, "Unable to create group. Unexpected HTTP #{res.code} response." if res.code != 201
group = JSON.parse(res.body)
return group if group
nil
end
# Delete a group
#
# @return [Bolean,GitLabClientError] True if successful deleted, Msf::Exploit::Remote::HTTP::Gitlab::GitLabClientError otherwise
def gitlab_delete_group(group_id, api_token)
res = send_request_cgi({
'method' => 'DELETE',
'uri' => normalize_uri('/api/v4/groups', group_id),
'ctype' => 'application/json',
'headers' => {
'PRIVATE-TOKEN' => api_token
}
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::GroupError, "Unable to delete group. Unexpected HTTP #{res.code} response." if res.code != 202
true
end
end end

View File

@ -2,45 +2,5 @@
# GitLab import mixin # GitLab import mixin
module Msf::Exploit::Remote::HTTP::Gitlab::Import module Msf::Exploit::Remote::HTTP::Gitlab::Import
# Import a repository from a remote URL include Msf::Exploit::Remote::HTTP::Gitlab::Rest::V4::Import
#
# @return [String,nil] Import ID if successfully enqueued, nil otherwise
def gitlab_import_github_repo(group_name:, github_hostname:, api_token:)
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/api/v4/import/github'),
'ctype' => 'application/json',
'headers' => {
'PRIVATE-TOKEN' => api_token
},
'data' => {
'personal_access_token' => Rex::Text.rand_text_alphanumeric(8),
'repo_id' => rand(1000),
'target_namespace' => group_name,
'new_name' => "gh-import-#{rand(1000)}",
'github_hostname' => github_hostname
}.to_json
})
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
if res.code == 422
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ImportError, ((res.get_json_document || {})['errors'] || 'Import failed')
end
# 201 is returned if the import was successfully enqueued
unless res.code == 201
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ImportError, ((res.get_json_document || {})['errors'] || 'Import failed')
end
# 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 = res.get_json_document
return body if body
nil
end
end end

View File

@ -0,0 +1,4 @@
# -*- coding: binary -*-
module Msf::Exploit::Remote::HTTP::Gitlab::Rest
end

View File

@ -0,0 +1,2 @@
module Msf::Exploit::Remote::HTTP::Gitlab::Rest::V4
end

View File

@ -0,0 +1,23 @@
# -*- coding: binary -*-
module Msf::Exploit::Remote::HTTP::Gitlab::Rest::V4::AccessTokens
# Revoke a Gitlab access token via the v4 REST api
#
# @return [nil,GitLabClientError] nil if revoke, Msf::Exploit::Remote::HTTP::Gitlab::GitLabClientError otherwise
def gitlab_revoke_personal_access_token(personal_access_token)
res = send_request_cgi({
'method' => 'DELETE',
'uri' => normalize_uri(target_uri.path, '/api/v4/personal_access_tokens/self'),
'ctype' => 'application/json',
'headers' => {
'PRIVATE-TOKEN' => personal_access_token
}
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError, "Failed to revoke access token. Unexpected HTTP #{res.code} response." unless res.code == 204
nil
end
end

View File

@ -0,0 +1,51 @@
# -*- coding: binary -*-
# GitLab Groups mixin
module Msf::Exploit::Remote::HTTP::Gitlab::Rest::V4::Groups
# Create a new group
#
# @return [String,nil] Group ID if successful create, nil otherwise
def gitlab_create_group(group_name, api_token)
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/api/v4/groups'),
'ctype' => 'application/json',
'headers' => {
'PRIVATE-TOKEN' => api_token
},
'data' => {
name: group_name, path: group_name, visibility: 'public'
}.to_json
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::GroupError, "Unable to create group. Unexpected HTTP #{res.code} response." if res.code != 201
group = JSON.parse(res.body)
return group if group
nil
end
# Delete a group
#
# @return [Bolean,GitLabClientError] True if successful deleted, Msf::Exploit::Remote::HTTP::Gitlab::GitLabClientError otherwise
def gitlab_delete_group(group_id, api_token)
res = send_request_cgi({
'method' => 'DELETE',
'uri' => normalize_uri('/api/v4/groups', group_id),
'ctype' => 'application/json',
'headers' => {
'PRIVATE-TOKEN' => api_token
}
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::GroupError, "Unable to delete group. Unexpected HTTP #{res.code} response." if res.code != 202
true
end
end

View File

@ -0,0 +1,45 @@
# -*- coding: binary -*-
module Msf::Exploit::Remote::HTTP::Gitlab::Rest::V4::Import
# Import a repository from a remote URL
#
# @return [String,nil] Import ID if successfully enqueued, nil otherwise
def gitlab_import_github_repo(group_name:, github_hostname:, api_token:)
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/api/v4/import/github'),
'ctype' => 'application/json',
'headers' => {
'PRIVATE-TOKEN' => api_token
},
'data' => {
'personal_access_token' => Rex::Text.rand_text_alphanumeric(8),
'repo_id' => rand(1000),
'target_namespace' => group_name,
'new_name' => "gh-import-#{rand(1000)}",
'github_hostname' => github_hostname
}.to_json
})
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
if res.code == 422
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ImportError, ((res.get_json_document || {})['errors'] || 'Import failed')
end
# 201 is returned if the import was successfully enqueued
unless res.code == 201
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ImportError, ((res.get_json_document || {})['errors'] || 'Import failed')
end
# 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 = res.get_json_document
return body if body
nil
end
end

View File

@ -0,0 +1,23 @@
# -*- coding: binary -*-
module Msf::Exploit::Remote::HTTP::Gitlab::Rest::V4::Version
# Extracts the Gitlab version information from various sources
#
# @return [String,nil] Gitlab version if found, nil otherwise
def gitlab_version
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/api/v4/version'),
'keep_cookies' => true
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::VersionError unless res.code == 200
body = JSON.parse(res.body)
version = body['version'][Regexp.new(Msf::Exploit::Remote::HTTP::Gitlab::GITLAB_VERSION_PATTERN), 1]
return version if version
nil
end
end

View File

@ -5,23 +5,5 @@ module Msf::Exploit::Remote::HTTP::Gitlab::Version
# Used to check if the version is correct: must contain at least one dot # Used to check if the version is correct: must contain at least one dot
GITLAB_VERSION_PATTERN = '(\d+\.\d+(?:\.\d+)*)'.freeze GITLAB_VERSION_PATTERN = '(\d+\.\d+(?:\.\d+)*)'.freeze
# Extracts the Gitlab version information from various sources include Msf::Exploit::Remote::HTTP::Gitlab::Rest::V4::Version
#
# @return [String,nil] Gitlab version if found, nil otherwise
def gitlab_version
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/api/v4/version'),
'keep_cookies' => true
})
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::ClientError.new message: 'Request timed out' unless res
raise Msf::Exploit::Remote::HTTP::Gitlab::Error::VersionError unless res.code == 200
body = JSON.parse(res.body)
version = body['version'][Regexp.new(GITLAB_VERSION_PATTERN), 1]
return version if version
nil
end
end end

View File

@ -173,7 +173,7 @@ class MetasploitModule < Msf::Exploit::Remote
github_hostname: get_uri, github_hostname: get_uri,
api_token: api_token api_token: api_token
)['id'] )['id']
# binding.pry
fail_with(Failure::UnexpectedReply, 'Failed to import a repository from GitHub') unless @import_id fail_with(Failure::UnexpectedReply, 'Failed to import a repository from GitHub') unless @import_id
# wait for the import tasks to finish # wait for the import tasks to finish
select(nil, nil, nil, datastore['IMPORT_DELAY']) select(nil, nil, nil, datastore['IMPORT_DELAY'])
@ -253,5 +253,4 @@ class MetasploitModule < Msf::Exploit::Remote
end end
send_response(cli, data, headers) send_response(cli, data, headers)
end end
end end