vi / websocat

Command-line client for WebSockets, like netcat (or curl) for ws:// with advanced socat-like functions
MIT License
6.72k stars 257 forks source link

Need help to debug a websocket #164

Closed lb544 closed 1 year ago

lb544 commented 1 year ago

HitachiWebSocket Hi, In order to connect my A/C Hitachi to my Home Assistant installation with some personal touch i would like to request the hitachi's websocket with websocat to have to state of the A/C (Powered On or Off) So ... I Use Charles Proxy (or fiddler) to sniffing websocket traffic in the Android App, but I can't reproduce it with websocket :

Client Message : CONNECT accept-version:1.1,1.2 heart-beat:10000,10000 Authorization:Bearer blabla_in_base64

Server Response : CONNECTED version:1.2 heart-beat:10000,10000 user-name:User(id=xxxxx, email=xxxxxxxx, phoneNumber=null, password=null, softLockCount=0, lockUntil=null, detailsUserInfo=null, status=null, socialAccounts=[])

Client Message :

MESSAGE Authorization:Bearer blabla_in_base64 destination:/app/racs/xxxx/xxxx {"racId":xxxx,"requestType":"REFRESH_INDIVIDUAL"}

Client Message : SUBSCRIBE id:xxxxxxx-xxxxx-xxxxx destination:/notification/xxxx/xxxx ack:auto

MESSAGE destination:/notification/3871/3871 content-type:application/json;charset=UTF-8 subscription:06e4d527-5448-48f0-bdad-1df4eea6a339 message-id:cc094ac5-0575-ccad-3b93-4412d25b5a02-45250 content-length:2594

Server response : The result in json that I expected, then I can parse it {"notificationType":"ON_CONNECT","data":[{"serialNumber":"XXXX-XXXX-XXXX","iduFrostWashStatus": [...] "iduTemperature":22.0,"humidity":126,"power":"OFF","relativeTemperature":0.0,"fanSpeed":"AUTO","fanSwing":"BOTH [...] }

I tried to send "CONNECT" to the websocket with websocat but I can't get the first expected result (with account details for example) :

./websocat.x86_64-unknown-linux-musl -vv -H="Authorization:Bearer blabla_in_base64" wss://[I Hide It to avoid any problems by Hitachi].aircloudhome.com/[Hidden]/websocket [INFO websocat::lints] Auto-inserting the line mode [DEBUG websocat] Done third phase of interpreting options. [DEBUG websocat] Done fourth phase of interpreting options. [DEBUG websocat] Preparation done. Now actually starting. [DEBUG websocat::sessionserve] Serving Line2Message(ThreadedStdio) to Message2Line(WsClient("wss://notification-global-prod.aircloudhome.com/rac-notifications/websocket")) with Options { websocket_text_mode: true, websocket_protocol: None, websocket_reply_protocol: None, udp_oneshot_mode: false, udp_broadcast: false, udp_multicast_loop: false, udp_ttl: None, udp_join_multicast_addr: [], udp_join_multicast_iface_v4: [], udp_join_multicast_iface_v6: [], udp_reuseaddr: false, unidirectional: false, unidirectional_reverse: false, max_messages: None, max_messages_rev: None, exit_on_eof: false, oneshot: false, unlink_unix_socket: false, unix_socket_accept_from_fd: false, exec_args: [], ws_c_uri: "ws://0.0.0.0/", linemode_strip_newlines: false, linemode_strict: false, origin: None, custom_headers: [("Authorization", [66, 101, 97, 114, 101, 114, 32, 101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 73, 85, 122, 85, 120, 77, 105, 74, 57, 46, 101, 121, 74, 122, 100, 87, 73, 105, 79, 105, 73, 122, 79, 68, 99, 120, 73, 105, 119, 105, 99, 50, 78, 118, 72, 100, 85, 99, 103])], custom_reply_headers: [], websocket_version: None, websocket_dont_close: false, websocket_ignore_zeromsg: false, one_message: false, no_auto_linemode: false, buffer_size: 65536, broadcast_queue_len: 16, read_debt_handling: Warn, linemode_zero_terminated: false, restrict_uri: None, serve_static_files: [], exec_set_env: false, no_exit_on_zeromsg: false, reuser_send_zero_msg_on_disconnect: false, process_zero_sighup: false, process_exit_sighup: false, socks_destination: None, auto_socks5: None, socks5_bind_script: None, tls_domain: None, tls_insecure: false, headers_to_env: [], max_parallel_conns: None, ws_ping_interval: None, ws_ping_timeout: None, request_uri: None, request_method: None, request_headers: [], autoreconnect_delay_millis: 20, ws_text_prefix: None, ws_binary_prefix: None, ws_binary_base64: false, ws_text_base64: false, close_status_code: None, close_reason: None, asyncstdio: false, foreachmsg_wait_reads: false, announce_listens: false, timestamp_monotonic: false, print_ping_rtts: false } [INFO websocat::stdio_threaded_peer] get_stdio_peer (threaded) [DEBUG websocat::sessionserve] Underlying connection established [INFO websocat::ws_client_peer] get_ws_client_peer [INFO websocat::ws_client_peer] Connected to ws CONNECT [INFO websocat::ws_peer] incoming None [DEBUG websocat::my_copy] BrokenPipe: read_done [DEBUG websocat::my_copy] done [INFO websocat::sessionserve] Reverse finished [DEBUG websocat::sessionserve] Reverse shutdown finished

Anyone can help me ? Thanks

vi commented 1 year ago

What is accept-version:1.1,1.2 and below? Are them supposed to be multi-line WebSocket text messages?

What would happen if you use --base64-text option and send Q09OTkVDVAphY2NlcHQtdmVyc2lvbjoxLjEsMS4yCmhlYXJ0LWJlYXQ6MTAwMDAsMTAwMDAKQXV0 aG9yaXphdGlvbjpCZWFyZXIgYmxhYmxhX2luX2Jhc2U2NAo= to Websocat? That would allow you to send the multi-line text message from Websocat. Line endings (\r\n vs \n) may be a problem though.

lb544 commented 1 year ago

@vi Thanks for your fast answer I don't know what is the accept-version ... maybe a specific option / header for the Hitachi's websocket ? Yes, it's a multi-line websocket : HitachiWebSocket-Fiddler

I tried the --base64-text option but not working :

echo "Q09OTkVDVAphY2NlcHQtdmVyc2lvbjoxLjEsMS4yCmhlYXJ0LWJlYXQ6MTAwMDAsMTAwMDAKQXV0aG9yaXphdGlvbjpCZWFyZXIgZXlKaGJHY2lPaUpJVXpVeE1pSjkuZXlKemRXSWlPaUl6T0RjeElpd2ljMk52Y0dWeklqcGJJbUZqWTJWemMxOTBiMnRsYmlKZExDSnBZWFFpT2pFMk5qTTJPRE0xTWpVc0ltbHpjeUk2SW1oMGRIQnpPaTh2WVhCcExXZHNiMkpoYkMxd2NtOWtMbUZwY21Oc2IzVmthRzl0WlM1amIyMHZhV0Z0SWl3aVlYVmtJam9pWVdseVkyeHZkV1JuYkc5aVlXd3RjSEp2WkNJc0ltVjRjQ0k2TVRZMk16WTROVE15TlgwLnp3T0pxb05qX0o1eWtlRXQtaHYwQjZ4Uk4tMGpfRlBuUk50M09KZmROb1dBcXJ1eFkzbGthRlFOZ2czQ0swN09FRG50MzduRl9tUlVXbURaVTRz[hidden]" | ./websocat.x86_64-unknown-linux-musl -vv --base64-text wss://notification-global-prod.aircloudhome.com/rac-notifications/websocket [INFO websocat::lints] Auto-inserting the line mode [DEBUG websocat] Done third phase of interpreting options. [DEBUG websocat] Done fourth phase of interpreting options. [DEBUG websocat] Preparation done. Now actually starting. [DEBUG websocat::sessionserve] Serving Line2Message(ThreadedStdio) to Message2Line(WsClient("wss://notification-global-prod.aircloudhome.com/rac-notifications/websocket")) with Options { websocket_text_mode: true, websocket_protocol: None, websocket_reply_protocol: None, udp_oneshot_mode: false, udp_broadcast: false, udp_multicast_loop: false, udp_ttl: None, udp_join_multicast_addr: [], udp_join_multicast_iface_v4: [], udp_join_multicast_iface_v6: [], udp_reuseaddr: false, unidirectional: false, unidirectional_reverse: false, max_messages: None, max_messages_rev: None, exit_on_eof: false, oneshot: false, unlink_unix_socket: false, unix_socket_accept_from_fd: false, exec_args: [], ws_c_uri: "ws://0.0.0.0/", linemode_strip_newlines: false, linemode_strict: false, origin: None, custom_headers: [], custom_reply_headers: [], websocket_version: None, websocket_dont_close: false, websocket_ignore_zeromsg: false, one_message: false, no_auto_linemode: false, buffer_size: 65536, broadcast_queue_len: 16, read_debt_handling: Warn, linemode_zero_terminated: false, restrict_uri: None, serve_static_files: [], exec_set_env: false, no_exit_on_zeromsg: false, reuser_send_zero_msg_on_disconnect: false, process_zero_sighup: false, process_exit_sighup: false, socks_destination: None, auto_socks5: None, socks5_bind_script: None, tls_domain: None, tls_insecure: false, headers_to_env: [], max_parallel_conns: None, ws_ping_interval: None, ws_ping_timeout: None, request_uri: None, request_method: None, request_headers: [], autoreconnect_delay_millis: 20, ws_text_prefix: None, ws_binary_prefix: None, ws_binary_base64: false, ws_text_base64: true, close_status_code: None, close_reason: None, asyncstdio: false, foreachmsg_wait_reads: false, announce_listens: false, timestamp_monotonic: false, print_ping_rtts: false } [INFO websocat::stdio_threaded_peer] get_stdio_peer (threaded) [DEBUG websocat::sessionserve] Underlying connection established [INFO websocat::ws_client_peer] get_ws_client_peer [INFO websocat::ws_client_peer] Connected to ws [DEBUG websocat::my_copy] zero len [DEBUG websocat::my_copy] read_done [DEBUG websocat::my_copy] done [INFO websocat::sessionserve] Forward finished [DEBUG websocat::sessionserve] Forward shutdown finished [DEBUG websocat::ws_peer] drop WsWriteWrapper [INFO websocat::ws_peer] Received WebSocket close message [DEBUG websocat::ws_peer] The close message is Some(CloseData { status_code: 1000, reason: "" }) [DEBUG websocat::my_copy] BrokenPipe: read_done [DEBUG websocat::my_copy] done [INFO websocat::sessionserve] Reverse finished [DEBUG websocat::sessionserve] Reverse shutdown finished [INFO websocat::sessionserve] Both directions finished

Used this base64 conversion :

HitachiWebSocket-Base64

vi commented 1 year ago

Ideas to try:

  1. Maybe it is a binary WebSocket message, not text one
  2. Are you sure you need "LF", not "CRLF"? Use HexView in the inspector above to find out what line endings are used in the working WebSocket messages. Also take special attention to trailing newline or newlines.
  3. Maybe HTTP connection request headers also matter (both WebSocket-specific like Websocket-Protocol and usual e.g. Referer)?
  4. Maybe the unit could be queried and controlled directly, not though the cloud?
  5. You can also run Websocat on Android (from some terminal app or "adb shell"). Maybe that would also be in purview of Fiddler/Charles and you can compare and contrast Websocat vs app more easily.
  6. Although WebSocket is probably indeed the easiest part to intercept, controlling the application itself instead of its connection to the cloud may also be an option.
lb544 commented 1 year ago

Ok I'll try it your purpose Thanks "Maybe the unit could be queried and controlled directly, not though the cloud?" Hum, it seems to be difficult, the unit is connected by WIFI, with airCloud Home adapter, no way to access to the device inside my network (no open ports ...)

lb544 commented 1 year ago

Hi, I decompiled the apk and I see that the app use Stomp https://stomp.github.io/stomp-specification-1.2.html So I tested with Postman the websocket it works with this trick : https://dev.to/danielsc/testing-stomp-websocket-with-postman-218a

But with the same base64 code with NULL octet sended with websocat : not working again :( Do you know how to use Stomp with websocat ?

Thanks

vi commented 1 year ago

If the problem is in the zero byte (i.e. NULL octet) at the end of the message then you should just use other base64 encoder. Features of that online encoder may be not enough.

For example, you can also base64-encode in typical Linux command line.

$ printf 'CONNECT\naccept-version:1.1,1.2\nheart-beat:10000,10000\nAuthorization:Bearer blabla_in_base64\n\0' | base64 -w0; echo
Q09OTkVDVAphY2NlcHQtdmVyc2lvbjoxLjEsMS4yCmhlYXJ0LWJlYXQ6MTAwMDAsMTAwMDAKQXV0aG9yaXphdGlvbjpCZWFyZXIgYmxhYmxhX2luX2Jhc2U2NAoA
lb544 commented 1 year ago

Hum not working with websocat but with postman OK Tested with native base64 encode from my AlmaLinux and with Notepad++ Base64 encode : [root]# echo "Q09OTkVDVAphY2NlcHQtdmVyc2lvbjoxLjEsMS4yCmhlYXJ0LWJlYXQ6MTAwMDAsMTAwMDAKQXV0aG9yaXphdGlvbjpCZWFyZXIgZXlKaGJHY2lPaUpJVXpVeE1pSjkuZXlKemRXSWlPaUl6T0RjeElpd2ljMk52Y0dWeklqcGJJbUZqWTJWemMxOTBiMnRsYmlKZExDSnBZWFFpT2pFMk5qTTNOVEk1TURnc0ltbHpjeUk2SW1oMGRIQnpPaTh2WVhCcExXZHNiMkpoYkMxd2NtOWtMbUZwY21Oc2IzVmthRzl0WlM1amIyMHZhV0Z0SWl3aVlYVmtJam9pWVdseVkyeHZkV1JuYkc5aVlXd3RjSEp2WkNJc0ltVjRjQ0k2TVRZMk16YzFORGN3T0gwLnFVNHI2blJDWEZnSFAxSU9DMTBvRzhSR1k0MGdfNTdwZ2N4WWV4ckY1TFhQRjltZzlvVUJvWWlWR19aaWJFYm8xRlNxcnJzRlZBdW93VXRHNjNVZTRRCgoA" | ./websocat.x86_64-unknown-linux-musl -vv --text --base64 wss://notification-global-prod.aircloudhome.com/rac-notifications/websocket [INFO websocat::lints] Auto-inserting the line mode [DEBUG websocat] Done third phase of interpreting options. [DEBUG websocat] Done fourth phase of interpreting options. [DEBUG websocat] Preparation done. Now actually starting. [DEBUG websocat::sessionserve] Serving Line2Message(ThreadedStdio) to Message2Line(WsClient("wss://notification-global-prod.aircloudhome.com/rac-notifications/websocket")) with Options { websocket_text_mode: true, websocket_protocol: None, websocket_reply_protocol: None, udp_oneshot_mode: false, udp_broadcast: false, udp_multicast_loop: false, udp_ttl: None, udp_join_multicast_addr: [], udp_join_multicast_iface_v4: [], udp_join_multicast_iface_v6: [], udp_reuseaddr: false, unidirectional: false, unidirectional_reverse: false, max_messages: None, max_messages_rev: None, exit_on_eof: false, oneshot: false, unlink_unix_socket: false, unix_socket_accept_from_fd: false, exec_args: [], ws_c_uri: "ws://0.0.0.0/", linemode_strip_newlines: false, linemode_strict: false, origin: None, custom_headers: [], custom_reply_headers: [], websocket_version: None, websocket_dont_close: false, websocket_ignore_zeromsg: false, one_message: false, no_auto_linemode: false, buffer_size: 65536, broadcast_queue_len: 16, read_debt_handling: Warn, linemode_zero_terminated: false, restrict_uri: None, serve_static_files: [], exec_set_env: false, no_exit_on_zeromsg: false, reuser_send_zero_msg_on_disconnect: false, process_zero_sighup: false, process_exit_sighup: false, socks_destination: None, auto_socks5: None, socks5_bind_script: None, tls_domain: None, tls_insecure: false, headers_to_env: [], max_parallel_conns: None, ws_ping_interval: None, ws_ping_timeout: None, request_uri: None, request_method: None, request_headers: [], autoreconnect_delay_millis: 20, ws_text_prefix: None, ws_binary_prefix: None, ws_binary_base64: true, ws_text_base64: false, close_status_code: None, close_reason: None, asyncstdio: false, foreachmsg_wait_reads: false, announce_listens: false, timestamp_monotonic: false, print_ping_rtts: false } [INFO websocat::stdio_threaded_peer] get_stdio_peer (threaded) [DEBUG websocat::sessionserve] Underlying connection established [INFO websocat::ws_client_peer] get_ws_client_peer [INFO websocat::ws_client_peer] Connected to ws [DEBUG websocat::my_copy] zero len [DEBUG websocat::my_copy] read_done [DEBUG websocat::my_copy] done [INFO websocat::sessionserve] Forward finished [DEBUG websocat::sessionserve] Forward shutdown finished [DEBUG websocat::ws_peer] drop WsWriteWrapper [INFO websocat::ws_peer] Received WebSocket close message [DEBUG websocat::ws_peer] The close message is Some(CloseData { status_code: 1000, reason: "" }) [DEBUG websocat::my_copy] BrokenPipe: read_done [DEBUG websocat::my_copy] done [INFO websocat::sessionserve] Reverse finished [DEBUG websocat::sessionserve] Reverse shutdown finished [INFO websocat::sessionserve] Both directions finished

Same base64 with Postman :

PostMan-Base64

lb544 commented 1 year ago

Sorry for the double post I find the solution by using -b -base64 option :

[root@ scripts]# echo "Q09OTkVDVAphY2NlcHQtdmVyc2lvbjoxLjEsMS4yCmhlYXJ0LWJlYXQ6MTAwMDAsMTAwMDAKQXV0aG9yaXphdGlvbjpCZWFyZXIgZXlKaGJHY2lPaUpJVXpVeE1pSjkuZXlKemRXSWlPaUl6T0RjeElpd2ljMk52Y0dWeklqcGJJbUZqWTJWemMxOTBiMnRsYmlKZExDSnBZWFFpT2pFMk5qTTNOak0xTmpFc0ltbHpjeUk2SW1oMGRIQnpPaTh2WVhCcExXZHNiMkpoYkMxd2NtOWtMbUZwY21Oc2IzVmthRzl0WlM1amIyMHZhV0Z0SWl3aVlYVmtJam9pWVdseVkyeHZkV1JuYkc5aVlXd3RjSEp2WkNJc0ltVjRjQ0k2TVRZMk16YzJOVE0yTVgwLjZWTGp6WnB5QlhZUWM4VkpfbzA1Mi13c2pmb2hhVWxKb1RzSFc3MFNPV1hycDBrS1Bhc3Z1djY1cWVsT2cwdTVrb3BzRW1FU05lNDJsR[censored]" | ./websocat.x86_64-unknown-linux-musl -b --base64 wss://notification-global-prod.aircloudhome.com/rac-notifications/websocket CONNECTED version:1.2 heart-beat:10000,10000 user-name:User(id=xxxx, email=xxxx, phoneNumber=null, password=null, softLockCount=0, lockUntil=null, detailsUserInfo=null, status=null, socialAccounts=[])

Thanks for your help :)

vi commented 1 year ago

--text --base64

For text mode (without -b) you need to use --base64-text, not --base64.

In binary mode just --base64 may be unreliable, as messages are assumed to be splittable arbitrarily by short reads from stdin. If --base64-text works instead of -b, you may prefer it. Alternatively, you can explicitly request line-based framing by using options like websocat -b --base64 line2msg:- wss://....

If you are using echo ... | websocat, you may be interested in -n option (i.e. don't close connection when stdin has been finished).

[censored]

Note that you posted base64 buffer with the auth token unredacted in pre-previous comment, so you may want to reset auth.