3DSCTF Writeup

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

How to submit a flag (TUTORIAL)

問題文にフラグが書かれている。

3DS{mY_F1rsT_fL4G}

Capo Di Tutti Capi (CRYPTO)

$ nc capoditutticapi01.3dsctf.org 8001

                +++     3DSCTF - Capo Di Tutti Capi     +++

 [+] One year after the death of the one of the most famous members of the
     mafia, the FBI found a notebook with a few weird annotations.
 
 [+] Trying to use the same strategy as the last time, all the FBI experts
     failed to translate the book. Look if you have some luck! 

 [+] Type start to read the first note: start
     Openning the book...

 [+] Page 1/10 [c, r, p]: [HGZBMEDSYKPVJCTQOLRWAUFIXN, 18, FQVKFMF SKDWBFMP DIVFE KFYJHKF FQVKFMF PDBJVHDEP]
     The answer is:

何をやっているかよくわからないので、何パターンか試してみる。

 [+] Page 1/10 [c, r, p]: [ZYVATFLKNDRUMEGJOHPIXBCQWS, 2, MLLH ZOCI KIRLJFX TEOXL, ACB MLLH ZOCI LJLGRLX TEOXLI]

 [+] Page 1/10 [c, r, p]: [SOJDTUEHZXVNQLKCRMGWABYPFI, 19, PRQVPUP ZVHATPUN HFQPE VPXLOVP PRQVPUP NHTLQOHEN]

 [+] Page 1/10 [c, r, p]: [TZPFCNDWBHYSXOGEJIURKAMLQV, 2, TJM CPE FD RYCH QYAHJMA XEJQYEB HJQ AJ FD IJQDRWMO FMA TJM CPEEJA FD IJQDRWMO QYAHJMA GJEDT]

 [+] Page 1/10 [c, r, p]: [WPZRYXAOLIJEMGHQUNDSBCVTKF, 4, TEL XLVT OLILNVL YJYMNVT TEL TCLYAELCDKV MV TCLYAELCZ]

 [+] Page 1/10 [c, r, p]: [CWKSLJGVEQHXAOYURIZFNPDBTM, 6, MOH VCTYFHTT NX MOH ZGXYG YT VCTYFHTT]

3個目のパラメータは換字式暗号の暗号文と推測して、quipqiupで復号してみる。

暗号:TJM CPE FD RYCH QYAHJMA XEJQYEB HJQ AJ FD IJQDRWMO FMA TJM CPEEJA FD IJQDRWMO QYAHJMA GJEDT
平文:YOU CAN BE RICH WITHOUT SNOWING HOW TO BE POWERFUL BUT YOU CANNOT BE POWERFUL WITHOUT MONEY

1個目のパラメータは換字テーブルのようだ。

平文:ABCDEFGHIJKLMNOPQRSTUVWXYZ
暗号:TZPFCNDWBHYSXOGEJIURKAMLQV

数値はシフト数のことかもしれない。

C: (C+2)E->C
H: (H+2)J->H
T: (Y+2)A->T

一致するので、推測通り!
変換テーブルに従い元に戻した後、数字分だけマイナス方向にシフトすれば復号できる。

import socket
import re
import string

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('capoditutticapi01.3dsctf.org', 8001))

data = s.recv(1024)
print data + 'start'
s.sendall('start\n')
data = s.recv(1024)
print data

for i in range(10):
    data = s.recv(1024)
    print data

    pattern = '\[c, r, p\]: \[(.+)]'
    m = re.search(pattern, data)
    elems = m.group(1).split(', ', 2)
    tbl = elems[0]
    shift = int(elems[1])
    enc = elems[2]

    ans = ''
    for i in range(len(enc)):
        if enc[i] in string.uppercase:
            idx = tbl.index(enc[i])
            ans += string.uppercase[(idx - shift) % 26]
        else:
            ans += enc[i]
    print ans
    s.sendall(ans + '\n')
    data = s.recv(1024)
    print data

実行結果は次の通り。

                +++     3DSCTF - Capo Di Tutti Capi     +++

 [+] One year after the death of the one of the most famous members of the
     mafia, the FBI found a notebook with a few weird annotations.
 
 [+] Trying to use the same strategy as the last time, all the FBI experts
     failed to translate the book. Look if you have some luck! 

 [+] Type start to read the first note: start
     Openning the book...


 [+] Page 1/10 [c, r, p]: [LHKJBMIQGEUXYNSOTRWPZDFCAV, 10, JRS IAHDN XSDACTK JA JRS VUJWSCJ FUC]
     The answer is: 
THE WORLD BELONGS TO THE PATIENT MAN
 [+] Correct!


 [+] Page 2/10 [c, r, p]: [TCMDJUQHYIZGWKVPFNLAEOBRSX, 2, OIQ DQEO UQHQPEQ MYMZPEO OIQ OAQMJIQAFBE ZE OAQMJIQAT]
     The answer is: 
THE BEST DEFENSE AGAINST THE TREACHEROUS IS TREACHERY
 [+] Correct!


 [+] Page 3/10 [c, r, p]: [YBZQMHGFRSOVWNTJUXDACPIEKL, 15, AWRGABA MGQUYABF QCRAZ GAHSEGA AWRGABA FQYSREQZF]
     The answer is: 
EXTREME PROBLEMS OFTEN REQUIRE EXTREME SOLUTIONS
 [+] Correct!


 [+] Page 4/10 [c, r, p]: [GMHBRAUPXKQVNTFEOWZDLJISCY, 3, BXIPL B CVAIWLM, JQBLZPO MWSL TOVXP]
     The answer is: 
AFTER A VICTORY, SHARPEN YOUR KNIFE
 [+] Correct!


 [+] Page 5/10 [c, r, p]: [APYRHGVONFKUSMECWLQXITDJZB, 15, IRV HXCLX NR VXJTY, WX VXCQM IRV UCV]
     The answer is: 
FOR PEACE TO REIGN, BE READY FOR WAR
 [+] Correct!


 [+] Page 6/10 [c, r, p]: [KRDTCVOHWLPAMNQUEFGBZIJXSY, 2, ILO TOZI SDK IE OUIOB EJB TJZPUOZZ PZ IE TO TEBU PUIE PI]
     The answer is: 
THE BEST WAY TO ENTER OUR BUSINESS IS TO BE BORN INTO IT
 [+] Correct!


 [+] Page 7/10 [c, r, p]: [MSKLAONWEQGYRIPDFUZXVBJTCH, 11, AZD ROLXCDLL HF AZD TYFXY XL ROLXCDLL]
     The answer is: 
THE BUSINESS OF THE MAFIA IS BUSINESS
 [+] Correct!


 [+] Page 8/10 [c, r, p]: [XSCMNUPQKAYWZJOBFTRLVHGIDE, 18, IPA QGRVG WP AGXDU, LG AGRHF IPA ORA]
     The answer is: 
FOR PEACE TO REIGN, BE READY FOR WAR
 [+] Correct!


 [+] Page 9/10 [c, r, p]: [DAORHQVZCUMIYSLXNGPKFJEBTW, 26, UFRVHP, IDETHGP DSR XLICKCOCDSP ZDJH D ICOHSPH KL PKHDI EH RLSK SHHR LSH]
     The answer is: 
JUDGES, LAWYERS AND POLITICIANS HAVE A LICENSE TO STEAL WE DONT NEED ONE
 [+] Correct!


 [+] Page 10/10 [c, r, p]: [FHQXZWOEDKYABNSLMIVTUPJRGC, 14, LVEEVW BQDW VHVFJVO QKVWVOEJFSEV BQDW OEDXJIJEB EPSH BQDW OPWVYIHVOO]
     The answer is: 
BETTER YOUR ENEMIES OVERESTIMATE YOUR STUPIDITY THAN YOUR SHREWDNESS
 [+] Correct!

 [+] Cool, the flag is: 3DS{k33p_y0Ur_fr13nDs_cl0S3_AND_y0uR_fL4Gs_cLOs3R}
3DS{k33p_y0Ur_fr13nDs_cl0S3_AND_y0uR_fL4Gs_cLOs3R}

Xesar (CRYPTO)

Base64デコードしたデータをファイルに保存する。
https://wiremask.eu/tools/xor-cracker/でXOR鍵のあたりをつけ、調整したXOR鍵で復号する。

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

cipher = data.decode('base64')

key = 'MOIIIGKEMK'

plain = ''
for i in range(len(cipher)):
    plain += chr(ord(cipher[i]) ^ ord(key[i%len(key)]))

print plain

復号結果は次の通り。

LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. MAURIS MATTIS RISUS EU SUSCIPIT CONSEQUAT. DONEC CONGUE DAPIBUS IPSUM, A IMPERDIET EST RUTRUM ID. DONEC NON IPSUM EGET ORCI VIVERRA PORTA. SED SAGITTIS IPSUM NEC LACUS SUSCIPIT CONVALLIS. NULLA IMPERDIET NISI UT DICTUM SUSCIPIT. DUIS AC SAGITTIS LOREM. PELLENTESQUE HABITANT MORBI TRISTIQUE SENECTUS ET NETUS ET MALESUADA FAMES AC TURPIS EGESTAS. PROIN BLANDIT NEQUE QUIS MAURIS MAXIMUS, VEL PRETIUM NISI PORTA. INTEGER NEC SUSCIPIT URNA.FUSCE IN PHARETRA LACUS. FUSCE FEUGIAT AC IPSUM ET MOLESTIE. SUSPENDISSE NEC ENIM A MASSA ACCUMSAN BLANDIT. SUSPENDISSE CONVALLIS ORCI SIT AMET IPSUM CONSEQUAT, VEL ALIQUAM ENIM FEUGIAT. INTEGER SED CONDIMENTUM DOLOR, VITAE CURSUS NULLA. VIVAMUS EGET NUNC A MI PLACERAT IMPERDIET. NULLAM EST JUSTO, ALIQUET RHONCUS ORCI SODALES, PULVINAR VEHICULA DOLOR. VIVAMUS RHONCUS DIGNISSIM ARCU EU PHARETRA. SUSPENDISSE ET DOLOR ODIO. CURABITUR IMPERDIET LECTUS SIT AMET LACINIA VEHICULA. FUSCE DIAM NISI, GRAVIDA ET SEMPER SED, ELEIFEND UT MI.MAURIS SODALES PURUS EFFICITUR NISL CURSUS, AT TEMPOR PURUS PULVINAR. INTEGER ULTRICIES, IPSUM NEC LUCTUS CONSEQUAT, MAURIS EX TEMPUS MASSA, NON MALESUADA LIGULA TORTOR UT MASSA. SUSPENDISSE VEHICULA NEQUE VITAE LECTUS CONSEQUAT, QUIS CONGUE ANTE VOLUTPAT. INTEGER SEMPER RUTRUM MAGNA, SIT AMET TRISTIQUE MAGNA. ALIQUAM ERAT VOLUTPAT. IN NON SOLLICITUDIN MI. MAECENAS VEHICULA AUGUE PURUS, A TINCIDUNT DOLOR FAUCIBUS SIT AMET. NULLAM BLANDIT EFFICITUR SAPIEN IN LAOREET. CRAS VOLUTPAT NEQUE MAGNA, VOLUTPAT CONSEQUAT LOREM FERMENTUM EU. ETIAM ET LUCTUS METUS. NULLA AT METUS TELLUS. PELLENTESQUE RISUS IPSUM, PORTA SIT AMET POSUERE ID, PHARETRA NON RISUS. QUISQUE EGET EST VEHICULA, TINCIDUNT URNA AT, MOLESTIE MI. SED UT SEM ALIQUAM, TINCIDUNT DIAM SED, PORTTITOR ORCI. SED QUIS BLANDIT SEM. MAECENAS EST PURUS, VOLUTPAT SIT AMET DUI EU, MATTIS AUCTOR ELIT.CRAS NEC PURUS SIT AMET URNA PLACERAT FEUGIAT. NULLAM DICTUM LIBERO ET ANTE GRAVIDA, LAOREET FACILISIS URNA EUISMOD. ETIAM ET QUAM AC NIBH ACCUMSAN MOLESTIE. VIVAMUS MOLESTIE EST QUIS SCELERISQUE IACULIS. IN VEL ERAT NEQUE. NULLAM SAGITTIS NEC TELLUS ET PLACERAT. MAURIS MOLESTIE SAPIEN SIT AMET SAPIEN PLACERAT, EGET VEHICULA NULLA DICTUM. NAM PURUS LECTUS, TRISTIQUE NEC BLANDIT SED, VEHICULA VITAE NULLA. ALIQUAM ERAT VOLUTPAT. QUISQUE DICTUM ELEMENTUM FELIS FEUGIAT TEMPUS. INTEGER ACCUMSAN RISUS EGET MAXIMUS EUISMOD. PELLENTESQUE HABITANT MORBI TRISTIQUE SENECTUS ET NETUS ET MALESUADA FAMES AC TURPIS EGESTAS. VIVAMUS BLANDIT, NISL ID CURSUS MOLESTIE, VELIT PURUS MATTIS LIGULA, SIT AMET ELEMENTUM AUGUE NISL VEL TORTOR. PROIN QUIS ULTRICES NISL. CURABITUR SEMPER NULLA NON ERAT PLACERAT, AT RUTRUM TELLUS PELLENTESQUE. AENEAN PELLENTESQUE ULLAMCORPER TELLUS, SOLLICITUDIN PORTTITOR LACUS CONDIMENTUM EU.UT NEC DUI MOLLIS EX VIVERRA CONVALLIS. SUSPENDISSE IN ANTE NON DIAM ULLAMCORPER VARIUS EU AC ODIO. DONEC ORCI VELIT, LUCTUS AC LECTUS QUIS, BIBENDUM ALIQUET DOLOR. AENEAN QUAM TURPIS, PLACERAT SED ALIQUAM NON, PLACERAT QUIS LIGULA. MAURIS AC ANTE PORTTITOR LEO TINCIDUNT POSUERE. VESTIBULUM MOLLIS ID NULLA EGET TRISTIQUE. MAECENAS VIVERRA LACUS AUCTOR LIGULA POSUERE, EU SUSCIPIT EX HENDRERIT.ETIAM EFFICITUR VESTIBULUM DOLOR, NON PORTTITOR LECTUS PHARETRA VEL. MAURIS PHARETRA ELEIFEND SCELERISQUE. PELLENTESQUE PORTA SEM LEO, NEC TEMPOR URNA SEMPER SED. SED DOLOR EX, PRETIUM VEL ENIM SED. THE FLAG IS 3DS{1_H4V3_50M3_CR1P70_5K1LL5}

最後にフラグが書いてある。

3DS{1_H4V3_50M3_CR1P70_5K1LL5}

Escape from Arkham I (CRYPTO)

the_joker.keyを内容をBase64をデコードする。空白行を詰めると、以下のようになる。

HA HA HA HA HA HA HA HA HA HA HA HA HA HA HA
WHAT A JOKE!
You thought that it would be so easy?
You ain't no Batsy!!!! He's a true detective! You are just... hmmm... alive, and I don't know why...
Really, really funny....
In fact, let's play a game since this is all a big joke.... 
The master key was ciphered using aes-256-cbc on OpenSSL 1.1.0g 2 Nov 2017
Enjoy your tail chasing, because when Batsy arrives, I'll not be here!!
HA HA HA HA HA HA HA HA HA HA HA HA HA HA HA

master.keyはAESで暗号されているようだ。ファイルの先頭にSalted__の文字列があるのでSaltも付けたAES暗号のようだ。
問題に書かれている16進数をデコードする。

>>> '5368616d6972'.decode('hex')
'Shamir'

Shamirの秘密分散法の問題であることを示していると推測できる。
https://github.com/jqueiroz/python-sslibのライブラリを使って分散された秘密情報を取得する。

#!/usr/bin/env python3
from sslib import shamir

DIR = 'arkham\\'
key_files = ['bane.key', 'dr_hugo_strange.key', 'harley_queen.key',
    'hush.key', 'killer_croc.key', 'poison_ivy.key', 'scarecrow.key',
    'the_penguin.key', 'the_riddler.key', 'two_face.key']

def read_key_file(fname):
    with open(fname, 'r') as f:
        data = f.read()
    data = data.replace('(', '')
    data = data.replace(')', '')
    data = data.split(', ')
    return data[0], int(data[1]), int(data[2])

shares = []
for i in range(10):
    idx, data, n = read_key_file(DIR + key_files[i])
    h_data = '%x' % (data % n)
    if len(h_data) % 2 != 0:
        h_data = '0' + h_data
    shares.append(idx + '-' + h_data)

s_data = {'required_shares': 4}
s_data['prime_mod'] = '%x' % n
s_data['shares'] = shares

dec = shamir.recover_secret(shamir.from_hex(s_data)).decode('ascii')
print(dec)

この結果、hy_so_crypto?_ha_ha_ha_ha という文字列が取れた。これでmaster.keyを復号してみる。

$ openssl enc -d -aes-256-cbc -salt -pass pass:hy_so_crypto?_ha_ha_ha_ha -in master.key -out decrypted
bad decrypt
140081836168864:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:539:

うまくいかない。hy_so_crypto?_ha_ha_ha_haの hy って何だろうと思い、改めてthe_joker.keyのデコード結果を見る。whyのwが削れてしまったのかもしれない。パスワードにwを付けて再度復号してみる。

$ openssl enc -d -aes-256-cbc -salt -pass pass:why_so_crypto?_ha_ha_ha_ha -in master.key -out decrypted

復号に成功した!

$ file decrypted
decrypted: PNG image data, 300 x 30, 8-bit/color RGBA, non-interlaced
$ mv decrypted flag.png

f:id:satou-y:20171231111832p:plain
復号したPNGファイルを見るとフラグが書かれていた。

3DS{Sh4m1r_S4b3_S3p4r4r_S3gr3d0s}