Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

afp.rb 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. # -*- coding: binary -*-
  2. require 'msf/core'
  3. require 'msf/core/exploit/tcp'
  4. module Msf
  5. module Exploit::Remote::AFP
  6. include Msf::Exploit::Remote::Tcp
  7. def initialize(info={})
  8. super
  9. @id = 1
  10. register_options(
  11. [
  12. Opt::RPORT(548)
  13. ], Msf::Exploit::Remote::AFP)
  14. end
  15. def get_info
  16. packet = "\00" # Flag: Request
  17. packet << "\x03" # Command: FPGetSrvrInfo
  18. packet << [@id].pack('n') # requestID
  19. packet << "\x00\x00\x00\x00" #Data offset
  20. packet << "\x00\x00\x00\x00" #Length
  21. packet << "\x00\x00\x00\x00" #Reserved
  22. sock.put(packet)
  23. @id += 1
  24. response = sock.timed_read(1024)
  25. return parse_info_response(response)
  26. end
  27. def open_session
  28. packet = "\00" # Flag: Request
  29. packet << "\x04" # Command: DSIOpenSession
  30. packet << [@id].pack('n') # requestID
  31. packet << "\x00\x00\x00\x00" #Data offset
  32. packet << "\x00\x00\x00\x06" #Length
  33. packet << "\x00\x00\x00\x00" #Reserved
  34. packet << "\x01" # Attention Quantum
  35. packet << "\x04" # Length
  36. packet << "\x00\x00\x04\x00" # 1024
  37. sock.put(packet)
  38. @id += 1
  39. response = sock.timed_read(1024)
  40. return parse_open_session_response(response)
  41. end
  42. def login(user, pass)
  43. if user == ''
  44. return no_user_authent_login
  45. end
  46. p = OpenSSL::BN.new("BA2873DFB06057D43F2024744CEEE75B", 16)
  47. g = OpenSSL::BN.new("7", 10)
  48. ra = OpenSSL::BN.new('86F6D3C0B0D63E4B11F113A2F9F19E3BBBF803F28D30087A1450536BE979FD42', 16)
  49. ma = g.mod_exp(ra, p)
  50. padded_user = (user.length + 1) % 2 != 0 ? user + "\x00" : user
  51. bin_user = [padded_user.length, padded_user].pack("Ca*")
  52. length = 18 + bin_user.length + ma.to_s(2).length
  53. packet = "\00" # Flag: Request
  54. packet << "\x02" # Command: DSICommand
  55. packet << [@id].pack('n') # requestID
  56. packet << "\x00\x00\x00\x00" # Data offset
  57. packet << [length].pack('N') # Length (42)
  58. packet << "\x00\x00\x00\x00" # Reserved
  59. packet << "\x12" # AFPCommand: FPLogin (18)
  60. packet << "\x06\x41\x46\x50\x33\x2e\x31" # AFPVersion: AFP3.1
  61. packet << "\x09\x44\x48\x43\x41\x53\x54\x31\x32\x38" #UAM: DHCAST128
  62. packet << bin_user # username
  63. packet << ma.to_s(2) # random number
  64. sock.put(packet)
  65. @id += 1
  66. begin
  67. start = Time.now
  68. response = sock.timed_read(1024, datastore['LoginTimeOut'])
  69. rescue Timeout::Error
  70. vprint_error("AFP #{rhost}:#{rport} Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)")
  71. return :connection_error
  72. end
  73. flags, command, request_id, error_code, length, reserved = parse_header(response)
  74. case error_code
  75. when -5001 #kFPAuthContinue
  76. return parse_login_response_add_send_login_count(response, {:p => p, :g => g, :ra => ra, :ma => ma,
  77. :password => pass, :user => user})
  78. when -5023 #kFPUserNotAuth (User dosen't exists)
  79. print_status("AFP #{rhost}:#{rport} User #{user} dosen't exists")
  80. return :skip_user
  81. else
  82. return :connection_error
  83. end
  84. end
  85. def close_session
  86. packet = "\00" # Flag: Request
  87. packet << "\x01" # Command: DSICloseSession
  88. packet << [@id].pack('n') # requestID
  89. packet << "\x00\x00\x00\x00" #Data offset
  90. packet << "\x00\x00\x00\x00" #Length
  91. packet << "\x00\x00\x00\x00" #Reserved
  92. sock.put(packet)
  93. @id += 1
  94. end
  95. def no_user_authent_login
  96. packet = "\00" # Flag: Request
  97. packet << "\x02" # Command: DSICommand
  98. packet << [@id].pack('n') # requestID
  99. packet << "\x00\x00\x00\x00" # Data offset
  100. packet << "\x00\x00\x00\x18" # Length (24)
  101. packet << "\x00\x00\x00\x00" # Reserved
  102. packet << "\x12" # AFPCommand: FPLogin (18)
  103. packet << "\x06\x41\x46\x50\x33\x2e\x31" #AFP3.1
  104. packet << "\x0f\x4e\x6f\x20\x55\x73\x65\x72\x20\x41\x75\x74\x68\x65\x6e\x74" #UAM: No User Authent
  105. sock.put(packet)
  106. @id += 1
  107. begin
  108. response = sock.timed_read(1024, datastore['LoginTimeOut'])
  109. rescue Timeout::Error
  110. vprint_error("AFP #{rhost}:#{rport} Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)")
  111. return :connection_error
  112. end
  113. flags, command, request_id, error_code, length, reserved = parse_header(response)
  114. if error_code == 0
  115. return :true
  116. else
  117. return false
  118. end
  119. end
  120. def parse_login_response_add_send_login_count(response, data)
  121. dhx_s2civ = 'CJalbert'
  122. dhx_c2civ = 'LWallace'
  123. flags, command, request_id, error_code, length, reserved = parse_header(response)
  124. body = get_body(response, length)
  125. id, mb, enc_data = body.unpack("nH32a*")
  126. mb = OpenSSL::BN.new(mb, 16)
  127. k = mb.mod_exp(data[:ra], data[:p] )
  128. cipher = OpenSSL::Cipher.new('cast5-cbc').decrypt
  129. cipher.key = k.to_s(2)
  130. cipher.iv = dhx_s2civ
  131. cipher.padding = 0
  132. nonce = cipher.update(enc_data)
  133. nonce << cipher.final
  134. nonce = nonce[0..15]
  135. nonce = OpenSSL::BN.new(nonce, 2) + 1
  136. plain_text = nonce.to_s(2) + data[:password].ljust(64, "\x00")
  137. cipher = OpenSSL::Cipher.new('cast5-cbc').encrypt
  138. cipher.key = k.to_s(2)
  139. cipher.iv = dhx_c2civ
  140. auth_response = cipher.update(plain_text)
  141. auth_response << cipher.final
  142. packet = "\00" # Flag: Request
  143. packet << "\x02" # Command: DSICommand
  144. packet << [@id].pack('n') # requestID
  145. packet << "\x00\x00\x00\x00" # Data offset
  146. packet << [auth_response.length + 2].pack("N") # Length
  147. packet << "\x00\x00\x00\x00" # Reserved
  148. packet << "\x13" # AFPCommand: FPLoginCont (19)
  149. packet << "\x00"
  150. packet << [id].pack('n')
  151. packet << auth_response
  152. sock.put(packet)
  153. @id += 1
  154. begin
  155. response = sock.timed_read(1024, datastore['LoginTimeOut'])
  156. rescue Timeout::Error
  157. vprint_error("AFP #{rhost}:#{rport} Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)")
  158. return :connection_error
  159. end
  160. flags, command, request_id, error_code, length, reserved = parse_header(response)
  161. if error_code == 0
  162. return true
  163. else
  164. return false
  165. end
  166. end
  167. def parse_open_session_response(response)
  168. flags, command, request_id, error_code, length, reserved = parse_header(response)
  169. return error_code == 0 ? true : false
  170. end
  171. def parse_info_response(response)
  172. parsed_data = {}
  173. flags, command, request_id, error_code, length, reserved = parse_header(response)
  174. raise "AFP #{rhost}:#{rport} Server response with error" if error_code != 0
  175. body = get_body(response, length)
  176. machine_type_offset, afp_versions_offset, uam_count_offset, icon_offset, server_flags =
  177. body.unpack('nnnnn')
  178. server_name_length = body.unpack('@10C').first
  179. parsed_data[:server_name] = body.unpack("@11A#{server_name_length}").first
  180. pos = 11 + server_name_length
  181. pos += 1 if pos % 2 != 0 #padding
  182. server_signature_offset, network_addresses_offset, directory_names_offset,
  183. utf8_servername_offset = body.unpack("@#{pos}nnnn")
  184. parsed_data[:machine_type] = read_pascal_string(body, machine_type_offset)
  185. parsed_data[:versions] = read_array(body, afp_versions_offset)
  186. parsed_data[:uams] = read_array(body, uam_count_offset)
  187. # skipped icon
  188. parsed_data[:server_flags] = parse_flags(server_flags)
  189. parsed_data[:signature] = body.unpack("@#{server_signature_offset}H32").first
  190. network_addresses = read_array(body, network_addresses_offset, true)
  191. parsed_data[:network_addresses] = parse_network_addresses(network_addresses)
  192. # skipped directory names
  193. #Error catching for offset issues on this field. Need better error handling all through here
  194. begin
  195. parsed_data[:utf8_server_name] = read_utf8_pascal_string(body, utf8_servername_offset)
  196. rescue
  197. parsed_data[:utf8_server_name] = "N/A"
  198. end
  199. return parsed_data
  200. end
  201. def parse_header(packet)
  202. header = packet.unpack('CCnNNN')
  203. return header
  204. end
  205. def get_body(packet, body_length)
  206. body = packet[16..body_length + 15]
  207. raise "AFP #{rhost}:#{rport} Invalid body length" if body.length != body_length
  208. return body
  209. end
  210. def read_pascal_string(str, offset)
  211. length = str.unpack("@#{offset}C").first
  212. return str.unpack("@#{offset + 1}A#{length}").first
  213. end
  214. def read_utf8_pascal_string(str, offset)
  215. length = str.unpack("@#{offset}n").first
  216. return str[offset + 2..offset + length + 1]
  217. end
  218. def read_array(str, offset, afp_network_address=false)
  219. size = str.unpack("@#{offset}C").first
  220. pos = offset + 1
  221. result = []
  222. size.times do
  223. result << read_pascal_string(str, pos)
  224. pos += str.unpack("@#{pos}C").first
  225. pos += 1 unless afp_network_address
  226. end
  227. return result
  228. end
  229. def parse_network_addresses(network_addresses)
  230. parsed_addreses = []
  231. network_addresses.each do |address|
  232. case address.unpack('C').first
  233. when 0 #Reserved
  234. next
  235. when 1 # Four-byte IP address
  236. parsed_addreses << IPAddr.ntop(address[1..4]).to_s
  237. when 2 # Four-byte IP address followed by a two-byte port number
  238. parsed_addreses << "#{IPAddr.ntop(address[1..4])}:#{address[5..6].unpack("n").first}"
  239. when 3 # DDP address (deprecated)
  240. next
  241. when 4 # DNS name (maximum of 254 bytes)
  242. parsed_addreses << address[1..address.length - 1]
  243. when 5 # This functionality is deprecated.
  244. next
  245. when 6 # IPv6 address (16 bytes)
  246. parsed_addreses << "[#{IPAddr.ntop(address[1..16])}]"
  247. when 7 # IPv6 address (16 bytes) followed by a two-byte port number
  248. parsed_addreses << "[#{IPAddr.ntop(address[1..16])}]:#{address[17..18].unpack("n").first}"
  249. else # Something wrong?
  250. raise "Error parsing network addresses"
  251. end
  252. end
  253. return parsed_addreses
  254. end
  255. def parse_flags(flags)
  256. flags = flags.to_s(2)
  257. result = {}
  258. result['Super Client'] = flags[0,1] == '1' ? true : false
  259. result['UUIDs'] = flags[5,1] == '1' ? true : false
  260. result['UTF8 Server Name'] = flags[6,1] == '1' ? true : false
  261. result['Open Directory'] = flags[7,1] == '1' ? true : false
  262. result['Reconnect'] = flags[8,1] == '1' ? true : false
  263. result['Server Notifications'] = flags[9,1] == '1' ? true : false
  264. result['TCP/IP'] = flags[10,1] == '1' ? true : false
  265. result['Server Signature'] = flags[11,1] == '1' ? true : false
  266. result['Server Messages'] = flags[12,1] == '1' ? true : false
  267. result['Password Saving Prohibited'] = flags[13,1] == '1' ? true : false
  268. result['Password Changing'] = flags[14,1] == '1' ? true : false
  269. result['Copy File'] = flags[5,1] == '1' ? true : false
  270. return result
  271. end
  272. end
  273. end