błąd formatu ciągu - luka w zabezpieczeniach formatu ciągu - uszkodzenie pamięci

Jak prosty błąd w ciągu formatującym może prowadzić do uszkodzenia pamięci (i jak go wykryć w kodzie)

Czym jest błąd ciągu formatującego i dlaczego nadal ma to znaczenie

A błąd ciągu formatującego występuje, gdy dane kontrolowane przez użytkownika są przekazywane jako ciąg formatujący w funkcjach takich jak printf, fprintf lub syslog, bez walidacji.

W prostych słowach, Błąd formatowania łańcucha występuje, gdy dane wprowadzane przez użytkownika są używane jako szablon formatowania, co umożliwia niezamierzony odczyt lub zapis w pamięci. Jest to szczególnie niebezpieczne w językach takich jak C / C ++, gdzie specyfikatory formatu, takie jak %s, %x, oraz %n może bezpośrednio manipulować danymi stosu.

Dlaczego ma to znaczenie w nowoczesnym DevSecOps? Ponieważ te błędy nadal można znaleźć w aktywnych bazach kodu, szczególnie w:

  • Komponenty Open Source ze starszym kodem C
  • Natywne powiązania w Pythonie, Go lub Rust
  • Automatycznie scalony kod stron trzecich w środowisku produkcyjnym pipelines

Skutki są realne: wycieki pamięci, uszkodzenie stosu, a nawet zdalne wykonanie kodu (RCE). Mimo to wiele zespołów ufa skanerom statycznym i nie zauważa tych błędów, chyba że robią to specjalnie.

Prosto do kodu: printf(dane_użytkownika) i niebezpieczeństwo

Przyjrzyjmy się częstemu błędowi:

printf(dane_użytkownika);

Ta jednolinijkowa odpowiedź to prosta droga do kłopotów. Jeśli wejście_użytkownika zawiera coś takiego %x %x % x% %x, to instruuje printf odczytywać wartości ze stosu, odsłaniając pamięć. Co gorsza, jeśli %n jest uwzględniony, atakujący może zapisać dowolne wartości w pamięci.

Ten schemat nie tylko powoduje wyciek danych ze stosu; może również uszkodzić pamięć i doprowadzić do zdalnego wykonania kodu (RCE). Dokładnie tak wygląda luka w zabezpieczeniach formatu ciągu znaków w praktyce. Luki te nie generują błędów ani ostrzeżeń kompilatora, dopóki nie zostaną włączone określone flagi lub mechanizmy oczyszczania. W wielu przypadkach są one ukryte w wrapperach lub funkcjach narzędziowych, przez co są niewidoczne dla pobieżnych przeglądów kodu.

Korupcja pamięci 101: Co jest naprawdę zagrożone

Kiedy fJeśli zostanie wykorzystany błąd związany z ciągiem znaków formatu, atakujący mogą:

  • Zrzuć adresy pamięci i wartości stosu za pomocą %x or %s
  • Nadpisz zmienne stosu lub adresy powrotne za pomocą %n
  • Spowodować błędy segmentacji lub błędy logiczne poprzez uszkodzenie pamięci
  • Należy przejść na pełną RCE, zwłaszcza jeśli zabezpieczenia takie jak ASLR lub kanarki stosu są nieprawidłowo skonfigurowane

Pomocne w tym przypadku będzie zrozumienie struktury stosu. printf nie wie, ile argumentów oczekiwać; opiera się wyłącznie na ciągu formatującym. Dlatego %x Przemieszcza się po stosie, ujawniając lub manipulując danymi. Rezultatem może być niewielki wyciek danych lub całkowita kontrola nad wskaźnikiem instrukcji.

Gdzie w nowoczesnych bazach kodu ukrywają się błędy związane z ciągami formatującymi

Te luki nie dotyczą wyłącznie starszego kodu C. Kryją się one również w nowoczesnych środowiskach:

  • Python (ctypes), Rust (FFI) i Go (cgo) powiązania, które komunikują się z bibliotekami natywnymi, często działają jak cienkie opakowania, przekazując parametry bezpośrednio do podatnych na ataki funkcji C.
  • Narzędzia CLI i demony innych firm integrują starszy kod C bez wystarczającej kontroli
  • Rejestrowanie opakowań, takich jak debug_log(dane_użytkownika) które wewnętrznie kierują do funkcji w stylu printf
  • Automatycznie scalone wkłady OSS zawierające starsze wzorce lub minimalną walidację

Błąd w natywnej warstwie C nie pozostaje tam, lecz rozprzestrzenia się w górę. Jeśli funkcja C, taka jak log_event(char *msg) jest niebezpieczne, wywoływanie go z Pythona przez ctypy, Rdza przez niebezpieczny zewnętrznylub Przejdź przez cgo przenosi podatność na zagrożenia do środowisk wyższego poziomu.

Problem? Takie integracje są powszechne, a DevSecOps często zakłada, że ​​powiązania są bezpiecznymi abstrakcjami. Nie są. Luka w zabezpieczeniach formatu string w jednej warstwie stosu może dyskretnie rozprzestrzeniać się między interfejsami, zwłaszcza gdy moduły natywne są opakowywane bez silnego wymuszania typów ani oczyszczania danych wejściowych. Jeśli podstawowa funkcja C jest podatna, kod wyższego poziomu dziedziczy lukę w zabezpieczeniach formatu string.

CI/CD Pipelines: Jak to omija Twoje kontrole bezpieczeństwa

Nowoczesne technologie pipelineSamochody są projektowane z myślą o prędkości, ale prędkość ta wprowadza martwe pola:

  • SAST narzędzia rzadko wychwytuje użycie dynamicznego ciągu formatującego, chyba że jest specjalnie skonfigurowany do śledzenia zanieczyszczonych danych
  • Recenzenci PR skupiają się na logice i stylu, a nie na podstawowym zachowaniu funkcji C
  • Łączenie CI umożliwia włączanie podatnych pakietów, które na pierwszy rzut oka wydają się nieszkodliwe
  • Skanery zależności często ignorują kod natywny lub niebezpieczną logikę rejestrowania

Standard SAST Narzędzia często nie wykrywają błędów w ciągach formatujących, chyba że zaimplementowano niestandardowe reguły wychwytujące argumenty formatujące niebędące literami. Bez tych dostosowanych kontroli dynamiczne ciągi formatujące łatwo prześlizgują się niezauważone.

Integrowanie reguł specyficznych dla ciągu formatującego CI/CD jest niezbędne do wczesnego wykrywania i zapobiegania tym błędom. Oznacza to:

  • Blokowanie kodu, w którym niezaufane dane wejściowe docierają do funkcji formatujących
  • Oznaczanie dynamicznych ciągów formatujących podczas analizy statycznej
  • Wymuszanie tych zasad w ramach procesu scalania CI

Bez tego twoje pipeline nie dostrzega klasy luk, które mogą prowadzić do uszkodzenia pamięci i RCE na długo przed wprowadzeniem kodu do produkcji.

Wykrywanie błędów: praktyczne wykrywanie za pomocą GDB i narzędzi statycznych

Aby znaleźć i potwierdzić te błędy, należy zastosować kombinację ręcznego debugowania i automatycznej analizy statycznej.

Analiza ręczna z GDB:

GDB jest szczególnie przydatny, gdy podejrzewasz niewłaściwe użycie ciągu formatującego, ale musisz potwierdzić jego zachowanie w czasie wykonywania:

  • Przerwa na printf lub pokrewne funkcje aby sprawdzić stos wywołań i argumenty.
  • Sprawdź, czy nie występują anomalie, nieoczekiwane odczyty pamięci, awarie podczas formatowania lub dziwne wartości na stosie.
  • Abstrakcyjne dane wejściowe, takie jak powtarzane %x or %s może pomóc określić, jak głęboko ciąg formatujący podąża w stosie.

Od ręcznego do automatycznego:

Po ręcznym potwierdzeniu schematu nadużyć, kolejnym krokiem jest przekształcenie tej wiedzy w regułę automatyczną. Na przykład:

  • Zastosowanie grep 'printf(' src/ aby znaleźć wywołania formatowania surowego.
  • Połącz to ze skryptem, aby oznaczyć każde użycie printf( gdzie pierwszy argument to nie dosłowny ciąg znaków.
  • Użyj narzędzi opartych na AST do śledzenia wartości ciągów formatujących, dynamicznie identyfikując ścieżki nieliterałowe.
  • Przetłumacz częste ustalenia ręczne, takie jak funkcje opakowujące przekazujące niezaufane dane wejściowe, na reguły CI, które automatycznie blokują takie przypadki.

Wskazówka dotycząca integracji CI: Skonfiguruj swoją pipeline kompilacje kończą się niepowodzeniem, jeśli jakakolwiek funkcja formatowania otrzyma dynamiczne dane wejściowe jako swój ciąg formatujący. Te sprawdzenia działają jak zapora sieciowa, która wymusza przestrzeganie zasad zdobytych podczas debugowania w środowisku GDB i środowisku wykonawczym.

Wzmocnienie kodu: walidacja danych wejściowych i bezpieczniejsze wzorce

Zapobieganie podatności na formatowanie ciągu zaczyna się od przyjęcia bezpieczniejszych nawyków kodowania i ich egzekwowania na dużą skalę:

  • Zawsze używaj stałych ciągów formatujących: printf(“%s”, dane wejściowe użytkownika);, nigdy nie przekazuj surowych danych wejściowych jako formatu.
  • Preferuj bezpieczniejsze warianty: snprintf, vsnprintf, i podobne funkcje pomagają kontrolować rozmiary buforów i egzekwować strukturę wyjściową.
  • Sprawdź wszystkie dane wprowadzone przez użytkownika co może wprowadzić logikę rejestrowania i formatowania, nawet w funkcjach opakowujących.

Zautomatyzowane środki łagodzące, które powinieneś włączyć:

  • AddressSanitizer (ASan): Wykrywa w czasie rzeczywistym uszkodzenia pamięci, w tym przepełnienia bufora i naruszenia stosu, często wywoływane przez nieprawidłowo sformatowane ciągi formatujące.
  • Niezdefiniowane zachowanie Środek dezynfekujący (UBSan): Oznacza niezdefiniowane zachowanie, takie jak przekazywanie niedopasowanych lub brakujących argumentów do funkcji formatujących.
  • -D_FORTIFY_SOURCE=2: Dodaje lekkie kontrole do funkcji libc podczas kompilacji, pomagając wykryć niewłaściwe użycie ciągu formatującego lub przekroczenia bufora przy minimalnym obciążeniu wydajności.

Narzędzia te powinny być włączone zarówno w środowiskach programistycznych, jak i w środowiskach CI, aby wykrywać problemy jeszcze przed ich pojawieniem się. W połączeniu z analizą statyczną tworzą one solidną sieć bezpieczeństwa, ostrzegając o nadużyciach, które w przeciwnym razie mogłyby pozostać niezauważone aż do uruchomienia lub po ataku.

Wskazówka: Uczyń te środki dezynfekujące częścią swojej konstrukcji pipeline z zasadami ostrzegania o błędzie. Traktuj każde naruszenie ciągu formatującego jak nieudany test.

Jak Xygeni zapobiega błędom w ciągach formatujących przed ich wysłaniem

Xygeni wzmacnia CI/CD poprzez egzekwowanie bezpieczeństwa ciągu formatującego za pomocą zapobiegania w czasie rzeczywistym, a nie tylko wykrywania:

  • Identyfikuje niebezpieczne wzorce lubić printf(dane_użytkownika) zanim kod dotrze do produkcji
  • Stosuje analizę skażenia statycznego do śledzenia niepewnych danych wejściowych do funkcji formatujących, nawet w wielu warstwach lub wywołaniach wrapperów
  • Automatycznie blokuje niebezpieczne połączenia w GitHub, GitLab, Bitbucket i Jenkins
  • Zapewnia jasną informację zwrotną ze śladami połączeń, źródłem wejściowym i wstępnymcissugestie dotyczące naprawy

Przykład w akcji: iprogramista fa commitjest linią log_debug(dane_użytkownika) oraz log_debug() wewnętrznie owija wrażliwą printfXygeni śledzi graf wywołań, rozpoznaje dynamiczną ścieżkę wejściową i blokuje scalanie. Deweloper widzi natychmiastowy komunikat w swoim żądaniu scalania:

⚠️ Wykryto lukę w zabezpieczeniach ciągu formatującego: wejście_użytkownika wpływa do printf() w src/logger.c:42. Użyj stałego ciągu formatującego i sprawdź poprawność danych wejściowych.

Ta informacja zwrotna jest dostarczana bezpośrednio do GitHub, GitLab, Jenkins lub Bitbucket w ramach procesu MR/PR. Deweloperzy nie mogą jej przegapić i otrzymują praktyczne wskazówki dotyczące rozwiązania problemu, a nie tylko mgliste ostrzeżenie.

Integracja jest bezproblemowa:

  • Konfiguruj reguły polityki dla każdego repozytorium, gałęzi lub projektu
  • Wymuś warunki blokowania w przypadku używania niebezpiecznego formatu
  • Automatyczne śledzenie niebezpiecznych danych wejściowych w językach C/C++, Python, Go, Rust i ich natywnych powiązaniach

Dzięki wbudowaniu zabezpieczeń w proces pracy i zapewnieniu informacji zwrotnej na poziomie deweloperów, Xygeni gwarantuje, że błędy w ciągach formatujących nigdy nie pojawią się w środowisku produkcyjnym, powstrzymując je dokładnie w miejscu ich wystąpienia.

Słowo końcowe: błędy w ciągach formatujących nie są martwe

Pomimo nowoczesnych narzędzi językowych, błędy formatowania ciągów znaków wciąż się pojawiają. Przechodzą one przez wrappery, pakiety firm trzecich i niedostatecznie sprawdzone żądania aktualizacji (PR). Ich skutki są realne: uszkodzenie pamięci, wyciek danych i potencjalne RCE (zdanie losowe).

Przeprowadź audyt swojego kodu. Utwardź swoje CI/CDWdrażaj zasady wykrywania i egzekwuj je. To nie tylko przestarzałe rozwiązania, to aktywne zagrożenie ukryte na widoku. Używaj zautomatyzowanych narzędzi, analizy statycznej i narzędzi czyszczących środowisko wykonawcze, aby wcześnie wykrywać problemy. Nie zakładaj, że jesteś bezpieczny tylko dlatego, że kod się kompiluje.

sca-tools-oprogramowanie-narzędzia-analizy-kompozycji
Określ priorytety, rozwiąż problemy i zabezpiecz zagrożenia związane z oprogramowaniem
Załóż darmowe konto.
Nie wymagamy karty kredytowej.

Zabezpiecz rozwój i dostarczanie oprogramowania

z pakietem produktów Xygeni