SECCON 2018 国内決勝の復習
12月23日に開催されたSECCON 2018国内決勝にTeam Enuのメンバーとして参加させていただきました。力不足だったので復習も兼ねてメモを残しておきます。主に関わった宮島という問題です。
宮島
30分に1問ずつ出題される問題の要件を満たすアセンブラのバイナリ(16進数 2桁揃え表記)を答える問題。要件を満たすことでアタックポイントを取得できます。また、他チームより短いバイナリを投稿することで、チームのフラグを設置できるためディフェンスポイントが取得できます。要は短いコードを早くだせば沢山ポイントがもらえるというルールです。
以下、各問題毎の復習です。
最短バイトは私が確認した限りでのトップのチームのバイト数を記載しています。
その時の回答については公開されていないので、私なりの回答案を書いています。
1. Increment
int型の引数に対して1加算する関数を実装してください。
条件:
-15000000 <= a <= 15000000
普通に関数を書くと、15バイト
55 push rbp 48 89 e5 mov rbp,rsp 89 7d fc mov DWORD PTR [rbp-0x4],edi 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 83 c0 01 add eax,0x1 5d pop rbp c3 ret
この関数は最短4バイト
8d 47 01 lea eax,[rdi+1] c3 ret
2. Decrement
int型の引数を1減算して返す関数を実装してください。
条件:
-15000000 <= a <= 15000000
最短: 4バイト
8d 47 ff lea eax,[rdi-1] c3 ret
3. Odd
int型の引数aを受け取って奇数なら1を、偶数なら0を返す関数を実装してください。
条件:
-15000000 <= a <= 15000000
最短: 5バイト
97 xchg edi,eax 83 e0 01 and eax,1 c3 ret
4. Product
long型の引数 a, b を受け取って、 a と b を乗算した結果を返す関数を実装してください。(返り値はlong型とし、longで扱えない範囲の値をとるa, bは入力されないものとします。)
条件:
-100000 <= a <= 100000
-100000 <= b <= 100000
最短: 6バイト
48 97 xchg rax,rdi 48 f7 ee imul rsi c3 ret
5. Division
int型の変数a, bを受け取って a を b で割ったときの商を返す関数を実装してください。
条件:
-2000000 <= a <= 2000000
1 <= b <= 2000000
最短: 5バイト
97 xchg edi,eax 99 cdq f7 fe idiv esi c3 ret
6. Modulo
int型の引数a, bを受け取って、aをbで割ったときの余りを返す関数を実装してください。
条件:
-2000000 <= a <= 2000000
1 <= b <= 2000000
最短: 6バイト
97 xchg edi,eax 99 cdq f7 fe idiv esi 92 xchg edx,eax c3 ret
7. Sum
int型の引数 a, b, c, d, e を受け取って、a + b + c + d + eを返す関数を実装してください。
条件:
-15000000 <= a <= 15000000
-15000000 <= b <= 15000000
-15000000 <= c <= 15000000
-15000000 <= d <= 15000000
-15000000 <= e <= 15000000
最短: 11バイト
01 fe add esi,edi 01 f2 add edx,esi 01 d1 add ecx,edx 42 8d 04 01 lea eax,[rcx+r8] c3 ret
8. Swap
int型のポインタaとbを受取り、2つのポインタ先の値を入れ替える関数を実装してください。
条件:
-1000000 <= a,bのポインタが指し示す中身の数値 <= 1000000
最短: 7バイト
8b 07 mov eax,[rdi] 87 06 xchg eax,[rsi] 89 07 mov [rdi],eax c3 ret
9. Pow
int型の引数a, bを受け取って、aのb乗をlong型で返す関数を実装してください。
条件:
-100 <= a <= 100
0 <= b <= 30
最短: 18バイト(以下、16バイト)
b8 01 00 00 00 mov rax,1 85 f6 test esi,esi 74 06 jz short 8 87 ce xchg ecx,esi f7 ef imul edi e2 fc loop -2 c3 ret
10. Average
int型の配列arrとその要素数lenを受け取って、配列の平均値を切り捨てで返却する関数を実装してください。
条件:
-10000 <= arrの各要素 <= 10000
1 <= len <= 10
最短: 16バイト(以下、15バイト)
48 31 c0 xor rax,rax 87 ce xchg ecx,esi 03 44 8f fc add eax,[rdi+rcx*4-4] e2 fa loop -4 99 cdq f7 fe idiv esi c3 ret
11. Hello World
十分な余白領域を持ったメモリバッファbufが与えられるので、bufの先頭に"hello world"という文字列を書き込む関数を実装してください。文字列の末端にはヌル文字を含めるようにしてください。
最短: 21バイト
48 b8 68 65 6c 6c 6f 20 77 6f mov rax,0x6f77206f6c6c6568 c7 47 08 72 6c 64 00 mov dword ptr[rdi+8],0x646c72 48 89 07 mov [rdi],rax c3 ret
12. Xor
引数として文字列strと文字列長len、およびint型のkeyが与えられるので、文字列strの各文字をkeyでxorする関数を実装してください。
条件
str: 末尾がヌル文字の文字列
1 <= len <= 20
0 <= key <= 255
最短: 9バイト
87 ce xchg ecx,esi 30 54 0f ff xor [rdi+rcx-1], dl e2 fa loop -4 c3 ret
機械語の確認方法
gdb-pedaのassembleを使いました。
% gdb -q gdb-peda$ assemble -b64 Warning: not running or target is remote Instructions will be written to stdout Type instructions (NASM syntax), one or more per line separated by ";" End with a line saying just "end" iasm|0xdeadbeef> lea eax,[rdi+1] hexify: "\x8d\x47\x01" iasm|0xdeadbef2> ret hexify: "\xc3" iasm|0xdeadbef3> end Assembled instructions: "\x8d\x47\x01" # 0x00000000: lea eax,[rdi+0x1] "\xc3" # 0x00000003: ret hexify: "\x8d\x47\x01\xc3"
まとめ
上位チームの早さと短さに圧倒されていました。普段、シェルコードを書く時くらいしか機械語の長さを気にしないのですが、それをより考えるきっかけになりました。
また、オフラインで集まってCTFに参加するのは楽しかったです。運営の皆さん、Team Enuの皆さん、貴重な経験をさせていただき、ありがとうございました。
誤りがあったり、より短く書けると言ったご意見があればご指摘ください。
SECCON 2018 決勝大会(国内)に「Team Enu」として参加しました。防御ポイントの獲得で差がつき、今年も6位という結果となりました(2年連続3回目)。来年こそより上位を狙いたいと思います! #SECCON pic.twitter.com/A5hP87Ioj9
— NTTセキュリティ・ジャパン株式会社 (@NTTSec_JP) December 25, 2018