Edited at

MNCTF2016 Writeup

More than 3 years have 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等