以下の内容はhttps://yocchin.hatenablog.com/entry/2025/02/04/071203より取得しました。


nullcon Goa HackIM CTF 2025 Writeup

この大会は2025/2/1 17:30(JST)~2025/2/2 17:30(JST)に開催されました。
今回もチームで参戦。結果は601点で1115チーム中137位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome to Nullcon HackIM CTF (sanity check)

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

ENO{WELCOME_TO_NULLCON_HACKIM_CTF}

Profound thought (misc)

$ zsteg l5b245c11.png                           
imagedata           .. text: "286-0.\t\n\t"
b1,r,lsb,xy         .. text: "rzsZA>FCNR^_]\""
b1,r,msb,xy         .. file: OpenPGP Public Key
b1,g,msb,xy         .. file: OpenPGP Secret Key
b1,rgb,lsb,xy       .. text: "ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57"                                                                                                                                       
b2,r,msb,xy         .. file: OpenPGP Secret Key
b2,bgr,msb,xy       .. file: OpenPGP Public Key
b3,r,msb,xy         .. file: RLE image data, 16888 x 4242, lower left corner: 8321, lower right corner: 65025, clear first, 16 color channels, 224 bits per pixel, 183 color map channels
b4,r,lsb,xy         .. text: "wYtE$ER\#$DUEFuREEf2E%5"
b4,g,lsb,xy         .. text: "#\"5DQ232UT2"
ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy}

USBnet (misc)

$ binwalk usbnet.pcapng     

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
29406         0x72DE          PNG image, 111 x 111, 1-bit colormap, non-interlaced

$ foremost usbnet.pcapng
Processing: usbnet.pcapng
|*|

PNGを抽出でき、画像にはQRコードが書かれていた。

コードリーダで読み取ると、フラグが取得できた。

ENO{USB_ETHERNET_ADAPTER_ARE_COOL_N!C3}

flag checker (rev)

Ghidraでデコンパイルする。

undefined8 FUN_00101318(void)

{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  char local_38 [40];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Enter the flag: ");
  fgets(local_38,0x23,stdin);
  sVar2 = strcspn(local_38,"\n");
  local_38[sVar2] = '\0';
  iVar1 = FUN_0010127a(local_38);
  if (iVar1 == 0) {
    puts("Incorrect!");
  }
  else {
    puts("Correct!");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

undefined8 FUN_0010127a(char *param_1)

{
  size_t sVar1;
  undefined8 uVar2;
  long in_FS_OFFSET;
  int local_3c;
  char local_38 [40];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  sVar1 = strlen(param_1);
  if (sVar1 == 0x22) {
    FUN_001011e9(param_1,local_38);
    for (local_3c = 0; local_3c < 0x22; local_3c = local_3c + 1) {
      if (local_38[local_3c] != (&DAT_00102020)[local_3c]) {
        uVar2 = 0;
        goto LAB_00101302;
      }
    }
    uVar2 = 1;
  }
  else {
    uVar2 = 0;
  }
LAB_00101302:
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return uVar2;
}

void FUN_001011e9(long param_1,long param_2)

{
  int local_c;
  
  for (local_c = 0; local_c < 0x22; local_c = local_c + 1) {
    *(byte *)(param_2 + local_c) = (*(byte *)(param_1 + local_c) ^ 0x5a) + (char)local_c;
    *(byte *)(param_2 + local_c) =
         *(char *)(param_2 + local_c) << 3 | *(byte *)(param_2 + local_c) >> 5;
  }
  return;
}

                             DAT_00102020                                    XREF[2]:     FUN_0010127a:001012dd(*), 
                                                                                          FUN_0010127a:001012e4(*)  
        00102020 f8              ??         F8h
        00102021 a8              ??         A8h
        00102022 b8              ??         B8h
        00102023 21              ??         21h    !
        00102024 60              ??         60h    `
        00102025 73              ??         73h    s
        00102026 90              ??         90h
        00102027 83              ??         83h
        00102028 80              ??         80h
        00102029 c3              ??         C3h
        0010202a 9b              ??         9Bh
        0010202b 80              ??         80h
        0010202c ab              ??         ABh
        0010202d 09              ??         09h
        0010202e 59              ??         59h    Y
        0010202f d3              ??         D3h
        00102030 21              ??         21h    !
        00102031 d3              ??         D3h
        00102032 db              ??         DBh
        00102033 d8              ??         D8h
        00102034 fb              ??         FBh
        00102035 49              ??         49h    I
        00102036 99              ??         99h
        00102037 e0              ??         E0h
        00102038 79              ??         79h    y
        00102039 3c              ??         3Ch    <
        0010203a 4c              ??         4Ch    L
        0010203b 49              ??         49h    I
        0010203c 2c              ??         2Ch    ,
        0010203d 29              ??         29h    )
        0010203e cc              ??         CCh
        0010203f d4              ??         D4h
        00102040 dc              ??         DCh

                             s_Enter_the_flag:_00102042                      XREF[0,2]:   FUN_00101318:00101333(*), 
                                                                                          FUN_00101318:0010133a(*)  
        00102041 42 45 6e        ds         "BEnter the flag: "
                 74 65 72 
                 20 74 68 

入力文字列の長さが34バイトであることをチェックしている。また、入力文字列の各文字を0x5aとXORしてインデックスをプラスした後、左シフト3、右シフト5したものについて、DAT_00102020以降のデータと一致しているかをチェックしている。このことから逆算して、正しい入力文字列を割り出す。

#!/usr/bin/env python3
enc = [0xf8, 0xa8, 0xb8, 0x21, 0x60, 0x73, 0x90, 0x83, 0x80, 0xc3, 0x9b, 0x80,
    0xab, 0x09, 0x59, 0xd3, 0x21, 0xd3, 0xdb, 0xd8, 0xfb, 0x49, 0x99, 0xe0,
    0x79, 0x3c, 0x4c, 0x49, 0x2c, 0x29, 0xcc, 0xd4, 0xdc, 0x42]

flag = ''
for i, c in enumerate(enc):
    code = ((((c >> 3) | (c << 5)) & 0xff) - i) ^ 0x5a
    flag += chr(code)
print(flag)
ENO{R3V3R53_3NG1N33R1NG_M45T3R!!!}

scrambled (rev)

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

・flag: 不明
・key: 不明
・scrambled_result, _ = encode_flag(flag, key)
 ・xor_result: flagの各文字のASCIIコードをkeyとXORした配列
 ・chunk_size = 4
 ・chunks: xor_resultを4つごとに配列の配列にしたもの
 ・seed: 0以上10以下のランダム整数
 ・seedを乱数のシード設定
 ・chunksをシャッフル
 ・scrambled_result: chunksの各chunk、そのchunkの各itemの配列
 ・scrambled_result, chunksを返却
・scrambled_resultの各値iを16進数文字列にし結合したものを出力

seed全パターンで元に戻していき、フラグが"E"から始まることを前提にkeyを割り出し、復号する。

#!/usr/bin/env python3
import random

result = '1e78197567121966196e757e1f69781e1e1f7e736d6d1f75196e75191b646e196f6465510b0b0b57'
scrambled_result = [int(result[i:i+2], 16) for i in range(0, len(result), 2)]
chunks = [scrambled_result[i:i+4] for i in range(0, len(scrambled_result), 4)]

l = len(chunks)

for seed in range(0, 11):
    smpl = list(range(l))
    random.seed(seed)
    random.shuffle(smpl)

    xor_result = []
    for i in range(l):
        index = smpl.index(i)
        xor_result += chunks[index]

    key = xor_result[0] ^ ord('E')
    flag = ''
    for c in xor_result:
        flag += chr(c ^ key)
    if flag.startswith('ENO{'):
        print(flag)
        break
ENO{5CR4M83L3D_3GG5_4R3_1ND33D_T45TY!!!}

Numberizer (web)

http://52.59.124.14:5004/?sourceを見ると、ソースコードが見える。

<?php
ini_set("error_reporting", 0);

if(isset($_GET['source'])) {
    highlight_file(__FILE__);
}

include "flag.php";

$MAX_NUMS = 5;

if(isset($_POST['numbers']) && is_array($_POST['numbers'])) {

    $numbers = array();
    $sum = 0;
    for($i = 0; $i < $MAX_NUMS; $i++) {
        if(!isset($_POST['numbers'][$i]) || strlen($_POST['numbers'][$i])>4 || !is_numeric($_POST['numbers'][$i])) {
            continue;
        }
        $the_number = intval($_POST['numbers'][$i]);
        if($the_number < 0) {
            continue;
        }
        $numbers[] = $the_number;
    }
    $sum = intval(array_sum($numbers));


    if($sum < 0) {
        echo "You win a flag: $FLAG";
    } else {
        echo "You win nothing with number $sum ! :-(";
    }
}
?>

<html>
    <head>
        <title>Numberizer</title>
    </head>
    <body>
        <h1>Numberizer</h1>
        <form action="/" method="post">
            <label for="numbers">Give me at most 10 numbers to sum!</label><br>
            <?php
            for($i = 0; $i < $MAX_NUMS; $i++) {
                echo '<input type="text" name="numbers[]"><br>';
            }
            ?>
            <button type="submit">Submit</button>
        </form>
        <p>To view the source code, <a href="/?source">click here.</a>
    </body>
</html>

入力できる文字列の長さは4文字までで、全て足すと負になれば、フラグが表示される。eを使って大きい数値にして、整数オーバーフローで負になるようにする。5つの欄に1e20を入力し、Submitすると、フラグが表示された。

ENO{INTVAL_IS_NOT_ALW4S_P0S1TiV3!}

Paginator (web)

http://52.59.124.14:5012/?sourceを見ると、ソースコードが見える。

<?php
ini_set("error_reporting", 0);
ini_set("display_errors",0);

if(isset($_GET['source'])) {
    highlight_file(__FILE__);
}

include "flag.php";

$db = new SQLite3('/tmp/db.db');
try {
  $db->exec("CREATE TABLE pages (id INTEGER PRIMARY KEY, title TEXT UNIQUE, content TEXT)");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Flag', '" . base64_encode($FLAG) . "')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 1', 'This is not a flag, but just a boring page.')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 2', 'This is not a flag, but just a boring page.')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 3', 'This is not a flag, but just a boring page.')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 4', 'This is not a flag, but just a boring page.')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 5', 'This is not a flag, but just a boring page.')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 6', 'This is not a flag, but just a boring page.')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 7', 'This is not a flag, but just a boring page.')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 8', 'This is not a flag, but just a boring page.')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 9', 'This is not a flag, but just a boring page.')");
  $db->exec("INSERT INTO pages (title, content) VALUES ('Page 10', 'This is not a flag, but just a boring page.')");
} catch(Exception $e) {
  //var_dump($e);
}


if(isset($_GET['p']) && str_contains($_GET['p'], ",")) {
  [$min, $max] = explode(",",$_GET['p']);
  if(intval($min) <= 1 ) {
    die("This post is not accessible...");
  }
  try {
    $q = "SELECT * FROM pages WHERE id >= $min AND id <= $max";
    $result = $db->query($q);
    while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
      echo $row['title'] . " (ID=". $row['id'] . ") has content: \"" . $row['content'] . "\"<br>";
    }
  }catch(Exception $e) {
    echo "Try harder!";
  }
} else {
    echo "Try harder!";
}
?>

<html>
    <head>
        <title>Paginator</title>
    </head>
    <body>
        <h1>Paginator</h1>
        <a href="/?p=2,10">Show me pages 2-10</a>
        <p>To view the source code, <a href="/?source">click here.</a>
    </body>
</html>
Try harder!
Paginator
Show me pages 2-10
To view the source code, click here.

以下のURLにアクセスすることによって、SQLインジェクションでIDが1のデータも表示させる。

http://52.59.124.14:5012/?p=2,2%20or%20ID=1

この結果、以下が表示された。

Page 1 (ID=2) has content: "This is not a flag, but just a boring page."
Flag (ID=1) has content: "RU5Pe1NRTDFfVzF0aF8wdVRfQzBtbTRfVzBya3NfU29tZUhvdyF9"
$ echo RU5Pe1NRTDFfVzF0aF8wdVRfQzBtbTRfVzBya3NfU29tZUhvdyF9 | base64 -d
ENO{SQL1_W1th_0uT_C0mm4_W0rks_SomeHow!}
ENO{SQL1_W1th_0uT_C0mm4_W0rks_SomeHow!}

Crahp (web)

http://52.59.124.14:5006/?sourceを見ると、ソースコードが見える。

<?php
ini_set("error_reporting", 0);
ini_set("display_errors",0);

if(isset($_GET['source'])) {
    highlight_file(__FILE__);
}


// https://www.php.net/manual/en/function.crc32.php#28012
function crc16($string) {
  $crc = 0xFFFF;
  for ($x = 0; $x < strlen ($string); $x++) {
    $crc = $crc ^ ord($string[$x]);
    for ($y = 0; $y < 8; $y++) {
      if (($crc & 0x0001) == 0x0001) {
        $crc = (($crc >> 1) ^ 0xA001);
      } else { $crc = $crc >> 1; }
    }
  }
  return $crc;
}


// https://stackoverflow.com/questions/507041/crc8-check-in-php/73305496#73305496
function crc8($input)
{
$crc8Table = [
    0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
    0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
    0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
    0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
    0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
    0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
    0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
    0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
    0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
    0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
    0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
    0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
    0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
    0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
    0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
    0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
    0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
    0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
    0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
    0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
    0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
    0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
    0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
    0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
    0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
    0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
    0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
    0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
    0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
    0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
    0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
];

    $byteArray = unpack('C*', $input);
    $len = count($byteArray);
    $crc = 0;
    for ($i = 1; $i <= $len; $i++) {
        $crc = $crc8Table[($crc ^ $byteArray[$i]) & 0xff];
    }
    return $crc & 0xff;
}

$MYPASSWORD = "AdM1nP@assW0rd!";
include "flag.php";

if(isset($_POST['password']) && strlen($MYPASSWORD) == strlen($_POST['password'])) {
    $pwhash1 = crc16($MYPASSWORD);
    $pwhash2 = crc8($MYPASSWORD);

    $password = $_POST['password'];
    $pwhash3 = crc16($password);
    $pwhash4 = crc8($password);

    if($MYPASSWORD == $password) {
        die("oops. Try harder!");
    }
    if($pwhash1 != $pwhash3) {
        die("Oops. Nope. Try harder!");
    }
    if($pwhash2 != $pwhash4) {
        die("OoOps. Not quite. Try harder!");
    }
    $access = true;
 
    if($access) {
        echo "You win a flag: $FLAG";
    } else {
        echo "Denied! :-(";
    }
} else {
    echo "Try harder!";
}
?>

<html>
    <head>
        <title>Craphp</title>
    </head>
    <body>
        <h1>Craphp</h1>
        <form action="/" method="post">
            <label for="password">Give me your password!</label><br>
            <input type="text" name="password"><br>
            <button type="submit">Submit</button>
        </form>
        <p>To view the source code, <a href="/?source">click here.</a>
    </body>
</html>
Try harder!
Craphp
Give me your password!

Submit
To view the source code, click here.

パスワード AdM1nP@assW0rd! 以外で、crc16とcrc8が同じになる長さが15の文字列を指定したら、フラグが表示される。ブルートフォースでこの条件を満たすものを探す。

#!/usr/bin/env python3
from fastcrc import crc8, crc16

MYPASSWORD = 'AdM1nP@assW0rd!'

pwhash1 = crc16.modbus(MYPASSWORD.encode())
pwhash2 = crc8.smbus(MYPASSWORD.encode())

for i in range(100000000000000, 1000000000000000):
    password = str(i)
    pwhash3 = crc16.modbus(password.encode())
    pwhash4 = crc8.smbus(password.encode())
    if pwhash1 == pwhash3 and pwhash2 == pwhash4:
        print(password)
        break

実行結果は以下の通り。

100000010130312

これを入力し、Submitしたらフラグが表示された。

ENO{Cr4hP_CRC_Collison_1N_P@ssw0rds!}

next-level (cry)

$ nc 52.59.124.14 5028          
779393179953408360283921776467389063540381973116844996143181786415649061981439880634265583530617439728965912321347568557334799149928093794896260028641186694167670960006416758814267178270348996878845065267688230273017609662332689636123075349696089124857394851089291578322331951457296501510938996282018057642756589073897235801436973217505838973766053825373712636581840684173954524392090865738767882555054348995736737152486856198985912037004297821420312844957433237
37701128638831197067150579201084785784877978471109714515477494002029370974810761336854622733313101379267641749371792395393766001510947322549108390575591866958403778775862913179829433737873860341692714812409441469469547610225935406294640957771712022236578262275578769814979649635804075921269137169843889087547625865450741692195921436589378387348791459060006217812482485083782635737388447907665434328728446907566914974254245446064110597224752752569407022924825028

nはp, q, rの積がpが決まるとq, rも決まるので、pを一定の値から, q, rを算出し、nに近づけていけば、nを素因数分解できる。あとは通常通り復号する。

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

n = 779393179953408360283921776467389063540381973116844996143181786415649061981439880634265583530617439728965912321347568557334799149928093794896260028641186694167670960006416758814267178270348996878845065267688230273017609662332689636123075349696089124857394851089291578322331951457296501510938996282018057642756589073897235801436973217505838973766053825373712636581840684173954524392090865738767882555054348995736737152486856198985912037004297821420312844957433237
c = 37701128638831197067150579201084785784877978471109714515477494002029370974810761336854622733313101379267641749371792395393766001510947322549108390575591866958403778775862913179829433737873860341692714812409441469469547610225935406294640957771712022236578262275578769814979649635804075921269137169843889087547625865450741692195921436589378387348791459060006217812482485083782635737388447907665434328728446907566914974254245446064110597224752752569407022924825028

p = int(gmpy2.iroot(n, 3)[0]) - 1000

while True:
    p = sympy.nextprime(p)
    q = sympy.nextprime(p)
    r = sympy.nextprime(q)
    if p * q * r == n:
        break

e = 0x10001
phi = (p - 1) * (q - 1) * (r - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
ENO{1_l0ve_7h3_pr1me_numb3r_the0r3m}

many caesars (cry)

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

・text: text.txtの内容を英小文字にしたもの
・flag: フラグから"ENO{"と"}"を除き、"_"を"+"に置換したもの
・chars: 英字、数字、"+/="を連結したもの
・regex: charsを5文字以上70文字以下という正規表現
・flagはregexの正規表現に一致することをチェック
・i = 0
・count = 0
・iがtextの長さより小さい間、以下繰り返し
 ・text[i]が英小文字以外場合
  ・text[i](改行なし)を出力
  ・i += 1
 ・text[i]が英小文字の場合
  ・j = i
  ・text[j]が英小文字の間、j += 1
  ・caesar(text[i:j], chars.index(flag[count % len(flag)]))(改行なし)を出力
  ・count += 1
  ・i = j

スペース区切りで考え、可能性のある単語をすべて洗い出す。

#!/usr/bin/env python3
import string

chars = string.ascii_letters + string.digits + '+/='
l = len(chars)

def rev_caesar(msg, shift):
    return ''.join(chars[(chars.index(c) - shift) % len(chars)] if c in chars else c for c in msg)

with open('output.txt', 'r') as f:
    enc = f.read().rstrip().split(' ')

for i, c in enumerate(enc):
    for j in range(l):
        text = rev_caesar(c, j)
        if text.islower():
            print('%02d: %s (shift=%d) -> %s' % (i, text, j, chars[j]))

実行結果から関係する部分のみ取り出す。

00: hacker (shift=19) -> t
01: ethics (shift=7) -> h
02: is (shift=55) -> 3
03: a (shift=62) -> +
04: set (shift=3) -> d
05: of (shift=53) -> 1
06: principles (shift=5) -> f
07: that (shift=5) -> f
08: guide (shift=4) -> e
09: the (shift=17) -> r
10: behavior (shift=55) -> 3
11: of (shift=13) -> n
12: individuals (shift=2) -> c
13: who (shift=4) -> e
14: explore (shift=57) -> 5
15: and (shift=62) -> +
16: manipulate (shift=12) -> m
17: computer (shift=56) -> 4
18: systems, (shift=10) -> k
19: often (shift=4) -> e
20: emphasizing (shift=62) -> +
21: curiosity, (shift=56) -> 4
22: creativity, (shift=11) -> l
23: and (shift=11) -> l
24: the (shift=62) -> +
25: pursuit (shift=19) -> t
26: of (shift=7) -> h
27: knowledge. (shift=55) -> 3
28: rooted (shift=62) -> +
29: in (shift=3) -> d
30: values (shift=8) -> i
31: such (shift=5) -> f
32: as (shift=5) -> f
33: openness, (shift=55) -> 3
34: free (shift=17) -> r
35: access (shift=4) -> e
36: to (shift=13) -> n
37: information, (shift=2) -> c
38: and (shift=55) -> 3
39: the (shift=19) -> t
40: belief (shift=7) -> h
41: in (shift=55) -> 3
42: using (shift=62) -> +
43: skills (shift=3) -> d
44: to (shift=53) -> 1
45: improve (shift=5) -> f
46: systems (shift=5) -> f
47: rather (shift=4) -> e
48: than (shift=17) -> r
49: harm (shift=55) -> 3
        :

繰り返しになってきたので、文字を並べ、"+"を"_"に置換し、フラグの形式にすればよい。

ENO{th3_d1ffer3nce5_m4ke_4ll_th3_diff3renc3}



以上の内容はhttps://yocchin.hatenablog.com/entry/2025/02/04/071203より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14