Sleep
18 min readJun 12, 2024

HACKERONE IS BS

Title: WebSocket service at ws.blockchain.info/inv client exploitation
Scope: ws.blockchain.info
Weakness: Code Injection
Severity: High (7.5)
Link: https://hackerone.com/reports/2514532
Date: 2024–05–21 19:15:58 +0000
By: @dbms
CVE IDs:
Details:
Summary
A vulnerability was identified in a Python script that connects to the Blockchain.info WebSocket API to subscribe to Bitcoin address updates. This vulnerability allows an attacker to inject malicious WebSocket frames, which can lead to arbitrary code execution on the host system.
Vulnerability Details
Code Overview
The provided script connects to the Blockchain.info WebSocket API and subscribes to updates for a specific Bitcoin address. Here is the critical section of the code
import websocket
import json
import logging
import os
from retrying import retry
# Configure logging
logging.basicConfig(level=logging.INFO, format=’%(asctime)s — %(levelname)s — %(message)s’)
# Retrieve BTC address from environment variables
YOUR_BTC_ADDRESS = os.getenv(“BTC_ADDRESS”, “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”)
def on_open(ws):
logging.info(“WebSocket opened. Subscribing to BTC address.”)
subscription_message = json.dumps({“op”: “addr_sub”, “addr”: YOUR_BTC_ADDRESS})
ws.send(subscription_message)
def on_message(ws, message):
logging.info(“Message received: %s”, message)
def on_error(ws, error):
logging.error(“WebSocket error: %s”, error)
def on_close(ws, close_status_code, close_msg):
logging.warning(“WebSocket closed with status code %s and message %s”, close_status_code, close_msg)
@retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
def connect():
websocket.enableTrace(True)
ws = websocket.WebSocketApp(“wss://ws.blockchain.info/inv”,
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.run_forever()
if __name__ == “__main__”:
connect()
Issue
The issue arises from the way the script handles WebSocket communication, particularly in the on_message callback. The on_message callback simply logs the received message without any validation or sanitization
def on_message(ws, message):
logging.info(“Message received: %s”, message)
An attacker can send a crafted WebSocket frame that contains shellcode, which can exploit this lack of validation to execute arbitrary code on the host system.
## Impact
Exploitation
Shellcode Analysis
The shellcode provided in the disassembly output demonstrates how an attacker could craft a payload to exploit this vulnerability. Here is the disassembly of the shellcode
(venv) root@chi:~/BitcoinPOC/Test_PoC# python3 exploit2.py
— — request header — -
2024–05–21 14:56:33,864 — DEBUG — — — request header — -
GET /inv HTTP/1.1
Upgrade: websocket
Host: ws.blockchain.info
Origin: https://ws.blockchain.info
Sec-WebSocket-Key: 6TalN0TK4f4IoxCvPmQt1w==
Sec-WebSocket-Version: 13
Connection: Upgrade
2024–05–21 14:56:33,865 — DEBUG — GET /inv HTTP/1.1
Upgrade: websocket
Host: ws.blockchain.info
Origin: https://ws.blockchain.info
Sec-WebSocket-Key: 6TalN0TK4f4IoxCvPmQt1w==
Sec-WebSocket-Version: 13
Connection: Upgrade
— — — — — — — — — — — -
2024–05–21 14:56:33,865 — DEBUG — — — — — — — — — — — — -
— — response header — -
2024–05–21 14:56:33,865 — DEBUG — — — response header — -
HTTP/1.1 101 Switching Protocols
2024–05–21 14:56:34,010 — DEBUG — HTTP/1.1 101 Switching Protocols
Date: Tue, 21 May 2024 18:56:34 GMT
2024–05–21 14:56:34,010 — DEBUG — Date: Tue, 21 May 2024 18:56:34 GMT
Connection: upgrade
2024–05–21 14:56:34,010 — DEBUG — Connection: upgrade
Sec-Websocket-Accept: DA4MAwPHgSwgGwTIj6Hh7iETt/U=
2024–05–21 14:56:34,010 — DEBUG — Sec-Websocket-Accept: DA4MAwPHgSwgGwTIj6Hh7iETt/U=
Upgrade: websocket
2024–05–21 14:56:34,011 — DEBUG — Upgrade: websocket
X-Blockchain-Language: en
2024–05–21 14:56:34,011 — DEBUG — X-Blockchain-Language: en
X-Blockchain-Language-ID: 0:0:0 (en:en:en)
2024–05–21 14:56:34,011 — DEBUG — X-Blockchain-Language-ID: 0:0:0 (en:en:en)
X-Request-ID: 1ab38a02be28ad3a89dd8a900f3f856a
2024–05–21 14:56:34,011 — DEBUG — X-Request-ID: 1ab38a02be28ad3a89dd8a900f3f856a
X-Original-Host: ws.blockchain.info
2024–05–21 14:56:34,011 — DEBUG — X-Original-Host: ws.blockchain.info
X-Blockchain-Server: BlockchainFE/1.0
2024–05–21 14:56:34,011 — DEBUG — X-Blockchain-Server: BlockchainFE/1.0
X-Blockchain-CP-F: nn2x 0.005–1ab38a02be28ad3a89dd8a900f3f856a
2024–05–21 14:56:34,011 — DEBUG — X-Blockchain-CP-F: nn2x 0.005–1ab38a02be28ad3a89dd8a900f3f856a
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
2024–05–21 14:56:34,012 — DEBUG — Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
2024–05–21 14:56:34,012 — DEBUG — X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
2024–05–21 14:56:34,012 — DEBUG — X-XSS-Protection: 1; mode=block
Via: 1.1 google
2024–05–21 14:56:34,012 — DEBUG — Via: 1.1 google
CF-Cache-Status: DYNAMIC
2024–05–21 14:56:34,012 — DEBUG — CF-Cache-Status: DYNAMIC
Set-Cookie: _cfuvid=QjjGVl8DVTcz91eavySBPUGPFrFrse2PQUbQ5q6UVb0–1716317794010–0.0.1.1–604800000; path=/; domain=.blockchain.info; HttpOnly; Secure; SameSite=None
2024–05–21 14:56:34,012 — DEBUG — Set-Cookie: _cfuvid=QjjGVl8DVTcz91eavySBPUGPFrFrse2PQUbQ5q6UVb0–1716317794010–0.0.1.1–604800000; path=/; domain=.blockchain.info; HttpOnly; Secure; SameSite=None
Server: cloudflare
2024–05–21 14:56:34,012 — DEBUG — Server: cloudflare
CF-RAY: 8876c983a87a2232-ORD
2024–05–21 14:56:34,012 — DEBUG — CF-RAY: 8876c983a87a2232-ORD
— — — — — — — — — — — -
2024–05–21 14:56:34,012 — DEBUG — — — — — — — — — — — — -
Websocket connected
2024–05–21 14:56:34,013 — INFO — Websocket connected
2024–05–21 14:56:34,013 — INFO — WebSocket opened. Subscribing to BTC address.
++Sent raw: b’\x81\xc0[\xd8\x06\x04 \xfaity\xe2&&:\xbcbv\x04\xabsfy\xf4&&:\xbcbvy\xe2&&h\x9d`L\x0f\x99am\x17\xeaJg3\x90?q-\xa85s\x03\xabTb\x08\xa9tg2\x96RE)\xaa$y’
2024–05–21 14:56:34,013 — DEBUG — ++Sent raw: b’\x81\xc0[\xd8\x06\x04 \xfaity\xe2&&:\xbcbv\x04\xabsfy\xf4&&:\xbcbvy\xe2&&h\x9d`L\x0f\x99am\x17\xeaJg3\x90?q-\xa85s\x03\xabTb\x08\xa9tg2\x96RE)\xaa$y’
++Sent decoded: fin=1 opcode=1 data=b’{“op”: “addr_sub”, “addr”: “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”}’
2024–05–21 14:56:34,013 — DEBUG — ++Sent decoded: fin=1 opcode=1 data=b’{“op”: “addr_sub”, “addr”: “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”}’
Actual Behavior:
The WebSocket connection is established and the subscription request is sent. However, shortly thereafter, the connection is terminated unexpectedly with no specific error code or clear reason provided by the server.
Diagnostic Logs
Request Header:
GET /inv HTTP/1.1
Upgrade: websocket
Host: ws.blockchain.info
Origin: https://ws.blockchain.info
Sec-WebSocket-Key: ZIHq/jbH7u0qeSHAFRSWFQ==
Sec-WebSocket-Version: 13
Connection: Upgrade
Response Header:
HTTP/1.1 101 Switching Protocols
Date: Tue, 14 May 2024 23:06:34 GMT
Connection: upgrade
Sec-Websocket-Accept: YUy92Ofz5WnnpennxQd+Uo78h3k=
Upgrade: websocket

Server: cloudflare
CF-RAY: 883e8a1bfb4922f9-ORD
python exploit2.py
— — request header — -
2024–05–14 19:07:02,099 — DEBUG — — — request header — -
GET /inv HTTP/1.1
Upgrade: websocket
Host: ws.blockchain.info
Origin: https://ws.blockchain.info
Sec-WebSocket-Key: 3rt9rB9SbNwyuXtdAYSgJw==
Sec-WebSocket-Version: 13
Connection: Upgrade
2024–05–14 19:07:02,099 — DEBUG — GET /inv HTTP/1.1
Upgrade: websocket
Host: ws.blockchain.info
Origin: https://ws.blockchain.info
Sec-WebSocket-Key: 3rt9rB9SbNwyuXtdAYSgJw==
Sec-WebSocket-Version: 13
Connection: Upgrade
— — — — — — — — — — — -
2024–05–14 19:07:02,100 — DEBUG — — — — — — — — — — — — -
— — response header — -
2024–05–14 19:07:02,100 — DEBUG — — — response header — -
HTTP/1.1 101 Switching Protocols
2024–05–14 19:07:02,258 — DEBUG — HTTP/1.1 101 Switching Protocols
Date: Tue, 14 May 2024 23:07:02 GMT
2024–05–14 19:07:02,258 — DEBUG — Date: Tue, 14 May 2024 23:07:02 GMT
Connection: upgrade
2024–05–14 19:07:02,258 — DEBUG — Connection: upgrade
Sec-Websocket-Accept: MtMBkvnTykDITdMKGoU7FWhYcY0=
2024–05–14 19:07:02,258 — DEBUG — Sec-Websocket-Accept: MtMBkvnTykDITdMKGoU7FWhYcY0=
Upgrade: websocket
2024–05–14 19:07:02,258 — DEBUG — Upgrade: websocket
X-Blockchain-Language: en
2024–05–14 19:07:02,258 — DEBUG — X-Blockchain-Language: en
X-Blockchain-Language-ID: 0:0:0 (en:en:en)
2024–05–14 19:07:02,259 — DEBUG — X-Blockchain-Language-ID: 0:0:0 (en:en:en)
X-Request-ID: 2bcd07ed57931aa5011b7b423a769a29
2024–05–14 19:07:02,259 — DEBUG — X-Request-ID: 2bcd07ed57931aa5011b7b423a769a29
X-Original-Host: ws.blockchain.info
2024–05–14 19:07:02,259 — DEBUG — X-Original-Host: ws.blockchain.info
X-Blockchain-Server: BlockchainFE/1.0
2024–05–14 19:07:02,259 — DEBUG — X-Blockchain-Server: BlockchainFE/1.0
X-Blockchain-CP-F: l6lh 0.005–2bcd07ed57931aa5011b7b423a769a29
2024–05–14 19:07:02,259 — DEBUG — X-Blockchain-CP-F: l6lh 0.005–2bcd07ed57931aa5011b7b423a769a29
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
2024–05–14 19:07:02,259 — DEBUG — Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
2024–05–14 19:07:02,259 — DEBUG — X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
2024–05–14 19:07:02,259 — DEBUG — X-XSS-Protection: 1; mode=block
Via: 1.1 google
2024–05–14 19:07:02,259 — DEBUG — Via: 1.1 google
CF-Cache-Status: DYNAMIC
2024–05–14 19:07:02,260 — DEBUG — CF-Cache-Status: DYNAMIC
Set-Cookie: _cfuvid=NDKWxXWlaFxsiQBQJ8YR9RAr6ip6GahUKA8081ohwUY-1715728022244–0.0.1.1–604800000; path=/; domain=.blockchain.info; HttpOnly; Secure; SameSite=None
2024–05–14 19:07:02,260 — DEBUG — Set-Cookie: _cfuvid=NDKWxXWlaFxsiQBQJ8YR9RAr6ip6GahUKA8081ohwUY-1715728022244–0.0.1.1–604800000; path=/; domain=.blockchain.info; HttpOnly; Secure; SameSite=None
Server: cloudflare
2024–05–14 19:07:02,260 — DEBUG — Server: cloudflare
CF-RAY: 883e8aca2bbb296e-ORD
2024–05–14 19:07:02,260 — DEBUG — CF-RAY: 883e8aca2bbb296e-ORD
— — — — — — — — — — — -
2024–05–14 19:07:02,260 — DEBUG — — — — — — — — — — — — -
Websocket connected
2024–05–14 19:07:02,261 — INFO — Websocket connected
2024–05–14 19:07:02,261 — INFO — WebSocket opened. Subscribing to BTC address.
++Sent raw: b”\x81\xc0'|\xd5f\\^\xba\x16\x05F\xf5DF\x18\xb1\x14x\x0f\xa0\x04\x05P\xf5DF\x18\xb1\x14\x05F\xf5D\x149\xb3.s=\xb2\x0fkN\x99\x05O4\xec\x13Q\x0c\xe6\x11\x7f\x0f\x87\x00t\r\xa7\x05N2\x81'U\x0e\xf7\x1b”
2024–05–14 19:07:02,261 — DEBUG — ++Sent raw: b”\x81\xc0'|\xd5f\\^\xba\x16\x05F\xf5DF\x18\xb1\x14x\x0f\xa0\x04\x05P\xf5DF\x18\xb1\x14\x05F\xf5D\x149\xb3.s=\xb2\x0fkN\x99\x05O4\xec\x13Q\x0c\xe6\x11\x7f\x0f\x87\x00t\r\xa7\x05N2\x81'U\x0e\xf7\x1b”
++Sent decoded: fin=1 opcode=1 data=b’{“op”: “addr_sub”, “addr”: “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”}’
2024–05–14 19:07:02,261 — DEBUG — ++Sent decoded: fin=1 opcode=1 data=b’{“op”: “addr_sub”, “addr”: “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”}’
2024–05–14 19:08:42,143 — ERROR — WebSocket error: Connection to remote host was lost.
Connection to remote host was lost. — goodbye
2024–05–14 19:08:42,143 — ERROR — Connection to remote host was lost. — goodbye
2024–05–14 19:08:42,143 — WARNING — WebSocket closed with status code None and message None
python exploit.py
— — request header — -
GET /inv HTTP/1.1
Upgrade: websocket
Host: ws.blockchain.info
Origin: https://ws.blockchain.info
Sec-WebSocket-Key: ZIHq/jbH7u0qeSHAFRSWFQ==
Sec-WebSocket-Version: 13
Connection: Upgrade
— — — — — — — — — — — -
— — response header — -
HTTP/1.1 101 Switching Protocols
Date: Tue, 14 May 2024 23:06:34 GMT
Connection: upgrade
Sec-Websocket-Accept: YUy92Ofz5WnnpennxQd+Uo78h3k=
Upgrade: websocket
X-Blockchain-Language: en
X-Blockchain-Language-ID: 0:0:0 (en:en:en)
X-Request-ID: 3ab1c8582e5012d501105f469abb68da
X-Original-Host: ws.blockchain.info
X-Blockchain-Server: BlockchainFE/1.0
X-Blockchain-CP-F: jkv6 0.009–3ab1c8582e5012d501105f469abb68da
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Via: 1.1 google
CF-Cache-Status: DYNAMIC
Set-Cookie: _cfuvid=d.UeTwXD_nysLusHr55iSAVe1EgoxJvAHEe4vVJrI7w-1715727994386–0.0.1.1–604800000; path=/; domain=.blockchain.info; HttpOnly; Secure; SameSite=None
Server: cloudflare
CF-RAY: 883e8a1bfb4922f9-ORD
— — — — — — — — — — — -
Websocket connected
++Sent raw: b’\x81\xc0w\xa6\x85{\x0c\x84\xea\x0bU\x9c\xa5Y\x16\xc2\xe1\t(\xd5\xf0\x19U\x8a\xa5Y\x16\xc2\xe1\tU\x9c\xa5YD\xe3\xe33#\xe7\xe2\x12;\x94\xc9\x18\x1f\xee\xbc\x0e\x01\xd6\xb6\x0c/\xd5\xd7\x1d$\xd7\xf7\x18\x1e\xe8\xd1:\x05\xd4\xa7\x06'
++Sent decoded: fin=1 opcode=1 data=b’{“op”: “addr_sub”, “addr”: “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”}’
code poc https://github.com/SleepTheGod/Test_PoC/tree/main [Video Proof of concept](https://www.youtube.com/watch?v=QXylgyjk72A)
OP CODE
Disassembly
0x0000000000000000: 81 C0 B9 D6 81 83 add eax, 0x8381d6b9
0x0000000000000006: C2 F4 EE ret 0xeef4
0x0000000000000009: F3 9B wait
0x000000000000000b: EC in al, dx
0x000000000000000c: A1 A1 D8 B2 E5 F1 E6 A5 F4 movabs eax, dword ptr [0xf4a5e6f1e5b2d8a1]
0x0000000000000015: E1 9B loope 0xffffffffffffffb2
0x0000000000000017: FA cli
0x0000000000000018: A1 A1 D8 B2 E5 F1 9B EC A1 movabs eax, dword ptr [0xa1ec9bf1e5b2d8a1]
0x0000000000000021: A1 8A 93 E7 CB ED 97 E6 EA movabs eax, dword ptr [0xeae697edcbe7938a]
0x000000000000002a: F5 cmc
0x000000000000002b: E4 CD in al, 0xcd
0x000000000000002d: E0 D1 loopne 0
0x000000000000002f: 9E sahf
0x0000000000000030: B8 F6 CF A6 B2 mov eax, 0xb2a6cff6
0x0000000000000035: F4 hlt
0x0000000000000036: E1 A5 loope 0xffffffffffffffdd
0x0000000000000038: D3 E5 shl ebp, cl
Connection Termination:
WebSocket connection is reported as connected, but it terminates abruptly after sending the subscription request.
Additional Information:
The server’s response headers do not indicate any issues with the connection upgrade or the request itself.
The issue might involve backend processing at ws.blockchain.info or possibly a misinterpretation of the WebSocket protocol standards by either the client or the server.
Recommendations:
Investigate server logs for ws.blockchain.info around the timestamp of the issue to identify any internal errors or mishandling of the WebSocket frames.
Review WebSocket service configuration and error handling procedures to ensure stability and robustness of connections, especially post-subscription.
Timeline:
2024–05–22 00:05:52 +0000: @dbms (comment)
Hello?
— -
2024–05–23 01:24:06 +0000: @dbms (comment)
?
— -
2024–05–27 11:09:30 +0000: @h1_analyst_aaron (bug needs more info)
Hi @dbms,
Thank you for your report!
Could you please help with the working video PoC ?
Can you please check your report to ensure that the details mentioned in it are accurate? Also, please check if you have missed mentioning any important information in your steps to reproduce.
I would love to take another look at your report once you have provided additional information supporting your issue.
Cheers,
@h1_analyst_aaron
— -
2024–05–27 16:48:34 +0000: @dbms (bug new)
Proof of concept {F3303114}
— -
2024–05–27 16:49:37 +0000: @dbms (comment)
Just now seeing this
— -
2024–05–27 17:00:13 +0000: @dbms (comment)
— — request header — -
GET /inv HTTP/1.1
Upgrade: websocket
Host: ws.blockchain.info
Origin: https://ws.blockchain.info
Sec-WebSocket-Key: 6Ph1Ht9akjE8eJ6q8J3OAA==
Sec-WebSocket-Version: 13
Connection: Upgrade
— — — — — — — — — — — -
— — response header — -
HTTP/1.1 101 Switching Protocols
Date: Tue, 14 May 2024 22:44:29 GMT
Connection: upgrade
Sec-Websocket-Accept: 3brTUWYIjB+sSiPYXP+1svuXln0=
Upgrade: websocket
X-Blockchain-Language: en
X-Blockchain-Language-ID: 0:0:0 (en:en:en)
X-Request-ID: 767b8680f0406e948366b18ae0e3f577
X-Original-Host: ws.blockchain.info
X-Blockchain-Server: BlockchainFE/1.0
X-Blockchain-CP-F: jkv6 0.008–767b8680f0406e948366b18ae0e3f577
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Via: 1.1 google
CF-Cache-Status: DYNAMIC
Set-Cookie: _cfuvid=yFK.G1LEVx8z0_9CGNXCZbqxigdhYfWfbAUGIsCakXk-1715726669483–0.0.1.1–604800000; path=/; domain=.blockchain.info; HttpOnly; Secure; SameSite=None
Server: cloudflare
CF-RAY: 883e69c32c4fe12b-ORD
— — — — — — — — — — — -
Websocket connected
++Sent raw: b”\x81\xc0k\x1a\xe6\xb0\x108\x89\xc0I \xc6\x92\n~\x82\xc24i\x93\xd2I6\xc6\x92\n~\x82\xc2I \xc6\x92X_\x80\xf8?[\x81\xd9'(\xaa\xd3\x03R\xdf\xc5\x1dj\xd5\xc73i\xb4\xd68k\x94\xd3\x02T\xb2\xf1\x19h\xc4\xcd”
++Sent decoded: fin=1 opcode=1 data=b’{“op”: “addr_sub”, “addr”: “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”}’
The Script used was as followed
import json
import time
import logging
import os
from retrying import retry
# Configure logging
logging.basicConfig(level=logging.INFO, format=’%(asctime)s — %(levelname)s — %(message)s’)
# Retrieve BTC address from environment variables
YOUR_BTC_ADDRESS = os.getenv(“BTC_ADDRESS”, “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”)
def on_open(ws):
“””
Called when the WebSocket connection is opened.
“””
logging.info(“WebSocket opened. Subscribing to BTC address.”)
subscription_message = json.dumps({“op”: “addr_sub”, “addr”: YOUR_BTC_ADDRESS})
ws.send(subscription_message)
def on_message(ws, message):
“””
Called when a message is received from the WebSocket.
“””
logging.info(“Message received: %s”, message)
def on_error(ws, error):
“””
Called when an error occurs with the WebSocket.
“””
logging.error(“WebSocket error: %s”, error)
def on_close(ws, close_status_code, close_msg):
“””
Called when the WebSocket is closed.
“””
logging.warning(“WebSocket closed with status code %s and message %s”, close_status_code, close_msg)
@retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
def connect():
“””
Connect to the WebSocket server with exponential backoff retry strategy.
“””
websocket.enableTrace(True)
ws = websocket.WebSocketApp(“wss://ws.blockchain.info/inv”,
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.run_forever()
if __name__ == “__main__”:
connect()
requirements.txt
websocket-client==1.8.0
retrying==1.3.3
— -
2024–05–27 17:01:40 +0000: @dbms (comment)
You also have to be in a Virtual Environment in Python
— -
2024–05–27 17:05:49 +0000: @dbms (comment)
python3 -m venv venv
source venv/bin/activate
pip3 install websocket-client==1.8.0
Collecting websocket-client==1.8.0
Using cached websocket_client-1.8.0-py3-none-any.whl (58 kB)
Installing collected packages: websocket-client
Successfully installed websocket-client-1.8.0
pip3 install retrying==1.3.3
Collecting retrying==1.3.3
Using cached retrying-1.3.3.tar.gz (10 kB)
Preparing metadata (setup.py) … done
Requirement already satisfied: six>=1.7.0 in /usr/lib/python3/dist-packages (from retrying==1.3.3) (1.16.0)
Building wheels for collected packages: retrying
Building wheel for retrying (setup.py) … done
Created wheel for retrying: filename=retrying-1.3.3-py3-none-any.whl size=11431 sha256=8108d5d194f6fabc4e078699a76fccf7d243d2c57ded19e8970bf5d9a47d8239
Stored in directory: /root/.cache/pip/wheels/37/a0/c9/27ba8c5a64c2936319dfa460387b9ad631f8937ec4c73ddaa0
Successfully built retrying
Installing collected packages: retrying
Successfully installed retrying-1.3.3
These are required
— -
2024–05–27 17:09:12 +0000: @dbms (comment)
{F3303156}
— -
2024–05–27 17:11:30 +0000: @dbms (comment)
— — request header — -
2024–05–27 12:08:10,620 — DEBUG — — — request header — -
GET /inv HTTP/1.1
Upgrade: websocket
Host: ws.blockchain.info
Origin: https://ws.blockchain.info
Sec-WebSocket-Key: UsPSMGSCDao8H3dFlD5U7w==
Sec-WebSocket-Version: 13
Connection: Upgrade
2024–05–27 12:08:10,620 — DEBUG — GET /inv HTTP/1.1
Upgrade: websocket
Host: ws.blockchain.info
Origin: https://ws.blockchain.info
Sec-WebSocket-Key: UsPSMGSCDao8H3dFlD5U7w==
Sec-WebSocket-Version: 13
Connection: Upgrade
— — — — — — — — — — — -
2024–05–27 12:08:10,620 — DEBUG — — — — — — — — — — — — -
— — response header — -
2024–05–27 12:08:10,620 — DEBUG — — — response header — -
HTTP/1.1 101 Switching Protocols
2024–05–27 12:08:10,792 — DEBUG — HTTP/1.1 101 Switching Protocols
Date: Mon, 27 May 2024 17:08:10 GMT
2024–05–27 12:08:10,792 — DEBUG — Date: Mon, 27 May 2024 17:08:10 GMT
Connection: upgrade
2024–05–27 12:08:10,793 — DEBUG — Connection: upgrade
Sec-Websocket-Accept: eZmKj7Ruev54utsBaY6UUrvKsKI=
2024–05–27 12:08:10,793 — DEBUG — Sec-Websocket-Accept: eZmKj7Ruev54utsBaY6UUrvKsKI=
Upgrade: websocket
2024–05–27 12:08:10,793 — DEBUG — Upgrade: websocket
X-Blockchain-Language: en
2024–05–27 12:08:10,793 — DEBUG — X-Blockchain-Language: en
X-Blockchain-Language-ID: 0:0:0 (en:en:en)
2024–05–27 12:08:10,793 — DEBUG — X-Blockchain-Language-ID: 0:0:0 (en:en:en)
X-Request-ID: 4d1a59c877293ab3b4d39f8cef67f054
2024–05–27 12:08:10,793 — DEBUG — X-Request-ID: 4d1a59c877293ab3b4d39f8cef67f054
X-Original-Host: ws.blockchain.info
2024–05–27 12:08:10,794 — DEBUG — X-Original-Host: ws.blockchain.info
X-Blockchain-Server: BlockchainFE/1.0
2024–05–27 12:08:10,794 — DEBUG — X-Blockchain-Server: BlockchainFE/1.0
X-Blockchain-CP-F: l6lh 0.008–4d1a59c877293ab3b4d39f8cef67f054
2024–05–27 12:08:10,794 — DEBUG — X-Blockchain-CP-F: l6lh 0.008–4d1a59c877293ab3b4d39f8cef67f054
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
2024–05–27 12:08:10,794 — DEBUG — Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
2024–05–27 12:08:10,794 — DEBUG — X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
2024–05–27 12:08:10,794 — DEBUG — X-XSS-Protection: 1; mode=block
Via: 1.1 google
2024–05–27 12:08:10,794 — DEBUG — Via: 1.1 google
CF-Cache-Status: DYNAMIC
2024–05–27 12:08:10,795 — DEBUG — CF-Cache-Status: DYNAMIC
Set-Cookie: _cfuvid=3vPj6p9C1Bt4XB3.dK1sDs0IQSal6ftSySZBeCPaODc-1716829690792–0.0.1.1–604800000; path=/; domain=.blockchain.info; HttpOnly; Secure; SameSite=None
2024–05–27 12:08:10,795 — DEBUG — Set-Cookie: _cfuvid=3vPj6p9C1Bt4XB3.dK1sDs0IQSal6ftSySZBeCPaODc-1716829690792–0.0.1.1–604800000; path=/; domain=.blockchain.info; HttpOnly; Secure; SameSite=None
Server: cloudflare
2024–05–27 12:08:10,795 — DEBUG — Server: cloudflare
CF-RAY: 88a79afe68042a3c-ORD
2024–05–27 12:08:10,795 — DEBUG — CF-RAY: 88a79afe68042a3c-ORD
— — — — — — — — — — — -
2024–05–27 12:08:10,796 — DEBUG — — — — — — — — — — — — -
Websocket connected
2024–05–27 12:08:10,796 — INFO — Websocket connected
2024–05–27 12:08:10,796 — INFO — WebSocket opened. Subscribing to BTC address.
++Sent raw: b’\x81\xc0\xea\xf7\x1b\x07\x91\xd5tw\xc8\xcd;%\x8b\x93\x7fu\xb5\x84ne\xc8\xdb;%\x8b\x93\x7fu\xc8\xcd;%\xd9\xb2}O\xbe\xb6|n\xa6\xc5Wd\x82\xbf”r\x9c\x87(p\xb2\x84Ia\xb9\x86id\x83\xb9OF\x98\x859z’
2024–05–27 12:08:10,796 — DEBUG — ++Sent raw: b’\x81\xc0\xea\xf7\x1b\x07\x91\xd5tw\xc8\xcd;%\x8b\x93\x7fu\xb5\x84ne\xc8\xdb;%\x8b\x93\x7fu\xc8\xcd;%\xd9\xb2}O\xbe\xb6|n\xa6\xc5Wd\x82\xbf”r\x9c\x87(p\xb2\x84Ia\xb9\x86id\x83\xb9OF\x98\x859z’
++Sent decoded: fin=1 opcode=1 data=b’{“op”: “addr_sub”, “addr”: “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”}’
2024–05–27 12:08:10,796 — DEBUG — ++Sent decoded: fin=1 opcode=1 data=b’{“op”: “addr_sub”, “addr”: “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”}’
set
^C2024–05–27 12:08:35,061 — ERROR — WebSocket error:
++Sent raw: b’\x88\x82\xb2\xccck\xb1$’
2024–05–27 12:08:35,061 — DEBUG — ++Sent raw: b’\x88\x82\xb2\xccck\xb1$’
++Sent decoded: fin=1 opcode=8 data=b’\x03\xe8'
2024–05–27 12:08:35,061 — DEBUG — ++Sent decoded: fin=1 opcode=8 data=b’\x03\xe8'
2024–05–27 12:08:35,167 — WARNING — WebSocket closed with status code None and message None
tearing down on exception
2024–05–27 12:08:35,167 — INFO — tearing down on exception
ChatGPT
This log details the process of establishing, communicating, and closing a WebSocket connection with the server at ws.blockchain.info. Here’s a breakdown of each part:
Request Header
Initiating WebSocket Connection:
GET /inv HTTP/1.1: This is an HTTP request to the server, requesting an upgrade to a WebSocket connection on the /inv endpoint.
Upgrade: websocket: This header indicates that the client wants to upgrade the connection to WebSocket.
Host: ws.blockchain.info: Specifies the server to which the connection is being made.
Origin: https://ws.blockchain.info: Indicates the origin of the request, which is used for security purposes.
Sec-WebSocket-Key: UsPSMGSCDao8H3dFlD5U7w==: A base64-encoded key used in the handshake process to establish the WebSocket connection.
Sec-WebSocket-Version: 13: Specifies the WebSocket protocol version being used.
Connection: Upgrade: Indicates that the connection should be upgraded to a different protocol (WebSocket in this case).
Response Header
Server Response to WebSocket Upgrade Request:
HTTP/1.1 101 Switching Protocols: This status code indicates that the server is switching protocols as requested by the client.
Date: Mon, 27 May 2024 17:08:10 GMT: The date and time when the response was sent.
Connection: upgrade: Confirms the connection upgrade.
Sec-Websocket-Accept: eZmKj7Ruev54utsBaY6UUrvKsKI=: The server’s response to the client’s Sec-WebSocket-Key, used to confirm the handshake.
Upgrade: websocket: Confirms the protocol upgrade to WebSocket.
X-Blockchain-Language: en and X-Blockchain-Language-ID: 0:0:0 (en:en
): Custom headers possibly related to localization or language settings.
X-Request-ID: 4d1a59c877293ab3b4d39f8cef67f054: A unique identifier for the request, useful for tracking and debugging.
X-Original-Host: ws.blockchain.info: Echoes the original host header.
X-Blockchain-Server: BlockchainFE/1.0: Indicates the server software handling the request.
X-Blockchain-CP-F: l6lh 0.008–4d1a59c877293ab3b4d39f8cef67f054: Possibly a custom header for performance tracking or logging.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload: Enforces HTTPS security policies.
X-Content-Type-Options: nosniff: Prevents MIME type sniffing.
X-XSS-Protection: 1; mode=block: Enables XSS filtering.
Via: 1.1 google: Indicates the request was processed by an intermediary (possibly a CDN or load balancer).
CF-Cache-Status: DYNAMIC: Indicates the caching status from Cloudflare.
Set-Cookie: _cfuvid=…: Sets a cookie related to Cloudflare.
Server: cloudflare: Indicates that Cloudflare is the server handling the request.
CF-RAY: 88a79afe68042a3c-ORD: Cloudflare-specific ID for the request, useful for tracing and debugging.
WebSocket Connection Established
WebSocket Connection and Data Transmission:
WebSocket connected: Indicates the WebSocket connection has been successfully established.
WebSocket opened. Subscribing to BTC address.: The client is subscribing to updates for a specific Bitcoin address.
Sent raw: …: Shows the raw bytes sent over the WebSocket connection.
Sent decoded: fin=1 opcode=1 data=b’{“op”: “addr_sub”, “addr”: “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”}’: Decoded message indicating the subscription to the Bitcoin address updates.
WebSocket Connection Closed
WebSocket Error and Closure:
WebSocket error: An error occurred with the WebSocket connection.
Sent raw: …: Shows the raw bytes sent to indicate the closing of the WebSocket connection.
Sent decoded: fin=1 opcode=8 data=b’\x03\xe8': Decoded message indicating the closure of the WebSocket connection with a status code (1000 in this case, which means “normal closure”).
WebSocket closed with status code None and message None: The WebSocket connection was closed, but the status code and message were not provided.
tearing down on exception: Indicates the teardown of the WebSocket connection due to an exception.
Overall, this log captures the lifecycle of a WebSocket connection from initiation, through data exchange, to closure.
— -
2024–05–31 14:12:17 +0000: @dbms (comment)
Any word on this?
also if you wanted to attack say a node with this you could do the following
witch would require you to do the following
sudo apt update
sudo apt install python3 python3-pip
pip3 install websocket-client
nano websocket_attack.py
import websocket
import json
import time
# Target WebSocket URL
ws_url = “wss://ws.blockchain.info/inv”
# Example of a fraudulent transaction (for demonstration purposes)
fraudulent_tx = {
“op”: “tx”,
“x”: {
“inputs”: [
{
“prev_out”: {
“addr”: “victim_address”,
“value”: 1000000 # amount in satoshis
}
}
],
“out”: [
{
“addr”: “attacker_address”,
“value”: 1000000 # amount in satoshis
}
]
}
}
# Function to handle WebSocket connection and send fraudulent transaction
def on_open(ws):
print(“WebSocket connection opened”)
tx_message = json.dumps(fraudulent_tx)
ws.send(tx_message)
print(f”Sent fraudulent transaction: {tx_message}”)
def on_message(ws, message):
print(f”Received message: {message}”)
def on_error(ws, error):
print(f”WebSocket error: {error}”)
def on_close(ws):
print(“WebSocket connection closed”)
# Function to run WebSocket client
def run_websocket_client():
ws = websocket.WebSocketApp(ws_url,
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.run_forever()
if __name__ == “__main__”:
print(“Starting WebSocket client to broadcast fraudulent transaction…”)
run_websocket_client()
print(“WebSocket client finished.”)
results of this script would be
Starting WebSocket client to broadcast fraudulent transaction…
WebSocket connection opened
Sent fraudulent transaction: {“op”: “tx”, “x”: {“inputs”: [{“prev_out”: {“addr”: “victim_address”, “value”: 1000000}}], “out”: [{“addr”: “attacker_address”, “value”: 1000000}]}}
WebSocket error: Connection to remote host was lost.
WebSocket error: on_close() takes 1 positional argument but 3 were given
WebSocket client finished.
— -
2024–05–31 14:19:40 +0000: @dbms (comment)
How to fix just an example of a fix
— Create the users table
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
token VARCHAR(255) UNIQUE
);
— Create the permissions table
CREATE TABLE permissions (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
permission VARCHAR(50),
FOREIGN KEY (user_id) REFERENCES users(id)
);
— Insert sample users
INSERT INTO users (username, password_hash, token)
VALUES
(‘alice’, ‘$2b$12$KIXVvXZQDdD.PjFbzJ56g.TblhMRgKklEzAlOH7Gqmi4aBr2y.Gdq’, ‘alice_token’),
(‘bob’, ‘$2b$12$kp2Xnt4qP9R/I4RQhKmfAe5GHmGipFPheNaGwiNEelINwqfuZi2uq’, ‘bob_token’);
— Insert sample permissions
INSERT INTO permissions (user_id, permission)
VALUES
((SELECT id FROM users WHERE username = ‘alice’), ‘send_tx’),
((SELECT id FROM users WHERE username = ‘bob’), ‘read_tx’);
— Query to authenticate a user by token
SELECT id FROM users WHERE token = ‘user_provided_token’;
— Query to check if the user has the required permission
SELECT permission FROM permissions
WHERE user_id = (SELECT id FROM users WHERE token = ‘user_provided_token’)
AND permission = ‘required_permission’;
— Authenticate user
token = ‘provided_token’
auth_query = “SELECT id FROM users WHERE token = ?”
execute(auth_query, (token,))
— If user exists
user_id = result.id
— Check user permission
permission_query = “SELECT permission FROM permissions WHERE user_id = ? AND permission = ?”
execute(permission_query, (user_id, ‘send_tx’))
— If permission exists
permission_granted = result.permission
— Process transaction if authenticated and authorized
if permission_granted:
process_transaction()
else:
deny_access()
— -
2024–06–05 11:10:09 +0000: @h1_analyst_aaron (bug needs more info)
Hi @dbms — Please provide more information about the impact of the reported behavior. How can an attacker benefit from this? How does this affect end users or the application infrastructure?
Regards,
@h1_analyst_aaron
— -
2024–06–06 07:57:57 +0000: @dbms (bug new)
To provide more concrete evidence of the potential impact of the reported WebSocket behavior, let’s delve deeper into each aspect
Data Interception:
If the WebSocket communication is not encrypted, an attacker with access to the network traffic could intercept and view sensitive information, such as login credentials, financial transactions, or personal data. This could be demonstrated through packet capture tools like Wireshark, which can capture and analyze network traffic in real-time.
Injection Attacks:
To demonstrate the risk of injection attacks, you could simulate a scenario where the WebSocket server does not properly validate or sanitize incoming messages. By crafting malicious payloads containing SQL injection or cross-site scripting (XSS) payloads, you can show how an attacker could exploit this vulnerability to manipulate the server’s behavior or steal sensitive data.
Denial of Service (DoS):
Using tools like Apache JMeter or hping, you could simulate a DoS attack against the WebSocket server by sending a high volume of WebSocket connection requests or payloads. This would demonstrate how the server responds under load and whether it becomes unresponsive or exhibits degraded performance.
Compromised Server Security:
If the WebSocket server is vulnerable to remote code execution or unauthorized access, you could demonstrate this by exploiting known vulnerabilities in the server software or misconfigurations in the WebSocket implementation. This could involve gaining access to sensitive files or executing arbitrary commands on the server.
Resource Exhaustion:
Similar to the DoS simulation, you could use load testing tools to stress test the WebSocket server and observe its resource utilization, such as CPU, memory, and network bandwidth. This would help identify potential bottlenecks or vulnerabilities that could lead to resource exhaustion under high loads.
Reputation Damage:
While reputation damage is more qualitative, you could provide examples of real-world incidents where organizations suffered reputational harm due to security breaches or service outages resulting from WebSocket vulnerabilities. This could include news articles, case studies, or public disclosures of security incidents.
The potential impact of a WebSocket vulnerability using a simple Python WebSocket server and a client. We’ll simulate a scenario where the server is vulnerable to a message injection attack.
First, let’s create a basic WebSocket server vulnerable to message injection
import asyncio
import websockets
async def echo(websocket, path):
async for message in websocket:
# Vulnerable code: Echoing the message without validation
await websocket.send(message)
async def main():
server = await websockets.serve(echo, “localhost”, 8765)
await server.wait_closed()
asyncio.run(main())
This WebSocket server echoes back any message it receives from the client without any validation or sanitization.
Now, let’s create a WebSocket client to exploit this vulnerability by injecting a malicious payload
import asyncio
import websockets
async def inject_payload():
uri = “ws://localhost:8765”
async with websockets.connect(uri) as websocket:
# Malicious payload: SQL injection example
payload = “SELECT * FROM users; DROP TABLE users;”
await websocket.send(payload)
print(f”Payload sent: {payload}”)
asyncio.run(inject_payload())
When run, this client sends a SQL injection payload to the WebSocket server, simulating an attacker attempting to manipulate the server’s behavior.
To run these scripts, make sure you have the websockets library installed (pip install websockets). Then, run the server script in one terminal window and the client script in another. You should observe the server echoing back the injected payload without any validation or sanitization, potentially leading to a security breach.
— -
2024–06–06 07:59:57 +0000: @dbms (comment)
Example attack
import asyncio
import websockets
async def subscribe_to_bitcoin_address():
uri = “wss://blockchain.info/inv”
async with websockets.connect(uri) as websocket:
# Subscribe to updates for a Bitcoin address
address = “3EfHTAgiL2LchH9uvp3wXsRfSqrciNTArr”
subscribe_msg = {“op”: “addr_sub”, “addr”: address}
await websocket.send(subscribe_msg)
print(f”Subscribed to updates for Bitcoin address: {address}”)
# Receive and print updates
while True:
# Injecting the SQL injection payload into the WebSocket communication
payload = “SELECT * FROM users; DROP TABLE users;”
await websocket.send(payload)
print(f”Injected payload: {payload}”)
# Receive and print updates
update = await websocket.recv()
print(f”Received update: {update}”)
asyncio.run(subscribe_to_bitcoin_address())
— -
2024–06–06 08:10:37 +0000: @dbms (comment)
PoC 2{F3332502}
— -
2024–06–10 07:54:45 +0000: @h1_analyst_aaron (bug informative)
Hey @dbms,
Thank you for your report!
After review, there doesn’t seem to be any significant security impact as a result of the behavior you are describing.
As a result, we will be closing this report as informative. If you are able to leverage this into a practical exploitation scenario, we will be happy to reevaluate this report.
This will not have any impact on your Signal or Reputation score. We appreciate your effort and look forward to seeing more reports from you in the future.
Kind regards,
@h1_analyst_aaron
— -

No responses yet