OCamlで簡単なプログラムを書いてみる

プログラミング言語のひとつであるOCamlで簡単なプログラムを書いてみる。

環境

Ubuntu 16.04.2 LTS 64bit版、OCaml 4.02.3

$ uname -a
Linux vm-ubuntu64 4.4.0-66-generic #87-Ubuntu SMP Fri Mar 3 15:29:05 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.2 LTS
Release:        16.04
Codename:       xenial

$ ocaml -version
The OCaml toplevel, version 4.02.3

OCamlの概要

OCamlはINRIA(フランス国立情報学自動制御研究所)によって開発されたプログラミング言語であり、関数型言語MLの方言であるCamlにオブジェクト指向の要素が取り入れられたものである。 他の関数型言語と同様に、静的な型システム・型推論、パターンマッチ、末尾呼び出し最適化等を備えている。

関数型言語として比較的近いものにHaskellがあるが、Haskellとの違いのひとつに評価戦略がある。 Haskellが遅延評価であるのに対し、OCamlは正格評価である。 遅延評価には不必要な計算が行われないという利点がある一方で、未評価の式(thunk)が溜まるとパフォーマンスが落ちる(スペースリーク)という難点がある。 OCamlは正格評価であるため、正格評価を行う多くのプログラミング言語と同様に考えることができる。 なお、Haskellseq$!で正格評価を扱うことができる(参考)ように、OCamllazyによって遅延評価を扱うことができる。

OCamlが使われているプロジェクトとしては、以下が挙げられる。

OCamlのインストール

Ubuntuの場合、標準リポジトリからパッケージをインストールできる。 ocaml-noxX11サポートなし、ocamlX11サポートありである。

$ sudo apt install ocaml-nox

なお、OCamlにはパッケージマネージャとしてOPAMがあるが、ここではインストールしないことにする。

ocamlコマンドでインタプリタを起動し、適当な計算を行ってみる。 インタプリタでは、;; で式の終わりを表す。

$ ocaml
        OCaml version 4.02.3

# let rec sigma a b f =
    if a > b then 0 else f a + sigma (a+1) b f;;
val sigma : int -> int -> (int -> int) -> int = <fun>
# sigma 1 10 (fun x -> x*x);;
- : int = 385
# [Ctrl+D]

上のコードでは、Σ_{i=1}^10 i^2 = 1^2 + 2^2 + ... + 10^2 = 385 を計算している。 OCamlにおいて、匿名関数(ラムダ式)は (fun PARAMETER_1 ... PARAMETER_n -> EXPR) と書く。

簡単なプログラムを書いてみる

プログラムの例として、素因数分解を行う factor.ml を書いてみる。

let factor n =
  let rec aux d n =
    if n < d*d then [n]
    else if n mod d = 0 then d :: aux d (n/d)
    else aux (d+2) n in
  let rec aux2 n =
    if n < 2 then []
    else if n mod 2 = 0 then 2 :: aux2 (n/2)
    else aux 3 n in
  aux2 n

(* entrypoint *)
let () =
  let n = int_of_string Sys.argv.(1) in
  (* let n = int_of_string @@ read_line () in *)
  let factors = factor n in
  print_endline @@ (string_of_int n) ^ ": " ^ (String.concat " " @@ List.map string_of_int factors)

factor は一通り2で割った後、平方根以下の奇数で試し割りを行う関数である。 OCamlにはC言語におけるmain関数のような概念がないため、慣習的に let () = ... 等でエントリポイントが表わされる。 コメントは (* *) で表し、C++における // のような1行コメントはない。 また、^ は文字列連結を行う演算子@@Haskell$ に相当し、g (f x) == g @@ f x のように括弧を避ける用途で用いられる演算子である(参考)。

ocamloptコマンドで実行ファイルにコンパイルし、実行してみると次のようになる。

$ ocamlopt factor.ml -o factor

$ file factor
factor: 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]=4a20beb80735f3abf4a7f6d85b343a7a737fcb87, not stripped

$ ./factor 360
360: 2 2 2 3 3 5

$ time ./factor 4611686018427387903
4611686018427387903: 3 715827883 2147483647

real    0m4.946s
user    0m4.944s
sys     0m0.000s

$ factor 4611686018427387903
4611686018427387903: 3 715827883 2147483647

上の結果から、正しく実行ファイルにコンパイルされ、素因数分解できていることが確認できる。

なお、64 bit環境においてOCamlのintは63 bit符号付き整数であり、-262から262-1までの値となる。 64 bitのうちの1ビットは内部でガベージコレクション用に利用される(参考)。

$ ./factor 4611686018427387904
Fatal error: exception Failure("int_of_string")

$ python
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> bin(4611686018427387904)
'0b100000000000000000000000000000000000000000000000000000000000000'
>>> len(_)-3
62
>>> [Ctrl+D]

クラスを使ったプログラムを書いてみる

OCamlらしいプログラムの例として、クラスを使った oop_example.ml を書いてみる。

class virtual animal (name : string) = object(self)
  val mutable name = name
  method set_name new_name = name <- new_name
  method virtual cry : unit
end

class dog name = object(self)
  inherit animal name
  method cry = print_endline @@ name ^ ": bowwow"
end

class cat name = object(self)
  inherit animal name
  method cry = print_endline @@ name ^ ": meow"
end

let () =
  let dog = new dog "Snoopy" in
  let cat = new cat "Kitty" in
  dog#cry;
  cat#cry;
  dog#set_name "Pochi";
  cat#set_name "Tama";
  dog#cry;
  cat#cry

animal#cry のような仮想メソッドを持つクラスには、クラス本体にも virtual キーワードを付ける必要があることに注意する。

コンパイルして実行すると次のようになる。

$ ocamlopt oop_example.ml -o oop_example

$ ./oop_example
Snoopy: bowwow
Kitty: meow
Pochi: bowwow
Tama: meow

関連リンク

「Can We Prevent Use-after-free Attacks?」というタイトルで発表した

2年前に発表していた勉強会が久しぶりに開催されるということで、Use-after-free攻撃の原理と対策について話した。

最近はCTFが盛り上がっていることもあり、攻撃手法についての話を見ることが多いのだけど、そこから先のどうやったら防げるかという話を見ることは相対的に少ないように思う。 しかし、Attack and Defence形式のCTFにおけるDefence部分だったり、Cyber Grand Challangeにおけるpatch適用の自動化だったり、防御側の視点というのもあるわけで、そういった視点への理解を深めることも必要なのではないか。

あとは、○○はダメだとか、○○すべきなのではないかとか、個々のセキュリティ対策についての議論を見ることも多いのだけど、必要なのはリスクの洗い出しからの評価・対応といったリスクマネジメントを行うことであって、○○が是か非かというような誤った二分法に落とし込むのは少し違うように感じる。 結局のところ、その環境において想定されるリスクに対してコスト・ベネフィットのバランスが取れた管理ができているかというところが「問題」なのではないか。

そういった問題意識から、Use-after-free攻撃という具体的な例について、全体としてどういう対策があるのかというのをまとめてみた。 自分自身、セキュアなアセンブリコードを吐くコンパイラ改良だったり、セキュアなメモリ管理を行うGC付きheapといったコンセプトについて考えるきっかけにもなった。

他の発表も、過去から今を知っているからこその資料的価値のあるものだったり、触れられることの少ないトピックに関する検証を行ったものだったりで、一参加者としてもとても楽しめる内容だった。 さまざまな人の反応を知れたり意見交換ができたことも、よい刺激になった。

ありがとうございました。

LWE格子暗号による暗号化をやってみる

近年、量子コンピュータ研究の進展により、量子コンピュータでも解くのが難しい暗号(Post-quantum cryptography; 耐量子計算機暗号)への注目が高まっている。 ここでは、耐量子計算機暗号のひとつであるLWE格子暗号の原理を説明し、単純化したアルゴリズムで暗号化を行うプログラムコードを書いてみる。

格子とLearning with Errors問題

LWE格子暗号は、Learning with Errorsと呼ばれる問題をもとにした公開鍵暗号方式である。

格子とは、基底ベクトルの整数係数線形結合で表される点(格子点)からなる集合である。 格子については、直交しない基底ベクトルからある点Pに最も近い格子点を求めることが計算量的に困難であるといわれている。 これは、最近ベクトル問題(Closest Vector Problem; CVP)と呼ばれる。

Learning with Errors問題は、この点Pを基底ベクトルの整数係数線形結合と誤差の和として表したとき、点Pから係数と誤差を求める問題である。 これは言い換えれば、次のような誤差付きの線形方程式を解く問題であるといえる。

A * s + e = t

誤差がない場合の線型方程式はガウスの消去法などを用いて容易に解けるが、誤差が加わった線形方程式を効率的に解くアルゴリズムは現時点で見つかっていない。

LWE格子暗号では、素数qを法とする有限体上での演算のもとでの格子を考える。 また、LWE格子暗号には暗号文のまま演算ができるという性質がある。 このような性質を持つ暗号は準同型暗号と呼ばれる。

純化したLWE格子暗号で暗号化してみる

次の資料では、単純化したLWE格子暗号による1ビット平文の暗号化・復号アルゴリズムが説明されている。

このアルゴリズムは本来のLWE格子暗号のアルゴリズムとは異なるが、説明を簡単にするため、ここではこのアルゴリズムをもとにした計算をやってみる。 パラメータ、秘密鍵、公開鍵は次のようになる。

  • パラメータ
    • n: 格子の次元
    • q: 法とする素数
    • A: 格子の基底行列(n x n)
    • α: 誤差の大きさに関連するパラメータ
  • 秘密鍵
    • s: 係数(n x 1)
    • e: 誤差(n x 1)
  • 公開鍵
    • T: 格子点に誤差を加えた点(n x 1)

numpyを用いて計算を行うプログラムコードを書くと次のようになる。

import numpy as np

# parameters
n = 230
q = 2053
A = np.random.randint(q, size=(n, n))
alpha = 8.0

def discrete_gaussian(size):
    sigma = alpha/np.sqrt(2*np.pi)
    x = np.random.normal(0, sigma, size)
    return np.rint(x)

print '[+] parameters'
print "lattice dimension: n = %d" % n
print "prime modulus: q = %d" % q
print 'lattice basis: A = \n', A
print "error distribution parameter: alpha = %f" % alpha
print

# keys
s = np.random.randint(q, size=n)
G = A.dot(s) % q
e = discrete_gaussian(size=n)
T = (G + e) % q

print '[+] secret key'
print 's =\n', s
print 'e =\n', e
print '[+] public key'
print 'T =\n', T
print

# encryption
plain_bit = 1
print "[+] plain_bit = %d" % plain_bit
print

r = discrete_gaussian(size=n)
C1 = r.dot(A) % q
M = (q+1)/2 * plain_bit
C2 = (r.dot(T) - M) % q

print '[+] ciphertext'
print 'C1 =\n', C1
print 'C2 =\n', C2
print

# decryption
p = (C1.dot(s) - C2) % q
decrypted_bit = int((q+1)/4 < p < 3*(q+1)/4)
print "[+] decrypted_bit = %d" % decrypted_bit

上のコードでは、誤差を離散ガウス分布に従って生成し、1ビットの平文を暗号化した後復号している。 復号時は秘密鍵である係数sを用いて -r*e + M を計算し、-r*e として期待される値かどうかで確率的に平文のビットを判定する。

実際に実行してみると、次のようになる。

$ python lwe.py
[+] parameters
lattice dimension: n = 230
prime modulus: q = 2053
lattice basis: A =
[[1543  329 1153 ..., 1259   18 1144]
 [ 686 1250  850 ...,  640 1822  502]
 [1817  901 1649 ..., 1272  900  145]
 ...,
 [ 207 1325 1223 ..., 1783 1011 1331]
 [ 225 1380 1143 ...,  468 1403   50]
 [ 937 1758   92 ..., 1938 1435  902]]
error distribution parameter: alpha = 8.000000

[+] secret key
s =
[ 977 2046 1632 1459 1308  729  574 1506 1244 1405 1546 1482  800  213 1551
 1122 1691 1910 1302  917 1495  465  681 1442 1953  432  997 2052 1002  105
  371 1161 1215 1015  626 1925 1318 1886 1850 1689  320  949  944  153 1536
 1112  405 1274  709  928 1049  522  639 1180 1644 1474 1667 2047  313 1693
  947 1655  383  703  570  112  737  870 1492 1489  409  348 1744 1759 1075
  596  883 1414  404 2004 1553 1624  896 1285 1469  611 1649  717  400  786
  963 1561 1142 1301 1457  829  354  291  715  332 1230 1954 1146 1083  761
 1233 1455  908 1165  666  542  883 1628  911  189 1058  864  442 1084   22
  293  655 1120 1187  190  574  164 1253 1718  676  311  116 1334 1184 1504
 1354 1448 1471 1066 1941  147  990 1082    5   25 1190 1692  557 1642  988
 1455  527  823 1620  620 1044  133  370 1281 1306 1793   85 1613 1594  380
 1430  466 1827 1517  421 1781  471  948 1281   87  568 1717  731 1439 1338
 1781 1273  764  580  398  816 1691 1657 1767  471  630  495  824  756  541
  644 1746 1991 1597 1223  234 1673 1280 1339 1472 1634 1118 1781 1050 1429
  289 1874  846 1510  250  532  662 1407  432  597  205 1442   92  943  403
  260 1973 1883  520  736]
e =
[ -3.   6.   1.  -2.  -6.   2.  -4.   1.   4.   2.   1.   1.   2.  -2.   3.
  -1.   3.   2.   6.  -2.  -3.  -6.   1.  -3.   3.  -1.  -3.   3.  -3.  -1.
  -5.  -3.   1.  -4.  -2.  -0.   2.   3.  -3.  -2.  -4.  -3.  -0.  -6.   1.
  -0.  -4.   1.  -2.   1.   0.  -1.   0.  -0.   1.   1.  -3.  -1.   0.   2.
   1.   2.  -4.  -3.   2.  -1.  -4.  -2.  -2.  -3.   3.   0.  -2.   0.   0.
  -1.   2.   7.   3.   1.  -2.  -1.  -1.  -1.   0.   3.   5.   8.   4.  -1.
  -0.   1.   5.  -3.   2.   2.  -3.  -6.   0.   6.   4.  -1.  -1.  -2.   4.
  -2.  -6.  -2.   2.   4.   5.  -2.  -1.  -0.  -2.   4.   7.  -2.  -1.   1.
  -2.   2.  -2.  -7.   0.   3.   3.   3.   1.  -4.   1.   3.   8.   2.   2.
  -1.  -1.  -4.  -0.   1.  -3.  -1.   1.  -2.  -3.  -1.   2.   4.  -0.   0.
   3.  -2.  -5.   1.  -0.   1.   4.  -3.  -5.  -4.   2.  -0.  -1.  -1.   1.
  -4.   6.   0.  -0.  -2.   0.  -6.   0.  -1.  -1.  -2.   2.   4.  -3.  -0.
  -2.   3.   3.  -0.   5.   1.   5.  -0.   5.  -1.   6.  -0. -11.   1.  -2.
   2.   2.   0.  -0.   0.  -1.  -2.   1.  -1.   4.   2.  -0.  -0.   4.   5.
  -1.  -3.   1.  -3.   0.  -4.   2.   3.  -2.   3.  -0.  -2.  -5.   1.  -4.
  -0.  -3.  -4.   4.  -3.]
[+] public key
T =
[  492.  1328.   983.  1435.  1316.   833.  1076.   444.    13.  1561.
  1409.   782.  1126.   408.   600.  1127.  1547.    95.   991.   458.
  1536.   814.  1926.   527.  1220.   650.  2030.   834.   613.  1191.
  1534.   790.   888.   765.   911.    56.  1935.  1782.     6.   491.
  1833.  1104.   579.     0.  1211.  1204.   674.  1553.   196.  1480.
  1001.  1707.   102.  1000.   331.   858.  1608.    27.   727.  1542.
  2050.   640.  1780.  1156.   390.   494.  1532.   874.   797.   993.
   859.  1448.  1634.  2029.   743.  1397.  1803.   595.    89.  1085.
  1955.   140.   272.  1866.  1113.  2052.  1581.  1103.  1611.   308.
   593.  1850.   557.   838.   595.  1999.  1924.   447.  1554.  1548.
  2013.  2030.   203.  1858.  1944.  1099.   446.  1872.  1042.  1009.
    15.  1215.   412.  1269.   863.  1235.   671.  1124.    28.   383.
   146.    58.   338.  1228.  1565.   303.   495.   519.    29.   380.
  1974.  1694.  1594.  1308.  1363.   690.  1291.  1096.   745.   642.
   309.  1579.  1701.   485.  1798.  1667.  1800.   769.  1095.  1388.
   720.   636.  2014.   391.  1238.  1984.  1272.  1810.  1604.  2028.
   829.   291.    92.  1187.  1477.   824.  1635.  1620.   187.  1105.
   632.  1215.   937.  1591.  1762.  1291.   656.  1128.  1957.   496.
   559.   882.  1718.  1746.  1281.  1932.   251.   814.   364.   196.
   558.  1910.   257.    11.  1831.   580.   301.  1330.  1405.   546.
   267.   690.  1040.   150.  1110.  1007.   874.   473.  1164.   327.
  1237.   963.   336.   602.  1421.  1488.  1367.  1440.  1631.  1425.
    96.  1250.   477.  1813.  1660.   605.   971.   117.   352.  1064.]

[+] plain_bit = 1

[+] ciphertext
C1 =
[ 1935.  1715.  1768.     9.  1957.   220.  1702.   150.   312.    94.
  1007.  1237.   260.   691.  1147.  1194.  1745.  1914.  1822.   825.
  1449.   278.  1890.   541.  1568.    50.   459.    29.  1318.   800.
  1515.  1593.   907.   596.   299.   302.  1799.   688.   376.  1963.
   524.   564.   104.  1975.  1734.  1068.   964.  1368.   748.  1473.
   163.  1796.  1579.  1536.  1963.   138.    39.   140.  1982.   382.
   676.   704.    83.  1448.  1836.  1888.   255.  1332.  1921.   111.
   928.  1942.   350.  2044.  1737.   459.  1646.   779.  1251.  1660.
  1209.   991.  1493.     8.   952.  1647.   885.   823.    41.    50.
   980.  1087.  1231.  1809.    79.  1054.  1140.   271.  1074.    54.
   261.  1063.  1109.  1253.  1019.  1673.   876.  1456.  1255.  1455.
   881.  1209.  1594.  1401.  1910.   898.   363.    64.  1485.   437.
   553.  1252.   245.  1691.  1897.    48.   624.   520.   258.   138.
   692.  1494.  1905.   767.   691.  2039.  1918.   547.   137.  1801.
   886.   606.  1480.   767.   924.   198.  1233.  1135.   837.   306.
  1465.  1926.    99.  1047.  1725.  2034.  1622.   853.  1992.  1870.
  1157.  1364.  1040.   720.  1385.  1013.   342.  2031.  1445.  1552.
  1119.  1876.  1053.   406.  1750.   955.  1688.   582.  1201.   900.
  2029.   491.  1475.   597.  1764.  1205.   349.  1231.  1852.  1426.
  1672.  1310.   354.   434.   225.  1161.   771.  1154.   722.   159.
  1986.  1354.  1096.   102.   592.   362.  1397.  1319.    14.  1735.
    69.   187.   494.  1413.  1786.  1331.   538.  2036.  1389.  1290.
  1587.   622.   260.   782.  1657.  1065.  1345.  1498.   877.   743.]
C2 =
1427.0

[+] decrypted_bit = 1

plain_bitを変えながら複数回実行すると、ほとんどの場合において平文のビットを正しく判定できていることが確認できる。

Ring-LWE格子暗号

上のコードは単純化したものの実装であるが、本来のLWE格子暗号は格子の次元をnとしたとき公開鍵の鍵長がO(n2)となる。 また、上の結果からもわかるように、暗号文の長さは平文のn倍になる。

これらの点を改良したものとして、有限体の代わりに円分体の整数環上での演算を用いたRing-LWE(RLWE)格子暗号がある。 Ring-LWE格子暗号では、公開鍵の鍵長を数千bit程度にでき、また、複数ビットの平文を対応する多項式の係数で表すことにより暗号文の長さを格段に小さくできる。

昨年GoogleNew Hopeと呼ばれる耐量子鍵交換方式の実験を行ったが、この鍵交換方式もRing-LWE問題をベースとしたものになっている。

関連リンク

RCTF 2017 供養(Writeup)

RCTF 2017に参加。185ptで176位。

Sign In (Misc 32)

Please join #rctf2017 on Freenode. And the flag is in topic.

Format: RCTF{…}

RCTF{Welcome_To_RCTF_2017}

easyre (Reverse 153)

32 bit ELF実行ファイル。

$ file easy_re
easy_re: ELF 32-bit LSB executable, Intel 80386, version 1, statically linked, corrupted section header size

straceすると別のELF実行ファイルを書き出していることがわかる。

$ strace -i ./easy_re
[00007f29d9c1dbc7] execve("./easy_re", ["./easy_re"], [/* 20 vars */]) = 0
strace: [ Process PID=6426 runs in 32 bit mode. ]
[080482a9] getpid()                     = 6426
[080482f1] open("/proc/6426/exe", O_RDONLY) = 3
[08048314] lseek(3, 1588, SEEK_SET)     = 1588
[08048238] read(3, "w\24\7\0t\35\0\0t\35\0\0", 12) = 12
[08048379] gettimeofday({2057282240221484, 3762246642832666671}, NULL) = 0
[080483ad] unlink("AAAAAAAAAW3PZK1AGI0") = -1 ENOENT (No such file or directory)
[080483d2] open("AAAAAAAAAW3PZK1AGI0", O_WRONLY|O_CREAT|O_EXCL, 0700) = 4
[080483e2] ftruncate(4, 7540)           = 0
[080483f8] mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf776b000
[08048238] read(3, "t\35\0\0\220\n\0\0", 8) = 8
[08048238] read(3, "\177?d\371\177ELF\1\0\2\0\3\0\r@\205\4\377o\263\335\0104\7p\21\27\v \0\10"..., 2704) = 2704
[08048484] write(4, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\2\0\3\0\1\0\0\0@\205\4\0104\0\0\0"..., 7540) = 7540
[08048238] read(3, "\0\0\0\0UPX!", 8)   = 8
[080484a5] unlink("AAAAAAAAAW3PZK1AGI0") = 0
[080484b1] exit(127)                    = ?
[????????] +++ exited with 127 +++

gdbでunlink直前まで実行して、ファイルを得る。

$ objdump -D -b binary -m i386 easy_re | grep -C3 '4a5:'
     499:       b8 0a 00 00 00          mov    eax,0xa
     49e:       bb 08 96 04 08          mov    ebx,0x8049608
     4a3:       cd 80                   int    0x80
     4a5:       bb 7f 00 00 00          mov    ebx,0x7f
     4aa:       b8 01 00 00 00          mov    eax,0x1
     4af:       cd 80                   int    0x80
     4b1:       eb f2                   jmp    0x4a5

$ gdb ./easy_re
Reading symbols from ./easy_re...(no debugging symbols found)...done.
(gdb) b *0x080484a3
Breakpoint 1 at 0x80484a3
(gdb) r
Starting program: /home/user/tmp/20170521_rctf/easy_re

Breakpoint 1, 0x080484a3 in ?? ()
1: x/i $pc
=> 0x80484a3:   int    0x80
(gdb) quit
A debugging session is active.

        Inferior 1 [process 6485] will be killed.

Quit anyway? (y or n) y

$ ls -al
total 24056
drwxr-xr-x 4 user user     4096 May 21 22:10 ./
drwxr-xr-x 9 user user     4096 May 21 04:25 ../
-rwx------ 1 user user     7540 May 21 22:10 AAAAAAAAA0H0BOBAGKV*

$ file AAAAAAAAA0H0BOBAGKV
AAAAAAAAA0H0BOBAGKV: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.15, BuildID[sha1]=f4ac362f7b89fbd142b55e02d1cc4906d669be44, not stripped

アセンブリコードを読むと、lol関数でflagが出力されていることが推測できる。

$ python ~/tmp/minipwn/objdump.py AAAAAAAAA0H0BOBAGKV
(snip)
080485f4 <lol>:
sub_80485f4:
 80485f4:       55                      push   ebp
 80485f5:       89 e5                   mov    ebp,esp
 ...
 80486b0:       c7 45 f4 00 00 00 00    mov    DWORD PTR [ebp-0xc],0x0
 80486b7:       83 7d f4 01             cmp    DWORD PTR [ebp-0xc],0x1
 80486bb:       75 16                   jne    80486d3 <lol+0xdf>
 80486bd:       b8 c0 88 04 08          mov    eax,0x80488c0                    ; '%s'
 80486c2:       8d 55 ed                lea    edx,[ebp-0x13]
 80486c5:       89 54 24 04             mov    DWORD PTR [esp+0x4],edx
 80486c9:       89 04 24                mov    DWORD PTR [esp],eax
 80486cc:       e8 ff fd ff ff          call   80484d0 <printf@plt>
 80486d1:       eb 0d                   jmp    80486e0 <lol+0xec>
loc_80486d3:
 80486d3:       b8 c3 88 04 08          mov    eax,0x80488c3                    ; 'flag_is_not_here'
 80486d8:       89 04 24                mov    DWORD PTR [esp],eax
 80486db:       e8 f0 fd ff ff          call   80484d0 <printf@plt>
loc_80486e0:
 80486e0:       c9                      leave
 80486e1:       c3                      ret
(snip)
loc_80487bc:
 80487bc:       b8 24 89 04 08          mov    eax,0x8048924                    ; '\nYou got the key\n '
 80487c1:       89 04 24                mov    DWORD PTR [esp],eax
 80487c4:       e8 07 fd ff ff          call   80484d0 <printf@plt>
 80487c9:       8d 44 24 2e             lea    eax,[esp+0x2e]
 80487cd:       89 04 24                mov    DWORD PTR [esp],eax
 80487d0:       e8 1f fe ff ff          call   80485f4 <lol>
(snip)

gdblol関数を実行し、最後の分岐の直前で止め、分岐の片方で出力される文字列を得る。

$ gdb ./AAAAAAAAA0H0BOBAGKV
Reading symbols from ./AAAAAAAAA0H0BOBAGKV...(no debugging symbols found)...done.
(gdb) set follow-fork-mode parent
(gdb) b *0x80486bb
Breakpoint 1 at 0x80486bb
(gdb) r
Starting program: /tmp/AAAAAAAAA0H0BOBAGKV

OMG!!!! I forgot kid's id
Ready to exit
^C
Program received signal SIGINT, Interrupt.
0xf7fd8be9 in __kernel_vsyscall ()
1: x/i $pc
=> 0xf7fd8be9 <__kernel_vsyscall+9>:    pop    ebp
(gdb) shell
$ ps auxf | grep AAAA
user      6666  0.5  0.9  63772 20072 pts/0    S    22:20   0:00  |           \_ gdb -q -nh -x /home/user/.gdbinit ./AAAAAAAAA0H0BOBAGKV
user      6668  0.0  0.0   2192   612 pts/0    t    22:20   0:00  |               \_ /tmp/AAAAAAAAA0H0BOBAGKV
user      6672  0.0  0.0      0     0 pts/0    Z    22:20   0:00  |               |   \_ [AAAAAAAAA0H0BOB] <defunct>
user      6684  0.0  0.0  11284   900 pts/0    S+   22:20   0:00  |                       \_ grep --color=auto AAAA

$ exit
(gdb) c
Continuing.
6672

You got the key

Breakpoint 1, 0x080486bb in lol ()
1: x/i $pc
=> 0x80486bb <lol+199>: jne    0x80486d3 <lol+223>
(gdb) x/s $ebp-0x13
0xffffdb45:     "rhelheg"
(gdb) quit
A debugging session is active.

        Inferior 1 [process 6668] will be killed.

Quit anyway? (y or n) y

これがフラグだった。

RCTF{rhelheg}

所感

低得点問題1問しか解けず厳しい。他に解きたかった問題は以下。

  • intoU (Misc 82)
  • RSA_sign1 (Crypto 307)
  • baby flash (Reverse 222)
  • Recho (Pwn 370)

関連リンク

Spring Bootでエラーページをカスタマイズする

「Spring Bootで簡単なWebアプリケーションを書いてみる」では、Spring Bootで簡単なWebアプリケーションを書いた。 ここでは、デフォルトのエラーページをカスタマイズしてみる。

環境

Windows 10 Pro、Java SE 8、Spring Framework 4.3.7.RELEASE(Spring Boot 1.5.2.RELEASE)

>systeminfo
OS 名:                  Microsoft Windows 10 Pro
OS バージョン:          10.0.14393 N/A ビルド 14393
OS ビルドの種類:        Multiprocessor Free
システムの種類:         x64-based PC
プロセッサ:             1 プロセッサインストール済みです。
                        [01]: Intel64 Family 6 Model 69 Stepping 1 GenuineIntel ~1596 Mhz

>java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

Custom error pageを作る

Spring Bootのデフォルトでは、コントローラで例外が発生するとHTTPの500エラーと合わせてWhitelabel Error Pageと呼ばれるページが表示される。 このページには例外の内容が表示されており、ユーザに不親切であると同時にセキュリティ上の問題を誘発するおそれがある。

Spring Bootでは、errorという名前のビューを作成することで独自のエラーページを表示させることができる。

テンプレートエンジンとしてThemeleafを使用している場合は、次のパスにファイルを作成すればよい。

  • src/main/resources/templates/error.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<title>Error</title>
</head>
<body>
<h1 th:text="${status} + ' ' + ${error}">500 Internal Server Error</h1>

<p><a th:href="@{/}">Back to index</a></p>
</body>
</html>

特定のHTTPステータスに対応するerror pageを作る

/error.htmlは任意のステータスについてのエラーページとなるが、404など特定のステータスに応じたエラーページを作成したい場合もある。 そのような場合は、/error/XXX.htmlを作成すればよい。

  • src/main/resources/templates/error/404.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<title>Error</title>
</head>
<body>
<h1 th:text="${status} + ' ' + ${error}">404 Not Found</h1>
<p>The requested URL was not found on this server &#x1f607;</p>

<p><a th:href="@{/}">Back to index</a></p>
</body>
</html>

アプリケーションを起動し、ブラウザから存在しないパス/four-zero-fourにアクセスした際のスクリーンショットを次に示す。

f:id:inaz2:20170503155203p:plain

関連リンク

Spring BootでMySQLを使う

「TomcatとApache HTTP ServerでSpring Bootアプリケーションをデプロイしてみる」では、作成したSpring BootアプリケーションをTomcat上にデプロイした。 しかし、現在の状態ではデータベースとしてH2を利用する状態になっている。 ここでは、アプリケーションを書き換え、データベースとしてMySQLを利用するようにしてみる。

環境

Ubuntu 16.04.2 LTS 64bit版

$ uname -a
Linux vm-ubuntu64 4.4.0-72-generic #93-Ubuntu SMP Fri Mar 31 14:07:41 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.2 LTS
Release:        16.04
Codename:       xenial

サーバにMySQLをインストールする

まず、接続先となるMySQLサーバを用意する。 ここでは、TomcatをインストールしたサーバにMySQLをインストールすることにする。

aptを使い、MySQLをインストールするには次のようにする。 このとき、ダイアログに従いrootパスワードを適当に設定する。

$ sudo apt install mysql-server

MySQLに接続し、接続先となるデータベースと接続に利用するユーザを作成する。

$ mysql -u root -p
Enter password: [type root password]

mysql> CREATE DATABASE demo CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
Query OK, 1 row affected (0.00 sec)

mysql> GRANT ALL PRIVILEGES ON demo.* TO demo@localhost IDENTIFIED BY 'youmustchangethis';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> \q
Bye

データベースの設定を行う

次に、アプリケーションを書き換え、MySQLデータベースを利用するように設定する。

MySQLを利用する場合、依存パッケージにMySQL Connector-Jを追加する必要がある。 プロジェクトにMySQL Connector-Jを追加するには次のようにする。

  • 「demo [boot]」を右クリックして、「Maven」→「Add Dependency」を選択
  • ダイアログの各フィールドに以下の文字列を入力してOK
    • GroupId: mysql
    • ArtifactId: mysql-connector-java
    • Version: (空白)

Spring Bootでは、設定ファイルとしてpropertiesファイル(またはYAMLファイル)を使用する。 まず、application.propertiesに本番環境用のプロファイルを使う設定を記述する。

  • src/main/resources/application.properties
spring.profiles.active=production

次に、本番環境用のプロファイルに接続先データベースの設定を記述する。

  • src/main/resources/application-production.properties
spring.jpa.hibernate.ddl-auto=create
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=demo
spring.datasource.password=youmustchangethis

spring.jpa.hibernate.ddl-auto=createを指定することで、アプリケーション起動時に必要なテーブルが作成される。 テーブルが作成された後は、updateに変更することでコードに合わせてテーブルが修正されるようになる。

Tomcatにデプロイする

「TomcatとApache HTTP ServerでSpring Bootアプリケーションをデプロイしてみる」と同様の手順でWARファイルをエクスポートし、ROOT##002.warにリネームする。 続けて、Tomcat ManagerからROOT##002.warをデプロイする。

アプリケーションに接続し、適当なデータを追加した後、MySQLデータベースを確認すると次のようになる。

$ mysql -u root -p
Enter password: [type root password]

mysql> use demo;

Database changed
mysql> show tables;
+------------------+
| Tables_in_demo   |
+------------------+
| message          |
| user             |
| user_authorities |
+------------------+
3 rows in set (0.00 sec)

mysql> select * from message;
+----+---------------------+-------+-------------+-------+
| id | created_at          | name  | remote_addr | text  |
+----+---------------------+-------+-------------+-------+
|  1 | 2017-04-26 10:21:24 | name1 | 127.0.0.1   | text1 |
|  2 | 2017-04-26 10:21:31 | name2 | 127.0.0.1   | text2 |
|  3 | 2017-04-26 10:21:40 | name3 | 127.0.0.1   | text3 |
+----+---------------------+-------+-------------+-------+
3 rows in set (0.00 sec)

mysql> \q
Bye

上の結果から、Entityに対応するテーブルが作成されていること、追加したデータが挿入されていることが確認できる。

本番環境でMySQL、開発環境でH2を使う

本番環境ではMySQLを使うが、開発時はH2を使いたい場合がある。 このような場合は、開発時spring.profiles.activeの行を#コメントアウトしておき、WARファイルのエクスポート前にコメントアウトを外すようにすればよい。

関連リンク

TomcatとApache HTTP ServerでSpring Bootアプリケーションをデプロイしてみる

「Spring Securityでユーザ認証を実装してみる」では、Spring Securityで簡単なユーザ認証を実装した。 ここでは、UbuntuサーバにTomcatをインストールし、作成したアプリケーションをデプロイしてみる。 また、Apache HTTP Serverをインストールし、アプリケーションをHTTPSで配信してみる。

環境

Ubuntu 16.04.2 LTS 64bit版

$ uname -a
Linux vm-ubuntu64 4.4.0-72-generic #93-Ubuntu SMP Fri Mar 31 14:07:41 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.2 LTS
Release:        16.04
Codename:       xenial

Tomcatのインストール

まず、Tomcat本体とTomcat Managerをインストールする。

$ sudo apt install tomcat8 tomcat8-admin

インストールが終わったら、Tomcat Managerへのアクセスを有効にするため、ユーザを作成する。 ここで、ユーザ名・パスワードは推測不可能なものに変えておくこと。

$ sudo vi /etc/tomcat8/tomcat-users.xml
(snip)
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user username="USERNAME" password="PASSWORD" roles="manager-gui,admin-gui"/>
</tomcat-users>

Tomcatサーバを再起動し、設定を反映させる。

$ sudo systemctl restart tomcat8

ブラウザから http://localhost:8080/ を開き、Tomcatのデフォルトページが表示されることを確認する。 また、http://localhost:8080/manager/html に設定したユーザ名・パスワードでアクセスし、Tomcat Managerが表示されることを確認する。

WARファイルをデプロイしてみる

Tomcatがインストールできたので、Tomcat ManagerからWARファイルをデプロイしてみる。

まず、「Spring Bootで簡単なWebアプリケーションを書いてみる」で説明した手順に従い、アプリケーションのWARファイルを作成する。

Tomcatでは、WARファイルのファイル名は パス名 + "##" + バージョン番号 + ".war" という命名規約に従って扱われる。 なお、配置するパスが / の場合はパス名を ROOT とする。 また、バージョン番号の比較は単純な文字列比較で行われるため、ゼロパディングすることが推奨されている。

配置するパスを /、バージョン番号を 001 とし、作成したWARファイルを ROOT##001.war にリネームする。 これを、Tomcat Managerからファイルアップロードし、デプロイする。

f:id:inaz2:20170424195008p:plain

アップロードが完了した後、http://localhost:8080/ にアクセスすると、デプロイしたアプリケーションが表示される。

アプリケーションを更新する際には、パス名をそのままに、バージョン番号の数字を上げたWARファイルを新たにデプロイする。 これにより、古いバージョンに接続しているセッションはそのままに、新規セッションが新バージョンに接続される(Parallel Deployment)。 ただし、同時に複数バージョンのアプリケーションを動作させることになるため、メモリ使用量に注意が必要である。

Apache HTTP Serverと連携してHTTPS対応してみる

Tomcatは簡易HTTPサーバとしても機能するが、パフォーマンスの観点からApache HTTP Server(以下Apache)と連携させて使うことが多い。 そこで、Apacheをインストールし、HTTPSでアプリケーションを配信してみる。 なお、HTTPの場合もSSLに関連する部分を除いて同様に行えばよい。

まず、TomcatAJPコネクタを有効にする。 具体的には、下記の箇所のコメントを外す。

$ sudo vi /etc/tomcat8/server.xml
    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Tomcatを再起動し、設定を反映させる。

$ sudo service tomcat8 restart

次に、Apacheをインストールし、proxy_ajpモジュールとsslモジュールを有効にする。

$ sudo apt install apache2

$ sudo a2enmod proxy_ajp ssl

default-sslをベースに、サイト設定ファイルを作成する。 具体的には、DocumentRootをコメントアウトし、ProxyPassを追記する。

$ sudo cp /etc/apache2/sites-available/{default-ssl,tomcat-ssl}.conf

$ sudo vi /etc/apache2/sites-available/tomcat-ssl.conf
                #DocumentRoot /var/www/html
                ProxyPass / ajp://localhost:8009/

作成したサイト設定を有効にし、Apacheを再起動する。

$ sudo a2ensite tomcat-ssl

$ sudo service apache2 restart

ブラウザから https://localhost/ にアクセスすると、HTTPSでアプリケーションにアクセスできることが確認できる。

f:id:inaz2:20170424193527p:plain

さらに、curlコマンドでレスポンスヘッダを確認してみると次のようになる。

$ curl -v --insecure https://localhost/login
(snip)
> GET /login HTTP/1.1
> Host: localhost
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 24 Apr 2017 07:56:29 GMT
< Server: Apache/2.4.18 (Ubuntu)
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-Frame-Options: DENY
< Set-Cookie: JSESSIONID=E49A71C1D34D08184F23B1BF59483BB1; Path=/; Secure; HttpOnly
< Content-Type: text/html;charset=UTF-8
< Content-Language: en-US
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
<
(snip)

上の結果から、Spring SecurityによりStrict-Transport-SecurityヘッダおよびCookieのSecure属性が付与されていることが確認できる。

あとは、通常のApacheと同様に、SSL証明書やログの設定を行えばよい。

実行可能jarとnginxによるデプロイ

Spring Bootでは従来のWARファイルによるデプロイの他に、組み込みtomcatを用いた実行可能jarによるデプロイも可能である。 最近ではTwelve-Factor Appの観点から、実行可能jarとして起動したアプリケーションをnginxで振り分ける方法が好まれることもある。 詳しくは、下記のページを参照。

関連リンク