CTF
MNCTF

MNCTF2016 Writeup

More than 1 year has passed since last update.

Macnica Networks DAYで行われたCTF、MNCTF2016に参加してきたので、Write-upとして簡単なメモを書いておく。

問題

点呼 (MISC)

(省略)

超標的型 (BINARY)

当日は、バイナリ問題への苦手意識と、Macで作業していたのでWindowsバイナリ面倒と思って後回しにしていたら、時間がなくなってしまって時間切れになってしまったのだけれど、実際は簡単な問題で、これを先に解いていればと後悔。

実行ファイルをstringsしてみると、以下のような文字列が見つかり、またGetComputerNameAが使われているのが分かる。

Computer Name: %s
This host is not the target.
Operation Aborted.
Targeted host found. Continue operation.

Hopperでこの文字列が使われている関数を見つけてデコンパイルしてみるとこんな感じに。

int sub_401080(int arg0, int arg1) {
    eax = (*_mbsicmp)(arg0, arg1);
    return eax;
}

int sub_4010a0() {
    (*GetComputerNameA)(var_24, 0x10);
    sub_401040("Computer Name: %s\n", var_24);
    esp = ((esp - 0x4 - 0x4 - 0x4 - 0x4) + 0x8 - 0x4 - 0x4) + 0x8;
    if (sub_401080(0x54, var_24) != 0x0) {
            sub_401040("This host is not the target.\nOperation Aborted.\n", stack[2038]);
    }
    else {
            sub_401040("Targeted host found. Continue operation.\n", stack[2038]);
            sub_401040(0x40217c, stack[2038]);
    }
    return 0x0;
}

どうもうまくデコンパイルできていないようなのでアセンブリを見てみると……

sub_4010a0:
004010a0         push       ebp                                                 ; XREF=sub_4011f8+268
004010a1         mov        ebp, esp
004010a3         sub        esp, 0x24
004010a6         mov        dword [ss:ebp+var_4], 0x10
004010ad         lea        eax, dword [ss:ebp+var_4]
004010b0         push       eax
004010b1         lea        ecx, dword [ss:ebp+var_24]
004010b4         push       ecx
004010b5         call       dword [ds:imp_GetComputerNameA]                     ; imp_GetComputerNameA
004010bb         lea        edx, dword [ss:ebp+var_24]
004010be         push       edx                                                 ; argument #2 for method sub_401040
004010bf         push       0x402108                                            ; "Computer Name: %s\\n", argument #1 for method sub_401040
004010c4         call       sub_401040
004010c9         add        esp, 0x8
004010cc         mov        byte [ss:ebp+var_14], 0x54
004010d0         mov        byte [ss:ebp+var_13], 0x45
004010d4         mov        byte [ss:ebp+var_12], 0x53
004010d8         mov        byte [ss:ebp+var_11], 0x48
004010dc         mov        byte [ss:ebp+var_10], 0x49
004010e0         mov        byte [ss:ebp+var_F], 0x47
004010e4         mov        byte [ss:ebp+var_E], 0x41
004010e8         mov        byte [ss:ebp+var_D], 0x57
004010ec         mov        byte [ss:ebp+var_C], 0x41
004010f0         mov        byte [ss:ebp+var_B], 0x52
004010f4         mov        byte [ss:ebp+var_A], 0x41
004010f8         mov        byte [ss:ebp+var_9], 0x2d
004010fc         mov        byte [ss:ebp+var_8], 0x50
00401100         mov        byte [ss:ebp+var_7], 0x43
00401104         mov        byte [ss:ebp+var_6], 0x0
00401108         lea        eax, dword [ss:ebp+var_24]
0040110b         push       eax                                                 ; argument #2 for method sub_401080
0040110c         lea        ecx, dword [ss:ebp+var_14]
0040110f         push       ecx                                                 ; argument #1 for method sub_401080
00401110         call       sub_401080
00401115         add        esp, 0x8
00401118         test       eax, eax
0040111a         je         0x40112b

sub_401080 の引数の文字列が以下で構築されているのが、アセンブラ超初心者の自分でも分かる。

004010cc         mov        byte [ss:ebp+var_14], 0x54
004010d0         mov        byte [ss:ebp+var_13], 0x45
004010d4         mov        byte [ss:ebp+var_12], 0x53
004010d8         mov        byte [ss:ebp+var_11], 0x48
004010dc         mov        byte [ss:ebp+var_10], 0x49
004010e0         mov        byte [ss:ebp+var_F], 0x47
004010e4         mov        byte [ss:ebp+var_E], 0x41
004010e8         mov        byte [ss:ebp+var_D], 0x57
004010ec         mov        byte [ss:ebp+var_C], 0x41
004010f0         mov        byte [ss:ebp+var_B], 0x52
004010f4         mov        byte [ss:ebp+var_A], 0x41
004010f8         mov        byte [ss:ebp+var_9], 0x2d
004010fc         mov        byte [ss:ebp+var_8], 0x50
00401100         mov        byte [ss:ebp+var_7], 0x43
00401104         mov        byte [ss:ebp+var_6], 0x0
00401108         lea        eax, dword [ss:ebp+var_24]
0040110b         push       eax                                                 ; argument #2 for method sub_401080

とりあえず、ghciで文字列に戻すと「TESHIGAWARA-PC」が得られた。

> map toEnum [0x54,0x45,0x53,0x48,0x49,0x47,0x41,0x57,0x41,0x52,0x41,0x2d,0x50,0x43,0x0] :: String
"TESHIGAWARA-PC\NUL"

同一集団 (MISC)

「住所などは本物である可能性が低い一方で、メールアドレスはドメイン取得時に確認メールを受け取る必要があるため、攻撃者が保持しているメールアドレスである可能性が高いです」というヒントが与えられていたので、shinobot.comのwhois情報にあったメールアドレスでググると、メールアドレスからドメインを検索できるようなサービスがあるのを発見。以下の4つのドメインはすぐに見つかったのだけれど、本番中には最後の一つが分からず。

  • noitalumis.info
  • mnd2015.info
  • shinolocker.com
  • mnctf.info

何か別の探し方をしないといけないのかと思ったが、単にそのサイトが更新されていなかったか何かで、結局特別な探し方は不要だった模様。最後のひとつはshinosec.comだった。

難読記録 (MISC)

ログ中のコンピュータ名にマッチする正規表現を書くだけ。適当にAD\\[A-F0-9]{6}とか書いたら余計なものもマッチしてしまったので、たとえばAD\\[A-F0-9]{6}\bなどとすれば良い。

超持株会 (WEB)

改竄したリクエストを送信すれば良い。FiddlerとかBurp Suiteとかを使えるとカッコよいのだけれど、使ったことないので、とりあえず開発者ツールからフォームを変更してサブミット。

document.getElementsByName("id")[0].value = "A20050023"
var option = document.createElement("option");
option.text = "1000口(1000,000株)";
option.value = "1000";
document.getElementsByName("stock")[0].add(option)
document.getElementsByName("stock")[0].value = "1000"

一行挿入 (WEB)

元のコードを適当に整形するとこんな感じ。

eval(
    function(p,a,c,k,e,r){
        e=function(c){
            return c.toString(a)
        };
        if(!''.replace(/^/,String)){
            while(c--)
                r[e(c)]=k[c]||e(c);
            k=[function(e){return r[e]}];
            e=function(){return'\\w+'};
            c=1
        };
        while(c--)
            if(k[c])
                p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);
        return p
    } ('e(6.5=="c 1.0.0.1"){3 4=[];3 2=6["5"];2=2.o("");b(3 i=0;i<2.d;i++){4.8(f.g(2[i].h(0)+i));j.k=(/[a-l-m-9]*/.n(4.7(""))).7("")+\'.p\'}}',26,26,'||ua|var|page|userAgent|navigator|join|push|||for|ShinoBrowser|length|if|String|fromCharCode|charCodeAt||window|location|zA|Z0|exec|split|html'.split('|'),0,{})
)

文字列を生成してevalしているので、evalalertに書き換えて実行すると、以下のようなソースが得られる(このソースは整形済み)。

if(navigator.userAgent=="ShinoBrowser 1.0.0.1"){
  var page=[];
  var ua=navigator["userAgent"];
  ua=ua.split("");
  for(var i=0;i<ua.length;i++){
    page.push(String.fromCharCode(ua[i].charCodeAt(0)+i));
    window.location=(/[a-zA-Z0-9]*/.exec(page.join(""))).join("")+'.html'
  }
}

そこで、少し書き換えて、以下を実行すると、フラグが得られた。

var page=[];
var ua="ShinoBrowser 1.0.0.1";
ua=ua.split("");
var loc;
for(var i=0;i<ua.length;i++){
  page.push(String.fromCharCode(ua[i].charCodeAt(0)+i));
  loc=(/[a-zA-Z0-9]*/.exec(page.join(""))).join("")+'.html'
}
loc //=> "SikqsGxv.html"

暗号新聞 (CRYPT)

クロスワードパズルを解くだけで、ほとんどの項目は簡単なのだけれど、「BitCoinにも使われているRSAを今後置き換える可能性のある暗号アルゴリズム」(3文字)が分からず、だいぶ時間を使ってしまった。

ただ、分からないのは二文字だけだったので、頑張ってググったりしてるより、総当たりしてしまった方が早かったかも知れない。

var cs = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for (var i = 0; i < cs.length; i++) {
  for (var j = 0; j < cs.length; j++) {
    var answer = "PNGRTHTTPSLLAESMD5" + cs[i] + "E" + cs[j] + "SHA"
    if(md5(answer).substr(1,4)=='5545'){
        //alert(answer)
        alert(md5(answer))
    }
  }
}

権限昇格 (BINARY)

本番では解き方のアイディアが思いつかず、「超標的型」と同じく後回しにしていたら、時間がなくなってしまった問題。

後から解いた際には、実行ファイルにPowerShellExecutorなどの文字列が見つかったため、PowerShell関係の脆弱性と踏んで、「PowerShell 権限昇格 CVE」でググったら、「CVE-2016-0099」を発見。問題文に「「CVE_-__」の形式でこたえてください」とあったので、最初のハイフンは含まない「CVE2016-0099」で投稿したら不正解で、「CVE-2016-0099」としたら正解となった。

ただ、現実のインシデント対応だと、こんな方法で特定できるとは限らないし、どうするのが想定解法だったんだろうなぁ、と思っていたら、MNCTF2016 - CTFのWrite-up用ブログに、「バイナリをVirusTotalにアップロードすればフラグが出ます」とあり、情シス向けということも考えると、なるほどと思った。

丸文字文 (CRYPT)

本番では、各文字の頻度を調べて頻度順に英語の文字の出現頻度とマッチングしたり、ユニコードのコードポイントに対してオフセットをとってみたりとか、色々やったけれど、結局それっぽい結果は得られず。

後から解いた際には、丸文字を普通の文字に戻すとbase64になっているということに気付いて復号できた。最初に各文字の出現頻度を調べた際に、文字種が64文字という時点で気づくべきだった……。あとは、復号結果に書いてある簡単なクイズに答えるだけ。

ただ、得られた答えをサブミットしても正解にならず、報告して対応してもらった。それまでに誰も指摘していなかったようなので、一番乗りだったかも知れない。

結果

当日の結果としては「点呼」、「同一集団」(4/5)、「難読記録」、「超持株会」、「一行挿入」、「暗号新聞」を解いて、計205点で12位だった。もうちょいいけるかと思ったが残念……

現在のスコアボードでは、丸文字文の1点分に一番乗りだったので、それを含む満点を最初にとったことになり、1位ということになっている。

参考

他の方のWrite-up等