暗号系処理のメモ(公開鍵暗号以外)

ROT13

※ 暗号化も復号も同じ処理

import codecs
s = "abcdefg"

enc = codecs.getencoder("rot13")
c = enc(s)[0]
print(c)
# nopqrst

dec = codecs.getdecoder("rot13")
m = dec(c)[0]
print(m)
# abcdefg

シーザー暗号(カエサル暗号)

def decrypt(str, key):
    plaintext = ""

    for ch in list(str):
        if 'A' <= ch <= 'Z':
            plaintext += chr((ord(ch) - ord('A') + key) % 26 + ord('A'))
        elif 'a' <= ch <= 'z':
            plaintext += chr((ord(ch) - ord('a') + key) % 26 + ord('a'))
        else:
            plaintext += ch

    return plaintext

ciphertext = "VMY{YETZ WTMT}"
# CTF{FLAG DATA}

for i in range(1, 26):
    print('{0:2d}'.format(i) + " : " + decrypt(ciphertext, i))
#  1 : WNZ{ZFUA XUNU}
#  2 : XOA{AGVB YVOV}
#  3 : YPB{BHWC ZWPW}
#  4 : ZQC{CIXD AXQX}
#  5 : ARD{DJYE BYRY}
#  6 : BSE{EKZF CZSZ}
#  7 : CTF{FLAG DATA}
#  (snip)

参考

公開鍵暗号系の処理メモ

公開鍵暗号で使用する基本処理

情報量(ビット数)の確認

n = 127*4567
print(n.bit_length())
# 20

bytes型->数値 , 数値->bytes型

from Crypto.Util.number import long_to_bytes,bytes_to_long
flag = b"CTF{TEST DATA}"

int_flag = bytes_to_long(flag)
print(int_flag)
# 1365598422142381043878339626156413

bytes_flag = long_to_bytes(int_flag)
print(bytes_flag)
# b'CTF{TEST DATA}'

平方根(Integer Square Root)を計算

def isqrt(n):
    x = n
    y = (x + n // x) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

n = 2503*2503
print(isqrt(n))
# 2503
n = 2503*2505
print(isqrt(n))
# 2503

拡張ユークリッド互除法1

import gmpy2

a = 2
b = 3
c,x,y = gmpy2.gcdext(a,b)
print(c)
print(a*x+b*y)
# 1
# 1

拡張ユークリッド互除法2(独自実装)

def xgcd(a, b):
    x0, y0, x1, y1 = 1, 0, 0, 1
    while b != 0:
        q, a, b = a // b, b, a % b
        x0, x1 = x1, x0 - q * x1
        y0, y1 = y1, y0 - q * y1
    return a, x0, y0

a = 2
b = 3
c,x,y = xgcd(a,b)
print(c)
print(a*x+b*y)
# 1
# 1

素因数分解(あまり大きくない数)

import sympy

n = 127*4567
f = sympy.factorint(n)
print(f)
# {127: 1, 4567: 1}

素因数分解(128bitくらい)(msieve使用)

>msieve.exe -e -p -q -v "97139961312384239075080721131188244842051515305572003521287545456189235939577"
(snip)
p39 factor: 299681192390656691733849646142066664329
p39 factor: 324144336644773773047359441106332937713
elapsed time 00:03:09

素因数分解フェルマー法」(pとqが近い場合)

def isqrt(n):
    x = n
    y = (x + n // x) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

def fermat(n):
    x = isqrt(n) + 1
    y = isqrt(x * x - n)

    while True:
        w = x * x - n - y * y
        if w == 0:
            break
        elif w > 0:
            y += 1
        else:
            x += 1
    return x+y, x-y

n = 94738740796943840961823530695778701408987757287583492665919730017973847138345511139064596113422435977583856843887008168202003855906708039013487349390571801141407245039011598810542232029634564848797998534872251549660454277336502838185642937637576121533945369150901808833844341421315429263207612372324026271327
print(n.bit_length())
# 1024
p, q = fermat(n)

print("p: %d" % p)
print("q: %d" % q)
# p: 9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987246769
# q: 9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987245583

素数判定

※ 264までは素数であるか決定的に判定できますが、それより大きい場合、間違える可能性があります。

import sympy

# 素数作成
n = sympy.randprime(2**60,2**63)
print(n)
print(n.bit_length())
# 8164999269211485191
# 63
print(sympy.isprime(n))
# True

二分探索

x10 = 25937424601を解くことを考えます。

x = 25937424601

y_min = 0
y_max = 10 ** 300
beki = 10

while True:
  mid = ( y_min + y_max ) // 2
  dy = pow (mid , beki)
  if dy == x:
    break
  elif dy > x:
    y_max = mid
    #print ("Too Big")
  elif dy < x:
    y_min = mid
    #print ("Too Small")

print (mid)
# 11
print(11**10)
# 25937424601

べき乗のmodを計算

※ (2**3)%5は遅いので、大きな数値の時は使わない

# 2^3
print(pow(2,3))
# 8

# 2^3 mod 5
print(pow(2,3,5))
# 3

モジュラ逆数を計算1

import gmpy2

print(gmpy2.invert(3,7))
# 5
print((3*5)%7)
# 1

モジュラ逆数を計算2

from Crypto.Util.number import inverse

print(inverse(3,7))
# 5

モジュラ逆数を計算3(独自実装)

def xgcd(a, b):
    x0, y0, x1, y1 = 1, 0, 0, 1
    while b != 0:
        q, a, b = a // b, b, a % b
        x0, x1 = x1, x0 - q * x1
        y0, y1 = y1, y0 - q * y1
    return a, x0, y0

def invmod(a, m):
    g, x, y = xgcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

print(invmod(3,7))
# 5

RSA関係

RSAの復号(n,e,d,cが分かっている場合)

e = 5
c = 225
n = 323
d = 29

m = pow(c,d,n)
print(m)
# 123
# 暗号化して確認
print(pow(m,e,n))
# 225

RSAの復号(e,c,p,qが分かっている場合)

import gmpy2

e = 5
c = 225
p = 17
q = 19
n = p*q

l = gmpy2.lcm(p-1,q-1)

d = gmpy2.invert(e,l)
m = pow(c,d,n)
print(m)
# 123
# 暗号化して確認
print(pow(m,e,n))
# 225

RSAの復号(e,c,p,qが分かっている場合)(独自実装)

import math

def lcm(x, y):
    return (x * y) // math.gcd(x, y)


def xgcd(a, b):
    x0, y0, x1, y1 = 1, 0, 0, 1
    while b != 0:
        q, a, b = a // b, b, a % b
        x0, x1 = x1, x0 - q * x1
        y0, y1 = y1, y0 - q * y1
    return a, x0, y0

def invmod(a, m):
    g, x, y = xgcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

e = 5
c = 225
p = 17
q = 19
n = p*q

l = lcm(p-1,q-1)

d = invmod(e,l)
m = pow(c,d,n)
print(m)
# 123
# 暗号化して確認
print(pow(m,e,n))
# 225

RSAの復号(nが素数の場合)

「ポーリック・ヘルマン暗号」と言うそうです。

from Crypto.Util.number import inverse

n = 29
e = 3
c = 10

d = inverse(e,n-1)
m = pow(c,d,n)
print(m)
# 21
# 暗号化して確認
print(pow(m,e,n))
# 10

RSAへの攻撃「Common Modulus Attack」(m,nが共通、異なるe,c)

import gmpy2

n=323
e1=5
e2=7
c1=225
c2=251

z,x,y = gmpy2.gcdext(e1,e2)

if x < 0:
    a = -x
    b = y
    c1_inv = gmpy2.invert(c1,n)
    c1a = pow(c1_inv, a, n)
    c2b = pow(c2, b, n)
else:
    a = x
    b = -y
    c2_inv = gmpy2.invert(c2,n)
    c1a = pow(c1, a, n)
    c2b = pow(c2_inv, b, n)

m = (c1a * c2b)%n
m,result = gmpy2.iroot(m,z)
print(result)
print(m)
#True
#123

#暗号化して確認
print(pow(m,e1,n))
print(pow(m,e2,n))
#225
#251

RSAへの攻撃「Low Public-Exponent Attack」(eが小さい(e=3等)場合)

m < n1/e の場合に攻撃可能

import gmpy2

n=1463772061
e=3
c=4913

m,result = gmpy2.iroot(c,e)

print(m)
# 17
# 暗号化して確認
print(pow(m,e,n))
# 4913

pem形式の鍵の操作(OpenSSLコマンド)

ファイルの作成

$ echo "hoge" > test.txt
$ cat test.txt 
hoge

秘密鍵・公開鍵の作成、ファイルの暗号化

$ openssl genrsa 128 > priv-key.pem
Generating RSA private key, 128 bit long modulus
....+++++++++++++++++++++++++++
.+++++++++++++++++++++++++++
e is 65537 (0x10001)
$ openssl rsa -in priv-key.pem -pubout -out pub-key.pem
writing RSA key
$ openssl rsautl -encrypt -pubin -inkey pub-key.pem -in test.txt -out test.encrypted
$ hexdump test.encrypted 
0000000 6c 0b 17 0f a4 10 73 95 c2 59 2b 55 79 c1 3f 68
0000010

ファイルの復号

$ openssl rsautl -decrypt -inkey priv-key.pem -in test.encrypted -out test.decrypted
$ cat test.decrypted 
hoge

公開鍵・秘密鍵の情報を表示

$ openssl rsa -pubin -in pub-key.pem -text -noout
Public-Key: (128 bit)
Modulus:
    00:db:c0:dd:06:f3:57:d7:d4:1a:73:59:79:b7:da:
    1b:8f
Exponent: 65537 (0x10001)

$ openssl rsa -in priv-key.pem -text -noout
Private-Key: (128 bit)
modulus:
    00:db:c0:dd:06:f3:57:d7:d4:1a:73:59:79:b7:da:
    1b:8f
publicExponent: 65537 (0x10001)
privateExponent:
    51:6a:c6:39:22:05:64:af:c0:07:8f:80:51:ce:95:
    09
prime1: 17426996876451044453 (0xf1d92153b2b4dc65)
prime2: 16761484328470451939 (0xe89cc130cac216e3)
exponent1: 9110104414105204433 (0x7e6d97b27d7576d1)
exponent2: 4086470186311562645 (0x38b60efbc0533595)
coefficient: 5413843611097833953 (0x4b21d6149d0121e1)

pem形式の鍵の操作(python)

公開鍵・秘密鍵の情報を表示

from Crypto.PublicKey import RSA

with open('pub-key.pem','r') as f:
    pub_key = RSA.importKey(f.read())

print("n: %X" % pub_key.n)
print("e:",pub_key.e)
# n: DBC0DD06F357D7D41A735979B7DA1B8F
# e: 65537

with open('priv-key.pem','r') as f:
    priv_key = RSA.importKey(f.read())

print("")
print("n: %X" % priv_key.n)
print("e:",priv_key.e)
print("p:",priv_key.p)
print("q:",priv_key.q)
print("d: %X" % priv_key.d)
# n: DBC0DD06F357D7D41A735979B7DA1B8F
# e: 65537
# p: 17426996876451044453
# q: 16761484328470451939
# d: 516AC639220564AFC0078F8051CE9509

ファイルの復号1

from Crypto.PublicKey import RSA

with open('priv-key.pem','r') as f:
    priv_key = RSA.importKey(f.read())

with open('test.encrypted','rb') as f:
    input_data = f.read()

decrypted = priv_key.decrypt(input_data)
print(decrypted)
# b'\x02D\x84\x97;\xa1q\xbf\xc9\x00hoge\n'
print(decrypted[10:])
# b'hoge\n'

ファイルの復号2

from Crypto.PublicKey import RSA
from Crypto.Util.number import bytes_to_long,long_to_bytes

with open('priv-key.pem','r') as f:
    priv_key = RSA.importKey(f.read())

n = priv_key.n
d = priv_key.d

with open('test.encrypted','rb') as f:
    input_data = f.read()

c = bytes_to_long(input_data)
m = pow(c,d,n)
m = long_to_bytes(m)
print(m)
# b'\x02D\x84\x97;\xa1q\xbf\xc9\x00hoge\n'
print(m[10:])
# b'hoge\n'

参考文献

機械学習メモ

参考資料

書籍

いろいろ機械学習の書籍を読んでみたところ、次の書籍が初心者にはわかりやすいと感じました。
本エントリでは、この書籍を参考にしています。

www.oreilly.co.jp

Pythonではじめる機械学習」は、scikit-learnを使用した実装、特徴量エンジニアリング、評価など機械学習を使うにあたり必要な内容が書かれています。
最初に読む本として最もオススメの書籍です。

URL

環境構築

Ubuntu 16.04を使用。

pyenv install 3.6.8
pyenv global 3.6.8
pip install --upgrade pip
pip install numpy scipy matplotlib ipython scikit-learn pandas pillow
pip install tensorflow==1.5
pip install keras

機械学習の基礎

機械学習とは

教師あり学習は「クラス分類」と「回帰」に大別できます。
本エントリではクラス分類を中心にまとめています。

  • クラス分類
    あらかじめ定められた選択肢の中からクラスラベルを予測すること。
    2クラス分類と多クラス分類に分けられる。

  • 回帰
    連続値を予測すること。

教師あり学習は、訓練データに基づいてモデルを構築し、(訓練データと同じ性質を持つ)未見のデータに対して正確な予想ができるようにすることが目標です。
未見のデータに対して正確に予測ができることを「汎化できている」と言います。
訓練データにおける個々の特徴にモデルを適合しすぎてしまい、未見のデータをうまく予測できないことを「過剰適合」と言います。
一方、単純すぎるモデルを選択してしまい、訓練データに対してすらうまく機能しないことを「適合不足」と言います。
過剰適合と適合不足を避け、最良の汎化性能を示すスイートスポットを見つけることが重要です。

各手法の特徴

  • 決定木
    可視化が可能で説明しやすい。
    データのスケールを考慮する必要なし。

  • ランダムフォレスト
    決定木より高速かつ頑健。 高次元の疎なデータには適さない。
    データのスケールを考慮する必要なし。

  • サポートベクタマシン
    中規模なデータセットに有効。
    データのスケールを調整する必要がある。

  • ニューラルネットワーク
    大規模なデータセットに有効。
    データのスケールを調整する必要がある。

前処理

サポートベクタマシンのようにスケールの影響を受ける手法を用いる場合、特徴量を適切なスケールに変換する必要がある。

  • StandardScaler
    個々の特徴量の平均が0で分散が1になるように変換する。
    特徴量の最大値と最小値が特定の範囲内に入ることを保証するものではない。

  • RobustScaler
    StandardScalerに近い変換方法であるが、中央値と四分位数を用いる点が異なる。
    極端に他の値と異なるような値(外れ値)を無視する。

  • MinMaxScaler
    データが0から1の間に入るように変換する。

  • Normalizer
    個々のデータポイントを、特徴量ベクトルがユークリッド長1になるように変換する。
    方向だけが問題になる場合に用いられる。

サンプルコード1(基本)

決定木

from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

digits = load_digits()
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

classifier = DecisionTreeClassifier(random_state=0)
classifier.fit(X_train, y_train)
print(classifier.score(X_test, y_test))
0.8377777777777777

ランダムフォレスト

from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

digits = load_digits()
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

classifier = RandomForestClassifier(random_state=0, n_jobs=-1)
classifier.fit(X_train, y_train)
print(classifier.score(X_test, y_test))
0.94

サポートベクタマシン

from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

digits = load_digits()
X = digits.data
y = digits.target

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

classifier = SVC(kernel="rbf", random_state=0, gamma=0.1, C=1)
classifier.fit(X_train, y_train)
print(classifier.score(X_test, y_test))
0.9377777777777778

ニューラルネットワーク

from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neural_network import MLPClassifier

digits = load_digits()
X = digits.data
y = digits.target

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

classifier = MLPClassifier(solver="lbfgs", random_state=0)
classifier.fit(X_train, y_train)
print(classifier.score(X_test, y_test))
0.9688888888888889

サンプルコード(応用)

交差検証

from sklearn.datasets import load_digits
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

digits = load_digits()
X = digits.data
y = digits.target

scaler = StandardScaler()
classifier = SVC(kernel="rbf", random_state=0, gamma=0.1, C=1)

pipeline = make_pipeline(scaler, classifier)

kf = StratifiedKFold(n_splits=10, shuffle=True, random_state=0)
cv_results = cross_val_score(pipeline, X, y, cv=kf, scoring="accuracy", n_jobs=-1)
print(cv_results)
print(cv_results.mean())
[0.95675676 0.93989071 0.97237569 0.96111111 0.90502793 0.96089385
 0.93296089 0.96067416 0.94915254 0.95454545]
0.9493389104644068

交差検証を用いたグリッドサーチ

from sklearn.datasets import load_breast_cancer
from sklearn.pipeline import make_pipeline
from sklearn.svm import SVC
from sklearn.model_selection import  train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import GridSearchCV

param_grid = {'svc__C':     [0.001, 0.01, 0.1, 1, 10, 100],
              'svc__gamma': [0.001, 0.01, 0.1, 1, 10, 100]}

cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state=0)

pipe = make_pipeline(MinMaxScaler(), SVC())

grid = GridSearchCV(pipe, param_grid, cv=5)
grid.fit(X_train, y_train)

print(grid.score(X_test, y_test))
print(grid.best_params_)
0.972027972027972
{'svc__C': 1, 'svc__gamma': 1}

CoinHive公判(第2回)傍聴メモ

CoinHive第2回公判を傍聴してきました。
傍聴内容について、どこまでブログに書いていいのかよく分かりませんので、どんな質問があったかについてのみ一部を記載しています。
もし問題がありましたら、ご指摘ください。

公判について

第2回公判 証人尋問
平成31年1月15日13:30-15:00
不正指令電磁的記録保管
証人:高木 浩光氏(産業技術総合研究所情報セキュリティ研究センター主任研究員)
場所:横浜地方裁判所第401号法廷

傍聴券の交付はありませんでしたが、傍聴希望者が多く、先着順となって傍聴できない人もいました。
私は開始45分ほど前に行きましたが、その時点で20人ほど並んでおり、最終的には50人以上(?)の傍聴希望者が来ていたように思います。
本件への関心の高さが窺えます。
ちなみに、開始10分前に法廷に入ることが出来ました。

内容

弁護側質問

JavaScriptの仕組みや、JavaScriptの性質(アクセス者はサイトアクセス前にどんなJavaScriptが実行されるか分からない、ブラウザを終了すればJavaScriptは止まる、ネイティブアプリと異なりファイルアクセスができない等の制限がある等)に関する質問
CoinHiveの実行によるPCへの影響、CoinHiveの設定値(スロットル)に関する質問
・クリプトジャッキングに関する質問

検察側質問

CoinHiveの実行によるPCへの影響に関する質問
JavaScriptの実行に関し、包括的許諾があるか。
CoinHiveについて、まわりや技術者の中で、望まない声はあるか。その人たちはなぜ望まないのか。 CoinHiveについて、規制は必要か。
・証人のブログでの表記について(魔女狩り、田舎警察 等)
マカフィー社のウイルス対策ソフトでCoinHiveが検出されることを検証したか。 ウイルス対策ソフトベンダによる検出の分類基準はあるか。

裁判官質問

CoinHive動作中に、アクセス者は(CoinHiveが動作していることに)気が付くか。
・他のサイトで実行されているJavaScriptと、CoinHiveは異なるものか。

参考URL

doocts.com

www.bengo4.com

detuxサンドボックス環境構築(暫定版)

Linuxプログラムのサンドボックスサービス「detux.org」が停止して困っていたので、GitHubに公開されているdetuxスクリプトGitHub - detuxsandbox/detux: The Multiplatform Linux Sandbox)を使って、サンドボックス環境を構築してみました。

ネットワーク回りはまだ不満があるので、とりあえず暫定版です。
GitHubに書かれていた内容を参考にしていますが、余計な処理が含まれているかもしれません。
その点はご了承ください。

目標

次のアーキテクチャ向けにコンパイルされたLinuxプログラムを実行し、通信先を調べるサンドボックス環境の構築

環境

今後は「VMware」、「Ubuntu」、「REMnux」と呼ぶことにします。
また、Ubuntu上でQEMUというソフトウェアを使って、各アーキテクチャDebianを動かしていきます。
こちらは「Debian」と呼んでいきます。

注意

この記事では、各種設定ファイルを直接編集していますが、編集前に設定ファイルのバックアップを取っておくことをオススメします。
また、スナップショットを利用できる仮想化ソフトウェアを使っている場合、適宜スナップショットを取ることをオススメします。

REMnuxの設定

REMnuxは、detuxサンドボックスから発生した通信に対してレスポンスを返すサーバの役割です。

REMnuxのサイト(https://remnux.org/)からイメージファイルをダウンロードし、VMwareにインポートします。
VMwareのネットワーク設定を「NAT」から「LANセグメント」に変更します。
REMnuxを起動し、次のとおりネットワークを設定します。

$ sudo leafpad /etc/network/interfaces
auto eth0
iface eth0 inet static
address 192.168.1.1
netmask 255.255.255.0
broadcast 192.168.1.255
network 192.168.1.0

再起動し、次のコマンドを実行します。

$ inetsim
$ fakedns
$ accept-all-ips start

Ubuntuの設定

VMwareのネットワーク設定を「NAT」にしてUbuntuを起動します。

アップデート

$ sudo apt-get update
$ sudo apt-get upgrade

detuxフォルダの作成

$ cd ~
$ mkdir detux
$ cd detux

今後、Ubuntu上での作業はすべてdetuxフォルダで行います。

detuxスクリプトのダウンロード

GitHubhttps://github.com/detuxsandbox/detux)からzipファイル等の形ですべてダウンロードし、展開したファイル・フォルダをdetuxフォルダ直下に保存します。

イメージファイルのダウンロード

「#」行はコメントです。

#x86
$ wget https://people.debian.org/~aurel32/qemu/i386/debian_wheezy_i386_standard.qcow2 -P qemu/x86/1/

#x86-64
$ wget https://people.debian.org/~aurel32/qemu/amd64/debian_wheezy_amd64_standard.qcow2 -P qemu/x86-64/1/

#arm
$ wget https://people.debian.org/~aurel32/qemu/armel/debian_wheezy_armel_standard.qcow2 -P qemu/arm/1/
$ wget https://people.debian.org/~aurel32/qemu/armel/initrd.img-3.2.0-4-versatile -P qemu/arm/1/
$ wget https://people.debian.org/~aurel32/qemu/armel/vmlinuz-3.2.0-4-versatile -P qemu/arm/1/

#mips
$ wget https://people.debian.org/~aurel32/qemu/mips/vmlinux-3.2.0-4-4kc-malta -P qemu/mips/1/
$ wget https://people.debian.org/~aurel32/qemu/mips/debian_wheezy_mips_standard.qcow2 -P qemu/mips/1/

#mipsel
$ wget https://people.debian.org/~aurel32/qemu/mipsel/vmlinux-3.2.0-4-4kc-malta -P qemu/mipsel/1/
$ wget https://people.debian.org/~aurel32/qemu/mipsel/debian_wheezy_mipsel_standard.qcow2 -P qemu/mipsel/1/

QEMU等のインストール

$ sudo apt-get -y install qemu
$ sudo apt-get -y install pcaputils
$ sudo apt-get -y install bridge-utils
$ sudo apt-get -y install python-pip
$ sudo apt-get -y install xtightvncviewer
$ sudo apt-get -y install wireshark-common

wireshark-commonインストールの際、「root以外のユーザがパケットをキャプチャできるようにしますか?」と聞かれたら「はい」を選択します。

pythonライブラリのインストール

$ pip install argparse==1.2.1
$ pip install dpkt==1.8.7
$ pip install ecdsa==0.13
$ pip install netaddr==0.7.18
$ pip install paramiko==1.16.0
$ pip install pexpect==4.0.1
$ pip install ptyprocess==0.5.1
$ pip install pycrypto==2.6.1
$ pip install python-magic==0.4.11
$ pip install wsgiref==0.1.2

QEMUをユーザ権限で実行するための設定

次の内容を追記します。「ユーザ名」の箇所はログインユーザ名(例:user)を記入してください。

$ sudo gedit /etc/sudoers
Cmnd_Alias QEMU_CMD = /usr/bin/qemu-*, /sbin/ip, /sbin/ifconfig, /sbin/brctl
ユーザ名 ALL = (ALL) NOPASSWD: QEMU_CMD

QEMU設定ファイルの編集

$ sudo gedit /etc/qemu-ifup

ファイルの内容をすべて消し、次の内容を書き込んでください。

#! /bin/sh
# Script to bring a network (tap) device for qemu up.
# The idea is to add the tap device to the same bridge
# as we have default routing to.

# in order to be able to find brctl
PATH=$PATH:/sbin:/usr/sbin
ip=$(which ip)
ifconfig=$(which ifconfig)

echo "Starting"  $1
if [ -n "$ip" ]; then
   ip link set "$1" up
else
   brctl=$(which brctl)
   if [ ! "$ip" -o ! "$brctl" ]; then
     echo "W: $0: not doing any bridge processing: neither ip nor brctl utility not found" >&2
     exit 0
   fi
   ifconfig "$1" 0.0.0.0 up
fi

switch=$(ip route ls | \
    awk '/^default / {
          for(i=0;i<NF;i++) { if ($i == "dev") { print $(i+1); next; } }
         }'
        )
    if [ -d /sys/class/net/br0/bridge/. ]; then
        if [ -n "$ip" ]; then
          ip link set "$1" master br0
        else
          brctl addif br0 "$1"
        fi
        exit    # exit with status of the previous command
    fi

echo "W: $0: no bridge for guest interface found" >&2

ネットワーク設定の確認

ifconfigでインターフェース名を調べます(ここではens33)。

$ ifconfig
ens33     Link encap:イーサネット  ハードウェアアドレス 00:0c:29:fd:21:80  
          inetアドレス:192.168.133.151  ブロードキャスト:192.168.133.255  マスク:255.255.255.0
          inet6アドレス: fe80::9e19:be2d:f2a6:f17f/64 範囲:リンク
          UP BROADCAST RUNNING MULTICAST  MTU:1500  メトリック:1
          RXパケット:1067793 エラー:196 損失:279 オーバラン:0 フレーム:0
          TXパケット:192198 エラー:0 損失:0 オーバラン:0 キャリア:0
          衝突(Collisions):0 TXキュー長:1000 
          RXバイト:1576292236 (1.5 GB)  TXバイト:10459681 (10.4 MB)
          割り込み:19 ベースアドレス:0x2000 

lo        Link encap:ローカルループバック  
          inetアドレス:127.0.0.1  マスク:255.0.0.0
          inet6アドレス: ::1/128 範囲:ホスト
          UP LOOPBACK RUNNING  MTU:65536  メトリック:1
          RXパケット:260 エラー:0 損失:0 オーバラン:0 フレーム:0
          TXパケット:260 エラー:0 損失:0 オーバラン:0 キャリア:0
          衝突(Collisions):0 TXキュー長:1 
          RXバイト:21239 (21.2 KB)  TXバイト:21239 (21.2 KB)

ネットワーク設定

ネットワーク設定を追記します。
bridge_portsの行には、先に調べたインターフェス名を指定します。

$ sudo gedit /etc/network/interfaces
auto br0
iface br0 inet static
address 192.168.1.2
network 192.168.1.0
netmask 255.255.255.0
broadcast 192.168.1.255

bridge_ports ens33
bridge_maxwait 0

dumpcapをユーザ権限で実行するための設定

ユーザ名には、ログインユーザ名を指定します。

$ sudo usermod -a -G wireshark ユーザ名
$ sudo chmod 750 /usr/bin/dumpcap
$ sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/dumpcap

Ubuntuのシャットダウン

シャットダウン後、Ubuntuからインターネットに接続できなくなるため、他に入れたいパッケージ等があればシャットダウンする前に入れておいてください。

$ sudo shutdown -h now

VMwareのネットワーク設定

「NAT」から「LANセグメント」に変更し、Ubuntuを起動します。

Debianイメージの起動

$ sudo qemu-system-i386 -hda qemu/x86/1/debian_wheezy_i386_standard.qcow2 -vnc 127.0.0.1:0 -net nic,macaddr=00:11:22:33:44:55 -net tap -monitor stdio

ここで指定したMACアドレスは後で使うのでメモしておきます。
実行後、(qemu)というプロンプトが表示されます。このターミナルは閉じないようにしてください。

VNCDebianに接続

少し時間をおいてから、別のターミナルを起動し、次のコマンドでDebianVNC接続します。

vncviewer localhost

しばらく待つと、ログインプロンプトが表示されるので、root/rootでログインします。

Debianのネットワーク設定

# vi /etc/network/interfaces
address 192.168.1.10
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1

# vi /etc/resolv.conf
nameserver 192.168.1.1

※もし、キーボードで「:」が出せない時は、shift-qを押すと「:」が出ると思います。

Debianの再起動

# shutdown -r now

シャットダウン後、VNC接続が切れるので、少し時間をおいて、ターミナルからDebianVNC接続します。

Debianから疎通確認

「//」行はコメントです。

// Ubuntuとの疎通確認
# ping 192.168.1.2

// REMnuxとの疎通確認
# ping 192.168.1.1

// DNSの確認(応答はREMnuxから返されます)
# ping aa.bb

// セグメント外IPアドレスとの疎通確認(応答はREMnuxから返されます)
# ping 5.5.5.5

疎通確認が済んだら、exitでDebianから抜け、VNCソフトウェアを落とします。

作成したDebianイメージの保存

(qemu)と表示されたプロンプトで次のコマンドを実行します。

(qemu) savevm init
(qemu) q

detux設定ファイルの編集

$ gedit detux.cfg

これまでDebianに設定した内容を、以下のように追記していきます。

#Config for x86 Linux VM
[x86-1]
#VM IP
ip=192.168.1.10
#SSH user (root)
user=root
#SSH password
password=root
#VM Nic's MAC Address
macaddr=00:11:22:33:44:55
#SSH PORT
port=22

Debianイメージの作成

ここまではx86Debianのイメージファイルを作成しましたが、x86-64、ARM、MIPS、MIPSELについても同様に作業していきます。
必要な作業は「Debianイメージの起動」から「detux設定ファイルの編集」までです。
イメージ起動時の指定方法は、以下の例を参考にしてください。 また、イメージ毎に変えた方がいい値は、DebianIPアドレスMACアドレスくらいかと思います。

  • 起動時のコマンド例
#x86-64
$ sudo qemu-system-x86_64 -hda qemu/x86-64/1/debian_wheezy_amd64_standard.qcow2 -vnc 127.0.0.1:0 -net nic,macaddr=00:11:22:33:44:56 -net tap -monitor stdio

#arm
$ sudo qemu-system-arm -M versatilepb -kernel qemu/arm/1/vmlinuz-3.2.0-4-versatile -initrd qemu/arm/1/initrd.img-3.2.0-4-versatile -hda qemu/arm/1/debian_wheezy_armel_standard.qcow2 -append "root=/dev/sda1" -vnc 127.0.0.1:0 -net nic,macaddr=00:11:22:33:44:57 -net tap -monitor stdio

#mips
$ sudo qemu-system-mips -M malta -kernel qemu/mips/1/vmlinux-3.2.0-4-4kc-malta -hda qemu/mips/1/debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -vnc 127.0.0.1:0 -net nic,macaddr=00:11:22:33:44:58 -net tap -monitor stdio

#mipsel
$ sudo qemu-system-mipsel -M malta -kernel qemu/mipsel/1/vmlinux-3.2.0-4-4kc-malta -hda qemu/mipsel/1/debian_wheezy_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0" -vnc 127.0.0.1:0 -net nic,macaddr=00:11:22:33:44:59 -net tap -monitor stdio
  • detux設定ファイルの例
#Config for x86 Linux VM
[x86-1]
#VM IP
ip=192.168.1.10
#SSH user (root)
user=root
#SSH password
password=root
#VM Nic's MAC Address
macaddr=00:11:22:33:44:55
#SSH PORT
port=22

#Config for x86-64 Linux VM
[x86-64-1]
ip=192.168.1.20
user=root
password=root
macaddr=00:11:22:33:44:56
port=22

#Config for ARM Linux VM
[arm-1]
ip=192.168.1.30
user=root
password=root
macaddr=00:11:22:33:44:57
port=22

#Config for MIPS Linux VM
[mips-1]
ip=192.168.1.40
user=root
password=root
macaddr=00:11:22:33:44:58
port=22

#Config for MIPSEL Linux VM
[mipsel-1]
ip=192.168.1.50
user=root
password=root
macaddr=00:11:22:33:44:59
port=22

detuxサンドボックスの実行

--sampleの後に解析対象のファイルを指定します。

$ python detux.py --sample test_script/example_binary1 --report reports/test.json

結果はpcapフォルダとreportsフォルダに記録されます。

オプション

$ python detux.py -h
usage: detux.py [-h] --sample SAMPLE [--cpu {x86,x86-64,arm,mips,mipsel}]
                [--int {python,perl,sh,bash}] --report REPORT

optional arguments:
  -h, --help            show this help message and exit
  --sample SAMPLE       Sample path (default: None)
  --cpu {x86,x86-64,arm,mips,mipsel}
                        CPU type (default: auto)
  --int {python,perl,sh,bash}
                        Architecture type (default: None)
  --report REPORT       JSON report output path (default: None)