【C言語】ARPキャッシュポイズニングを実装する

こんにちは、まさです!

前回までで、実際にC言語のpcapライブラリを使って生のパケットを送信する方法について紹介しました。
生のパケットは、unsigned char型の配列で表されており、パケットの送信先などの情報もOSI参照モデルで決まった書式でパケットに書き込まなければいけません。


今回は実際に下の画像のような偽のARPパケットを生成する方法を紹介します。
つまり、IPアドレス192.168.0.1、MACアドレス11:22:33:44:55:66のPCに対して、自分がIPアドレス192.168.0.2であると”偽った”ARPパケットを生成します。

f:id:kasayanus:20180724213021p:plain

まず最初に言っておきますが、ARPパケットは必ず42バイトのデータにすることが決まっています。
なので、送信するパケットを表すunsigned char配列の要素数は42になります。
以下では具体的に、このこの配列の要素にはどのようなデータが入るのか説明していきます。

次の画像を見てください
f:id:kasayanus:20180802174845j:plain

パケットは必ずこのような階層構造になっており、
今回送りたいARPパケットは第三層ネットワーク層に位置していることがわかります。

つまり今回は
①第二層データリンク層のイーサヘッダ
②第三層ネットワーク層ARPヘッダ

この2つを配列のデータとして記述することになります。

①イーサヘッダ
f:id:kasayanus:20180802175703p:plain
第二層で使うイーサネットのヘッダはこのような14バイトのデータの並びになっています。

1〜6バイトまで→宛先のMACアドレス(今回の場合11:22:33:44:55:66)
7〜12バイトまで→送信元のMACアドレス(今回の場合12:34:56:78:9a:bc)
13〜14バイトまで→パケットのデータのタイプ(今回はARPのタイプを表す8,6の配列)


ARPヘッダ
f:id:kasayanus:20180802172945g:plain
データリンク層のデータが確定したので次はARPヘッダを続いて書き込んでいきます。ARPヘッダは28バイトのデータの並びになっています。

1〜2バイト→ハードウェア識別番号(今回は0,1の配列)
3〜4バイト→プロトコルのタイプ番号(今回は8,0の配列)
5バイト→ハードウェアのサイズ番号(今回は6)
6バイト→プロトコルのサイズ番号(今回は4)
7〜8バイト→オプコード(今回は0,2の配列)
9〜14バイト→ARPリプライの送信元のMACアドレス(今回は12:34:56:78:9a:bc)
15〜18バイト→ARPリプライの送信元のIPアドレス
(今回は192.168.0.2を偽る)→ココ重要!

19〜24バイト→ARPリプライの宛先MACアドレス(今回は11:22:33:44:55:66)
25〜28バイト→ARPリプライの宛先IPアドレス(今回は192.168.0.1)

以上のことをまとめると、今回送るパケットの配列の中身は

unsigned char packet[42]={
    11,22,33,44,55,66,12,34,56,78,0x9a,0xbc,8,6,
    0,1,8,0,6,4,0,2,12,34,56,78,0x9a,0xbc,192,168,0,2,
    11,22,33,44,55,66,192,168,0,1
};

この配列を前回紹介したsend_packet関数で送信すればARPキャッシュポイズニングに成功するわけです。

これで具体的な偽のパケットの作り方の紹介はおしまいですが、実際には今回生成したイーサネットヘッダ、ARPヘッダは構造体として定義したほうが後々便利でしょう。例えば次のような構造体を定義しておきます。

#define ETHER_ADDR_LEN 6
#define ETHER_HDR_LEN 14

struct ether_hdr{
	unsigned char ether_dest_addr[ETHER_ADDR_LEN];
	unsigned char ether_src_addr[ETHER_ADDR_LEN];
	unsigned short ether_type;
};

struct arp_hdr{
	unsigned short hardware_type;
	unsigned short protocol_type;
	unsigned char hardware_size;
	unsigned char protocol_size;
	unsigned short opcode;
	unsigned char sender_mac[6];
	unsigned char sender_ip[4];
	unsigned char target_mac[6];
	unsigned char target_ip[4];
};

またヘッダの各部分の意味については今回省略しましたが次のサイトに詳細が載っていますので参考にしてください。
www.atmarkit.co.jp

C言語で"生"のパケットを送信する!その2【ARPキャッシュポイズニング】

こんにちは!まさです。

前回ARPキャッシュポイズニングについて大まかに説明しました。

f:id:kasayanus:20180724213214p:plain

その中で、この攻撃を成立させるためにはニセのARPリプライを送る必要があることを述べました。

今回は具体的なARPパケットの作り方と送信のプログラムについて説明します。

今回はC言語のpcapライブラリというものを使っていきます。

C言語を使う理由としては、

ARPプロトコルOSI参照モデルネットワーク層(第三層)に位置する非常に低レイヤーなプロトコルである。

MACアドレスを詐称するためデータリンク層(第二層)のヘッダー情報を書き換える必要がある。

以上の2つが主な理由です。

では実際にパケットを送信するまでのプログラムを以下に示します。
数字をのせた行については後で説明します。

#include <pcap.h>//pcapライブラリインクルード

char* device="イーサネットデバイス名を入力";//①
pcap_t *pcap_handle; //パケット送信用ハンドラー
char errbuf[PCAP_ERRBUF_SIZE];//エラーメッセージ格納用の変数

int main(int argc,char** argv)
{
  u_char* packet={0x00};//②送信するパケットの内容(16進数)
  pcap_handle=pcap_open_live(device,8192,1,0,errbuf);//③ハンドルのオープン
  pcap_sendpacket(pcap_handle,packet,100);//④パケットを送信

}

コードの①〜④を説明します。
① PCが使っているイーサネットバイスを入力します。これはipconfigやifconfig、ip aなどのコマンドによって確認してください。

② pcapライブラリで送信するパケットは16進数の配列で表される"生"のパケットです。ここではサンプルでは{0x00}を送信していますが、
宛先の情報も書かれていないのでこのパケットには意味がありません。
(u_char型はunsigned charの略です。)

pcap_t* pcap_open_live(char* device , int snaplen ,int promisc ,int to_ms,char* errbuf)
パケット送信用のハンドルをオープンします。引数は、デバイス名、取得したいパケットの最大長、デバイスをプロミスキャスモードにするかどうかのフラグ、読み込みタイムアウト時間(0を永遠に待ち続ける。)、エラーメッセージ格納バッファ


int pcap_sendpacket(pcap_handle* handle,u_char* packet,int size)

これが一番重要なパケット送信関数です。引数はハンドル、送信パケット、送信パケットサイズです。
注意してほしいのは、引数に宛先やポートを指定していないことです。
ここが他の言語に備わっているようなソケット通信とは違うところです。
pcapライブラリは生のパケットを送ります。つまりパケットそのものに宛先などの情報を書かなければならないのです。


生のパケットには、ヘッダーと言われるパケットそのものの種類や宛先、送信元などの情報を記載するための各プロトコルごとに決まった書式があります。
もちろんARPプロトコルにも独自のARPヘッダーというものが存在します。

今回はpcapライブラリの関数を使ったパケットの送信の仕方について説明しました。
次回は実際に送信する偽のパケットのARPヘッダを生成していきたいと思います。


ARPキャッシュポイズニングを実装その1
ARPキャッシュポイズニングを実装その3

C言語で通信を盗聴する !? その1【ARPキャッシュポイズニング】

こんにちは!まさです。

今日では有線より無線で通信する機会が多くなっています。それに伴い、ますます盗聴のリスクは高まっていると思います。具体的に盗聴はどのようにしてなされるのでしょうか。

今回は2つの通信機器同士の通信の盗聴の具体的な仕組みについて紹介したいと思います。

 

通信機器同士が通信をする場合その2つをつなぐ通信経路ができます。この通信経路の間に第三者が入り込み、通信路を流れるパケットを盗み見る攻撃のことを総称して"中間者攻撃(MITM攻撃)"といいます。

この攻撃を成功させるためには通信経路を攻撃者の都合のいいように変更できなければなりません。一体どのようにして通信の向きを変更するのでしょうか?

今回は"ARPキャッシュポイズニング"という手法について紹介したいと思います。

f:id:kasayanus:20180724210852p:plain

 

 

簡単のため、今回は攻撃者と被害者はすべて同一のネットワーク(LAN)にいることを想定してください。

この攻撃を理解するためにはまず、ARPについて理解する必要があります。

 

★ ARP(Address Resolution Protocol)

ARPは通信相手のMACアドレスIPアドレスの対応づけのためのプロトコルです。

f:id:kasayanus:20180724211518p:plain

ここでは、IPアドレスは通信したい相手の名前だと思ってください。

スマホを使って誰かに電話したいとき、相手の名前(IPアドレス)を知っているだけでは通話できません。電話番号が必要です。この電話番号の役割をするのがここではMACアドレスだと思ってください。

つまり通信機器が通信するためにはIPアドレス(名前)とMACアドレス(電話番号)の対応づけが必要なのです。これを担うのがARPです。

 

具体的なARPによるやり取りはARPリクエストとARPリプライで成立します。

f:id:kasayanus:20180724212143p:plain

まず通信したい機器はARPリクエストを送ります。このとき、通信したい相手のIPアドレスは知っていますが、そのMACアドレスを知りません。そのため、"LAN全体にブロードキャスト"でリクエストを送ります。

 

f:id:kasayanus:20180724212155p:plain

リクエストを受け取った機器の中で、該当するIPアドレスの機器"だけ"がリクエストに応答して、自分のMACアドレスARPリプライとして送信します。

 

f:id:kasayanus:20180724212456p:plain

これにより、二者の間のIPアドレスMACアドレスの対応が取れたので通信が開始できるわけです。

これがARPの仕組みです。

 

さて、ここでもし再び同じ相手と通信したい場合はどうすればいいでしょうか?

もう一度ブロードキャストでリクエストを送るのでしょうか?

それでは効率が悪いのでPCには一度通信した相手のMACアドレスIPアドレスを記憶するための"ARPキャッシュ"というものが存在します。

 

f:id:kasayanus:20180724212741p:plain

ARPキャッシュはARPリプライをもとに生成されます。

(ちなみに"arp"コマンドでARPキャッシュは確認することができます。)

PCはこのARPキャッシュを参照して通信相手にパケットを送るわけです。

 

ここからが "ARPキャッシュポイズニング" の話になります。

もし悪意を持った者が偽りのARPリプライを送ったらどうなるでしょうか?

f:id:kasayanus:20180724213021p:plain

つまり、攻撃者が自分と異なるIPアドレスを以てして、自分自身のMACアドレスを付与したARPリプライを送りつけるとどうなるか。

被害者はニセのARPリプライの情報をもとにARPキャッシュを上書きしてしまいます。

f:id:kasayanus:20180724213152p:plain

被害者は上書きされたARPキャッシュを参照してパケットを送信します。

これによって、中間者攻撃が成立するのです。

f:id:kasayanus:20180724213214p:plain

 

ここまでの話は2つのPCの間の通信を例に説明しましたが、MACアドレスIPアドレスを持っている機器であればPCかどうかを問いません。

つまり、"スマホ"や"ルータ"に対してARPキャッシュポイズニングを行えば、スマホからインターネットに流れるパケットを盗聴することができます。

f:id:kasayanus:20180724213504p:plain

ではどうやってこの攻撃を防ぐことができるのでしょうか。

具体的な対策として考えられるのは以下の2つだと考えています。

 

f:id:kasayanus:20180724213544p:plain

今ではほとんどのウェブサイトがhttpsでの通信を行っており、パケットが暗号化されているので、この攻撃を受けてもデータを盗み見ることはできません。

しかし、httpではパケットが平文のまま送信されるため、もしウェブサイトがログインフォームでクレジット情報やパスワードの入力を求めてきた場合はすべて筒抜けになります。

2つ目にFreeWifiなどホストが不明なアクセスポイントに接続してしまうと、そのホストが悪意ある者だった場合、この攻撃を受ける危険性があります。

 

今回は中間者攻撃の仕組みについて説明しました。

次回は実際にプログラムで偽のARPリプライを生成する方法について説明したいと思います。



ARPキャッシュポイズニングの実装その2

 

 

 

 

 

【ハッキングの気持ち】パスワード"AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"でログインする

こんにちは!まさです

今回はプログラムのIF文を不正にパスするハッキングの方法を紹介したいと思います。
この方法を使ってIF文をパスできてしまうと、不正な文字列でのログインや、Linuxなどの場合、管理者権限を奪われてしまうという恐ろしい危険性が
あります。
そんな攻撃の中でも最も簡単な方法を紹介したいと思います。

今回攻撃するのは"auth_overflow"というプログラムです。
(下にC言語のコードを載せています)
このプログラムは、ユーザが入力した文字列が、あらかじめ決まったパスワードと一致しているか判定し、その結果を出力するというものです。
今回は"brillig"または"outgrabe"という文字列が入力されるとパスするようにしています。




★ auth_overflowの挙動

1. gccを使ってauth_overflow.cをコンパイルし、実行ファイル(a.out)を作ります。
今回はオプションで"-fno-stack-protector"を付け加えます。
このオプションは、"メモリへの不正な上書きを許す"という意味だと思ってください。

gcc auth_overflow.c -fno-stack-protector

2. パスワード以外の文字列を入力するとアクセス拒否します。
f:id:kasayanus:20180712170650p:plain



3. パスワード文字列を入力するとアクセス許可されます。
f:id:kasayanus:20180712170701p:plain


このプログラムの動きはこれだけです。


以下は"auth_overflow.c"のソースコードです。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>

int check_authentication(char *password) { 
   char password_buffer[16];
   int auth_flag = 0; 

   strcpy(password_buffer, password);

   if(strcmp(password_buffer, "brillig") == 0) 
      auth_flag = 1;
   if(strcmp(password_buffer, "outgrabe") == 0) 
      auth_flag = 1;

   return auth_flag;
}

int main(int argc, char *argv[]) { 
   if(argc < 2) {
      printf("使用方法: %s <パスワード>\n", argv[0]); 
      exit(0);
   }
   if(check_authentication(argv[1])) {
      printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); 
      printf("   アクセスを許可します。\n"); 
      printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
   } else { 
      printf("\nアクセスを拒否しました。\n");
   }
}



さて、ここでパスワードとは全く異なる文字列"AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"(A×29回)を入力したらどうなるでしょうか?
その結果は...
f:id:kasayanus:20180712170928p:plain


”アクセスを許可してしまいました!”


プログラムのソースコードにはもちろんこのような文字列での分岐は想定されていません。
ではなぜこの文字列でパスしてしまうのでしょうか。

その秘密はバッファーオーバーフロー攻撃という、有名な攻撃手法にあります。

このプログラムで起きたことを説明します。

このプログラムでは"password_buffer[16]"という配列にユーザーからの入力文字列を格納します。
しかし、ここに29文字のAを格納しようとすると、溢れてしまった13文字はpassword_bufferのすぐ隣のメモリ領域に"無理矢理"格納されてしまいます。
これがバッファーオーバーフローです。
その結果、運の悪いことに、その隣にあった変数"auth_flag"のメモリ領域が、"A"という文字(16進数で41)で上書きされてしまったというわけです。
c言語では0以外の数はTrueとみなされるので、その結果アクセスを許可してしまったというわけです。
41という文字がAを意味しています。


ここでは長くなるので触れませんが、実際にデバッグツールgdbを使うとpassword_bufferからバッファーオーバーフローが起きている様子を確認することができます。
下の写真では、auth_flagのアドレス”0x7fffffffdbec”を見事なまでに"A"が侵食していることがわかります。

f:id:kasayanus:20180712173158p:plain



いかがでしたでしょうか。

気づいた方もいるかもしれませんが、この攻撃はpassword_bufferのメモリアドレスの”後ろ”にauth_flagのメモリアドレスがあったからこそ成功していました。

では逆の位置関係の場合どうなるでしょうか。

ソースコードでこの2つの変数宣言の順番を変えれば、メモリアドレスの位置関係を逆にすることができます。

この場合同様の攻撃は通用しません。

しかし、攻撃の仕方を変えると、実は不正な文字列でパスすることが可能です!
この場合は少し話が複雑になります。
アセンブリやスタックセグメント、インストラクションポインタの仕組みを理解すると、攻撃可能になります。
具体的には、auth_flagを汚染するのではなく、スタックフレームの戻りアドレスを汚染することで可能となります。

また機会があれば書きたいと思います。

【ハッキングの気持ち】パスワード"AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"でログイン成功


今回はプログラムのIF文を不正にパスをハッキングの方法を紹介したいと思います。
今回攻撃するのは"auth_overflow"というプログラムです。
(下にC言語のコードを載せています)
このプログラムは、ユーザが入力された文字列が、あらかじめ決まったパスワードと一致しているか判定し、その結果を出力するものです。
今回は"brillig"または"outgrabe"という文字列が入力されるとパスするようにしています。




★ auth_overflowの使い方

1. gccを使ってauth_overflow.cをコンパイルし、実行ファイル(a.out)を作ります。
今回はオプションで"-fno-stack-protector"を付け加えます。
このオプションは、"メモリへの不正な上書きを許す"という意味だと思ってください。

gcc auth_overflow.c -fno-stack-protector

2. パスワード以外の文字列を入力するとアクセス拒否します。
f:id:kasayanus:20180712170650p:plain

3. パスワード文字列を入力するとアクセス許可されます。
f:id:kasayanus:20180712170701p:plain


以下は"auth_overflow.c"のソースコードです。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>

int check_authentication(char *password) { 
   char password_buffer[16];
   int auth_flag = 0; 

   strcpy(password_buffer, password);

   if(strcmp(password_buffer, "brillig") == 0) 
      auth_flag = 1;
   if(strcmp(password_buffer, "outgrabe") == 0) 
      auth_flag = 1;

   return auth_flag;
}

int main(int argc, char *argv[]) { 
   if(argc < 2) {
      printf("使用方法: %s <パスワード>\n", argv[0]); 
      exit(0);
   }
   if(check_authentication(argv[1])) {
      printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); 
      printf("   アクセスを許可します。\n"); 
      printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
   } else { 
      printf("\nアクセスを拒否しました。\n");
   }
}



さて、ここでパスワードとは全く異なる文字列"AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"(A×29回)を入力したらどうなるでしょうか?
その結果は...
f:id:kasayanus:20180712170928p:plain


”アクセスを許可してしまいました!”


プログラムのソースコードにはもちろんこのような文字列での分岐は想定されていません。
ではなぜこの文字列でパスしてしまうのでしょうか。

その秘密はバッファーオーバーフロー攻撃という、有名な攻撃手法にあります。

このプログラムで起きたことを説明します。

このプログラムでは"password_buffer[16]"という配列にユーザーからの入力文字列を格納します。
しかし、ここに29文字のAを格納しようとすると、溢れてしまった13文字はpassword_bufferのすぐ隣のメモリ領域に"無理矢理"格納されてしまいます。
これがバッファーオーバーフローです。
その結果、運の悪いことに、その隣にあった変数"auth_flag"のメモリ領域が、"A"という文字(16進数で41)で上書きされてしまったというわけです。
c言語では0以外の数はTrueとみなされるので、その結果アクセスを許可してしまったというわけです。
41という文字がAを意味しています。


ここでは長くなるので触れませんが、実際にデバッグツールgdbを使うとpassword_bufferからバッファーオーバーフローが起きている様子を確認することができます。
下の写真では、auth_flagのアドレス”0x7fffffffdbec”を見事なまでに"A"が侵食していることがわかります。

f:id:kasayanus:20180712173158p:plain



いかがでしたでしょうか。

気づいた方もいるかもしれませんが、この攻撃はpassword_bufferのメモリアドレスの”後ろ”にauth_flagのメモリアドレスがあったから成功していました。
では逆の位置関係の場合どうなるでしょうか。

int check_authentication(char *password) { 
   char password_buffer[16];
   int auth_flag = 0; 
char password_buffer[16];

たったの30行でYouTubeの動画を簡単ダウンロード!【Python】

pythonは僕が好きなプログラミング言語の一つです。
その魅力はそれなりに使えるスクリプトを自分で簡単にかけることだと思います。

自分はよくYoutubeを利用するので、好きな動画や曲を簡単にダウンロードできたらいいなぁ
ということで、youtubeの動画ダウンロード用スクリプトを昔作りました。

すでにオンラインサービスやCravingExplorerなどでyoutubeの動画をダウンロードできる既存のアプリは
存在するのですが、自分で書いたスクリプトなので愛着もあり、
このスクリプトを今でも愛用しています。

以下簡単に使い方を説明します。


1. python3コマンドでこのスクリプトを起動します。

python3 download.py


2. 起動したら下の画像のようにURLを聞かれるので落としたい動画のURLを入力してENTERします。


      f:id:kasayanus:20180705001036p:plain


3.  数秒後、スクリプトと同じディレクトリにmp4とmp3のファイルが保存されます。

    f:id:kasayanus:20180705001230p:plain




下のコードが今回紹介したdownload.pyです。
(このスクリプトではダウンロードしたmp4をmp3に変換するためにfffmpegを併用しています)

みていただければわかるように非常に短いコードで、簡単に
使える実用的なスクリプトが作れます。
pythonは学習コストも低く初心者でも勉強しやすいことで知られています。
新しいフレームワークやライブラリのインストールもコマンド一つで簡単にでき、知っているととても便利な言語だと思います!

import pytube
import subprocess
import os
import sys

while(True):
 input_text=input("URL or EXIT:")

 if(input_text=="exit"):
  sys.exit()
 else:
  yt = pytube.YouTube(input_text)
   if(yt==None):
    print("invalid URL")
   else:
    vids= yt.streams.all()
    parent_dir = r""
    vids[0].download(parent_dir)
    default_filename = vids[0].default_filename 
    new_filename = default_filename+".mp3"
    subprocess.call(['ffmpeg', '-i', 
			    os.path.join(parent_dir, default_filename),
			    os.path.join(parent_dir, new_filename)
			])

    print('done')

"はじめてのハッカソン"に参加してきました

今日は六本木一丁目の泉ガーデンというコワーキングスペースハッカソンのイベントに参加しました。

4〜6人のグループを4つ作りそれぞれのグループがアイデアを出し合って、一からWebサービスを開発するというものでした。

率直に言えば自分は今まで共同開発の経験がなかったので、未熟さを感じました。

それは技術力の未熟さでもあるし、自分の考えていることをうまく言葉で伝えられないという情報伝達力(コミュ力)の未熟さでもありました。

自分はRailsでサーバプログラムを担当しましたが、他のメンバーの方が作ったHTMLやCSSJavascriptクラウドサーバとのマージ作業がうまく行かず当初予定していたようになかなか開発が進みませんでした。

結論としては、今回のハッカソンでは、ハッカソンのもつハッカソンが大好きな人たちの感じるハッカソンの”本当の楽しさ”には達しなかったんじゃないかな、と思いました。

あくまで想像ですが、ハッカソンの本当の楽しさというのは、それぞれが作った部品を結合させていきプロトタイプを作り上げて、そこからさまざまな実現可能な拡張機能をメンバー全員で検討していき、それを再びプロトタイプの上に乗っけて、どんどんサービスの規模を広げていくこと、これがハッカソンの楽しさなんじゃないかな、と思いました。

今回のハッカソンではこの段階まで行かなかったのがとても残念でした。

また、今回のハッカソンで得た気づきというのがあります。

それは深く狭く型の学習の重要性です。

自分はウェブプログラミングを始めてまだ日が浅いので、とにかくいろんなフレームワークやウェブ技術を"広く浅く"学習していくという方針で今まで学習を勧めていました。そうすることで、例えばなにかネット上記事を読んでいたり、人との会話で出てきたウェブ技術に対して知らないよりは少しでも知っていたほうが飲み込みやすくなると思ったからです。しかし、それはサービスを作り上げる上ではあまりよくない勉強法だというふうに気づきました。一つの技術をもっと深く勉強していくことの必要性を今回のハッカソンで感じました。

なにはともあれ。今回のハッカソンは自分の技術力をはじめて客観的に見ることができたいい機会でした。

参加した皆さんありがとうございました!

めちゃくちゃ疲れて眠いのでこのへんで終わります。