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文字以下か確認し, 数値に変換して幾つかの計算を行う.
計算結果が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"
になると思われる.
それぞれ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を書いて欲しいとのこと.
- this is an easy challenge
- 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!...."という文字列を出力する.
その直後にハードコードされている怪しげな文字列をコピーしている. 多分License Keyをエンコードしたもの.
sub_56593C2E
の左側のルーチンでハードコードされている文字列を処理し, 右側でscanf関数を実行してLicense Keyの入力を受け付けるっぽい.
面倒なので, 0x56593F0F
周辺まで処理を進めてスタック上からデコードされたLicense Keyを探したら見つかった. License KeyがFLAGになっているっぽい.
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}