Średnia

Ekstraktor Fortran w Graphify: niekompletne zabezpieczenie cpp #include pozwala na odczyt/eksfiltrację dowolnych plików hosta przy indeksowaniu niezaufanego repozytorium

  • Ujawniono: 19.10.2026
  • Aktualizacja: 20.10.2026 09:53

Podsumowanie

Krok preprocesora C w ekstraktorze Fortran (graphify/extractors/fortran.py, _cpp_preprocess) stosuje niekompletne zabezpieczenie #include. Flagi -nostdinc -I /dev/null, na których się opiera (komentarz „Security (F-007)”), nie powstrzymują pliku źródłowego Fortran z wielką literą F przed dołączaniem ścieżek bezwzględnych (#include "/etc/passwd") lub przed wyjściem poza korpus poprzez path traversal (#include "../../../../home/victim/.ssh/id_rsa"). cpp zawsze przeszukuje katalog preprocesowanego pliku pod kątem dołączeń w cudzysłowach i zawsze respektuje bezwzględne ścieżki include, niezależnie od -nostdinc/-I. W rezultacie indeksowanie niezaufanego repozytorium zawierającego plik .F/.F90/.F95/.F03/.F08 powoduje odczytanie dowolnych lokalnych plików z hosta i wstrzyknięcie ich zawartości do grafu wiedzy, który następnie jest przekazywany do modelu LLM/agenta oraz zapisywany w plikach wyjściowych na dysku (graph.json, GRAPH_REPORT.md, wiki Obsidian, eksporty HTML/SVG).

Jest to dokładnie ten sam „wektor eksfiltracji plików ze strony korpusu”, przed którym według komentarza ma chronić mechanizm F-007; zabezpieczenie to można jednak pominąć.

Poziom zagrożenia

Wysoki. Odczyt dowolnych plików lokalnych na hoście wywoływany przez podstawową operację graphify (indeksowanie niezaufanego katalogu), obchodzący udokumentowany mechanizm kontroli bezpieczeństwa. Działa w domyślnej, działającej offline ścieżce AST bez użycia klucza API (graphify update . / graphify extract) — nie są wymagane poświadczenia, połączenie sieciowe ani wywołania LLM. CWE-73 (zewnętrzna kontrola nazwy/ścieżki pliku) / CWE-22 (path traversal) / CWE-200 (ujawnienie informacji).

Gdzie występuje

graphify/extractors/fortran.py, _cpp_preprocess():

result = subprocess.run(
    ["cpp", "-w", "-P", "-nostdinc", "-I", "/dev/null", str(path.resolve())],
    capture_output=True, timeout=30,
)

Osiągane z poziomu extract_fortran() (ten sam plik) dla dowolnego rozszerzenia z _FORTRAN_CPP_EXTS = {".F", ".F90", ".F95", ".F03", ".F08"}, które są domyślnie zbierane (detect.CODE_EXTENSIONS) i przekazywane przez extract._DISPATCH.

Dlaczego zabezpieczenie nie działa

-nostdinc usuwa jedynie standardowe systemowe katalogi include, a -I /dev/null ustawia jedynie dodatkową ścieżkę wyszukiwania. Żadne z nich nie wpływa na:

  1. Bezwzględne ścieżki include — #include "/etc/passwd" jest otwierany bezpośrednio przez cpp.
  2. Ścieżki include w cudzysłowach względne do bieżącego pliku — cpp zawsze przeszukuje katalog zawierający przetwarzany plik dla #include "...", więc #include "../../../../etc/shadow" wychodzi poza korpus.

Proof of Concept (zweryfikowany)

Odtworzono na wydanym kodzie przy użyciu dokładnego polecenia budowanego przez _cpp_preprocess (cpp -w -P -nostdinc -I /dev/null <file>) oraz bezpośrednio na extract_fortran().

Sekret na dysku (/tmp/secret.txt): TOP_SECRET_...

(a) Include bezwzględny — evil_abs.F90:

program evil
#include "/absolute/path/to/secret.txt"
end program evil

(b) Traversal względny — evil_rel.F90:

program evil
#include "../secret.txt"
end program evil

Oba warianty generują wynik preprocesora zawierający pełną zawartość tajnego pliku ("SECRET IN OUTPUT: True" dla obu).

Ujawnienie w grafie — leak.F90 umieszcza include w pozycji identyfikatora poprzez swobodną kontynuację wiersza, dzięki czemu ujawniona wartość staje się węzłem/krawędzią:

module m
contains
subroutine s
use &
#include "/path/to/token.secret"
end subroutine s
end module m

extract_fortran() na tym pliku tworzy węzeł, którego etykietą jest cała jednowierszowa zawartość token.secret oraz krawędź imports prowadząca do niego. Węzeł ten trafia do graph.json / GRAPH_REPORT.md / wiki i jest zwracany przez narzędzia MCP query_graph / get_node / get_neighbors — tj. zostaje wyeksfiltrowany do kontekstu agenta oraz do trwałych, współdzielonych artefaktów.

Uwaga dotycząca dokładności: Fortran nie rozróżnia wielkości liter, więc przechwycone identyfikatory są zamieniane na małe litery. Dane niewrażliwe na wielkość liter (ścieżki plików, nazwy hostów, nazwy użytkowników, tokeny szesnastkowe, fragmenty parametrów połączenia oraz sam fakt istnienia/częściowa zawartość czytelnego pliku) wyciekają bez zakłóceń; dane o wysokiej entropii zależne od wielkości liter (base64/JWT) ulegają częściowemu uszkodzeniu przez zmianę liter na małe, lecz nadal potwierdzają czytelność i strukturę. Pliki sekretów z pojedynczym tokenem w wierszu (pliki z kluczami API, poświadczeniami/tokenami w jednej linii) wyciekają w całości (zamienione na małe litery). Wymaga obecności cpp/gcc w PATH (powszechne w środowiskach deweloperskich i CI, w które celuje graphify); w przypadku braku bezpiecznie powraca do przetwarzania surowych bajtów.

Sugerowane rozwiązanie

Zastosowanie podejścia izolacji używanego już w innych miejscach kodu (_project_xml_is_safe, security.validate_graph_path): przed wywołaniem cpp przeskanować źródło w poszukiwaniu dyrektyw #include i odrzucać lub neutralizować każde dołączenie, którego cel jest bezwzględny lub wskazuje poza katalog główny korpusu/repozytorium. Konkretnie w _cpp_preprocess:

  • Parsować dyrektywy #include "..." / #include <...>.
  • Rozwiązywać każdy cel względem katalogu pliku; jeśli jest on bezwzględny lub jego .resolve() wychodzi poza korzeń skanowania, usunąć tę linię (lub powrócić do path.read_bytes() bez preprocesowania).
  • Zezwalać wyłącznie na dołączenia wskazujące na ścieżkę wewnątrz korzenia korpusu.

Prostszą, ale bezpieczną alternatywą jest usunięcie wszystkich linii #include przed preprocesowaniem (makra nadal będą rozwijane; dołączenia międzyplikowe stracą na dokładności, ale odczyt plików hosta będzie niemożliwy). Uruchomienie cpp wewnątrz piaskownicy systemu plików (bwrap/unshare z zamontowanym wyłącznie korpusem) również rozwiązałoby problem, lecz jest rozwiązaniem cięższym.

Wykryte przez

Skaner podatności Aeon. Semgrep oflagował ścieżkę podprocesu Fortran; manualna weryfikacja mechanizmu F-007 oraz empiryczny PoC w cpp potwierdziły podatność.

Co trzeba zrobić

Zaktualizuj do wersji 0.9.70 lub nowszej.

Czego dotyczy

Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.

PlatformaPaczkaPodatne wersjePoprawka w wersji
Python (PyPI) graphifyy >= 0.7.2, < 0.9.70 0.9.70

Odnośniki

Ocena CVSS -
Wektor CVSS
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Słabość (CWE)
CWE-22, CWE-73
Platforma
Python (PyPI)
Paczka
graphifyy
Ujawniono
19.10.2026
Aktualizacja
20.10.2026 09:53

Źródła i licencje · Redakcja lub tłumaczenie: DockRay. Zachowano prawa źródeł i obowiązujące licencje.

OSV / GitHub Advisory Database - GHSA-pcc4-rvhr-2pr8 - CC BY 4.0

Historia podatności: graphifyy

Wszystkie opisane podatności tej paczki (Python (PyPI)), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.

To jedyna znana podatność tej paczki.

Dowiedz się, które podatności dotyczą Twojej aplikacji

Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.

Podłącz swoją aplikację
Newsletter DockRay

Bezpieczeństwo aplikacji w Twojej skrzynce

Najważniejsze podatności, konkretne zalecenia i praktyczne wskazówki dla Twoich aplikacji. Wybierz technologie i śledź to, co ma znaczenie dla Twojego projektu.

  • Codzienne zestawienie nowych podatności, także krytycznych.
  • Powiadomienia o ważnych aktualizacjach.
  • Praktyczne porady o bezpieczeństwie i monitoringu aplikacji.

Pozostaw puste, aby otrzymywać informacje o wszystkich technologiach.

Zapis jest bezpłatny. Wypiszesz się jednym kliknięciem w wiadomości.