Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for bypass #16

Closed
amaduain opened this issue Nov 4, 2022 · 21 comments
Closed

Support for bypass #16

amaduain opened this issue Nov 4, 2022 · 21 comments
Assignees
Labels
enhancement New feature or request

Comments

@amaduain
Copy link

amaduain commented Nov 4, 2022

Hi all,
thanks for the great work, I was guessing if you already thought on supporting bypassing, I've been trying to find the api for bypassing, but so far no luck.
Do you have any details on the api?

Thanks!

@bitcanon
Copy link
Owner

bitcanon commented Dec 9, 2022

Hi,

I don't think I have stumbled upon something called bypassing so I'm not sure what the purpose of this function is. The way I have developed the library is by reverse engineering the mobile app. What is bypassing used for? Is there a way for you to use it in the mobile app? If there is a way in the app, what are the steps I need to take to use bypassing.
Sadly, I have no access to the API documentation which makes it a bit harder to develop against :)

No problems, thanks for your input!

@bitcanon bitcanon closed this as completed Dec 9, 2022
@bitcanon bitcanon reopened this Dec 9, 2022
@amaduain
Copy link
Author

Thanks a lot for the answer, the application I'm using is Connect Alarm, and there is an option called bypass that disables one of the sensor, to avoid false positives. Which application are you reversing? Maybe I can take a look too.
Anyway, feel free to close it, if I found anything I will tell you.
Best regards!

@bitcanon
Copy link
Owner

bitcanon commented Dec 12, 2022

Hi, I'm using Connect Alarm as well, but the reason I have not implemented it might be that I can't see that option in my alarm system. If I click Devices and select a sensor (a "Contact" for example), the only option that is available for me is "Rename". Can you see other options here?
Please let me know if you figure it out.

@amaduain
Copy link
Author

In my case, some sensors can by bypassed and renamed:
WhatsApp Image 2022-12-12 at 16 27 55
WhatsApp Image 2022-12-12 at 16 28 41

@amaduain
Copy link
Author

Also, any good source / training for reverse engineering apps?

@bitcanon
Copy link
Owner

bitcanon commented Dec 12, 2022

It's probably a setting in the alarm panel itself or simply that my sensors doesn't have that feature.

If you are comfortable with Python and know how to setup a reverse proxy with a Let's Encrypt certificate you can try my dummy API server that I've built.

The way I have developed this library is by implementing a Python Flask application that responds to the API requests that the Connect Alarm app sends.

The workflow is something like this:

  1. Run my API server script on the development machine.
  2. Setup a reverse proxy (I run mine on my Synology NAS), that points for example https://alarm.mydomain.com to http://10.0.0.123:8080 (my dev machine). It's important to have a valid certificate, otherwise the app won't connect.
  3. Then I just press the buttons in the app and see which endpoints the app tries to contact.
  4. I then implement that endpoint in my API server script and check what the request from the app looks like.
  5. After that I implement a method in my visonicalarm library that sends that same information to the real API server and now I can see what the response from the real server looks like.
  6. Now I have the JSON messages sent from both the app and the server and can stitch together the visonicalarm library piece by piece.
  7. Go back to step 3 and press another button i the app and do it all again :)

Initially I tried using applications like Proxyman and Burp Suite, but this approach didn't work since the Connect Alarm app require a valid SSL certificate for its connections.

I hope this made it a bit more clear and let me know if you would like to try my Python Flask API server to find out which endpoint the Bypass button calls on the API server.

I don't think I have any good training material for you more than the apps I mentioned earlier and perhaps to check out my Python server script and simply learn by doing :)

@amaduain
Copy link
Author

Thanks a lot for the hints! I have no problem with python or let's encrypt I use them on other projects, can you share your api server? Once I got the info I will share it with you of course!
I'm using your actual libraries on the Home Assistant (I got a script to translate from your scripts to MQTT).

@bitcanon
Copy link
Owner

No problems! :) You can get the server script here:

Get up and running in a few steps.

  1. cd visonicalarm-api-server
  2. paste the gist code into server.py
  3. virtualenv venv
  4. source venv/bin/activate
  5. pip install flask
  6. python server.py

Great to hear that the library is being used!

Happy hunting 😄

@amaduain
Copy link
Author

amaduain commented Dec 13, 2022

Thanks for the heads up, it was nice to have a way to reverse engineer the app, super cool.
So, what I did is create the certs for the flask server, once you have them you can skip the reverse proxy if you have a local dns (I got Pihole) and route it to the fake server. So in the server code you can add this:
import ssl ... ... if __name__ == '__main__': context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) context.load_cert_chain("fullchain2.pem","privkey2.pem") app.run(host='0.0.0.0', port=443, ssl_context=context)
After that I was able to emulate the zone bypassing:
URL : /rest_api/9.0/set_bypass_zone
Action: Post
Enable bypassing payload json:
{ "zone": 5, "set": true }
Disable bypassing json:
{ "zone": 5, "set": false }
Zone id has to be one that you have defined on the panel :-D
Don't know if you plan to add it to your code, if so, let me know if you need anything else.

@bitcanon
Copy link
Owner

Thanks! It has been very helpful in developing the library. But I would call it an "ugly hack" rather than "super cool" 😉

Also, thanks for the tip on using the certificate with Flask, I didn't know you could do that. I will definitely use this approach in the future, since I seem to be using the Flask server a lot in different projects. Much appreciated! 👍🏻

Sure, I'm already working on adding this to the library, so I will need some help testing it soon 😄

One thing that would be nice if you could check is what the output of the following call is on your system. It will print the raw output from the API server for the get_panel_info endpoint:

print(alarm.api.get_panel_info())

What I'm interested in is the value of the bypass_mode key value pair on your system. On my alarm system it is set to 'bypass_mode': 'no_bypass', which is probably why I can't see this option in the app. I suspect this would make it possible to trick the app to show the Bypass button for some dummy sensors.

@amaduain
Copy link
Author

amaduain commented Dec 13, 2022 via email

@bitcanon
Copy link
Owner

Thanks for the output, sadly I wasn't able to get the Bypass button appear in the app.

But, to the good news. I have pushed the code for set_bypass_zone() to GitHub now so you should be able to call:

alarm.set_bypass_zone(5, True)

My guess is that the API will return a token that we can poll to check if the operation succeeded.

I haven't pushed the changes to PyPI yet so you have to clone the GitHub repository and try it that way to see if it works first.

Let me know how it goes or if any weird messages appear (most likely) 😄

@amaduain
Copy link
Author

amaduain commented Dec 13, 2022 via email

@amaduain
Copy link
Author

You can find your server.py with the valid payloads for bypassing here:
https://github.com/amaduain/visonic_findings/blob/main/server.py

@amaduain
Copy link
Author

amaduain commented Dec 13, 2022

Thanks for the output, sadly I wasn't able to get the Bypass button appear in the app.

But, to the good news. I have pushed the code for set_bypass_zone() to GitHub now so you should be able to call:

alarm.set_bypass_zone(5, True)

My guess is that the API will return a token that we can poll to check if the operation succeeded.

I haven't pushed the changes to PyPI yet so you have to clone the GitHub repository and try it that way to see if it works first.

Let me know how it goes or if any weird messages appear (most likely) 😄

Yep, the return is something like:

{
    "process_token": "d32aa971-4ed4-4674-8dd0-ea6b5263f10b"
}

@bitcanon
Copy link
Owner

You can find your server.py with the valid payloads for bypassing here: https://github.com/amaduain/visonic_findings/blob/main/server.py

Thanks, with your code I could figure out that each device that support bypassing also has a trait called bypass:

  {
    ...
    "traits": {
      "rssi": {},
      "bypass": {
        "enabled": false
      },
      "soak": {
        "enabled": false
      },
      "rarely_used": {
        "enabled": false
      }
    },
    ...
  },

This attribute will have to be added to the Device class.

@bitcanon
Copy link
Owner

I will test it tomorrow and let you know. For your testing I can sanitize the whole data set and send it to you. Seems there is relationship between the panel, and the devices. It didn't allow me to bypass till I add all data.

I have now added support for bypassing a device in the GitHub repository, so you should now be able to check the bypass status of a device like so:

print("Gettings Devices...")
for device in alarm.get_devices():
    print(f"Device: {device.id} - Bypassed: {device.bypass}")

Output:

Gettings Devices...
<output truncated>
Device: 1234004 - Bypassed: False
Device: 1234005 - Bypassed: True
Device: 1234006 - Bypassed: False
<output truncated>

And like previously mentioned, bypass a device with:

alarm.set_bypass_zone(5, True)

If your testing goes smoothly I can push to PyPI as well 😄

@amaduain
Copy link
Author

amaduain commented Dec 14, 2022

Nice, I have been testing in my system today and it is working. Only issue I had it was with the GSM device, which is not reporting the signal so the get_devices was failing on line 181 alarm.py so I added a check as a workaround:

elif device['device_type'] == 'GSM':
                if 'signal_level' in device['traits'].keys():
                    signal_level=device['traits']['signal_level']['level']
                else:   
                    signal_level=0
                contact_device = GSMDevice(
                    device_number=device['device_number'],
                    device_type=device['device_type'],
                    enrollment_id=device['enrollment_id'],
                    id=device['id'],
                    name=device['name'],
                    partitions=device['partitions'],
                    preenroll=device['preenroll'],
                    removable=device['removable'],
                    renamable=device['renamable'],
                    subtype=device['subtype'],
                    warnings=device['warnings'],
                    zone_type=device['zone_type'],
                    signal_level=signal_level
                )

If you have time maybe you can check the inputs before assign them, traits seems to be quite tricky.

Shall you close this issue?

@bitcanon bitcanon added the enhancement New feature or request label Dec 14, 2022
@bitcanon bitcanon self-assigned this Dec 14, 2022
@bitcanon
Copy link
Owner

Nice, I have been testing in my system today and it is working. Only issue I had it was with the GSM device, which is not reporting the signal so the get_devices was failing on line 181 alarm.py so I added a check as a workaround:

elif device['device_type'] == 'GSM':
                if 'signal_level' in device['traits'].keys():
                    signal_level=device['traits']['signal_level']['level']
                else:   
                    signal_level=0
                contact_device = GSMDevice(
                    device_number=device['device_number'],
                    device_type=device['device_type'],
                    enrollment_id=device['enrollment_id'],
                    id=device['id'],
                    name=device['name'],
                    partitions=device['partitions'],
                    preenroll=device['preenroll'],
                    removable=device['removable'],
                    renamable=device['renamable'],
                    subtype=device['subtype'],
                    warnings=device['warnings'],
                    zone_type=device['zone_type'],
                    signal_level=signal_level
                )

If you have time maybe you can check the inputs before assign them, traits seems to be quite tricky.

Shall you close this issue?

Great, thanks for all of your help and support! Much appreciated 😄

Version 3.10.0 of the library is now available for installation via PyPI/pip, and I also fixed the bug you found regarding the signal_level trait (created issue #18 for this). Traits sure are quite tricky to predict since they seem to be different on different systems.

I will close the issue for now. The new version seem to be working fine, let me know otherwise.

@msp1974
Copy link

msp1974 commented Apr 18, 2023

I know this is a closed issue but found the info here really helpful in understanding the rest api. There are a few endpoints we could add to the api (which I will do a PR for soon) but wanted to a) thanks for the effort on this and b) contribute some enhancement I did on your server.py script. This gist server.py

Instead of having to modify it with each endpoint you discover, it is actually forwarding the request to the true endpoint servers, getting the response, and passing back to the client. This is then very easy to now use the app to see the resp api request and response. Hope it helps!

@bitcanon
Copy link
Owner

Hi, thanks a lot for your improvement to the server.py script. I have already given it a go and it will make development much easier in the future. Great work and thanks again!👍🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants