しゃちの備忘録

これからC++を始める人によるC++の備忘録です(今のところ)

SECCON 2016 Online CTFに参加しました

12月10日, 11日に開催されたSECCON 2016 Online CTFに参加しました. CTFをやり始めて3年目ですが, CTFに参加するのはほぼ1年ぶりの参加なので実質初参加です….

最近ここの更新が滞っていたので, 折角なので書いてみようと思い立ったので書きます.

SECCON CTFって?

はじめにCTFとは, Capture The Flagの略で, 問題で与えられた物に隠されたキーワード(Flag)を入手するという形式のコンテストになります. 出題の形式は様々で, Flagが隠されている物は実行形式のファイルだったり, ソースコードだったり, パケットの一部だったり, webのリンクや画像だったりもします. それらに対し, さまざまな知識を使ってアプローチを試みて隠されたキーワードを入手することを目指します. 例えば対象が, 実行形式のファイルなどだった場合はリバースエンジニアリングを試みたり, パケットの一部ならパケット解析をしたり, webのリンクだったらそのサイトに行ってSQLインジェクションしたりする といった感じです. 要求される知識の幅がとにかく広く, コンテスト中のググりでフラグ取得に必要な知識を勉強するのはいつものことです.

CTF自体は本来セキュリティに関する技術の勉強の側面がメインであり, Flag=機密情報に見立てそれを入手する技術を勉強し実践する機会として設けられています. 普通のサイトでSQLインジェクションをやると犯罪になるので, それを競技として実践しどんなものか実感できる貴重な場だと思います.

で今回参加したSECCON CTFとは国籍、性別、年齢、問わず参加可能な日本最大のオンラインCTFコンテストです. 私は過去2回参加していて, 破損QRコードを復号することに定評がありました……. 今年は自分もQRコード以外が解きたくなったので, 個人参加です. どんな結果になっても泣きません.

挑んだ問題

Vigenere(Crypto 100)

タイトルからしてヴィジュネル暗号を解く問題っぽいです. 暗号化された文字列と, 元の文と, 鉤の文字数がと対応表が以下のように与えられました. k: ???????????? p: SECCON{???????????????????????????????????} c: LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ 対応表は略

さらに, md5で元の文をハッシュ化した文字列が与えられました. md5(p)=f528a6ab914c1ecf856a1d93103948fe

ので, フラグGettoに向け, 以下のような二つの方法を考えました.

その1 1.key(以降k)である「????????????」を取得(銭湯七文字がわかってるとはいえ, 275 = 14348907パターンある…) 2.そのkで変換後の文(c)を原文(p)に復号 3.pをmd5ハッシュ値計算をして, 「f528a...」といっちしているかを調べる 4.一致したら終了, しなかったら1にもどり別のkを生成する

その2 1.md5(p)を適当に逆変換 2.先頭が「SECCON{」になっていれば採用, なってなければ他のパターンを試す

その2を試したらあっけなく通ってしまいました…. 先頭のSECCONが分かってるのがでかかった感じです. 正統派で行くなら, その1ですよね.

VoIP(Forensics 100)

「Extract a voice」という説明とともにpcapファイルが配られました. pcapファイルとはPacket Capture Dataの略です, パケットの一部が格納されたファイルです. で, パケット解析にはWiresharkというソフトが便利です. のでそれでこのpcapファイルを開きます.

ところで問題が「VoIP」って名前です. VoIPとは, 音声をパケットに変換したものをIPネットワークでリアルタイム伝送する技術のことを指します. そういうわけで, 「このパケットは音声が変換されたものなのかなー」とか思ってました. ので, WiresharkについてるVoIP機能で再生してやると, それっぽい言葉が. 何回か間違えつつ, 入力するとFlagがでましたとさ. 聞き取りむずかしかった

まとめ

実は今年は2問しか解けませんでした…200点ですね(低い). CTFへの参加は1年ぶりでしたが, 各種ツールを使うのにも手間取ってしまいもったいなかったです.

やっぱりCTFも競プロもちょっと気合い入れてやらないと中途半端だし, できないと悔しいですね…… もっと精進します.

C++における範囲ベース for ループ

C++でカウンタを使わずに, 要素を1つづつ参照する方法として, 範囲ベース for ループなるものを見つけたのでまとめておきます*1

範囲ベース for ループとは

C++11から新しく追加された言語機能. range-based for loopとも書くらしい.

似た機能として'for_each'があるらしいが, あちらはSTLの関数テンプレートであり, こちらはネイティブの機能として追加されたものであるらしい.

記法は以下の通り.

int v[] = {1, 2, 3, 4, 5};
for(auto &x : v) {
  std::cout << x << "\n";
}

基本的な記法は, for ( xxx : array ) { }らしいのだが, 型名を記述しないために同じくC++11で導入されたautoを使用. 加えて, 無駄なコストを書けないために参照渡し&xの形をとっている.

参照渡しをしているのでxの値を書き換えると, その時参照しているvの値も変わるので注意. 値を書き換えたくないときは, const auto &x : vをすればよい.

indexを取得できない(しようとすると結構助長なコードになるっぽい?)ので, そこは一長一短だなーと覚えておきます.

*1:旧タイトル 『C++ちょっとわからないメモ』です. 更新しました

Pythonにおける『and』と『or』の動作

Pythonの『and』と『or』の動作は, 他の言語と少し違う変わった動作をするのでそれについてメモ.

一般的な『and』と『or』の動作

bool型(True, またはFalse)の変数A, Bとしたときに,

A B A and B A or B
True True True True
True False False True
False True False True
False False False False

に, なります. ここでのポイントは, 『AとBはBool型のみである』ということです.

Pythonにおける『and』と『or』の動作①

では, Pythonではどのような動作になるか, まず入力の条件から書きます. 『AとBの型についての制限はありません』.

何を言っているのかと思うと思いますが, Pythonでのand,orAとBの型についての制限がないのです. これには, andorがそれぞれ行っている処理が深くかかわっています.

and の処理

Pythonandは以下のような処理を行います.

A and Bと書くとき, 第一引数A(第一引数) が偽なら A(第一引数), そうでなければ B(第二引数)

コードの形で記述すると以下のようになります.

if not bool(A):
    return A
else:
    return B

2種類の返値を見るとわかるように, 引数のどちらかをそのまま返しているのがわかると思います. ではここに示されているbool(A)では何が起こっているのでしょう.

Bool の処理

boolは基本的にどんなものでも真偽値判定を行ってくれます. ポイントとなるのが, 偽と見なされる以下の値達です.

  • None
  • False
  • 数値型におけるゼロ ( ex. 0, 0.0)
  • 空のシーケンス ( ex. '', (), [] )
  • 空のマッピング ( ex. {} )
  • ユーザ定義クラスのインスタンスで, bool() , len()メソッドを定義していて, 整数 0 または bool値 False を返すとき

逆にこれ以外のケースは全て真になります.

or の処理

Pythonorは以下のような処理を行います.

A or Bと書くとき, 第一引数A(第一引数) が偽なら B(第二引数), そうでなければ A(第一引数)

コードの形で記述すると以下のようになります.

if not bool(A):
    return B
else:
    return A

見た目はandと逆の構造になっています.

Pythonにおける『and』と『or』の動作②

上記のBoolの性質を理解したうえで, Pythonの場合のandの入出力の対応表を書くと以下の通りになります.(ちょっとだけ書き方自体も違うので読むとき注意)

それぞれのA,BをBoolにかけたときにどんな値が帰ってくるかも書いておきました. これを見るともともと真偽値表と同じ形になっていて, 正しくand , or として機能しているのがわかると思います.

bool(A) bool(B) A and B A or B
True True B(bool(B)->True) A(bool(A)->True)
True False B(bool(B)->False) A(bool(A)->True)
False True A(bool(A)->False) B(bool(B)->True)
False False A(bool(A)->False) B(bool(B)->False)

おまけ:サンプルコード

これの検証に書いたしょぼいコードですが置いておきます. 実行結果も合わせてどうぞ.

A  = ["abc", ""]
B  = ["xyz", ""]
for a in A:
  for b in B:
    print( "A(" + a + ")  or B(" + b + ") -> " + (a or b))

for a in A:
  for b in B:
    print( "A(" + a + ") and B(" + b + ") -> " + (a and b))
A(abc)  or B(xyz) -> abc
A(abc)  or B() -> abc
A()  or B(xyz) -> xyz
A()  or B() ->
A(abc) and B(xyz) -> xyz
A(abc) and B() ->
A() and B(xyz) ->
A() and B() ->

競プロで使えそうなC++標準入力まとめ

競プロの問題は標準入力で与えられることがほとんどです(だと思うんですけどどうなんですかね…間違ってたらごめんなさい).

まずは競プロでよくある出題のシチュエーションを想定して, その時にどのように標準入力で受け取ってやればいいかをまとめたいと思います.

一応適宜更新予定.

単一の入力が与えられる時

input >>> n
nは数値

std::int n;
std::cin >> n

input >>> c
cは文字

std::char n;
std::cin >> n

input >>> s
sは自由長の文字列

string s;
std::cin >> n

ただし, このやり方だとスペースが入るとそこで終わってしまうのでそこは注意.

1行で複数の入力が与えられる時

input >>> a b c

int a, b, c;
cin >> a >> b >> c;

最初の1行で入力される行数が, そのあとに単一の入力が与えられる時

input >>> n
input >>> a1
input >>> a2
input >>> ...
input >>> an

for文使ってくるくるします.

int n;
std::cin >> n;
int a[n];
for(int i=0; i<n; i++) std::cin >> a[i];

最初の1行で入力される行数と各行の個数が, そのあとに複数の入力が与えられる時

input >>> n m
input >>> a11 ... a1m
input >>> a21 ... a2m
input >>> ...
input >>> an1 ... anm

n×mの配列を作って, for文くるくる

はじめに

競プロを通してC++を勉強したくなったので, 衆人環視のためにやったことを1つずつまとめるために始めました.

ので, 暫くはC++中心の備忘録になりそうです. 競プロで使えるようになるまで, 四苦八苦しながら頑張ります.

因みに, 普段はPython書いてます.

最初の記事という事なので,コードハイライトのテストもかねて, はろわやります.

#include <iostream>
int main(){
    std::cout << "Hello World!\n";
    return 0;
}
追記

gistを埋め込むこともできるらしいので, それも合わせて

gist23f2d919bb14b3e2ea64220b2221b25f