Szukanie sekretów w plikach kodu bajtowego języka Python
Napisał: Patryk Krawaczyński
29/09/2020 w Pen Test Brak komentarzy. (artykuł nr 749, ilość słów: 577)
K
iedy wykonujemy program Python to w tle wykonuje się najpierw kompilacja kodu źródłowego (instrukcje znajdujące się w danym pliku .py
) do formatu zwanego jako kod bajtowy. Kompilacja to proces tłumaczenia kodu na inny format, a w tym wypadku kod bajtowy jest niskopoziomową, niezależną od platformy reprezentacją kodu źródłowego. Język programowania Python przekłada każdą z instrukcji źródłowych na grupę instrukcji kodu bajtowego poprzez podzielenie ich na pojedyncze kroki. Proces przekładania kodu źródłowego na kod bajtowy odbywa się z myślą o szybkości wykonania – kod bajtowy może działać o wiele szybciej od oryginalnych instrukcji z kodu źródłowego zawartego w pliku tekstowym.
Jeżeli uruchomiony program Pythona ma uprawnienia do zapisu na naszym komputerze to kod bajtowy zostanie zapisany w pliku z rozszerzeniem .pyc
. W wersji języka Python niższych niż 3.2, pliki takie pojawiają się po uruchomieniu programu obok plików źródłowych (z rozszerzeniem .py
) w tym samym katalogu. Na przykład:
-rw-r--r--@ 1 patryk.krawaczynski staff 5515 Sep 29 21:04 settings.py -rw-r--r--@ 1 patryk.krawaczynski staff 2474 Sep 29 21:00 settings.pyc
W wersji 3.2 i nowszych zapis plików kodu bajtowego przeniesiono do podkatalogu o nazwie __pycache__ (znajdującym się w katalogu z plikami źródłowymi), nadając im nazwy identyfikujące wersję Pythona, przy użyciu której zostały utworzone (np. secrets.cpython-37.pyc
). W ten sposób podkatalog __pycache pomaga unikać nieporządku, a nowa konwencja nazewnictwa plików kodu bajtowego uniemożliwia różnym wersjom języka Python (jeśli mamy zainstalowanych kilka w systemie) wzajemne nadpisywanie zapisanego kodu bajtowego.
Powszechną praktyką w rozwijających się projektach Python jest również przechowywanie różnego rodzaju konfiguracji, kluczy, haseł i innych wrażliwych danych w plikach źródłowych np.: secrets.py
, config.py
lub settings.py
. Zapewnia to dobre oddzielenie sekretów i kodu źródłowego – w dodatku pliki takie wczytuje się za pomocą wbudowanego importu języka i nie trzeba zajmować się operacjami wejścia / wyjścia dla innych formatów typu YAML, czy JSON.
O ile takie wrażliwe pliki często są dodawane do wykluczeń (np. .gitignore
), aby nie znalazły się w systemach kontroli wersji to początkujący programiści zdają się zapominać o potencjalnym zagrożeniu wynikającym z tworzenia plików kodu bajtowego, gdzie ich sekrety w skompilowanej formie i tak lądują w repozytorium. Jeśli przeszukamy sobie serwis GitHub pod kątem różnych plików będziemy mogli poznać skalę tego zjawiska:
filename:apikey.pyc filename:secret.pyc filename:secrets.pyc filename:secrets.cpython-37.pyc filename:secrets.cpython-38.pyc filename:config.pyc filename:settings.pyc
Wyniki najlepiej posortować po ostatnio zaindeksowanych plikach (Sort: Recently indexed
). Posiadając już ściągnięty plik, który podejrzewamy o przechowywanie wrażliwych danych – możemy poddać go procesowi dekompilacji – czyli odwrócić proces kompilacji – kod bajtowy przekształcić do kodu źródłowego. Pomóc nam w tym może narzędzie uncompyle6 (obsługuje wersję Python od 1.0 do 3.8):
agresor@stardust:~$ uncompyle6 secrets.cpython-37\ \(1\).pyc # uncompyle6 version 3.7.4 # Python bytecode 3.7 (3394) # Decompiled from: Python 3.8.2 (v3.8.2:7b3ab5921f, Feb 24 2020, 17:52:18) # [Clang 6.0 (clang-600.0.57)] # Embedded file name: /Users/n******s/Library/CloudDocs/Documents/tinderbot/secrets.py # Compiled at: 2020-08-08 12:15:30 # Size of source mod 2**32: 80 bytes username = 'n************l@googlemail.com' password = 'r**********************t' # okay decompiling secrets.cpython-37 (1).pyc
Podsumowanie:
Nawet doświadczonym programistom zdarza się popełnić błąd i wypchnąć wrażliwe pliki do repozytorium. Szczególnie, że wiele edytorów IDE ukrywa specjalne foldery i pliki w drzewie kodu źródłowego, aby uniknąć zaśmiecania ekranu. W ten sposób łatwo zapomnieć lub zorientować się, że pliki takie w ogóle istnieją. Unikanie tego typu wypadków wymaga używania dobrych plików .gitignore lub średniozaawansowanej wiedzy na temat elementów git (np. pre-receive sprawdzający wszystkie pliki *.pyc) i języka Python.
Więcej informacji: Finding secrets by decompiling Python bytecode in public repositories