フロムスクラッチ開発者ブログ

from scratch Engineers' Blog

CTFのバイナリ解析から学ぶセキュリティとハッキング vol.1

 こんにちは、フロムスクラッチにこの春入社した新人エンジニアの遠藤です!

 フロムスクラッチでは、データを取り扱う会社として、定期的にセキュリティに関する社内勉強会を実施しております。今回はこの勉強会で得た学びを記事にしたいと思います。社内勉強会ではマイナビ出版の『セキュリティコンテストチャレンジブック CTFで学ぼう情報を守るための戦い方』を参考にしており、今回は「CTFのバイナリ解析から学ぶセキュリティとハッキング」というテーマでお話できればと思います。
 今回学びが非常に多く、分量が大きくなり過ぎたので、「CTFとバイナリ解析の基本」と「CTFのPwn問題の基本的な攻略法」の2回に分けて投稿していきます。

1.CTFとは?

 CTFとは情報技術に関する問題に対して適切な形で対処し、それに応じて得られた得点で勝敗を決める大会です。”Capture The Flag”の頭文字をとってCTFといいます。CTFでは得点となる答えの文字列のことを「フラグ(Flag)」と呼び、これを得ること で得点となるのが基本です。
 そこで、例えばこんなクイズが出るかもしれません。
 
 フラグはなんでしょう?

LRGM OY [LXUSYIXGZIN]
続きを読む

Apache ZooKeeperを内部解析してみる vol.3 〜データツリー編〜

こんにちはfukuです。

前回は実際にZooKeeperをスタンドアローンで起動して、zkCliからデータツリーの操作に関して説明を行いました。 今回は実際のソースコードからデータツリーがどのように実装されているのかを解説していこうと思います。ソースコードはバージョン3.5.3をもとにしています。

データツリーのデータ構造

データツリーはこれまで説明を行ってきたように、znodeで構成されます。znodeはソースコードではDataNodeクラスで管理されます。まずはDataNodeについてみていきましょう。

DataNode

DetaNodeクラスのインスタンス変数には以下のようなものがあります。

public class DataNode implements Record {
  /** the data for this datanode */
  byte data[];

  /** 
   * the acl map long for this datanode. the datatree has the map
   */
  Long acl;

  /** 
   * the stat for this node that is persisted to disk.
   */
  public StatPersisted stat;

  /** 
   * the list of children for this node. note that the list of children string
   * does not contain the parent path -- just the last part of the path. This
   * should be synchronized on except deserializing (for speed up issues).
   */
  private Set<String> children = null;

dataはcreateやsetコマンドで指定するznodeのデータを保持します。またaclはznodeに設定されている権限情報を管理しますが、今回は権限には触れずに説明を行います。

childrenは対象のznodeの子ノードを保持します。データはSet<String>と なっているので、子ノードの親ノードを除いたパス名が格納されます。例として、/parent/parent/child1/parent/child2 とznodeがある場合には/parrentノードのchildren変数には"child1" と"child2"が格納されることになります。

最後にstatはStatPersistedクラスのインスタンスが格納されます。StatPersistedクラスはディスク永続化するためのノードの情報が格納されます。StatPersistedクラスの定義は以下となります。

// information explicitly stored by the server persistently
class StatPersisted {
    long czxid;      // created zxid
    long mzxid;      // last modified zxid
    long ctime;      // created
    long mtime;      // last modified
    int version;     // version
    int cversion;    // child version
    int aversion;    // acl version
    long ephemeralOwner; // owner id if ephemeral, 0 otw
    long pzxid;      // last modified children
}

StatPersistedでは主にノードに関してのバージョン情報とZooKeeperトランザクションID(ZXID)に関する情報を保持します。バージョンやトランザクションに関しては次回以降に説明をできればと思います。

StatPersistedの状態は前回紹介した、zkCli.shからgetコマンドの-sオプションによって参照が行えます。

[zk: localhost:2181(CONNECTED) 0] get -s /parent
hogehoge
cZxid = 0x9
ctime = Sun Jan 07 17:37:14 JST 2018
mZxid = 0xe
mtime = Sun Jan 07 17:48:47 JST 2018
pZxid = 0x9
cversion = 0
dataVersion = 4
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 0

ただしCLIから表示される内容はStatPersistedインスタンスそのものではなく、以下に示すStatクラスのインスタンスへ変換した内容を表示します(StatPersistedのcopyStatメソッドにより変換が行われます)。ただ内容に関してはdataの長さ(dataLength)や子ノードの数(numChildren)が追加される程度で、ほとんど内容としては同じとなります。

 // information shared with the client
 class Stat {
     long czxid;      // created zxid
     long mzxid;      // last modified zxid
     long ctime;      // created
     long mtime;      // last modified
     int version;     // version
     int cversion;    // child version
     int aversion;    // acl version
     long ephemeralOwner; // owner id if ephemeral, 0 otw
     int dataLength;  //length of the data in the node
     int numChildren; //number of children of this node
     long pzxid;      // last modified children
 }

DataTree

つづいてデータツリーをメモリ上で管理するDataTreeクラスについてみていきます。DataTreeクラスから、znode管理に関する変数を以下に示します。

public class DataTree {
  /**
   * This hashtable provides a fast lookup to the datanodes. The tree is the
   * source of truth and is where all the locking occurs
   */
  private final ConcurrentHashMap<String, DataNode> nodes =
    new ConcurrentHashMap<String, DataNode>();

  /**
   * This hashtable lists the paths of the ephemeral nodes of a session.
   */
  private final Map<Long, HashSet<String>> ephemerals = 
    new ConcurrentHashMap<Long, HashSet<String>>();

  /**
   * This set contains the paths of all container nodes
   */
  private final Set<String> containers = 
    Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());

  /**  
   * This set contains the paths of all ttl nodes
   */
  private final Set<String> ttls =
    Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
}

ここで上記で紹介したかくインスタンス変数に関して説明をしていきます。

nodes

nodesはデータツリーが管理するすべてznodeの情報を格納します。ZooKeeperではツリー構造のデータを扱いますが、データ構造上は一般的な親ノードが子ノードのデータポインタを管理するようなツリー構造のデータ構造をしておらず(DataNodeではあくまで子ノードのパス名した管理していませんでした)、DataTreeインスタンスがすべてのznodeのデータポインタを管理するような構造となっています。

ZooKeeperでは内部のメタデータを管理するためにデフォルトのデータツリーの状態をDataTreeのコンストラクタで生成します。デフォルトのデータツリーの状態は以下のようになっています。

f:id:fuku_dw:20180107184731p:plain:w300

そのためデフォルトのnodesの状態は"/"、"/zookeeper"、"zookeeper/config"、"/zookeeper/quota" それぞれのパス名とDataNodeへ参照となります。

ephemerals

変数ephemeralsではEPHEMERALモードのznodeを管理します。Map<Long, HashSet<String>>構造になっていますが、KeyでセッションIDを管理してValueで対象のセッションに紐づくEPHEMERALノードパスを管理するようになっており、特定のセッションが切断された場合に、削除するべきEPHEMERALノードを簡単に参照できるような構造となっています。

参考としてセッションを切断した時に呼び出すkillSessionメソッドを記載します。

void killSession(long session, long zxid) {
  HashSet<String> list = ephemerals.remove(session);
  if (list != null) {
    for (String path : list) {
      try {
        deleteNode(path, zxid);
        if (LOG.isDebugEnabled()) {
          LOG
            .debug("Deleting ephemeral node " + path
              + " for session 0x"
              + Long.toHexString(session));
        }
      } catch (NoNodeException e) {
         LOG.warn("Ignoring NoNodeException for path " + path
           + " while removing ephemeral for dead session 0x"
           + Long.toHexString(session));
      }
    }
  }
}

2行目のHashSet<String> list = ephemerals.remove(session);で切断対象のセッションに紐づくEPHEMERALノードのパス一覧を取得していることがわかります。

containers

containers変数ではCONTAINERモードのznodeのパスを管理しています。これはZooKeeper内部で自動的にCONTAINERモードのznodeを削除する際に、すべてのCONTAINERノードを簡単に参照できるようにするためです。

ttls

ttls変数はTTLモードのznodeのパスを管理します。ttlscontainersと同じように内部的にttlsを効率よく参照して、生存期間が切れたのznodeを削除するために利用されます。

このようにNodeTreeインスタンスでは、すべてのznodeをnodes変数で管理して、EPHEMERALモードなどの特定のモードに関してのznodeに対しては、追加でそれぞれのモードを管理する変数にそのパス情報を格納しながらデータツリー全体を管理しています。

おわりに

今回は実際にソースコードからデータツリーがメモリ上でどのように管理しているのかをみていきました。 次回はさらにznodeを深掘りして理解を深めていけたらと思います。

ではでは...

Amazon RDSでMySQLからAuroraに移行する方法

こんにちは。フロムスクラッチのインフラチームに所属している山崎です。
今回は「Amazon RDSでMySQLからAuroraへの移行」について投稿させて頂きます。

 
AWSの運用管理者の方の中には、「MySQLをAuroraに移行したい」という思いを抱いている方もいるのではないでしょうか?私もそのうちの1人でした。結局、移行を実施したのですが、移行の方法についてまとまっている手順がAWSドキュメントやネット記事に無かったので、断片的なドキュメントをまとめつつ、検証を行い完成させました。今回は、その手順について共有させて頂きます。

 

f:id:daisuke-yamasaki:20180322171802p:plain

 

結論から言うと、「Auroraリードレプリカを作成後、移行対象MySQLの更新を止め(Read-onlyにする)、Auroraリードレプリカを昇格、その後アプリの接続をMySQLからAuroraに切り替える」という方法をとります。簡単なイメージ図は次の通りです。

f:id:daisuke-yamasaki:20180322174341p:plain

 

 こちらが手順の目次になります。以降、手順を解説していきます。

----------------------------------------------------------------------- 

◆事前作業
 1.移行対象MySQL専用のパラメータグループを作成
 2.Auroraリードレプリカを作成

  
◆移行作業
 1.移行対象MySQLのパラメータグループを変更して、Read-onlyにする
 2.レプリカラグが無いことを確認
 3.Auroraリードレプリカの昇格
 4.アプリの切り替え(移行対象MySQL→昇格したAurora)
 5.事後確認
 6.移行対象MySQLの削除

----------------------------------------------------------------------- 

 

<手順の解説>

◆事前作業

本番の移行作業では、「移行対象MySQLの更新を止める(Read-only)」、「Auroraリードレプリカが作成されている」の2点が必要です。そのための準備をここで実施します。

 

1.移行対象MySQL専用のパラメータグループを作成

Read-onlyはRDSのパラメータグループを用いて実現します。

(1)移行対象MySQLのパラメータグループをコピーして、ユニークなパラメータグループを作成

AWS RDSダッシュボード>移行対象MySQLのパラメータグループを選択>コピー>名前を任意にして作成

 

(2)(1)で作成したパラメータグループを編集、read_onlyの値を1にする

AWS RDSダッシュボード>(1)で作成したパラメータグループを選択>パラメータの編集>read_onlyの値を1にして作成

 


2.Auroraリードレプリカを作成


移行作業前に作成します。作成時間は環境によるので、事前検証を行い作成時間を見積もっておいて下さい。

(1)Auroraリードレプリカを作成

AWS RDSダッシュボード>移行対象MySQLを選択>インスタンスの操作>Auroraリードレプリカの作成を選択

作成時の設定は、Aurora独自の設定(パラメータグループ)以外は移行対象MySQLと同じにする。

 

 (2)作成完了の確認 

次の項目で、作成の完了を確認する。
・移行対象MySQLのレプリケーション欄で、Auroraリードレプリカのロールが”レプリカ”となっていること
・Auroraリードレプリカのレプリケーション欄で、DBクラスターロールが”レプリカ”であること

 

 

◆移行作業


移行作業では、Read-only、接続断がそれぞれ数分発生するので、サービス影響が最も無い時間帯に実施します。


1.移行対象MySQLのパラメータグループを変更して、Read-onlyにする

(1)移行対象MySQLのパラメータグループを、事前準備の1で作成したものに変更

AWS RDSダッシュボード>移行対象MySQLを選択>インスタンスの操作>変更>パラメータグループを変更

 

(2)移行対象MySQLを再起動する 

AWS RDSダッシュボード>移行対象MySQLを選択>インスタンスの操作>再起動

 
(3)移行対象MySQLでRead-onlyであることを確認

移行対象MySQLに接続後、次のようなコマンドを実行し、Read-onlyであることを確認する。

 


 mysql> create table hogehoge;

 次のエラーが出ること。
 ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it  cannot execute this statement

 

 

2.レプリカラグが無いことを確認

移行対象MySQLとAuroraリードレプリカとでデータの差(レプリカラグ)が無いことを確認する。Auroraリードレプリカに接続し以下を実行、レプリケーションラグが無いこと.

 


 mysql> show slave status\G

(`Seconds_Behind_Master: 0`)を確認する。

 


3.Auroraリードレプリカの昇格

(1)昇格を実行する

Auroraリードレプリカを選択>インスタンスの操作>リードレプリカの昇格

 

(2)昇格完了を確認する

昇格したAuroraの詳細欄で、DBクラスターロール=マスターを確認

 

 


4.アプリの切り替え(移行対象MySQL→昇格したAurora)

アプリの接続先(エンドポイント)を「移行対象MySQL」から「昇格したAurora」に切り替えます。切り替えは、ご自身の環境に応じて実行して下さい。


なお、この方法では移行対象MySQL(インスタンス名をAとする)を別名の昇格したAurora(インスタンス名をBとする)に変える形なります。インスタンス名が変わるので、それを避けたい場合は、次の手順でインスタンス名の入れ替えを行って下さい。 

Bの昇格後、Aのインスタンス名をCにする。
その後、Bのインスタンス名をAにする。

 


5.事後確認

ご自身の環境に応じて、適切に正常性を確認して下さい。

 


6.移行対象MySQLの削除

不要な移行対象MySQLを削除します。

AWS RDSダッシュボード>移行対象MySQLを選択>インスタンスの操作>削除

 

  

 

以上です。作業実施前は、必ず作業の検証(リハーサル)を実施して下さい。
ご覧頂きありがとうございました。