# TriCascade i-Bright7x - [device](#device) - [digging](#digging) - [nmap](#nmap) - [jnlp](#jnlp) - [live ports](#live_ports) - [23](#23) - [80](#80) - [8080](#8080) ## device name | value ---------|----- model | i-Bright7x firmware | unknown currently features | WiFi capable remote controled power supply. that runs linux ## digging ### nmap from `nmap -PN -sV -p 1-65535 192.168.17.1`, get: ``` PORT STATE SERVICE VERSION 23/tcp open telnet BusyBox telnetd 53/tcp open domain dnsmasq 2.59rc1 80/tcp open http 8080/tcp open http GoAhead WebServer ``` the same service looks to be running on both `80` and `8080`, prompting for a username and password, but none are provided by the manufacturer. it appears that the only way to configure this device is to use the Java applet through [bright.tricascade.com](https://bright.tricascade.com) ### jnlp by watching traffic, see that the applet is served from [resources/applet/WifiDevice.jnlp](https://bright.tricascade.com/resources/applet/WiFiDeviceAPI.jnlp) finding the corresponding `.jar` was more of a pain than expected, basically: ``` $ find ~/Library/Application Support/Oracle/Java/Deployment/cache/6.0 -type f -exec file {} \; ./10/78085f0a-4eab1a82: XML 1.0 document text, ASCII text, with CRLF, LF line terminators ./10/78085f0a-4eab1a82.idx: data ./10/78085f0a-c5a84a41ecc99150f548eb0393049f0d8c67849abe845af78c894a676aa18086-6.0.lap: ASCII text ./39/3dc5b4e7-2469db01-1.4.2-: Java archive data (JAR) ./39/3dc5b4e7-2469db01-1.4.2-.idx: data ./45/4766c42d-176a5aa0: XML 1.0 document text, ASCII text, with CRLF, LF line terminators ./45/4766c42d-176a5aa0.idx: data ./45/4766c42d-8bd095d4847349acdbba85a1e70f0d08d3990cfbb19c6007fde8f07bf0b52dbd-6.0.lap: ASCII text ./63/4600b2ff-0e27775cc2e9212bf6c7096ed895db16ef7cdd6b6f1d4e615eec8c92787d0f53-6.0.lap: ASCII text ./63/4d02c2bf-562ad327: Java archive data (JAR) ./63/4d02c2bf-562ad327.idx: data ... $ 7z l 39/3dc5b4e7-2469db01-1.4.2- Listing archive: ./39/3dc5b4e7-2469db01-1.4.2- -- Path = ./39/3dc5b4e7-2469db01-1.4.2- Type = zip Physical Size = 77973 Date Time Attr Size Compressed Name ------------------- ----- ------------ ------------ ------------------------ 2015-06-12 19:10:02 ..... 8149 3263 META-INF/MANIFEST.MF 2015-06-12 19:10:04 ..... 7954 3196 META-INF/BRIGHTEN.SF 2015-06-12 19:10:04 ..... 6165 4081 META-INF/BRIGHTEN.RSA 2015-06-12 19:10:02 ..... 2232 884 com/insnergy/wifi/applet/b.class ``` lucky guess. unzipping gives a bit of a clue about the structure - but nothing really interesting. [decompiling](decompile.sh) with the help of [cfr](http://www.benf.org/other/cfr/), however, does give us some hints: ```java // from com/insnergy/wifi/value/DeviceAP.java public final String getSsid(String string) { return this.ssidPattern + DeviceAP.extractMac(string); } public static String extractMac(String string) { return string.substring(8, 20); } public final String getPassword(String string) { return a.a(DeviceAP.extractMac(string)).substring(0, 10).toLowerCase(); } ``` it looks like the password is derived from the MAC address of the device - which is worse than it sounds, because the WiFi network it exposes for configuration is `'B7' + $MAC_ADDRESS` walking the Java code backwards: * `DeviceAP.extractMac(string)` returns characters 8-20 of whatever it is passed * `a`, is called with the result of above, which initially was misunderstood since the decompilation was ambiguous * `substring(0, 10).toLowerCase()` is called on whatever `a` returns within the context of what concrete values our device uses: key | value ---------|------- MAC | `8C:C7:AA:02:97:48` SSID | `B78CC7AA029748` // so.. 'B7' + $MAC - ':' ID | `TC0600008CC7AA029748` // so 'TC060000' + $MAC password | `97451790c9` 8-20 characters fits as the actual MAC address inside the ID value, but that gives us '8cc7aa0297', not '97451790c9' however, using the second-level deobfuscation feature of jadx turns `a` into `m56a`: ```java // com/insnergy/wifi/p002b/C0020a.java public static String m56a(String str) { try { if (C0021b.m61a((CharSequence) str)) { return ""; } MessageDigest instance = MessageDigest.getInstance("MD5"); instance.update(str.getBytes()); return new HexBinaryAdapter().marshal(instance.digest()); } catch (NoSuchAlgorithmException e) { e.toString(); return ""; } } ``` giving us the final step we need: ``` $ echo -n 8CC7AA029748 | md5sum 97451790c91d3c78bee70be7bac5f9b0 $ echo 97451790c91d3c78bee70be7bac5f9b0 | cut -c1-10 97451790c9 ``` the first 10 characters of the MD5 sum of the MAC address is the SSID password - and the SSID contains the MAC address. unfortunately, this password does not work when attempting to log in to the web application running on :80 ## live ports ### 23 ``` $ nc 192.168.17.1 23 BRIGHT7 login: admin admin Password: admin Login incorrect ``` there is a 1-2 second delay before declaring the login incorrect, making bruteforce even less desirable than usual. ### 80 attempts to login leak validity of username and password independently. when trying with `admin`, get redirected to `/index.htm?wrongpass1`: ``` $ curl http://192.168.17.1/cgi-bin/login.apply -v -d 'username=admin&password=password1234' ... * Connected to 192.168.17.1 (192.168.17.1) port 80 (#0) > POST /cgi-bin/login.apply HTTP/1.1 ... > < HTTP/1.0 200 OK < Content-Type: text/html ... * Closing connection 0 ``` and when trying with `user`, get redirected to `/index.htm?usernotfound`: ``` $ curl http://192.168.17.1/cgi-bin/login.apply -v -d 'usern ame=user&password=user' * Trying 192.168.17.1... * TCP_NODELAY set * Connected to 192.168.17.1 (192.168.17.1) port 80 (#0) > POST /cgi-bin/login.apply HTTP/1.1 > Host: 192.168.17.1 > User-Agent: curl/7.54.0 > Accept: */* > Content-Length: 27 > Content-Type: application/x-www-form-urlencoded > * upload completely sent off: 27 out of 27 bytes * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Content-Type: text/html < Pragma: no-cache < Cache-Control: no-cache,must-revalidate < Expired: -9999 < Vary: * < Set-Cookie: username=user;expire=-1;path=/ Set-Cookie: password=user;expire=-1;path=/ Set-Cookie: lang=en_US;expire=-1;path=/ Set-Cookie: cookieno=388505;expire=-1;path=/ Content-type: text/html ``` but curiously, the `/dashboard_overview.html` route leaks. unfortunately it is just a mobile/desktop redirector: ``` $ curl http://192.168.17.1/dashboard_overview/ Index ``` ### 8080 ```java // protected a(String string, String string2) { String string3 = "http://192.168.17.1:8080/goform/"; int n = 30000; if (com.insnergy.wifi.b.b.b((CharSequence)string)) { string3 = string; } if (com.insnergy.wifi.b.b.c(string2)) { n = Integer.parseInt(string2); } this.b = string3; this.c = n; } ``` this is the only reference to this address, and 'goform' doesn't lead to any obvious web frameworks or patterns making recon difficult is the 'always 200' responses we see: ``` $ curl http://192.168.17.1:8080/goform/test Document Error:Data follows

Access Error:200 Data follows

Form test is not defined

``` trying a few simple form names all proved to return the same response