
Wprowadzenie do problemu / definicja luki
Pod koniec grudnia 2025 ujawniono krytyczną podatność w langchain-core (Python) – podstawowej bibliotece ekosystemu LangChain – która pozwala atakującemu „przemycić” spreparowaną strukturę danych do procesu serializacji/deserializacji i w efekcie wyciągać sekrety (np. zmienne środowiskowe) oraz inicjować niebezpieczne ścieżki wykonania w ramach obiektów frameworka. Luka otrzymała identyfikator CVE-2025-68664 i przydomek LangGrinch.
Równolegle opisano podobny problem w implementacji LangChain.js (CVE-2025-68665), dotyczący sposobu serializacji w JavaScript/TypeScript.
W skrócie
- CVE-2025-68664 (Python / langchain-core): podatność typu serialization injection w dumps()/dumpd(), oceniona jako krytyczna (CVSS 9.3).
- Mechanizm nadużycia opiera się o wewnętrzny znacznik "lc", który LangChain traktuje jako sygnał, iż dane reprezentują „prawdziwy” obiekt frameworka, a nie zwykły słownik.
- Najczęstszy wektor: pola odpowiedzi LLM (np. additional_kwargs, response_metadata) sterowane pośrednio przez prompt injection, a następnie serializowane i deserializowane w przepływach strumieniowych.
- Poprawki: aktualizacja do langchain-core 0.3.81 lub 1.2.5 (w zależności od gałęzi).
- Dodatkowo: analogiczna luka w LangChain.js (CVE-2025-68665, CVSS 8.6) – poprawione m.in. w @langchain/core 0.3.80 / 1.1.8 i langchain 0.3.37 / 1.2.3.
Kontekst / historia / powiązania
LangChain i langchain-core stały się fundamentem wielu wdrożeń „agentowych” (orchestracja narzędzi, pamięć, streaming, logowanie zdarzeń). Problem LangGrinch jest groźny nie dlatego, iż dotyczy rzadkiego modułu, ale dlatego, iż dotyka mechanizmu wymiany/utrwalania danych (serializacja), który bywa używany „w tle” w popularnych API i integracjach.
W praktyce to kolejny przykład klasycznej kategorii błędów (deserializacja danych niezaufanych), ale w nowym kontekście: LLM output jako dane wejściowe. Wiele zespołów przez cały czas traktuje odpowiedź modelu jak „bezpieczny tekst”, podczas gdy jest to treść, którą przeciwnik może kształtować promptami, danymi w RAG, a czasem choćby treścią zewnętrznych źródeł.
Analiza techniczna / szczegóły luki
Na czym polega „serialization injection” w LangChain?
W LangChain istnieje wewnętrzny format, który opisuje obiekty frameworka jako struktury danych. Klucz lc jest częścią tego mechanizmu: sygnalizuje, iż dana struktura ma być traktowana jak serializowany obiekt LangChain.
W CVE-2025-68664 problem polegał na tym, iż funkcje dumps() i dumpd() nie „uciekały” (nie neutralizowały) słowników zawierających lc w dowolnych, swobodnych danych. Gdy taki wynik został później przepuszczony przez load()/loads(), wstrzyknięta struktura mogła zostać zinterpretowana jako legalny obiekt LangChain, a nie zwykłe dane użytkownika.
Co realnie umożliwia atak?
Z advisory wynika kilka praktycznych wektorów:
- Ekstrakcja sekretów z env – historycznie ryzykowny wariant, bo wcześniejsze domyślne ustawienia pozwalały automatycznie pobierać sekrety ze zmiennych środowiskowych podczas deserializacji (secrets_from_env było domyślnie włączone).
- Instancjonowanie klas w „zaufanych” przestrzeniach nazw (langchain_core, langchain, langchain_community) – choćby jeżeli to nie jest „dowolna klasa z systemu”, przez cały czas mogą istnieć konstruktory z efektami ubocznymi (połączenia sieciowe, operacje na plikach, itp.).
- Powiązanie z prompt injection – ponieważ pola typu additional_kwargs/response_metadata mogą zostać ukształtowane przez atakującego (np. przez wymuszenie specyficznego JSON-a w odpowiedzi), a potem trafić do serializacji w strumieniowaniu.
Cyata opisuje też scenariusze, w których instancjonowane obiekty mogą powodować wyjściowe żądania sieciowe albo prowadzić do dalszych eskalacji, jeżeli aplikacja po deserializacji wykona kolejne kroki „ufając” obiektom.
Co zmieniły poprawki (i dlaczego mogą „boleć”)?
W przypadku Pythona łatka nie tylko naprawia błąd escapowania, ale też wprowadza utwardzenie bezpieczeństwa:
- domyślna allowlista (allowed_objects="core"),
- secrets_from_env domyślnie False,
- blokada szablonów Jinja2 przez init_validator (zmiana potencjalnie „breaking” dla części użytkowników).
Praktyczne konsekwencje / ryzyko
Największe ryzyka dla zespołów budujących aplikacje i agentów LLM:
- Wyciek kluczy API (LLM provider, narzędzia, bazy wektorowe, systemy zewnętrzne), jeżeli środowisko wykonawcze ma sekrety w zmiennych środowiskowych, a ścieżka deserializacji została osiągnięta.
- Nieoczekiwane zachowanie agenta – atakujący może „dosztukować” struktury, które zmienią sposób działania łańcucha, logowania, pamięci, narzędzi lub dalszego generowania odpowiedzi (w praktyce: prompt injection + nadużycie serializacji).
- Efekty uboczne w zaufanych klasach – choćby bez pełnego RCE, sam fakt inicjowania ruchu wychodzącego, odczytów plików czy nietypowych operacji może być bolesny (SSRF, exfil, kosztowe DoS).
Rekomendacje operacyjne / co zrobić teraz
- Zaktualizuj zależności natychmiast
- Python: przejdź na langchain-core 0.3.81 albo 1.2.5 (zależnie od używanej linii).
- JS: @langchain/core 0.3.80 / 1.1.8 oraz langchain 0.3.37 / 1.2.3.
- Załóż, iż output LLM to dane niezaufane
- Traktuj additional_kwargs, response_metadata, metadata jak payload z internetu.
- Jeśli logujesz/serializujesz odpowiedzi modelu – wprowadź walidację i/lub filtrację (np. blokada klucza lc w danych swobodnych).
- Usuń automatyczne ładowanie sekretów z env przy deserializacji
- Po łatkach domyślnie jest bezpieczniej, ale warto audytować kod: czy gdziekolwiek jawnie włączasz secrets_from_env / secretsFromEnv.
- Ogranicz deserializację do minimum
- Jeśli musisz używać load()/loads(): trzymaj się allowlisty i nie deserializuj niczego, co może pochodzić od użytkownika/LLM/RAG/cache/hub bez walidacji.
- Sprawdź „wrażliwe” ścieżki z advisory
- Python: szczególnie przypadki użycia streamingu i narzędzi, które wewnętrznie serializują zdarzenia (np. astream_events w wersji v1, Runnable.astream_log() i inne wskazane w advisory).
- Dodaj kontrolę w pipeline
- SCA/Dependabot + blokada wdrożeń z podatnymi wersjami.
- SBOM i alertowanie przy wykryciu langchain-core w podatnym zakresie.
Różnice / porównania z innymi przypadkami
- Python (CVE-2025-68664): podatność w dumps()/dumpd() + twarde zmiany bezpieczeństwa w load()/loads() (allowlista, wyłączenie sekretów z env, blokada Jinja2).
- JavaScript (CVE-2025-68665): podatność w Serializable.toJSON() / JSON.stringify() + deserializacja przez load(); hardening obejmuje m.in. jawne wyłączenie secretsFromEnv oraz limit głębokości (maxDepth) przeciw DoS.
W obu światach wspólny mianownik jest ten sam: framework myli dane użytkownika z danymi strukturalnymi (bo klucz lc ma specjalne znaczenie), a to tworzy „most” między prompt injection a klasycznymi kategoriami błędów bezpieczeństwa.
Podsumowanie / najważniejsze wnioski
LangGrinch (CVE-2025-68664) to sygnał ostrzegawczy dla zespołów budujących agentów i aplikacje LLM: jeżeli jakikolwiek fragment odpowiedzi modelu trafia do serializacji/deserializacji, to w praktyce traktujesz LLM jak niezaufanego nadawcę danych. Najważniejsze działania to szybka aktualizacja do wersji naprawionych, ograniczenie deserializacji, wyłączenie automatycznego ładowania sekretów z env i wprowadzenie allowlist / walidacji struktur.
Źródła / bibliografia
- The Hacker News – opis CVE-2025-68664 i CVE-2025-68665 oraz podatne/naprawione wersje (The Hacker News)
- GitHub Advisory (langchain-core, CVE-2025-68664 / GHSA-c67j-w6g6-q2cm) – wektory ataku, wpływ, zmiany hardening (GitHub)
- GitHub Advisory (LangChain.js, CVE-2025-68665 / GHSA-r399-636x-v7f6) – zakres npm, hardening load() (GitHub)
- Cyata Research – kontekst „LangGrinch”, scenariusze ryzyka w agentach (Cyata)




