Project
vgrep
Description
The HTTP client in src/server/client.rs constructs raw HTTP requests by directly interpolating user-configurable host and port values into the request headers without any CRLF (\r\n) sanitization. This allows an attacker who can control the server configuration (via config file, environment variables, or CLI arguments) to inject arbitrary HTTP headers into outgoing requests.
Vulnerable Code Locations
Line 73-84 (search method):
let request = format!(
"POST /search HTTP/1.1\r\n\
Host: {}\r\n\ // host_port directly interpolated
Content-Type: application/json\r\n\
Content-Length: {}\r\n\
Connection: close\r\n\
\r\n\
{}",
host_port, // <-- NO SANITIZATION
body.len(),
body
);
Line 121-132 (embed_batch method):
let http_request = format!(
"POST /embed_batch HTTP/1.1\r\n\
Host: {}\r\n\
Content-Type: application/json\r\n\
...
Line 163-167 (health method):
let request = format!(
"GET /health HTTP/1.1\r\n\
Host: {}\r\n\
...
Attack Vector
The host value comes from:
- Config file (
~/.vgrep/config.json → server_host)
- Environment variable (
VGREP_HOST)
- CLI argument (
--host)
If an attacker can set server_host to a value containing CRLF sequences, they can inject additional headers:
# Malicious host value
export VGREP_HOST=$'127.0.0.1\r\nX-Injected: malicious\r\nX-Another: header'
vgrep "test query"
This would produce:
POST /search HTTP/1.1
Host: 127.0.0.1
X-Injected: malicious
X-Another: header
Content-Type: application/json
...
Error Message
Debug Logs
System Information
Bounty Version: 0.1.0
OS: Ubuntu 24.04 LTS
CPU: AMD EPYC-Genoa Processor (8 cores)
RAM: 15 GB
Screenshots
No response
Steps to Reproduce
Method 1: Via Environment Variable
# Set malicious host with CRLF injection
export VGREP_HOST=$'127.0.0.1\r\nX-Pwned: true'
# Run any vgrep command that contacts the server
vgrep "test query"
# Or check the health endpoint
# The injected header will be sent in the HTTP request
Method 2: Via Config File
# Modify config to include CRLF in host
cat > ~/.vgrep/config.json << 'EOF'
{
"server_host": "127.0.0.1\r\nX-Injected: malicious",
"server_port": 7777
}
EOF
# Run vgrep - the injected headers are sent
vgrep status
Method 3: Test Script
#[test]
fn test_crlf_injection_vulnerability() {
use vgrep::server::Client;
// Malicious host with CRLF
let malicious_host = "127.0.0.1\r\nX-Injected: evil";
let client = Client::new(malicious_host, 7777);
// The base_url now contains CRLF
assert!(client.base_url.contains("\r\n"), "CRLF should be blocked!");
}
Expected Behavior
- The client should reject or sanitize host values containing CRLF characters (
\r, \n)
- Invalid characters in the host should cause an error, not be silently accepted
- HTTP headers should be properly escaped or constructed using a safe HTTP library
Actual Behavior
- CRLF characters in the host configuration are passed through without validation
- The raw HTTP request includes the injected headers
- No error or warning is shown to the user
- Attacker can inject arbitrary HTTP headers into all outgoing requests
Additional Context
Security Impact
| Aspect |
Impact |
| Severity |
HIGH |
| CVSS Score |
7.5 (High) |
| Attack Vector |
Local (requires config/env access) |
| Privileges Required |
Low (can write to config or set env) |
| Impact |
Request smuggling, cache poisoning, auth bypass |
Potential Attack Scenarios
- Request Smuggling: Inject
Transfer-Encoding: chunked or Content-Length to cause request smuggling
- Cache Poisoning: Inject
X-Forwarded-Host to poison caches
- Authentication Bypass: Inject
Authorization headers if vgrep server is behind a proxy
- Session Hijacking: Inject
Cookie headers if targeting a web service
Why This Is Different From Existing Issues
| Existing Issue |
This Bug |
| #115 - Response size limit |
This is about request injection, not response handling |
| #98 - No timeout |
This is about header injection, not timing |
| #39 - Chunked encoding |
This is about injecting headers, not parsing responses |
| #124 - CORS |
This is client-side injection, not server-side CORS |
Suggested Fix
Option 1: Validate host at construction time
impl Client {
pub fn new(host: &str, port: u16) -> Result<Self, Error> {
// Reject CRLF in host
if host.contains('\r') || host.contains('\n') {
return Err(Error::InvalidHost("Host contains invalid characters"));
}
Ok(Self {
base_url: format!("http://{}:{}", host, port),
})
}
}
Option 2: Use a proper HTTP library
// Instead of raw TCP, use reqwest or ureq
use reqwest::blocking::Client;
impl VgrepClient {
pub fn search(&self, query: &str) -> Result<SearchResponse> {
let client = Client::new();
let response = client
.post(&format!("{}/search", self.base_url))
.json(&SearchRequest { query: query.to_string(), ..Default::default() })
.send()?;
Ok(response.json()?)
}
}
Option 3: Escape special characters
fn sanitize_host(host: &str) -> String {
host.chars()
.filter(|c| !matches!(c, '\r' | '\n' | '\0'))
.collect()
}
Project
vgrep
Description
The HTTP client in
src/server/client.rsconstructs raw HTTP requests by directly interpolating user-configurablehostandportvalues into the request headers without any CRLF (\r\n) sanitization. This allows an attacker who can control the server configuration (via config file, environment variables, or CLI arguments) to inject arbitrary HTTP headers into outgoing requests.Vulnerable Code Locations
Line 73-84 (search method):
Line 121-132 (embed_batch method):
Line 163-167 (health method):
Attack Vector
The
hostvalue comes from:~/.vgrep/config.json→server_host)VGREP_HOST)--host)If an attacker can set
server_hostto a value containing CRLF sequences, they can inject additional headers:This would produce:
Error Message
Debug Logs
System Information
Screenshots
No response
Steps to Reproduce
Method 1: Via Environment Variable
Method 2: Via Config File
Method 3: Test Script
Expected Behavior
\r,\n)Actual Behavior
Additional Context
Security Impact
Potential Attack Scenarios
Transfer-Encoding: chunkedorContent-Lengthto cause request smugglingX-Forwarded-Hostto poison cachesAuthorizationheaders if vgrep server is behind a proxyCookieheaders if targeting a web serviceWhy This Is Different From Existing Issues
Suggested Fix
Option 1: Validate host at construction time
Option 2: Use a proper HTTP library
Option 3: Escape special characters