DawgCTF 2021 Writeup

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

DawgCTF Discord (Misc 5)

Discordに入り、#flagチャネルを見ると、フラグが書いてあった。

DawgCTF{3nj0y_th3_c0mp3t1t10n!}

Third Eye (Audio/Radio 75)

Sonic Visualiserでmp3を開き、スペクトログラムを見る。
f:id:satou-y:20210517203050p:plain

44 61 77 67 43 54 46 7b 73 79 6e
33 73 74 68 33 73 31 61 63 73 7d

この16進数の羅列をASCIIコードとしてデコードする。

>>> codes = '44 61 77 67 43 54 46 7b 73 79 6e 33 73 74 68 33 73 31 61 63 73 7d'.split(' ')
>>> ''.join([chr(int(code, 16)) for code in codes])
'DawgCTF{syn3sth3s1acs}'
DawgCTF{syn3sth3s1acs}

Tag, You're It! (Audio/Radio 100)

Mp3tagで開いてみると、コメントにこう書いてある。

Ḑ̶͙̀á̴̡̳͈̏ẃ̸͇͚g̸̭̣̱͂C̵̹̆̂Ṱ̴̡͍̀F̴̻͚͐̿̄{̴̟̃̀̐w̵̺̻͒̔͋h̴̭͛0̵͍̤͒͆͝_̷̟̈́͘̚d̶͙͕͜͝0̶͕͚͎̏̚w̸̦͙̃̽ǹ̷͙͚l̶̛̜̈́0̴̧̱͓͝a̶̘̮͚̿̈́ď̷̡̬́ŝ̴̢͔̌͝ͅ_̶̬̺͛̎̈́ͅm̵̳͗ű̶͎̊s̷̰̀̄͆1̸͕͖̈́c̶͔͆_̷̢̧̔̉̚â̵̙̔ǹ̵̖̦͈̇̿ỵ̴̬̓̔m̸̛͉̩̑0̸̮͓̏̊̀r̴͇͕̈́̄̉3̶̙̭͎͋̚͝?̴͔̟̩͊͛}̴̤̲͂͜

読みにくいが、サクラエディタで見て、文字を抜き出す。

DawgCTF{wh0_d0wnl0ads_mus1c_anym0r3?}

Just A Comment (Fwn (Forensics/Web/Network) 50)

pcapngのコメントを見ると、フラグが書いてあった。

DawgCTF{w3 h34r7 0ur 1r4d 734m}

These Ladies Paved Your Way (Fwn (Forensics/Web/Network) 150)

$ exiftool radia_perlman.jpg 
ExifTool Version Number         : 10.80
File Name                       : radia_perlman.jpg
Directory                       : .
File Size                       : 10 kB
File Modification Date/Time     : 2021:04:24 08:11:46+09:00
File Access Date/Time           : 2021:05:08 09:10:37+09:00
File Inode Change Date/Time     : 2021:04:24 08:11:46+09:00
File Permissions                : rwxrwxrwx
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Current IPTC Digest             : 8d370a1f7871e76616c0f06987707b84
Credit                          : U3Bhbm5pbmdUcmVlVmlnCg==
Application Record Version      : 4
Keywords                        : VpwtPBS{r0m5 0W t4x3IB5}
Comment                         : CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 80.
Image Width                     : 227
Image Height                    : 244
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 227x244
Megapixels                      : 0.055

$ echo U3Bhbm5pbmdUcmVlVmlnCg== | base64 -d
SpanningTreeVig

Vigenere暗号と推測し、https://www.dcode.fr/vigenere-cipherで復号する。

暗号:VpwtPBS{r0m5 0W t4x3IB5}
鍵 :SpanningTreeVig
DawgCTF{l0t5 0F p4t3NT5}

BBomb - Phase 1 (Binary Bomb 25)

Ghidraでデコンパイルする。

undefined4 phase1(undefined8 param_1)

{
  char *__s;
  size_t sVar1;
  size_t sVar2;
  undefined4 local_34;
  int local_30;
  
  puts(
      "\nStarting off easy... reversing (things) is fun! (Wrap all flags in DawgCTF{} when submitting to the scoreboard)"
      );
  local_34 = 1;
  __s = (char *)calloc(0x29,1);
  getInput(1,param_1,&DAT_001028d1,__s);
  local_30 = 0;
  sVar1 = strlen(__s);
  while( true ) {
    sVar2 = strlen("Gn1r7s_3h7_Gn15Rev3R");
    if (sVar2 <= (ulong)(long)local_30) break;
    sVar2 = strlen(__s);
    if (sVar2 <= (ulong)(long)local_30) break;
    if ("Gn1r7s_3h7_Gn15Rev3R"[local_30] != __s[(long)((int)sVar1 - local_30) + -1]) {
      local_34 = 0;
    }
    local_30 = local_30 + 1;
  }
  sVar1 = strlen("Gn1r7s_3h7_Gn15Rev3R");
  if ((long)local_30 != sVar1) {
    local_34 = 0;
  }
  free(__s);
  return local_34;
}

文字列を逆にして比較しているので、逆にして比較する文字列を割り出す。

>>> "Gn1r7s_3h7_Gn15Rev3R"[::-1]
'R3veR51nG_7h3_s7r1nG'
DawgCTF{R3veR51nG_7h3_s7r1nG}

BBomb - Phase 2 (Binary Bomb 50)

Ghidraでデコンパイルする。

undefined4 phase2(undefined8 param_1)

{
  char *__s;
  size_t sVar1;
  undefined4 local_30;
  int local_2c;
  
  puts("\nCan you help me find my lost key so I can read my string?");
  local_30 = 1;
  __s = (char *)calloc(0x29,1);
  getInput(2,param_1,&DAT_001028d1,__s);
  local_2c = 0;
  while( true ) {
    sVar1 = strlen("Dk52m6WZw@s6w0dIZh@2m5a");
    if (sVar1 <= (ulong)(long)local_2c) break;
    sVar1 = strlen(__s);
    if (sVar1 <= (ulong)(long)local_2c) break;
    if ("Dk52m6WZw@s6w0dIZh@2m5a"[local_2c] != (byte)(__s[local_2c] ^ 5U)) {
      local_30 = 0;
    }
    local_2c = local_2c + 1;
  }
  sVar1 = strlen("Dk52m6WZw@s6w0dIZh@2m5a");
  if ((long)local_2c != sVar1) {
    local_30 = 0;
  }
  free(__s);
  return local_30;
}

"Dk52m6WZw@s6w0dIZh@2m5a"の各文字と、入力文字と5をXORしたものを比較している。

>>> ''.join([chr(ord(c) ^ 5) for c in "Dk52m6WZw@s6w0dIZh@2m5a"])
'An07h3R_rEv3r5aL_mE7h0d'
DawgCTF{An07h3R_rEv3r5aL_mE7h0d}

BBomb - Phase 3 (Binary Bomb 75)

Ghidraでデコンパイルする。

bool phase3(undefined8 param_1)

{
  int iVar1;
  char *__s1;
  char *pcVar2;
  char *local_20;
  
  puts("\nReflections? Rotations? Translations? This is starting to sound like geometry...");
  __s1 = (char *)calloc(0x29,1);
  getInput(3,param_1,&DAT_001028d1,__s1);
  local_20 = __s1;
  while (*local_20 != '\0') {
    pcVar2 = (char *)func3_1(local_20);
    *local_20 = *pcVar2;
    pcVar2 = (char *)func3_2(local_20);
    *local_20 = *pcVar2;
    local_20 = local_20 + 1;
  }
  iVar1 = strcmp(__s1,"\"_9~Jb0!=A`G!06qfc8\'_20uf6`2%7");
  free(__s1);
  return iVar1 == 0;
}

char * func3_1(char *param_1)

{
  char cVar1;
  
  if (('@' < *param_1) && (*param_1 < '[')) {
    *param_1 = *param_1 + -0xd;
    if (*param_1 < 'A') {
      cVar1 = '\x1a';
    }
    else {
      cVar1 = '\0';
    }
    *param_1 = cVar1 + *param_1;
  }
  if (('`' < *param_1) && (*param_1 < '{')) {
    *param_1 = *param_1 + -0xd;
    if (*param_1 < 'a') {
      cVar1 = '\x1a';
    }
    else {
      cVar1 = '\0';
    }
    *param_1 = cVar1 + *param_1;
  }
  return param_1;
}

char * func3_2(char *param_1)

{
  char cVar1;
  
  if ((' ' < *param_1) && (*param_1 != '\x7f')) {
    *param_1 = *param_1 + -0x2f;
    if (*param_1 < '!') {
      cVar1 = '^';
    }
    else {
      cVar1 = '\0';
    }
    *param_1 = cVar1 + *param_1;
  }
  return param_1;
}

func3_1はrot13、func3_2はrot47。rot13して、rot47したものを"\"_9~Jb0!=A`G!06qfc8\'_20uf6`2%7"と比較しているので、元に戻す。

def rot47(s):
    d = ''
    for c in s:
        code = ord(c) - 47
        if code <= 32:
            code += 94
        d += chr(code)
    return d


enc = "\"_9~Jb0!=A`G!06qfc8\'_20uf6`2%7"

flag = rot47(enc).decode('rot13')
print flag

実行結果は以下の通り。

D0uBl3_Cyc1iC_rO74tI0n_S7r1nGs
DawgCTF{D0uBl3_Cyc1iC_rO74tI0n_S7r1nGs}

BBomb - Phase 4 (Binary Bomb 150)

Ghidraでデコンパイルする。

undefined4 phase4(undefined8 param_1)

{
  long lVar1;
  int iVar2;
  void *__ptr;
  long lVar3;
  long in_FS_OFFSET;
  undefined8 uVar4;
  undefined4 local_5c;
  int local_58;
  long local_48 [5];
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  puts("\nThis is the phase you have been waiting for... one may say it\'s the golden stage!");
  puts(
      "Let\'s switch things up! Numerical inputs map to line numbers in rockyou.txt, and each word is separated by a \'_\' (if the phase\'s solution is 4 5, the flag would be DawgCTF{password_iloveyou})"
      );
  local_5c = 1;
  local_48[0] = 1;
  local_48[1] = 0x7b;
  local_48[2] = 0x3b18;
  local_48[3] = 0x1c640d;
  iVar2 = func4(10);
  uVar4 = 0x10157f;
  __ptr = calloc(4,4);
  getInput(4,param_1,"%d%d%d%d",__ptr,(long)__ptr + 4,(long)__ptr + 8,(long)__ptr + 0xc,uVar4);
  local_58 = 0;
  while (local_58 < 4) {
    lVar1 = local_48[local_58];
    lVar3 = func4(*(undefined4 *)((long)__ptr + (long)local_58 * 4));
    if (lVar1 * iVar2 - lVar3 != 0) {
      local_5c = 0;
    }
    local_58 = local_58 + 1;
  }
  free(__ptr);
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return local_5c;
}

long func4(int param_1)

{
  long lVar1;
  long lVar2;
  
  if (param_1 < 1) {
    lVar1 = 0;
  }
  else {
    if (param_1 == 1) {
      lVar1 = 1;
    }
    else {
      lVar2 = func4(param_1 + -1);
      lVar1 = func4(param_1 + -2);
      lVar1 = lVar1 + lVar2;
    }
  }
  return lVar1;
}

func4はフィボナッチ数列の関数。lVar1 * iVar2 - lVar3 = 0を満たすようなlVar3を求め、それがフィボナッチ数列の何番目かを割り出せばよい。

def fibonacci(n):
    return round((((1 + 5 ** 0.5) / 2) ** n - ((1 - 5 ** 0.5) / 2) ** n) / 5 ** 0.5)

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

local_48 = [1, 0x7b, 0x3b18, 0x1c640d]
iVar2 = fibonacci(10)

ns = []
for i in range(100):
    ns.append(fibonacci(i))

flag = 'DawgCTF{'
for i in range(4):
    lVar1 = local_48[i]
    num = lVar1 * iVar2
    index = ns.index(num)
    print index,
    flag += lines[index - 1].rstrip()
    flag += '_'
print

flag = flag[:-1] + '}'
print flag

実行結果は以下の通り。

10 20 30 40
DawgCTF{abc123_qwerty_anthony_123123}
DawgCTF{abc123_qwerty_anthony_123123}

BBomb - Phase 6 (Binary Bomb 175)

Ghidraでデコンパイルする。

undefined4 phase6(undefined8 param_1)

{
  char *__s;
  size_t sVar1;
  long in_FS_OFFSET;
  undefined4 local_48;
  int local_44;
  char local_38 [4];
  undefined local_34;
  undefined local_33;
  undefined local_32;
  undefined local_31;
  undefined local_30;
  undefined local_2f;
  undefined local_2e;
  undefined local_2d;
  undefined local_2c;
  undefined local_2b;
  undefined local_2a;
  undefined local_29;
  undefined local_28;
  undefined local_27;
  undefined local_26;
  undefined local_25;
  undefined local_24;
  undefined local_23;
  undefined local_22;
  undefined local_21;
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  puts("\nOh no... I lost the key to my string again :(");
  local_48 = 1;
  local_38[0] = '@';
  local_38[1] = 0x77;
  local_38[2] = 0x23;
  local_38[3] = 0x91;
  local_34 = 0xb0;
  local_33 = 0x72;
  local_32 = 0x82;
  local_31 = 0x77;
  local_30 = 99;
  local_2f = 0x31;
  local_2e = 0xa2;
  local_2d = 0x72;
  local_2c = 0x21;
  local_2b = 0xf2;
  local_2a = 0x67;
  local_29 = 0x82;
  local_28 = 0x91;
  local_27 = 0x77;
  local_26 = 0x26;
  local_25 = 0x91;
  local_24 = 0;
  local_23 = 0x33;
  local_22 = 0x82;
  local_21 = 0xc4;
  __s = (char *)calloc(0x29,1);
  getInput(6,param_1,&DAT_001028d1);
  local_44 = 0;
  while( true ) {
    sVar1 = strlen(local_38);
    if (sVar1 <= (ulong)(long)local_44) break;
    sVar1 = strlen(__s);
    if (sVar1 <= (ulong)(long)local_44) break;
    __s[local_44] = (byte)((int)__s[local_44] << 4) | (byte)__s[local_44] >> 4;
    __s[local_44] = __s[local_44] ^ 100;
    if (__s[local_44] != local_38[local_44]) {
      local_48 = 0;
    }
    local_44 = local_44 + 1;
  }
  sVar1 = strlen(local_38);
  if ((long)local_44 != sVar1) {
    local_48 = 0;
  }
  free(__s);
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return local_48;
}

各バイトで前半4ビットと後半4ビットを入れ替え、100とのXORした結果を比較している。このことから逆算してフラグを求める。

codes = [ord('@'), 0x77, 0x23, 0x91, 0xb0, 0x72, 0x82, 0x77, 99, 0x31, 0xa2,
    0x72, 0x21, 0xf2, 0x67, 0x82, 0x91, 0x77, 0x26, 0x91, 0, 0x33, 0x82, 0xc4]

flag = ''
for code in codes:
    code = code ^ 100
    code = (code >> 4) | ((code << 4) & 0xff)
    flag += chr(code)

print flag

実行結果は以下の通り。

B1t_Man1pUlaTi0n_1$_Fun
DawgCTF{B1t_Man1pUlaTi0n_1$_Fun}

BBomb - Phase 7 (Binary Bomb 250)

Ghidraでデコンパイルする。

uint phase7(undefined8 param_1)

{
  undefined uVar1;
  int iVar2;
  int iVar3;
  uint uVar4;
  undefined8 *__ptr;
  void *pvVar5;
  uint local_38;
  int local_34;
  int local_30;
  int local_2c;
  int local_28;
  int local_24;
  
  puts("\nAt least we can say our code is resuable");
  local_38 = 1;
  __ptr = (undefined8 *)malloc(0x18);
  local_34 = 0;
  while (local_34 < 3) {
    pvVar5 = calloc(0x29,1);
    __ptr[local_34] = pvVar5;
    local_34 = local_34 + 1;
  }
  getInput(7,param_1,"%s%s%s",*__ptr,__ptr[1],__ptr[2]);
  local_30 = 0;
  local_2c = 0;
  do {
    if (2 < local_2c) {
      if (local_30 != 0x1fd) {
        local_38 = 0;
      }
      local_24 = 0;
      while (local_24 < 3) {
        free((void *)__ptr[local_24]);
        local_24 = local_24 + 1;
      }
      free(__ptr);
      return local_38;
    }
    iVar2 = atoi((char *)__ptr[local_2c]);
    local_30 = local_30 + iVar2;
    if (0 < local_2c) {
      iVar2 = atoi((char *)__ptr[(long)local_2c + -1]);
      iVar3 = atoi((char *)__ptr[local_2c]);
      if (iVar3 < iVar2) {
        local_38 = 0;
      }
    }
    local_28 = 0;
    while (local_28 < 3) {
      iVar2 = atoi((char *)__ptr[local_2c]);
      if (iVar2 < 100) {
        local_38 = 0;
        break;
      }
      iVar2 = atoi((char *)__ptr[local_2c]);
      uVar4 = func5(iVar2);
      local_38 = local_38 & uVar4;
      uVar1 = *(undefined *)(__ptr[local_2c] + 2);
      *(undefined *)(__ptr[local_2c] + 2) = *(undefined *)(__ptr[local_2c] + 1);
      *(undefined *)(__ptr[local_2c] + 1) = *(undefined *)__ptr[local_2c];
      *(undefined *)__ptr[local_2c] = uVar1;
      local_28 = local_28 + 1;
    }
    local_2c = local_2c + 1;
  } while( true );
}

func5は素数判定。3つの数値はすべて素数で、100以上。3つの数値の合計は0x1fd。3つの数値は昇順になっている。3桁の数値をシフトしても(137の場合、713, 371)、100以上、素数の条件を満たす。以上の条件を満たす数値を探す。

from Crypto.Util.number import *
import itertools

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

target = 0x1fd

ps = []
for p in range(100, 300):
    if isPrime(p):
        s = str(p)
        p1 = int(s[2] + s[:2])
        p2 = int(s[1:] + s[0])
        if p1 > 100 and p2 > 100 and isPrime(p1) and isPrime(p2):
            ps.append(p)

flag = 'DawgCTF{'
for p in itertools.combinations(ps, 3):
    if sum(p) == target:
        print ' '.join(map(str, p))
        for i in range(len(p)):
            flag += lines[p[i] - 1].rstrip()
            flag += '_'
        break

flag = flag[:-1] + '}'
print flag

実行結果は以下の通り。

113 197 199
DawgCTF{iloveme_123abc_batman}
DawgCTF{iloveme_123abc_batman}

It's Not RSA! (Crypto 100)

Enigma暗号。CyberChefで復号する。
f:id:satou-y:20210517205045p:plain

DAWGC TFSPI NNING ANDRO TATIN GROTO RS
DawgCTF{spinningandrotatingrotors}

Really Secure Algorithm (Crypto 150)

RSA暗号だが、eが非常に大きいので、Wiener attackで復号する。

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

def wiener(e, n):
    m = 12345
    c = pow(m, e, n)
    q0 = 1

    list1 = continued_fraction(Integer(e)/Integer(n))
    conv = list1.convergents()
    for i in conv:
        k = i.numerator()
        q1 = i.denominator()

        for r in range(30):
            for s in range(30):
                d = r*q1 + s*q0
                m1 = pow(c, d, n)
                if m1 == m:
                    return d
        q0 = q1
    return None

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

n = int(lines[0].strip().split(': ')[1])
e = int(lines[1].strip().split(': ')[1])
c = int(lines[2].strip().split(': ')[1])

d = wiener(e, n)
m = pow(c, d, n)

flag = long_to_bytes(m)
print flag
DawgCTF{sm@ll_d_b1g_dr3am5}

The Obligatory RSA Challenge (Crypto 200)

nをfactordbで素因数分解し、phiを計算する。

n = (21816257788879800226266741950501282709401872894176288619472731956291218914324742537604641219560786978413613510633536886641581153942571579359519401327796021367732695476711467566761398025402445133259848384123905962932802004021079944067083532491720877926448099933753336517689984808846750048960375488528766110009254176926887611598941876012437215971816681184483796662759984833863168641346915636162467824574775331116852844756225674938392321848711476249893809700776552828990831593983374320915711192051109410295925205263499219444742867868898381959251178728127024835656647566724333855154762699836449704050690295585931350731821)**2

p = 21816257788879800226266741950501282709401872894176288619472731956291218914324742537604641219560786978413613510633536886641581153942571579359519401327796021367732695476711467566761398025402445133259848384123905962932802004021079944067083532491720877926448099933753336517689984808846750048960375488528766110009254176926887611598941876012437215971816681184483796662759984833863168641346915636162467824574775331116852844756225674938392321848711476249893809700776552828990831593983374320915711192051109410295925205263499219444742867868898381959251178728127024835656647566724333855154762699836449704050690295585931350731821
phi = p * (p - 1)

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

from Crypto.Util.number import *

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

n = int(lines[0].strip().split(' = ')[1])
e = int(lines[1].strip().split(' = ')[1])
c = int(lines[2].strip().split(' = ')[1])

p = 21816257788879800226266741950501282709401872894176288619472731956291218914324742537604641219560786978413613510633536886641581153942571579359519401327796021367732695476711467566761398025402445133259848384123905962932802004021079944067083532491720877926448099933753336517689984808846750048960375488528766110009254176926887611598941876012437215971816681184483796662759984833863168641346915636162467824574775331116852844756225674938392321848711476249893809700776552828990831593983374320915711192051109410295925205263499219444742867868898381959251178728127024835656647566724333855154762699836449704050690295585931350731821
assert n == p**2

phi = p * (p - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m)
print flag
DawgCTF{wh0_n33ds_Q_@nyw@y}

TrashChain (Crypto 250)

$ nc umbccd.io 3100
Welcome to TrashChain!
In this challenge, you will enter two sequences of integers which are used to compute two hashes. If the two hashes match, you get the flag!
Restrictions:
  - Integers must be greater than 1.
  - Chain 2 must be at least 3 integers longer than chain 1
  - All integers in chain 1 must be less than the smallest element in chain 2
Type "done" when you are finished inputting numbers for each chain.

Provide inputs for chain 1.
> 2
> done

Provide inputs for chain 2.
> 3
> 4
> 5
> 6
> done
Hash for chain 1: e15e50ea527143a8da8ca4602316de76
Hash for chain 2: 103fe8e38a2cc887b8628c4ef649b81e
Sorry, hashes don't match

chainsに2つの数列を指定する。その際、chains[1]はchains[0]より要素数を3つ以上多く指定する。また、chains[0]の最大値はchains[1]の最小値より小さい。この条件で、ハッシュ値が同じものが指定できたら、フラグが表示される。

[a0, a1, a2]の場合のハッシュ
h0 = H(a0,  1, 1) =  1 * pow(a0 + 1, B, A) % A
h1 = H(a1, h0, 2) = h0 * pow(a1 + 2, B, A) % A 
h2 = H(a2, h1, 3) = h1 * pow(a2 + 3, B, A) % A
h = (pow(a0 + 1, B, A) * pow(a1 + 2, B, A) * pow(a2 + 3, B, A)) % A

pow(A-1, B, A) = A-1になることを使うと、以下の配列の場合、ハッシュが同じになるはず。

[(A-1)-1]
[(A*2-1)-1, (A*3-1)-2, (A*4-1)-3, (A*5-1)-4, (A*6-1)-5]
import socket

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

A = 340282366920938460843936948965011886881
B = 127605873542257115442148455720344860097

chains = [[], []]
chains[0].append((A-1)-1)
chains[1].append((A*2-1)-1)
chains[1].append((A*3-1)-2)
chains[1].append((A*4-1)-3)
chains[1].append((A*5-1)-4)
chains[1].append((A*6-1)-5)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('umbccd.io', 3100))

for i in range(len(chains[0])):
    data = recvuntil(s, '> ')
    print data + str(chains[0][i])
    s.sendall(str(chains[0][i]) + '\n')

data = recvuntil(s, '> ')
print data + 'done'
s.sendall('done\n')

for i in range(len(chains[1])):
    data = recvuntil(s, '> ')
    print data + str(chains[1][i])
    s.sendall(str(chains[1][i]) + '\n')

data = recvuntil(s, '> ')
print data + 'done'
s.sendall('done\n')

for _ in range(3):
    data = recvuntil(s, '\n').rstrip()
    print data

実行結果は以下の通り。

Welcome to TrashChain!
In this challenge, you will enter two sequences of integers which are used to compute two hashes. If the two hashes match, you get the flag!
Restrictions:
  - Integers must be greater than 1.
  - Chain 2 must be at least 3 integers longer than chain 1
  - All integers in chain 1 must be less than the smallest element in chain 2
Type "done" when you are finished inputting numbers for each chain.

Provide inputs for chain 1.
> 340282366920938460843936948965011886879
> done

Provide inputs for chain 2.
> 680564733841876921687873897930023773760
> 1020847100762815382531810846895035660640
> 1361129467683753843375747795860047547520
> 1701411834604692304219684744825059434400
> 2041694201525630765063621693790071321280
> done
Hash for chain 1: ffffffffffffff720000000000001320
Hash for chain 2: ffffffffffffff720000000000001320
Correct! Here's your flag: DawgCTF{We1rd_RSA_2nd_Pre1m4g3_th1ng}
DawgCTF{We1rd_RSA_2nd_Pre1m4g3_th1ng}

What the Flip?! (Crypto 300)

$ nc umbccd.io 3000
########################################################################
#                            Welcome                                   #
#             All connections are monitored and recorded               #
#      Disconnect IMMEDIATELY if you are not an authorized user!       #
########################################################################
username: a
a's password: b
Leaked ciphertext: 72a98b45a20ee804960da219b43a3ab1193747172a0f5d2dd988eee155a3bb00
enter ciphertext: 0123456789abcdef0123456789abcdef
Padding is incorrect.

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

・key: ランダム16バイト文字列
・iv: ランダム16バイト文字列
・user: 入力
・passwd: 入力
・msg = 'logged_username=<user>&password=<passwd>'
 msgに'admin&password=goBigDawgs123'が含まれるのはNG
・msgの暗号化を表示
・enc_msg: 入力(16進数)
・enc_msgを復号
 →チェックに合格していると、フラグが表示される。
 →paddingが妥当でない場合は、そのメッセージが表示される。

■暗号化
・PKCS7方式でパディング
・AES暗号化(16進数)

■復号
・16進数デコード後、AES復号
・unpadした後に、'admin&password=goBigDawgs123'が含まれていたら、1を返す。
 それ以外の場合は0を返す。

1文字だけ変え、前の暗号データを変更することによって、'admin&password=goBigDawgs123'が含まれるようにする。

0123456789abcdef
logged_username= p0 ^ iv --AES暗号--> c0 
admim&password=g p1 ^ c0 --AES暗号--> c1
oBigDawgs123PPPP p2 ^ c1 --AES暗号--> c2

p1_new = 'admin&password=g'とする。
p1 ^ c0 = p1_new ^ c0_new

これが成り立つようにすれば、最初のブロックの平文はくずれるが、'admin&password=goBigDawgs123'が含まれるようにできる。

import socket
from Crypto.Util.strxor import strxor

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('umbccd.io', 3000))

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

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

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

pt1 = user + '&password=g'
pt1_new = 'admin&password=g'
ct0 = ct[:16]
ct12 = ct[16:]

ct0_new = strxor(strxor(pt1, ct0), pt1_new)
ct_new = (ct0_new + ct12).encode('hex')
data = recvuntil(s, ': ')
print data + ct_new
s.sendall(ct_new + '\n')
data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '}')
print data

実行結果は以下の通り。

########################################################################
#                            Welcome                                   #
#             All connections are monitored and recorded               #
#      Disconnect IMMEDIATELY if you are not an authorized user!       #
########################################################################
username: admim
admim's password: goBigDawgs123
Leaked ciphertext: 72a98b45a20ee804960da219b43a3ab1e4ba207bb23ca5a4690179ae874c62c7f31070ad27e67701a69fedfebe4c2a63
enter ciphertext: 72a98b45a10ee804960da219b43a3ab1e4ba207bb23ca5a4690179ae874c62c7f31070ad27e67701a69fedfebe4c2a63
Logged in successfully!
Your flag is: DawgCTF{F1ip4J0y}
DawgCTF{F1ip4J0y}