yyy

CTFつよくなりたい

SECCON Beginners CTF 2018 Activation & crackme

はじめに

ちょうど帰省していて,少しだけ時間があったのでぼっちで参加してみました.チーム名は,テレビでFoxチャンネルのリスナーって海外ドラマが流れていたのと,途中でANAのCMが流れていたので"foxana"です.適当です.

本当の初心者しか参加してはいけないのかと思ってたら割といろんな人が参加していたので,メンバーに声かけていつものチームで参加すればよかったかなぁと思いました.

スコアは1014ptで72位でした.Writeupはいろんな人が書いてくれると思うので,僕はReversingのWarmup以外の2問を簡単に書いておきます.

Activation

問題文

この問題の FLAG は ctf4b{アクティベーションコード} です。
$ file Activation.exe
Activation.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

.Net assemblyなのでdnspyでデコンパイルしてみます.

f:id:ywkw1717:20180527135618p:plain

僕の環境だとdriveinfoのところでflagがtrueになってくれなくて(CDドライブがないから?),そもそもアクティベーションコードを入力するところまでいかなかったので,赤くなっている部分にブレークポイントをしかけて無理やりflagをtrueにしました.

f:id:ywkw1717:20180527135957p:plain

重要なのは選択しているif文のところです.ここで,入力したアクティベーションコードが正しいかのチェックをしています. 実行してここでステップインすることで,Cという関数の中でなにをやっているのかを見ていきます.

f:id:ywkw1717:20180527140427p:plain

AESで暗号化しているっぽいので,適当な場所で止めてIVとKeyの値を控えます.

また,これを E2AA8B78-798D-49BF-B9E7-13D334768E86.F() と比較しているのでこれも何が入っているのか調べます.

あとは復号するスクリプトを書くだけです. E3c0Iefcc2yUB5gvPWge1vHQK+TBuUYzST7hT+VrPDhjBt0HCAo5FLohfs/t2Vf5E2AA8B78-798D-49BF-B9E7-13D334768E86.F() の中身です.

gist.github.com

デコンパイルしたC#コードをそのまま使ったので,実行環境がない人は

paiza.io

とか使うといいかもです.

以上より,フラグは ctf4b{ae03c6f3f9c13e6ee678a92fc2e2dcc5} です.

crackme

問題文

バイナリを解析して、入力値を求めてください。
$ file crackme
crackme: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=7e64cdb9686f4ec7c55701b9bbf8b69417da0c46, stripped

コマンドライン引数としてフラグを与えて,それが正しいかどうか検証しています.

$ ./crackme
usage: ./crackme <FLAG>%                                                                                                                                                                                 $ ./crackme hoge
Try again...% 

objdumpで逆アセンブルしてみると,mainで2つの関数を読んでいることがわかります.一つは

  400978:   e8 09 fe ff ff         call   400786 <exit@plt+0x2c6>

で,もう1つは

  40098f:    e8 42 fc ff ff         call   4005d6 <exit@plt+0x116>

です.

あとは,具体的な処理を追う為にgdbで実行していきます.

この2つの関数は入力値検証関数で,それぞれ前の16バイト,後ろの16バイトを検証しています. 一つ目の関数を見てみると,検証するための値として,以下の16バイトの値をスタックに置いています.

0032| 0x7fffffffc8f0 --> 0x3f7b64683e8c9e9c
0040| 0x7fffffffc8f8 --> 0x641e73557f0a6350

入力値に対して特定の処理を施した結果がこれらと等しいかどうかを1バイトずつ検証していきます.入力値に対してどのような処理をするのかというと,以下のような処理を4回繰り返すことで16バイトの値それぞれに対して検証しています.これ↓は1周目の値です.meと名前を付けましたが,この変数はスタック上に保存されている値で,これを変化させながら計算処理に使用しています.

# first
入力値の1バイト目とme(0xff)とのxor
0x9cとの比較

# second
me(0xff)と0x15のxor -> me(0xea)
入力値の2バイト目とme(0xea)とのxor
0x9eとの比較

# third
me(0xea)と0x20のor -> me(0xea)
入力値の3バイト目とme(0xea)とのxor
0x8cとの比較

# fourth
me(0xea)と0xfのand -> me(0xa)
入力値の4バイト目とme(0xa)とのxor
0x3eとの比較

どんな処理をしているのかがわかったので,あとは2つ目の検証関数も見ていくだけですが,2つ目の検証関数の処理は少々複雑(他の人のwriteupを見る限り大したことやってない…?なにを見ていたのか…)で,z3で書くのが辛かったので,angrを使ってみました.

以下がangrを使って書いたスクリプトです.

gist.github.com

コマンドライン引数で渡すタイプのangrの書き方がわからなくて,hamaさんのブログを参考にしました.

hama.hatenadiary.jp

(参考というかほぼ丸パクリですね...)

重要なのはfindの部分とavoidの部分で,findはmain関数にて”正しい値か正しくない値かで最後の出力を分岐しているところ”があるので,このアドレスを入れています.avoidに書いた9つの値は,1つは”mainにある間違った値が入力されたときに通る箇所”,もう4つは”検証関数1にて,間違った値が入力されたときに通るアドレス”,残りの4つは”検証関数2にて,間違った値が入力されたときに通る箇所”です.

これを実行するとフラグが出てきます.

$ python solve_angr.py
WARNING | 2018-05-27 14:39:50,238 | angr.analyses.disassembly_utils | Your verison of capstone does not support MIPS instruction groups.
WARNING | 2018-05-27 14:39:51,556 | angr.factory | factory.path_group() is deprecated! Please use factory.simgr() instead.
Deprecation warning: Use eval(expr, cast_to=str) instead of any_str
'ctf4b{D0_y0u_l!k3_x86_4ssembly?}'

よってフラグは, ctf4b{D0_y0u_l!k3_x86_4ssembly?} です.

検証関数1だけの処理を書いて諦めたz3バージョンも載せておきます.このスクリプト,なんとフラグが半分だけ求まります.

gist.github.com

rthornton128/goncursesがgo getで入らない時

はじめに

GWも終わりそうですね. 今回, rthornton128/goncurses を使ってみようとしたらgo getで躓いたのでいろいろ書いておきます.環境はUbuntu16.04 LTSです.

rthornton128/goncurses

github.com

ncursesというTUIを作成するための便利ライブラリがありますが,それをGo言語でも使えるようなものです.

で,こちらをインストールしようと go get したのですが

$ go get -u github.com/rthornton128/goncurses
# pkg-config --cflags ncurses form menu ncurses ncurses panel
Package ncurses was not found in the pkg-config search path.
Perhaps you should add the directory containing `ncurses.pc'
to the PKG_CONFIG_PATH environment variable
No package 'ncurses' found
Package form was not found in the pkg-config search path.
Perhaps you should add the directory containing `form.pc'
to the PKG_CONFIG_PATH environment variable
No package 'form' found
Package menu was not found in the pkg-config search path.
Perhaps you should add the directory containing `menu.pc'
to the PKG_CONFIG_PATH environment variable
No package 'menu' found
Package ncurses was not found in the pkg-config search path.
Perhaps you should add the directory containing `ncurses.pc'
to the PKG_CONFIG_PATH environment variable
No package 'ncurses' found
Package ncurses was not found in the pkg-config search path.
Perhaps you should add the directory containing `ncurses.pc'
to the PKG_CONFIG_PATH environment variable
No package 'ncurses' found
Package panel was not found in the pkg-config search path.
Perhaps you should add the directory containing `panel.pc'
to the PKG_CONFIG_PATH environment variable
No package 'panel' found
pkg-config: exit status 1

このようなエラーが出てしまいインストールできませんでした.思い当たる節としては,そもそもncurses入れてなかったので,まずはncursesをインストールしていきます.

yaneno-suzume.at.webry.info

ちょうど,同じ環境で試してる人がいましたがgccのバージョンの問題で最新版が入らないと書いてあります.僕のgccはこの5.31よりは新しい5.4だったので入るかもしれませんでしたが,確実に入りそうなncurses-5.7を入れることにしました.

$ wget http://ftp.gnu.org/gnu/ncurses/ncurses-5.7.tar.gz
$ tar -xvzf ncurses-5.7.tar.gz
$ cd ncurses-5.7
$ ./configure
$ make
$ sudo make install

これでncursesは入りましたが go get で出ていたエラーは全て,pcファイルを PKG_CONFIG_PATH に追加するべきだ,みたいなやつです. PKG_CONFIG_PATH ってなんだと思い調べてみると, /usr/lib/pkgconfig または /usr/local/lib/pkgconfig を通せばいいっぽいです.

askubuntu.com

自分の場合は /usr/lib/pkgconfig があったのでこれを通すようにシェルの設定ファイルに export PKG_CONFIG_PATH=/usr/lib/pkgconfig を追加しました.

そして拡張子がpcのファイルを PKG_CONFIG_PATH に追加する必要がありますが,これらがどこにあるのかわからずググると以下のgistが見つかりました.

gist.github.com

これを参考に,上記でmakeしたディレクトリを /usr/local/opt/ へコピーし,/usr/local/opt/ncurses-5.7 という風にしました.あとは,上記のgistにあるpcファイルの prefix/usr/local/opt/ncurses-5.7 に, Description のバージョンを6.0から5.7にして4つのpcファイルを PKG_CONFIG_PATH に配置してから go get したところ上手く入りました.

libncurses5-dev

ここまでいろいろと書きましたが,これパッケージマネージャーから入らんの?と思い調べてみると, libncurses5-dev というパッケージが見つかったので,これを apt-get install してからパスをいい感じに設定してもよかったかもしれません.

Ubuntu – パッケージのファイル一覧: libncurses5-dev/xenial/amd64

おまけ

OS Xでやっている方もいらっしゃったので,OS Xの人はこちらを参考にするといいと思います.

kandayasu.hatenablog.com

NVM_DIRを正しく設定していなかったせいでパスが破壊されていた話

はじめに

特に書くことがなく4ヶ月ぶりのブログです.

最近golangを始めて, export PATH=$GOPATH/bin:$PATH とかでパスを通そうとしたらなぜかパスが通らないという問題が起きました.いろいろ探っていくとどうやら nvm.sh をsourceしている場所より後ろに配置すると通るという謎現象が起きていて,さらに言うと $GOPATH/bin のbinをhogeとか別の名前にすると通るという意味が分からないことになっていたので, nvm.sh を少し読んでみたら原因がわかったという話です.というかブログのタイトル通りなので,あとはダラダラと書いていきます.

nvm.sh

github.com

nvmはNode Version Managerの略でNode.jsのバージョン管理ツールであり,使っている人も多いのではないでしょうか.僕はNode.jsはほぼ書いたことがないのですが,たまに必要になったりすることもあるので一応使っています.

そんなnvmですが,シェルの設定ファイルとかに nvm.sh を実行するように書いて使用します.この nvm.sh の中にnvmコマンドの本体も書かれています.

nvm() {
  if [ $# -lt 1 ]; then
    nvm --help
    return
  fi

  local DEFAULT_IFS
  DEFAULT_IFS=" $(nvm_echo t | command tr t \\t)
"
  if [ "${IFS}" != "${DEFAULT_IFS}" ]; then
    IFS="${DEFAULT_IFS}" nvm "$@"
    return $?
  fi
...........................

起きていた現象について

まず, nvm.sh をsourceしている直前で $GOPATH/binのパスを通してから echo $PATH で正しく反映されていることを確認しました.その後, nvm.sh の直後にも echo $PATH を置いて,パスの頭に追加した $GOPATH/binそっくり消えていることも確認しました.これは冒頭でも書いたようにbinを別名にすると通ります.

そこで, nvm.sh を読むことにしました.この時僕が使っていた nvm.sh は少し古いものでしたが(ver 0.33.1),最新版(ver 0.33.9)を持ってきてもパスが通らないことに変わりはありませんでした.以下,まずは古い nvm.sh(0.33.1) について書きます.

nvm.sh では nvm_process_parametersという関数を実行していてその中でさらに nvm_auto を実行しています.この nvm_auto の中にあった以下の行でパスは更新されていました.

.......
   VERSION="$(nvm_resolve_local_alias default 2>/dev/null || nvm_echo)"
    if [ -n "$VERSION" ]; then
      nvm use --silent "$VERSION" >/dev/null
.......

VERSION には v8.11.1などが入ってきます.次に,nvmのuseというサブコマンドを見てみます.nvmに渡されたサブコマンドはcase文によって分岐していて,useも同様です.

    "use" )
      local PROVIDED_VERSION
      local NVM_USE_SILENT
      NVM_USE_SILENT=0
      local NVM_DELETE_PREFIX
      NVM_DELETE_PREFIX=0
.......

このuseの中に以下のような行を見つけました.

.......
      # Strip other version from PATH
      PATH="$(nvm_strip_path "$PATH" "/bin")"
.......

見るからに原因がこれっぽいです....

nvm_strip_path を見てみると,

nvm_strip_path() {
  if [ -z "${NVM_DIR-}" ]; then
    nvm_err '${NVM_DIR} not set!'
    return 1
  fi
  nvm_echo "${1-}" | command sed \
    -e "s#${NVM_DIR}/[^/]*${2-}[^:]*:##g" \
    -e "s#:${NVM_DIR}/[^/]*${2-}[^:]*##g" \
    -e "s#${NVM_DIR}/[^/]*${2-}[^:]*##g" \
    -e "s#${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*:##g" \
    -e "s#:${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*##g" \
    -e "s#${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*##g"
}

このように渡された引数を sed を使って置換しています.第1引数には現在のパスを,第2引数には"/bin"という文字列を渡していて,冒頭で説明した追加しているのに消えているパスというのは /home/yyy/.go/bin です.これは先頭に追加していたので,追加後は /home/yyy/.go/bin:/home/yyy/... という風になるはずです.

また, NVM_DIR はどんな値になっているのか調べてみると NVM_DIR=/home/yyy になっていました.

ということでsedで置換している1行目, ${NVM_DIR}/ は"/home/yyy/"になり, [^/] は否定の文字クラスであり"/"以外全てを表し,直後の*である0回以上の繰り返しによって".go"が当てはまり,その後の ${2-} が"/bin,最後の":"も合わせて `/home/yyy/.go/bin:" が当てはまってしまうことになります.

よって,sedのこの1行目でパスが破壊されていたということになります.

NVM_DIRについて

謎の現象が起きていた原因はもちろん, NVM_DIR を正しく設定していなかったことです.なにを見て追加したかは忘れましたが,とにかく設定ファイルに NVM_DIR は書いてありませんでした....

それでも NVM_DIR が設定されていたのは nvm.sh の以下の箇所が原因でした.

# Auto detect the NVM_DIR when not set
if [ -z "${NVM_DIR-}" ]; then
  # shellcheck disable=SC2128
  if [ -n "${BASH_SOURCE-}" ]; then
    # shellcheck disable=SC2169
    NVM_SCRIPT_SOURCE="${BASH_SOURCE[0]}"
  fi
  # shellcheck disable=SC1001
  NVM_DIR="$(nvm_cd ${NVM_CD_FLAGS} "$(dirname "${NVM_SCRIPT_SOURCE:-$0}")" > /dev/null && \pwd)"
  export NVM_DIR
fi
unset NVM_SCRIPT_SOURCE 2> /dev/null

NVM_CD_FLAGS は "-q" になっていて, nvm_cd というのは以下のようになっています.

nvm_cd() {
  # shellcheck disable=SC1001,SC2164
  \cd "$@"
}

また, "$(dirname "${NVM_SCRIPT_SOURCE:-$0}")"nvm.sh があるディレクトリになるので,僕の場合 "$(nvm_cd ${NVM_CD_FLAGS} "$(dirname "${NVM_SCRIPT_SOURCE:-$0}")" > /dev/null && \pwd)" は以下のような文字列になるはずです.

cd -q /home/yyy/.nvm > /dev/null && \pwd

.nvm絶対パスを取得して,この結果を NVM_DIR に格納していると思うのですが, -q オプションをつけるのとつけないので比較してみると, -q を付けた場合は格納される値がホームディレクトリの絶対パスになります.そもそも -q オプションなんて cd にはないと思うのですが,よくわかりません.

NVM_CD_FLAGS は以下の箇所で定義されています.

# Make zsh glob matching behave same as bash
# This fixes the "zsh: no matches found" errors
if [ -z "${NVM_CD_FLAGS-}" ]; then
  export NVM_CD_FLAGS=''
fi
if nvm_has "unsetopt"; then
  unsetopt nomatch 2>/dev/null
  NVM_CD_FLAGS="-q"
fi

このコメントを見る限り, NVM_CD_FLAGS は必要なものだと思います.これを除いて NVM_DIR="$(nvm_cd "$(dirname "${NVM_SCRIPT_SOURCE:-$0}")" > /dev/null && \pwd)" とすると, NVM_DIR には.nvmの絶対パスが入ってくれるのですが....

最新版のnvm.sh

現在(2018/04/20)の最新版は 0.33.9 です.この nvm.sh では上記の箇所が以下のように変わっていました.

# Change current version
PATH="$(nvm_change_path "$PATH" "/bin" "$NVM_VERSION_DIR")"
nvm_change_path() {
  # if there’s no initial path, just return the supplementary path
  if [ -z "${1-}" ]; then
    nvm_echo "${3-}${2-}"
  # if the initial path doesn’t contain an nvm path, prepend the supplementary
  # path
  elif ! nvm_echo "${1-}" | nvm_grep -q "${NVM_DIR}/[^/]*${2-}" \
    && ! nvm_echo "${1-}" | nvm_grep -q "${NVM_DIR}/versions/[^/]*/[^/]*${2-}"; then
    nvm_echo "${3-}${2-}:${1-}"
  # use sed to replace the existing nvm path with the supplementary path. This
  # preserves the order of the path.
  else
    nvm_echo "${1-}" | command sed \
      -e "s#${NVM_DIR}/[^/]*${2-}[^:]*#${3-}${2-}#g" \
      -e "s#${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*#${3-}${2-}#g"
  fi
}

nvm_strip_path ではなく nvm_change_path を使っています.これも同様に,sedの1行目に当てはまってしまっていたのでパスが消えていたという事でした.

まとめ

結局 NVM_CD_FLAGS がなぜ必要なのかわからず,バグなのかどうかもわからずじまいでした.issueでも立てたほうがいいんですかね....

NVM_DIR を正しく設定しましょうという話でした.

2017年を振り返る

去年も1年を振り返る記事を書いた.

ywkw1717.hatenablog.com

これが意外とその時期に何をやっていたのかわかったりして便利だったので,今年も1年の振り返りを書いておこうと思う.

去年みたいに携帯の画像欄を漁っていく.

1月

アセンブラ短歌

f:id:ywkw1717:20171231155417p:plain 新年なにをやっていたか見てみたら,アセンブラ短歌を勉強していた.バイト先の勉強会でやろうと思っていろいろ試行錯誤していた時期.限られたバイト数でアセンブリを書く技術はCTFでシェルコードを書いた時に役に立ったし,なによりアセンブラ短歌という文化が面白いので,オススメ.

ノートPCのメモリ増設

ywkw1717.hatenablog.com

今ではもう使っていないPCだけど,この時メモリもっと欲しいなぁと思って増設していた.結果的に快適環境になったし,買ってよかった.

2月

絵ろうそく祭り

f:id:ywkw1717:20171231160455p:plain

f:id:ywkw1717:20171231160847p:plain

会津では毎年2月に絵ろうそく祭りというのが開催される.多くの絵ろうそくが鶴ヶ城に設置されてめっちゃ綺麗なんだけど,これのバイトをやることになって,鎧を着て観光客と写真撮影するっていう仕事を友達と一緒にやっていた.足周りがくそ寒くて,辛かった覚えがある.

双葉杏を描いた

f:id:ywkw1717:20171231161145p:plain

双葉杏が可愛いので,描いた.模写じゃなくてオリジナルのポーズでキャラクターを描くのは初めてで苦戦してた.

後から細かく見直すと修正したくなるところが多々でてくるので,あまり直視しないようにしていたりする.

3月

FUKUSHIMA Hackathon 2017 セキュリティ部門

fukushimahackathon2017.peatix.com

セキュリティ部門で参加した.このハッカソンはバイト先が開催しているやつで,参加者として出れることになったのでセキュリティ部門で脆弱性を探していた.圧倒的技術力不足でほとんどなにもできなかったけど,参加者に某有名CTFチームの人や某セキュリティベンダーのメンバーで構成されたチームが出ていたり,プロの凄さに圧倒された.

f:id:ywkw1717:20171231162107p:plain

ThinkPad X1 Carbon を買った

f:id:ywkw1717:20171231162050p:plain

今年一番,いや過去最高の買い物かもしれない.入学当時から使っていたPCが物理的な限界を迎えていて,スペックがもっといいPCが欲しいなぁと思っていたので思い切って購入した.確か18万ぐらいで,親に12万借りて一括で買った.その12万は今でも返済していて,終わるのは来年の4月頃になりそう.

X1 Carbon よい.

4月

新入生LT

f:id:ywkw1717:20171231162444p:plain

バイト先が開催している新入生LT.自分は,ノートPCのWebカメラは必要ない時以外は塞いでセキュリティ意識高めましょうみたいな話をした.Kaliを使ってデモをしようとしていたが,会場のネットワークがゴミで結局できなかったのを覚えている.

応用情報技術者試験

午後の点数が27点も足りなくて落ちる.FEの時もそうだったけど,3週間前くらいになってからノロノロ始めて焦るやつをまたやってしまって,FEみたいにいけるやろとか思ってたら甘かった....秋にまた受けた.

5月

モーニング娘。'17ライブ in 仙台

f:id:ywkw1717:20171231164630p:plain

画像は帰りに食べた牛タン.さすが仙台というだけあって,今まで食べた牛タンの中で一番美味かった.誘われてライブに行くことになったけど,メンバーの中の「工藤遥」って子可愛いなぁぐらいにしか思ってなかった.

だけど実際にライブが始まってみるとモーニング娘。半端ない.めっちゃかっこよくて女性ファンが多い理由もわかった.リアル工藤遥を遠目だけど目にしてしまい,ハマることになる.

セキュリティキャンプ全国大会2017の応募用紙

f:id:ywkw1717:20171231165157p:plain

5月はなんといってもこれ.1ヵ月丸々費やしていて,GWで帰省した実家でも大学でも自宅でも,ひたすら応募用紙を書いていた.応募できるのは年齢的に最初で最後だし,異常なほど時間を費やしていて,毎日頭から離れなかった.

最初は,共通問題を埋めながら選択問題のA-4を進めていた.そしてその後A-6に1週間かけて取り組み,PEファイルをパースするプログラムをCで書いた.最後に残しておいたA-5がかなり難しくて,上に貼った画像がそれなんだけど,わからないことが多すぎて辛かった.けど,調べて実践しての繰り返しで試行錯誤しまくり,その過程を応募用紙で熱く語った.文字数的には5万字オーバーで作文用紙100枚以上書いていたわけで,今振り返ると異常.

6月

地元の風景を描いた

f:id:ywkw1717:20171231171722p:plain

地元の風景をモチーフに,絵を描いた.友達が描いた絵とか見ると自分の絵の雑さや粗さが目立つので,もっと綺麗に描けるようになったらなぁとか思ったりはする.これ以来絵を描いていなくて引退しつつあるが,描きたいと思ったときに描くのがちょうどいいのかも.

セキュリティキャンプ格通

f:id:ywkw1717:20171231172046p:plain

合格した!!!!!!!!!!!!!!

確かメールが来たのは大学で講義を受けているときで,嬉しすぎて講義の内容とか1ミリも入ってこなかった.一緒に応募していた友人も受かっていて,最高だった.

7月

逆求人イベント(渋谷)

f:id:ywkw1717:20171231172225p:plain

初めての逆求人イベント.よね君に紹介されて渋谷へ.画像は渋谷で朝に食べたオシャレパンケーキ.

参加したことない身としては,あ~逆求人とか最近よく聞くね~ってぐらいの感覚で,具体的にどんなことをやっているのか全く知らなかった.参加してみるといろんな企業の人事やエンジニアと話せて自分のアピールができて,またそれに対するアドバイスを貰えるので,自己分析とかしたことがなかった自分としては,客観的な意見はとても貴重だったし,1度は参加したほうがいいと思うイベントだった.

エフスタ!!勉強会&東北情報セキュリティ勉強会

techplay.jp

猪俣さんの話ではRSAの話はサイドチャネル攻撃の話があり,また法林さんからはコミュニティの運営側の話を聞くことができ,勉強になった.郡山で開かれたのも良くて,交通費が東京ほどかからないので,是非もっといろんなイベントを開催してほしい.

8月

SECCON2017 x CEDEC CHALLENGE

大学院の先輩含めた3人で wabisabi from ISM というチームを組み,参加した.序盤は順調だったけど,本題であるゲームアプリの解析は難航していて,簡単なチートしかできなかったのが悔やまれる.それでも6位とかで,参加したチームは40以上いたらしいので,意外と順位は上のほうだったっぽい.キャンプが始まって時間が取れなかったので,最終日に解析結果を先輩に丸投げしてスライド作成をお願いしてしまったので,来年はもっと計画的にやりたい.

セキュリティキャンプ全国大会2017

f:id:ywkw1717:20171231175045p:plain

ywkw1717.hatenablog.com

キャンプどうだった?と聞かれるとき,毎回決まって「人生が変わるキャンプ」と雑に応えてるけど当然嘘は言ってなくて,本当にそれぐらいすごいイベントで,一言で言い表すなんてできない.参加者は技術的にずば抜けた人や頭がキレる人とか,人としてスペック高いやろみたいな人達ばかりで,圧倒された.もっともっと成長していきたい.

株式会社ラック インターン

ywkw1717.hatenablog.com

マルウェア解析をした.それとJSOCを見学させて貰えて,とても綺麗だった.

下手なことは書けないためか,文章が出てこない.

Katagaitai CTF 勉強会

f:id:ywkw1717:20171231175024p:plain

恒例のKatagaitai勉強会.ReversingとWebをやって,Reversingはbataさんが担当で Hack.lu CTF 2012 Reversing 500 DonnBeach をやった.ついていくのに必死で難しかった.セキュキャン勢と早めの再開を果たし,一緒にご飯食べたりした.午後はWebでやぎはしゅさんが担当.ひたすらXSSしていた.

9月

男10人で車を借りて富士急へ

f:id:ywkw1717:20171231175449p:plain

f:id:ywkw1717:20171231175508p:plain

大規模遠征.静岡の旅館に泊まってから富士急へ.富士急は初めてでワクワクしながら行ったんだけど,アトラクション系が全部乗り物酔いの要素が含まれていて辛かった.特にトンデミーナとかいうアトラクションは面白要素無いし,ただひたすらに酔わされて,人生最悪な乗り物だった.でも絶叫系は普通に楽しかったし,最後に高飛車に乗るかどうかで渋って,結局乗ってみたら綺麗な夜景をみることができて最高だった.次の大規模遠征はどこになるだろう.

SECCON Beginners 2017 仙台

ywkw1717.hatenablog.com

CTF初心者ではないけどWebを全く知らなかったり,他にも参加したい理由があって仙台へ.ステッカーが貰えたので早速貼ったりした.

10月

応用情報技術者試験 再び

再びの受験.今度は前回受けた時の知識が貯金として脳内にあるだろうと思って,残り3週間切ったときから勉強を始めた.当日は午前はまぁまぁ,午後は30分ぐらい余ったりして合格を確信するぐらいは手応えがあった.結果受かってたので,早く奨励金の2万円が欲しい.

11月

A(izu)LT 0x09

www.slideshare.net

「シェルを起動するまほうのことば」で発表した.他の発表者のレベルが高くてビビった.

仙台CTF 2017

f:id:ywkw1717:20171231181404p:plain

仙台でフォレンジックの講義を含めたCTFがあるということで,参加した.参加者は30人弱ぐらい.Beginnersみたいな感じで,講義と個人戦のCTFがセットになっていた.Top3にはトロフィーなどがあって,少しはやっているんだし取れるっしょとか思って挑んだら,後半で失速してしまい,8位という悔やまれる結果に.Webの問題は1問目がわからないと,それ以降の問題が解けないようになっていて,1問目がわからずタイムアップ.解答は id:admin , password:admin で入れちゃうということで, id:adminSQLインジェクションを考えていた自分は「エスパー問題かよw」とか思ったけど,実際にそういう事例があるらしい.一緒に参加していたkobadlveは2位に入っていて流石だった.

Hardening 2017 Fes

f:id:ywkw1717:20171231181640p:plain

バイト先が4年前に優勝しているらしく,また参加することになって社員さんに誘われて参加した.全力で競技に挑み,全力で観光し,本当に充実した4日間だった.まだまだ技術力不足なのを痛感.

人生で初めて4日連続で温泉に入るということを経験した.明石海峡大橋を見ながら入る露天風呂とか最高だったし,淡路島よい.

12月

SECCON 2017 Online予選

f:id:ywkw1717:20171231182012p:plain

ywkw1717.hatenablog.com

Qubic Rubeの印象が強すぎる.Web強いマンがいたら + あと1,2問解けていたらもしかしたら国内決勝には出れたっぽくて,悔しいので来年また参加する.

Advent Calendar

ywkw1717.hatenablog.com

ywkw1717.hatenablog.com

2つのAdvent Calendarに投稿した.

Google Home Mini が家にきた

f:id:ywkw1717:20171231182323p:plain

バイト先のプレゼン大賞で,自分がやったアセンブラ短歌のやつとルービックキューブのプレゼン2つが同率1位になり,Google Home Mini を貰うことができた.まだ,天気とか音楽とかでしか使ってないので有効活用していきたい.

まとめ

大晦日1日で一気に書いたのでなにか抜けているイベントがありそうだけど,画像欄見る限りはこれぐらいっぽい.

今年は,"経験"というインプットを大量にできた年だったと思う.逆に言うと技術書とかをあまり読めてなくて,Webの情報からのインプットが多かったためにどうしても部分的なものになってしまい,体系的に学ぶということができていなかった.

だけど,成長という面で見たらかなりでかくて,ある程度経験を積むことで大局的な視点を持つことができたし,今自分に足りないものがなんなのかを見極めることができた.

去年のブログで,こうなったらいいなぁという目標っぽいのを書いたらその通りになったので今年も書いておくと,来年は経験より知識としてのインプットを増やしていきたい.当然アウトプットもしないと定着していかないと思うのでそちらも重視するけど,とにかく本を読もうと思った.それと,英語もどうにかしたい.最近話すにしても書くにしても,なにかと英語に触れる機会が多くなってきたので,英語力の向上が必要っぽい.

今年が充実しすぎていたので来年下がりそうで心配だけど,なんとかやっていきたい.

Smart ContractをデプロイするときにEVMで行われていること

この記事は Aizu Advent Calendar 2017 - Adventar 21日目の記事です.

前の人は id:ktr_0731 で,

syfm.hatenablog.com

次は id:slme9364 です.

slme9364.hatenablog.com

はじめに

去年に引き続き2回目の参加ですが,今回は最近某所でEthereumを触ることがあり,その際疑問に思ったことを掘り下げていこうと思います.レイヤーが低めの話になります.

今回やること

Ethereumの内部でSmart Contractというものが使われていますが,これをブロックチェーン上へデプロイする時って基本的にはsolcでコンパイルしてからgethで

> eth.contract(abi).new({ from: ..... , data: ...... , gas: ..... })

とかでトランザクションを発行します.この時にdataに指定しているものってEVM bytecodeと呼ばれる16進数の列なわけで,大体こんな感じ

606060405260006001600061010...................................

になっていると思います.

また,他のユーザーに自身の作成したコントラクトを利用してもらうために、「コントラクトのアドレス」と「ABI」の2つ情報を伝えて初めて他のユーザーがデプロイされたコントラクトを使うことができるわけですが,実は利用者はコントラクトのコードに対して以下のようにアクセスすることができます.

> eth.getCode(コントラクトのアドレス)
"0x606060405260006001600061010...................................

そこでふと思ったのが,デプロイされたEVM bytecodeをリバースエンジニアリングし,コンパイルされる前のSolidityで記述されたファイルまで戻すことはどこまで可能なのか?ということです.

と言っても,EVMで使われているアセンブリも知らない初心者がリバースエンジニアリングをしようとしても詰むだけなので,今回はコントラクトをデプロイするときの処理を中心に,EVMの中でなにが行われているのか,EVM assemblyを読むことで明らかにしていきたいと思います.コントラクトを書く言語は他にもあるらしいですが,今回はとりあえずSolidityでやります.

EVMで使われるデータ構造や命令について

まず,EVM内ではどのようなデータ構造が使われているのか,どのような命令(オペコード)が使われているのか,などを簡単に調べていきます.

Ethereumの仕様書でYellow Paperと呼ばれるものや,あとはEthereumのwikiとかが参考になります.

github.com

github.com

まずデータ構造ですが,各命令はデータを保存するために以下の3つの領域を使います.

  • stack
  • memory
  • storage

stack は普通のスタックだったので省略し, memory は「無限に拡張可能なバイト配列」です.storagestackmemory と違い,長い期間存続するkey/valueストアです.

また,内部で使われている命令ですが,これは Yellow Paperの23ページに全て載っているので割愛します.

やってみる

まずは以下のようなSmart Contractから始めていきます.

pragma solidity ^0.4.0;

contract HelloWorld {
}

なにもしない空のコントラクトです.これを,以下のようにアセンブリを出力するようにして,読んでいきます.

$ solc --optimize --asm HelloWorld.sol > HelloWorld.sol.dis
======= HelloWorld.sol:HelloWorld =======
EVM assembly:
    /* "HelloWorld.sol":25:48  contract HelloWorld {... */
  mstore(0x40, 0x60)
  jumpi(tag_1, iszero(callvalue))
  0x0
  dup1
  revert
tag_1:
  dataSize(sub_0)
  dup1
  dataOffset(sub_0)
  0x0
  codecopy
  0x0
  return
stop

sub_0: assembly {
        /* "HelloWorld.sol":25:48  contract HelloWorld {... */
      mstore(0x40, 0x60)
      0x0
      dup1
      revert

    auxdata: 0xa165627a7a72305820a6b4f23e09acc57a4a971849c21344d45e014c2944a13821c5804492f1c50c720029
}

また,比較のために以下のようなコントラクトも用意します.

pragma solidity ^0.4.0;

contract HelloWorld {
  uint data;

  function HelloWorld() {
    data = 0xdeadbeef;
  }
}

これもアセンブリを出力してみます.

======= HelloWorld2.sol:HelloWorld =======
EVM assembly:
    /* "HelloWorld2.sol":25:115  contract HelloWorld {... */
  mstore(0x40, 0x60)
    /* "HelloWorld2.sol":63:113  function HelloWorld() {... */
  jumpi(tag_1, iszero(callvalue))
  0x0
  dup1
  revert
tag_1:
    /* "HelloWorld2.sol":98:108  0xdeadbeef */
  0xdeadbeef
    /* "HelloWorld2.sol":91:95  data */
  0x0
    /* "HelloWorld2.sol":91:108  data = 0xdeadbeef */
  sstore
    /* "HelloWorld2.sol":25:115  contract HelloWorld {... */
  dataSize(sub_0)
  dup1
  dataOffset(sub_0)
  0x0
  codecopy
  0x0
  return
stop

sub_0: assembly {
        /* "HelloWorld2.sol":25:115  contract HelloWorld {... */
      mstore(0x40, 0x60)
      0x0
      dup1
      revert

    auxdata: 0xa165627a7a7230582008092ae2364624e84432682e083b5c6bad044762c66a05fae7e553e4ce9258460029
}

一番最初のコントラクトを Hello World1 ,コンストラクタで0xdeadbeef を代入するほうを Hello World2 とすると,2では

/* "HelloWorld2.sol":98:108  0xdeadbeef */
0xdeadbeef
/* "HelloWorld2.sol":91:95  data */
0x0
/* "HelloWorld2.sol":91:108  data = 0xdeadbeef */
sstore

このような命令が追加されていて,これが

function HelloWorld() {
  data = 0xdeadbeef;
}

この部分にあたります. sstore という見慣れない命令がありますが,これがどういう命令なのかをYellow Paperで調べてみると Save word to storage. と書かれていました.つまり,この処理ではkey/valueストアであるstorageに 0x0 をkeyとして 0xdeadbeef (正しくは0xdeadbeefを32byteに拡張したもの)を保存しているということになります.

また,0xdeadbeef0x0 と値だけ書かれているのはpush命令が省略されて書かれていて,実際にEVM bytecodeを見てみるとこの処理は

63deadbeef600055

このようなバイト列になっています.63が push4 にあたるので, 63deadbeefpush4 0xdeadbeef , 60が push1 なので 6000push1 0x00 になり,最後の55が sstore です.

これを見るとEVMではビッグエンディアンが使われているということもわかります.

コンストラクタ以外の部分について

コンストラクタの data = 0xdeadbeef にあたる命令は

0xdeadbeef
0x0
sstore

であり, sstore(0x0, 0xdeadbeef) であることはわかったものの,他の大部分のコードは未だ謎です.これらは一体なんなのでしょうか.そもそも,EVM bytecodeをデプロイしてそれがEVM上で実行されるためには,デプロイするコード(EVM上へ登録するためのコード)が必要なはずです.コンストラクタが存在しているということ以外は Hello World1Hello World2 に違いはないので,その他のコードはデプロイするために必要なコードである可能性が高いです.

ということで,上から命令を地道に追っていきます.以下では Hello World1 (なにもしないコントラクト)を使っていきます.

コントラクトがデプロイされる処理を追う

EVM assembly:
    /* "HelloWorld.sol":25:48  contract HelloWorld {... */
  mstore(0x40, 0x60)

まずは mstore(0x40, 0x60) という命令から始まっています. mstoreSave word to memory. であり,この場合 0x40 の場所に 0x60 (正確には0x60を32byteに拡張したもの)を保存するという命令になります.実際にどのような実装になっているのか,go-ethereumを見てみます.

github.com

go-ethereum/core/vm/instructions.go の493行目にあります.

func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
    // pop value of the stack
    mStart, val := stack.pop(), stack.pop()
    memory.Set(mStart.Uint64(), 32, math.PaddedBigBytes(val, 32))

    evm.interpreter.intPool.put(mStart, val)
    return nil, nil
}

mstore を呼ぶときのstackの状態は

 0x40
+-+-+
 0x60
+-+-+

このようになっていて,1回目の stack.pop()0x40mStart に入り2回目の stack.pop()0x60val に入ります.その後memoryに保存するのですが,その際 math.PaddedBigBytes を使って val を32byte拡張しています.そして, go-ethereum/core/vm/memory.go の32行目のSet

func (m *Memory) Set(offset, size uint64, value []byte) {
    // length of store may never be less than offset + size.
    // The store should be resized PRIOR to setting the memory
    if size > uint64(len(m.store)) {
        panic("INVALID memory: store empty")
    }

    // It's possible the offset is greater than 0 and size equals 0. This is because
    // the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP)
    if size > 0 {
        copy(m.store[offset:offset+size], value)
    }
}

これが呼ばれ,最終的に copy(m.store[offset:offset+size], value) でstoreされるという処理になっています.

次ですが,

  jumpi(tag_1, iszero(callvalue))

jumpiiszero , callvalue など見慣れない命令が出てきました.

jumpiConditionally alter the program counter と書かれていて要するに条件分岐命令であり,その後の説明を見る限り第2引数が0でなければ第1引数へ飛ぶ命令っぽいです.また, iszero はスタックから1つpopして0であれば1,そうでないなら0をスタックへpushする命令です.では callvalue がなんなのかですが,正直よくわかってません.実際のコードは以下のようになっているのですが,

func opCallValue(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
    stack.push(evm.interpreter.intPool.get().Set(contract.value))
    return nil, nil
}

contract.value がなにを指すのかがわからず...とにかくこの値が0になっていないと iszero により1がセットされないので,0であるべき値なのは確かなのですが...

では次に,tag_1へ飛んだ先の処理を追っていきます.

tag_1:
  dataSize(sub_0)
  dup1
  dataOffset(sub_0)
  0x0
  codecopy
  0x0
  return
stop

まず, dataSize(sub_0) ですがこれば実際のバイト列を見てみると 6035 のようになっていて,今回の場合は push1 0x35 だということがわかります.この 0x35 って何かというと sub_0 でラベル付けされたルーチンのバイト数であり,つまりここでは sub_0 のサイズがstackへpushされているということになります.次の dup1 ですが,これは Duplicate 1st stack item. と説明がある通りstackの1番目の要素を複製します.ここでは,現在stackへ積まれている 0x35 が複製されるということです.

次に, dataOffset(sub_0) ですがこれも実際のバイト列を見てみると 601b のようになっていて, push1 0x1b だということがわかります.この 0x1b は先頭から sub_0 までのオフセットを指していて,このオフセットをstackへ積んでいます.その後 0x0 をpushして codecopy を実行するわけですが,この時のstackの状態を整理してみると以下のようになっています.

 0x0
+-+-+
 0x1b
+-+-+
 0x35
+-+-+
 0x35
+-+-+

この状態で codecopy を呼ぶとどのように実行されていくのか, go-ethereum/core/vm/instructions.go の408行目を見ていきます.

func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
    var (
        memOffset  = stack.pop()
        codeOffset = stack.pop()
        length     = stack.pop()
    )
    codeCopy := getDataBig(contract.Code, codeOffset, length)
    memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)

    evm.interpreter.intPool.put(memOffset, codeOffset, length)
    return nil, nil
}

今回の場合, memOffset0x0 が入り, codeOffset0x1blength0x35 が入ります.そして, getDataBig での結果が codeCopy に入るわけですが, getDataBiggo-ethereum/core/vm/common.go で宣言されていて

// getDataBig returns a slice from the data based on the start and size and pads
// up to size with zero's. This function is overflow safe.
func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
    dlen := big.NewInt(int64(len(data)))

    s := math.BigMin(start, dlen)
    e := math.BigMin(new(big.Int).Add(s, size), dlen)
    return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
}

スライスを返す関数だとわかります.つまり,コントラクト全体のコードから sub_0 にあたるコードだけを取り出してそれを返します.その後, memOffset の位置 ( 0x0 )に sub_0 のコードを格納しています.

codecopy の後は 0x0 をpushして return ですが, これはメモリオフセットとサイズの2つの値をstackからpopし,memory.GetPtr での返り値を返すような処理になっています.

func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
    offset, size := stack.pop(), stack.pop()
    ret := memory.GetPtr(offset.Int64(), size.Int64())

    evm.interpreter.intPool.put(offset, size)
    return ret, nil
}

memory.GetPtr は以下です.

// GetPtr returns the offset + size
func (self *Memory) GetPtr(offset, size int64) []byte {
    if size == 0 {
        return nil
    }

    if len(self.store) > int(offset) {
        return self.store[offset : offset+size]
    }

    return nil
}

呼び出し元へ戻ってからも後は revert しているぐらいなのですが, revert の説明が Yellow Paper に無くて,このサイト で見つけたので,後から追加された命令なんでしょうか.

ここまで説明した処理がコントラクトをデプロイする処理にあたります.

evm --debugでコントラクトがデプロイされる処理を見てみる

恐らくethereumをインストールした際に一緒に入ってくるコマンドだと思うんですが, evm というコマンドがあり,コマンドラインでいろいろできるやつです.

f:id:ywkw1717:20171221002742p:plain

これを使うと任意のEVM bytecodeをコマンドライン上で実行してくれて, --debug オプションを付けておくとstackやmemory,storageの情報を表示してくれるので,これを使って上記の処理を追うとさらにわかりやすいかと思います.では,今度は Hello World2 のコードを使っていきます.

$ evm --debug --code 60606040523415600e57600080fd5b63deadbeef60005560358060236000396000f3006060604052600080fd00a165627a7a723058201ccb8cf1608dcc548e886094db68d49551c88613cccdcfb5b89884e4028ae32f0029 run

上から順番に見ていきます.

f:id:ywkw1717:20171221004759j:plain

現在のプログラムカウンタやgasの量,それぞれにかかるコストも表示してくれています.

なにをpushしているのか書かれていないのでわかり辛さもありますが, mstore を実行する時には 0x400x60 がそれぞれ積まれているのがわかります.また, mstore によって 0x40 の位置に 0x60 を32byte拡張したものが格納されるので,赤く丸をしたところに60という数字が現れています.jumpする直前まで飛ばします.

f:id:ywkw1717:20171221005255p:plain

jumpi が実行されるときには jump先のオフセットと iszero(callvalue) の返り値である1がstackへ積まれています. jumpi の次は jumpdest となっていますが,このようにjumpする直前では実は Mark a valid destination for jumps. This operation has no effect on machine state during execution. と説明が書かれている jumpdest 命令が実行されています.

tag_1へ移動してからは,まずコンストラクタの処理である「 0xdeadbeefdata に代入」があります.

f:id:ywkw1717:20171221010315p:plain

sstore によって,32byte拡張された0をkeyにvalueである32byte拡張された 0xdeadbeef がstorageへと保存されているのがわかります.その後, codecopy まで処理は進んでいき,

f:id:ywkw1717:20171221010824j:plain

codecopy が実行された直後のmemoryを見てみると, sub_0 のコードがmemoryへ展開されているのがわかります.

いくつかの疑問点

コントラクトをデプロイする過程を追ってきましたが,いくつか疑問が残っています.

  1. mstore(0x40, 0x60) とは何のためにあるのか
  2. sub_0 のラベルが付いているルーチンはなんなのか
  3. sub_0 に書かれている auxdata とは

とりあえずこの3つについて書いていきます.

mstore(0x40, 0x60)について

まず, 0x40 というのは特別な場所らしく free memory pointer という空きメモリへのポインタを保存しておく場所です.この命令から始まることで, 0x40 の位置に 0x60 という値を入れておき, 0x60 が空いているよと伝えることができます.また, 0x60mstore を使って保存した値をもし保持しておきたい場合は free memory pointer を他の値で更新し,次はその場所を使うようにしたりと,そんな風に使うっぽいです.

ですが,これって慣習として”そういう風に使う領域”と言われているだけで,書き込みが禁止されていたりとかはないんですよね.ですので,コントラクトの本体コードが大きいものをデプロイした場合,↑で見た処理の通り sub_0 がmemoryの 0x0 に保存されるので, 0x40 にある free memory pointer はすぐ上書きされてしまうわけで.

結局よくわからなくなったので,もう少し調べてみる必要がありそうです.

sub_0のラベルが付いているルーチンについて

これは恐らく,コンストラクタ以外のコントラクトの関数が含まれる箇所で,例えば getset だけのシンプルなコントラクトにした場合, getset にあたる処理は sub_0 へ入ります.もう少し大きなコントラクトで試した場合も sub_? というラベルは sub_0 しか出てくることはなかったので,自分はコンストラクタ以外の関数(処理),つまりコントラクトの本体が含まれる箇所だと解釈しています.

sub_0に書かれている auxdata とは

これはググっても本当に情報が出てこなくて,恐らく auxdata という呼称を使っているのは Solidity なので,Solidity のソースコードを探ってみました.ちなみに auxdata という言葉自体は auxiliary data の略称であり,補助データという意味です.

github.com

solidity/libevmasm/Assembly.h の158行目にこうありました.

/// Data that is appended to the very end of the contract.
bytes m_auxiliaryData;

説明は書かれておらず,コントラクトの最後に追加されるデータってそんなことわかっとんねんと思いながら他を探しました.

solidity/libsolidity/codegen/Compiler.cpp の39行目.

m_runtimeContext.appendAuxiliaryData(_metadata);

とあり,この _metadata

void Compiler::compileContract(
    ContractDefinition const& _contract,
    std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts,
    bytes const& _metadata
)
{
..........................

この関数の引数として渡ってきます.結局 metadata でしかないのはわかるんですが,これが何のためにあって,どういう使われ方をするのかはよくわかりませんでした.

auxdata: 0xa165627a7a7230582") == 0

コードのテストにはこのような1行もあり,決まったフォーマットであることもわかるのですが,正体が謎です.改ざんされていないか検証するための値とかでしょうか.

Porosityについて

今年のDEFCONでPorosityというEthereum Smart Contractsのデコンパイラが発表されました.なぜこんなリバースエンジニアリングツールが出てきたのかというと,そもそもSmart Contract自体に脆弱性が存在することがあるらしく,それを悪用して不正送金されたケースがあったとか.

で,セキュリティ対策としてコンパイルされる前のコードを静的解析するツールはあるものの,これは開発者がコードを提供することで初めて成り立つものです.セキュリティを意識しない開発者がそのままコンパイルしてブロックチェーン上へデプロイした場合はコードが安全であることを保証する方法が無く,もし新たに脆弱性が見つかった場合に開発者自信が元々のコードを保持・または共有していない限り,脆弱なコントラクトを特定するのは非常に難しくなります.

というような内容が書かれているPorosityを紹介しているサイトがあるので,詳しくはこちらをご覧ください.

blog.comae.io

ということで,このツールを少しだけ使ってみます.足りないパッケージがあったりしたので,インストール方法を載せておきます.

インストール ( Ubuntu16.04 )

$ git clone https://github.com/comaeio/porosity.git
$ cd porosity
$ sudo apt-get install libboost-all-dev
$ cd porosity/porosity
$ wget https://raw.githubusercontent.com/chriseth/solidity/0a2a2cf38b7794826a17efe44aea5dc96de98dc7/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp
$ porosity.hの中の `#include "Common.h"``#include <boost/dynamic_bitset.hpp>` の上に移動
$ make

以下のようなコントラクトで試してみます.

pragma solidity ^0.4.0;

contract Aizu {
  uint data;

  function set(uint input) public {
    data = input;
  }

  function get() public constant returns (uint) {
    return data;
  }
}

実行.

$ abi=`cat output/Aizu.abi`
$ code=`cat output/Aizu.bin`
$ porosity --abi $abi --code $code --decompile --verbose 0
Porosity v0.1 (https://www.comae.io)
Matt Suiche, Comae Technologies <support@comae.io>
The Ethereum bytecode commandline decompiler.
Decompiles the given Ethereum input bytecode and outputs the Solidity code.

Attempting to parse ABI definition...
Success.
Hash: 0x60FE47B1
executeInstruction: NOT_IMPLEMENTED: REVERT
function set(uint256) {
      if (!msg.value) {
      }
      store[var_LK0e1] = arg_4;
      return;
}


LOC: 6
Hash: 0x6D4CE63C
executeInstruction: NOT_IMPLEMENTED: REVERT
function get() {
      if (!msg.value) {
      }
      return;
      return;
}


LOC: 6

んー...どうなんですかね. set は大体戻せていますが, get は最後 return が2つ出ていたりして怪しいです.GitHubを見てみるとサンプルでは自明な脆弱性があるところは教えてくれたりするらしいですが,まだまだ発展途上というところでしょうか.

まとめ

gethの使い方!とか基本的なセットアップとかの情報は日本語でも増えてきたものの,もっと深い実装寄りの話においては日本語の記事はほとんど存在していなくて,英語の記事もまとまっているものは少なくて部分的だったり,情報を探すのに本当に苦労しました. でも,情報がここまで多くない時代ってそれが当たり前で,皆さんいろいろ探りながらブログとかへまとめていったんだなぁと思うと本当に先人に感謝だし,自分ももっと情報をアウトプットするべきだなと感じました.

以上ですが,何か間違いなどあればご指摘お願いします.

参照

2度目の退学を経て院進する話

この記事は 退学 Advent Calendar 2017 - Adventar 20日目の記事です.

前の人は@muscle_azosonさん,次は@showmeearさんです.

はじめに

この記事は,近況報告も含めた自分のこれまでの話みたいな,いわゆるポエムっぽいやつ.

4月から院進できそうだけど,その過程でいろいろ特殊なことが多くて,いっそ退学Advent Calendarとやらに公開しようってなった.

今の大学に入る前の話

元々は高校卒業後,別の大学に通っていた.実家から電車で通える私立大学.お金の問題で国公立か自宅から通える範囲の私立大学しか許されなくて,目指してた第1志望に落ちて入学した.

入学後のオリエンテーション新宿駅のバス停に現地集合だったけど迷路のような新宿駅で見事に迷い,間に合わずにサボった.結果,オリエンテーションが終わった後では既にグループが形成されていて.「あれ?ぼっちじゃね?」という事故が起きる.

それ以降,授業で1人の人を見つけ話しかけたりするも上手くいかず,唯一他の学科に1人だけ友達のような奴ができた.

友人関係はそんな感じだったんだけど,5月頃になると講義に違和感を覚えるようになった.

まずプログラミングの講義.無難にC言語だったけど,最早講義じゃない.テキストがあるのでそれ通りに自分で進めて課題をこなし,わからないところがあればTAが教えてくれるような自主学習に近い形態だった.確か教授は課題の問題をごく稀に前で解説してくれるだけ.ほぼいる意味がない. 次に英語の講義.本当に中学英語のようなことしかやらなかった記憶がある.思い出せないけど,いろいろアレだった.

ここで不満を言ってもしょうがないのでやめておくけど,そんなこともあって徐々にもう一度大学受験がしたいみたいな感情が湧いてきた.

実際に行動に移したのは6月下旬か7月頃だった気がする.親に話したら一回だけならって事で許してくれた.本当に感謝すぎる.そこから,休学届を出したのが8月頃.勉強を始めたのは8月下旬に差し掛かる頃.ここで,休学すると奨学金が止まるということや,他の大学に合格して入学した場合,借りていた半期分の奨学金は支払いが猶予されるということなど,奨学金に関する知見を得た.

その後は予備校に通うお金もないので家に引きこもって勉強したり,図書館を使ってみたり,模試やその他かかるお金をバイトして稼いだり,そんな感じ.で,元々目指してたところいけるぐらいにはセンター試験の過去問で点数が取れるようになったけど,いざ本番受けてみたら無事死亡した.

そこで出てきたのが今の大学だった.高校時代も存在は知ってたけど,関東の大学しか行くつもりはなかった.けど,結局受験して合格して入学した.

前の大学には,受験して合格したのでそっち行きますって正直に伝えた.休学理由は,お金が無いので授業料を稼ぐって話だったので嫌な顔されるかと思ったけど,そんな事はなくてむしろおめでとうとか頑張ってねとか言って貰えたので,あざっすって感じだった.最後に担任みたいな存在の教授と謎の握手を交わしたのが面白くて覚えてる.友達はほぼいなかったようなもんなので,そこら辺のことを気にする必要もなく綺麗サッパリ退学した.

これが1度目の退学.

今の大学に入学後

反省を活かし,オリエンテーションは死ぬ気で行くぞと思って行った.友人を頑張って作った.

プログラミングの講義や英語の講義に関しては,ほぼ文句がなかった.大学の講義の質がどうとか言う人はいるけど,自分にとっては以前の大学に比べたら良質な講義だと思った.プログラミングの講義は進むのが遅いかもしれないけど,講義と演習のバランスは取れてると思ったし,できる人はスキップテストが用意されてるので受ければいいし,英語は教員が外国人なので頑張って聞き取る必要があった.

そんな感じで真面目に受けていて,この頃は親への申し訳なさと自分は1年遅れているってのを妙に意識して,それでモチベーションを維持していた気がする.

GPAもそれなりに気にしていた.予習復習なんて事は流石にしてなかったけど,課題は真面目に出していた.他の時間は技術書読んだりだとか,そういう事にあてたほうが有意義だと思う.

いろいろ有利になるからって事で頑張ってたけど,研究室の選考にも使えなかったし,頑張った意味みたいな感じだった.

けど,徐々に5年一貫教育プログラムというものに興味を持った.4+1や3+2とか言われているやつで,学部4年+大学院1年っていうAと,学部を3年で中退して大学院2年っていうB1,学部を3年で卒業して大学院2年っていうB2がこの大学にはある.このプログラムはGPAが特に関係してくる.

1年早まるっていうのもそうだけど,給付型の奨学金を貰いながら大学院に通えるらしいというのに惹かれた.前の大学で借りてた奨学金もあったし,院進はお金が足りないって問題がそれで解決した.

そこからはGPAの最低ラインを守って講義を受けて,結果B1のプログラムに申し込むことができた.

申し込んで1ヶ月後の10月中旬頃に,認定書みたいなのを貰えた.あと退学願も貰った.大学院受かったら提出してくださいということらしい.

あと,大学院では研究室を変えることにした.理由は今の指導教員が自分のやりたい分野の人ではない為.やりたい事をやらせてくれると言ってくれてたけど,やっぱりある程度のサポートをしてくれる教員じゃないと厳しいと思う.研究室を変更する過程で,奨学金の推薦書を変更予定先の先生に書いてもらったり,いろいろ大変だった.

これで2度目の退学になる予定.

最後に

大学が肌に合わないと感じたらすぐに切り替えてもう一度受験し直すってのは有りで,変えて良かったと思う.

あと,GPA意識してめっちゃ頑張るんじゃなくて"最低限"頑張っておくと後々役に立つ(かも)

この大学,いろいろすごい人が多いし,そういう人達の刺激を受けることができて本当に良いし,大学を変えて良かったとしみじみ思う.

とりあえず2月の入試に合格して,その後は最終学歴が高卒にならないようにちゃんと卒業したい.

SECCON 2017 Online CTF Writeup(Powerful Shell) & おまけ(Qubic Rube)

開催期間(JST)

12/09 PM3:00 ~ 12/10 PM3:00

結果

・チーム名:wabisabi

・得点:1200 pt

・順位:170/1028

解いた問題

・Powerful Shell (Binary 300)

取り組んだが解けなかった問題

・Qubic Rube (Programming 300)

はじめに

参加してました.日本チームがめっちゃ多くてびっくりしてました.チームメンバーの活躍で去年より順位が100位ぐらい上がってました.僕は結局Powerful Shellしか通せなくて,Qubic Rubeを後輩とやっていたのですが,あと2時間あれば解けただろうって感じで,37/50 ステージ目でタイムアップだったので悲しい.

以下,Powerful ShellのWriteupです.

Writeup

Powerful Shell (Binary 300)

最初はずっとQubic Rubeをやっていたのですが,スクリプトを他の人に書いてもらっているときに「なんか解けるやつないかなぁ」と思って解いたやつです.高得点なのに解いている人が多くて,それで着手しました.

Crack me. という問題文と共に以下のような,20546行のテキストが渡されます.

$ECCON="";
$ECCON+=[char](3783/291);
$ECCON+=[char](6690/669);
$ECCON+=[char](776-740);
$ECCON+=[char](381-312);
$ECCON+=[char](403-289);
$ECCON+=[char](-301+415);
$ECCON+=[char](143-32);
$ECCON+=[char](93594/821);
$ECCON+=[char](626-561);
..........................................................
$ECCON+=[char](7631/587);
$ECCON+=[char](137-127);
$ECCON+=[char](-905+918);
$ECCON+=[char](873-863);
$ECCON+=[char](721-708);
$ECCON+=[char](803-793);
$ECCON+=[char](10426/802);
Write-Progress -Activity "Extracting Script" -status "20040" -percentComplete 99;
$ECCON+=[char](520-510);
Write-Progress -Completed -Activity "Extracting Script";.([ScriptBlock]::Create($ECCON))

Power Shellのスクリプトだということを先に取り組んでいた@kobadlveから教えてもらい,その後も情報を貰いながら(Set-ExecutionPolicyでExecution Policyを変更する必要がある,など)Power Shellで動かすところまではいきました.@kobadlveは仮想マシンで取り組んでいたらしいのですが, Checking Host... というところで落ちてしまうということでした.自分のPCではちゃんと動いたので,環境の問題でしょうか.

上手く起動できると以下のような画面が現れます.

f:id:ywkw1717:20171210204911p:plain

僕はPCをミュートしていて後々気づくことになるのですが,鍵盤にある記号を入力すると音がでるようになっています.面白い.

まずPower Shellスクリプトから読み取れることとしては,ECCONという名前の変数にひたすらなにかを足していっているということです.Power Shellでは変数の前に$を付けて使うらしいですね.そして,最終的にそれを実行しているように見えます.

僕がまず行ったことはソースコードの抽出です.$ECCONには1文字ずつ足してスクリプトを書いていっているのだから$ECCONを別ファイルとかに書き出してしまえばソースコードは見れるのではないか?と考えました.

そこで,スクリプトの最後を以下のように変更しました.

$ECCON+=[char](803-793);
$ECCON+=[char](10426/802);
Write-Progress -Activity "Extracting Script" -status "20040" -percentComplete 99;
$ECCON+=[char](520-510);
#Write-Progress -Completed -Activity "Extracting Script";.([ScriptBlock]::Create($ECCON))

Out-File -InputObject $ECCON -FilePath C:\Users\ywkw1\Work\script.ps1

これで,script.ps1として$ECCONを保存できました.早速中身を見てみます.

$ErrorActionPreference = "ContinueSilently"
[console]::BackgroundColor = "black";[console]::ForegroundColor = "white";cls;Set-Alias -Name x -Value Write-Host;$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size 95,25;$host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.Size 95,25;$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size 95,25;$host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.Size 95,25;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x;x;

<# Host Check #>
Write-Host -b 00 -f 15 Checking Host... Please wait... -n
Try{
    If ((Get-EventLog -LogName Security | Where EventID -Eq 4624).Length -Lt 1000) {
        Write-Host "This host is too fresh!"
        Exit
    }
}Catch{
    Write-Host "Failed: No admin rights!"
    Exit
}
Write-Host "Check passed"

$keytone=@{'a'=261.63}
$pk='a'
ForEach($k in ('w','s','e','d','f','t','g','y','h','u','j','k')){
    $keytone+=@{$k=$keytone[$pk]*[math]::pow(2,1/12)};$pk=$k  
}
Write-Host -b 00 -f 15 "Play the secret melody."

Write-Host -b 15 -f 00 -n '   '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '   |   '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 '   |    ' 
Write-Host -b 15 -f 00 -n '   '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '   |   '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 '   |    ' 
Write-Host -b 15 -f 00 -n '   '
Write-Host -b 00 -f 15 -n ' w '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n ' e '
Write-Host -b 15 -f 00 -n '   |   '
Write-Host -b 00 -f 15 -n ' t '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n ' y '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n ' u '
Write-Host -b 15 -f 00 '   |    ' 
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00  '    '
Write-Host -b 15 -f 00 -n '  a |'
Write-Host -b 15 -f 00 -n '  s |'
Write-Host -b 15 -f 00 -n '  d |'
Write-Host -b 15 -f 00 -n '  f |'
Write-Host -b 15 -f 00 -n '  g |'
Write-Host -b 15 -f 00 -n '  h |'
Write-Host -b 15 -f 00 -n '  j |'
Write-Host -b 15 -f 00  '  k '
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00  '    '
Write-Host
$stage1=@();$f="";
While($stage1.length -lt 14){
    $key=(Get-Host).ui.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    $k=[String]$key.Character
    $f+=$k;
    If($keytone.Contains($k)){
        $stage1+=[math]::floor($keytone[$k])
        [console]::beep($keytone[$k],500)
    }
}
$secret=@(440,440,493,440,440,493,440,493,523,493,440,493,440,349)
If($secret.length -eq $stage1.length){
    For ($i=1; $i -le $secret.length; $i++) {
        If($secret[$i] -ne $stage1[$i]){
            Exit
        }
    }
    x "Correct. Move to the next stage."
}
$text=@"
YkwRUxVXQ05DQ1NOE1sVVU4TUxdTThBBFVdDTUwTURVTThMqFldDQUwdUxVRTBNEFVdAQUwRUxtT
TBEzFVdDQU8RUxdTbEwTNxVVQUNOEFEVUUwdQBVXQ0NOE1EWUUwRQRtVQ0FME1EVUU8RThdVTUNM
EVMVUUwRFxdVQUNCE1MXU2JOE0gWV0oxSk1KTEIoExdBSDBOE0MVO0NKTkAoERVDSTFKThNNFUwR
FBVINUFJTkAqExtBSjFKTBEoF08RVRdKO0NKTldKMUwRQBc1QUo7SlNgTBNRFVdJSEZCSkJAKBEV
QUgzSE8RQxdMHTMVSDVDSExCKxEVQ0o9SkwRQxVOE0IWSDVBSkJAKBEVQUgzThBXFTdDRExAKhMV
Q0oxTxEzFzVNSkxVSjNOE0EWN0NITE4oExdBSjFMEUUXNUNTbEwTURVVSExCKxEVQ0o9SkwRQxVO
EzEWSDVBSkJAKBEVQUgzThAxFTdDREwTURVKMUpOECoVThNPFUo3U0pOE0gWThNEFUITQBdDTBFK
F08RQBdMHRQVQUwTSBVOEEIVThNPFUNOE0oXTBFDF0wRQRtDTBFKFU4TQxZOExYVTUwTSBVMEUEX
TxFOF0NCE0oXTBNCFU4QQRVBTB1KFU4TThdMESsXQ04TRBVMEUMVThNXFk4TQRVNTBNIFUwRFBdP
EUEXQ0ITShdME0EVThBXFU4TWxVDThNKF0wRMBdMETUbQ0wRShVOE0MWThMqFU1ME0gVTBFDF08R
QxdMHUMVQUwTSBVOEEEVThNNFUwRNRVBTBFJF0wRQxtME0EVTBFAF0BOE0gVQhNGF0wTKhVBTxFK
F0wdMxVOEzUXQ04QSBVOE0AVTBFVFUFMEUkXTBFDG0wTQRVMETMXQE4TSBVCE0MXTBNBFU4QQRVB
TB1KFU4TQxdMEVYXTBEUG0NMEUoVThNBFk4TQRVCEygXQ0wRShdPEUMXTB1DFU4TQBdDThBIFU4T
SBVMESgVQUwRSRdMEUYbTBMWFUNOE0gWThNCFUITFBdDTBFKF08RQxdMHUMVThNVF0NOEEgVThNN
FUwRQxVOE0IWQUwRShtME0EVTBFVF08RQxdDQhNKF0wTQRVOEEEVThM9FUNOE0oXTBFFF0wRKBtD
TBFKFU4TQRZOE0EVQhNAF0NMEUoXTxFDF0wdVRVOEzMXQ04QSBVOE00VTBFVFU4TQRZBTBFKG0wT
RBVMESgXQE4TSBVCE0MXTBNBFU4QKhVBTB1KFU4TFBdMEUIXQ04TRBVMEUMVThNBFk4TNxVNTBNI
FUwRQxdPEUMXTB01FUFME0gVThBBFU4TTRVMERQVQUwRSRdMEUMbTBNBFUwRQxdAThNIFUITQxdM
E0EVThAxFUFMHUoVThNDF0wRVhdMEVUbQ0wRShVOE0QWThMWFU1ME0gVTBFDF08RRhdDQhNKF0wT
QRVOEFcVQUwdShVOE0EXTBFFF0NOE0QVTBFDFU4TVxZOEyoVTUwTSBVMETMXTxFVF0NCE0oXTBNE
FU4QQhVBTB1KFU4TQBdMERcXQ04TRBVMEUAVThNDFkFMEUobTBNCFUwRQRdAThNIFUITQRdMExYV
QU8RShdMHUEVThNOF0NOEEgVThNIFUwRKBVBTBFJF0wRMxtMEzcVQ04TSBZOE0EVQhNVF0wTQRVB
TxFKF0wdQxVOE0MXTBFFF0NOE0QVTBFGFU4TKhZBTBFKG0wTRBVMERQXQE4TSBVCE04XTBNXFUFP
EUoXTB0zFU4TThdDThBIFU4TTRVMEUMVThMWFkFMEUobTBNCFUwRFBdAThNIFUITQxdME0EVThAx
FUFMHUoVThNGF0wRQxdDThNEFUwRQRVOEyoWQUwRShtMEzcVTBFDF0BOE0gVQhMzF0wTFhVBTxFK
F0wdMxVOExQXQ04QSBVOE0gVTBEUFUFMEUkXTBEzG0wTQRVDThNIFk4TQRVCEygXTBNEFUFPEUoX
TB1DFU4TRhdDThBIFU4TTRVMEVUVQUwRSRdMERQbQ0wRShVOE0wWThNDFU1ME0gVTBFDF08RQxdM
HTMVQUwTSBVOEEEVThNbFUwRNRVBTBFJF0wRQxtME0EVTBFAF0BOE0gVQhNDF0wTVxVOEEEVQUwd
ShVOEzMXTBE2F0NOE0QVTBFBFU4TKhZBTBFKG0wTQRVMEUMXTxFDF0NCE0oXTBNBFU4QQRVOEzsV
Q04TShdMEUAXTBFDG0wTQhVDThNIFk4TRBVCEygXQ0wRShdPEUYXTB0UFUFME0gVThBDFU4TTRVD
ThNKF0wRQBdMEUMbTBNBFUNOE0gWThNBFUITQxdME0EVQU8RShdMHUMVThNVF0wRVhdDThNEFUwR
RhVOEyoWQUwRShtME0MVTBEzF0BOE0gVQhNDF0wTQRVOEEEVQUwdShVOExQXTBFNF0NOE0QVTBFG
FU4TRBZBTBFKG0wTRBVMERQXQE4TSBVCEzUXTBMWFUFPEUoXTB1DFU4TRhdDThBIFU4TTRVMEVUV
QUwRSRdMERQbQ0wRShVOE0wWThNDFU1ME0gVTBFDF08RQxdMHTMVQUwTSBVOEEEVThNbFUwRNRVB
TBFJF0wRQxtME0EVTBFAF0BOE0gVQhNDF0wTVxVOEEEVQUwdShVOEzMXTBE2F0NOE0QVTBFBFU4T
KhZBTBFKG0wTQRVMEUMXTxFDF0NCE0oXTBNBFU4QQRVOEzsVQ04TShdMEUAXTBFDG0wTQhVDThNI
Fk4TRBVCEygXQ0wRShdPEUYXTB0zFUFME0gVThBMFU4TSBVDThNKF0wRQxdMERQbQ0wRShVOE0IW
ThNDFU1ME0gVTBFAF08RQRdDQhNKF0wTQxVOEBYVQUwdShVOE0EXTBFNF0NOE0QVTBFDFU4TKhZO
E0QVTUwTSBVMEUYXTxFAF0NCE0oXTBNCFU4QFhVBTB1KFU4TQBdMEUIXQ04TRBVMEUAVThNDFkFM
EUobTBNDFUwRFBdAThNIFUITQRdME0wVQU8RShdMHUMVThMoF0wRNhdDThNEFUwRRhVOEzEWQUwR
ShtME0EVTBFGF0BOE0gVQhNDF0wTVxVBTxFKF0wdQxVOEygXTBE2FxROE10VShZOTBFTF2E=
"@

$plain=@()
$byteString = [System.Convert]::FromBase64String($text)
$xordData = $(for ($i = 0; $i -lt $byteString.length; ) {
    for ($j = 0; $j -lt $f.length; $j++) {
        $plain+=$byteString[$i] -bxor $f[$j]
        $i++
        if ($i -ge $byteString.Length) {
            $j = $f.length
        }
    }
})
iex([System.Text.Encoding]::ASCII.GetString($plain))

While($stage1.length -lt 14) でループして入力値を受け取り,その後のifとforでチェックして If($secret[$i] -ne $stage1[$i]) を通るようであればExitしているらしいです.まずはここを通らないようにする必要がありそうです.

まずsecretですが $secret=@(440,440,493,440,440,493,440,493,523,493,440,493,440,349) このように宣言されています.なんかドレミの周波数っぽいです.440はラですし.

$stage1に上記の通りに音階を準備したいところですが,どの鍵盤を叩けばラ(440)がでるかとかピアノやっている人なら鍵盤を見てわかるのでしょうか,僕はわからないので大人しくコードを読みました.

$stage1は $stage1+=[math]::floor($keytone[$k]) とあるので$keytoneと$kを見る必要がありますが,この直前に$kには入力値を入れていることがわかるので,$keytoneを見ていきます.

$keytone=@{'a'=261.63}
$pk='a'
ForEach($k in ('w','s','e','d','f','t','g','y','h','u','j','k')){
    $keytone+=@{$k=$keytone[$pk]*[math]::pow(2,1/12)};$pk=$k  
}

$keytoneは連想配列として定義されていて,どうやら'a'がドらしいです.$keytoneにはこれら ('w','s','e','d','f','t','g','y','h','u','j','k') をkeyにして値を追加していっています.

この処理を別ファイルに書き出して,最終的に$keytoneを出力してみました.

PS C:\Users\ywkw1\Work> .\keytone.ps1
a 261.63
w 277.187329377222
s 293.669745699181
e 311.132257498162
d 329.633144283996
f 349.234151046506
t 370.000694323673
g 392.002080523246
y 415.31173722644
h 440.007458245659
u 466.171663254114
j 493.891672853823
k 523.26

よって, (440,440,493,440,440,493,440,493,523,493,440,493,440,349) この順番になるように打つとすると, hhjhhjhjkjhjhf です.

早速打ってみると「さくらさくら」が流れて,以下のようにpasswordを求められるようになりました.

Correct. Move to the next stage.
Enter the password: hoge

再びスクリプトを見ていきます.

$plain=@()
$byteString = [System.Convert]::FromBase64String($text)
$xordData = $(for ($i = 0; $i -lt $byteString.length; ) {
    for ($j = 0; $j -lt $f.length; $j++) {
        $plain+=$byteString[$i] -bxor $f[$j]
        $i++
        if ($i -ge $byteString.Length) {
            $j = $f.length
        }
    }
})
iex([System.Text.Encoding]::ASCII.GetString($plain))

$textというbase64エンコードされた変数に対して,先ほどの正しい入力値を使ってxorで復号しているようです.その後,それを実行しているようなので,再びどんなコードが実行されているのか$plainを別ファイルへ書き出してみました.

すると,以下のような内容のファイルが.

${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;};
${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;};
${"}="["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]";
${;}="".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]);
${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"];"${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${(}${+}+${"}${&}${@}+${"}${+}${=}${+}+${"}${|}${)}+${"}${+}${=}${=}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${[}${]}+${"}${&}${=}+${"}${+}${+}${[}+${"}${+}${+}${+}+${"}${+}${=}${|}+${"}${+}${+}${@}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${(}${|}+${"}${+}${+}${=}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${+}${+}${[}+${"}${.}${@}+${"}${+}${+}${(}+${"}${+}${=}${[}+${"}${+}${=}${+}+${"}${.}${@}+${"}${+}${+}${@}+${"}${|}${)}+${"}${+}${+}${]}+${"}${+}${+}${]}+${"}${+}${+}${|}+${"}${+}${+}${+}+${"}${+}${+}${[}+${"}${+}${=}${=}+${"}${.}${|}+${"}${+}${.}+${"}${+}${=}+${"}${)}${.}+${"}${+}${=}${@}+${"}${[}${=}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${.}${@}+${"}${[}${]}+${"}${+}${=}${+}+${"}${+}${+}${.}+${"}${.}${@}+${"}${.}${|}+${"}${&}${=}+${"}${[}${&}+${"}${+}${+}${|}+${"}${(}${|}+${"}${+}${+}${[}+${"}${.}${(}+${"}${)}${@}+${"}${]}${+}+${"}${[}${|}+${"}${[}${|}+${"}${.}${|}+${"}${[}${+}+${"}${+}${@}${.}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${)}${+}+${"}${+}${+}${+}+${"}${+}${+}${+}+${"}${+}${=}${=}+${"}${.}${@}+${"}${)}${[}+${"}${+}${+}${+}+${"}${|}${&}+${"}${.}${.}+${"}${.}${|}+${"}${]}${|}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${[}+${"}${&}${.}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${.}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${]}+${"}${.}${[}+${"}${+}${.}+${"}${+}${=}+${"}${+}${@}${]}|${;}"|&${;}

なんだか凄いことになっていますが,記号プログラミングっぽいとわかります.

見やすいように整理します.

${;} =+$();
${=} =${;};
${+} =++${;};
${@} =++${;};
${.} =++${;};
${[} =++${;};

${]}=++${;};
${(}=++${;};
${)}=++${;};
${&}=++${;};
${|}=++${;};

${"} = "["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]";

${;} = "".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]);

${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"];"${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${(}${+}+${"}${&}${@}+${"}${+}${=}${+}+${"}${|}${)}+${"}${+}${=}${=}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${[}${]}+${"}${&}${=}+${"}${+}${+}${[}+${"}${+}${+}${+}+${"}${+}${=}${|}+${"}${+}${+}${@}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${(}${|}+${"}${+}${+}${=}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${+}${+}${[}+${"}${.}${@}+${"}${+}${+}${(}+${"}${+}${=}${[}+${"}${+}${=}${+}+${"}${.}${@}+${"}${+}${+}${@}+${"}${|}${)}+${"}${+}${+}${]}+${"}${+}${+}${]}+${"}${+}${+}${|}+${"}${+}${+}${+}+${"}${+}${+}${[}+${"}${+}${=}${=}+${"}${.}${|}+${"}${+}${.}+${"}${+}${=}+${"}${)}${.}+${"}${+}${=}${@}+${"}${[}${=}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${.}${@}+${"}${[}${]}+${"}${+}${=}${+}+${"}${+}${+}${.}+${"}${.}${@}+${"}${.}${|}+${"}${&}${=}+${"}${[}${&}+${"}${+}${+}${|}+${"}${(}${|}+${"}${+}${+}${[}+${"}${.}${(}+${"}${)}${@}+${"}${]}${+}+${"}${[}${|}+${"}${[}${|}+${"}${.}${|}+${"}${[}${+}+${"}${+}${@}${.}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${)}${+}+${"}${+}${+}${+}+${"}${+}${+}${+}+${"}${+}${=}${=}+${"}${.}${@}+${"}${)}${[}+${"}${+}${+}${+}+${"}${|}${&}+${"}${.}${.}+${"}${.}${|}+${"}${]}${|}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${[}+${"}${&}${.}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${.}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${]}+${"}${.}${[}+${"}${+}${.}+${"}${+}${=}+${"}${+}${@}${]}|${;}"|&${;}

最初いくつかの変数を宣言した後,最下行で実行しているっぽいです.末尾のパイプを使った |${;}"|&${;} 辺りで実行してそうなのでこれを削除してから,なにが実行されているのかを見るために Write-Host ${;} してみます.

PS C:\Users\ywkw1\Work> .\plain.ps1
[CHar]36+[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]61+[CHar]82+[CHar]101+[CHar]97+[CHar]100+[CHar]45+[CHar]72+[CHar]111+[CHar]115+[CHar]116+[CHar]32+[CHar]45+[CHar]80+[CHar]114+[CHar]111+[CHar]109+[CHar]112+[CHar]116+[CHar]32+[CHar]39+[CHar]69+[CHar]110+[CHar]116+[CHar]101+[CHar]114+[CHar]32+[CHar]116+[CHar]104+[CHar]101+[CHar]32+[CHar]112+[CHar]97+[CHar]115+[CHar]115+[CHar]119+[CHar]111+[CHar]114+[CHar]100+[CHar]39+[CHar]13+[CHar]10+[CHar]73+[CHar]102+[CHar]40+[CHar]36+[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]32+[CHar]45+[CHar]101+[CHar]113+[CHar]32+[CHar]39+[CHar]80+[CHar]48+[CHar]119+[CHar]69+[CHar]114+[CHar]36+[CHar]72+[CHar]51+[CHar]49+[CHar]49+[CHar]39+[CHar]41+[CHar]123+[CHar]13+[CHar]10+[CHar]9+[CHar]87+[CHar]114+[CHar]105+[CHar]116+[CHar]101+[CHar]45+[CHar]72+[CHar]111+[CHar]115+[CHar]116+[CHar]32+[CHar]39+[CHar]71+[CHar]111+[CHar]111+[CHar]100+[CHar]32+[CHar]74+[CHar]111+[CHar]98+[CHar]33+[CHar]39+[CHar]59+[CHar]13+[CHar]10+[CHar]9+[CHar]87+[CHar]114+[CHar]105+[CHar]116+[CHar]101+[CHar]45+[CHar]72+[CHar]111+[CHar]115+[CHar]116+[CHar]32+[CHar]34+[CHar]83+[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]123+[CHar]36+[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]125+[CHar]34+[CHar]13+[CHar]10+[CHar]125
iex

(僕はここで,[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]125+[CHar]34+[CHar]13+[CHar]10+[CHar]125 がフラグやんけ!って思ってASCII変換してでてきた SECCON{$ECCON} という文字列をsubmitして見事に間違えました.)

全て文字に直してみると,以下のようになりました.

$ECCON=Read-Host -Prompt 'Enter the password'
If($ECCON -eq 'P0wEr$H311'){
    Write-Host 'Good Job!';
    Write-Host "SECCON{$ECCON}"
}

$ECCONに入力値を流し込んで,それが P0wEr$H311 という文字列と等しければそれをフラグとして出力するようになっています.

ということで,フラグは SECCON{P0wEr$H311} でした.

取り組んだが解けなかった問題

Qubic Rube (Programming 300)

QRコードが6面に貼られた3x3x3のルービックキューブが出てきて,揃えて出てくるQRコードを読むと次の問題へっていうやつです.これを50回繰り返す必要があります.配色が日本配色でした.QRコードについて調べていた時期があったり,ルービックキューブも趣味でやっているので俺得問題やんけ!ってなって始めたのがきっかけです.

この問題では実際にQubic Rubeを作ってみて解いていたのですが,そもそも作った経緯が,問題が変わってもQRコード自体は変らないと思い込んでいて,なら実際に作ったほうが楽になるんじゃねと思ったのがきっかけです.チームメンバーの一人はステージ3ぐらいまでペイントツールで解いていて問題には気づいていたらしいのですが,お互いの情報共有や会話が上手く噛み合っていなかったのもあって,情報共有大事だなと思いました.

さらに運の悪いことに,6面それぞれの画像がWebサイトのソースコードから入手できることに気づかず,ドット絵を描くツールを使って全部手打ちで書き起こしていました.結果,7時間ほどでQubic Rubeは爆誕したわけです.

以下,製作工程です.ルービックキューブは自宅にあった Dayan ZhanChi 57mm を使いました.

ルービックキューブの大きさに合わせて5.7cmで6面をカラー印刷してきたら,綺麗に切り抜きます.

その後,裏面に両面テープを貼り付けていきます.

綺麗に貼り終わりました.

その後,これを9分割するのですが注意点として真ん中が13マス,両端が10マスになるように切っていきます.

これをキューブに丁寧に貼っていけば完成です.

自分のミスがわかってから,深夜にチーム全体で話し合って解く方針を決めました.流れとしては以下の順です.

  1. 6面の画像全てをサイトから落としてきて,それぞれを9分割する
  2. ルービックキューブを解くライブラリを使うために,9分割した画像を色判定して特定の順番で記号に置き換える
  3. 置き換えた記号をライブラリに流しこんで,解を得る
  4. 実際のルービックキューブを使って,出てきた解を逆の順番かつ逆の手順で回し,同じ状態にする
  5. URLがでてきそうな色に特定の順番で番号を振っていき,その後ライブラリから得られた手順を使って元に戻す
  6. すると,その色の各パーツの配置がわかるので,それを元に9枚の画像を配置し,向きがあっていない場合はそれぞれの画像を回転させてQRコードを得る

1,2,3,6辺りはスクリプト化します.ライブラリはkociembaというアルゴリズムを実装したものを使いました.

github.com

ここで,実際に作ったQubic Rubeが,QRコードを動かすイメージがついたり付箋で番号を貼って実際に回して位置を把握したりと,意外と役に立ったので良かったです.

上記の手順で10ステージまではいったのですが,その後ライブラリが上手く動かなくなるということが起きました.どうやら問題のキューブの配色が微妙に変わるということが起きていて,このライブラリは決められた配色でしか動かないようだったので,途中からは以下のサイトで手作業で手順を求めました.

rubiks-cube-solver.com

結局,1・2・6は後輩が,3・4・5は僕がやるという連携プレイで解いていました.URLが出てくる色が10ステージ目までは黄色だったのに,そこからはランダムになって苦戦したのですが,URLがでてきそうな色の判別は全て目視で行えるようになって,主にQRコードのデータコード語の部分の配置を見てデータが入って無さそうなやつはURLではない,とか形式情報の下位8bitで判断したりもしていました.

こんな感じ↓で作業してました.

そんなこんなで,ようやく流れに乗れたのが朝の11時とかだったので,そこからどんどん解いていきましたが最終的に37/50ステージ目でタイムアップしてしまいました.

あと2時間あれば絶対解けたはず...

まとめ

本当に楽しかったです.某所に男4人が集まってやっていたのですが,深夜にチーム全体で話し合ったときなどはこれぞチームって感じでした.

来年こそは100位以内に入れるように...