CSAW CTF Qualification Round 2016 Writeup

この大会は2016/9/17 7:00(JST)~2016/9/19 7:00(JST)に開催されました。
今回もチームで参戦。結果は1301点で1274チーム中118位でした。
自分で解けた問題をWriteupとして書いておきます。

Coinslot (Misc 25)

提示された金額をできるだけ大きいコインで支払っていくようにする。

#!/usr/bin/env python

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('misc.chal.csaw.io', 8000))

for i in range(10000):
    data = s.recv(256)
    print data
    if i == 0:
        sum = float(data.split('\n')[0][1:]) * 100
    else:
        sum = float(data.split('\n')[1][1:]) * 100
    sum = int(str(sum).split('.')[0])

    b10000 = int(sum / 1000000)
    sum = sum % 1000000
    ans = str(b10000)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    b5000 = int(sum / 500000)
    sum = sum % 500000
    ans = str(b5000)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    b1000 = int(sum / 100000)
    sum = sum % 100000
    ans = str(b1000)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    b500 = int(sum / 50000)
    sum = sum % 50000
    ans = str(b500)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    b100 = int(sum / 10000)
    sum = sum % 10000
    ans = str(b100)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    b50 = int(sum / 5000)
    sum = sum % 5000
    ans = str(b50)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    b20 = int(sum / 2000)
    sum = sum % 2000
    ans = str(b20)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    b10 = int(sum / 1000)
    sum = sum % 1000
    ans = str(b10)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    b5 = int(sum / 500)
    sum = sum % 500
    ans = str(b5)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    b1 = int(sum / 100)
    sum = sum % 100
    ans = str(b1)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    c50 = int(sum / 50)
    sum = sum % 50
    ans = str(c50)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    c25 = int(sum / 25)
    sum = sum % 25
    ans = str(c25)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    c10 = int(sum / 10)
    sum = sum % 10
    ans = str(c10)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    c5 = int(sum / 5)
    sum = sum % 5
    ans = str(c5)
    print ans
    s.sendall(ans + '\n')

    data = s.recv(256)
    print data

    c1 = int(sum / 1)
    sum = sum % 1
    ans = str(c1)
    print ans
    s.sendall(ans + '\n')

    print 'Round %d' % (i + 1)

400ラウンドしたら、フラグが表示された。

flag{started-from-the-bottom-now-my-whole-team-fucking-here}

Notesy 2.0 (Crypto 1)

問題にフラグが書いてあった。

abcdefghijklmnopqrstuvwxyz

Sleeping Guard (Crypto 50)

ncで接続すると、Base64データらしき長いデータが返ってくる。このデータをdataファイルに保存する。
与えられたコードはKEYについてどんな処理をしているか書かれていないが、PNGファイルの先頭12バイトは決まっていることとKEYとXORをしているのではないかと予想してコードを書いてみる。

import base64

png_head = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
    0x00, 0x00, 0x00, 0x0d]

with open('data', 'rb') as f:
    data = f.read()

data = base64.b64decode(data)

key = []
for i in range(12):
    key.append(png_head[i] ^ ord(data[i:i+1]))

flag = ''
for i in range(len(data)):
    code = ord(data[i:i+1]) ^ key[i % 12]
    flag += chr(code)

with open('flag.png', 'wb') as f:
    f.write(flag)

実行すると、画像にフラグが書かれている。
f:id:satou-y:20160920220726p:plain

flag{l4zy_H4CK3rs_d0nt_g3T_MAg1C_FlaG5}

Watchword (Forensics 250)

添付されていたmp4ファイルに対してforemostを使う。

$ foremost powpow.mp4 
Processing: powpow.mp4
|*|

するとpngファイルが抽出できた。抽出したファイル名をoreo.pngに変える。
f:id:satou-y:20160920221106p:plain

stepicのURLがヒントに出ていたので、stepicを使って画像を抽出する。

$ stepic -d -i oreo.png -o out.png

抽出したファイルはjpg形式のため、out.pngからout.jpgに変更する。
f:id:satou-y:20160920221442j:plain

powpow.mp4のプロパティのタイトルにこう書いてある。

aHR0cDovL3N0ZWdoaWRlLnNvdXJjZWZvcmdlLm5ldC8=

Base64デコードすると、以下のURLになる。
http://steghide.sourceforge.net/

今度はsteghideを使う。パスフレーズはpassword。

$ steghide info out.jpg
"out.jpg":
  format: jpeg
  capacity: 1.7 KB
Try to get information about embedded data ? (y/n) y
Enter passphrase: 
  embedded file "base64.txt":
    size: 83.0 Byte
    encrypted: rijndael-128, cbc
    compressed: yes

$ steghide extract -sf out.jpg

すると、base64.txtが抽出できた。

ここで第2のヒント「Python3のbase64モジュールを使う」という情報から、Base85で復号する。

>c:\python35\python
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import base64
>>> e = 'W^7?+dsk&3VRB_4W^-?2X=QYIEFgDfAYpQ4AZBT9VQg%9AZBu9Wh@|fWgua4Wgup0ZeeU}c_3kTVQXa}eE'
>>> d = base64.b85decode(e)
>>> d
b'flag{We are fsociety, we are finally free, we are finally awake!}'
>>>
flag{We are fsociety, we are finally free, we are finally awake!}

WhiteHat Contest 12 Writeup

この大会は2016/9/10 11:00(JST)~2016/9/11 11:00(JST)に開催されました。
今回もチームで参戦ですが、結果は100点で81チーム中62位でした。
自分で解けた問題1問だけ正解という結果でしたが、その1問をWriteupとして書いておきます。

Crypto002 (Cryptography 100)

8進数が並んでいるように見えるので、それをASCIIコードとして読み込む。

with open('cipher.txt', 'r') as f:
    codes = f.read()

codes = codes.split(' ')

data = ''
for code in codes:
    data += chr(int(code, 8))

print data

その結果以下のようになった。Brainfuck言語かな...。

+++++ +++[- >++++ ++++< ]>+++ +++++ +++.+ ++.<+ ++[-> +++<] >+.<+ +++[-
>---- <]>-- .++++ +++.. +++++ +.<++ +++[- >---- -<]>- ----- --.<+ +++[-
>++++ <]>++ +++.+ +++++ +.--- ----- ..+++ ++++. <+++[ ->+++ <]>++ +.---
--.-- ----. -.+++ +.<++ +++[- >---- -<]>- ----. <++++ ++[-> +++++ +<]>.
<++++ [->-- --<]> .<+++ [->++ +<]>+ +.<++ +[->- --<]> ----- ..<++ +[->+
++<]> ++.<+ ++++[ ->--- --<]> -.<++ ++[-> ++++< ]>+++ .-.<+ +++[- >----
<]>-- -.<++ +++[- >++++ +<]>+ +++.- ----- -.<++ +[->+ ++<]> +++.< +++[-
>---< ]>--- .<+++ +[->+ +++<] >.<++ ++++[ ->--- ---<] >-.<+ +++[- >++++
<]>.< +++[- >+++< ]>+++ ++.<+ ++++[ ->--- --<]> ----. <++++ [->++ ++<]>
+++++ .+++. ----- .<+++ +[->- ---<] >---- -.--. <++++ ++[-> +++++ +<]>+
+.--- --.+. <++++ +[->- ----< ]>--- ----- -.<++ ++[-> ++++< ]>+++ ++++.
.<+++ [->++ +<]>+ +++.< +++++ [->-- ---<] >---- ----- -.<++ ++++[ ->+++
+++<] >.<++ ++[-> ----< ]>.<+ ++[-> +++<] >+++. <++++ [->-- --<]> .-.++
+++.- ----. <++++ [->++ ++<]> .++++ .<+++ [->-- -<]>- ---.+ +++++ ++.--
----- .<+++ [->++ +<]>+ ++.-- ----- --.-- -.<++ +[->+ ++<]> +++++ .<+++
+[->- ---<] >---. <++++ [->-- --<]> ---.< +++++ [->++ +++<] >++.< +++++
[->-- ---<] >--.< +++++ [->++ +++<] >+++. ++.-- ----. ++.-- --.++ +++++
++.++ +++++ +.<++ ++++[ ->--- ---<] >---. <++++ +[->+ ++++< ]>+++ +++++
++.-- ----- .++++ .<+++ ++[-> ----- <]>-- ----. <++++ [->++ ++<]> ++.<+
+++[- >---- <]>-- -.+.< ++++[ ->+++ +<]>+ +++++ +.<++ ++[-> ++++< ]>.<+
++[-> ---<] >--.+ +++++ +.<++ +[->- --<]> ----- .-.<+ +++[- >---- <]>--
-.<++ +++[- >++++ +<]>+ +++++ .---- ---.< +++[- >+++< ]>+++ .<+++ [->--
-<]>- ----. <++++ [->-- --<]> ----- .<+++ +[->+ +++<] >++++ ++.-- ----.
--.<+ ++[-> +++<] >++.< +++[- >---< ]>--. <+++[ ->+++ <]>++ .--.< +++[-
>---< ]>--. <++++ [->++ ++<]> +++++ +++.< ++++[ ->--- -<]>- ----. <++++
[->-- --<]> --.<+ ++[-> +++<] >++.. .<

Brainfuckオンラインツール https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行してみると、次のテキストになる。

KNXFMMS2GNFFMYTNMQ3WGRDDO5HG4QJVJZ5ES6KNI42XST2IIV3WGSCBGBRVIQJVMJXE2M2NPJLHQY2UNR3E23JZOVHG4SLXJ5KECNCNLAYD2===

アルファベットが全部大文字なので、Base32かも...。
https://emn178.github.io/online-tools/base32_decode.htmlでBase32デコードすると、以下のようになった。

SnV2Z3JVbmd7cDcwNnA5NzIyMG5yOHEwcHA0cTA5bnM3MzVxcTlvMm9uNnIwOTA4MX0=

今度はBase64デコードする。

$ echo 'SnV2Z3JVbmd7cDcwNnA5NzIyMG5yOHEwcHA0cTA5bnM3MzVxcTlvMm9uNnIwOTA4MX0=' | base64 -d
JuvgrUng{p706p97220nr8q0pp4q09ns735qq9o2on6r09081}

http://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherでROT13の復号をするとフラグになった。

WhiteHat{c706c97220ae8d0cc4d09af735dd9b2ba6e09081}

Tokyo Westerns/MMA CTF 2nd 2016 Writeup

この大会は2016/9/3 9:00(JST)~2016/9/5 9:00(JST)に開催されました。
今回もチームで参戦。結果は560点で835チーム中94位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome!! (Misc, Warmup 10)

問題文にフラグが書いてある。

TWCTF{Welcome_To_TW_MMACTF!!}

Twin Primes (Crypto, Warmup 50)

RSA暗号で復号するのに必要な情報はn=p*qの場合の(p-1)*(q-1)とe。
与えられている情報はn1(=p*q)とeのペアと、n2(=(p+2)*(q+2))とeのペア。
それぞれ簡単にオイラー関数の値を計算できるので、n2の方で復号した後にn1の方で復号する。

with open('encrypted', 'r') as f:
    c = int(f.read().strip())

with open('key1', 'r') as f:
    n1 = int(f.readline().strip())
    e1 = int(f.readline().strip())

with open('key2', 'r') as f:
    n2 = int(f.readline().strip())
    e2 = int(f.readline().strip())

sum = (n2 - n1 -4) / 2
phi1 = n1 - sum + 1
phi2 = n1 + sum + 1

x = 0
while True:
    if (phi2 * x + 1) % e2 == 0:
        d = (phi2 * x + 1) / e2
        break
    x = x + 1

m1 = pow(c, d, n2)

x = 0
while True:
    if (phi1 * x + 1) % e1 == 0:
        d = (phi1 * x + 1) / e1
        break
    x = x + 1

m = pow(m1, d, n1)

flag = ('%x' % m).decode('hex').split('\0')[0]
print flag
TWCTF{3102628d7059fa267365f8c37a0e56cf7e0797ef}

Super Express (Crypto 100)

encryptedが60桁の16進数。文字ごとにASCIIコードが計算されて、暗号化される。
暗号データが「805eed80cbbccb94c36413275780ec94a857dfec8da8ca94a8c313a8ccf9」なので、フラグは30文字。
またフラグはTWCTF{で始まり、}で終わる。
コードから逆の処理をしたり、ブルートフォースをするのが難しいため、わかる範囲から特徴を探す。

それぞれ暗号化の結果は以下のようになっている。[]内はASCIIコード。

T:[84]→0x80(=128)
W:[87]→0x5e(=94)
C:[67]→0xed(=237)
F:[70]→0xcb(=203)
{:[123]→0xbc(=188)
}:[125]→0xf9(=249)

ASCIIコード順で2つ置きに217プラスして251で割って暗号化していそう。
([暗号コード] * 214 + 51) % 251で復号できるようだ。

crypt = '805eed80cbbccb94c36413275780ec94a857dfec8da8ca94a8c313a8ccf9'

flag = ''
for i in range(0, len(crypt), 2):
    code = int(crypt[i:i+2], 16)
    code = (code * 214 + 51) % 251
    flag += chr(code)

print flag
TWCTF{Faster_Than_Shinkansen!}

BioTerra CTF 2016 参戦

この大会は2016/8/21 05:08(JST)~2016/8/22 06:08(JST)に開催されました。
今回もチームで参戦。結果は101点で150チーム中68位でした。
残念ながら自分が得点した問題は1問もなかったです。
非常に悔しい!

Hackcon 2016 Writeup

この大会は2016/8/19 23:30(JST)~2016/8/21 3:30(JST)に開催されました。
今回もチームで参戦。結果は1051点で689チーム中40位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome to the game (1)

freenodeの##hackconに入る。

13:28 *topic : Hackcon 2016 - hackcon.in. First flag is HACKCON{GLHF}. Pwnie binary changed!
HACKCON{GLHF}

In Rainbows (150)

StegSolveでいろいろ見る。
少し色が違う部分を浮き上がらせる。

++++++++[>++++>++++++>++++++++>++++++++++>++++
++++++++<<<<<-]>
>>>>++++++++++++++++++++.---------------
.<++++++++.>+++++++++++++++.<+++++++.----------.<+++.-
-.>-------.++++++.+++++++++++.>--.<<<+++.+.>>+++++.-----
.>---------.++++++++++.<<<.>>.+++.>-.<-.++++++++.>----.<---
.<<<++++++++++..>>>>---.
[>]<[[-]<]

Brainfuck言語のようだ。
https://sange.fi/esoteric/brainfuck/impl/interp/i.html で実行してみると、フラグが表示された。

teXt_UCANT_r34d_is4_brainf**k

You Can't See Me (150)

apkを解凍し、classes.dexをjarに変換する。

>d2j-dex2jar.bat classes.dex
dex2jar classes.dex -> .\classes-dex2jar.jar

JD-GUIでclasses-dex2jar.jarを開いて、ソースコードを見てみる。

package com.mayank13059.theoracle;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity
{
  private String genLoginForm1()
  {
    Integer localInteger = Integer.valueOf(656);
    return Integer.valueOf(686964656).toString() + "c" + Integer.valueOf(696).toString() + "b" + Integer.valueOf(656163).toString() + Integer.valueOf(68616).toString() + "d" + localInteger.toString() + "c" + localInteger.toString() + "f6e";
  }

  private String genLoginForm2()
  {
    Integer localInteger = Integer.valueOf(696);
    return Integer.valueOf(6265).toString() + Integer.valueOf(66696).toString() + Integer.valueOf(57263656).toString() + "c" + localInteger.toString() + "b" + Integer.valueOf(65616).toString() + "c" + localInteger.toString() + "f6e";
  }

  protected void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130968601);
    paramBundle = (TextView)findViewById(2131492970);
    TextView localTextView = (TextView)findViewById(2131492971);
    paramBundle.setText(genLoginForm1());
    localTextView.setText(genLoginForm2());
    setSupportActionBar((Toolbar)findViewById(2131492969));
  }

  public boolean onCreateOptionsMenu(Menu paramMenu)
  {
    getMenuInflater().inflate(2131558400, paramMenu);
    return true;
  }

  public boolean onOptionsItemSelected(MenuItem paramMenuItem)
  {
    if (paramMenuItem.getItemId() == 2131492994)
      return true;
    return super.onOptionsItemSelected(paramMenuItem);
  }
}

genLoginForm1()とgenLoginForm2()に注目する。

genLoginForm1(): 686964656c696b65616368616d656c656f6eを返す。
genLoginForm2(): 62656669657263656c696b65616c696f6eを返す。

それぞれhexデコードする。

686964656c696b65616368616d656c656f6e → hidelikeachameleon
62656669657263656c696b65616c696f6e → befiercelikealion

結合した文字列がフラグだった。

hidelikeachameleonbefiercelikealion

LabyREnth 2016 Writeup

この大会は2016/7/16 9:00(JST)~2016/8/15 9:00(JST)に開催されました。
個人戦の大会ですが、順位とか点数とかがよくわからない大会でした。
解けた問題をWriteupとして書いておきます。

unix tier level 1

perlスクリプトで、スクリプトコードやデータがBase64エンコードされている。それぞれデコードするスクリプトを作成する。

key_begin   = '. MIME::Base64::decode'
key_begin_e = 'eval MIME::Base64::decode'
key_end     =  '")'

def decode(a, data):
    index = 0
    while True:
        i_begin = data.find(key_begin, index)
        if i_begin < 0:
            break
        i_end = data.find(key_end, i_begin)
        enc = data[i_begin + 24:i_end]
        a += enc.decode('base64')
        index = i_end

    i_begin = data.find(key_begin_e)
    if i_begin < 0:
        return a, ""
    i_end = data.find(key_end, i_begin)
    e_data = data[i_begin + 27:i_end].decode('base64')

    return a, e_data

with open('bowie.pl', 'r') as f:
    data = f.read()

a = ''

while True:
    a, e_data = decode(a, data)
    if e_data == "":
        break
    data = e_data

with open('flag.gif', 'wb') as f:
    f.write(a)

print data

実行すると、gifファイルが生成され、フラグが埋め込まれている。
f:id:satou-y:20160901205727g:plain

PAN{L3ts_533_h0W_U_deal_w_th1s_little_511CE}

threat tier level 1

GETのアクセス先URLの文字列がフラグに関係ありそう。Base64文字列になっているので、いろいろ試してみる。

/[A].php?[B]&L4bry1nth_(数字)?[C]-<***>の形式。
[A]をBASE64デコードすると2桁数値
[C]+[B]をBASE64デコードするとよくわからないデータ。
ASCIIアートの一部かもしれない。

[A]のデコードした数値の順に[C]+[B]のデコードしたものを並べるスクリプトを書く。

from scapy.all import *
import base64
import re

pattern = 'GET /(.{4})\.php\?(.+)&L4bry1nth_.+\?(.+)-.+'

packets = rdpcap('dopefish_labyrinth.pcap')

d = {}
for p in packets:
    if p['TCP'].dport == 80:
        l = p['Raw'].load
        m = re.match(pattern, l)
        p1 = m.group(1)
        p2 = m.group(2)[::-1]
        p3 = m.group(3)[::-1]
        id = base64.b64decode(p1).replace('\n', '')
        data = base64.b64decode(p3 + p2).replace('\n', '')
        d[id] = data

for key, val in sorted(d.items()):
    print val

これを実行すると、魚のようなASCIIアートが出来上がる。

317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317W.317.;317;;317;;317.W317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317W.317;;317;;317;;317;W317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317;;317;;317;;317;;317;W317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317W.317.W317WW317WW317WW317WW317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317W.317;;317;;317;;317;;317.W317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317..317;;317.W317WW317WW317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317W.317..317;;317;.317..317..317..317;;317..317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317.;317;.317.W317WW317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317WW317..317.;317;;317;;317P;317;;317;;317;;317;;317;;317;;317;;317;.317.W317WW317WW317WW317WW317WW317WW317WW317WW317WW317W.317;;317;.317WW317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317.;317;;317;;317;;317;;317;A317;;317;;317;;317;;317;;317;;317;;317;;317;;317..317WW317WW317WW317WW317WW317WW317WW317WW317W.317;;317;;317..317WW317WW317WW
317WW317WW317WW317WW317WW317W;317;;317;;317;;317;;317;;317;;317;;317N;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317.W317WW317WW317WW317WW317WW317WW317W.317;;317;;317;.317.W317WW317WW
317WW317WW317WW317WW317W.317;;317;;317;;317;;317;;317;;317;;317;;317;{317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317.W317WW317WW317WW317WW317WW317W.317;;317;;317;;317..317WW317WW
317WW317WW317WW317W.317..317.;317;;317;;317;;317;;317;;317;;317;;317;;317t;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;.317WW317WW317WW317WW317WW317.;317;;317;;317;;317;.317.W317WW
317WW317WW317W.317WW317W.317.W317WW317WV317..317;;317;;317;;317;;317;;317;h317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;+317..317WW317WW317WW317W.317;;317;;317;;317;;317;.317.W317WW
317WW317W.317WW317WW317WW317WW317WW317WW317WW317WW317;;317;;317;;317;;317;;3173;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;.317.W317WW317W.317;;317;;317;;317;;317;;317;.317.W317WW
317WW317.V317W.317..317..317.W317WW317WW317WW317WW317.;317;;317;;317;;317;;317;D317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;.317..317.;317;;317;;317;;317;;317;;317..317WW317WW
317WW317.W317W.317..317..317.W317WW317WW317WW317WW317W.317;;317;;317;;317;;317;;3170;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;.317.W317WW317WW
317WW317..317WW317W.317..317..317WW317WW317WW317WW317V.317;;317;;317;;317..317.;317;p317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317..317WW317WW317WW317WW
317WW317.;317.W317.W317WW317WW317WW317WW317WW317WW317.;317;;317;;317;;317;.317.;317;;3173;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;.317WW317WW317WW317WW
317WW317..317.;317.V317W.317.W317WW317WW317WW317W.317;.317.;317;;317;;317;.317.;317;;317;f317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317..317WW317WW317WW
317WW317W.317..317..317.;317;;317..317..317..317;;317;;317;;317..317;;317;.317.;317;;317;;317s;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;.317.W317WW317WW
317WW317W.317WW317.V317V.317..317..317..317..317..317..317..317;;317;;317;.317.;317;;317;;317;h317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317..317WW317WW
317WW317W.317WW317.W317WW317WV317.;317;;317;;317;.317;.317..317.;317;;317..317.;317;;317;;317;;317l;317;;317;;317;;317;;317;;317;;317;;317;.317..317.;317;;317;;317;;317;;317;;317..317WW317WW
317WW317W.317WW317.W317WW317WV317..317..317;;317;;317;;317;.317.;317;;317..317;;317;;317;;317;;317;1317;;317;;317;;317;;317;;317;;317;.317.W317WW317W.317.;317;;317;;317;;317;;317..317WW317WW
317WW317W.317WW317.W317WW317WV317.;317;;317;;317;.317;;317;;317.;317;;317;;317;;317;;317;;317;;317;;317v;317;;317;;317..317;;317..317..317WW317WW317WW317..317;;317;;317;;317;;317..317WW317WW
317WW317W.317WW317.W317WW317WV317..317..317.;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;3317.;317;;317;.317..317.W317WW317WW317WW317WW317WW317.;317;;317;;317;.317.W317WW317WW
317WW317W.317WW317.W317WW317WV317.;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317s;317;.317..317..317WW317WW317WW317WW317WW317WW317.;317;;317;;317;.317.W317WW317WW
317WW317W.317..317.W317WW317WV317..317;;317;;317;;317;.317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317.}317..317..317WW317WW317WW317WW317WW317WW317WW317.;317;;317;;317..317WW317WW317WW
317WW317WW317WW317W.317..317WV317.W317W.317..317..317.+317;;317;;317;;317;;317;;317;;317;;317;;317;;317;;317;.317..317WW317WW317WW317WW317WW317WW317WW317W.317;;317;;317;;317.W317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317W.317.;317;;317;;317;;317;;317;;317;;317;;317;;317..317.W317WW317WW317WW317WW317WW317WW317WW317WW317W.317;;317;.317.W317WW317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317..317;;317;;317;;317;;317;.317..317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317.;317;.317WW317WW317WW317WW317WW317WW
317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW317WW

このアスキーアートの中に斜めにフラグが入っている。

PAN{th3D0p3fshl1v3s}

docs tier level 1

デバッグしながら実行すると、以下のURLにGETでアクセスしていることがわかる。

http://10.1.33.7/b64/x58/MDgxOTE2MjMwZTMxMDIzMTNhNjk2YjA3NjgzNjM0MjE2YTJjMzA2ODJiNmIwNzBmMzA2ODA3MTMz\nNjY4MmYwNzJmMzA2YjJhNmI2YTM0Njg2ODMzMjU=/evil.exe

でも実際にアクセスしても接続することはできない。URLのパスをヒントにして、まず以下の部分を "\n" は改行に変え、Base64デコードする。

MDgxOTE2MjMwZTMxMDIzMTNhNjk2YjA3NjgzNjM0MjE2YTJjMzA2ODJiNmIwNzBmMzA2ODA3MTMz\nNjY4MmYwNzJmMzA2YjJhNmI2YTM0Njg2ODMzMjU=
→081916230e3102313a696b07683634216a2c30682b6b070f3068071336682f072f306b2a6b6a3468683325

さらに0x58とXORをとり、ASCIIコードとして文字に変換する。

enc = '081916230e3102313a696b07683634216a2c30682b6b070f3068071336682f072f306b2a6b6a3468683325'
key = 0x58

dec = ''
for i in range(0, len(enc), 2):
    code = int(enc[i:i+2], 16)
    dec += chr(code ^ key)

print dec
PAN{ViZib13_0nly2th0s3_Wh0_Kn0w_wh3r32l00k}

docs tier level 2

マクロを参照しようとしたが、パスワードがかかっている。https://home.ayra.ch/unlock/でパスワードを解除した後、マクロのUserForm1のコードを見る。

Private Sub button_Click()
    x = suchcrypto(key.Text, "General Vidal")
    If x = "171,184,42,184,88,26,47,154,20,219,203,130,52,19,180,214,156,94,186,74,30,248,119,235,139,130,175,141,179,197,8,204,252," Then
        MsgBox "Wow. Good Job! Such crack."
    Else
        MsgBox "U can do. Try harder..."
    End If
End Sub

Function suchcrypto(sMessage, strKey)
    Dim kLen, x, y, i, j, temp
    Dim s(256), k(256)
    kLen = Len(strKey)
    For i = 0 To 255
        s(i) = i
        k(i) = Asc(Mid(strKey, (i Mod kLen) + 1, 1))
    Next
    j = 0
    For i = 0 To 255
        j = (j + k(i) + s(i)) Mod 256
        temp = s(i)
        s(i) = s(j)
        s(j) = temp
    Next
    x = 0
    y = 0
    For i = 1 To 3072
        x = (x + 1) Mod 256
        y = (y + s(x)) Mod 256
        temp = s(x)
        s(x) = s(y)
        s(y) = temp
    Next
    For i = 1 To Len(sMessage)
        x = (x + 1) Mod 256
        y = (y + s(x)) Mod 256
        temp = s(x)
        s(x) = s(y)
        s(y) = temp
 
        suchcrypto = suchcrypto & (s((s(x) + s(y)) Mod 256) Xor Asc(Mid(sMessage, i, 1))) & ","
    Next
End Function

Private Sub UserForm_Click()

End Sub

途中までそのまま計算し、計算結果とIf文のチェックの内容とをXORで元に戻していく。

strKey = 'General Vidal'
strCode = '171,184,42,184,88,26,47,154,20,219,203,130,52,19,180,214,156,94,186,74,30,248,119,235,139,130,175,141,179,197,8,204,252,'

kLen = len(strKey)

s = []
k = []
for i in range(256):
    s.append(i)
    pos = i % kLen
    k.append(ord(strKey[pos:pos+1]))

j = 0
for i in range(256):
    j = (j + k[i] + s[i]) % 256
    s[i], s[j] = s[j], s[i]

x = 0
y = 0
for i in range(1, 3073):
    x = (x + 1) % 256
    y = (y + s[x]) % 256
    s[x], s[y] = s[y], s[x]

codes = strCode[:-1].split(',')

sMessage = ''
for i in range(1, len(codes) + 1):
    x = (x + 1) % 256
    y = (y + s[x]) % 256
    s[x], s[y] = s[y], s[x]
    pos = (s[x] + s[y]) % 256
    sMessage += chr(s[pos] ^ int(codes[i - 1]))

print sMessage
PAN{L4$t_Night_@f@iry_Vizited_M3}

docs tier level 3

PDF Stream Dumperで見ようとしても見ることができない。どうやら壊れているようだ。https://www.pdf-online.com/osa/repair.aspx で修復し、修復したファイルをPDF Stream Dumperで見る。赤で表示されている「10 HLen: 0x189」のところを見ると、フラグが入っていた。

<<
	
	/JS ty.i.f.(.e.v.e.n.t...v.a.l.u.e.=.=.".P.A.N.{.g.0.o.b.y.g.o.0.b.y.d.0.0.w.h.3.r.3.r.u.?.}.".).{.a.p.p...a.l.e.r.t.(.".y.e.s.".).;.}.e.l.s.e.{.a.p.p...a.l.e.r.t.(.".n.o.".).;.}.

	/S /JavaScript
	/Type /Action
>>
PAN{g0obygo0byd00wh3r3ru?}