Yaml bibliotecas python também são capazes de serializar objetos python e não apenas dados brutos:
print(yaml.dump(str("lol")))
lol
...
print(yaml.dump(tuple("lol")))
!!python/tuple
- l
- o
- l
print(yaml.dump(range(1,10)))
!!python/object/apply:builtins.range
- 1
- 10
- 1
Verifique como a tupla não é um tipo de dado bruto e, portanto, foi serializada. E o mesmo aconteceu com o range (retirado dos builtins).
safe_load() ou safe_load_all() usa SafeLoader e não suporta a desserialização de objetos de classe. Exemplo de desserialização de objeto de classe:
import yamlfrom yaml import UnsafeLoader, FullLoader, Loaderdata =b'!!python/object/apply:builtins.range [1, 10, 1]'print(yaml.load(data, Loader=UnsafeLoader))#range(1, 10)print(yaml.load(data, Loader=Loader))#range(1, 10)print(yaml.load_all(data))#<generator object load_all at 0x7fc4c6d8f040>print(yaml.load_all(data, Loader=Loader))#<generator object load_all at 0x7fc4c6d8f040>print(yaml.load_all(data, Loader=UnsafeLoader))#<generator object load_all at 0x7fc4c6d8f040>print(yaml.load_all(data, Loader=FullLoader))#<generator object load_all at 0x7fc4c6d8f040>print(yaml.unsafe_load(data))#range(1, 10)print(yaml.full_load_all(data))#<generator object load_all at 0x7fc4c6d8f040>print(yaml.unsafe_load_all(data))#<generator object load_all at 0x7fc4c6d8f040>#The other ways to load data will through an error as they won't even attempt to#deserialize the python object
O código anterior usou unsafe_load para carregar a classe python serializada. Isso ocorre porque na versão >= 5.1, não permite desserializar qualquer classe python serializada ou atributo de classe, com Loader não especificado em load() ou Loader=SafeLoader.
Note que em versões recentes você não pode mais chamar .load()sem um Loader e o FullLoadernão é mais vulnerável a este ataque.
RCE
Payloads personalizados podem ser criados usando módulos Python YAML como PyYAML ou ruamel.yaml. Esses payloads podem explorar vulnerabilidades em sistemas que desserializam entradas não confiáveis sem a devida sanitização.