w3lc0m3 writeup

某CTFのバイナリ問題 "w3lc0m3"のwriteup
※このCTFチャレンジは個人的なツテで入手したもので、作問者からCTFのイベント名を公開しないことを条件にWrite Up記載の許可をもらっています。

サマリ
提供されるバイナリは"w3lc0m3"という32ビットのELFファイル。ユーザーの入力した値を暗号化してエンコードした後、ファイル内に暗号化された状態でハードコードされている正解flagと比較して、一致していれば"Key Accepted! Congrats!!!"と表示して終了。一致していなければ"Bad Key!"と表示して終了。

解析
※逆アセンブル内の変数名や関数名は一部解析に当たり、わかりやすいようにデフォルトのものから変更しています。

"w3lc0m3"はまず、ユーザーの入力した値のサイズが29バイトかどうか確認する。


.text:08048881 B8 60 8A 04 08          mov     eax, offset aEnterTheKey ; "\nEnter the key: "
.text:08048886 89 04 24                mov     [esp], eax
.text:08048889 E8 82 FB FF FF          call    _printf
.text:0804888E B8 71 8A 04 08          mov     eax, offset aS  ; "%s"
.text:08048893 8D 54 24 2F             lea     edx, [esp+2Fh]
.text:08048897 89 54 24 04             mov     [esp+4], edx
.text:0804889B 89 04 24                mov     [esp], eax
.text:0804889E E8 DD FB FF FF          call    ___isoc99_scanf
.text:080488A3 8D 44 24 2F             lea     eax, [esp+2Fh]
.text:080488A7 C7 44 24 1C FF FF FF FF mov     dword ptr [esp+1Ch], 0FFFFFFFFh
.text:080488AF 89 C2                   mov     edx, eax
.text:080488B1 B8 00 00 00 00          mov     eax, 0
.text:080488B6 8B 4C 24 1C             mov     ecx, [esp+1Ch]
.text:080488BA 89 D7                   mov     edi, edx
.text:080488BC F2 AE                   repne scasb
.text:080488BE 89 C8                   mov     eax, ecx
.text:080488C0 F7 D0                   not     eax
.text:080488C2 83 E8 01                sub     eax, 1
.text:080488C5 83 F8 1D                cmp     eax, 29
.text:080488C8 75 33                   jnz     short loc_80488FD


入力した値のサイズが29バイトだった場合、flag検証の関数(アドレス: 0x80486ED)へ処理を渡す。

flag検証の関数を眺めてみて、まず目についたのが以下のデータ。


.text:0804870D C6 45 CB 65             mov     [ebp+encKey], 'e'
.text:08048711 C6 45 CC 58             mov     [ebp+var_34], 'X'
.text:08048715 C6 45 CD 52             mov     [ebp+var_33], 'R'
.text:08048719 C6 45 CE 71             mov     [ebp+var_32], 'q'
.text:0804871D C6 45 CF 5A             mov     [ebp+var_31], 'Z'
.text:08048721 C6 45 D0 52             mov     [ebp+var_30], 'R'
.text:08048725 C6 45 D1 49             mov     [ebp+var_2F], 'I'
.text:08048729 C6 45 D2 46             mov     [ebp+var_2E], 'F'
.text:0804872D C6 45 D3 47             mov     [ebp+var_2D], 'G'
.text:08048731 C6 45 D4 78             mov     [ebp+var_2C], 'x'
.text:08048735 C6 45 D5 34             mov     [ebp+var_2B], '4'
.text:08048739 C6 45 D6 59             mov     [ebp+var_2A], 'Y'
.text:0804873D C6 45 D7 65             mov     [ebp+var_29], 'e'
.text:08048741 C6 45 D8 32             mov     [ebp+var_28], '2'
.text:08048745 C6 45 D9 67             mov     [ebp+var_27], 'g'
.text:08048749 C6 45 DA 44             mov     [ebp+var_26], 'D'
.text:0804874D C6 45 DB 5A             mov     [ebp+var_25], 'Z'
.text:08048751 C6 45 DC 57             mov     [ebp+var_24], 'W'
.text:08048755 C6 45 DD 4D             mov     [ebp+var_23], 'M'
.text:08048759 C6 45 DE 67             mov     [ebp+var_22], 'g'
.text:0804875D C6 45 DF 5A             mov     [ebp+var_21], 'Z'
.text:08048761 C6 45 E0 67             mov     [ebp+var_20], 'g'
.text:08048765 C6 45 E1 4E             mov     [ebp+var_1F], 'N'
.text:08048769 C6 45 E2 5A             mov     [ebp+var_1E], 'Z'
.text:0804876D C6 45 E3 44             mov     [ebp+var_1D], 'D'
.text:08048771 C6 45 E4 58             mov     [ebp+var_1C], 'X'
.text:08048775 C6 45 E5 35             mov     [ebp+var_1B], '5'
.text:08048779 C6 45 E6 32             mov     [ebp+var_1A], '2'
.text:0804877D C6 45 E7 63             mov     [ebp+var_19], 'c'
.text:08048781 C6 45 E8 77             mov     [ebp+var_18], 'w'
.text:08048785 C6 45 E9 77             mov     [ebp+var_17], 'w'
.text:08048789 C6 45 EA 58             mov     [ebp+var_16], 'X'
.text:0804878D C6 45 EB 64             mov     [ebp+var_15], 'd'
.text:08048791 C6 45 EC 47             mov     [ebp+var_14], 'G'
.text:08048795 C6 45 ED 70             mov     [ebp+var_13], 'p'
.text:08048799 C6 45 EE 7A             mov     [ebp+var_12], 'z'
.text:0804879D C6 45 EF 5A             mov     [ebp+var_11], 'Z'
.text:080487A1 C6 45 F0 6E             mov     [ebp+var_10], 'n'
.text:080487A5 C6 45 F1 63             mov     [ebp+var_F], 'c'
.text:080487A9 C6 45 F2 3D             mov     [ebp+var_E], '='


上記のデータは暗号化された正解flagでユーザーが入力した値との比較に使用される。

flag比較の前にまずユーザーの入力内容が暗号化される。


.text:080487C1 8B 45 BC                mov     eax, [ebp+counter]
.text:080487C4 83 C0 23                add     eax, 23h        ; 35
.text:080487C7 89 45 C0                mov     [ebp+xor_key], eax
.text:080487CA 8B 45 BC                mov     eax, [ebp+counter]
.text:080487CD 03 45 B4                add     eax, [ebp+user_input]
.text:080487D0 8B 55 BC                mov     edx, [ebp+counter]
.text:080487D3 03 55 B4                add     edx, [ebp+user_input]
.text:080487D6 0F B6 0A                movzx   ecx, byte ptr [edx]
.text:080487D9 8B 55 C0                mov     edx, [ebp+xor_key]
.text:080487DC 31 CA                   xor     edx, ecx
.text:080487DE 88 10                   mov     [eax], dl
.text:080487E0 83 45 BC 01             add     [ebp+counter], 1



上記はユーザーの入力内容を1バイトずつXORする処理。最初に使用されるXORキーは0x23で、キーの値は処理が進むごとに1ずつ加算される。(1文字目は0x23と、 2文字目は0x24と、3文字目は0x25とXORする..といった具合。)

ユーザー入力のXOR暗号化が完了すると、それをさらにカスタムBase64エンコードして先述した暗号化された正解flag "eXRqZRIFGx4Ye2gDZWMgZgNZDX52cwwXdGpzZnc=" と一致するか比較する。


.text:0804881D 89 04 24                mov     [esp], eax      ; encrypted user input
.text:08048820 E8 1F FD FF FF          call    key_customBase64_8048544 ;
.text:08048825 89 45 C4                mov     [ebp+var_3C], eax ; custom b64 encoded user input
.text:08048828 8D 45 CB                lea     eax, [ebp+encKey] ; eXRqZRIFGx4Ye2gDZWMgZgNZDX52cwwXdGpzZnc=
.text:0804882B 89 44 24 04             mov     [esp+4], eax
.text:0804882F 8B 45 C4                mov     eax, [ebp+var_3C]
.text:08048832 89 04 24                mov     [esp], eax
.text:08048835 E8 C6 FB FF FF          call    _strcmp


key_customBase64_8048544関数はBase64エンコードを行う関数だが、通常のBase64変換テーブルではなく、以下のカスタムの変換テーブルを使用する。

ABCDEFGHIJKLNMOPQRSTUVWXYZabcdefghijklnmopqrstuvwxyz0123456789+/
※ "N"と"M"、"n"と"m"の位置が通常の変換テーブルと逆

以上の解析から、暗号化された正解flag "eXRqZRIFGx4Ye2gDZWMgZgNZDX52cwwXdGpzZnc="をカスタムBase64デコードした後、XOR処理すれば平文の正解flagを取得できることが判明した。

カスタムBase64デコードには以下のスクリプトを使用。


#!/usr/bin/python
# This is a simple script to decode / encode custom base64
# Fill the "CUSTOM_ALPHABET" with custom base64 table

'''
# Standard table
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
'''

import string
import base64
import argparse

STANDARD_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

#ENCODE_TRANS = string.maketrans(STANDARD_ALPHABET, CUSTOM_ALPHABET)
#DECODE_TRANS = string.maketrans(CUSTOM_ALPHABET, STANDARD_ALPHABET)

parser = argparse.ArgumentParser(description="Decode or encode custom base64")
parser.add_argument("-s", "--string", action="store", help="String to encode / decode", dest="STRING")
parser.add_argument("-d", "--decode", action="store_true", help="Decode the string", dest="DECODE")
parser.add_argument("-e", "--encode", action="store_true", help="Encode the string", dest="ENCODE")
parser.add_argument("-t", "--table", action="store", help="Table for custom base64", dest="TABLE")

args = parser.parse_args()

if args.DECODE and args.TABLE and not args.ENCODE:
    CUSTOM_ALPHABET = args.TABLE
    DECODE_TRANS = string.maketrans(CUSTOM_ALPHABET, STANDARD_ALPHABET)
    print base64.b64decode(args.STRING.translate(DECODE_TRANS))
elif args.ENCODE and args.TABLE and not args.DECODE:
    CUSTOM_ALPHABET = args.TABLE
    ENCODE_TRANS = string.maketrans(STANDARD_ALPHABET, CUSTOM_ALPHABET)
    print base64.b64encode(args.STRING.translate(ENCODE_TRANS))
else:
    parser.print_help()


デコード結果はキレイに平文にはならないので、いったんhexエンコードする。
$ python custom_base64.py -t "ABCDEFGHIJKLNMOPQRSTUVWXYZabcdefghijklnmopqrstuvwxyz0123456789+/" -d -s "eXRqZRIFGx4Ye2gDZWMgZgNZDX52cwwXdGpzZnc=" | tr -d '\r\n' | xxd -p
79746a6512051b1e187b68036563606603197e76730c17746a736667

以下のスクリプトを用いてXORを行う。


#!/usr/bin/env python

import binascii

flag = "79746a6512051b1e187b68036563606603190d7e76730c17746a736667"


flag = bytearray(binascii.unhexlify(flag))
flag_length = len(flag)
XORed_bytearray = bytearray(flag_length)
key = "23"
key = bytearray(binascii.unhexlify(key))

for i in range(0, flag_length):
    XORed_bytearray[i] = flag[i] ^ key[0]
    key[0] += 1

print(XORed_bytearray)


$ python flag_decryptor.py
ZPOC5-243WE-JSQT0-8HAK5-OVNXX

flagはZPOC5-243WE-JSQT0-8HAK5-OVNXX

以上。

Leave a Reply

Your email address will not be published. Required fields are marked *