j0h

IDEC_FTP_HMI

j0h
Jul 2nd, 2026
142
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.70 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import socket
  3. import random
  4. import time
  5. from ftplib import FTP, error_perm, all_errors
  6.  
  7. HMI_IP = "192.168.1.150"
  8. DISM_PORT = 2537
  9. FTP_PORT = 2539
  10. """
  11. Explore IDEC HMI via FTP.
  12. Maintenance Protocol initializes the connection with:
  13. AB/LB/LF commands. the PC says: here is my 16 byte user,
  14. and 15 byte password.
  15. Then the HMI enstantiates the connection.
  16. while the user and pass will time out after 15 minutes,
  17. there doesn not seem to be an immediate limit on the number of sessions.
  18.  
  19. This program lists files and folders, and does some naviations
  20.  
  21. """
  22. START_DIRS = [".", "/", "tmp", "../", "/tmp", "exmem", "/exmem",]
  23.  
  24.  
  25. def rand_hex(n):
  26.     return "".join(random.choice("0123456789abcdef") for _ in range(n))
  27.  
  28.  
  29. def bcc(body: bytes) -> int:
  30.     x = 0x05
  31.     for b in body:
  32.         x ^= b
  33.     return x
  34.  
  35.  
  36. def frame(seq: int, body: bytes) -> bytes:
  37.     body2 = body + f"{bcc(body):02X}".encode("ascii") + b"\r"
  38.     total_len = 19 + len(body2)
  39.     header = bytes([
  40.         0x01,
  41.         (total_len >> 16) & 0xff,
  42.         (total_len >> 8) & 0xff,
  43.         total_len & 0xff,
  44.         0x00, 0x01,
  45.         0x00, 0x00, 0x00, 0x00,
  46.         0x00, 0x00, 0x00, 0x00,
  47.         seq & 0xff,
  48.         0x01, 0x01, 0x01,
  49.         0x05,
  50.     ])
  51.     return header + body2
  52.  
  53.  
  54. def make_lb_body(username: str, password: str) -> bytes:
  55.     user_field = username.encode("ascii").hex().encode("ascii").upper().ljust(64, b"0")
  56.  
  57.     doubled_pass = bytes((ord(ch) * 2) & 0xff for ch in password)
  58.     pass_field = doubled_pass.hex().encode("ascii").upper().ljust(128, b"0")
  59.  
  60.     return b"00FFLB" + bytes([0xC0, 0x00, 0x01]) + user_field + pass_field
  61.  
  62.  
  63. def send_cmd(sock, seq, body, name):
  64.     pkt = frame(seq, body)
  65.     print(f"{name} TX:", pkt.hex(" "))
  66.     sock.sendall(pkt)
  67.     rx = sock.recv(4096)
  68.     print(f"{name} RX:", rx.hex(" "))
  69.     return rx
  70.  
  71.  
  72. def open_hmi_session(username, password):
  73.     with socket.create_connection((HMI_IP, DISM_PORT), timeout=5) as s:
  74.         send_cmd(s, 5, b"00FFAB", "AB")
  75.         send_cmd(s, 6, make_lb_body(username, password), "LB")
  76.         send_cmd(s, 7, b"00FFLF", "LF")
  77.  
  78.  
  79. def connect_ftp(username, password):
  80.     last = None
  81.  
  82.     for attempt in range(1, 21):
  83.         try:
  84.             print(f"FTP attempt {attempt}/20")
  85.             ftp = FTP()
  86.             ftp.connect(HMI_IP, FTP_PORT, timeout=5)
  87.             print(ftp.getwelcome())
  88.             ftp.login(username, password)
  89.             ftp.voidcmd("TYPE I")
  90.             return ftp
  91.         except all_errors as e:
  92.             print("FTP not ready/login failed:", repr(e))
  93.             last = e
  94.             time.sleep(0.25)
  95.  
  96.     raise last
  97.  
  98.  
  99. def try_list(ftp, path):
  100.     print("\n" + "=" * 70)
  101.     print("PATH:", path)
  102.  
  103.     try:
  104.         ftp.cwd(path)
  105.         print("PWD:", ftp.pwd())
  106.     except all_errors as e:
  107.         print("CWD failed:", repr(e))
  108.         return
  109.  
  110.     print("\nLIST:")
  111.     try:
  112.         lines = []
  113.         ftp.retrlines("LIST", lines.append)
  114.         for line in lines:
  115.             print(" ", line)
  116.     except all_errors as e:
  117.         print("LIST failed:", repr(e))
  118.  
  119.     print("\nNLST:")
  120.     try:
  121.         names = ftp.nlst()
  122.         for name in names:
  123.             print(" ", name)
  124.     except all_errors as e:
  125.         print("NLST failed:", repr(e))
  126.  
  127.  
  128. def main():
  129.     username = rand_hex(16)
  130.     password = rand_hex(15)
  131.  
  132.     print("Using temporary FTP creds:")
  133.     print("USER", username)
  134.     print("PASS", password)
  135.  
  136.     open_hmi_session(username, password)
  137.  
  138.     ftp = connect_ftp(username, password)
  139.  
  140.     print("\nInitial PWD:", ftp.pwd())
  141.  
  142.     for path in START_DIRS:
  143.         try_list(ftp, path)
  144.  
  145.     ftp.quit()
  146.  
  147.  
  148. if __name__ == "__main__":
  149.     main()
  150.  
Advertisement
Add Comment
Please, Sign In to add comment