UMDCTF 2023 Writeup

この大会は2023/4/29 7:00(JST)~2023/5/1 10:00(JST)に開催されました。
今回もチームで参戦。結果は2646点で745チーム中109位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (misc)

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

UMDCTF{w3lc0m3_t0_th3_p0k3v3rs3}

Ports (misc)

port-1.txt.zipを解凍すると、以下の内容が記載されているテキストファイルが展開される。

Go to port 21105 instead :(

Random message: ofbllnyusmwbyghepmvricemlgbgiziiavfgaabcht

次のポート番号の名前が書いてあるので、例外が発生するまで解凍していく。

#!/usr/bin/env python3
import zipfile
import re

zip_name_format = 'port-%s.txt.zip'
txt_name_format = 'port-%s.txt'
pattern = 'Go to port (\d+) instead'

port = '1'
while True:
    print('[+] port:' , port)
    fname = zip_name_format % port
    pwd = str(port).encode()
    try:
        with zipfile.ZipFile(fname, 'r') as zf:
            zf.extractall(path='.', pwd=pwd)
    except:
        break

    fname = txt_name_format % port
    with open(fname, 'r') as f:
        data = f.read()
    m = re.search(pattern, data)
    if m is not None:
        port = m.group(1)
    else:
        print(data)
        break

実行結果は以下の通りで、port-42237.txt.zipは解凍できなかった。

        :
[+] port: 39192
[+] port: 8352
[+] port: 8637
[+] port: 2600
[+] port: 7590
[+] port: 14491
[+] port: 14987
[+] port: 23905
[+] port: 7326
[+] port: 23671
[+] port: 42237

port-42237.txt.zipを調べてみる。7zipでは解凍でき、展開されたテキストファイルにフラグが書いてあった。

UMDCTF{dDSA-d_23+t0ta11y_n0t_NSFW_tCp_pAcKET-0_0-15039254&((*#@!}

A TXT For You and Me (misc)

$ dig -t TXT a-txt-for-you-and-me.chall.lol

; <<>> DiG 9.18.12-0ubuntu0.22.04.1-Ubuntu <<>> -t TXT a-txt-for-you-and-me.chall.lol
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20207
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;a-txt-for-you-and-me.chall.lol.	IN	TXT

;; ANSWER SECTION:
a-txt-for-you-and-me.chall.lol.	5 IN	TXT	"UMDCTF{just_old_school_texting}"

;; Query time: 8 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Sat Apr 29 14:00:53 JST 2023
;; MSG SIZE  rcvd: 103
UMDCTF{just_old_school_texting}

Gone Missing 1 (OSINT)

周囲を見ると、特徴的な建物がある。

この建物を画像検索すると、ノルウェー王宮であることがわかる。これで周囲の景色が合うところを探し、以下の場所でSubmitしたところ、フラグが表示された。

UMDCTF{I_b3t_rainbolt_c0uld_g3t_th1s_!n_thr33_s3c0nd5}

Gone Missing 2 (OSINT)

周囲を見ると、特徴的な星型の枠組みがある。

この星型の枠組みを画像検索すると、Castle Rock butteが見つかった。
https://ja.m.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Castle_Rock_butte_in_Castle_Rock_Colorado.JPG
これで周囲の景色が合うところを探し、以下の場所でSubmitしたところ、フラグが表示された。

UMDCTF{why_d0es_th1s_r0ck_h4ve_a_st4r??}

Welcome to Python! (rev)

$ strings chal | grep python
blib-dynload/_bz2.cpython-310-x86_64-linux-gnu.so
blib-dynload/_codecs_cn.cpython-310-x86_64-linux-gnu.so
blib-dynload/_codecs_hk.cpython-310-x86_64-linux-gnu.so
        :

Python3.10製の実行ファイルのようなので、pyinstxtractor.pyでモジュールを抽出する。

$ python3 pyinstxtractor.py chal
[+] Processing chal
[+] Pyinstaller version: 2.1+
[+] Python version: 3.10
[+] Length of package: 6544209 bytes
[+] Found 35 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: chal.pyc
[+] Found 99 files in PYZ archive
[+] Successfully extracted pyinstaller archive: chal

You can now use a python decompiler on the pyc files within the extracted directory

chal.pycファイルをデコンパイルする。

$ cd chal_extracted/
$ pycdc chal.pyc
# Source Generated with Decompyle++
# File: chal.pyc (Python 3.10)

from math import sqrt, sin, cos
from ctypes import c_uint32, c_float
from sys import exit as exit_
source = [
    672662614,
    741343303,
    495239261,
    744259788,
    722021046,
    0xA70AA247L,
    1053692,
    0xA8050035L,
    0xA982A820L,
    624689,
    0xA90D20BCL,
    41134,
    295340,
    0xA0028102L,
    622681,
    576469,
    671170814,
    0x8041086EL,
    765,
    680595550,
    0x80200166L,
    698368102,
    2437137,
    0x8042C1EEL,
    570966112,
    4612341,
    0x800008D4L,
    0xA94D02CEL,
    16484,
    2103301,
    136226,
    9438506,
    663820758,
    0x8013523BL,
    8405532,
    0xA4000875L,
    0x80030A78L,
    136768]
seed = 64

def wandom(x):
    return x * x * cos(x) * sin(x) / 1000


def evil_bit_hack(y):
    return int(c_uint32.from_buffer(c_float(y)).value)

print('==========================================')
print('Professional flag checker service (v 97.2)')
print('==========================================')
flag = input('Show me the flag: ')
lf = len(flag)
ls = len(source)
l = lf if lf < ls else ls
for i in range(seed, seed + l):
    w = wandom(i)
    c = ~(~ord(flag[i - seed]) ^ evil_bit_hack(wandom(wandom(w))) & evil_bit_hack(w)) + 1
    if source[i - seed] != c:
        print("Uh oh! We don't think your flag is correct... :(")
        exit_(1)
if lf == ls:
    print('Your flag is correct!')
    return None
None('Some of your flag is correct...')

ブルートフォースで1文字ずつフラグを割り出す。

#!/usr/bin/env python2
from math import sqrt, sin, cos
from ctypes import c_uint32, c_float
from sys import exit as exit_
source = [
    672662614,
    741343303,
    495239261,
    744259788,
    722021046,
    0xA70AA247L,
    1053692,
    0xA8050035L,
    0xA982A820L,
    624689,
    0xA90D20BCL,
    41134,
    295340,
    0xA0028102L,
    622681,
    576469,
    671170814,
    0x8041086EL,
    765,
    680595550,
    0x80200166L,
    698368102,
    2437137,
    0x8042C1EEL,
    570966112,
    4612341,
    0x800008D4L,
    0xA94D02CEL,
    16484,
    2103301,
    136226,
    9438506,
    663820758,
    0x8013523BL,
    8405532,
    0xA4000875L,
    0x80030A78L,
    136768]
seed = 64

def wandom(x):
    return x * x * cos(x) * sin(x) / 1000

def evil_bit_hack(y):
    return int(c_uint32.from_buffer(c_float(y)).value)

l = len(source)

flag = ''
for i in range(seed, seed + l):
    w = wandom(i)
    for code in range(32, 127):
        c = ~(~code ^ evil_bit_hack(wandom(wandom(w))) & evil_bit_hack(w)) + 1
        if source[i - seed] == c:
            flag += chr(code)
            break
    i += 1

print(flag)
UMDCTF{0_0+-+eXP-eLLiARm_us_!!!-12345}

Terps Ticketing System (web)

以下のように適当入力して、Get Ticketsをクリックする。

Name: a
Email: test@test.com

すると、https://tts.chall.lol/ticket?num=338に遷移して。以下のメッセージが表示されている。

Your Ticket # is: 338

https://tts.chall.lol/ticket?num=0にアクセスしてみると、フラグが表示された。

UMDCTF{d0nt_b3_@n_id0r_@lw@ys_s3cur3_ur_tick3ts}

Malware Chall Disclaimer (forensics)

「Doctors Hate Him」の添付ファイルはマルウェアと同等の注意が必要ということを説明するためだけの問題。
得点にはならないが、一応回答することにした。フラグは問題に書いてあった。

UMDCTF{i_understand_that_malware_chall_is_sus}

Mirror Unknown (forensics)

次の画像が問題になっている。

以下のURLのページを参考に、ポケモンアンノーンの姿をアルファベットに置き換える。

https://www.dopr.net/pokemongoupdates/28-unown-event

ただし、画像が左右逆になっていることに注意する。

SINJOH
RUINS
UMDCTF{SINJOHRUINS}

No. 352 (forensics)

パスワード1はポケモンNo.352の名前の小文字表記とのこと。調べると次の名前だとわかる。

kecleon
$ steghide extract -sf hide-n-seek.jpg -p kecleon
wrote extracted data to "kecleon.jpg".

パスワード2は"timetofindwhatkecleonishiding"とのことなので、抽出されたファイルについてもこのパスワードで秘密情報を抽出する。

$ steghide extract -sf kecleon.jpg -p timetofindwhatkecleonishiding
wrote extracted data to "flag.txt".
$ cat flag.txt
UMDCTF{KECLE0NNNNN}
UMDCTF{KECLE0NNNNN}

Fire Type Pokemon Only (forensics)

問題タイトルからもFTPの通信があやしい。「ftp || ftp-data」でフィルタリングする。secretpic1.pngやsecretが転送されていることがわかる。
まずsecretpic1.pngを抽出するためにNo.15292, 15293のパケットをエクスポートし、結合する。

$ cat 15292.bin 15293.bin > secretpic1.png

secretpic1.pngにはポケモンのSpearの画像があるだけ。
次にsecretの断片を拾い、結合する。

#!/usr/bin/env python3
from scapy.all import *

packets = rdpcap('fire-type-pokemon-only.pcapng')

flag = b''
for i in range(28928, 29113):
    if packets[i].haslayer(Raw):
        flag += packets[i][Raw].load

with open('secret', 'wb') as f:
    f.write(flag)
$ file secret
secret: Zip archive data, at least v2.0 to extract, compression method=deflate

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

$ fcrackzip -u -D -p dict/rockyou.txt secret


PASSWORD FOUND!!!!: pw == pika
$ unzip secret
Archive:  secret
[secret] wisdom.mp4 password: 
  inflating: wisdom.mp4

解凍し、展開されたmp4の動画を再生すると、下の方にフラグが書いてあった。

UMDCTF{its_n0t_p1kachu!!}

pokecomms (crypto)

"CHU!", "PIKA"で構成されていて、1行8個含まれている。1行の先頭は必ず"CHU!"になっていることから、"CHU!"は"0"、"PIKA"は"1"にしてデコードする。

#!/usr/bin/env python3
with open('pokecomms.txt', 'r') as f:
    lines = f.read().splitlines()

flag = ''
for line in lines:
    code = line.replace(' CHU!', '0').replace(' PIKA', '1')
    flag += chr(int(code, 2))
print(flag)
UMDCTF{P1K4CHU_Once_upon_a_time,_there_was_a_young_boy_named_Ash_who_dreamed_of_becoming_the_world's_greatest_Pokemon_trainer._He_set_out_on_a_journey_with_his_trusty_Pokemon_partner,_Pikachu,_a_cute_and_powerful_electric-type_Pokemon._As_Ash_and_Pikachu_traveled_through_the_regions,_they_encountered_many_challenges_and_made_many_friends._But_they_also_faced_their_fair_share_of_enemies,_including_the_notorious_Team_Rocket,_who_were_always_trying_to_steal_Pikachu._Despite_the_odds_stacked_against_them,_Ash_and_Pikachu_never_gave_up._They_trained_hard_and_battled_even_harder,_always_looking_for_ways_to_improve_their_skills_and_strengthen_their_bond._And_along_the_way,_they_learned_valuable_lessons_about_friendship,_determination,_and_the_power_of_believing_in_oneself._Eventually,_Ash_and_Pikachu's_hard_work_paid_off._They_defeated_powerful_opponents,_earned_badges_from_Gym_Leaders,_and_even_competed_in_the_prestigious_Pokemon_League_tournaments._But_no_matter_how_many_victories_they_achieved,_Ash_and_Pikachu_never_forgot_where_they_came_from_or_the_importance_of_their_friendship._In_the_end,_Ash_and_Pikachu_became_a_legendary_team,_admired_by_Pokemon_trainers_around_the_world._And_although_their_journey_may_have_had_its_ups_and_downs,_they_always_knew_that_as_long_as_they_had_each_other,_they_could_overcome_any_obstacle_that_stood_in_their_way}

CBC-MAC 1 (crypto)

サーバの処理概要は以下の通り。

・key: ランダム16バイト文字列
・queries = []
・queriesの長さが10未満の間、以下を実行
 ・choice: 入力
 ・choiceが'1'の場合
  ・msg: 入力
  ・msg: msgをhexデコード
  ・msgの長さが16の倍数でない場合はエラーメッセージ表示
  ・msgの長さが16の倍数の場合
   ・queriesにmsgを追加
   ・t = cbc_mac(msg, key)
    ・iv = b'\x00' * 16
    ・cipher: AES-CBC(key, iv)のインスタンス
    ・t: msgをAES-CBC暗号化した最後のブロック
    ・tの16進数表記文字列を返却
   ・tを表示
 ・choiceが'2'の場合
  ・msg: 入力
  ・tag: 入力
  ・msg: msgをhexデコード
  ・msgの長さが16の倍数でない場合、エラーメッセージ表示
  ・tagの長さが32バイトでない場合、エラーメッセージ表示
  ・queriesの中にmsgがある場合、エラーメッセージ表示
  ・t_ret = cbc_mac(msg, key)
  ・t_retとtagが同じ場合、フラグを表示

平文1ブロックの場合と平文2ブロックの場合で、以下のようなイメージになる。

PT0 ^ IV  --(AES暗号) --> CT0 = tag0

PT0 ^ IV  --(AES暗号) --> CT0
PT1 ^ CT0 --(AES暗号) --> CT1 = tag1

1回目でtag0を取得し、2回目でtag1を取得する。PT1 ^ CT0 を1ブロックのみを平文とした場合も、tagの値はtag1になることを使えばよい。

#!/usr/bin/env python3
import socket
from binascii import hexlify, unhexlify
from Crypto.Util.strxor import strxor

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('0.cloud.chals.io', 12769))

pt = b'a' * 32

data = recvuntil(s, b': ')
print(data + '1')
s.sendall(b'1\n')
data = recvuntil(s, b': ')
print(data + hexlify(pt[:16]).decode())
s.sendall(hexlify(pt[:16]) + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
tag0 = bytes.fromhex(data.split(' ')[-1])

data = recvuntil(s, b': ')
print(data + '1')
s.sendall(b'1\n')
data = recvuntil(s, b': ')
print(data + hexlify(pt).decode())
s.sendall(hexlify(pt) + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
h_tag1 = data.split(' ')[-1]

h_pt = strxor(pt[16:], tag0).hex()

data = recvuntil(s, b': ')
print(data + '2')
s.sendall(b'2\n')
data = recvuntil(s, b': ')
print(data + h_pt)
s.sendall(h_pt.encode() + b'\n')
data = recvuntil(s, b': ')
print(data + h_tag1)
s.sendall(h_tag1.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

Team Rocket told me CBC-MAC with arbitrary-length messages is safe from forgery. If you manage to forge a message you haven't queried using my oracle, I'll give you something in return.

What would you like to do?
        (1) MAC Query
        (2) Forgery
        (3) Exit

Choice: 1
msg (hex): 61616161616161616161616161616161
CBC-MAC(msg): ac1df0534caa0b03b707b955e22984d1

What would you like to do?
        (1) MAC Query
        (2) Forgery
        (3) Exit

Choice: 1
msg (hex): 6161616161616161616161616161616161616161616161616161616161616161
CBC-MAC(msg): 679b4c2357e1d15a3512a3c4309f89dc

What would you like to do?
        (1) MAC Query
        (2) Forgery
        (3) Exit

Choice: 2
msg (hex): cd7c91322dcb6a62d666d8348348e5b0
tag (hex): 679b4c2357e1d15a3512a3c4309f89dc
If you reach this point, I guess we need to find a better MAC (and not trust TR). UMDCTF{Th!s_M@C_Sch3M3_1s_0nly_S3cur3_f0r_f!xed_l3ngth_m3ss4g3s_78232813}
UMDCTF{Th!s_M@C_Sch3M3_1s_0nly_S3cur3_f0r_f!xed_l3ngth_m3ss4g3s_78232813}

Noisy Bits (crypto)

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

・POLY = 0xC75
・FLAG_BIN_LENGTH = 360
・binary_str: flagの2進数表記で360バイトになるよう"0"を先頭にパディング
・blocks: binary_strを12バイトごとのブロック配列にしたもの
・block_nos: blocksの各値を10進数数値にしたもの
・encoded = [encode(cw) for cw in block_nos]
・encodedの各要素につき、1bitまたは3bit反転
・encoded_bin: encodedの各要素を2進数にし、23バイトになるよう"0"を先頭にパディング
・encoded_binの各要素をスペース区切りで連結して出力

decodeするときは末尾12ビットを取得すればよい。encodeして1ビットまたは3ビット反転して、条件に合うものを探す。

#!/usr/bin/env python3
import itertools

def encode(cw):
    cw = (cw & 0xfff)
    c = cw
    for i in range(1, 12+1):
        if cw & 1 != 0:
            cw = cw ^ POLY
        cw = cw >> 1

    return (cw << 12) | c

def get_bits_list_for_all_cases(s):
    ret = []
    for r in [1, 3]:
        for x in itertools.combinations(range(23), r):
            c = list(s)
            for i in range(r):
                c[x[i]] = str(int(c[x[i]]) ^ 1)
            c = ''.join(c)
            ret.append(c)
    return ret

POLY = 0xC75

with open('output.txt', 'r') as f:
    encoded_bin = f.read().rstrip().split(' ')

bin_flag = ''
for i in range(len(encoded_bin)):
    c = encoded_bin[i]
    bits = get_bits_list_for_all_cases(c)
    for b in bits:
        int_b = int(b, 2)
        if encode(int(b[-12:], 2)) == int_b:
            bin_flag += b[-12:]
            break

flag = ''
for i in range(0, len(bin_flag), 8):
    flag += chr(int(bin_flag[i:i+8], 2))
print(flag)
UMDCTF{n0i5y_ch4nn3l5_ar3_n0t_@_pr0bleM_4_u}

Hidden Message (crypto)

大文字を"-"、小文字を"."にしてモールス信号として書き出す。

-- ----- .-. ... ...--! -.-. ----- -.. ...--! -- ...-- ....- -. ...! .... ....- ...- .---- -. --.! ... -----! -- ---.. -.-. ....! ..-. ---.. -. ... .---- ...-- ...!

これをデコードする。

m0rs3!c0d3!m34ns!h4v1ng!s0!m8ch!f8ns13s!

!を区切りとして"_"に変更して、フラグにする。

UMDCTF{M0RS3_C0D3_M34NS_H4V1NG_S0_M8CH_F8NS13S}

Reduce Thyself (crypto)

サーバの処理概要は以下の通り。

・n,e,d = gen_params()
・flag_ct = pow(b2l(FLAG), e, n)
・n, e, flag_ctを表示
・以下繰り返し
 ・ct: 数値入力
 ・pt = decrypt(flag_ct, ct, d, n)
  ・pt = 'null'
  ・ctが素数でflag_ctと異なる場合
   ・pt = pow(ct, d, n)の文字列化したものの16進数表記
  ・ptを返却
 ・ptを表示

flag_ctに素数をかけてnで割った余りが素数になるものを探す。

(flag_ct * ct1) % n = ct2

pow(ct1, d, n)とpow(ct2, d, n)を取得して、以下の計算でフラグを求めることができる。

flag = pow(flag_ct, d, n)
     = pow(ct2, d, n) * inverse(pow(ct1, d, n), n) % n
#!/usr/bin/env python3
import socket
from Crypto.Util.number import *
from sympy import *

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('0.cloud.chals.io', 33047))

data = recvuntil(s, b'\n').rstrip()
print(data)
n = int(data.split(' ')[-1], 16)
data = recvuntil(s, b'\n').rstrip()
print(data)
e = int(data.split(' ')[-1], 16)
data = recvuntil(s, b'\n').rstrip()
print(data)
flag_ct = int(data.split(' ')[-1], 16)

ct1 = 2
while True:
    ct2 = (flag_ct * ct1) % n
    if isPrime(ct2):
        break
    ct1 = nextprime(ct1)

data = recvuntil(s, b': ')
print(data + hex(ct1))
s.sendall(hex(ct1).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
pt1 = int(data.split(' ')[-1], 16)

data = recvuntil(s, b': ')
print(data + hex(ct2))
s.sendall(hex(ct2).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
pt2 = int(data.split(' ')[-1], 16)

m = pt2 * inverse(pt1, n) % n
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

n: 0x7fdfa59c3868e12f74f7628a2b4488b48c19f97ed923f0bdfa3b475488956d3502f99b38c8ab00b147c34749d5677f526d8f073c499e11878bb87f1fa514f59a26984557bf7537d2a5a55daa0dd378fc4ed33716de7b8cfaf44928d3f6cd3b6cbeb6a6ad38afa32e1564146cf6d7d72cc00879608b5d0658e5e2338087eecf6ca238bb0b13a8e0ceaed32ee49941bebb7bd32b6a385b547b92720c607e9ec5cfb9e8e0731bb0510215002066ec26dea5ccdb433badad29fdf7861ecd2d362297d5b455bfbcda35e8ea30a11e3341a1ee1e04d32db2116d60c3d7b06490d2e6f6f153cafe49a3b03932e29e0029428fd90ca7965df95264e0dcfbf717c7f4ce23
e: 0x10001
flag_ct: 0x18eacb5008c17c9c6d87bd74f13f28c1545d63a280d708bad7d068e21a2216c2e853585c2a303af8639764f96ee008b3a447b4af730a42d5004c86d5d5253c36af6f3cc286e0c46145a87de48d90c8ab31770189153ab7507df8a37bb98736fe8080bf1a630d7779f24053b7bb197231c120f7557b6e2c732bbb2a963196eb6f071ef0210d9076670328f97334c9515baf91276499c4c906bf6c057a7a581d2fc54439199a557ce8cf3a658314c0ade2634e2ff9655707bc505af560c49a3a9ff84e6aeb0accaa20079dce743041da119f7f96db230d16469433f6095d7c58228642d49a3525bf41fbcaf419445c13fdf03bd5094d816a3d41a44c158dd0b914

Gimme ct (hex): 0x1039
result: 047b49c80743ef864353a8f2fb71f0de6c7508a1d1b2aab4e406653c73f9b29b1efbe92a58abe22ed7f9f2eccab77646b85425569305bb9fe59baed27646908469b8013178b4165a508f1f13a172d4c2980d87e7a6f72c14f9251ee476024c640a3b68f27836d82f7e9211550c7d47a0277a1f2482d1260321c3a42b04de1d4b83cb025e50ddad351363c32f841aed1c2ec497af35aa201768325ec88d9c06f5c94231ff275cca38848655749c021565b0d0055833ec01cf9328a2e9aa53e38a8f713b0ad56c4fd5cdf0cfd0cbe0944200655fac6a52caeeb3e07b86ac556dc53a7a07a774f33f7fb98f9e2befa47984a3bb25bb4d06ece620c6f4a735205101

Gimme ct (hex): 0x1f39eaafc76ee6b43d4a177aee0825c2e0efe55fedd8d6bb4ea111d652cd23648948e8b64407774bdae8ca244c5bcec7d64454bc9cf0cc194f6da7ee19d1af000c5d8d720ae4655592cd5256e07e0be05a1ed194511753bd9b621c4dd3fa5c0bf96779a8c26c8ca95c4993422fb68cbb3504d7a6f5560971f7811d5ce7a3dddddf9e7233ebe25ffbca578d8b052519e8db73dc5670c333f594fbc2fa9bb25e84aed5049634a75d356cf2663b62ef43a89e80cd4528a2ca605cbf587189edbb50d9418416649f848f7b5d1272e1e1ee679f96ed93e63bb82f5a55f01febb5e5b3705e7c117b13d0abf14c9f639667bda53d08d4f17313b1238a166f82ba6b08d9
result: 7b0e948ba1f395f6cf968cdc96489705767fee9ca928466dc6ab309afc4c099a6e7aeec5927ab0cf54adf3fd4ad5dbcecdecf1e760ac7aac75522becae3a4f75c4a93a18fee311a9edf3e2193a7092d7ac402d6d14c3bc95a87290b699e3af563161cffeeb57a95da48f7b756a30a5d6eb95a38d9bfdcc0dcbe9391e9b1786be8a6aab0c5ca71bfc68d2bb8fcbf58934bd328a828d21bd9b1a1e5af35c604b549001ab2fa4c781da24d21ad0c44e80ccad639a5df00dd433808937ca8130314da1e6e9e8085515918931d55279de92f38db5f2758ad195c38e2d10323e223c5a858514158db534eb5a9ed75897d293b4057eb56f2f7fc827e024b851a7d1e142
UMDCTF{s3lf_r3duc!bility_1s_n0t_ju5t_f0r_DH_97912837923}
UMDCTF{s3lf_r3duc!bility_1s_n0t_ju5t_f0r_DH_97912837923}