Una dirección IPv4 se puede expresar de varias formas, en decimal, entero, octal y hexadecimal, aunque el decimal es lo más común. Por ejemplo: 8.8.8.8 en octal es 0010.0010.0010.0010.
Algo que sorprenderá a muchos… si ponemos en el navegador https://0127.0.0.1 realmente nos redireccionará a https://87.0.0.1/. Eso es porque las secciones de una dirección IPv4 se pueden interpretar como octal si tienen el prefijo «0», de acuerdo con la especificación original de IETF para direcciones IP ambiguas.
Pero, ¿qué pasa en las versiones de Python de la 3.8.0 a a la 3.10? Pues que la librería ipaddress elimina o descarta los ceros a la izquierda. De esta manera 010.8.8.8 acaba siendo 10.8.8.8:
$ python3.8
Python 3.8.0 (default, Feb 25 2021, 22:10:10)
[GCC 8.4.0] on linux
Type «help», «copyright», «credits» or «license» for more information.
>>> import subprocess
>>> import ipaddress
>>>
>>> SUSPECT = ‘010.8.8.8’
>>>
>>> print(ipaddress.ip_network(SUSPECT, strict=True))
10.8.8.8/32
>>>
>>> BAD_IP = ipaddress.ip_address(SUSPECT)
>>>
>>> print(‘http://’+str(BAD_IP))
http://10.8.8.8
>>>
>>> print(str(subprocess.check_output(«ping -W3 -v -c1 «+str(SUSPECT), shell=True, universal_newlines=True).strip()))
ping: socket: Permission denied, attempting raw socket…
ping: socket: Permission denied, attempting raw socket…
PING 010.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=5.13 ms— 010.8.8.8 ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 5.137/5.137/5.137/0.000 ms
>>>
>>> print(str(subprocess.check_output(«ping -W3 -v -c1 «+str(BAD_IP), shell=True, universal_newlines=True).strip()))
ping: socket: Permission denied, attempting raw socket…
ping: socket: Permission denied, attempting raw socket…
Traceback (most recent call last):
File «», line 1, in
File «/usr/lib/python3.8/subprocess.py», line 411, in check_output
return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
File «/usr/lib/python3.8/subprocess.py», line 512, in run
raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command ‘ping -W3 -v -c1 10.8.8.8’ returned non-zero exit status 1.
Esta vulnerabilidad añadida en este commit, bautizada como CVE-2021-29921 y similar a la de netmask de Node.js de principios de año, apunta a grandes «daños colaterales»:
«La validación inadecuada de la entrada de cadenas en formato octal en ipaddress permitiría a atacantes remotos no autentificados realizar ataques Server-Side Request Forgery (SSRF), Remote File Inclusion (RFI) y Local File Inclusion (LFI) en múltiples programas que dependen de la stdlib ipaddress de Python».
En efecto, podemos concluír que la validación inadecuada de las cadenas octales en Python 3.8.0 hasta la v3.10 en las librerías stdlib e ipaddress permite a los atacantes remotos no autentificados realizar ataques indeterminados SSRF, RFI y LFI en muchos programas que dependen de las mencionadas librerías involucradas y afectadas por el susodicho incidente de seguridad. Los octectos de direcciones IP se dejan despojados en lugar de evaluarse como direcciones IP válidas. Por ejemplo, un atacante que envía una dirección IP a una aplicación web que depende de las librerías stdlib e ipaddress, podría causar SSRF a través de la entrada de datos octales. De esta manera, un atacante puede enviar direcciones IP explotables si el octeto es de 3 dígitos, con el octecto mínimo explotable siendo 08 (Denegación de Servicio) y el octeto máximo explotable es 099. Por ejemplo, un atacante puede enviar 010.8.8.8, que es 8.8.8.8, pero la función ipaddress de Python lo evaluará como 10.8.8.8.