#はじめに
IPアドレスの8進数の不適切な検証の脆弱性(CVE-2021-28918)がPythonのipaddressモジュール影響あるということで、ipaddressにおけるIPアドレスの取扱いを調べた。
また、ドット区切りIPアドレスからドット区切りなしIPアドレスへの変換を行う関数を作成した。
#ipaddressモジュールにおけるIPアドレスの扱い
以下のように、ipaddressモジュールがインポートされていることを前提とします。
>>> import ipaddress
例として、ローカルループバックアドレス(127.0.0.1)を用います。
##ドット区切りIPアドレスにおける8進数の扱い
先頭の0は単に除外される
>>> ipaddress.IPv4Address('127.0.0.01')
IPv4Address('127.0.0.1')
3桁を超えるとエラーになる(この例はChromeなどでは「88.0.0.1」に変換される。)
>>> ipaddress.IPv4Address('0127.0.0.1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.9/ipaddress.py", line 1302, in __init__
self._ip = self._ip_int_from_string(addr_str)
File "/usr/lib/python3.9/ipaddress.py", line 1199, in _ip_int_from_string
ipaddress.AddressValueError: At most 3 characters permitted in '0127' in '0127.0.0.1'
##ドット区切りなしIPアドレスの扱い
###10進数
>>> ipaddress.IPv4Address(2130706433)
IPv4Address('127.0.0.1')
###8進数
>>> ipaddress.IPv4Address(0o17700000001)
IPv4Address('127.0.0.1')
(Pythonでは、0oXXXが与えられるとXXXを8進数と認識し、内部では10進数として扱います。すなわち、本質的には10進数の場合と同じことをしています。)
###16進数
>>> ipaddress.IPv4Address(0x7f000001)
IPv4Address('127.0.0.1')
(Pythonでは、0xXXXが与えられるとXXXを16進数と認識し、内部では10進数として扱います。すなわち、本質的には10進数の場合と同じことをしています。)
###バイト型
>>> ipaddress.IPv4Address(b'\x7f\x00\x00\x01')
IPv4Address('127.0.0.1')
#ドット区切りIPアドレスからドット区切りなしIPアドレスに変換
例として、ローカルループバックアドレス(127.0.0.1)を用います。
##10進数
ドットごとに数値を取得し、最初の数値(例では、127)から順にに$ 2^{24}, 2^{16}, 2^8, 2^0 $を掛けて、合計を出力することで変換しています。
$ n\cdot2^x $の部分は、左シフトで計算しています。
>>> def ipv4d2nd(ipv4):
... return sum((int(n)<<8*d for d,n in enumerate(reversed(ipv4.split('.')))))
>>> ipv4d2nd('127.0.0.1')
2130706433
(以降は単に変換方法になります。)
##8進数
>>> oct(ipv4d2nd('127.0.0.1'))
'0o17700000001'
##16進数
>>> hex(ipv4d2nd('127.0.0.1'))
'0x7f000001'
#バイト型
>>> ipv4d2nd('127.0.0.1').to_bytes(4, 'big')
b'\x7f\x00\x00\x01'
#参考文献
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-28918