GlacierCTF 2023 Writeup

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

Welcome challenge (intro)

Discordに入り、#rulesチャネルにあるルール内にフラグが書いてあった。

gctf{w3lc0m3_t0_g1ac13rctf_2023}

Skilift (intro)

逆算して、tmp1を算出し、keyの1つを割り出す。

#!/usr/bin/env python3
tmp4 = 0x5443474D489DFDD3
tmp3 = tmp4 + 12345678
tmp2 = tmp3 ^ int.from_bytes(b'HACKERS!', 'big')
tmp1 = tmp2 >> 5
print(hex(tmp1))

この結果は以下の通り。

0xe0102030604060

これを入力する。

$ nc chall.glacierctf.com 13375
  ∗               ∗        ∗               ∗          ∗     
         ∗                                                  
                                ∗                     ◦◦╽◦◦ 
   ∗               ∗                      ∗          ◦◦ █  ◦
                                ∗                   ◦◦  █   
            ∗                         ∗         ∗  ◦◦   █   
     ∗              ∗    ◦╽◦◦                   ◦◦◦◦    █   
                       ◦◦ █ ◦◦◦         ◦◦╽◦◦◦◦◦◦       █   
                      ◦◦  █   ◦◦◦◦◦◦◦◦◦◦◦ █             █   
      ■■■■■■■■     ◦◦◦◦   █        ▛      █  ∗          █  ∗
     ▟        ▙ ◦◦◦       █  ∗     ▌      █         ∗   █   
 ∗  ▟          ▙          █     ██████  ∗ █             █   
   ▟            ▙     ∗   █     █    █    █             █   
   ▛▀▀▀▀▀▀▀▀▀▀▀▀▜         █     ██████    █             █░░░
   ▌            ▐         █               █    ∗       ░░░░░
   ▌            ▐  ∗      █               █          ░░░░░▒▒
   ▌  ▛▀▀▀▜     ▐         █   ∗           █        ░░░░░▒░░░
∗  ▌  ▌   ▐     ▐      ∗  █          ∗   ░░░░░░░▓░░░░░░▒▒░░░
   ▌  ▌ ╾ ▐     ▐         █░░░░░      ░░░░▒░░░░▓░░░░░░░░░░░░
   ▌  ▌   ▐     ▐     ░░░░░▒▒▒░░░░░░░░░░░░░░░░░▒▒▒░░░░░░░▓▓▓
   ▙▄▄▙▄▄▄▟▄▄▄▄▄▟     ░░░░▒▒░░░░▓▓░░░░░░░░░▓░░░░░░░░░░░░░░░░
░░░░░░░░░░░▒▒▒░░░░▒░░░░░░░░░░░░░░░░░░░░▓▓░░░░░░░░░▓▓░░▒▒░░░░
░░▓░░▒░░░▓░░░░░░░░░░░░░░░░░▒░▓░░░▒░░░░▓░░░░░▒░░░░▓▓░▒▒░░░░░░
░▓▓░░▒░░░░░░▒░░░░░░░░░░░░░░░▓▓▓░░░▒░░░░░░░░░▒▒░▒░░░░░░░░▒░░░
░░░░░░░▒░░░░░░░░▓▓▓░░░░▒▒░░▒░░░░░░▒▓▓░░▒▒░░░░░░▓░░▓░░░░▓▒░░░
░░░▒░░░▓░░░░░▒░░░░░░▒▓░░░░░░░░░░░░░▓░░░░░░░▓░░▓░▓░░░░░░▓░░░░
░░░░░░▓▓░░░▒▒▒░░░░░░░▓▓▓▓▓░░░░▒░░░░░▒░░░░░░░░░░▒░░░░▒░░░░░░░
░░░░▓░▒▒▒░░░░░░░░░░▒░░░░░░░░░░▓▓▓▒░░░░░░░░░▒░░░░▓░░░░░▓▓░░▒░
░░▓▓░░░░░░░▓░░▒░░░░░░░░░▒▒▒▒▒░░░░░░░░▒░▒▒░░░░░▓▓░░░░▓▓░░░░░░


               ╔═════════════════════════════╗
               ║ > Welcome to SkiOS v1.0.0   ║
               ║                             ║
               ║ > Please provide the        ║
               ║   master key to start       ║
               ║   the ski lift              ║
               ║                             ║
               ║ (format 0x1234567812345678) ║
               ║                             ║
               ╚═════════════════════════════╝

                    Please input your key
                    >0xe0102030604060
 gctf{V3r1log_ISnT_SO_H4rd_4fTer_4ll_!1!}
gctf{V3r1log_ISnT_SO_H4rd_4fTer_4ll_!1!}

ARISAI (intro)

Multi-Prime RSAの問題。構成する素数のビット数は小さいので、Nをfactordbで素因数分解する。

N = 8441831 * 8450987 * 8452019 * 8473027 * 8476817 * 8523661 * 8525711 * 8608673 * 8633423 * 8641453 * 8725153^2 * 8786017 * 8796721 * 8824679 * 8850601 * 8913481 * 8933437 * 9016037 * 9041551 * 9075889 * 9095939 * 9126197 * 9142547 * 9163981 * 9172531 * 9196001 * 9223867 * 9253319 * 9265309 * 9277921 * 9298747 * 9300803 * 9357883 * 9368759 * 9405353 * 9444839 * 9552029 * 9569057 * 9584371 * 9663629 * 9696719 * 9720223 * 9748049 * 9770723 * 9801269 * 9828727 * 9836483 * 9838117 * 9853043 * 9873373 * 9883469 * 9884603 * 9905167 * 9989579 * 10000759 * 10064897 * 10114409 * 10122389 * 10213001 * 10214591 * 10228861 * 10235447 * 10344643 * 10428001 * 10433911 * 10438013 * 10441523 * 10476001 * 10514083 * 10523977 * 10605817 * 10650929 * 10667479 * 10699517 * 10731407 * 10732091 * 10754837 * 10773781 * 10849837 * 10861127 * 10893173 * 10918459 * 10943417 * 10944433 * 11028001 * 11049739 * 11057621 * 11073793 * 11084419 * 11113789 * 11152859 * 11156681 * 11230451 * 11239903 * 11369903^2 * 11462177 * 11470343 * 11504419 * 11519971 * 11543971 * 11559637 * 11625619 * 11633267 * 11661121 * 11768401 * 11847721 * 11909747 * 11915809 * 11925691 * 11928173 * 11945093 * 11990089 * 12010259 * 12089663 * 12109277 * 12231853 * 12240667 * 12274813 * 12319117 * 12339689 * 12350357 * 12358079 * 12387329 * 12407609 * 12407959 * 12515033 * 12550357 * 12599803 * 12621067 * 12652597 * 12705883 * 12804707 * 12808151 * 12824027 * 12932669 * 12967831 * 13046717 * 13059269 * 13076249 * 13128433 * 13170671 * 13202297 * 13227367 * 13328803 * 13366687 * 13371181 * 13415921 * 13417357 * 13424921 * 13430423 * 13534007 * 13561657 * 13566431 * 13568981 * 13587683 * 13625263 * 13653811 * 13655797 * 13669967 * 13673927 * 13755149 * 13799299 * 13823059 * 13865617 * 13870601 * 13997617 * 14013617 * 14044937 * 14046449 * 14086979 * 14103413 * 14162843 * 14217041 * 14311291 * 14339863 * 14340289 * 14377679 * 14407667 * 14423561 * 14435203 * 14465153 * 14466281 * 14475521 * 14482381 * 14535811 * 14548939 * 14549063 * 14588369 * 14624459 * 14633851 * 14650763 * 14693927 * 14713939 * 14738869 * 14797501 * 14880347 * 14910199 * 14922409 * 14982181 * 15005579 * 15020413 * 15031937 * 15103373 * 15181499 * 15185399 * 15209617 * 15232961 * 15299831 * 15365261 * 15441739 * 15459343 * 15470893 * 15475193 * 15489707 * 15501071 * 15682181 * 15689647 * 15689981 * 15707093 * 15707143 * 15748631 * 15792169 * 15793247 * 15798877 * 15922301 * 15947639 * 16032721 * 16045049 * 16071229 * 16080319 * 16175597 * 16177433^2 * 16198717 * 16199101 * 16212913 * 16225283 * 16254883 * 16312763 * 16336267 * 16359283 * 16405027 * 16432721 * 16497373 * 16593167 * 16594681 * 16629163 * 16632713 * 16643707 * 16657153 * 16679137 * 16701907 * 16738913 * 16755269

このことを元に同じ値のべき乗があることに注意しphiを算出し、さらにdを算出し復号する。

#!/usr/bin/env python3
from Crypto.Util.number import *

with open('output.txt', 'r') as f:
    params = f.read().splitlines()

N = int(params[0].split('=')[1])
e = int(params[1].split('=')[1])
ct = int(params[2].split('=')[1])

factor = '8441831 * 8450987 * 8452019 * 8473027 * 8476817 * 8523661 * 8525711 * 8608673 * 8633423 * 8641453 * 8725153^2 * 8786017 * 8796721 * 8824679 * 8850601 * 8913481 * 8933437 * 9016037 * 9041551 * 9075889 * 9095939 * 9126197 * 9142547 * 9163981 * 9172531 * 9196001 * 9223867 * 9253319 * 9265309 * 9277921 * 9298747 * 9300803 * 9357883 * 9368759 * 9405353 * 9444839 * 9552029 * 9569057 * 9584371 * 9663629 * 9696719 * 9720223 * 9748049 * 9770723 * 9801269 * 9828727 * 9836483 * 9838117 * 9853043 * 9873373 * 9883469 * 9884603 * 9905167 * 9989579 * 10000759 * 10064897 * 10114409 * 10122389 * 10213001 * 10214591 * 10228861 * 10235447 * 10344643 * 10428001 * 10433911 * 10438013 * 10441523 * 10476001 * 10514083 * 10523977 * 10605817 * 10650929 * 10667479 * 10699517 * 10731407 * 10732091 * 10754837 * 10773781 * 10849837 * 10861127 * 10893173 * 10918459 * 10943417 * 10944433 * 11028001 * 11049739 * 11057621 * 11073793 * 11084419 * 11113789 * 11152859 * 11156681 * 11230451 * 11239903 * 11369903^2 * 11462177 * 11470343 * 11504419 * 11519971 * 11543971 * 11559637 * 11625619 * 11633267 * 11661121 * 11768401 * 11847721 * 11909747 * 11915809 * 11925691 * 11928173 * 11945093 * 11990089 * 12010259 * 12089663 * 12109277 * 12231853 * 12240667 * 12274813 * 12319117 * 12339689 * 12350357 * 12358079 * 12387329 * 12407609 * 12407959 * 12515033 * 12550357 * 12599803 * 12621067 * 12652597 * 12705883 * 12804707 * 12808151 * 12824027 * 12932669 * 12967831 * 13046717 * 13059269 * 13076249 * 13128433 * 13170671 * 13202297 * 13227367 * 13328803 * 13366687 * 13371181 * 13415921 * 13417357 * 13424921 * 13430423 * 13534007 * 13561657 * 13566431 * 13568981 * 13587683 * 13625263 * 13653811 * 13655797 * 13669967 * 13673927 * 13755149 * 13799299 * 13823059 * 13865617 * 13870601 * 13997617 * 14013617 * 14044937 * 14046449 * 14086979 * 14103413 * 14162843 * 14217041 * 14311291 * 14339863 * 14340289 * 14377679 * 14407667 * 14423561 * 14435203 * 14465153 * 14466281 * 14475521 * 14482381 * 14535811 * 14548939 * 14549063 * 14588369 * 14624459 * 14633851 * 14650763 * 14693927 * 14713939 * 14738869 * 14797501 * 14880347 * 14910199 * 14922409 * 14982181 * 15005579 * 15020413 * 15031937 * 15103373 * 15181499 * 15185399 * 15209617 * 15232961 * 15299831 * 15365261 * 15441739 * 15459343 * 15470893 * 15475193 * 15489707 * 15501071 * 15682181 * 15689647 * 15689981 * 15707093 * 15707143 * 15748631 * 15792169 * 15793247 * 15798877 * 15922301 * 15947639 * 16032721 * 16045049 * 16071229 * 16080319 * 16175597 * 16177433^2 * 16198717 * 16199101 * 16212913 * 16225283 * 16254883 * 16312763 * 16336267 * 16359283 * 16405027 * 16432721 * 16497373 * 16593167 * 16594681 * 16629163 * 16632713 * 16643707 * 16657153 * 16679137 * 16701907 * 16738913 * 16755269'
ps = factor.split(' * ')

phi = 1
for p in ps:
    if '^' in p:
        b = int(p.split('^')[0])
        pwr = int(p.split('^')[1])
        phi *= pow(b, pwr - 1) * (b - 1)
    else:
        phi *= int(p) - 1

d = inverse(e, phi)
m = pow(ct, d, N)
flag = long_to_bytes(m).decode()
print(flag)
gctf{maybe_I_should_have_used_bigger_primes}

Password recovery (rev)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  byte bVar1;
  int iVar2;
  ulong uVar3;
  size_t sVar4;
  long in_FS_OFFSET;
  ulong local_b8;
  ulong local_b0;
  byte local_98 [64];
  char local_58 [56];
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Enter your name: ");
  __isoc99_scanf(&DAT_00102016,local_98);
  printf("Enter your password: ");
  __isoc99_scanf(&DAT_00102016,local_58);
  local_b8 = 0;
  while( true ) {
    sVar4 = strlen((char *)local_98);
    if (sVar4 <= local_b8) break;
    uVar3 = next_rand_value();
    sVar4 = strlen((char *)local_98);
    bVar1 = local_98[local_b8];
    local_98[local_b8] = local_98[uVar3 % sVar4];
    local_98[uVar3 % sVar4] = bVar1;
    local_b8 = local_b8 + 1;
  }
  local_b0 = 0;
  while( true ) {
    sVar4 = strlen((char *)local_98);
    if (sVar4 <= local_b0) break;
    local_98[local_b0] = local_98[local_b0] ^ *(byte *)((long)&key + (ulong)((uint)local_b0 & 7));
    local_98[local_b0] = (char)local_98[local_b0] % '\x1a';
    local_98[local_b0] = local_98[local_b0] + 0x61;
    local_b0 = local_b0 + 1;
  }
  iVar2 = strcmp((char *)local_98,local_58);
  if (iVar2 == 0) {
    puts("Valid!");
  }
  else {
    puts("Invalid!");
  }
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

undefined8 next_rand_value(void)

{
  state = xor_shift(state);
  return state;
}

ulong xor_shift(ulong param_1)

{
  param_1 = param_1 ^ param_1 << 0xd;
  param_1 = param_1 ^ param_1 >> 0x11;
  return param_1 ^ param_1 << 5;
}

                             state                                           XREF[4]:     Entry Point(*), 
                                                                                          next_rand_value:00101227(R), 
                                                                                          next_rand_value:00101236(W), 
                                                                                          next_rand_value:0010123d(R)  
        00104010 37 13 00        undefined8 0000000000001337h
                 00 00 00 
                 00 00


                             key                                             XREF[3]:     Entry Point(*), main:001013a3(*), 
                                                                                          main:001013cf(*)  
        00104018 de c0 ef        undefined8 1337DEADBEEFC0DEh
                 be ad de 
                 37 13

nameからpasswordを算出している。gdbで比較している文字列を確認する。

$ gdb -q ./app
Reading symbols from ./app...
(No debugging symbols found in ./app)
gdb-peda$ start
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[----------------------------------registers-----------------------------------]
RAX: 0x555555555246 (<main>:    endbr64)
RBX: 0x7fffffffde78 --> 0x7fffffffe203 ("/media/sf_Shared/app")
RCX: 0x555555557d98 --> 0x5555555551a0 (<__do_global_dtors_aux>:        endbr64)
RDX: 0x7fffffffde88 --> 0x7fffffffe218 ("CLUTTER_IM_MODULE=xim")
RSI: 0x7fffffffde78 --> 0x7fffffffe203 ("/media/sf_Shared/app")
RDI: 0x1 
RBP: 0x7fffffffdd60 --> 0x1 
RSP: 0x7fffffffdd60 --> 0x1 
RIP: 0x55555555524e (<main+8>:  push   rbx)
R8 : 0x0 
R9 : 0x7ffff7fcfaf0 (<_dl_fini>:        push   rbp)
R10: 0x7ffff7fcb858 --> 0xa00120000000e 
R11: 0x7ffff7fe1cf0 (<_dl_audit_preinit>:       mov    eax,DWORD PTR [rip+0x1b162]        # 0x7ffff7ffce58 <_rtld_global_ro+888>)
R12: 0x0 
R13: 0x7fffffffde88 --> 0x7fffffffe218 ("CLUTTER_IM_MODULE=xim")
R14: 0x555555557d98 --> 0x5555555551a0 (<__do_global_dtors_aux>:        endbr64)
R15: 0x7ffff7ffd000 --> 0x7ffff7ffe2c0 --> 0x555555554000 --> 0x10102464c457f
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555555246 <main>:       endbr64
   0x55555555524a <main+4>:     push   rbp
   0x55555555524b <main+5>:     mov    rbp,rsp
=> 0x55555555524e <main+8>:     push   rbx
   0x55555555524f <main+9>:     sub    rsp,0xb8
   0x555555555256 <main+16>:    mov    rax,QWORD PTR fs:0x28
   0x55555555525f <main+25>:    mov    QWORD PTR [rbp-0x18],rax
   0x555555555263 <main+29>:    xor    eax,eax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdd60 --> 0x1 
0008| 0x7fffffffdd68 --> 0x7ffff7ded6ca (<__libc_start_call_main+122>:  mov    edi,eax)
0016| 0x7fffffffdd70 --> 0x0 
0024| 0x7fffffffdd78 --> 0x555555555246 (<main>:        endbr64)
0032| 0x7fffffffdd80 --> 0x100000000 
0040| 0x7fffffffdd88 --> 0x7fffffffde78 --> 0x7fffffffe203 ("/media/sf_Shared/app")
0048| 0x7fffffffdd90 --> 0x7fffffffde78 --> 0x7fffffffe203 ("/media/sf_Shared/app")
0056| 0x7fffffffdd98 --> 0x19653f11ae234a58 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Temporary breakpoint 1, 0x000055555555524e in main ()
gdb-peda$ disas main
Dump of assembler code for function main:
   0x0000555555555246 <+0>:     endbr64
   0x000055555555524a <+4>:     push   rbp
   0x000055555555524b <+5>:     mov    rbp,rsp
=> 0x000055555555524e <+8>:     push   rbx
   0x000055555555524f <+9>:     sub    rsp,0xb8
   0x0000555555555256 <+16>:    mov    rax,QWORD PTR fs:0x28
   0x000055555555525f <+25>:    mov    QWORD PTR [rbp-0x18],rax
   0x0000555555555263 <+29>:    xor    eax,eax
   0x0000555555555265 <+31>:    lea    rax,[rip+0xd98]        # 0x555555556004
   0x000055555555526c <+38>:    mov    rdi,rax
   0x000055555555526f <+41>:    mov    eax,0x0
   0x0000555555555274 <+46>:    call   0x5555555550d0 <printf@plt>
   0x0000555555555279 <+51>:    lea    rax,[rbp-0x90]
   0x0000555555555280 <+58>:    mov    rsi,rax
   0x0000555555555283 <+61>:    lea    rax,[rip+0xd8c]        # 0x555555556016
   0x000055555555528a <+68>:    mov    rdi,rax
   0x000055555555528d <+71>:    mov    eax,0x0
   0x0000555555555292 <+76>:    call   0x5555555550f0 <__isoc99_scanf@plt>
   0x0000555555555297 <+81>:    lea    rax,[rip+0xd7b]        # 0x555555556019
   0x000055555555529e <+88>:    mov    rdi,rax
   0x00005555555552a1 <+91>:    mov    eax,0x0
   0x00005555555552a6 <+96>:    call   0x5555555550d0 <printf@plt>
   0x00005555555552ab <+101>:   lea    rax,[rbp-0x50]
   0x00005555555552af <+105>:   mov    rsi,rax
   0x00005555555552b2 <+108>:   lea    rax,[rip+0xd5d]        # 0x555555556016
   0x00005555555552b9 <+115>:   mov    rdi,rax
   0x00005555555552bc <+118>:   mov    eax,0x0
   0x00005555555552c1 <+123>:   call   0x5555555550f0 <__isoc99_scanf@plt>
   0x00005555555552c6 <+128>:   mov    QWORD PTR [rbp-0xb0],0x0
   0x00005555555552d1 <+139>:   jmp    0x55555555536a <main+292>
   0x00005555555552d6 <+144>:   mov    eax,0x0
   0x00005555555552db <+149>:   call   0x55555555521f <next_rand_value>
   0x00005555555552e0 <+154>:   mov    rbx,rax
   0x00005555555552e3 <+157>:   lea    rax,[rbp-0x90]
   0x00005555555552ea <+164>:   mov    rdi,rax
   0x00005555555552ed <+167>:   call   0x5555555550b0 <strlen@plt>
   0x00005555555552f2 <+172>:   mov    rcx,rax
   0x00005555555552f5 <+175>:   mov    rax,rbx
   0x00005555555552f8 <+178>:   mov    edx,0x0
   0x00005555555552fd <+183>:   div    rcx
   0x0000555555555300 <+186>:   mov    QWORD PTR [rbp-0x98],rdx
   0x0000555555555307 <+193>:   lea    rdx,[rbp-0x90]
   0x000055555555530e <+200>:   mov    rax,QWORD PTR [rbp-0xb0]
   0x0000555555555315 <+207>:   add    rax,rdx
   0x0000555555555318 <+210>:   movzx  eax,BYTE PTR [rax]
   0x000055555555531b <+213>:   mov    BYTE PTR [rbp-0xb1],al
   0x0000555555555321 <+219>:   lea    rdx,[rbp-0x90]
   0x0000555555555328 <+226>:   mov    rax,QWORD PTR [rbp-0x98]
   0x000055555555532f <+233>:   add    rax,rdx
   0x0000555555555332 <+236>:   movzx  eax,BYTE PTR [rax]
   0x0000555555555335 <+239>:   lea    rcx,[rbp-0x90]
   0x000055555555533c <+246>:   mov    rdx,QWORD PTR [rbp-0xb0]
   0x0000555555555343 <+253>:   add    rdx,rcx
   0x0000555555555346 <+256>:   mov    BYTE PTR [rdx],al
   0x0000555555555348 <+258>:   lea    rdx,[rbp-0x90]
   0x000055555555534f <+265>:   mov    rax,QWORD PTR [rbp-0x98]
   0x0000555555555356 <+272>:   add    rdx,rax
   0x0000555555555359 <+275>:   movzx  eax,BYTE PTR [rbp-0xb1]
   0x0000555555555360 <+282>:   mov    BYTE PTR [rdx],al
   0x0000555555555362 <+284>:   add    QWORD PTR [rbp-0xb0],0x1
   0x000055555555536a <+292>:   lea    rax,[rbp-0x90]
   0x0000555555555371 <+299>:   mov    rdi,rax
   0x0000555555555374 <+302>:   call   0x5555555550b0 <strlen@plt>
   0x0000555555555379 <+307>:   cmp    QWORD PTR [rbp-0xb0],rax
   0x0000555555555380 <+314>:   jb     0x5555555552d6 <main+144>
   0x0000555555555386 <+320>:   mov    QWORD PTR [rbp-0xa8],0x0
   0x0000555555555391 <+331>:   jmp    0x555555555467 <main+545>
   0x0000555555555396 <+336>:   mov    rax,QWORD PTR [rbp-0xa8]
   0x000055555555539d <+343>:   and    eax,0x7
   0x00005555555553a0 <+346>:   mov    rdx,rax
   0x00005555555553a3 <+349>:   lea    rax,[rip+0x2c6e]        # 0x555555558018 <key>
   0x00005555555553aa <+356>:   add    rax,rdx
   0x00005555555553ad <+359>:   mov    QWORD PTR [rbp-0xa0],rax
   0x00005555555553b4 <+366>:   lea    rdx,[rbp-0x90]
   0x00005555555553bb <+373>:   mov    rax,QWORD PTR [rbp-0xa8]
   0x00005555555553c2 <+380>:   add    rax,rdx
   0x00005555555553c5 <+383>:   movzx  edx,BYTE PTR [rax]
   0x00005555555553c8 <+386>:   mov    rax,QWORD PTR [rbp-0xa0]
   0x00005555555553cf <+393>:   movzx  eax,BYTE PTR [rax]
   0x00005555555553d2 <+396>:   xor    edx,eax
   0x00005555555553d4 <+398>:   lea    rcx,[rbp-0x90]
   0x00005555555553db <+405>:   mov    rax,QWORD PTR [rbp-0xa8]
   0x00005555555553e2 <+412>:   add    rax,rcx
   0x00005555555553e5 <+415>:   mov    BYTE PTR [rax],dl
   0x00005555555553e7 <+417>:   lea    rdx,[rbp-0x90]
   0x00005555555553ee <+424>:   mov    rax,QWORD PTR [rbp-0xa8]
   0x00005555555553f5 <+431>:   add    rax,rdx
   0x00005555555553f8 <+434>:   movzx  edx,BYTE PTR [rax]
   0x00005555555553fb <+437>:   mov    ecx,0x4f
   0x0000555555555400 <+442>:   mov    eax,ecx
   0x0000555555555402 <+444>:   imul   dl
   0x0000555555555404 <+446>:   shr    ax,0x8
   0x0000555555555408 <+450>:   sar    al,0x3
   0x000055555555540b <+453>:   mov    ecx,edx
   0x000055555555540d <+455>:   sar    cl,0x7
   0x0000555555555410 <+458>:   sub    eax,ecx
   0x0000555555555412 <+460>:   mov    ecx,0x1a
   0x0000555555555417 <+465>:   imul   eax,ecx
   0x000055555555541a <+468>:   mov    ecx,eax
   0x000055555555541c <+470>:   mov    eax,edx
   0x000055555555541e <+472>:   sub    eax,ecx
   0x0000555555555420 <+474>:   lea    rcx,[rbp-0x90]
   0x0000555555555427 <+481>:   mov    rdx,QWORD PTR [rbp-0xa8]
   0x000055555555542e <+488>:   add    rdx,rcx
   0x0000555555555431 <+491>:   mov    BYTE PTR [rdx],al
   0x0000555555555433 <+493>:   lea    rdx,[rbp-0x90]
   0x000055555555543a <+500>:   mov    rax,QWORD PTR [rbp-0xa8]
   0x0000555555555441 <+507>:   add    rax,rdx
   0x0000555555555444 <+510>:   movzx  eax,BYTE PTR [rax]
   0x0000555555555447 <+513>:   add    eax,0x61
   0x000055555555544a <+516>:   mov    ecx,eax
   0x000055555555544c <+518>:   lea    rdx,[rbp-0x90]
   0x0000555555555453 <+525>:   mov    rax,QWORD PTR [rbp-0xa8]
   0x000055555555545a <+532>:   add    rax,rdx
   0x000055555555545d <+535>:   mov    BYTE PTR [rax],cl
   0x000055555555545f <+537>:   add    QWORD PTR [rbp-0xa8],0x1
   0x0000555555555467 <+545>:   lea    rax,[rbp-0x90]
   0x000055555555546e <+552>:   mov    rdi,rax
   0x0000555555555471 <+555>:   call   0x5555555550b0 <strlen@plt>
   0x0000555555555476 <+560>:   cmp    QWORD PTR [rbp-0xa8],rax
   0x000055555555547d <+567>:   jb     0x555555555396 <main+336>
   0x0000555555555483 <+573>:   lea    rdx,[rbp-0x50]
   0x0000555555555487 <+577>:   lea    rax,[rbp-0x90]
   0x000055555555548e <+584>:   mov    rsi,rdx
   0x0000555555555491 <+587>:   mov    rdi,rax
   0x0000555555555494 <+590>:   call   0x5555555550e0 <strcmp@plt>
   0x0000555555555499 <+595>:   test   eax,eax
   0x000055555555549b <+597>:   jne    0x5555555554ae <main+616>
   0x000055555555549d <+599>:   lea    rax,[rip+0xb8b]        # 0x55555555602f
   0x00005555555554a4 <+606>:   mov    rdi,rax
   0x00005555555554a7 <+609>:   call   0x5555555550a0 <puts@plt>
   0x00005555555554ac <+614>:   jmp    0x5555555554bd <main+631>
   0x00005555555554ae <+616>:   lea    rax,[rip+0xb81]        # 0x555555556036
   0x00005555555554b5 <+623>:   mov    rdi,rax
   0x00005555555554b8 <+626>:   call   0x5555555550a0 <puts@plt>
   0x00005555555554bd <+631>:   mov    eax,0x0
   0x00005555555554c2 <+636>:   mov    rdx,QWORD PTR [rbp-0x18]
   0x00005555555554c6 <+640>:   sub    rdx,QWORD PTR fs:0x28
   0x00005555555554cf <+649>:   je     0x5555555554d6 <main+656>
   0x00005555555554d1 <+651>:   call   0x5555555550c0 <__stack_chk_fail@plt>
   0x00005555555554d6 <+656>:   mov    rbx,QWORD PTR [rbp-0x8]
   0x00005555555554da <+660>:   leave
   0x00005555555554db <+661>:   ret
End of assembler dump.
gdb-peda$ b * 0x0000555555555494
Breakpoint 2 at 0x555555555494
gdb-peda$ c
Continuing.
Enter your name: LosCapitan
Enter your password: password!!
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffdcd0 ("]^WR\\\\lcTI")
RBX: 0x272c8fe355ddd8e8 
RCX: 0x149 
RDX: 0x7fffffffdd10 ("password!!")
RSI: 0x7fffffffdd10 ("password!!")
RDI: 0x7fffffffdcd0 ("]^WR\\\\lcTI")
RBP: 0x7fffffffdd60 --> 0x1 
RSP: 0x7fffffffdca0 --> 0x0 
RIP: 0x555555555494 (<main+590>:        call   0x5555555550e0 <strcmp@plt>)
R8 : 0xb ('\x0b')
R9 : 0x7ffff7f99aa0 --> 0xfbad2288 
R10: 0x0 
R11: 0x7ffff7f9a580 --> 0x7ffff7f96820 --> 0x7ffff7f5d5f7 --> 0x5a5400544d470043 ('C')
R12: 0x0 
R13: 0x7fffffffde88 --> 0x7fffffffe218 ("CLUTTER_IM_MODULE=xim")
R14: 0x555555557d98 --> 0x5555555551a0 (<__do_global_dtors_aux>:        endbr64)
R15: 0x7ffff7ffd000 --> 0x7ffff7ffe2c0 --> 0x555555554000 --> 0x10102464c457f
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555555487 <main+577>:   lea    rax,[rbp-0x90]
   0x55555555548e <main+584>:   mov    rsi,rdx
   0x555555555491 <main+587>:   mov    rdi,rax
=> 0x555555555494 <main+590>:   call   0x5555555550e0 <strcmp@plt>
   0x555555555499 <main+595>:   test   eax,eax
   0x55555555549b <main+597>:   jne    0x5555555554ae <main+616>
   0x55555555549d <main+599>:   lea    rax,[rip+0xb8b]        # 0x55555555602f
   0x5555555554a4 <main+606>:   mov    rdi,rax
Guessed arguments:
arg[0]: 0x7fffffffdcd0 ("]^WR\\\\lcTI")
arg[1]: 0x7fffffffdd10 ("password!!")
arg[2]: 0x7fffffffdd10 ("password!!")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdca0 --> 0x0 
0008| 0x7fffffffdca8 --> 0x6100000000000000 ('')
0016| 0x7fffffffdcb0 --> 0xa ('\n')
0024| 0x7fffffffdcb8 --> 0xa ('\n')
0032| 0x7fffffffdcc0 --> 0x555555558019 --> 0x1337deadbeefc0 
0040| 0x7fffffffdcc8 --> 0x2 
0048| 0x7fffffffdcd0 ("]^WR\\\\lcTI")
0056| 0x7fffffffdcd8 --> 0x4954 ('TI')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, 0x0000555555555494 in main ()
gdb-peda$ 

比較している文字列は以下であることがわかった。

"]^WR\\\\lcTI"

エスケープ文字列を外し、フラグ形式になるようラップする。

gctf{]^WR\\lcTI}

Missing Bits (crypto)

priv.keyは先頭何バイトか削除されている。
秘密鍵の構成は以下のようになっている。

RSAPrivateKey ::= SEQUENCE {
    version           Version,
    modulus           INTEGER, -- n
    publicExponent    INTEGER, -- e
    privateExponent   INTEGER, -- d
    prime1            INTEGER, -- p
    prime2            INTEGER, -- q
    exponent1         INTEGER, -- d mod (p-1)
    exponent2         INTEGER, -- d mod (q-1)
    coefficient       INTEGER, -- (inverse of q) mod p
    otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

priv.keyのわかっている範囲でbase64デコードする。各オフセットの以下の範囲で以下のような値が設定されていることがわかる。

0x001e-0x0020: 0x010001 ← e
0x0025-0x0124: 0x0a9a...6981 ← d
0x0128-0x01a8: 0x00e4...2a21 ← p
0x01ac-0x022c: 0x00f1...69e7 ← q
     :

このパラメータを元に、RSA暗号の復号を行う。

#!/usr/bin/env python3
from Crypto.Util.number import *
from base64 import *

with open('ciphertext_message', 'rb') as f:
    ct = bytes_to_long(f.read())

with open('priv.key', 'r') as f:
    lines = f.read().splitlines()

data = b64decode(''.join(lines[6:-1]))

e = int.from_bytes(data[0x001e:0x0021], 'big')
d = int.from_bytes(data[0x0025:0x0125], 'big')
p = int.from_bytes(data[0x0128:0x01a9], 'big')
q = int.from_bytes(data[0x01ac:0x022d], 'big')

phi = (p - 1) * (q - 1)
assert inverse(e, phi) == d

m = pow(ct, d, p * q)
filecontent = long_to_bytes(m).decode()
print(filecontent)

実行結果は以下の通り。

Hey Bob this is Alice.
I want to let you know that the Flag is gctf{7hi5_k3y_can_b3_r3c0ns7ruc7ed}
gctf{7hi5_k3y_can_b3_r3c0ns7ruc7ed}

SLCG (crypto)

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

・encryption = Encryptor()
 ・encryption.lcgs = (LCG.random_values(), LCG.random_values())
・ct = encryption.encrypt(FLAG)
 ・result = []
 ・FLAGの各文字のASCIIコードascii_charについて、以下を実行
  ・bin_char: ascii_charの2進数のリスト
  ・bin_charの各bitについて以下を実行
   ・next(encryption.lcgs[bit])をresultに追加
   ・encryption.lcgs = (
                LCG(
                    next(encryption.lcgs[0]), next(encryption.lcgs[0]),
                    next(encryption.lcgs[0]), next(encryption.lcgs[0])
                ),
                LCG(
                    next(encryption.lcgs[1]), next(encryption.lcgs[1]),
                    next(encryption.lcgs[1]), next(encryption.lcgs[1])
                )
            )
・ctを出力

フラグが"gctf{"から始まることを前提に考える。

>>> bin(ord('g'))
'0b1100111'
>>> bin(ord('c'))
'0b1100011'
>>> bin(ord('t'))
'0b1110100'
>>> bin(ord('f'))
'0b1100110'
>>> bin(ord('{'))
'0b1111011'

最初のLCGを以下のように置く。

lcgs = (LCG(m0_0, a0_0, c0_0, s0_0), LCG(m1_0, a1_0, c1_0, s1_0))

このとき、以下のようになる。

s1_1 = (s1_0 * a1_0 + c1_0) % m1_0 = ct[0]
s1_2 = (s1_1 * a1_0 + c1_0) % m1_0 = ct[1]
s0_1 = (s0_0 * a0_0 + c0_0) % m0_0 = ct[2]
s0_2 = (s0_1 * a0_0 + c0_0) % m0_0 = ct[3]
s1_3 = (s1_2 * a1_0 + c1_0) % m1_0 = ct[4]
s1_4 = (s1_3 * a1_0 + c1_0) % m1_0 = ct[5]
s1_5 = (s1_4 * a1_0 + c1_0) % m1_0 = ct[6]

例えば"g"の場合などは"1"が5個あり、情報量からLCGの2つ目のみ、パラメータを割り出すことができる。パラメータを割り出したら、計算していき、あてはまる場合は"1"、そうでない場合は"0"にすれば復号できる。

#!/usr/bin/env python3
from Crypto.Util.number import *
from functools import reduce

with open('ciphertext.txt', 'r') as f:
    ct = eval(f.read().split(' = ')[1])

flag_head = b'gctf{'
bin_flag_head = list(''.join([bin(c)[2:].zfill(7) for c in flag_head]))

b0 = bin(flag_head[0])[2:].zfill(7)

ct1 = []
for i in range(len(b0)):
    if b0[i] == '1':
        ct1.append(ct[i])

delta = [d1 - d0 for (d0, d1) in zip(ct1, ct1[1:])]
p_mul = [d0 * d2 - d1 * d1 for (d0, d1, d2) in zip(delta, delta[1:], delta[2:])]
m = reduce(GCD, p_mul)
a = (delta[1] * inverse(delta[0], m)) % m
c = (ct1[1] - a * ct1[0]) % m
s = (ct1[0] - c) * inverse(a, m) % m

flag = ''
for i in range(0, len(ct), 7):
    s = (s * a + c) % m
    tmp_ct = ct[i:i+7]
    bin_char = ''
    for j in range(7):
        if tmp_ct[j] == s:
            bin_char += '1'
            s = (s * a + c) % m
        else:
            bin_char += '0'

    s_list = []
    for j in range(4):
        s_list.append(s)
        s = (s * a + c) % m
    m, a, c, s = s_list
    flag += chr(int(bin_char, 2))
print(flag)
gctf{th15_lcg_3ncryp710n_w4sn7_s0_5s3cur3_aft3r_4ll}

Glacier Spirit (crypto)

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

・key: ランダム16バイト文字列
・nonce, ct, tag = create_message_and_mac(key, FLAG)
 ・padded_message = pad_message(FLAG)
  ・first_block_pad: FLAGの長さを16で割った余り(0の場合は16)
  ・first_block_padのバイト文字を16-first_block_padの数分連結した文字列 + FLAGを返す。
 ・nonce, ct = encrypt(key, padded_message)
  ・message_blocks: padded_message16バイトごとの配列
  ・nonce: ランダム15バイト文字列
  ・cts = []
  ・message_blocksの各messageについて(インデックス:ctr)、以下を実行
   ・cipher_input = nonce + [(ctr+1)の文字列(1バイト)]
   ・enc = ascon.encrypt(key, cipher_input, b'', b'')
   ・ct: messageとencのXOR
   ・ctsにctを追加
  ・nonceとctsの連結文字列を返す。
 ・tag = mac_creation(key, padded_message)
  ・message_blocks: padded_message16バイトごとの配列
  ・enc_out = b"\x00" * 16
  ・message_blocksの各messageについて、以下を実行
   ・chaining_values: messageとenc_outのXOR
   ・enc_out = ascon.encrypt(key, chaining_values, b'', b'')
  ・enc_outを返す。
 ・nonce, ct, tagを返す。
・nonce, ct, tagを16進数表記で表示
・8回以下繰り返し
 ・user_msg: 入力
 ・msg: user_msgをhexデコード
 ・nonce, ct, tag = create_message_and_mac(key, msg)
 ・nonce, ct, tagを16進数表記で表示

ctは以下のように生成される。

'' --(nonce: nonce + \x01で暗号化)--> CT0 ^ PT0
'' --(nonce: nonce + \x02で暗号化)--> CT1 ^ PT1
'' --(nonce: nonce + \x03で暗号化)--> CT2 ^ PT2

tagは1ブロックの場合以下のように生成される。

'' --(nonce: PT0で暗号化)       --> CT0

フラグの暗号化で使われたnonceを使ってメッセージを指定する。
1回目はmessage = nonce + '01'を指定し、そのタグとフラグの暗号化の1ブロック目をXORすれば1ブロック目を復号できる。
2回目以降同様にして2ブロック目以降を復号し、結合すればパディングしたフラグが得られ、フラグがわかる。

#!/usr/bin/env python3
import socket

BLOCK_SIZE = 16

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

def xor(a, b):
    return bytes([x ^ y for x, y in zip(a, b)])

def unpad_message(message):
    first_block_pad = message[0]
    assert message[:BLOCK_SIZE - first_block_pad] == bytes([first_block_pad]) * (BLOCK_SIZE - first_block_pad)
    return message[BLOCK_SIZE - first_block_pad:]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chall.glacierctf.com', 13379))

data = recvuntil(s, b'glacier!\n').rstrip()
print(data)
nonce = data.splitlines()[13].split(', ')[0]
ct = bytes.fromhex(data.splitlines()[13].split(', ')[1])

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

flag = b''
for i in range(len(ct) // BLOCK_SIZE):
    data = recvuntil(s, b':\n').rstrip()
    print(data)
    message = nonce + str(i + 1).zfill(2)
    print(message)
    s.sendall(message.encode() + b'\n')
    for _ in range(3):
        data = recvuntil(s, b'\n').rstrip()
        print(data)
    tag = bytes.fromhex(data.split(', ')[2])
    flag += xor(ct[i*BLOCK_SIZE:(i+1)*BLOCK_SIZE], tag)

print()

flag = unpad_message(flag).decode()
print(flag)

実行結果は以下の通り。

              Glacier Spirit


           ,                  /\.__      _.-\
          /~\,      __       /~    \   ./    \
        ,/  /_\   _/  \    ,/~,_.~''\ /_\_  /'\
       / \ /## \ / V#\/\  /~8#  # ## V8  #\/8 8\
     /~#'#'#''##V&#&# ##\/88#'#8# #' #\#&'##' ##\
    j# ##### #'#\&&'####/###&  #'#&## #&' #'#&#'#'\
   /#'#'#####'###'\&##'/&#'####'### # #&#&##'#'### \
  J#'###'#'#'#'####'\# #'##'#'##'#'#####&'## '#'&'##|\
The spirit of the glacier gifts you a flag!

ce83fc02c986095dde690e9d842770, f922f14cbdbd3cf6fede7188c58594b6d07b24a29017f77cad54c46677c099c852b396bea4c0a5e30d1be5a11d56162c, 263866a764190098d4d2a13846c1ae29

Now you can bring forth your own messages to be blessed by the spirit of the glacier!

Offer your message:
ce83fc02c986095dde690e9d84277001
The spirit of the glacier has blessed your message!

ec75573858082f6c7f9e8cb27d7b52, 86d38c2f74596dfd72ef42dbc74d3f8c, f6459238dbc67fa28c813cb8a1b6cbd5
Offer your message:
ce83fc02c986095dde690e9d84277002
The spirit of the glacier has blessed your message!

656e8802cd862e066069db9a01f1a1, 84a67a9d9d5d811d9616fc6b5d048b31, b2187befa454a83dfe178b282884fc8e
Offer your message:
ce83fc02c986095dde690e9d84277003
The spirit of the glacier has blessed your message!

5a647b14be84a935ff6ddfea71f269, 7bb04770e826c9a3d3e2e7e81db179db, 61f2c29afb94edd0523f95906f1f4251

gctf{CTr_M0d3_cbc_M4C_ASCON_DeF3AT$_TH3_$p1rIT}
gctf{CTr_M0d3_cbc_M4C_ASCON_DeF3AT$_TH3_$p1rIT}