09-10-2018 / Od 0 do pentestera

YAML w Python i wykonanie kodu poprzez python/object

Popularnym stwierdzeniem jest aby nie używać pickle1 na danych od użytkownika ponieważ podczas ich deserializacji może dojść do ataku object injection i wykonania złośliwego kodu.

pickle

Ale co z innymi formatami? Czy również są niebezpieczne?

Dlaczego więc programiści decydują się na używanie właśnie tego formatu?

Za pewne jedni podniosą argument o czytelności - format ten bowiem wymusza przechowywanie danych w jednej linii co wpływa na czytelności

Przykładowa treść w języku yaml

Pozwala to również lepiej mergować zmiany - na przykład jeśli używamy systemu kontroli wersji - takich jak GIT.

Prościej bowiem edytować kod rozbity na linie niż plik json, który może nie być sformatowany i widoczny dla użytkownika jako jeden ciąg w jednej linii.

Popatrzmy zatem na dzisiejszy przykład.

import yaml
with open("test.yaml", "r") as s:
	y = yaml.load(s)
	print y['imie']

Odczytujemy w nim treści pliku test.yaml przy pomocy funkcji yaml.load a następnie wyświetlamy imię.

Format yaml nie jest tak prosty jak mogło by się wydawać.2

Wycinek specyfikacji formatu yaml

W standardowym użyciu sam rozpoznaje on typ danych który jest do niego przekazywany.

Czasami jednak chcielibyśmy mieć kontrolę nad typem przechowywanym w konfiguracji.

Stąd też tagi - czyli polecenia rozpoczynające się od wykrzyknika, dzięki którym możemy wybrać typ samodzielnie.

Przykładowe tagi

Tak jak tutaj - gdzie wartość 0.5 będzie traktowana jako string a nie jako float.

W yamlu możemy również przechowywać pliki binarne, zapisując je przy pomocy kodowania base64 oraz używając tagu !!binary.

Niektóre parsery implementują specyficzne dla danego języka tagi.

W przypadku Pythonia możemy zdefiniować tuple przy pomocy tagu python/tuple.

Gdzie zatem znajduje się dzisiejszy błąd?

W standardowym parserze używanym w Pythonie możemy również skorzystać z tagu apply3, który pozwala na wywołanie dowolnej funkcji z dowolnego modułu oraz przekazanie do nich odpowiednich parametrów.

Jest to więc równoznaczne z możliwością wykonania dowolnego kodu na atakowanym serwerze.

Tag apply

W naszym przypadku użyjemy funkcji os.system do wyświetlenia zawartości bieżącego katalogu.

Teraz, zamiast imienia użytkownika widzimy treść katalogu.

Treść katalogu

Jak się ochronić przed tym błędem?

Zamienić load na safe_load.

safe_load

Wtedy wszystkie niebezpieczne tagi przestaną działać.

Szkoda, że nie jest to standardowy sposób w jaki działa ta funkcja ponieważ myślę, że sporo osób nie zdaje sobie sprawy z niebezpieczeństwa.

Prawidłowe ładowanie pliku yaml