flareman / homebridge-caddx-interlogix

A Homebridge plugin for the CaddX/Interlogic NetworX NX-595E and Hills ComNav network interface.
Apache License 2.0
8 stars 1 forks source link

HTTPS support for Hills ComNav - 403 Access Denied: SSL Required #10

Closed s-01010011 closed 2 years ago

s-01010011 commented 2 years ago

Hi! I was really excited to find your plugin and try it out on my Hills system with the ComNav board (version 2 possibly) however it doesn't seem to work with my system. As best I can guess from the Homebridge.io logs, it is trying to use http to talk to the board but the ComNav is insisting on requiring https so it's throwing back a 403 Access Denied requiring SSL and rejecting the login attempts.

Is this something your code already handles? I didn't see a simple way to enable https in the plugin configuration.

[07/11/2021, 01:03:31] Homebridge v1.3.5 (Homebridge 0B6B) is running on port 51492. Error: Forbidden at Request.callback (/usr/lib/node_modules/homebridge-caddx-interlogix/node_modules/superagent/src/node/index.js:879:15) at IncomingMessage. (/usr/lib/node_modules/homebridge-caddx-interlogix/node_modules/superagent/src/node/index.js:1130:18) at IncomingMessage.emit (node:events:402:35) at endReadableNT (node:internal/streams/readable:1343:12) at processTicksAndRejections (node:internal/process/task_queues:83:21) { status: 403, response: <ref *1> Response { _events: [Object: null prototype] {}, _eventsCount: 0, _maxListeners: undefined, res: IncomingMessage { _readableState: [ReadableState], _events: [Object: null prototype], _eventsCount: 4, _maxListeners: undefined, socket: [Socket], httpVersionMajor: 1, httpVersionMinor: 1, httpVersion: '1.1', complete: true, rawHeaders: [Array], rawTrailers: [], aborted: false, upgrade: false, url: '', method: null, statusCode: 403, statusMessage: 'Forbidden', client: [Socket], _consuming: true, _dumped: false, req: [ClientRequest], text: '403 Access Denied: SSL Required - use HTTPS\r\n',

  [Symbol(kHeaders)]: [Object],
  [Symbol(kHeadersCount)]: 2,
  [Symbol(kTrailers)]: null,
  [Symbol(kTrailersCount)]: 0,
  [Symbol(RequestTimeout)]: undefined
},
request: Request {
  _events: [Object: null prototype],
  _eventsCount: 1,
  _maxListeners: undefined,
  _enableHttp2: false,
  _agent: false,
  _formData: null,
  method: 'POST',
  url: 'http://10.0.0.xxxx/login.cgi',
  _header: [Object],
  header: [Object],
  writable: true,
  _redirects: 0,
  _maxRedirects: 0,
  cookies: '',
  qs: {},
  _query: [],
  qsRaw: [],
  _redirectList: [],
  _streamRequest: false,
  _data: [Object],
  req: [ClientRequest],
  protocol: 'http:',
  host: '10.0.0.xxxx',
  _endCalled: true,
  _callback: [Function (anonymous)],
  _fullfilledPromise: [Promise],
  res: [IncomingMessage],
  _resBuffered: true,
  response: [Circular *1],
  called: true,
  [Symbol(kCapture)]: false
},
req: ClientRequest {
  _events: [Object: null prototype],
  _eventsCount: 3,
  _maxListeners: undefined,
  outputData: [],
  outputSize: 0,
  writable: true,
  destroyed: false,
  _last: true,
  chunkedEncoding: false,
  shouldKeepAlive: false,
  maxRequestsOnConnectionReached: false,
  _defaultKeepAlive: true,
  useChunkedEncodingByDefault: true,
  sendDate: false,
  _removedConnection: false,
  _removedContLen: false,
  _removedTE: false,
  _contentLength: 20,
  _hasBody: true,
  _trailer: '',
  finished: true,
  _headerSent: true,
  _closed: false,
  socket: [Socket],
  _header: 'POST /login.cgi HTTP/1.1\r\n' +
    'Host: 10.0.0.xxxxx\r\n' +
    'Accept-Encoding: gzip, deflate\r\n' +
    'Content-Type: application/x-www-form-urlencoded\r\n' +
    'Content-Length: 20\r\n' +
    'Connection: close\r\n' +
    '\r\n',
  _keepAliveTimeout: 0,
  _onPendingData: [Function: nop],
  agent: [Agent],
  socketPath: undefined,
  method: 'POST',
  maxHeaderSize: undefined,
  insecureHTTPParser: undefined,
  path: '/login.cgi',
  _ended: true,
  res: [IncomingMessage],
  aborted: false,
  timeoutCb: null,
  upgradeOrConnect: false,
  parser: null,
  maxHeadersCount: null,
  reusedSocket: false,
  host: '10.0.0.xxxxx',
  protocol: 'http:',
  [Symbol(kCapture)]: false,
  [Symbol(kNeedDrain)]: false,
  [Symbol(corked)]: 0,
  [Symbol(kOutHeaders)]: [Object: null prototype]
},
text: '403 Access Denied: SSL Required - use HTTPS\r\n',
body: {},
files: undefined,
buffered: true,
headers: { connection: 'close' },
header: { connection: 'close' },
statusCode: 403,
status: 403,
statusType: 4,
info: false,
ok: false,
redirect: false,
clientError: true,
serverError: false,
error: Error: cannot POST /login.cgi (403)
    at Response.toError (/usr/lib/node_modules/homebridge-caddx-interlogix/node_modules/superagent/src/node/response.js:95:15)
    at Response._setStatusProperties (/usr/lib/node_modules/homebridge-caddx-interlogix/node_modules/superagent/src/response-base.js:126:48)
    at new Response (/usr/lib/node_modules/homebridge-caddx-interlogix/node_modules/superagent/src/node/response.js:41:8)
    at Request._emitResponse (/usr/lib/node_modules/homebridge-caddx-interlogix/node_modules/superagent/src/node/index.js:928:20)
    at IncomingMessage.<anonymous> (/usr/lib/node_modules/homebridge-caddx-interlogix/node_modules/superagent/src/node/index.js:1130:38)
    at IncomingMessage.emit (node:events:402:35)
    at endReadableNT (node:internal/streams/readable:1343:12)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  status: 403,
  text: '403 Access Denied: SSL Required - use HTTPS\r\n',
  method: 'POST',
  path: '/login.cgi'
},
created: false,
accepted: false,
noContent: false,
badRequest: false,
unauthorized: false,
notAcceptable: false,
forbidden: true,
notFound: false,
unprocessableEntity: false,
type: '',
links: {},
setEncoding: [Function: bound ],
redirects: [],
pipe: [Function (anonymous)],
[Symbol(kCapture)]: false

} } Could not process zones; not logged in Could not process zones; not logged in Could not process zones; not logged in Could not process zones; not logged in

flareman commented 2 years ago

The plug-in does not handle https:// connections; to be more specific, my installation (NX-595E on CN 0.108-0 firmware) does not connect when using HTTP Secure at all.

Could you please go into your web interface and check the source code? The \<head> section should contain a \<script> tag that references the main language script and should look like this:

<script src="/v_CN_0.108.0/lang_engeu.js" charset="utf-8"></script>

Copy that line here for me please; it indicates the firmware your network module is running.

s-01010011 commented 2 years ago

<script src="/v_CN_0.106-j/master.js" charset="utf-8"></script> <script src="/v_CN_0.106-j/status.js" charset="utf-8"></script>

I found an old copy of the ComNav installer manual which suggests it defaults to SSL by default but you could disable SSL (not that it's something I really want to do) but unfortunately I don't have the installer code for my Alarm system to even attempt to re-program it. I also found the SSL on the module is really old and took a bit of messing around finding an old enough web browser that still supported the insecure version. TLS_RCA_WITH_128_MDS (128 bit key).

May all just be getting a bit too hard unless you really like a challenge.

flareman commented 2 years ago

The odd thing is that SSL is enabled in my setup and regardless I can only connect using standard HTTP. Your firmware version is supported though, so if we get past the SSL issue, the plugin should work just fine.

I will try to find some time and work on that; I'll keep you posted.

flareman commented 2 years ago

I just pushed version 1.1.7, which adds SSL capabilities to the plugin; please update your installation and let me know if it works for you.

s-01010011 commented 2 years ago

Thanks for looking at it so quickly!

Updated to 1.1.7 and enabled SSL, sadly still having some issues connecting with what I suspect is due to that old TLS version.

Error: write EPROTO 1996331520:error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol:../deps/openssl/openssl/ssl/statem/statem_lib.c:1994:

at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:98:16) {

errno: -71, code: 'EPROTO', syscall: 'write', response: undefined } Could not process zones; not logged in Could not process zones; not logged in

flareman commented 2 years ago

I did some reading around to see what is the issue; seems like NX-595E uses an older TLS protocol version, as you have yourself found out. The setup manual does mention that "some older browsers will not be able to connect to the web interface using https://", which does explain the inability to connect using the 1.1.7 version (which has SSL capabilities), but they also do mention that enabling SSL in the settings should allow both secure and insecure connections. That - obviously - defeats the purpose of using SSL, but the interface itself is very shoddily written, so it wouldn't surprise me.

In any case, Node.js (which Homebridge uses) requires TLSv1.2 as a bare minimum for secure connections, and that is the reason why the plugin fails to connect. I have pushed v1.1.9, which attempts to force the client to use TLSv1.0 in an effort to bypass the issue (along with adding output relay capabilities to the plugin). Please try this and let me know how that works out for you.

s-01010011 commented 2 years ago

Slightly different error with 1.1.9 but same result unfortunately. Maybe both MIN and MAX TLS version needs to be specified in the .ts file? Some googling suggests sometimes you need to specify both before it takes effect or Node.js just may not allow it to protect everyone from broken TLS much like most modern browsers these days.

Error: write EPROTO 1996265984:error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol:../deps/openssl/openssl/ssl/statem/statem_lib.c:1994:

at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:98:16) {

errno: -71, code: 'EPROTO', syscall: 'write', response: undefined } Could not process zones; not logged in Could not process zones; not logged in Could not process zones; not logged in

s-01010011 commented 2 years ago

Also looking at the Node.js TLS(SSL) documentation, it looks like RC4 cyphers might also be disabled by default which could become an issue if the TLS1 problem can be resolved.

I think from previously poking around in the ComNav SSL, its using TLS_RSA_WITH_RC4_128_MD5

Node.js TLS

Old clients that rely on insecure and deprecated RC4 or DES-based ciphers (like Internet Explorer 6) cannot complete the handshaking process with the default configuration. If these clients must be supported, the TLS recommendations may offer a compatible cipher suite. For more details on the format, see the OpenSSL cipher list format documentation.

flareman commented 2 years ago

Yes, that seems to be the issue; Node.js implementation does not include the TLS_RSA_WITH_RC4_128_MD5 cipher in the list of available ones. Maybe the ComNav web server supports some other SSL cipher that we could use? You could try using something like Wireshark to sniff out the responses and check out which SSL/cipher combinations are available.

I'll ask some CompSec friends of mine on the side, on the off chance that they have experienced something similar.

flareman commented 2 years ago

Could you please try this Perl script? It will scan the 595 server for any and all SSL available protocols and ciphers available; we might find another supported cipher to use.

https://github.com/CiscoCXSecurity/ssl-cipher-suite-enum

s-01010011 commented 2 years ago

Unfortunately it's only RSA_WITH_RC4_128_MD5 by the looks according to the perl script. Might be a case that my particular revision of the board is just too old for support by modern tools since SSLv3 is pretty well removed from most dependency libraries and browsers these days. Maybe an upgrade to a newer version of the board will be my eventual fix to bring it back into some level of supportability. I even tried some tools on Kali linux to see if I could brute force the installer code to login and enable HTTP but even the tools in Kali weren't compatible back to SSLv3/TLS1.0 (openssl, jdk11 etc).

Thank you for putting so much time into trying to make it work but I suspect we've hit a dead end for my system with it's current firmware.

[+] Summary of support cipher suites for 10.0.0.xxxx:443

SSLv3.0:

flareman commented 2 years ago

All right, I did quite a lot of digging and the issue seems to be clear now.

The network interface uses port 443 for SSL connections using SSLv3 and the RC4_MD5 RSA cipher, something I confirmed using a custom openssl build (and it worked). The problem is that Node.js includes and uses recent openssl versions, where SSLv3 is removed (and the required cipher is also removed, but that can be mitigated more easily). Any attempts to package a custom openssl build in the plug-in package result in the node server crashing, as there are two sets of symbols in the executable. The only way this would work is for the end user to use a custom-built Node.js binary with both the RC4_MD5 cipher enabled AND a custom openssl build with SSLv3 enabled, which is entirely impractical and not at all secure (and I really wouldn't suggest it either way πŸ˜…). So this is a dead end, unfortunately; the network interface is using an old SSL version for its web server and it can't be fixed with a simple firmware upgrade, as a) the firmware files are not commercially available and b) the only way to upgrade the module (even if we DID have the necessary files) is by using a proprietary tool called USBUP; essentially, a flash drive which functions as an integrated firmware flasher.

What I really don't understand is why your particular installation does not allow you to connect over unsecure HTTP to your web interface. The manuals mention that even with the SSL port enabled, the interface should allow http:// calls, and I have had other 0.106x users that are able to connect thus. Can you connect to the web interface using standard http://10.0.0.xxx and a modern browser?

s-01010011 commented 2 years ago

Yeah, that seems to be the conclusion I've come to also. It's just too old and the various libraries have moved so far past supporting SSLv3 that it is impractical and probably unsafe to even try to work it back into apps to hack it back together.

When I try regular HTTP, it gives a 403 Forbidden error. 403 Access Denied: SSL Required - use HTTPS

I suspect the difference could be that I have a Hills branded alarm so parent company DAS probably customise the standard Interlogix firmware in order to rebrand it as their "ComNav" product, disabling http when turning on https seems like an unusual change to make but maybe it's a change they requested from Interlogix as one of their requirements when using the NX-595E as a "ComNav" module.

flareman commented 2 years ago

One second: if I may ask, how do you use your NX-595? I mean, haw have you been using it until now? If you can't even access the web interface on your own for manual use then it's useless to you. Was it preinstalled by your installer?

s-01010011 commented 2 years ago

I use it exclusively with the Ultrasync+ App on my phone. I've used the web interface maybe once or twice several years ago when the installer first installed the alarm system and was showing me how to use the system. In the years since it's always been via the Hills Xconnect app, followed by the original Ultrasync app when Hills stopped updating Xconnect and more recently Ultrasync+ which still works perfectly with the Hills branded version of the Interlogix system.

flareman commented 2 years ago

That makes sense; Ultrasync+ loads the local interface via the zerowire servers over the internet.

Your only bet at this point is asking the installer for the installer pin (he pretty much has to give it up; my own installer gave it to me immediately when I asked him), then use that in conjunction with the Ultrasync+ app to access the interface over the internet to edit the network settings and delete the SSL port. If that doesn't disable SSL access then I really have no other suggestions left :(

s-01010011 commented 2 years ago

Yep that's maybe the next option, I can access the https interface using an old Firefox browser running in a virtual machine so if I can get the Installer pin, I should be able to login using their login and turn https off. The Hills ComNav manual does say https is optional and can be enabled/disabled via the installer configuration page.

My other option might just be to get the Installer to come out and upgrade my board to the latest ComNav module (seems there might be a version 3 now) which hopefully has been updated to a recent version of TLS which fixes the issue correctly. Maybe I'll call the alarm installers, play dumb and complain I can't login to my ComNav and ask them to fix it.. see what they say - maybe they will offer a firmware update if it's just a real software issue and not software+hardware.

Thanks for all the help and patience @flareman.

flareman commented 2 years ago

My board has the 0.108-O version and it still can't connect at port 443 πŸ˜‚ However, it does not block standard port 80 calls, so that is a solution as well. Do try the installer pin route first, it might solve the problem in one swift stroke.

Thank you for the patience and the support; let me know how the situation turns out for you ✌🏻