--- tags: ctf --- # redpwnCTF 2020 - chezzzboard 問題のリポジトリ: <https://github.com/redpwn/redpwnctf-2020-challenges> これまでに解いた問題: https://hackmd.io/@Xornet/BkemeSAhU ## Writeup ### Outline C++製のチェスを模したバイナリが渡される。普通のチェスが出来るが相手のキングを取ってもゲームは続行する。 駒を移動させた直後に盤面からスコアを算出し、それが0x1d3( = 467)であればフラグを読んで表示する。このスコアはマスごとに駒の種類と場所によって決まる値の総和になる。 というわけでスコアがちょうどそうなるような盤面を計算し、そうなるように駒を配置する。2つのルークだけでこの値を構成出来るのでそうした。 ### Binary 特にAIがプレイしているとかそういうのは無く、黒も白も自分で動きを指定することが出来る。いずれのプレイヤーのターンでも駒を動かした直後に次のようなスコアの計算をする関数が呼ばれる ```c= ulong get_score(long board,undefined8 param_2,undefined8 param_3,undefined8 param_4, undefined8 param_5,undefined8 param_6) { undefined4 piece; int iVar1; uint score; int j; int i; score = 0; j = 0; while (j < 8) { i = 0; while (i < 8) { piece = get_piece(board + ((long)i + (long)j * 8) * 0x10); get_player(board + ((long)i + (long)j * 8) * 0x10); switch(piece) { case 0: iVar1 = add(board,(ulong)(8 - j),(ulong)(i + 9)); score = score + iVar1; break; case 1: iVar1 = mul(board,(ulong)(8 - j),(ulong)(i + 9)); score = score + iVar1; break; case 2: iVar1 = twice(board,(ulong)(8 - j),(ulong)(i + 9)); score = score + iVar1; break; case 3: iVar1 = mod(board,(ulong)(8 - j),(ulong)(i + 9)); score = score + iVar1; break; case 4: iVar1 = 0x100_diff(board,(ulong)(8 - j),(ulong)(i + 9)); score = score + iVar1; break; case 5: iVar1 = diff(board,(ulong)(8U - j),(ulong)(i + 9),(ulong)(8U - j)); score = score + iVar1; } i = i + 1; } j = j + 1; } return (ulong)score; } ``` case文で駒によって算出する関数を変えている、ちなみに`j`は縦のインデックスで`i`は横のインデックスになる。したがってスコア算出時に渡される引数は次のようになる ``` |9 10 11 12 13 14 15 16 <- 9+i ------------------------ 8| 7| 6| 5| 4| 3| 2| 1| ^ 8-j ``` 駒の種類と計算式は次の通り 0. キング: `(8-i)+(9+j)` 1. クイーン: `(8-i)*(9+j)` 2. ビショップ: `2*((8-i)+(9+j))` 3. ナイト: `(9+j)%(8-i)` 4. ルーク: `0x100-(8-i)-(9+j)` 5. ポーン: `(8-i)-(9+j)` これを計算すると次のようになる ``` $ python make_board.py | 17|| 18|| 19|| 20|| 21|| 22|| 23|| 24| | 16|| 17|| 18|| 19|| 20|| 21|| 22|| 23| | 15|| 16|| 17|| 18|| 19|| 20|| 21|| 22| | 14|| 15|| 16|| 17|| 18|| 19|| 20|| 21| | 13|| 14|| 15|| 16|| 17|| 18|| 19|| 20| | 12|| 13|| 14|| 15|| 16|| 17|| 18|| 19| | 11|| 12|| 13|| 14|| 15|| 16|| 17|| 18| | 10|| 11|| 12|| 13|| 14|| 15|| 16|| 17| ---------------------------------------- | 72|| 80|| 88|| 96||104||112||120||128| | 63|| 70|| 77|| 84|| 91|| 98||105||112| | 54|| 60|| 66|| 72|| 78|| 84|| 90|| 96| | 45|| 50|| 55|| 60|| 65|| 70|| 75|| 80| | 36|| 40|| 44|| 48|| 52|| 56|| 60|| 64| | 27|| 30|| 33|| 36|| 39|| 42|| 45|| 48| | 18|| 20|| 22|| 24|| 26|| 28|| 30|| 32| | 9|| 10|| 11|| 12|| 13|| 14|| 15|| 16| ---------------------------------------- | 34|| 36|| 38|| 40|| 42|| 44|| 46|| 48| | 32|| 34|| 36|| 38|| 40|| 42|| 44|| 46| | 30|| 32|| 34|| 36|| 38|| 40|| 42|| 44| | 28|| 30|| 32|| 34|| 36|| 38|| 40|| 42| | 26|| 28|| 30|| 32|| 34|| 36|| 38|| 40| | 24|| 26|| 28|| 30|| 32|| 34|| 36|| 38| | 22|| 24|| 26|| 28|| 30|| 32|| 34|| 36| | 20|| 22|| 24|| 26|| 28|| 30|| 32|| 34| ---------------------------------------- | 1|| 2|| 3|| 4|| 5|| 6|| 7|| 0| | 2|| 3|| 4|| 5|| 6|| 0|| 1|| 2| | 3|| 4|| 5|| 0|| 1|| 2|| 3|| 4| | 4|| 0|| 1|| 2|| 3|| 4|| 0|| 1| | 1|| 2|| 3|| 0|| 1|| 2|| 3|| 0| | 0|| 1|| 2|| 0|| 1|| 2|| 0|| 1| | 1|| 0|| 1|| 0|| 1|| 0|| 1|| 0| | 0|| 0|| 0|| 0|| 0|| 0|| 0|| 0| ---------------------------------------- |239||238||237||236||235||234||233||232| |240||239||238||237||236||235||234||233| |241||240||239||238||237||236||235||234| |242||241||240||239||238||237||236||235| |243||242||241||240||239||238||237||236| |244||243||242||241||240||239||238||237| |245||244||243||242||241||240||239||238| |246||245||244||243||242||241||240||239| ---------------------------------------- | -1|| -2|| -3|| -4|| -5|| -6|| -7|| -8| | -2|| -3|| -4|| -5|| -6|| -7|| -8|| -9| | -3|| -4|| -5|| -6|| -7|| -8|| -9||-10| | -4|| -5|| -6|| -7|| -8|| -9||-10||-11| | -5|| -6|| -7|| -8|| -9||-10||-11||-12| | -6|| -7|| -8|| -9||-10||-11||-12||-13| | -7|| -8|| -9||-10||-11||-12||-13||-14| | -8|| -9||-10||-11||-12||-13||-14||-15| ---------------------------------------- ``` ここでルークの結果(上から5番目)を見てみると`235, 232`があるのでここにルークを配置するだけの盤面にすれば盤面のスコアは467となり、フラグが表示される。 ## Code ### 本体 ```python= from pwn import process def move(s, _from, _to): s.recvuntil("Move from\n") s.sendline(_from) s.recvuntil("To\n") s.sendline(_to) if __name__ == '__main__': """ コマ - 0: キング - 1: クイーン - 2: ビショップ - 3: ナイト - 4: ルーク - 5: ポーン - スコアを0x1d3(= 467)にすればよい """ s = process("./chezzz") move(s, "2E", "3E") move(s, "7E", "6E") move(s, "1D", "5H") move(s, "8D", "4H") move(s, "5H", "7H") move(s, "4H", "2H") white = "7" black = "2" row = "ABCDEFGH" for i, c in enumerate(reversed(row)): if i != 7: move(s, white + c, white + row[-i-2]) move(s, black + c, black + row[-i - 2]) move(s, "7A", "8B") move(s, "2A", "1A") move(s, "8B", "8C") move(s, "1A", "1B") move(s, "8C", "8D") move(s, "1B", "1C") move(s, "8D", "8E") move(s, "1C", "1D") move(s, "8E", "8F") move(s, "1D", "1E") move(s, "8F", "8G") move(s, "1E", "1F") move(s, "8G", "6E") move(s, "1F", "1G") move(s, "6E", "4G") move(s, "1G", "1H") move(s, "3E", "4E") move(s, "1H", "4E") move(s, "4G", "4E") move(s, "8A", "8B") move(s, "4E", "5E") move(s, "8B", "8C") move(s, "5E", "6E") move(s, "8C", "8D") move(s, "6E", "8E") move(s, "8D", "8E") print(s.recvline().decode()) ``` ### スコア計算 ```python= def display_board(f): for i in range(8, 0, -1): row = "" for j in range(9, 17): row += f"|{f(i, j):3}|" print(row) print("-----" * 8) if __name__ == '__main__': display_board(lambda x, y: x+y) display_board(lambda x, y: x*y) display_board(lambda x, y: 2*(x+y)) display_board(lambda x, y: y%x) display_board(lambda x, y: 0x100 - x - y) display_board(lambda x, y: x - y) ``` ## Flag `flag{y3s_i_kn0w_p4wns_c4n_m0v3_tw1c3_0n_th31r_f1rst_pl4y}` ## 感想 デコンパイル結果をまともに読む気が無かったので駒とインデックスの方向は動的解析とデバッガで返り値覗いて特定しました。動的解析力(gdb力)があまり無いのでこれを上手く養成していきたいです。