ぷらおり

適当にプログラムとかCTFとか

KOSEN SECCON 2020 Write-Up(hk)

hk君のwriteupを載せています。 その他のwriteupは以下のリンクからどうぞ。
tk , つた , olton

1.デコードせよ(encode/crypto)【50】

FLAG%7B%25encoding%3C!%3E%7D

HINT: URLエンコードやパーセントエンコーディングと呼ばれます

ヒントの通り、これはエンコードされたURLなので、デコードすればよい。

URLエンコード・デコード

FLAG{%encoding<!>}

10.rotten3(encode/crypto)【100】

与えられたファイルを読むと、シーザー暗号で暗号化されたHTMLファイルだったので、Cryptii で復号した。

f:id:Mahoroa:20201121221633p:plain

復号されたHTMLファイルをブラウザで開くと、Of courseの左側にFLAGらしき画像があった。

これをダウンロードして、GIMPなどで見てみると、FLAGが読める。

FLAG{rot_ten_plus_3_is_rot13}

11.ECCp-20(encode/crypto)【250】

楕円曲線に関する問題が2問出題される。

スカラー

$ nc (IPアドレス) (ポート番号)
#####################################
Let's play with ECC!
ECC : y^2 = x^3 + x + 3 (mod 0xddccb)
P(x, y) = (0xaaaef, 0xbcdea)
#####################################
2P = (?, 0x47231) : 

最初に接続すると、楕円曲線の式やP点などの情報が与えられる。

Elliptic Curve Calculator に各パラメータを入力して計算すると、 $$ 2P = (558440, 291377) $$ が得られ、?部分を16進数変換した0x88568が答えとなる。

離散対数問題

$ nc(IPアドレス) (ポート番号)
#####################################
Let's play with ECC!
ECC : y^2 = x^3 + x + 3 (mod 0xddccb)
P(x, y) = (0xaaaef, 0xbcdea)
#####################################
2P = (?, 0x47231) : 0x88568

Right!!
Q(x, y) = (0xcddee, 0xcddef)
?P = Q : 

$$ Q=nP $$

を満たすnを求める問題となっている。この場合、値が小さいのでnを求める事ができる。

今回はPollard's rho algorithmを用いて解いた。

参考:楕円曲線上の離散対数問題に対するアプローチ(Baby-step giant-stepとPollard's rho algorithm)

from random import randint


def pollards_rho(E, P, Q, div):

    n = E.order()
    R = []
    Rab = []
    a = randint(0, n)
    b = randint(0, n)
    R.append((a*P) + (b*Q))
    Rab.append([a, b])

    M = []
    Mab = []
    for i in range(div):
        a = randint(0, n)
        b = randint(0, n)
        M.append((a*P) + (b*Q))
        Mab.append([a, b])

    S = R[0]
    i = 0
    while True:
        m = int(S[0]) % div
        S = S + M[m]
        Sa = Rab[i][0] + Mab[m][0]
        Sb = Rab[i][1] + Mab[m][1]

        if S in R:
            Ra, Rb = Rab[R.index(S)]
            d = (Sa - Ra)*inverse_mod((Rb - Sb), n) % n
            return d
        else:
            R.append(S)
            Rab.append([Sa, Sb])
            i += 1

p = 0xddccb
a = 1
b = 3

E = EllipticCurve(GF(p), [a, b])
P = E([0xaaaef, 0xbcdea])
Q = E([0xcddee, 0xcddef])

print(hex(pollards_rho(E, P, Q, 20)))
$ sage solve.sage
0x975b2
$ nc(IPアドレス) (ポート番号)
#####################################
Let's play with ECC!
ECC : y^2 = x^3 + x + 3 (mod 0xddccb)
P(x, y) = (0xaaaef, 0xbcdea)
#####################################
2P = (?, 0x47231) : 0x88568

Right!!
Q(x, y) = (0xcddee, 0xcddef)
?P = Q : 0x975b2

Excellent!!
FLAG{Su-gaku_is_tanosii}

FLAG{Su-gaku_is_tanosii}

17.熱血計算塾(programming)【50】

15-14や5+31のような計算問題が100問出題される。

渡されたauto_calc_sample_v3.pyを読むと、

# ここに計算する処理を記述する

と書かれた行があり、その辺りを変更して実行するとFLAGが得られる(過去の自分よ、なぜevalを使わなかったんだ…)。

equation = data.decode().split("=")
nums = equation[0].split("+")
if len(nums) == 1:
    nums = nums[0].split("-")
    nums[1] = "-" + nums[1]
answer = int(nums[0]) + int(nums[1])

18.15game(programming)【200】

  1. 2人で交互に数字を言い合う(プレイヤーは先攻)

  2. 最初は1からはじめ、1度に1~k個の数字を言うことができる

    (例.2から5なら、2:5と入力)

  3. nを言ったら負け

このようなゲームに5回勝利したらフラグが表示される(5ラウンド目は地獄)。

このゲームは先攻必勝であることが知られており、 $$ n-1,n-1-(k+1),n-1-2(k+1),n-1-3(k+1),\dots $$ を言えばよい。

import ptrlib


def must_nums(k, n):
    i = 0
    nums = []
    while True:
        x = n - 1 - i * (k + 1)
        i += 1
        if x < 1:
            break
        else:
            nums.append(x)
    return nums


def send_num(sock):
    # kとnを受け取る
    d = sock.recvlineafter("Up to ").decode()
    k = int(d.split()[0])
    n = int(sock.recvlineafter("BadNum is "))
    nums = must_nums(k, n)
    nums.reverse()

    # 初手
    sock.recvuntil("Your turn:")
    sock.send("1:" + str(nums[0]))

    # それ以降
    for i in range(1, len(nums)):
        m = sock.recvlineafter("My   turn: ").decode()
        s = int(m.split(":")[1])
        sock.recvuntil("Your turn:")
        sock.send(str(s + 1) + ":" + str(nums[i]))


sock = ptrlib.Socket("(IPアドレス)", (ポート番号))
# 5ラウンド
for _ in range(5):
    send_num(sock)

# 4は適当
for _ in range(4):
    print(sock.recvline().decode().strip())

FLAG{このゲームには必勝法がある・・・}

19.値段の比較はお手の物(programming)【200】

ページ内には名前や値段などが書かれた商品列があり、数秒ごとに更新される仕組みになっている。

「"最高"値の商品名を入力してね」「"最安"値の商品名を入力してね」「〇〇番目に高い商品名を入力してね」「〇〇番目に安い商品名を入力してね」の4パターンの指示が出され、それを連続で150問正解するとFLAGが表示される。

ということで、問題を自動で解くプログラムをPythonで書いた(汚いof汚い)。

import time
from selenium import webdriver
from bs4 import BeautifulSoup


# chromeを起動
driver = webdriver.Chrome("c:\\webdriver\\chromedriver.exe")
url = "(URL)"
# urlを開く
driver.get(url)
# 待機(待たないと読み込みが間に合わない)
time.sleep(10)
for _ in range(150):
    # ソースを取得
    html = driver.page_source
    # BeautifulSoupでhtml解析
    soup = BeautifulSoup(html, "html.parser")

    lead = soup.find_all(class_="lead")
    message = lead[0].text.split()[0]

    results = soup.find_all(class_="card")

    # 名前
    d1 = []
    # 値段
    d2 = []

    for r in results:
        datas = r.text.split()
        d1.append(datas[0].split(":")[1])
        d2.append(int(datas[1].split(":")[1].split("円")[0]))

    if message == '"最高"値の商品名を入力してね':
        sort_d2 = sorted(d2, reverse=True)
        x = sort_d2[0]
        name = d1[d2.index(x)]
    elif message == '"最安"値の商品名を入力してね':
        sort_d2 = sorted(d2)
        x = sort_d2[0]
        name = d1[d2.index(x)]
    else:
        m = message.split("に")
        # 番号
        find_n = int(m[0].split("番")[0])
        if m[1] == "高い商品名を入力してね":
            sort_d2 = sorted(d2, reverse=True)
        else:
            sort_d2 = sorted(d2)
        x = sort_d2[find_n - 1]
        name = d1[d2.index(x)]

    driver.find_element_by_id("name").send_keys(name)
    driver.find_element_by_class_name("btn").click()
    time.sleep(1)

FLAG{けんじつなありのさんもわすれないでね}

20.おいしく焼けました(web)【100】

Cookieに適当な値を入れると、hintとflagが出現した(ほとんど覚えていない)。

FLAG{cookie_yakiyaki}