OSのMACアドレス変更-1
1 はじめに
本コースでは、Pythonの基礎文法の習得を目的として、MACアドレスの変更プログラムを作成します。ここでは、仮想環境で構築したKaililinuxのMACアドレスの変更プログラムを作成します。
2 MACアドレスの変更ができるようになると
MACアドレスの変更とは、つまり、MACアドレスの偽装になりますので、送信元(攻撃者)の端末の特定が難しくなります。
3 環境
開発環境:kalilinux
Pythonバージョン:2.7
4 講義
4-1 環境確認
まず、KalilinuxのInterfaceの確認を行います。
ether 00:0c:29:36:74:9a という部分が、MACアドレスとなります。
root@kali:~/Documents# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.128 netmask 255.255.255.0 broadcast 192.168.32.255
inet6 fe80::20c:29ff:fe36:749a prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:36:74:9a txqueuelen 1000 (Ethernet)
4-2 PythonからMACアドレスの変更
subprocessモジュールを利用して以下のコードを作成し実行します。
# mac-change.py
import subprocess
subprocess.call("ifconfig eth0 down", shell=True)
subprocess.call("ifconfig eth0 hw ether 00:11:22:33:44:66", shell=True)
subprocess.call("ifconfig eth0 up", shell=True)
# result
root@kali:~/Documents# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.132 netmask 255.255.255.0 broadcast 192.168.32.255
inet6 fe80::20c:29ff:fe36:749a prefixlen 64 scopeid 0x20<link>
ether 00:11:22:33:44:66 txqueuelen 1000 (Ethernet)
4-2 変数の利用と出力
subprocessコマンドの引数の中に文字列として変更したいinterfceと変更後のMACアドレスを直接書いていると効率が悪いので、変数に格納しておきます。
結果の表示には、print()関数を使います。また、文字列の連結には、+を利用し、変数は、""で囲わないようにします。
# mac-change.py
import subprocess
interface = "eth0"
new_mac = "00:11:22:33:44:77"
print("[+] Changing MAC address for " + interface + " to " + new_mac)
# subprocess.call("ifconfig eth0 down", shell=True)
# subprocess.call("ifconfig eth0 hw ether 00:11:22:33:44:66", shell=True)
# subprocess.call("ifconfig eth0 up", shell=True)
# result
# result
[+] Changing MAC address for eth0 to 00:11:22:33:44:77
4-3 変数を利用してのMACアドレス変更
subprocess内の引数に、変数を利用する場合にも、print()関数と同様に、文字列の連結には、+を利用し、変数は、""で囲わないようにします。
# mac-change.py
import subprocess
interface = "eth0"
new_mac = "00:11:22:33:44:77"
print("[+] Changing MAC address for " + interface + " to " + new_mac)
subprocess.call("ifconfig " + interface + " down", shell=True)
subprocess.call("ifconfig " + interface + " hw ether " + new_mac, shell=True)
subprocess.call("ifconfig " + interface + " up", shell=True)
# result
root@kali:~/Documents# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.133 netmask 255.255.255.0 broadcast 192.168.32.255
ether 00:11:22:33:44:77 txqueuelen 1000 (Ethernet)
4-4 ユーザー入力
毎回、コード内に、直接、変更対象のInterface名や変更後のMACアドレスを書いていると効率が悪いです。プログラム実行時に、変更対象のinterface名や変更後のMACアドレスを入力できるようにします。ユーザー入力を行うには、以下の関数を利用します。
Python2系→ raw_input()
Python3系→onput()
# mac-change.py
import subprocess
interface = raw_input("interface > ")
new_mac = raw_input("new Mac > ")
print("[+] Changing MAC address for " + interface + " to " + new_mac)
subprocess.call("ifconfig " + interface + " down", shell=True)
subprocess.call("ifconfig " + interface + " hw ether " + new_mac, shell=True)
subprocess.call("ifconfig " + interface + " up", shell=True)
# result
root@kali:~/Documents# python mac-changer.py
interface > eth0
new Mac > 00:11:22:33:44:44
[+] Changing MAC address for eth0 to 00:11:22:33:44:44
root@kali:~/Documents# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.134 netmask 255.255.255.0 broadcast 192.168.32.255
ether 00:11:22:33:44:44 txqueuelen 1000 (Ethernet)
4-5 ユーザー入力時のエラー対応
以下の様に、; を付けると、他のコマンドも入力できてしまいます。このようなことができると意図しないコマンドが実行されたりする可能性がありセキュリティー上好ましくありません。
root@kali:~/Documents# python mac-changer.py
interface > eth0;ls
new Mac > 11
[+] Changing MAC address for eth0;ls to 11
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.135 netmask 255.255.255.0 broadcast 192.168.32.255
inet6 fe80::20c:29ff:fe36:749a prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:36:74:9a txqueuelen 1000 (Ethernet)
RX packets 6840 bytes 9533197 (9.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1084 bytes 84530 (82.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ls: cannot access 'down': No such file or directory
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.135 netmask 255.255.255.0 broadcast 192.168.32.255
inet6 fe80::20c:29ff:fe36:749a prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:36:74:9a txqueuelen 1000 (Ethernet)
RX packets 6840 bytes 9533197 (9.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1084 bytes 84530 (82.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ls: cannot access 'hw': No such file or directory
ls: cannot access 'ether': No such file or directory
ls: cannot access '11': No such file or directory
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.135 netmask 255.255.255.0 broadcast 192.168.32.255
inet6 fe80::20c:29ff:fe36:749a prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:36:74:9a txqueuelen 1000 (Ethernet)
RX packets 6840 bytes 9533197 (9.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1084 bytes 84530 (82.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ls: cannot access 'up': No such file or directory
このような意図しない入力や不正なコマンドを実行させないように、subprocess()の呼び出しかたをリスト形式にすることで回避できます。
subprocess.call("ifconfig " + interface + " down", shell=True)
このコードは、シェルコマンドを文字列として1つの引数として渡しています。shell=Trueを指定することで、シェルを介してコマンドが実行されます。具体的には、ifconfigコマンドとinterface変数の値が連結され、それに対してシェルが起動されます。
# mac-change.py
import subprocess
interface = raw_input("interface > ")
new_mac = raw_input("new Mac > ")
print("[+] Changing MAC address for " + interface + " to " + new_mac)
# subprocess.call("ifconfig " + interface + " down", shell=True)
# subprocess.call("ifconfig " + interface + " hw ether " + new_mac, shell=True)
# subprocess.call("ifconfig " + interface + " up", shell=True)
subprocess.call(["ifconfig", interface, "down"])
subprocess.call(["ifconfig", interface, "hw", "ether", new_mac])
subprocess.call(["ifconfig", interface, "up"])
# result
interface > eth0;ls
new Mac > aaa
[+] Changing MAC address for eth0;ls to aaa
eth0;ls: ERROR while getting interface flags: No such device
hw ether: Unknown host
ifconfig: `--help' gives usage information.
eth0;ls: ERROR while getting interface flags: No such device
この方法は、文字列をシェルに渡しているため、シェルの機能(変数の展開、リダイレクトなど)を利用できます。しかし、シェルを介してコマンドを実行すると、セキュリティ上のリスクが存在する可能性があります。特に、外部からの不正な入力がある場合、シェルインジェクション攻撃に対して脆弱になる可能性があります。
subprocess.call(["ifconfig", interface, "down"])
こちらのコードは、subprocess.call にコマンドと引数を個別の要素として渡しています。リスト内の各要素は、コマンドライン引数として解釈されます。この方法は、シェルの利用を回避し、直接指定されたコマンドを実行します。
この形式は一般的にセキュリティ上のリスクが低く、外部からの不正な入力に対する耐性が高いです。シェルコマンドの実行時に引数が適切にエスケープされ、意図しないコマンドが実行されないようになります。
総じて、外部からの入力を含むコマンドを実行する場合、2番目の方法(リスト形式)が推奨されます。これにより、シェルインジェクション攻撃からの保護が強化され、セキュリティが向上します。
4-6コマンド実行時の引数の指定とhelp表示について
このScriptを実行する時に、引数として何を指定するか分かる様にScriptにhelp表示機能を加えます。新たに、optparse というモジュールを利用します。
optparse.OptionParser() は、コマンドライン引数を処理するための OptionParser クラスのインスタンスを作成しています。これは、コマンドラインから引数を解析し、それに基づいてプログラムの振る舞いを変更するためのツールです。
parser.add_option("-i", "--interface", dest="interface", help="Interface to change its Mac address") は、-i または --interface というオプションを追加しています。このオプションは、指定されたネットワークインターフェースのMACアドレスを変更するために使用されます。dest="interface" は、このオプションの値がプログラム内で interface という変数に格納されることを示しています。help パラメータは、このオプションに関するヘルプメッセージを指定します。
parser.parse_args() は、コマンドライン引数を解析し、指定されたオプションの値を取得します。
# mac-change.py
import subprocess
import optparse
parser = optparse.OptionParser()
parser.add_option("-i", "--interface", dest="interface", help="Interface to change its Mac address")
parser.parse_args()
interface = raw_input("interface > ")
new_mac = raw_input("new Mac > ")
print("[+] Changing MAC address for " + interface + " to " + new_mac)
subprocess.call(["ifconfig", interface, "down"])
subprocess.call(["ifconfig", interface, "hw", "ether", new_mac])
subprocess.call(["ifconfig", interface, "up"])
# result
root@kali:~/Documents# python mac-changer.py -help
Usage: mac-changer.py [options]
Options:
-h, --help show this help message and exit
-i INTERFACE, --interface=INTERFACE
Interface to change its Mac address
MACアドレス部分についても説明を付けるように以下の様に修正します。
# mac-change.py
import subprocess
import optparse
parser = optparse.OptionParser()
parser.add_option("-i", "--interface", dest="interface", help="Interface to change its Mac address")
parser.add_option("-m", "--mac", dest="new_mac", help="new_MAC address")
parser.parse_args()
interface = raw_input("interface > ")
new_mac = raw_input("new Mac > ")
print("[+] Changing MAC address for " + interface + " to " + new_mac)
subprocess.call(["ifconfig", interface, "down"])
subprocess.call(["ifconfig", interface, "hw", "ether", new_mac])
subprocess.call(["ifconfig", interface, "up"])
# result
root@kali:~/Documents# python mac-changer.py -help
Usage: mac-changer.py [options]
Options:
-h, --help show this help message and exit
-i INTERFACE, --interface=INTERFACE
Interface to change its Mac address
-m NEW_MAC, --mac=NEW_MAC
new_MAC address
python mac-changer.pyを実行する時に、引数にMACアドレスを変更するinterface名、MACアドレスを入力して実行してみましょう。
# result
root@kali:~/Documents# python mac-changer.py -i eth0 -m 00:11:22:33:33:33
[+] Changing MAC address for eth0 to 00:11:22:33:33:33
root@kali:~/Documents# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.138 netmask 255.255.255.0 broadcast 192.168.32.255
inet6 fe80::20c:29ff:fe36:749a prefixlen 64 scopeid 0x20<link>
ether 00:11:22:33:33:33 txqueuelen 1000 (Ethernet)
RX packets 7577 bytes 9627326 (9.1 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1205 bytes 99304 (96.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
4-7 関数(return無し)
ここからは、コードを少し変更してMACアドレスを変更する処理を行っている部分を関数化します。
optparse.OptionParser() で OptionParser クラスのインスタンスを作成します。これは、コマンドライン引数を処理するためのツールです。
parser.add_option() メソッドを使用して、-i または --interface オプションと -m または --mac オプションを追加します。これらのオプションはそれぞれネットワークインターフェースの名前と新しいMACアドレスを指定します。
parser.parse_args() を使用して、コマンドライン引数を解析し、options にはオプションの値が、arguments には位置引数が格納されます。このコードでは、options には -i オプションで指定された値が interface に、-m オプションで指定された値が new_mac に格納されます。arguments はこの場合は空です。
change_mac 関数は、指定されたネットワークインターフェースのMACアドレスを変更するための関数です。subprocess.call を使用して、ifconfig コマンドを呼び出してネットワークインターフェースを一時的にダウンし、新しいMACアドレスに変更し、最後にネットワークインターフェースを再度アップさせます。
最後に、change_mac 関数を呼び出して、指定されたネットワークインターフェースのMACアドレスを変更します
# mac-change.py
import subprocess
import optparse
def change_mac(interface, new_mac):
print("[+] Changing MAC address for " + interface + " to " + new_mac)
subprocess.call(["ifconfig", interface, "down"])
subprocess.call(["ifconfig", interface, "hw", "ether", new_mac])
subprocess.call(["ifconfig", interface, "up"])
parser = optparse.OptionParser()
parser.add_option("-i", "--interface", dest="interface", help="Interface to change its Mac address")
parser.add_option("-m", "--mac", dest="new_mac", help="new_MAC address")
(options, arguments) = parser.parse_args()
change_mac(options.interface, options.new_mac)
# result
root@kali:~/Documents# python mac-changer.py -i eth0 -m 00:11:22:33:33:99
[+] Changing MAC address for eth0 to 00:11:22:33:33:99
root@kali:~/Documents# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.139 netmask 255.255.255.0 broadcast 192.168.32.255
ether 00:11:22:33:33:99 txqueuelen 1000 (Ethernet)
RX packets 7723 bytes 9637218 (9.1 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1216 bytes 101076 (98.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
4-8 関数(return有り)
次に、Script実行時に入力を行う部分についても関数化を行います。このようにすることで、入力に関する機能(def get_arguments():)とMACアドレスの変更に関する機能(def change_mac(interface, new_mac):)を分けることが可能になり、コードのメンテナンスが効率的に行えるようになります。また、returnは関数内で処理した結果を再利用したい場合に使います。ここでは、Script実行時に入力したinterface名やMACアドレスを再利用するために、returnさせます。
# mac-change.py
import subprocess
import optparse
def get_arguments():
parser = optparse.OptionParser()
parser.add_option("-i", "--interface", dest="interface", help="Interface to change its Mac address")
parser.add_option("-m", "--mac", dest="new_mac", help="new_MAC address")
return parser.parse_args()
def change_mac(interface, new_mac):
print("[+] Changing MAC address for " + interface + " to " + new_mac)
subprocess.call(["ifconfig", interface, "down"])
subprocess.call(["ifconfig", interface, "hw", "ether", new_mac])
subprocess.call(["ifconfig", interface, "up"])
(options, arguments) = get_arguments()
change_mac(options.interface, options.new_mac)
# result
root@kali:~/Documents# python mac-changer.py -i eth0 -m 00:11:22:33:33:00
[+] Changing MAC address for eth0 to 00:11:22:33:33:00
root@kali:~/Documents# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.32.140 netmask 255.255.255.0 broadcast 192.168.32.255
ether 00:11:22:33:33:00 txqueuelen 1000 (Ethernet)
RX packets 7828 bytes 9644398 (9.1 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1232 bytes 103030 (100.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0