[ksnctf] Are you ESPer?

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 はかなり書きにくそうです。