NFLabs. エンジニアブログ

セキュリティやソフトウェア開発に関する情報を発信する技術者向けのブログです。

IPv4アドレスの難読化手法について

この記事は、NFLaboratories Advent Calendar 2025 5日目の記事です。

研究開発部 研究開発担当の堺 です。

IDS/IPS/Webフィルタリングの検知回避方法を探していたところ、一般的でないIPv4アドレスの書き方を見つけたので共有します。

はじめに

https://github.com/OsandaMalith/IPObfuscator というリポジトリを見てIPv4アドレスを普段のドット区切りではなく表記できることを知りました。

IPv4アドレスは通常、ドット区切りの10進数表記です。 一方でドット区切り10進数表記以外でもIPv4アドレスを表現することができます。

例えば8進数や16進数など異なる基数を使う表記やドット区切り無しの10進数表記などで検知回避をする手法が現実の攻撃で使われることがあります。

どのようなIPv4アドレスか

2130706433
0x7f.0x00.0x00.0x01
0177.00.00.01
0x000000007f.0x0000000000.0x0000000000.0x0000000001
00000000177.00000000000.00000000000.00000000001

0x7f.0x00.0x00.1
0x7f.0x00.0.1
0x7f.0.0.1

0177.00.00.1
0177.00.0.1
0177.0.0.1

0x7f.0x00.1
0177.00.1
0x7f.00.1
0x7f.1
0177.1
0x7f.0x0.00.01
0x7f.00.00.01

上記表記のIPv4アドレスはすべて 127.0.0.1 を表します。

RFC3986

RFC3986 Uniform Resource Identifier (URI): Generic Syntax の section 7.4 Rare IP Address Formatsにて挙動が定義されています。

https://datatracker.ietf.org/doc/html/rfc3986#section-7.4

Adding further to the confusion, some implementations allow each dotted part to be interpreted as decimal, octal, or hexadecimal, as specified in the C language (i.e., a leading 0x or 0X implies hexadecimal; a leading 0 implies octal; otherwise, the number is interpreted as decimal).

曰く、システムによっては10進数表記、8進数表記、16進数表記を解釈するとのことです。 同様に、ドット区切りの数値パートが3個以下の場合も言及されています

ping 2130706433
PING 2130706433 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.065 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.078 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.165 ms
^C

こんな感じで ping でも使えてしまいます。

各種ブラウザもRFCに準拠するためにこのようなIPv4アドレスをサポートし、結果としてフィッシング攻撃などに使われているという事情があります。

実装

どのようなIPv4アドレスが利用可能なのかGo言語でサクッと実装してみました。 https://github.com/famasoon/IPObfuscator-go

あまりきれいではないですが、基数変換を実施しています。

package obfuscator

import (
    "encoding/binary"
    "fmt"
    "net"
)

func IPtoDecimal(ip net.IP) string {
    return fmt.Sprintf("%d", binary.BigEndian.Uint32(ip))
}

func IPtoHex(ip net.IP) string {
    return fmt.Sprintf("0x%02x.0x%02x.0x%02x.0x%02x", ip[0], ip[1], ip[2], ip[3])
}

func IPtoOct(ip net.IP) string {
    return fmt.Sprintf("0%o.0%o.0%o.0%o", ip[0], ip[1], ip[2], ip[3])
}

func IPtoHexPadding(ip net.IP) string {
    return fmt.Sprintf("0x%010x.0x%010x.0x%010x.0x%010x", ip[0], ip[1], ip[2], ip[3])
}

func IPtoOctPadding(ip net.IP) string {
    return fmt.Sprintf("0%010o.0%010o.0%010o.0%010o", ip[0], ip[1], ip[2], ip[3])
}

func IPtoHexAndDec(ip net.IP) string {
    return fmt.Sprintf("0x%02x.0x%02x.0x%02x.%d", ip[0], ip[1], ip[2], ip[3])
}

func IPtoHexAndDec2(ip net.IP) string {
    return fmt.Sprintf("0x%02x.0x%02x.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func IPtoHexAndDec3(ip net.IP) string {
    return fmt.Sprintf("0x%02x.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func IPtoOctAndDec(ip net.IP) string {
    return fmt.Sprintf("0%o.0%o.0%o.%d", ip[0], ip[1], ip[2], ip[3])
}

func IPtoOctAndDec2(ip net.IP) string {
    return fmt.Sprintf("0%o.0%o.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func IPtoOctAndDec3(ip net.IP) string {
    return fmt.Sprintf("0%o.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func HexAndDecimal(ip net.IP) string {
    low := binary.BigEndian.Uint32(ip) & 0x0000ffff
    return fmt.Sprintf("0x%02x.0x%02x.%d", ip[0], ip[1], low)
}

func OctAndDecimal(ip net.IP) string {
    low := binary.BigEndian.Uint32(ip) & 0x0000ffff
    return fmt.Sprintf("0%o.0%o.%d", ip[0], ip[1], low)
}

func HexOctDec(ip net.IP) string {
    low := binary.BigEndian.Uint32(ip) & 0x0000ffff
    return fmt.Sprintf("0x%x.0%o.%d", ip[0], ip[1], low)
}

func HexAndDec(ip net.IP) string {
    low := binary.BigEndian.Uint32(ip) & 0x00ffffff
    return fmt.Sprintf("0x%x.%d", ip[0], low)
}

func OctAndDec(ip net.IP) string {
    low := binary.BigEndian.Uint32(ip) & 0x00ffffff
    return fmt.Sprintf("0%o.%d", ip[0], low)
}

func Hex2Oct2(ip net.IP) string {
    return fmt.Sprintf("0x%x.0x%x.0%o.0%o", ip[0], ip[1], ip[2], ip[3])
}

func HexOct3(ip net.IP) string {
    return fmt.Sprintf("0x%x.0%o.0%o.0%o", ip[0], ip[1], ip[2], ip[3])
}

上記コードでは、IPv4アドレスを各種基数に変換して表示しています。

おわりに

攻撃者は検知回避のためにバグのみならず仕様の穴のような挙動を使ったりと日々進化しています。 皆様もどうかご注意ください。