Podsumowanie projektu IDPS AI
Opis projektu
Projekt zakłada wykorzystanie uczenia maszynowego do stworzenia aplikacji przechwytującej ataki po protokołach HTTP/HTTPS za pomocą regresji logistycznej.
Model
Model został wytrenowany na danych dostępnych na portalu Kaggle, udostępniającym dane do trenowania modeli. Z danych, które udało się uzyskać model nabył zdolność do wykrywania ataków XSS, Command Injection I SQL injection. Na ten moment model ma skuteczność 73% stwierdzoną na podstawie automatycznego testu, więcej w rozdziale "Testy"
Architektura Aplikacji
Aplikacja została zbudowana w oparciu o bibliotkę "Man In The Middle Proxy", która ułatwia przechwytywanie ruchu HTTP i HTTPS, likwidując potrzebę pisania kodu do zestawiania tunelu, wymiany certyfikatów i umożliwia pisanie wtyczek, które zachowują się jak filtry, pozwalając na blokowanie, odczyt lub modyifikacje zapytań
Biblioteki
Biblioteka Man In The Middle Proxy
Główna biblioteka, odpowiadająca za zestawie monitorowanego połączenia między użytkownikiem a serwerem aplikacji.
Biblioteka Scikit-Learn
Biblioteka oferująca gotowe klasy do trenowania podstawowych modeli w tym modeli regresyjnych.
Biblioteka Pandas
Biblioteka wspomagająca operacje na danych do uczenia maszynowego
Bibilioteka OS
Wbudowany w python zbiór funkcji systemowych, między innymi operacje na ścieżkch
Struktura klasy wtyczki
class AIIDS:
def **init**(self):
# Inicjalizacja klasy, ustawienie parametów uczenia maszynowego
def train(self):
# Trenowanie modelu na danych z pliku
def predict(self, text):
# Próba przewidzenia czy podany text jest atakiem
def request(self, flow: http.HTTPFlow):
# Metoda wymagana przez mitm proxy, jest wywoływana za każdym razem kiedy do aplikacji kierowany jest ruch
# Dopisek wymagany, aby mitm proxy wiedziało która klasa jest odpowiedzialna z przechwytywanie ruchu
addons = [AIIDS()]
Testy
Przypadki
| # | Payload | Typ ataku | Oczekiwany | Otrzymany | Status |
|---|---|---|---|---|---|
| 1 | 1 UNION SELECT null, username || '\~' || password FROM users -- | SQLI | 403 | 403 | OK |
| 2 | 105' OR 1=1 | SQLI | 403 | 200 | FAIL |
| 3 | ' OR EXISTS(SELECT \* FROM users WHERE username='admin') -- | SQLI | 403 | 403 | OK |
| 4 | '; EXEC xp_cmdshell('dir'); -- | SQLI | 403 | 403 | OK |
| 5 | ' AND SLEEP(5) -- | SQLI | 403 | 403 | OK |
| 6 | " OR "" = " | SQLI | 403 | 403 | OK |
| 7 | abc | SQLI | 200 | 200 | OK |
| 8 | 105 | SQLI | 200 | 200 | OK |
| 9 | 1 ORDER BY 10 -- | SQLI | 403 | 403 | OK |
| 10 | 1; SHUTDOWN -- | SQLI | 403 | 200 | FAIL |
| 11 | <iframe src='javascript:alert(1)'></iframe> | XSS | 403 | 403 | OK |
| 12 | <body onload=alert('test')> | XSS | 403 | 403 | OK |
| 13 | <math><mi>x</mi><mtext><svg onload=alert(1)></svg></mtext></math> | XSS | 403 | 403 | OK |
| 14 | <div onmouseover=alert(1)>XSS</div> | XSS | 403 | 403 | OK |
| 15 | ";alert(1);// | XSS | 403 | 403 | OK |
| 16 | Welcome back! | XSS | 200 | 200 | OK |
| 17 | <a href='http://example.com'>Click me</a> | XSS | 200 | 200 | OK |
| 18 | XSS | XSS | 200 | 200 | OK |
| 19 | <b>bold</b> | XSS | 200 | 200 | OK |
| 20 | <img src='x' onerror='console.log(1)'> | XSS | 403 | 200 | FAIL |
| 21 | 8.8.8.8; rm -rf / | CMDI | 403 | 200 | FAIL |
| 22 | 127.0.0.1 && sleep 5 | CMDI | 403 | 403 | OK |
| 23 | localhost | whoami | CMDI | 403 | 200 | FAIL |
| 24 | ;echo vulnerable | CMDI | 403 | 200 | FAIL |
| 25 | 192.168.1.1 | CMDI | 200 | 200 | OK |
| 26 | 127.0.0.1 | CMDI | 200 | 403 | FAIL |
| 27 | 8.8.4.4 | CMDI | 200 | 200 | OK |
| 28 | ;ping -c 4 evil.com | CMDI | 403 | 200 | FAIL |
| 29 | || ls | CMDI | 403 | 403 | OK |
| 30 | | id | CMDI | 403 | 403 | OK |
Struktura Testu Automatcznego
# Parametry testu
BASE_URL = "http://localhost"
TIMEOUT = 5
# Payload został wycięty ze względu na rozdział "Przypadki"
# Deklaracje poszczególnych punktów końcowych w aplikacji DVWA
def sqli(payload: str) -> int:
endpoint = f"{BASE_URL}/vulnerabilities/sqli/"
params = {"id": payload, "Submit": "Submit"}
resp = requests.get(endpoint, params=params, timeout=TIMEOUT)
return resp.status_code
def xss(payload: str) -> int:
endpoint = f"{BASE_URL}/vulnerabilities/xss_r/"
params = {"name": payload, "Submit": "Submit"}
resp = requests.get(endpoint, params=params, timeout=TIMEOUT)
return resp.status_code
def cmdi(payload: str) -> int:
endpoint = f"{BASE_URL}/vulnerabilities/exec/"
data = {"ip": payload, "Submit": "Submit"}
resp = requests.post(endpoint, data=data, timeout=TIMEOUT)
return resp.status_code
# Mapa zamiany tekstu na konkretną metodę
method_map = {
"sqli": sqli,
"xss": xss,
"cmdi": cmdi,
}
# Metoda odpowiedzialna za odpalanie testów automatycznych na podstawie zadeklarowanych przypadków
def run_tests():
total = len(tests)
correct = 0
failed_cases = []
print("\n=== Running tests ===\n")
for i, (payload, method_name, expected_status) in enumerate(tests, 1):
test_func = method_map[method_name]
try:
actual_status = test_func(payload)
match = actual_status == expected_status
if match:
correct += 1
status = "[OK]"
else:
status = "[FAIL]"
failed_cases.append(
(i, payload, method_name, expected_status, actual_status)
)
except Exception as e:
status = "[ERROR]"
failed_cases.append(
(i, payload, method_name, expected_status, f"ERROR: {e}")
)
actual_status = "ERR"
print(
f"{status} {i:02d} | {method_name.upper()} | payload={repr(payload):<30} | "
f"expected={expected_status}, got={actual_status}"
)
# Wydruk podsumowania testu w konsoli
print("\n=== Summary ===")
print(f"Correct: {correct}/{total}")
print(f"Accuracy: {correct / total \* 100:.2f}%")
if failed_cases:
print("\n Failed cases:")
for i, payload, method, expected, got in failed_cases:
print(
f" - Test {i}: {method.upper()} | {repr(payload)} | expected={expected}, got={got}"
)
# Dobra praktyka, która blokuje uruchamianie kodu w przypadku importu pliku jako biblioteki
if **name** == "**main**":
run_tests()
Wyniki
=== Running tests ===
[OK] 01 | SQLI | payload="1 UNION SELECT null, username || '\~' || password FROM users --" | expected=403, got=403
[FAIL] 02 | SQLI | payload='105 OR 1=1' | expected=403, got=200
[OK] 03 | SQLI | payload="' OR EXISTS(SELECT \* FROM users WHERE username='admin') --" | expected=403, got=403
[OK] 04 | SQLI | payload="'; EXEC xp_cmdshell('dir'); --" | expected=403, got=403
[OK] 05 | SQLI | payload="' AND SLEEP(5) --" | expected=403, got=403
[OK] 06 | SQLI | payload='" OR "" = "' | expected=403, got=403
[OK] 07 | SQLI | payload='abc' | expected=200, got=200
[OK] 08 | SQLI | payload='105' | expected=200, got=200
[OK] 09 | SQLI | payload='1 ORDER BY 10 --' | expected=403, got=403
[FAIL] 10 | SQLI | payload='1; SHUTDOWN --' | expected=403, got=200
[OK] 11 | XSS | payload="<iframe src='javascript:alert(1)'></iframe>" | expected=403, got=403
[OK] 12 | XSS | payload="<body onload=alert('test')>" | expected=403, got=403
[OK] 13 | XSS | payload='<math><mi>x</mi><mtext><svg onload=alert(1)></svg></mtext></math>' | expected=403, got=403
[OK] 14 | XSS | payload='<div onmouseover=alert(1)>XSS</div>' | expected=403, got=403
[OK] 15 | XSS | payload='";alert(1);//' | expected=403, got=403
[OK] 16 | XSS | payload='Welcome back!' | expected=200, got=200
[OK] 17 | XSS | payload="<a href='http://example.com'>Click me</a>" | expected=200, got=200
[OK] 18 | XSS | payload='XSS' | expected=200, got=200
[OK] 19 | XSS | payload='<b>bold</b>' | expected=200, got=200
[FAIL] 20 | XSS | payload="<img src='x' onerror='console.log(1)'>" | expected=403, got=200
[FAIL] 21 | CMDI | payload='8.8.8.8; rm -rf /' | expected=403, got=200
[OK] 22 | CMDI | payload='127.0.0.1 && sleep 5' | expected=403, got=403
[FAIL] 23 | CMDI | payload='localhost | whoami' | expected=403, got=200
[FAIL] 24 | CMDI | payload=';echo vulnerable' | expected=403, got=200
[OK] 25 | CMDI | payload='192.168.1.1' | expected=200, got=200
[FAIL] 26 | CMDI | payload='127.0.0.1' | expected=200, got=403
[OK] 27 | CMDI | payload='8.8.4.4' | expected=200, got=200
[FAIL] 28 | CMDI | payload=';ping -c 4 evil.com' | expected=403, got=200
[OK] 29 | CMDI | payload='|| ls' | expected=403, got=403
[OK] 30 | CMDI | payload='| id' | expected=403, got=403
=== Summary ===
Correct: 22/30
Accuracy: 73.33%
Failed cases:
- Test 2: SQLI | '105 OR 1=1' | expected=403, got=200
- Test 10: SQLI | '1; SHUTDOWN --' | expected=403, got=200
- Test 20: XSS | "<img src='x' onerror='console.log(1)'>" | expected=403, got=200
- Test 21: CMDI | '8.8.8.8; rm -rf /' | expected=403, got=200
- Test 23: CMDI | 'localhost | whoami' | expected=403, got=200
- Test 24: CMDI | ';echo vulnerable' | expected=403, got=200
- Test 26: CMDI | '127.0.0.1' | expected=200, got=403
- Test 28: CMDI | ';ping -c 4 evil.com' | expected=403, got=200Wnioski
System poprawnie sklasyfikował 22 spośród 30 przypadków, osiągając skuteczność na poziomie 73,33%. W większości przypadków poprawnie wykryto klasyczne formy ataków SQLi oraz XSS, co potwierdza skuteczność podejścia bazującego na analizie tekstowej payloadów. Problemy pojawiły się przede wszystkim w kontekście mniej oczywistych form ataków CMDi oraz bardziej subtelnych wariantów XSS.
aplikacja wykazuje dobrą skuteczność w wykrywaniu typowych form ataków aplikacyjnych. Jego zdolność detekcji można jednak zwiększyć poprzez uwzględnienie bardziej subtelnych lub rzadziej spotykanych wzorców ataków oraz dopracowanie procesu klasyfikacji Command Injection.
Analiza błędów (False Negative i False Positive)
Błędne klasyfikacje dotyczyły ośmiu przypadków:
- SQL Injection: '105 OR 1=1' oraz '1; SHUTDOWN --' nie zostały rozpoznane jako ataki. Może to sugerować, że model nie rozpoznaje prostych operatorów logicznych lub poleceń jako szkodliwych, jeśli ich forma nie odbiega znacznie od danych treningowych.
- XSS:
<img src='x' onerror='console.log(1)'> został sklasyfikowany jako bezpieczny, mimo że zawiera aktywny wektor XSS. Błąd ten może wynikać z niedostatecznej reprezentacji XSS z onerror w zbiorze treningowym lub z heurystyki ograniczającej się do słowa alert.
- Command Injection:
Aż pięć przypadków dotyczyło błędów klasyfikacji CMDi, w tym:
8.8.8.8; rm -rf /
localhost | whoami
;echo vulnerable
;ping -c 4 evil.com
Dodatkowo odnotowano jedno false positive – nieszkodliwy payload 127.0.0.1 został niesłusznie zablokowany.
Możliwe kierunki rozwoju
- Rozbudowa zbioru treningowego o bardziej różnorodne przykłady CMDi i XSS, w tym takie zawierające inne niż alert() funkcje JavaScript oraz polecenia shellowe.
- Dodanie warstwy heurystycznej lub filtrującej kontekst, np. rozpoznającej ciągi zaczynające się od ;, |, && w polach adresów IP jako podejrzane.
- Zastosowanie klasyfikatora typu multiclass (np. SQLi, XSS, CMDi, Benign) w celu umożliwienia dokładniejszego przypisania typu ataku.
- Weryfikacja przypadków false positive może również sugerować potrzebę większej precyzji w klasyfikacji danych nieszkodliwych, np. adresów IP.
- Dodanie interfejsu do obsługi graficznej aplikacji
- Zbieranie logów dodedykowanej aplikacji
- Blokada ip po zbyt wielu próbach zidentyfikowanych jako atak