選択問題.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ビットの部分を表しているのかと考えています。以前にGCCとXcodeでヒープ領域がどこに割り振られるのか見たことがあるのですがその際、先頭アドレスが前者は0x7fで後者が0x10であったので処理系によって変わっているのではないかと感じました。
選択問題.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にないのな。。。いじめだ急遽作ったわ。。。
選択問題.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メッセージを処理することでサービス運用妨害(無限ループ)状態にされる可能性がある。などかなり怖いものばかりです。しかしあまりその時は知識もなかったので詳しく見るということをしませんでした。(ベンダー情報しか見てなかった)ですので今回見てみました。(かなりガバガバこと&多分ソース読み違えたことしか書いてませんが。。。)