boot2root 2020 Writeup

この大会は2020/12/6 18:30(JST)~2020/12/7 18:30(JST)に開催されました。
今回もチームで参戦。結果は2386点で124チーム中46位でした。
自分で解けた問題をWriteupとして書いておきます。

Euler's Empire (Cryptography)

pow(2, pow(2, t), n) = pow(2, pow(2, t, phi(n)), n)であることを使って復号する。nを素因数分解し、phiを算出する。

n = 611738359 * 648863389 * 775357619 * 806952673 * 834612803 * 848994661 * 856368371 * 914351299 * 940957051 * 973139887
from Crypto.Util.number import *

n = 126176329335043454027341235009057683290541781096785538088437185779950106283534462102786883
t = 1138895128842708275167
z = 22456023784134158064387550786352078427103553489348641216173010466267938277785173301732037264951635050700506776189809535326121257316490294752439819127486709456258090566784874248887194200916292508316349668172694553726134727726937633467532396106605496205841004277926961591604901357597262377953003319850049478502819166543968493292912201066602832545685929727421332846592671475883986049409546411338070434784675992855275254782158499562211464363321053581615280674425860714715529804670567409115827118589244016504450514019111124308126165185658681843519312254372108072512792854040590016772780908271525769845284796145687079308339

c = 22456023784134158064387550786352078427103553489348641216173010466267938277785173301732037264951635050700506776189809535326121257316490294752439819127486709456258090566784874248887194200916292508316349668172694553726134727726937633467532396106605496205841004277926961591604901357597262377953003319850049478502819166543968493292912201066602832545685929727421332846592671475883986049409546411338070434784675992855275254782158499562211464363321053581615280674425860714715529804670567409115827118589244016504450514019111124308126165145471041974060620715112664770187765206877310029241277556084680387431469989638143534950344

ps = [611738359, 648863389, 775357619, 806952673, 834612803, 848994661, 856368371, 914351299, 940957051, 973139887]
phi = 1
for p in ps:
    phi *= p - 1

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

Try try but don't cry (Cryptography)

フラグの前半と後半をXORしたあと、base64またはhexエンコードしている。base64やhexデコードを一回ずつしながら元に戻し、11バイトまで戻したら、フラグが"b00t2root{"で始まり、"}"で終わることを使って、復号する。

with open('chall.txt', 'r') as f:
    c = f.read()

c = c.decode('hex')
c = c.decode('hex')
c = c.decode('base64')
c = c.decode('hex')
c = c.decode('hex')
c = c.decode('hex')
c = c.decode('hex')
c = c.decode('hex')
c = c.decode('base64')
c = c.decode('base64')
c = c.decode('base64')
c = c.decode('base64')
c = c.decode('base64')
c = c.decode('base64')
c = c.decode('base64')
c = c.decode('hex')

head_flag = 'b00t2root{'
tail_flag = '}'

flag1 = chr(ord(c[-1]) ^ ord(tail_flag))
flag2 = ''
for i in range(len(head_flag)):
    flag2 += chr(ord(c[i]) ^ ord(head_flag[i]))

flag = head_flag + flag1 + flag2 + tail_flag
print flag
b00t2root{fantasticcc}

007 (Cryptography)

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

・アルファベット小文字のみ、何回かシーザー暗号(シフト数不明)
 ⇒結局1回シフト数不明の暗号をしているのと変わらない。
・base64エンコード
・文字列の0番目と1番目でXORを繰り返し、連結。最後は先頭と末尾でXOR。
・base64エンコード

XORではprintableな文字列に復号できることを前提に復号し、あとはシーザー暗号で"b"から始まることを前提に復号する。

import string

def rot(s, num):
    l = ''
    for i in s:
        if(ord(i) in range(97, 97 + 26)):
            l += chr((ord(i) - 97 + num) % 26 + 97)
        else:
            l += i
    return l

def decrypt_xor(s, k):
    d = chr(k)
    for i in range(len(s) - 1):
        d += chr(ord(s[i]) ^ ord(d[-1]))
    return d

def is_base64(s):
    b64chars = string.letters + string.digits + '/+'
    b64 = s.rstrip('\n').rstrip('=')
    for i in range(len(b64)):
        if b64[i] not in b64chars:
            return False
    return True

cipher = 'MRU2FDcePBQlPwAdVXo5ElN3MDwMNURVDCc9PgwPORJTdzATN2wAN28='
l = cipher.decode('base64')

for code in range(32, 127):
    cipher = decrypt_xor(l, code)
    if is_base64(cipher):
        break

cipher = cipher.decode('base64')
flag = rot(cipher, ord('b') - ord(cipher[0]))
print flag
b00t2root{Bond. James Bond.}

brokenRSA (Cryptography)

e = 4のため、通常のRSA暗号として復号できない。Tonelli-Shanks Algorithmにより、平方剰余を2回繰り返すことによってフラグを求める。

#!/usr/bin/python3
from Crypto.Util.number import *
def legendre(a, p):
    return pow(a, (p - 1) // 2, p)

def tonelli_shanks(a, p):
    if legendre(a, p) != 1:
        raise Exception("not a square (mod p)")

    q = p - 1
    s = 0
    while q % 2 == 0:
        q >>= 1
        s += 1

    for z in range(2, p):
        if legendre(z, p) == p - 1:
            break

    m = s
    c = pow(z, q, p)
    t = pow(a, q, p)
    r = pow(a, (q + 1) // 2, p)

    t2 = 0
    while True:
        if t == 0: return 0
        if t == 1: return r
        t2 = (t * t) % p
        for i in range(1, m):
            if t2 % p == 1:
                break
            t2 = (t2 * t2) % p
        b = pow(c, 1 << (m - i - 1), p)
        m = i
        c = (b * b) % p
        t = (t * c) % p
        r = (r * b) % p

n = 11183632493295722900188836927564142822637910363304123337597708503476804292242860556684644449701772313571249316546794463854991452685201761786385895405863639
c = 8939043592146774508422725937231398285333145869395369605787177287036646137314173055510198460479672008589091362568215564488685390459997440273900039337645280

r = tonelli_shanks(c, n)
rs = [r, n - r]

for i in range(len(rs)):
    m = tonelli_shanks(rs[i], n)
    flag = long_to_bytes(m)
    if flag.startswith(b'b00t2root'):
        print(flag.decode())
        break
b00t2root{finally_legendre_symbol_came_in_handy}

The Heist (Cryptography)

IVと鍵は同じもので暗号化されているので、IVを求め、鍵として渡せば、フラグが表示される。

[平文1ブロック目] ^ IV                  --(暗号化)--> [暗号文1ブロック目]
[平文2ブロック目] ^ [暗号文1ブロック目] --(暗号化)--> [暗号文2ブロック目]

以下の方針でIVを求める。

1.aaaaaaaaaaaaaaaa\x10...\x10 を暗号化
・aaaa... ^ IV  -> ct1
・\x10... ^ ct1 -> ct2

2.ct2を復号
・\x10... ^ ct1 ^ ivが表示される。
 ⇒iv = \x10... ^ ct1 ^ 復号結果

以上を元にスクリプトにする。

import socket

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

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('157.230.237.229', 2200))

try_pt = 'a' * 16
data = recvuntil(s, ': ')
print data + '2'
s.sendall('2\n')

h_try_pt = try_pt.encode('hex')
data = recvuntil(s, ': ')
print data + h_try_pt
s.sendall(h_try_pt + '\n')

data = recvuntil(s, '\n').rstrip()
print data
try_ct = data.split(':  ')[1].decode('hex')

data = recvuntil(s, ': ')
print data + '3'
s.sendall('3\n')

xor_key = str_xor(try_ct[:16], chr(16) * 16)

h_try_ct = try_ct[16:].encode('hex')
data = recvuntil(s, ': ')
print data + h_try_ct
s.sendall(h_try_ct + '\n')

data = recvuntil(s, '\n').rstrip()
print data
try_pt = data.split(':  ')[1].decode('hex')

key = str_xor(try_pt, xor_key).encode('hex')
data = recvuntil(s, ': ')
print data + '1'
s.sendall('1\n')

data = recvuntil(s, ': ')
print data + key
s.sendall(key + '\n')

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

実行結果は以下の通り。

1. Enter key and get flag
2. Encrypt plaintext
3. Decrypt ciphertext

Enter option: 2
Enter hex plaintext: 61616161616161616161616161616161
Ciphertext:  d035b7dd8704f7b634a65a25e29d931045f8da8740a09825fd6512895d3618b7

1. Enter key and get flag
2. Encrypt plaintext
3. Decrypt ciphertext

Enter option: 3
Enter hex ciphertext: 45f8da8740a09825fd6512895d3618b7
Plaintext:  b04ad7a8ee7193ce41c52b5c9ee2f172

1. Enter key and get flag
2. Encrypt plaintext
3. Decrypt ciphertext

Enter option: 1
Enter hex key: 706f706579657468657361696c6f7272
b00t2root{th3y_4r3_g0ing_t0_k1ll_u5}
b00t2root{th3y_4r3_g0ing_t0_k1ll_u5}

DefCamp CTF 2020 Online Writeup

この大会は2020/12/5 18:00(JST)~2020/12/7 18:00(JST)に開催されました。
今回もチームで参戦。結果は600点で216チーム中68位でした。
自分で解けた問題をWriteupとして書いておきます。

stug-reference (Steganography)

$ steghide extract -sf stug.jpg -p stug
wrote extracted data to "flag.txt".
$ cat flag.txt
ctf{32849dd9d7e7b313c214a7b1d004b776b4af0cedd9730e6ca05ef725a18e38e1}
ctf{32849dd9d7e7b313c214a7b1d004b776b4af0cedd9730e6ca05ef725a18e38e1}

yopass-go (Reverse Engineering)

$ strings yopass | grep ctf{
found bad pointer in Go heap (incorrect use of unsafe or cgo?)runtime: internal error: misuse of lockOSThread/unlockOSThreadruntime.SetFinalizer: pointer not at beginning of allocated blockstrconv: internal error: extFloat.FixedDecimal called with n == 0runtime:greyobject: checkmarks finds unexpected unmarked object obj=ctf{0962393ce380c3cf696c6c59a085cde0f7edd1382f2e9090220abdf9a6396c88}runtime: found space for saved base pointer, but no framepointer experiment
ctf{0962393ce380c3cf696c6c59a085cde0f7edd1382f2e9090220abdf9a6396c88}

why-xor (Cryptography)

keyは"ctf"あたりから推測して、復号する。

xored = ['\x00', '\x00', '\x00', '\x18', 'C', '_', '\x05', 'E', 'V', 'T', 'F', 'U', 'R', 'B', '_', 'U', 'G', '_', 'V', '\x17', 'V', 'S', '@', '\x03', '[', 'C', '\x02', '\x07', 'C', 'Q', 'S', 'M', '\x02', 'P', 'M', '_', 'S', '\x12', 'V', '\x07', 'B', 'V', 'Q', '\x15', 'S', 'T', '\x11', '_', '\x05', 'A', 'P', '\x02', '\x17', 'R', 'Q', 'L', '\x04', 'P', 'E', 'W', 'P', 'L', '\x04', '\x07', '\x15', 'T', 'V', 'L', '\x1b']

pt_head = 'ctf'

key = []
for i in range(3):
    key.append(ord(xored[i]) ^ ord(pt_head[i]))

flag = ''
for i in range(len(xored)):
    flag += chr(ord(xored[i]) ^ key[i%len(key)])
print flag
ctf{79f107231696395c004e87dd7709d3990f0d602a57e9f56ac428b31138bda258}

pbctf 2020 Writeup

この大会は2020/12/5 9:00(JST)~2020/12/7 9:00(JST)に開催されました。
今回もチームで参戦。結果は65点で457チーム中118位でした。
参加表明の問題だけですが、自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Welcome)

ルールのページにフラグがあった。コピペしようとすると、ページ遷移するので、手打ちする。

pbctf{y0u_kn0w_the_ru1es_4nd_s0_d0_i}

HITCON CTF 2020 Writeup

この大会は2020/11/28 11:00(JST)~2020/11/29 23:00(JST)に開催されました。
今回もチームで参戦。結果は50点で710チーム中234位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (reverse)

$ ssh welcome@18.176.232.130
The authenticity of host '18.176.232.130 (18.176.232.130)' can't be established.
ECDSA key fingerprint is SHA256:2nRBoRgG1pfAq1PJG4EYWpdTcr8ubnkyQ7pUIxv74fI.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '18.176.232.130' (ECDSA) to the list of known hosts.
welcome@18.176.232.130's password: 
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-1029-aws x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sat Nov 28 02:30:02 UTC 2020

  System load:  0.01              Processes:             168
  Usage of /:   25.6% of 7.69GB   Users logged in:       1
  Memory usage: 20%               IPv4 address for eth0: 172.31.25.82
  Swap usage:   0%


0 updates can be installed immediately.
0 of these updates are security updates.



The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.


The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

Last login: Sat Nov 28 02:29:58 2020 from 115.68.208.205
$ ls

ASCII ARTの汽車が走っていった。

$ cat flag
tac: failed to open 'galf' for reading: No such file or directory

単語ごとに逆になるっぽい。lsコマンドは"sl"ということだったらしい。

$ sl
flag
$ tac galf
hitcon{!0202 ftcnoctih ot emoclew}
hitcon{!0202 ftcnoctih ot emoclew}

Dragon CTF 2020 Writeup

この大会は2020/11/21 6:00(JST)~2020/11/23 6:00(JST)に開催されました。
今回もチームで参戦。結果は257点で539チーム中81位でした。
参加表明の問題だけですが、自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Miscellaneous)

freenodeで#dragonsectorチャネルに入ると、メッセージにフラグが書いてあった。

07:58 *topic : --== Dragon CTF -=- 20/11/2020 21:00 UTC to 22/11/2020 21:00 UTC -=- https://ctf.dragonsector.pl/ -=- https://dragonsector.pl/ -=- DrgnS{This_is_what_a_flag_looks_like!_Now_go_and_solve_some_real_challenges!} ==--
DrgnS{This_is_what_a_flag_looks_like!_Now_go_and_solve_some_real_challenges!}

JISCTF 2020 Qualifications Writeup

この大会は2020/11/20 2:00(JST)~2020/11/22 4:00(JST)に開催されました。
今回もチームで参戦。結果は3750点で546チーム中14位でした。
自分で解けた問題をWriteupとして書いておきます。

Common (Misc 150)

違いがある文字のfile3.txt側の文字を拾っていくと、フラグの逆順になるので、逆にしてフラグにする。

with open('file2.txt', 'rb') as f:
    data1 = f.read()

with open('file3.txt', 'rb') as f:
    data2 = f.read()

flag = ''
for i in range(len(data1)):
    if data1[i] != data2[i]:
        flag += data2[i]

flag = flag[::-1]
print flag
JISCTF{s0m3_c0mm0n_dt4_b3t33n_tw0_f1l3s_jisctf_2019}

Oh my rain!!! (Misc 150)

パスワード付きzipになっているので、クラックする。

$ zip2john OH-MY-R41N.zip > hash.txt
ver 81.9 OH-MY-R41N.zip/data.txt is not encrypted, or stored with non-handled compression type
$ john --wordlist=dict/rockyou.txt hash.txt
Warning: detected hash type "ZIP", but the string is also recognized as "ZIP-opencl"
Use the "--format=ZIP-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 128/128 AVX 4x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
management       (OH-MY-R41N.zip/data.txt)
1g 0:00:00:01 DONE (2020-11-21 09:57) 0.5235g/s 9650p/s 9650c/s 9650C/s chatty..sweetgurl
Use the "--show" option to display all of the cracked passwords reliably
Session completed

パスワード management で解凍すると、data.txtが展開される。その内容にはアルファベット大文字と数字、最後に=のパディングがあることからbase32と推測する。

import base64

with open('data.txt', 'r') as f:
    data = f.read().replace('\n', '')

while True:
    try:
        data = base64.b32decode(data)
        data = data.replace('\n', '')
    except:
        break

print data

何回もbase32デコードすると、brainf*ck言語になる。

-[------->+<]>+.-.++++++++++.--[->++++<]>-.>-[--->+<]>-.[----->+<]>++.>--[-->+++++<]>.>-[--->+<]>-.------------.[--->++<]>+.>-[--->+<]>--.+[-->+<]>+++.++++.>-[--->+<]>--.+[-->+<]>+++.---[->++<]>-.>-[----->+<]>--.+[-->+++<]>++.+++.----.-[--->++<]>+.------.-[-->+++<]>+.+++++.[++>-----<]>.[-->+++<]>--..-[--->++<]>+.+[-->+++<]>.-------.>-[----->+<]>.>--[-->+++<]>.

https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行すると、フラグが表示された。

JISCTF{TH1S-1S-S1MPL3-CH4LL3NG3}

Baby Reverse (Reversing 100)

Ghidraでデコンパイルする。

undefined8 main(int param_1,long param_2)

{
  size_t sVar1;
  long in_FS_OFFSET;
  int local_134;
  int local_130;
  int local_12c;
  char *local_118 [4];
  char *local_f8;
  char *local_f0;
  char *local_e8;
  char *local_e0;
  char *local_d8;
  char *local_d0;
  char *local_c8;
  char *local_c0;
  char *local_b8;
  char *local_b0;
  char *local_a8;
  char *local_a0;
  char *local_98;
  char *local_90;
  char *local_88;
  char *local_80;
  char *local_78;
  char *local_70;
  char *local_68;
  char *local_60;
  char *local_58;
  char local_48 [4];
  undefined local_44;
  undefined local_43;
  undefined local_42;
  undefined local_28;
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  if (param_1 == 1) {
    puts("Try harder!!");
  }
  else {
    if (param_1 == 2) {
      local_130 = 0;
      local_134 = 0;
      while( true ) {
        sVar1 = strlen(*(char **)(param_2 + 8));
        if (sVar1 <= (ulong)(long)local_134) break;
        if (*(char *)((long)local_134 + *(long *)(param_2 + 8)) == '-') {
          local_130 = local_130 + 1;
        }
        local_134 = local_134 + 1;
      }
      if (local_130 == 3) {
        puts("drink water!!");
      }
      else {
        puts("First argument must be in the format number-number-number-number");
      }
    }
    else {
      if (2 < param_1) {
        puts("Think out of the BOX!");
      }
    }
  }
  local_118[0] = "AFLdfgArTkdwelkfkmkasFFEWSWasCFsfsdfewRRWDSCFAWDHg";
  local_118[1] = "sddgrlksjpothjafkgpOEWFKQW0EOG9AWRI90VA8RUGWE9R8BIhDJFOIBSDF";
  local_118[2] = "SDSL;FKOKEIER0T9W85690843GJDKVJASFQLIGUQWO;ECLDskvjar;lgksjg1dflb";
  local_118[3] = "kjhKKHJLKlkjhlksriufer9erudskjhLKHJW3EROIWUEOIUkjkhsdllkejgrgpdffSljk";
  local_f8 = "sdrdgkj4598fgjkksdffvggvljkt66yhpooijjKJWEROJISDOVIHEERGNoqiqwjedqwkd_jaasfefn";
  local_f0 = "wewro3k34j4r39vjwewlkrfjwefoiasjALDLADSLFKWJEFWk23eqwqfweoryirut0h9dfbjlkwelkw1kj3e3f"
  ;
  local_e8 = 
  "xdlkjefwi9eru8239r2efjkldsvjlwk4g439berflklkjwff1982eiuqukKJSDFKJWEEF0OSDVJLKSKSLKJEFSJW0";
  local_e0 = 
  "098765432QWERTYUIOPASDFGHJKLZXCVBNM,W453948573498EWIFUERRGKHJFGBKDJGNBDDGksljgerogijdflkb_ckdfbj"
  ;
  local_d8 = 
  "dflfkgdjflgkjdflgsktjhworepgqajrlvLKLSADDKGJWREOGSJIFDLBKXDFGJBLKEKRTHHJOPSFGBJLSKFDGBJSLsdlfkdf4ldkfgj"
  ;
  local_d0 = 
  "DSLDFKGJWREGPOIWERJBSDFLKJBDFGLBKJklsdjff3948tueg98idduvvoierrguj0eer9gjgodfbbojiereoigsdfkgjdfdfdfdfdfNdb"
  ;
  local_c8 = 
  "dglkjerge9r0gwe9fwfoiuiSKDFJFWER9GDEORIJGDFOIBJRTOIBJDFOBIKJER90GGU3U3EGF09USEDGOISDFJGDOetewrtwertwfwFIJB_DF"
  ;
  local_c0 = 
  "XFLLKVJERGPERG9W8UER0G9SPUDFBOIIJDFOIIBSRT9BURERFBOIDFDBKSJDFBSDFOIBUJSFOIBJDDFLKLKBJSRT09HWRBBOJIDFJOIdfdfsdeffsdWOI"
  ;
  local_b8 = 
  "DFDVLKJWWEFF983R7239812IUEASDFKJSDKJSFrgsdfgsdfgsdfgsddRTOISIJFBDFKGJBDFOGOLNIJDdherethdrgbrffbPWRGQ98ETW98EFSIUDdfsd4fsdfVH"
  ;
  local_b0 = 
  "XDKXDJVER9847T398289341092E09EFUFDVJW5Rdfgdfg947T98EREGIUUDFVHJEE9R88TUEED99FVIUHJW9E8FFUQW039DID0CVIDKDFV0E9RUVE09UUJdsfsdfSsdfs"
  ;
  local_a8 = 
  "REZEVkxLSldXRUZGOTgzUjcyMzk4MTJJVUVBU0RT0lTSUpGQkRGS0dKQkRGT0dPTE5JSkRkaGVyZXRoZHJnYnJmZmJQV1JHUTk4RVRXOThFRlNJVURWSAsdfsdfsdfsdfysd=="
  ;
  local_a0 = 
  "3158117bae155dab40b42244b9554d0d92ad05e8f11d5fe22d3334b84fd3aa5b0289431069ae8757448ae6dfd61c8edasfsdfsdfsdefweoifusiodfjsdfdfsdfsdfsdf_sdfk"
  ;
  local_98 = 
  "sdldfkerjguwoierfhjKJLJSDFHROIGEHVSDOIHkjsdhgeorighsdslvkndfljkbfjgbldhkfjghdflgkjsdfpogewrigwweroiojdflkvjdfklgdfjgdlfkgjdfgdlfSDFDRGERGDFRGEkj21"
  ;
  local_90 = 
  "xlxdkfjddrgloeiut9348ut398gf3jur9gfeirjklefejlkKLSDSJGEORIGEJRGOIEJRGOEIRGJDLFKGDRGEIOWUERWEIORUWEksehj9f38ru239r2e9jf3948fj34oiferjgDRGSDFGDSFGSD3FGSDFGSDeoirjg"
  ;
  local_88 = 
  "dfgldkjgw958yt3u4ro09eijusdikjhOSIDHFWW9E8FWOJEFSWJFDLKFDJskdgher49g383egijoigjerlgkejrglgeworigjwregvdfmvdfoigerugeoirjgrolgDFGDSFGSDFGSDFGSDFGSDFGWERTIWjidfdlkvvgdjfge0rg"
  ;
  local_80 = 
  "dlkfjdflfkdfjfgjoi4353KSDJDHFW938RR23IFHWksdjdhgweoituweoigjeglksdgjdslkgjweoijdskvgjdfsklbjhdfgwoeireruqwopqiuyiuIUQWY293R23OIF23R9O32R2378R23IUR2H3sfsdfsdfsdfsdfsfsdfsdfs3dfsR"
  ;
  local_78 = 
  "SDSFLKSDFLRKGJkjsdhfw933r823urowieruwoeitueoriteuergoidfjgdlfkgjeeroigerjgoijgvdlllllllllllllllllllkoerieurtoieruteroituDFSDFSDFSDFSDFSDFSDFSERTWEReeewereeeeeeorituwpoqwiposfkjsRld"
  ;
  local_70 = 
  "fdghjkytrewqdfgbh9u8toiu4jrwefud98uvgijerkwefiudsvosoifjwrek3efoiusdvffijkewioiduvjekwiosdouvijsdfkelodsviudjsfqeudvs9oijfkiodvdknfdhewuwksam,m,.fgm,glkflkflkvoicxjdksedhsfvoijnfsdskehdvio"
  ;
  local_68 = 
  "KSJDFHSIDFWEUIshf3w894efiowejknefioewuwghjkfkkdfkjvdfkdvbfiourifiuruifodrjifjoonvjfkiivojiodjdoivvjdfovdifjdvooooooooooooooooooooooooooooooooffijvdlfkvdjfl2329382@@@@@##$#$#$$%vdfkjvdlfkvj1df"
  ;
  local_60 = 
  "lxkcvjdfolvekjvkldkfvjdlllllllllllllllkeowfiefwjeofwoejwfeeeeeeeeeeeeeeeeeeeeeeeeidjsdlvskdfjsdlkfjweoiweifjovdjivoeijrveorijverlkjelrbkkbjerlkejervl%$$%TRGEFGFHFGHFGHJFGHkvjeerlgjerovjervoerNijve"
  ;
  local_58 = 
  "LKSDJFWKERJ329FWJEFLSSKDFJSDLKsksejfeh4foeiwrjverlg4jrtlgelkjSLKDFJGBRTORJTBLKDFJBE9RTG3U4GFWOIJjveroibjrthhjtie0orithrjtbrhoithjrotiwjer0oihgeirtDFGDFGDDFGWERGRETHRTYJR^TYUJTYJT%%oidjfgblektrjbelgrgkbjdfnglkjdflg"
  ;
  local_12c = 0;
  bzero(local_48,0x23);
  local_48[0] = 'J';
  local_48[1] = 0x49;
  local_48[2] = 0x53;
  local_48[3] = 0x43;
  local_44 = 0x54;
  local_43 = 0x46;
  local_42 = 0x7b;
  local_134 = 7;
  while (local_134 < 0x20) {
    if (local_12c == 0) {
      local_48[local_134] = local_118[0][8];
    }
    else {
      sVar1 = strlen(local_118[local_12c + -1]);
      local_48[local_134] = local_118[local_12c][sVar1];
    }
    local_134 = local_134 + 1;
    local_12c = local_12c + 1;
  }
  local_28 = 0x7d;
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

このコードからlocal_48のデータを算出する。

data = [
    'AFLdfgArTkdwelkfkmkasFFEWSWasCFsfsdfewRRWDSCFAWDHg',
    'sddgrlksjpothjafkgpOEWFKQW0EOG9AWRI90VA8RUGWE9R8BIhDJFOIBSDF',
    'SDSL;FKOKEIER0T9W85690843GJDKVJASFQLIGUQWO;ECLDskvjar;lgksjg1dflb',
    'kjhKKHJLKlkjhlksriufer9erudskjhLKHJW3EROIWUEOIUkjkhsdllkejgrgpdffSljk',
    'sdrdgkj4598fgjkksdffvggvljkt66yhpooijjKJWEROJISDOVIHEERGNoqiqwjedqwkd_jaasfefn',
    'wewro3k34j4r39vjwewlkrfjwefoiasjALDLADSLFKWJEFWk23eqwqfweoryirut0h9dfbjlkwelkw1kj3e3f',
    'xdlkjefwi9eru8239r2efjkldsvjlwk4g439berflklkjwff1982eiuqukKJSDFKJWEEF0OSDVJLKSKSLKJEFSJW0',
    '098765432QWERTYUIOPASDFGHJKLZXCVBNM,W453948573498EWIFUERRGKHJFGBKDJGNBDDGksljgerogijdflkb_ckdfbj',
    'dflfkgdjflgkjdflgsktjhworepgqajrlvLKLSADDKGJWREOGSJIFDLBKXDFGJBLKEKRTHHJOPSFGBJLSKFDGBJSLsdlfkdf4ldkfgj',
    'DSLDFKGJWREGPOIWERJBSDFLKJBDFGLBKJklsdjff3948tueg98idduvvoierrguj0eer9gjgodfbbojiereoigsdfkgjdfdfdfdfdfNdb',
    'dglkjerge9r0gwe9fwfoiuiSKDFJFWER9GDEORIJGDFOIBJRTOIBJDFOBIKJER90GGU3U3EGF09USEDGOISDFJGDOetewrtwertwfwFIJB_DF',
    'XFLLKVJERGPERG9W8UER0G9SPUDFBOIIJDFOIIBSRT9BURERFBOIDFDBKSJDFBSDFOIBUJSFOIBJDDFLKLKBJSRT09HWRBBOJIDFJOIdfdfsdeffsdWOI',
    'DFDVLKJWWEFF983R7239812IUEASDFKJSDKJSFrgsdfgsdfgsdfgsddRTOISIJFBDFKGJBDFOGOLNIJDdherethdrgbrffbPWRGQ98ETW98EFSIUDdfsd4fsdfVH',
    'XDKXDJVER9847T398289341092E09EFUFDVJW5Rdfgdfg947T98EREGIUUDFVHJEE9R88TUEED99FVIUHJW9E8FFUQW039DID0CVIDKDFV0E9RUVE09UUJdsfsdfSsdfs',
    'REZEVkxLSldXRUZGOTgzUjcyMzk4MTJJVUVBU0RT0lTSUpGQkRGS0dKQkRGT0dPTE5JSkRkaGVyZXRoZHJnYnJmZmJQV1JHUTk4RVRXOThFRlNJVURWSAsdfsdfsdfsdfysd==',
    '3158117bae155dab40b42244b9554d0d92ad05e8f11d5fe22d3334b84fd3aa5b0289431069ae8757448ae6dfd61c8edasfsdfsdfsdefweoifusiodfjsdfdfsdfsdfsdf_sdfk',
    'sdldfkerjguwoierfhjKJLJSDFHROIGEHVSDOIHkjsdhgeorighsdslvkndfljkbfjgbldhkfjghdflgkjsdfpogewrigwweroiojdflkvjdfklgdfjgdlfkgjdfgdlfSDFDRGERGDFRGEkj21',
    'xlxdkfjddrgloeiut9348ut398gf3jur9gfeirjklefejlkKLSDSJGEORIGEJRGOIEJRGOEIRGJDLFKGDRGEIOWUERWEIORUWEksehj9f38ru239r2e9jf3948fj34oiferjgDRGSDFGDSFGSD3FGSDFGSDeoirjg',
    'dfgldkjgw958yt3u4ro09eijusdikjhOSIDHFWW9E8FWOJEFSWJFDLKFDJskdgher49g383egijoigjerlgkejrglgeworigjwregvdfmvdfoigerugeoirjgrolgDFGDSFGSDFGSDFGSDFGSDFGWERTIWjidfdlkvvgdjfge0rg',
    'dlkfjdflfkdfjfgjoi4353KSDJDHFW938RR23IFHWksdjdhgweoituweoigjeglksdgjdslkgjweoijdskvgjdfsklbjhdfgwoeireruqwopqiuyiuIUQWY293R23OIF23R9O32R2378R23IUR2H3sfsdfsdfsdfsdfsfsdfsdfs3dfsR',
    'SDSFLKSDFLRKGJkjsdhfw933r823urowieruwoeitueoriteuergoidfjgdlfkgjeeroigerjgoijgvdlllllllllllllllllllkoerieurtoieruteroituDFSDFSDFSDFSDFSDFSDFSERTWEReeewereeeeeeorituwpoqwiposfkjsRld',
    'fdghjkytrewqdfgbh9u8toiu4jrwefud98uvgijerkwefiudsvosoifjwrek3efoiusdvffijkewioiduvjekwiosdouvijsdfkelodsviudjsfqeudvs9oijfkiodvdknfdhewuwksam,m,.fgm,glkflkflkvoicxjdksedhsfvoijnfsdskehdvio',
    'KSJDFHSIDFWEUIshf3w894efiowejknefioewuwghjkfkkdfkjvdfkdvbfiourifiuruifodrjifjoonvjfkiivojiodjdoivvjdfovdifjdvooooooooooooooooooooooooooooooooffijvdlfkvdjfl2329382@@@@@##$#$#$$%vdfkjvdlfkvj1df',
    'lxkcvjdfolvekjvkldkfvjdlllllllllllllllkeowfiefwjeofwoejwfeeeeeeeeeeeeeeeeeeeeeeeeidjsdlvskdfjsdlkfjweoiweifjovdjivoeijrveorijverlkjelrbkkbjerlkejervl%$$%TRGEFGFHFGHFGHJFGHkvjeerlgjerovjervoerNijve',
    'LKSDJFWKERJ329FWJEFLSSKDFJSDLKsksejfeh4foeiwrjverlg4jrtlgelkjSLKDFJGBRTORJTBLKDFJBE9RTG3U4GFWOIJjveroibjrthhjtie0orithrjtbrhoithjrotiwjer0oihgeirtDFGDFGDDFGWERGRETHRTYJR^TYUJTYJT%%oidjfgblektrjbelgrgkbjdfnglkjdflg'
]

flag = ''
flag += 'J'
flag += chr(0x49)
flag += chr(0x53)
flag += chr(0x43)
flag += chr(0x54)
flag += chr(0x46)
flag += chr(0x7b)
for i in range(7, 0x20):
    if i == 7:
        flag += data[0][8]
    else:
        l = len(data[i-8])
        flag += data[i-7][l]
flag += chr(0x7d)
print flag
JISCTF{Th1S_1S_4N_e4Sy_R3v3Rs1Ng}

Rev 102 (Reversing 150)

$ file be_true 
be_true: python 2.7 byte-compiled
$ mv be_true be_true.pyc
$ uncompyle6 be_true.pyc
# uncompyle6 version 3.7.4
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.17 (default, Sep 30 2020, 13:38:04) 
# [GCC 7.5.0]
# Embedded file name: be_true.py
# Compiled at: 2019-12-06 22:36:32
import operator
flag = 0
power = (12 * flag + 44) / 4 - 1234 / 617 * flag - sum([1, 4, 7])
flag *= power
ppc = filter(lambda cc22: not any(cc22 % uu22 == 0 for uu22 in range(2, cc22)), range(2, 10000))
dat = reduce(operator.mul, (ppc[i] ** int(str(flag)[i]) for i in range(len(str(flag)))))
print dat == 3560267726635400465627540581996487760054035685444946475657505105403063442856963534755133504166293811169018648637812735995934052617540980495952470560844800443626277335201401028364068797946796579956457852457279957300619892623751175557650873016730889125068146398488627192356718363004881175012104986391177956396290571688585499385019181192105543180552708742672275440530116680223358366613050183523086165910780504269235376773473500L
# okay decompiling be_true.pyc

逆算していけばよい。

ppcは2-10000の素数の配列。
flagは数値。

dat = 2**(flagの1桁目) * 3**(flagの2桁目) * 5**(flagの3桁目) * ...

期待値を素因数分解すればpowerを掛けた後のflagの値がわかる。

flag = 27390185364980124152929536025508671562111273743374147056

この値をCとする。

power = (12 * flag + 44) / 4 - 1234 / 617 * flag - sum([1, 4, 7])
      = (3 * flag + 11) - 2 * flag - 12
      = flag - 1

C = flag * (flag - 1)

2次方程式を解けばフラグがわかる。

JISCTF{5233563352533350594343304433}

このままだとフラグが通らない。この数値を2桁ずつ16進数としてデコードする。

from sympy import *

ppc = filter(lambda cc22: not any(cc22 % uu22 == 0 for uu22 in range(2, cc22)), range(2, 10000))
target = 3560267726635400465627540581996487760054035685444946475657505105403063442856963534755133504166293811169018648637812735995934052617540980495952470560844800443626277335201401028364068797946796579956457852457279957300619892623751175557650873016730889125068146398488627192356718363004881175012104986391177956396290571688585499385019181192105543180552708742672275440530116680223358366613050183523086165910780504269235376773473500

fac = factorint(target)
C = ''
mal = 1
for i in range(len(ppc)):
    if ppc[i] in fac:
        power = fac[ppc[i]]
        C += str(power)
        mal *= pow(ppc[i], power)
    else:
        C += '0'
    if mal == target:
        break

C = int(C)

x = symbols('x')
ans = solve(x**2 - x - C, x)

for i in range(len(ans)):
    if ans[i] > 0:
        flag = ans[i]
        break

print '[+] flag =', flag

flag = str(flag)
msg = ''
for i in range(0, len(flag), 2):
    msg += chr(int(flag[i:i+2], 16))

flag = 'JISCTF{%s}' % msg
print '[*] flag =', flag

実行結果は以下の通り。

[+] flag = 5233563352533350594343304433
[*] flag = JISCTF{R3V3RS3PYCC0D3}
JISCTF{R3V3RS3PYCC0D3}

Malicious (Forensics 100)

docxが添付されている。ZIP解凍したら、バーコードの画像が入っているので、読み取る。
f:id:satou-y:20201201221758p:plain

JISCTF{B4RC0D3_1M4G3_2019}

So Easy (Forensics 100)

No.182のパケットで認証情報をPOSTしていて、usernameにフラグが設定されていた。

Form item: "username" = "JISCTF{V3RY_34SY_PC4P_F1L3}"
JISCTF{V3RY_34SY_PC4P_F1L3}

Malicious 2 (Forensics 100)

docxが添付されている。

$ file mycv.docx 
mycv.docx: CDFV2 Encrypted

パスワードがかかっているので、johnでクラックする。

$ office2john.py mycv.docx > hash.txt
$ john --wordlist=dict/rockyou.txt hash.txt
Warning: detected hash type "Office", but the string is also recognized as "office-opencl"
Use the "--format=office-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (Office, 2007/2010/2013 [SHA1 128/128 AVX 4x / SHA512 128/128 AVX 2x AES])
Cost 1 (MS Office version) is 2007 for all loaded hashes
Cost 2 (iteration count) is 50000 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
princess101      (mycv.docx)
1g 0:00:00:22 DONE (2020-11-21 07:16) 0.04349g/s 434.6p/s 434.6c/s 434.6C/s sammy2..nopassword
Use the "--show" option to display all of the cracked passwords reliably
Session completed

パスワード princess101 で開くと、フラグが書いてあった。

JISCTF{H4PPY_HUNT1NG}

Unknow Ransomware (Forensics 150)

13回base64デコードすると、PNGファイルの逆順の内容になっているので、元に戻す。

with open('flag.enc', 'rb') as f:
    data = f.read()

for _ in range(13):
    data = data.decode('base64')

with open('flag.png', 'wb') as f:
    f.write(data[::-1])

生成された画像に逆順でフラグが書いてあった。
f:id:satou-y:20201201222253p:plain

JISCTF{R3V3RS3_1M4G3_C0NT3NTS}

Colorfull (Forensics 300)

No.524パケットからFTPで送受信しているfiles.zipを保存する。パスワード付きZIPになっているが、パスワードはパケットに見当たらないので、クラックしてみる。

$ zip2john files.zip > hash.txt
ver 81.9 files.zip/secret_data.txt is not encrypted, or stored with non-handled compression type
$ john --wordlist=dict/rockyou.txt hash.txt
Warning: detected hash type "ZIP", but the string is also recognized as "ZIP-opencl"
Use the "--format=ZIP-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 128/128 AVX 4x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
labeba           (files.zip/secret_data.txt)
1g 0:00:00:01 DONE (2020-11-21 10:29) 0.9900g/s 10138p/s 10138c/s 10138C/s toodles..11221122
Use the "--show" option to display all of the cracked passwords reliably
Session completed

パスワード labeba で解凍すると、secret_data.txtが展開された。その内容は数字が3個、"-"区切りで10693行並んでいる。

34-177-76
34-177-76
34-177-76
34-177-76
34-177-76
34-177-76
34-177-76
34-177-76
34-177-76
34-177-76
34-177-76
34-177-76
  :
34-177-76
29-100-17
0-0-0
8-100-66
34-177-76
29-100-17
0-0-31
24-177-66
20-40-0
0-0-17
20-152-76
  :

RGBを表していると推測する。画像にするためには、縦、横を知る必要があるので、行数を素因数分解してみる。

10693 = 17 * 17 * 37

横:17 * 17、縦:37で画像にしてみる。

from PIL import Image

WIDTH = 17 * 17
HEIGHT = 37

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

img = Image.new('RGB', (WIDTH, HEIGHT), (255, 255, 255))

i = 0
for y in range(HEIGHT):
    for x in range(WIDTH):
        r = int(lines[i].rstrip().split('-')[0])
        g = int(lines[i].rstrip().split('-')[1])
        b = int(lines[i].rstrip().split('-')[2])
        img.putpixel((x, y), (r, g, b))
        i += 1

img.save('flag.png')

生成した画像にフラグが書いてあった。
f:id:satou-y:20201201222459p:plain

JISCTF{EXF1LT3R4T3D_D4T4_1N_1M4G3_F1L3}

Hidden (Crypto-Stego 100)

StegSolveで開き、[Analyse]-[Data Extract]で、RGB、それぞれのLSBのみチェックを入れると、フラグが現れる。
f:id:satou-y:20201201222611p:plain

JISCTF{G00D_J0B_Y0U_EXTR4KT_M3!!!}

Upside Down!! (Crypto-Stego 150)

{}の中がAtbash暗号で暗号化されている。https://www.dcode.fr/atbash-cipherで復号する。

JISCTF{upside_down_english_characters_cryptography}

Baby Crypto (Crypto-Stego 200)

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

ct: 現在時刻(小数第2位まで)
ctをシードにランダム値をとる。
k1: flagの長さだけ256までのランダム値の配列
ciphertext: flag+c1とk1+[0x99]*len(ct)でXOR

末尾18桁を0x99とXORすれば時刻がわかり、ランダム値もわかるので、復号できる。

#!/usr/bin/python3
import random

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

ct = ''
for i in range(18):
    ct += chr(enc[-18+i] ^ 0x99)
print('[+] ct =', ct)

random.seed(ct)
k1 = [random.randrange(256) for _ in range(len(enc) - 18)]

flag = ''
for i in range(len(k1)):
    flag += chr(enc[i] ^ k1[i])

print(flag)

実行結果は以下の通り。

[+] ct = 1605733600.6308804
JISCTF{B4BY_ENCRYPT10N_JISCTF2020_QUALIFICATION_RND_101}
JISCTF{B4BY_ENCRYPT10N_JISCTF2020_QUALIFICATION_RND_101}

Logic (Crypto-Stego 200)

1バイトの鍵のXOR暗号と推測し、復号する。

with open('ciphertext.txt', 'rb') as f:
    enc = f.read()

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

print flag
JISCTF{W34K_X0R_3NKRYPT10N_M4N_$!}

Not baby crypto (Crypto-Stego 300)

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

■generate_key()
・ran: ランダム32バイト文字列の16進数文字列
・key_list: ranを2バイトごとにした配列
・key_listの各要素を10で割った余りを結合して返す。

■encrypt(key)
・file: 'plainData'ディレクトリのファイル
・enc_file: fileの拡張子をencにしたもの
・平文の各文字と鍵のXORをとる。(A)
・(A)の各要素と鍵の2倍のXORをとる。

各文字のASCIIコードは末端6bitしか影響しないので、それより前の情報から1ビットずつ鍵を導く。その後に末端6ビットを条件を満たすようにして鍵を求める。鍵が分かればXORで復号できる。

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

enc_l = [int(enc[i:i+32]) for i in range(0, len(enc), 32)]

b_enc_base = bin(enc_l[0] - (enc_l[0] & 0b1111111))[2:]

k1 = '0'
k2 = '1'
for i in range(len(b_enc_base)-7):
    c = int(b_enc_base[i+1])
    k1 += k2[-1]
    k2 += str(c ^ int(k1[-1]))

k1 += '0' * 6
k1_base = int(k1, 2)

for i in range(64):
    key = k1_base + i
    cb0 = ord('J') ^ key
    ct0 = cb0 ^ (key * 2)
    if ct0 == enc_l[0]:
        break

flag = ''
for c in enc_l:
    code = c ^ key ^ (key * 2)
    flag += chr(code)
print flag
JISCTF{R4ND0M_NUMB3RS_4S_K3Y_!!!}

Not that easy!! (Crypto-Stego 400)

Fを0、Tを1として逆順にすると、2進数としてデコードできそう。このあと、また0, 1の文字列になる。いろいろ試した結果、ベーコニアン暗号と推測できるので、対応付けて復号する。

bacon = {'00000': 'A', '00001': 'B', '00010': 'C', '00011': 'D', '00100': 'E',
    '00101': 'F', '00110': 'G', '00111': 'H', '01000': 'I', '01001': 'K',
    '01010': 'L', '01010': 'M', '01100': 'N', '01101': 'O', '01110': 'P',
    '01111': 'Q', '10000': 'R', '10001': 'S', '10010': 'T', '10011': 'U',
    '10100': 'W', '10101': 'X', '10110': 'Y', '10111': 'Z'}

with open('my_data.dat', 'r') as f:
    data = f.read().rstrip()

data = data.replace('F', '0')
data = data.replace('T', '1')
data = data[::-1]

b64 = ''
for i in range(0, len(data), 8):
    b64 += chr(int(data[i:i+8], 2))

ct = b64.decode('base64').rstrip()
flag = ''
for i in range(0, len(ct), 5):
    flag += bacon[ct[i:i+5]]

flag = 'JISCTF{%s}' % flag
print flag
JISCTF{BACONCIPHERISNOTGOODTOENCRYPTDATA}

Affinity CTF Lite Writeup

この大会は2020/11/17 3:00(JST)~2020/11/18 3:00(JST)に開催されました。
今回もチームで参戦。結果は1116点で690チーム中100位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome! (OTHER 1)

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

AFFCTF{This_is_just_to_check_if_ALL_is_ok}

DiscOrder (MISC 5)

Discordに入り、#2020-liteチャネルのトピックを見ると、フラグが書いてあった。

Welcome to AFFCTF 2020. Good Flag Hunting!  ```AFFCTF{Pr0p3r_C0ms_aR3_4lways_g00d!}```
AFFCTF{Pr0p3r_C0ms_aR3_4lways_g00d!}

DIGme (OSINT 10)

$ dig -t txt www.affinityctf.com

; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> -t txt www.affinityctf.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58424
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;www.affinityctf.com.		IN	TXT

;; ANSWER SECTION:
www.affinityctf.com.	5	IN	TXT	"QUZGQ1RGe0hlcmUnNXkwdXJUcmVhNXVyZX0="

;; Query time: 117 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Tue Nov 17 22:37:27 JST 2020
;; MSG SIZE  rcvd: 97

あやしいbase64文字列があるので、デコードする。

$ echo QUZGQ1RGe0hlcmUnNXkwdXJUcmVhNXVyZX0= | base64 -d
AFFCTF{Here'5y0urTrea5ure}
AFFCTF{Here'5y0urTrea5ure}

Black Dots (STEGO 10)

小さい白黒のドットの画像ファイルが添付されている。横に白は0、黒は1で2進数にし、デコードする。

from PIL import Image

img = Image.open('image.png').convert('L')
w, h = img.size

bin_flag = ''
for y in range(h):
    for x in range(w):
        v = img.getpixel((x, y))
        if v == 0:
            bin_flag += '1'
        else:
            bin_flag += '0'

flag = ''
for i in range(0, len(bin_flag), 8):
    flag += chr(int(bin_flag[i:i+8], 2))

print flag
AFFCTF{MonochromatiC ThinkinG}

One is missing (STEGO 10)

$ strings full_of__cuteness.jpg | grep AFFCTF
Hidden.txtAFFCTF{HIDDENKITTEN}PK
AFFCTF{HIDDENKITTEN}

Revendless64 (STEGO 30)

逆順にしてbase64デコードすることを繰り返す。

with open('z', 'r') as f:
    data = f.read()

while True:
    try:
        data = data[::-1]
        data = data.decode('base64')
    except:
        break

flag = data[::-1]
print flag
AFFCTF{s1mPle_Rev4s_D0Ne_oNe1}

Char Wrap (FORENSICS 10)

$ strings charwrap
    :
AFFCTF{yH
ou_foundH
_somethiH
ng!}
    :
AFFCTF{you_found_something!}

Shifter Salad (CRYPTO 10)

アルファベットのみシフト数を増やしていく。

import string

enc = 'AGHFXK{5ai5b1cee10z}'

flag = ''
i = 0
for c in enc:
    if c in string.uppercase:
        index = (string.uppercase.index(c) - i) % 26
        flag += string.uppercase[index]
        i += 1
    elif c in string.lowercase:
        index = (string.lowercase.index(c) - i) % 26
        flag += string.lowercase[index]
        i += 1
    else:
        flag += c

print flag
AFFCTF{5ub5t1tut10n}

Hongqiao (CRYPTO 10)

sha1のようなので、CrackStationでクラックする。

AFFCTF{Unimaginatively}

dias skeerG tneicna (CRYPTO 20)

以下の表のような対応になっていると推測し、対応付ける。

  5 4 3 2 1
5 A B C D E
4 F G H I K
3 L M N O P
2 Q R S T U
1 V W X Y Z
※Iの箇所はJになることもある。
554545532245{22434223_4223_42212322_55_234234313551_34553131423344}
A F F C T F {T H I S _I S _J U S T _A _S I M P L E _M A P P I N G }
AFFCTF{THIS_IS_JUST_A_SIMPLE_MAPPING}

BreakMe (CRYPTO 500)

RSA暗号。公開鍵パラメータのnを素因数分解する。

p = 286748798713412687878508722355577911069
q = 300290718931931563784555212798489747397

あとはそのまま復号する。

from Crypto.PublicKey import RSA
from Crypto.Util.number import *

with open('public.pem', 'r') as f:
    pub_data = f.read()

with open('encrypted.txt', 'rb') as f:
    c = bytes_to_long(f.read())

pubkey = RSA.importKey(pub_data)
n = pubkey.n
e = pubkey.e

p = 286748798713412687878508722355577911069
q = 300290718931931563784555212798489747397

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

復号結果は以下の通り。

Ca1シ隴5蕈 AFFCTF{PermRecord}
AFFCTF{PermRecord}

Collision Course (CRYPTO 500)

100000b以下で、"AFFCTF"が含まれる2つの異なるファイルで、同じmd5になるものをアップロードすればよい。
baseに"AFFCTF"を含めて、fastcollでファイルを作成する。

$ cat base.txt
AFFCTF
$ ./clone-fastcoll/fastcoll base.txt
Generating first block: .....
Generating second block: S10.
use 'md5sum md5_data*' check MD5

md5_data1とmd5_data2をアップロードする。
f:id:satou-y:20201128131524p:plain

Checking, please wait...
String found in the first file
String found in the second file
Checking if files are different...
Files are different
Checking if files are MD5 Hash is the same for both files...
MD5Hashes are the same. You were right. The flag is: AFFCTF{One_Way_Or_Another}
AFFCTF{One_Way_Or_Another}