ゼオスTTのブログ

気が向いた時に、主にプログラミング関係の記事を書くつもり。しかし気が向かない。

DefCamp CTF Qualification 2018 write-up

Harekazeで参加。決勝圏内の15位。
結構解けて満足している。

Junior

EagleEye

例) stegsolve

flag: DCTF{912c07726142de12943b76a89d40847028330f0a1a0be1ac24503c57242404ab}

RobotsVSHumans

robots.txt に以下の記述。

Did you know that robots.txt is not the only .txt file in a website? BTW: I am against humans!

それなら、と humans.txt にアクセスしてみたら、あった。

flag: DCTF{1091d2144edbffaf5dd265cb7c93e799c4659eb16ee79735b3bd6e09dd6e791f}

Passport

渡された demo.bin をアップロードすると、

This is the demo Passport! You have to find its evil twin!

適当なファイルをアップロードすると、

Your Passport is invalid. Our MD5SUM detected a false document!

MD5を衝突させればよさそう。

$ md5sum demo.bin 
cee9a457e790cf20d4bdaa6d69f01e41  demo.bin

cee9a457e790cf20d4bdaa6d69f01e41ググるとこの通り。

stackoverflow.com

flag: DCTF{04c8d0052e3ffd8d21934e392c272a0494f23433901941c93fab82b50be27c1a}

終盤でDcuaに同点まで追いつかれて急遽解いたのだが、
DcuaはJuniorでない問題を解き、我々を突き放していった。

Web

Get Admin

問題概要

ソースコード付き。

$_SESSION['userid'] === "1" ならフラグが得られる。

ログインすると、次のようにしてユーザ情報がcookieに入る。

<?php   // これがないとシンタックスハイライトが効かなかったので......

$user = ['id' => $userid, 'username' => $username, 'email' => $email];
$user['checksum'] = crc32(compress($user)); // compress() は後述

$plaintext = compress($user);
$ciphertext = openssl_encrypt($plaintext, 'AES-128-CBC', AES_KEY /* hidden */, OPENSSL_RAW_DATA, AES_IV /* hidden */);
$cookie = base64_encode($ciphertext) . sprintf('%06d', strlen($plaintext));

setcookie('user', $cookie, time() + 60 * 60 * 24 * 30);

compress() は、配列を受け取り、最初の要素から順にkeyとvalueの組を連結した文字列を返す。
この時、keyとvalueの区切りは ¡ 、keyとvalueの組の区切りは ÷ である。

例えば、 ['id' => 123, 'username' => 'piyo', 'email' => 'piyo@piyo.com'] の場合、 $plaintext
id¡123÷username¡piyo÷email¡piyo@piyo.com÷checksum¡3405045270 となる。

また、セッションがない状態でcookieがある場合は、cookieから次のようにユーザ情報が復元される。

<?php

$ciphertext = base64_decode(substr($_COOKIE['user'], 0, -6));
$plaintext_padded = openssl_decrypt($ciphertext, 'AES-128-CBC', AES_KEY, OPENSSL_RAW_DATA, AES_IV);
$plaintext = substr($plaintext_padded, 0, intval(substr($_COOKIE['user'], -6, 6)));

$user = decompress($plaintext); // decompress() は後述
$_SESSION['userid'] = $user['id'];

decompress() は、文字列を受け取り、まず各keyとvalueの組に関して先頭から順に代入処理を行い、配列Aを生成する。
また、Aからchecksumを unset() した配列Bを用意する。
その後、Aに含まれるchecksumと、B を compress() したものの crc32() が一致するかを確認し、一致した場合Bを返す。

目標

decompress() は各keyとvalueの組に関して先頭から順に代入処理を行うため、
同じkeyが複数回現れた場合は、最後のvalueが残る。

そのため、先ほど compress()で示した例を改変した
id¡123÷username¡piyo÷email¡piyo@piyo.com÷id¡1÷checksum¡3925280164 という文字列(Sとする)を decompress() した場合、
Aは ['id' => '1', 'username' => 'piyo', 'email' => 'piyo@piyo.com', 'checksum' => '3925280164']
Bは ['id' => '1', 'username' => 'piyo', 'email' => 'piyo@piyo.com'] となる。

唐突に現れた 3925280164 は、checksumのチェックが通るよう、
Bを compress() したものの crc32() をあらかじめ計算したものである。

よって、Sを decompress() させられれば、
checksumのチェックに引っかかることなく無事 $_SESSION['userid'] === "1" となり、フラグが得られる。

解法

まず、emailを piyo@piyo.com から piyo@piyo.com÷id¡1÷checksum¡3925280164 に変更する。

すると、cookieに値を入れる処理における $plaintext は、
Sの crc32() である 1599925957 をSの末尾に付与した
id¡123÷username¡piyo÷email¡piyo@piyo.com÷id¡1÷checksum¡3925280164÷checksum¡1599925957 (S'とする)となる。

また、cookieに入る値は
S'をAESで暗号化してbase64エンコードしたもの + 000097 となる。

このcookieの値のままでセッションを破棄した場合、
S' (= S + ÷checksum¡1599925957) が decompress() されるため、
checksumが 1599925957 で上書きされ、チェックに通らない。

そのため、cookieの値の末尾(復号結果の先頭何バイトを decompress() するか)を調整し、
S'をAESで暗号化してbase64エンコードしたもの + 000075 とする。

すると、晴れてSが decompress() され、フラグが得られる。

flag: DCTF{4EF853DFC818AFEC39497CD1B91625F9E6E19D34D8E43E56722026F26A95F13E}

なお、emailの代わりにusernameを細工してもよい。
ちょっとログインしづらくなるが。

Reverse

Ransomware

ransomware.pycyoufool!.exe が渡される。

ransomware.pycデコンパイルするとこうなる。

import string
from random import *
import itertools

def caesar_cipher(x, y):
    y = y * (len(x) / len(y) + 1)
    return ''.join((chr(ord(i) ^ ord(j)) for i, j in itertools.izip(x, y)))

f = open('./FlagDCTF.pdf', 'r')
buf = f.read()
f.close()
allchar = string.ascii_letters + string.punctuation + string.digits
password = ''.join((choice(allchar) for _ in range(randint(60, 60))))
buf = caesar_cipher(buf, password)
f = open('./youfool!.exe', 'w')
buf = f.write(buf)
f.close()

60バイトのpasswordが確定すれば勝ち。

よくあるOTPの使い回しの問題と同様、少しずつ確定部分を広げていけば解ける。

元のファイルはPDFらしいので、先頭の %PDF-1. 辺りからスタートするとよい。

flag: DCTF{d915b5e076215c3efb92e5844ac20d0620d19b15d427e207fae6a3b894f91333}

Exploit

Online linter

CVE-2018-11235で任意のコードが実行できる。

otameshi61.hatenablog.com

/var/www/html/index.php の最初の行にフラグが書いてある。

flag: DCTF{4a49b863ba931ac65b077a504b973d9ddab4f343b00651a0b4ff9b8d7575f41f}

Misc

Message

単語(スペースで分割した各文字列)が全て qwertyasdfghzxcvbn のいずれか(Xとする)で終わっており、
また数字や記号の前後が必ずXになっていることから、Xが何らかの単位となっていることが予想できる。

そこで、Xを適当な文字に置換してやると、
前後をXに挟まれた部分文字列(のうち、数字や記号でないもの)が、
キーボード上で順になぞるとアルファベットの形になることに気付く。

最終的にこうなる。

Lorem ipsum is simply dummy text of the printing and typesetting industry. Lorem ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of letraset sheets containing lorem ipsum passages, and more recently with desktop publishing software like aldus pagemaker including versions of lorem ipsum. DCTF{b66ecaaa90ad05df5dab33d71a8f70934408f3a5847a4c5c38db75891b0f0e32}lorem ipsum is simply dummy text of the printing and typesetting industry. Lorem ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of letraset sheets containing lorem ipsum passages, and more recently with desktop publishing software like aldus pagemaker including versions of lorem ipsum.

flag: DCTF{b66ecaaa90ad05df5dab33d71a8f70934408f3a5847a4c5c38db75891b0f0e32}

Broken TV

各走査線がランダムにrotateしているが、手作業で十分復元できる。

フラグ付近だけ抜き出した画像は以下の通り。

flag: DCTF{1e20cabc8098b16cfeefb05af0a9032bb953871d6d627e7f88b81d1a3c5fa809}