Files
c-relay/tests/sql_injection_tests.sh

254 lines
9.5 KiB
Bash
Executable File

#!/bin/bash
# SQL Injection Test Suite for C-Relay
# Comprehensive testing of SQL injection vulnerabilities across all filter types
set -e
# Configuration
RELAY_HOST="127.0.0.1"
RELAY_PORT="8888"
TEST_TIMEOUT=10
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Test counters
TOTAL_TESTS=0
PASSED_TESTS=0
FAILED_TESTS=0
# Function to send WebSocket message and check for SQL injection success
test_sql_injection() {
local description="$1"
local message="$2"
TOTAL_TESTS=$((TOTAL_TESTS + 1))
echo -n "Testing $description... "
# Send message via websocat and capture response
local response
response=$(echo "$message" | timeout 2 websocat ws://$RELAY_HOST:$RELAY_PORT 2>/dev/null | head -1 || echo 'TIMEOUT')
# Check if the response indicates successful query execution (which would be bad)
# Look for signs that SQL injection worked (like database errors or unexpected results)
if [[ "$response" == "TIMEOUT" ]]; then
echo -e "${YELLOW}UNCERTAIN${NC} - Connection timeout (may indicate crash)"
FAILED_TESTS=$((FAILED_TESTS + 1))
return 1
elif [[ -z "$response" ]]; then
# Empty response - relay silently rejected malformed input
echo -e "${GREEN}PASSED${NC} - SQL injection blocked (silently rejected)"
PASSED_TESTS=$((PASSED_TESTS + 1))
return 0
elif [[ "$response" == *"NOTICE"* ]] && [[ "$response" == *"error:"* ]]; then
# Relay properly rejected the input with a NOTICE error message
echo -e "${GREEN}PASSED${NC} - SQL injection blocked (rejected with error)"
PASSED_TESTS=$((PASSED_TESTS + 1))
return 0
elif [[ "$response" == *"EOSE"* ]] || [[ "$response" == *"COUNT"* ]] || [[ "$response" == *"EVENT"* ]]; then
# Query completed normally - this is expected for properly sanitized input
echo -e "${GREEN}PASSED${NC} - SQL injection blocked (query sanitized)"
PASSED_TESTS=$((PASSED_TESTS + 1))
return 0
elif [[ "$response" == *"SQL"* ]] || [[ "$response" == *"syntax"* ]]; then
# Database error leaked - potential vulnerability
echo -e "${RED}FAILED${NC} - SQL error leaked: $response"
FAILED_TESTS=$((FAILED_TESTS + 1))
return 1
else
# Unknown response
echo -e "${YELLOW}UNCERTAIN${NC} - Unexpected response: $response"
FAILED_TESTS=$((FAILED_TESTS + 1))
return 1
fi
}
# Function to test valid message (should work normally)
test_valid_query() {
local description="$1"
local message="$2"
TOTAL_TESTS=$((TOTAL_TESTS + 1))
echo -n "Testing $description... "
local response
response=$(echo "$message" | timeout 2 websocat ws://$RELAY_HOST:$RELAY_PORT 2>/dev/null | head -1 || echo 'TIMEOUT')
if [[ "$response" == *"EOSE"* ]] || [[ "$response" == *"EVENT"* ]]; then
echo -e "${GREEN}PASSED${NC} - Valid query works"
PASSED_TESTS=$((PASSED_TESTS + 1))
return 0
else
echo -e "${RED}FAILED${NC} - Valid query failed: $response"
FAILED_TESTS=$((FAILED_TESTS + 1))
return 1
fi
}
echo "=========================================="
echo "C-Relay SQL Injection Test Suite"
echo "=========================================="
echo "Testing against relay at ws://$RELAY_HOST:$RELAY_PORT"
echo
# Test basic connectivity first
echo "=== Basic Connectivity Test ==="
test_valid_query "Basic connectivity" '["REQ","basic_test",{}]'
echo
# SQL Injection Payloads
SQL_PAYLOADS=(
# Classic SQL injection
"'; DROP TABLE events; --"
"1' OR '1'='1"
"1; SELECT * FROM sqlite_master; --"
"' OR 1=1 --"
"admin'--"
"1' UNION SELECT password FROM users --"
# Time-based blind SQL injection
"1' AND SLEEP(5) --"
"1' AND 1=1 WAITFOR DELAY '0:0:5' --"
# Error-based SQL injection
"1' AND 1=CAST((SELECT version()) AS INT) --"
"1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --"
# Union-based injection
"' UNION SELECT NULL,NULL,NULL --"
"' UNION SELECT 1,2,3 --"
"' UNION ALL SELECT NULL,NULL,NULL --"
# Stacked queries
"'; SELECT * FROM events; --"
"'; DELETE FROM events; --"
"'; UPDATE events SET content='hacked' WHERE 1=1; --"
# Comment injection
"/*"
"*/"
"/**/"
"--"
"#"
# Hex encoded injection
"0x53514C5F494E4A454354494F4E" # SQL_INJECTION in hex
# Base64 encoded injection
"J1NSTCBJTkpFQ1RJT04gLS0=" # 'SQL INJECTION -- in base64
# Nested injection
"'))); DROP TABLE events; --"
"')) UNION SELECT NULL; --"
# Boolean-based blind injection
"' AND 1=1 --"
"' AND 1=2 --"
"' AND (SELECT COUNT(*) FROM events) > 0 --"
# Out-of-band injection (if supported)
"'; EXEC master..xp_cmdshell 'net user' --"
"'; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --"
)
echo "=== Authors Filter SQL Injection Tests ==="
for payload in "${SQL_PAYLOADS[@]}"; do
test_sql_injection "Authors filter with payload: $payload" "[\"REQ\",\"sql_test_authors_$RANDOM\",{\"authors\":[\"$payload\"]}]"
done
echo
echo "=== IDs Filter SQL Injection Tests ==="
for payload in "${SQL_PAYLOADS[@]}"; do
test_sql_injection "IDs filter with payload: $payload" "[\"REQ\",\"sql_test_ids_$RANDOM\",{\"ids\":[\"$payload\"]}]"
done
echo
echo "=== Kinds Filter SQL Injection Tests ==="
# Test numeric kinds with SQL injection attempts (these will fail JSON parsing, which is expected)
test_sql_injection "Kinds filter with string injection" "[\"REQ\",\"sql_test_kinds_$RANDOM\",{\"kinds\":[\"1' OR '1'='1\"]}]"
test_sql_injection "Kinds filter with negative value" "[\"REQ\",\"sql_test_kinds_$RANDOM\",{\"kinds\":[-1]}]"
test_sql_injection "Kinds filter with very large value" "[\"REQ\",\"sql_test_kinds_$RANDOM\",{\"kinds\":[999999999]}]"
echo
echo "=== Search Filter SQL Injection Tests ==="
for payload in "${SQL_PAYLOADS[@]}"; do
test_sql_injection "Search filter with payload: $payload" "[\"REQ\",\"sql_test_search_$RANDOM\",{\"search\":\"$payload\"}]"
done
echo
echo "=== Tag Filter SQL Injection Tests ==="
TAG_PREFIXES=("#e" "#p" "#t" "#r" "#d")
for prefix in "${TAG_PREFIXES[@]}"; do
for payload in "${SQL_PAYLOADS[@]}"; do
test_sql_injection "$prefix tag filter with payload: $payload" "[\"REQ\",\"sql_test_tag_$RANDOM\",{\"$prefix\":[\"$payload\"]}]"
done
done
echo
echo "=== Timestamp Filter SQL Injection Tests ==="
# Test since/until parameters
test_sql_injection "Since parameter injection" "[\"REQ\",\"sql_test_since_$RANDOM\",{\"since\":\"1' OR '1'='1\"}]"
test_sql_injection "Until parameter injection" "[\"REQ\",\"sql_test_until_$RANDOM\",{\"until\":\"1; DROP TABLE events; --\"}]"
echo
echo "=== Limit Parameter SQL Injection Tests ==="
test_sql_injection "Limit parameter injection" "[\"REQ\",\"sql_test_limit_$RANDOM\",{\"limit\":\"1' OR '1'='1\"}]"
test_sql_injection "Limit with UNION" "[\"REQ\",\"sql_test_limit_$RANDOM\",{\"limit\":\"0 UNION SELECT password FROM users\"}]"
echo
echo "=== Complex Multi-Filter SQL Injection Tests ==="
# Test combinations that might bypass validation
test_sql_injection "Multi-filter with authors injection" "[\"REQ\",\"sql_test_multi_$RANDOM\",{\"authors\":[\"admin'--\"],\"kinds\":[1],\"search\":\"anything\"}]"
test_sql_injection "Multi-filter with search injection" "[\"REQ\",\"sql_test_multi_$RANDOM\",{\"authors\":[\"valid\"],\"search\":\"'; DROP TABLE events; --\"}]"
test_sql_injection "Multi-filter with tag injection" "[\"REQ\",\"sql_test_multi_$RANDOM\",{\"#e\":[\"'; SELECT * FROM sqlite_master; --\"],\"limit\":10}]"
echo
echo "=== COUNT Message SQL Injection Tests ==="
# Test COUNT messages which might have different code paths
for payload in "${SQL_PAYLOADS[@]}"; do
test_sql_injection "COUNT with authors payload: $payload" "[\"COUNT\",\"sql_count_authors_$RANDOM\",{\"authors\":[\"$payload\"]}]"
test_sql_injection "COUNT with search payload: $payload" "[\"COUNT\",\"sql_count_search_$RANDOM\",{\"search\":\"$payload\"}]"
done
echo
echo "=== Edge Case SQL Injection Tests ==="
# Test edge cases that might bypass validation
test_sql_injection "Empty string injection" "[\"REQ\",\"sql_edge_$RANDOM\",{\"authors\":[\"\"]}]"
test_sql_injection "Null byte injection" "[\"REQ\",\"sql_edge_$RANDOM\",{\"authors\":[\"admin\\x00' OR '1'='1\"]}]"
test_sql_injection "Unicode injection" "[\"REQ\",\"sql_edge_$RANDOM\",{\"authors\":[\"admin' OR '1'='1' -- 💣\"]}]"
test_sql_injection "Very long injection payload" "[\"REQ\",\"sql_edge_$RANDOM\",{\"search\":\"$(printf 'a%.0s' {1..1000})' OR '1'='1\"}]"
echo
echo "=== Subscription ID SQL Injection Tests ==="
# Test if subscription IDs can be used for injection
test_sql_injection "Subscription ID injection" "[\"REQ\",\"'; DROP TABLE subscriptions; --\",{}]"
test_sql_injection "Subscription ID with quotes" "[\"REQ\",\"sub\"'; SELECT * FROM events; --\",{}]"
echo
echo "=== CLOSE Message SQL Injection Tests ==="
# Test CLOSE messages
test_sql_injection "CLOSE with injection" "[\"CLOSE\",\"'; DROP TABLE subscriptions; --\"]"
echo
echo "=== Test Results ==="
echo "Total tests: $TOTAL_TESTS"
echo -e "Passed: ${GREEN}$PASSED_TESTS${NC}"
echo -e "Failed: ${RED}$FAILED_TESTS${NC}"
if [[ $FAILED_TESTS -eq 0 ]]; then
echo -e "${GREEN}✓ All SQL injection tests passed!${NC}"
echo "The relay appears to be protected against SQL injection attacks."
exit 0
else
echo -e "${RED}✗ SQL injection vulnerabilities detected!${NC}"
echo "The relay may be vulnerable to SQL injection attacks."
echo "Failed tests: $FAILED_TESTS"
exit 1
fi