ksnctfにチャレンジ 第36問目です。
※ksnctfは常駐型のCTFサイトです。 ※問題のページはコチラです。
(Take36の掲載忘れてました…)
Are you ESPer?
この問題は、本質部分は簡単なのに 仕上げるのにちょっと戸惑う部分があります。
esp というプログラムは 1~9 の数字から正解のものを あてるゲームプログラムで 最初のうちは複数回チャレンジしてもよいので 手動二分検索っぽく入力していけば大丈夫ですが 4,5問目になるとミスが許されなくなっていき 最終的にノーミスで20問連続正解しないといけません。 エスパーの方以外はクリア不能ですね。
ですが、espプログラムを覗くと実は正解の 数字がわかってしまうよ という問題です。
逆アセンブルした結果から 大まかにespの内容をC言語に起こすと
void main()
{
int i,n …ループ数など… ;
int var1, var2;
int flag;
srandom(time(NULL));
for(i=0; i<20; i++)
{
var1 = rand()%10;
for(;;)
{
printf("Level %d/20 …\n",i, …);
scanf("%d", var2);
if( var1 > var2 ){
puts("Too small\n");
flag = 1;
}else{
if(var1 < var2){
puts("Too large\n");
flag = 1;
}else{
puts("Correct!\n");
flag = 0;
}
}
~~ チャレンジ回数チェック ~~
if(flag) continue;
}
}
}
まぁここまで書きおこす必要はありません ようは先頭で srandom(time(NULL)); が使われていることさえ確認できれば終了です。
C言語の rand() は 線形合同法を用いた擬似乱数なので 本来出力するのは乱数っぽい数列です。 よって"同じ条件"では同じ数値を返してきます。
その条件と言うのが seed値 でsrand() で seed値を設定します。
今回は現在時刻 time(NULL) をseed値として利用しているわけです。 プログラムの実行時間は実行するたびに異なるので 毎回違う乱数っぽい数列が得られるっという寸法です。
ということは
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void main()
{
int i;
srandom(time(NULL));
for(i=0; i<20; i++) printf("%d\n",rand()%10);
return;
}
こんなテストプログラム take36.cを組んで
$ gcc take36.c
$ ./a.out | ./esp
と実行すると、 a.out と esp はほぼ同時刻に実行されるので
この通り ./a.out と ./esp で同じ乱数が生成されて 一瞬でオールコレクト になります。
ということで完成しましたので
$ gcc take36.c
$ ./a.out | nc ctfq.sweetduet.info 10777
これでクリア!…あれ?
全然合いません。
仕組み上違ってはいけないので違うとしたら 時刻です。 どういうことかというと
$ ./a.out | nc ctfq.sweetduet.info 10777
これが実行されるとき a.out の time(NULL) は手元のコンピュータに設定された現在時刻 esp の time(NULL) はksnctfのサーバーに設定された現在時刻 です。
これが何秒かズレているのでseed値が違ってしまっているのでしょう。
take36.c のsrand()に
srandom(time(NULL)+X);
Xに±1〜10秒程度のズレ調整分を設けてやってみました…が失敗。
どうしようか暫く悩んだのですが 結局のところksnctfのサーバーの設定時間が分かればいいので Take13 Proverb を利用することにしました。
sshでQ13にログインして 現在時刻をUNIX時間で取得します。
$ date +'%s'
これを実行。 素早く自分のマシンでも同じコマンドを実行して 両者の時計のズレを計算したところ なんと 1183秒 もズレていました。 いやーなズレ具合ですね…。
何はともあれ時計のズレがわかったので take36 を修正して
srandom(time(NULL)+1183);
こうすると
無事通りました。
このズレはわざとなのでしょうか? しかし回線状況や手元のPCの時間のズレもあるので どちらにせよ時計の調整は必須かもしれません。
Write up はかなり書きにくそうです。