お腹.ヘッタ。

関数型とかセキュリティとか勉強したい。進捗つらぽよ

セキュリティ・キャンプ全国大会2016の応募用紙を晒す(後編)

自信がないなぁ。。。。後編は選択問題についてです。選択問題は4つ 選択でした。多分間違っていますしボロボロです。。。がそれよりも多くやってしまってたので提出してない分も晒す。(提出したのは4,5,6,11)
以下本文〜〜まんま載せます。

【選択問題】

選択問題.1: 以下は変数hogeとfugaのメモリアドレスを表示するプログラムと、その実行結果です。実行結果のhogeとfugaのメモリアドレスを見て、思うことを説明してください。
・ソースコード
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char **argv){
 int hoge[10];
 int *fuga;
 
 fuga = malloc(1);
 
 printf("hoge address = %p\n", hoge);
 printf("fuga address = %p\n", fuga);
 
 free(fuga);
 return 0;
}
 
・実行結果
hoge address = 0x7fff539799f0
fuga address = 0x7fca11404c70 

スタックに積まれたhogeは上位のアドレスに積まれて、fugaはヒープ領域の下位アドレスに積まれています。
先頭の0x7fというのは7ビットの部分を表しているのかと考えています。以前にGCCXcodeでヒープ領域がどこに割り振られるのか見たことがあるのですがその際、先頭アドレスが前者は0x7fで後者が0x10であったので処理系によって変わっているのではないかと感じました。

選択問題.3 :RAMは主記憶装置、HDDやSSDなどは補助記憶装置と呼ばれます。一般にCPUは主記憶装置上のプログラムしか実行できません。ではなぜ、私たちは普段から補助記憶装置に書き込んだプログラムを実行できているのでしょうか?パソコンの電源を入れてからのストーリーを考えてみてください。

電源を入れハードウェアの初期設定が行われ、ブートストラップローダを動かしてBIOSが起動する。(ブートストラップローダはROMに用意されてる可能性もある)Power On Self Testが行われ、CPU、RAM、割り込みやHDD、GPUハードウェアの初期化が行われる。次にBIOSは起動デバイスに設定された記憶装置を探し、。ブートローダが起動する。ブートローダには段階がありプログラムからプログラムを呼び出し、初めてOSを呼び出すことができる。一次ブートローダ、二次ブートローダと細分化できる。この場合は二次ブートローダーでありBIOSは1次ブートローダーである。上でもあるが補助記憶装置に書き込んだプログラムを実行するにはRAM上にROMのデータを展開して起動する。その際できるだけ高速で使うためにキャッシュメモリと呼ばれる使用頻度の多い命令データを保存したりして処理装置と記憶装置を本来の速度を隠蔽したりする。また補助記憶装置をメモリのように振舞わせる仮想メモリはあくまでスワップであり稼働してないものを充てがうのが普通だと考えている。

選択問題.4

突然だが、RH Protocolで用いられるRHパケットのフォーマットを以下に示す。なおRH Protocolは実在しないプロトコルであり、その内容について特に意味は無い。
Format of RH Packet

———————— —————...— ———————…— ———————...— ———————...—
 Magic (2byte)      Source(20byte) Destination(20byte) Data Length(4byte) Data( variable )      
———————— —————...— ———————…— ———————...— ———————...—

 
char Magic [2];
char Source[20]; /* null(‘\0’) terminated ascii strings */
char Destination[20]; /* null(‘\0’) terminated ascii strings*/
uint32_t DataLength; /* min 0, max 4,294,967,295 */
char Data[DataLength]; /* null(‘\0’) terminated ascii strings */
 
バイトオーダーはbig endian(network byte order)とする。
添付するバイナリは、とあるRHストリームのうち片方向のみを抽出したものである。このバイナリストリームを読み込み、1つのRHパケットが以下の条件のすべてにマッチするときに標準出力に文字列”PASS”、 それ以外の場合は”REJECTED”と表示するCもしくはC++のプログラムを記述し、実行結果と共に提出せよ。また、マッチングにかかるCPUサイクル及びメモリ使用量を計測し記載した場合、評価に加味する。
 
Condition(条件)1: Magicがchar[0] = ‘R’、 char[1] = ‘H’であること。
Condition 2: Sourceが”rise-san”または”cocoa-san”であること。なお、”RiSe”や”Cocoa”など、小文字大文字が混ざっていても、マッチさせること。
Condition 3: Destinationが”Chino-chan”または”Chino"であること。なお、cond. 2と同じく、小文字大文字が混ざっていても、マッチさせること。
Condition 4: Sourceが”cocoa-san”かつDestinationが”Chino”の場合はREJECTする。
Condition 5:  Dataに下記の文字列を厳密に含むこと。
char** valid_order_brand =
{
    “BlueMountain"
    “Columbia”,
    “OriginalBlend"
};
Condition 6: Dataに下記の文字列を厳密に含まないこと。なお、cond. 4よりも、cond. 5が優先される。
char** invalid_order_brand =
{
    “DandySoda"
    “FrozenEvergreen”
};

以下送ったやつ

・実行結果 
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
PASS
PASS
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
REJECTED
PASS
REJECTED
REJECTED
ソースコード

////
////  main.cpp
////
////  Created by 〜〜〜on 2016/05/06.
////  Copyright © 2016年 〜〜〜. All rights reserved.
////
//

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cstring>
#include <sys/time.h>
#include <sys/resource.h>
using namespace std;

int Mstricmp (const char *s1, const char *s2)
{
    int cmp;
    
    while (*s1)
    {
        //toupper:大文字小文字に変えれる関数
        if ((cmp = toupper (*s1) - toupper (*s2)) != 0){
            return cmp;
        }
        s1++;
        s2++;
    }
    return (*s2) ? -1 : 0;
}
int main(){
    char outfile[] = "/Users/HayasakaTakeru/Desktop/RH_secamp/pyonpyon.rh";  //読み込むファイルの指定
    char Magic [2];
    char Source[20]; /* null(‘\0’) terminated ascii strings */
    char Destination[20]; /* null(‘\0’) terminated ascii strings*/
    uint32_t DataLength; /* min 0, max 4,294,967,295 */
    char Data[256] ; /* null(‘\0’) terminated ascii strings */
    string buf;

    ifstream fin( outfile, ios::in | ios::binary );
    //  ファイルを開く
    //  ios::in は読み込み専用  ios::binary はバイナリ形式
    
 //  ファイルが開けなかったときの対策
    if (!fin){
        cout << "ファイル:pyonpyon.rh が開けません";
        return 1;
    }
    
    //文字列ではないデータ
    
    while(!fin.eof()){  //ファイルの最後まで続ける
        //データを読みこむ
        fin.read( ( char * ) &Magic, sizeof( Magic ) );
        fin.read( ( char * ) &Source, sizeof( Source ) );
        fin.read( ( char * ) &Destination, sizeof( Destination ) );
        fin.read( ( char * ) &DataLength, sizeof( uint32_t ) );
        size_t DataLengther = htonl(DataLength);//ホストバイトオーダーからネットワークバイトオーダーに変換
        
        int i=0;
        do{
            fin.read( ( char * ) &Data,  DataLengther);
            buf+=Data;//一時保存
        }while (256<DataLengther-(256*i++));
        
        
       //paternmach
        //Condition1
        if (Magic[0]!='R'&&Magic[1]!='H') {
            goto False;
            
        }else if((0!=Mstricmp("RISE-SAN",Source))&&0!=Mstricmp("COCOA-SAN",Source)){//Condition2
            goto False;
            
        }else if((0!=Mstricmp("CHINO-CHAN",Destination))&&(0!=Mstricmp("CHINO",Destination))){//Condition 3
            goto False;
            
        }else if ((0==Mstricmp("Chino",Destination))&&(0==Mstricmp("cocoa-san",Source))){//Condition 4
            goto False;
            
        }else if (string::npos != buf.find("DandySoda")||string::npos != buf.find("FrozenEvergreen")){//Condition 6
            goto False;
            
        }else if (string::npos == buf.find("BlueMountain")||string::npos == buf.find("Columbia")||string::npos == buf.find("OriginalBlend")){//Condition 5
            goto False;
            
        }else{
            printf("PASS\n");
            buf="";
            continue;
            }
       
       
    False:printf("REJECTED\n");
        buf="";
    }
    
    fin.close();  //ファイルを閉じる
    return 0;
}

・メモリ/CPUサイクル使用量計測
環境:MacBook Pro Retina2014(Core i5-4308U &RAM 8G)、GCCコンパイルしたもので行った。
#include をincludeし、 getrusage関数による最大消費メモリ量のチェックを行った。
Max RSS : 684.000000 MB

rdtsc命令を使用して計測を行った。
CPUCycle(10回計測):
1443299938,
1133714246,
766179364,
3249800630,
2473480668,
3753169854,
4201688936,
1112906710,
3299073340,
420707638

10回の平均値:2185402132.4

最後に計測に使用したソースコードを添付します。

// 最大消費メモリ量のチェック
    int chk;
    struct rusage usage;
    chk = getrusage(RUSAGE_SELF, &usage);
    if(chk != 0){
        printf("error\n");
        exit(-1);
    }
    printf("Max RSS = %lf MB\n", usage.ru_maxrss / 1024.0);    // このプロセスが消費した最大メモリ領域

  //CPUサイクル取得
static inline uint64_t get_cycles()
{
    uint64_t t;
    __asm volatile ("rdtsc" : "=A"(t));
    return t;
}
  cout<<get_cycles()<<endl;

てかstricmpはXcodeにないのな。。。いじめだ急遽作ったわ。。。

選択問題.5 :PCなどに搭載されているOSは「汎用OS」と呼ばれますが、それに対して、家電やAV機器などの「組込みシステム」に搭載されているOSは「組込みOS」と呼ばれます。組込みOSと汎用OSの違い、「OSが無い」や「ベアメタル」という環境、そもそもOSとは何なのか?など、あなた自身はどう考えているのかを、あなた自身の言葉で自由に説明してください。(「正しい答え」を聞いているわけではありません。あなた自身の考えを教えてください)

組み込みOSは必要がない機能を削除する事が容易で、それ故にいらないものを削ることにより小さい構成にしてリソースを確保してるもの、汎用OSは使われそうな機能はすべて盛り込んでおいているものでアップデートで汎用性をもたせているものと思います。
 そもそもOSというものは今までそれぞれ独自の仕様を持っていたハードウェアとのインターフェースとなりアプリケーションやソフトウェアを作るのに一定の指針を持たせたものであり、逆に言えば今日の多様なプログラムが使われているのはOSがあるからと言えると考えています。またアプリケーションとハードウェアのインターフェースだけではなくユーザーとのインターフェースでもあると言えて操作方法が統一されるので、1つの操作方法やシステムコールを覚えれば他の場所でもそのノウハウをそのまま生かすことができます。他にもデスクトップリビューションを組み込むことによりそのOSのブランド性というものも演出できると考えれます。このようにOSというのはアプリケーションとハードウェアだけではなく多くに対するインターフェースと私は捉えています。
 しかし逆にOSがないとどうなるのでしょうか、一般的にベアメタル実行やベアメタルコードなどと呼ばれるのですが具体的には直接ハードウェアを叩いていることを指しています。OSやフレームワークがなくて何が嬉しいかというと自分の手で高速化をすることとだと言えます。普通はメモリを意識しないようなライブラリやフレームワークAPIを使わせてそれを使えばどこでも簡単に駆動を可能に設計しています。しかし高速なIOをしたい時やコンパイルされたプログラムの容量を削りたい時などはレジスタを意識して必要最低限のコードに自力で最適化することにより高速に出来ます。実際Arduinoを高速で使いたくてIOをライブラリではなくポートをいじってあつかった際は数倍速度の向上がありました。このようにレジスタを意識するということだけでもチューニングが出来るようになります。
 違うOSに観点を移すと、例えば組み込みOSは「実時間制約を保証のOS」という言い方も可能であり、RTLinux やART-Linuxが有名です。実際にロボットなどを制御する場合,通常は一定の時間間隔でセンサ信号を読み取り,制御演算を行い,アクチュエータに指令を出します。外部との情報の入出力の可否は,ソフトウェア上では一般にI/Oポートの操作 とメモリの読み書きの可否 でありそのタイミングの周期的な制御動作を達成すれば実際に動かすことも可能だとも言えると思います。組み込みOSでなくても普通のLinuxカーネルで時刻を指定して行いたい処理の直前で、タイマ割り込みの直後などで目的の実行時刻と現時刻の差を指定し、休眠システムコールを行うなどプロセスの優先度を高めておくなどで周期動作は得ることができると考えています。あえて問題を挙げるとすればART-Linuxなど、ある特定のハードウェアでは対応してないなどということがあるのでもっとマルチに作成することが望まれると考えています。
 自分自身のまとめとするとOSは現在のエンドユーザーの手にソフトウェアを私たちエンジニアが届けるのに必要不可欠のもだと思います。しかしそれは今の潤沢なマシーン環境と高レイヤーな考えがあるからであり環境が変わればそれを維持するためにどれだけ高速にチューニングするかというのが勝負に踏んでいます。また今ある資源をうまく使うのもOSの仕事でありリソースの管理を行い、必要に応じてはポーリング、スレッド、メモリの処理もするものだと思っています。例えばスレッドであれば、windowsのThreadやiOSのGCDなどが挙げられ、実際これらの処理は簡単でGCDであればキューに投げ込むだけでそれをOS側が入れられたものの優先度とFIFOで実行していくという形で成り立っています。しかし並列であるならばデッドロックが起きる可能性もありえます。チューニングしていく上で仕方がないことではありますがOSだけではエンドユーザに良いソフトウェアを届けることはできないと言えるところだと思います。つまるところの結論というものはまだ未熟な私には出せませんが一番感じていることはOSはという考え方があったからこそ潤沢なアプリケーションがあり我々はハードウェアに苦悩しないのだと感じています。

選択問題.6 :IDとパスワードを入力してユーザの認証を行うWebアプリがあります。あなたがこのアプリに対してセキュリティテストを行う場合、まず、どのようなテストをしますか? なぜそのテストを選択したのか、その背景や技術的根拠と共に記載してください。アプリの内部で使われている技術やシステム構成に、前提を置いても構いません

今回使われているのはバックにDBがありNoSQLとSQL系の2パターンと想定して、その際使われているのはApacheサーバ(PHP、Laravelやcakephpといったフレームワーク)や他のサーバフレームワーク(node.jsなど)という想定でテストを考えた。

まず使用しているものバージョンを確認し、node.jsであればnpmのパッケージに脆弱性があったりなどosinjectionの危険を回避したいのでバージョンが古い際は最新版にできるだけ変更します。
次にWiresharktcpdumpなどを開いてパケットを見ながらレスポンスを確認を行います。その際PHPのバージョンの隠蔽などを確認し、Cookieにhttponlyの属性を付けるなどもしておくべきと考えます。(その際ヘッダ周りを見るべきでX-Content-Type-Optionsなどが叩かれる恐れがありえます)。またワンタイムトークンなどを導入するなどでCSRF などの対策をすべきで、他にもXSSの対策について確認を行い、バインドして『”’<>&』などを文字実体参照・数値文字参照に置き換え、弾くようにします。
その次にDBに対してチェックを行い、NoSQL系であれば演算子インジェクションやSSJI(Server Side JavaScript Injection)、JSONを使ったinjectionなどで攻撃が可能なので、対策としてはSSJIであればバインド機構を入れたり動的なJSを避けたり、演算子インジェクションであれば型にキャストするなどで対策ができ、ホワイトリストを入れたりしてパラメータをを追加されるのを阻止するのも有効だと考えれます。
SQL系であればSQLinjectionがあるのでバインド機構を入れてサニタリーングをしたりホワイトリストを入れるのも有効だと言えると思います。
他にもSQLmapなどを使って脆弱性の確認などをするのも有効だと言えます。
次にサーバの権限の制限を確認し、トラバーサルができないようにしておくなど、またidなどをパラメータに入れて投げるときはすでに許可してるものをキーとしている時などに気をつけるべきだと思います。それを含むとトラバーサルできてしまうので、気を付けたいところと考えれます。

選択問題.11 :2015 年に発行された CVE の内、あなたが興味を持った”サーバに存在した”脆弱性について1つ提示してください。その脆弱性を悪用した攻撃を検知する方法について詳細に記述してください。また、興味を持った理由を記述してください。 CVE番号:CVE-2015-◯◯◯◯

CVE-2015-1793について
攻撃を検知する方法:
 この脆弱性はOpenSSLの証明書チェーンのチェック機構に不備があったというものです。この脆弱性を利用した際に想定されるのはman-in-the-middle attackが想定されます。証明書の検証において初めの証明書チェーンの構築に失敗した場合、代替の証明書チェーンの構築を試みますが、この処理の実装に不備がありました。その結果、例えば CA フラグが FALSE とされている証明書を使って発行された証明書を、不正なものであると検知せず、バイパスして信頼している CA によって発行された証明書として扱ってしまう可能性があります。
 具体的に効率的な対策を思いつかなかったのですが、今回考えられるのは中間者攻撃なので直接サーバーにつなぐのではなくサーバを経由して別サーバでのチェックとサーバーでのIPのチェックをするという手を取るのが良いかと考えました。
 具体的にはAが接続者、Bがサーバ、Cが別サーバとした際に攻撃者がAB間、AC間のどこかに接続をしなくてはなりません。流れとしては、まずAC間およびBC間の確認を行います。
A->B: CのIPアドレス->B:認証、
B->A: CのIPアドレス->A:認証
これにより、Aの確認者とBの確認者のIPアドレスが一致しなければ、 AC間またはBC間に攻撃者がいることが判明します。
次にAB間の確認をします。
A->C:BのIPアドレス、C->B:AのIPアドレスをCに送る、B->C:AのIPアドレス、C:認証、C->A: 認証結果、C->B: 認証結果。
これにより攻撃者がいない通信経路が成り立っていることが確認できたなら通信が窃盗されるということはないと思います。
弱点としては攻撃者がAB間およびAC間に同時に割り込むことで迂回される恐れがあると考えます。その際はCを動的に変え続けるなどして接続を乱すことで対応できるかなと思っています。

せっかくなので脆弱性のコードを見てみました。”openssl-1.0.2c/crypto/x509/x509_vfy.c”が攻撃される原因のところです。

// CAが追加されるところのソースです。
        // continue to construct the chain of certificate(add issuer)
        for (;;) {
            // self-sign and exit
            if (cert_self_signed(x))
                break;
            // look for the issuer in the trust chain
            ok = ctx->get_issuer(&xtmp, ctx, x);
            // error
            if (ok < 0)
                return ok;
            //  not found
            if (ok == 0)
                 break;
            x = xtmp;
            // add the issuer for the untrusted certificate to the chain of verification
            if (!sk_X509_push(ctx->chain, x)) {
                X509_free(xtmp);
                X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
                return 0;
            }
            num++;
        }
// CAのチェック機構です
        // verify the issuer chain in for (;;) 
        i = check_trust(ctx);
        if (i == X509_TRUST_REJECTED)
            goto end;
        retry = 0;
 // 代替チェーンを検証
        // verify the alternative chain
        if (i != X509_TRUST_TRUSTED
            && !(ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST)
            && !(ctx->param->flags & X509_V_FLAG_NO_ALT_CHAINS)) {
            while (j-- > 1) {
                xtmp2 = sk_X509_value(ctx->chain, j - 1);
                 // return when get a “reasonable-like” certificate,in fact look for the issuer based on CN domain
                ok = ctx->get_issuer(&xtmp, ctx, xtmp2);
                if (ok < 0)
                    goto end;
                // alternative chain exists
                if (ok > 0) {
                    X509_free(xtmp);
 
                    // remove the part above the alternative chain
                    while (num > j) {
                        xtmp = sk_X509_pop(ctx->chain);
                        X509_free(xtmp);
                        num--;
                        // 信頼できない証明書を削除
                        // the problem is 
                        ctx->last_untrusted--;
                    }
                    retry = 1;
                    break;
                }
            }
        }
    } while (retry);

考察:今までのOpenSSLは代替チェーンの検証をサポートしていない古いバージョンであったのでバグらなかっけれどもある程度まともにサポートしたせいでバグが出てきたのかなと推測してます。

興味を持った理由:
 なぜこの脆弱性に興味を持ったかというと中間者攻撃は通常はSSLが正常に動いていれば不可能な攻撃なのですが、今回は肝心要のSSLに問題がありました。ですので実際ゼロデイとかで叩かれたら大変だと強く感じたのでこれを選択しました。
 この脆弱性が出た時他のOpenSSLの脆弱性もかなり多く一緒に公開されました。しかもほぼ緊急度が高めのばかりでそれで大変印象に残っていたからです。例えば2015-1792であればcrypto/cms/cms_smime.cのdo_free_upto関数には、signedDataメッセージの処理に脆弱性が存在し、攻撃者によって細工されたsignedDataメッセージを処理することでサービス運用妨害(無限ループ)状態にされる可能性がある。などかなり怖いものばかりです。しかしあまりその時は知識もなかったので詳しく見るということをしませんでした。(ベンダー情報しか見てなかった)ですので今回見てみました。(かなりガバガバこと&多分ソース読み違えたことしか書いてませんが。。。)

感想

楽しかった!!

自分自身こうやって知見を広げれるのはやはり大きな強みだなぁと感じた。でもかなりボロボロなことばかり書いてるのでもし受かったら気合って感じで通ったって感じですね。。。
あと機種依存文字今度は死なないように
機種依存チェッカー | さぶみっと!JAPAN

を置いておきますね。来年も同じパターンならこれ使って提出してください><