読者です 読者をやめる 読者になる 読者になる

バランスを取りたい

よくCTFの記事を書きます

BCTF 2015 experiment

CTF

--- English version is here (poor English)---

Hi, we have a short survey with 50 questions. You can get a flag as award once you finished it.
$ nc 104.197.7.111 13135

問題文によれば、50問問題を解けばフラグをくれるらしい。簡単っぽい?

最初の10問は四則演算の結果が整数か否かを答える問題。
次に、

Description: The prime numbers.
The sequence starts with: 2,3,5,7,11,13,17,19,23,29
(first term is for n = 1)
n = 2418, Answer:

のような問題が出されるので、該当する数を答える。
ちなみにはじめ数問は固定の問題だったが途中からは完全にランダムな問題になった。
制限時間はそこそこ長く、1~2分くらい放っておいても大丈夫。


数列をオンライン大数列辞典で検索すると、全く同じDescriptionの数列があることがわかる。
それを元に人力で何度も計算して解いていると、LINKSの欄に計算済みの値が載っていてかつ問題で提示されるnが含まれていることに気づいた。

最後の5問はちょっと形式が変わって、計算済みの値が載っていない問題が出題された。

46問目: 2000万オーバーの順番にある素数
47問目: リストに載っていないカタラン数
48問目: Number of sets of rooted connected graphs where every block is a complete graph.
49問目: Shapes of height-balanced AVL trees with n nodes.
50問目: a(n) = number of partitions of n (the partition numbers) modulo 1000000007

最後の方はなんだかよくわからない。Pari/GPとかMathematicaに式をコピペしてCPUをぶん回した。
46問目

? prime(23338403)
%1 = 439756091

48問目

etr[p_] := Module[{b}, b[n_] := b[n] = If[n == 0, 1, Sum[Sum[d*p[d], {d, Divisors[ j]}]*b[n-j], {j, 1, n}]/n]; b]; b = etr[aa]; c = etr[b]; aa = Function[{n}, If[n == 0, 0, c[n-1]]]; a = etr[aa]; Table[a[n], {n, 0, 700}] 

49問目

a[n_] := Module[{B}, B[x_, y_, d_, a_, b_] := If[a+b <= d, Expand[x+B[x^2+2*x*y, x, d, a+b, a]], x]; Coefficient[B[z, 0, n, 1, 1], z, n]]; 
a[1298]

計算式内にExpandを入れると計算量が減って速く解ける。
これに気づいていなければ永遠に解けなかった。

50問目

? {a(n) = if( n<0, 0, polcoeff( 1 / eta(x + x * O(x^n)), n))};
? a(23370) % 1000000007
%2 = 438545155


以下、コードです

import re
import time
import gmpy
import sympy
import requests
from pwn import *
 
s = remote('104.197.7.111', 13135)
s.recvuntil("Okay, let's begin with simple questions.\n")
 
problem_number = 1
 
for _ in range(10):
    print 'No. %d --------------------------' % problem_number
    x = s.recvuntil('Answer (yes/no): ')
    print x
    m = re.search('Is (.+) an integer or not?', x)
    expr = m.group(1)
    try:
        ans = str(safeeval.expr(expr))
        print expr
        print ans
        if ('.' not in ans) or re.search(r'\.0+$', ans):
            s.send('yes\n')
        else:
            s.send('no\n')
    except:
        s.send('no\n')
    problem_number += 1
 
print s.recvuntil('Time for calculating weird integer sequences.')
 
for i in range(35):
    print 'No. %d --------------------------' % problem_number
    s.recvuntil('Description: ')
    desc = s.recvuntil('\n')[:-1]
 
    buf = s.recv(1)
    seq = None
    if 'T' in buf:
        s.recvuntil('starts with: ')
        seq = s.recvuntil('\n')[:-1]
 
    print 'Description:', desc
    print 'The sequence starts with:', seq
    search = 'name:"%s"' % desc
    if seq and len(seq) < 40:
        search += ' seq:"%s"' % seq
 
    print 'Searching %s' % search
    res = requests.get('http://oeis.org/search', params={'q': search})
 
    m = re.search('<a href="/A([0-9]+)"', res.text)
    num = m.group(1)
    print 'found page:', num
 
    res = s.recvuntil('Answer: ')
    m = re.search(r' = (\d+), ', res)
    n = int(m.group(1))
    print 'n = ', n
 
    res = requests.get('http://oeis.org/A%s/b%s.txt' % (num, num))
    m = re.search('^%d +(-?[0-9]+)' % n, res.text, re.MULTILINE)
    if m is None:
        print 'not found'
        ans = raw_input('Input answer: ')
    else:
        ans = int(m.group(1))
    print 'Answer =', ans
    s.send(str(ans) + '\n')
    problem_number += 1
    time.sleep(0.2)
 
 
print 'No. %d --------------------------' % problem_number
s.recvuntil('Description: ')
desc = '"' + s.recvuntil('\n')[:-1] + '"'
s.recvuntil('The sequence starts with: ')
seq = s.recvuntil('\n')
res = s.recvuntil('Answer: ')
m = re.search(r'n = (\d+), ', res)
n = int(m.group(1))
print 'n = ', n
ans = raw_input('Input answer: ')
print 'Answer =', ans
s.send('%s\n' % ans)
problem_number += 1
 
print 'No. %d --------------------------' % problem_number
s.recvuntil('Description: ')
desc = '"' + s.recvuntil('\n')[:-1] + '"'
s.recvuntil('The sequence starts with: ')
seq = s.recvuntil('\n')
res = s.recvuntil('Answer: ')
m = re.search(r'n = (\d+), ', res)
n = int(m.group(1))
ans = sympy.binomial(2*n, n)/(n+1)
print 'Answer =', ans
s.send('%d\n' % ans)
problem_number += 1
 
s.interactive()

ちなみにこのコードは3回に1回くらいしか完走してくれない、プログラミングは難しい。

BCTF{Y0u_h4ve_m0ar_7ermz_than_205}