Shi0shishi0

汐鹿生

ENCRYPT CTF - Writeup

はじめに

4月2日~4月4日に開催されたENCRYPT CTFにチームで参加して3536点を獲得しました. 自分は825点分をSubmitしました.

Forensics

Journey to the centre of the file 1(75pt, 248 Solves)

gzファイル ziptunnel1.gz が渡される.

与えられたgzファイルを解凍するとzipファイルが得られて, 更にこれを解凍すると再びgzファイルが得られる.

gz -> zip -> gz -> zip ... という構成になっている. よくあるマトリョーショカっぽい問題.

適当にシェルスクリプトを書いて解凍する.

#!/bin/sh

while true
do
    ft=`file flag.zip`
    echo "$ft"

    if echo "$ft" | grep "Zip"
    then
        unzip flag.zip
        mv -f flag.gz flag.zip
        continue
    fi

    if echo "$ft" | grep "gzip"
    then
        mv -f flag.zip flag.gz
        gzip -d -f flag.gz
        mv -f flag flag.zip
        continue
    fi

    if echo "$ft" | grep "ASCII"
    then
        mv -f flag.zip flag.txt
        cat flag.txt
        exit
    fi

    break
done

encryptCTF{w422up_b14tch3s}

Reversing

crackme03(250pt, 134 Solves)

x86-64のELFファイルが与えられる. このELFファイルが問題文に記載されているIPアドレス 104.154.106.182:7777 でも動作しているっぽい.

実行すると入力を求められるCrackme系っぽい雰囲気. "Enter input #0:"とあることから, 複数回の入力が必要になりそう.

hamasho@hamasho-virtual-machine:~$  ./crackme03 
Hi!, i am a BOMB!
I will go boom if you don't give me right inputs
Enter input #0: AAAAAAAAA
BOOM!
Bye!

アドレス0x00001286から0x00001410までに入力値をチェックする関数が5つ存在する.

1個目は入力値とハードコードされた文字列 CRACKME02 を比較している.

2個目は入力値とハードコードされた値 DEADBEEF(16進数値) を比較している. これは標準出力から普通に英数字を入れてもチェックを突破できないと思うので, Pythonスクリプトを書くことにする.

3個目は入力値とハードコードされた文字列 ZXytUb9fl78evgJy3KJN を比較している. 4文字ずつに分割されてハードコードされた文字列を結合して使用している.

4個目は以下の画像のような処理になっている. 入力値が3文字以下か確認し, 数値に変換して幾つかの計算を行う.

f:id:echoha610:20190406234525p:plain

計算結果が0になる値を求めれば良さそう. z3で雑なスクリプトを書いて計算した(1がこれを満たす)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from z3 import *

x = Int('x')

solver = Solver()
solver.add(((x*x*x) + (((x*x)*2-x)*2)-3) == 0)
solver.add(x<100)

if solver.check() == sat:
    m = solver.model()
    print m

5個目は以下の画像のような処理になっている. 9文字の入力値 が求められており, ハードコードされた文字列から, 5文字目は"i" になると思われる.

f:id:echoha610:20190406234431p:plain

それぞれ2値の和がハードコードされた値と一致するようだが, これだけでは制約が弱くてz3で入力値を求めるのは難しい気がする.

しばらく悩んでいたところ, 4個目の答えを入力した後で表示される文字列 SUBSCRIBE TO PEWDIEPIE の存在に気が付いた. 何となくGoogle先生に聞いてみるとPEWDIEPIEが有名なYouTuberの名前?であることが分かる.

https://ja.wikipedia.org/wiki/%E3%83%94%E3%83%A5%E3%83%BC%E3%83%87%E3%82%A3%E3%83%91%E3%82%A4

この pewdiepie という単語は5文字目が"i"かつ9文字なのでこれが5個目のKeyなのかな?と思って入力してみたら正解だった.

自分の解き方が悪いor理解が間違っているだけな気もするが, イマイチ釈然としないので想定解が気になる.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from pwn import *

context(os='linux', arch='i386')
context.log_level = 'debug'

HOST = "104.154.106.182"
PORT = 7777

io = remote(HOST,PORT)
io.recvuntil('#0')
payload = "CRACKME02"
io.sendline(payload)

io.recvuntil('#1')
payload = pack(0xDEADBEEF)
io.sendline(payload)

io.recvuntil('#2')
payload = "ZXytUb9fl78evgJy3KJN"
io.sendline(payload)

io.recvuntil('#3')
payload = "1"
io.sendline(payload)

io.recvuntil('#4')
payload = "pewdiepie"
io.sendline(payload)

io.recvuntil('encryptCTF')
root@kali:~# python solver_ENCRYPTCTF_crackme03.py 
[+] Opening connection to 104.154.106.182 on port 7777: Done
[DEBUG] Received 0x53 bytes:
    'Hi!, i am a BOMB!\n'
    "I will go boom if you don't give me right inputs\n"
    'Enter input #0: '
[DEBUG] Sent 0xa bytes:
    'CRACKME02\n'
[DEBUG] Received 0x10 bytes:
    'Enter input #1: '
[DEBUG] Sent 0x5 bytes:
    00000000  ef be ad de  0a                                     │····│·│
    00000005
[DEBUG] Received 0x10 bytes:
    'Enter input #2: '
[DEBUG] Sent 0x15 bytes:
    'ZXytUb9fl78evgJy3KJN\n'
[DEBUG] Received 0x10 bytes:
    'Enter input #3: '
[DEBUG] Sent 0x2 bytes:
    '1\n'
[DEBUG] Received 0x27 bytes:
    'SUBSCRIBE TO PEWDIEPIE\n'
    'Enter input #4: '
[DEBUG] Sent 0xa bytes:
    'pewdiepie\n'
[DEBUG] Received 0x21 bytes:
    'Validating Input 4\n'
    'you earned it\n'
[DEBUG] Received 0x1a bytes:
    'encryptCTF{B0mB_D!ffu53d}\n'
[*] Closed connection to 104.154.106.182 port 7777

encryptCTF{B0mB_D!ffu53d}

dontlook at this Challenge(500pt, 96 Solves)

x86 ELFファイルが与えられる. また, 問題文には以下のような文章が記載されていた. Writeupを書いて欲しいとのこと.

  1. this is an easy challenge
  2. pls post a writeup if you solve it.

実行するとLicense Keyの入力を求められる, Crackme系っぽい.

hamasho@hamasho-virtual-machine:~$ ./a.out 
Welcome To The Uncrackable Software!!!
make a keygen and get rewarded!....
Enter The Correct License Key: AAAAAAAAAAAAAAAAAAAAAAAAAA

ltrace/straceから実行すると"so you wanna trace me?"と表示されて入力を求められずに終了する. 耐解析機能としてptraceを使用していると思われる.

hamasho@hamasho-virtual-machine:~$ ltrace -i ./a.out 
[0x5659d891] __libc_start_main(0x5659d99d, 1, 0xfffc6c94, 0x5659e160 <unfinished ...>
[0x5659d9da] ptrace(0, 0, 1, 0)                                                                                               = 0xffffffff
[0x5659d9f1] puts("so you wanna trace me?..."so you wanna trace me?...
)                                                                                = 26
[0xffffffffffffffff] +++ exited (status 255) +++

hamasho@hamasho-virtual-machine:~$ strace -i ./a.out 
[00007f2f93f05777] execve("./a.out", ["./a.out"], [/* 60 vars */]) = 0
strace: [ Process PID=13810 runs in 32 bit mode. ]
[f77047a7] brk(NULL)                    = 0x56cea000
[f7705a91] access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
[f7705b40] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf76e8000
[f7705a91] access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
[f77059d4] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[f770595b] fstat64(3, {st_mode=S_IFREG|0644, st_size=106874, ...}) = 0
[f7705b40] mmap2(NULL, 106874, PROT_READ, MAP_PRIVATE, 3, 0) = 0xf76cd000
[f7705aed] close(3)                     = 0
[f7705a91] access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
[f77059d4] open("/lib32/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
[f7705a04] read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\207\1\0004\0\0\0"..., 512) = 512
[f770595b] fstat64(3, {st_mode=S_IFREG|0755, st_size=1775464, ...}) = 0
[f7705b40] mmap2(NULL, 1784348, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xf7519000
[f7705bb4] mprotect(0xf76c6000, 4096, PROT_NONE) = 0
[f7705b40] mmap2(0xf76c7000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ad000) = 0xf76c7000
[f7705b40] mmap2(0xf76ca000, 10780, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xf76ca000
[f7705aed] close(3)                     = 0
[f7705b40] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7518000
[f76ed98d] set_thread_area({entry_number:-1, base_addr:0xf7518700, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 (entry_number:12)
[f7705bb4] mprotect(0xf76c7000, 8192, PROT_READ) = 0
[f7705bb4] mprotect(0x5660f000, 4096, PROT_READ) = 0
[f7705bb4] mprotect(0xf7710000, 4096, PROT_READ) = 0
[f7705b91] munmap(0xf76cd000, 106874)   = 0
[f76ecbe9] ptrace(PTRACE_TRACEME, 0, 0x1, NULL) = -1 EPERM (Operation not permitted)
[f76ecbe9] fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
[f76ecbe9] brk(NULL)                    = 0x56cea000
[f76ecbe9] brk(0x56d0b000)              = 0x56d0b000
[f76ecbe9] write(1, "so you wanna trace me?...\n", 26so you wanna trace me?...
) = 26
[f76ecbe9] exit_group(4294967295)       = ?
[????????] +++ exited with 255 +++

ELFファイルをIDA Linux Debuggerで調査する.

ptrace, signalによる耐解析機能のコードを実行後, "Welcome To The Uncrackable Software!!!" "make a keygen and get rewarded!...."という文字列を出力する.

f:id:echoha610:20190406235145p:plain

f:id:echoha610:20190406235149p:plain

その直後にハードコードされている怪しげな文字列をコピーしている. 多分License Keyをエンコードしたもの.

f:id:echoha610:20190406235203p:plain

sub_56593C2E の左側のルーチンでハードコードされている文字列を処理し, 右側でscanf関数を実行してLicense Keyの入力を受け付けるっぽい.

f:id:echoha610:20190406235223p:plain

面倒なので, 0x56593F0F 周辺まで処理を進めてスタック上からデコードされたLicense Keyを探したら見つかった. License KeyがFLAGになっているっぽい.

f:id:echoha610:20190406235235p:plain

hamasho@hamasho-virtual-machine:~$ ./a.out 
Welcome To The Uncrackable Software!!!
make a keygen and get rewarded!....
Enter The Correct License Key: encryptCTF{dontl00k__1ts_my_secret}
[*] Access Granted!

encryptCTF{dontl00k__1ts_my_secret}