# HSCTF 10 2023-Writeup [TOC] ## 最後排名 XD ![](https://hackmd.io/_uploads/HJM7aMgwn.png) ## Web ### th3-w3bsite hint: F12 就會看到 ### an-inaccessible-admin-panel hint: F12 source code 在 login.js 中 ```javascript= window.onload = function() { var loginForm = document.getElementById("loginForm"); loginForm.addEventListener("submit", function(event) { event.preventDefault(); var username = document.getElementById("username").value; var password = document.getElementById("password").value; function fii(num){ return num / 2 + fee(num); } function fee(num){ return foo(num * 5, square(num)); } function foo(x, y){ return x*x + y*y + 2*x*y; } function square(num){ return num * num; } var key = [32421672.5, 160022555, 197009354, 184036413, 165791431.5, 110250050, 203747134.5, 106007665.5, 114618486.5, 1401872, 20702532.5, 1401872, 37896374, 133402552.5, 197009354, 197009354, 148937670, 114618486.5, 1401872, 20702532.5, 160022555, 97891284.5, 184036413, 106007665.5, 128504948, 232440576.5, 4648358, 1401872, 58522542.5, 171714872, 190440057.5, 114618486.5, 197009354, 1401872, 55890618, 128504948, 114618486.5, 1401872, 26071270.5, 190440057.5, 197009354, 97891284.5, 101888885, 148937670, 133402552.5, 190440057.5, 128504948, 114618486.5, 110250050, 1401872, 44036535.5, 184036413, 110250050, 114618486.5, 184036413, 4648358, 1401872, 20702532.5, 160022555, 110250050, 1401872, 26071270.5, 210656255, 114618486.5, 184036413, 232440576.5, 197009354, 128504948, 133402552.5, 160022555, 123743427.5, 1401872, 21958629, 114618486.5, 106007665.5, 165791431.5, 154405530.5, 114618486.5, 190440057.5, 1401872, 23271009.5, 128504948, 97891284.5, 165791431.5, 190440057.5, 1572532.5, 1572532.5]; function validatePassword(password){ var encryption = password.split('').map(function(char) { return char.charCodeAt(0); }); var checker = []; for (var i = 0; i < encryption.length; i++) { var a = encryption[i]; var b = fii(a); checker.push(b); } console.log(checker); if (key.length !== checker.length) { return false; } for (var i = 0; i < key.length; i++) { if (key[i] !== checker[i]) { return false; } } return true; } if (username === "Admin" && validatePassword(password)) { alert("Login successful. Redirecting to admin panel..."); window.location.href = "admin_panel.html"; } else if (username === "default" && password === "password123") { var websiteNames = ["Google", "YouTube", "Minecraft", "Discord", "Twitter"]; var websiteURLs = ["https://www.google.com", "https://www.youtube.com", "https://www.minecraft.net", "https://www.discord.com", "https://www.twitter.com"]; var randomNum = Math.floor(Math.random() * websiteNames.length); alert("Login successful. Redirecting to " + websiteNames[randomNum] + "..."); window.location.href = websiteURLs[randomNum]; } else { alert("Invalid credentials. Please try again."); } }); }; ``` #### 解題思路 從上面的程式可以了解到,要真正的登入必須使用 ```Admin``` 的帳號,而密碼要經過 fii 開始往下的四個 function,最後跟放在 key 裡面的值做比對 所以直接將 ascii code (從32開始,因為之前是一些指令,沒記錯的話) 上的全部加密放進 map 容器裡(好用的東西,C++ 也有但是 Python 的功能跟這個不一樣),然後跟 key 做比對,從 ```//create``` 開始才是我真正寫的程式,阿原本的程式就順便借用一下 XD ```javascript= var key = [32421672.5, 160022555, 197009354, 184036413, 165791431.5, 110250050, 203747134.5, 106007665.5, 114618486.5, 1401872, 20702532.5, 1401872, 37896374, 133402552.5, 197009354, 197009354, 148937670, 114618486.5, 1401872, 20702532.5, 160022555, 97891284.5, 184036413, 106007665.5, 128504948, 232440576.5, 4648358, 1401872, 58522542.5, 171714872, 190440057.5, 114618486.5, 197009354, 1401872, 55890618, 128504948, 114618486.5, 1401872, 26071270.5, 190440057.5, 197009354, 97891284.5, 101888885, 148937670, 133402552.5, 190440057.5, 128504948, 114618486.5, 110250050, 1401872, 44036535.5, 184036413, 110250050, 114618486.5, 184036413, 4648358, 1401872, 20702532.5, 160022555, 110250050, 1401872, 26071270.5, 210656255, 114618486.5, 184036413, 232440576.5, 197009354, 128504948, 133402552.5, 160022555, 123743427.5, 1401872, 21958629, 114618486.5, 106007665.5, 165791431.5, 154405530.5, 114618486.5, 190440057.5, 1401872, 23271009.5, 128504948, 97891284.5, 165791431.5, 190440057.5, 1572532.5, 1572532.5]; function fii(num){ return num / 2 + fee(num); } function fee(num){ return foo(num * 5, square(num)); } function foo(x, y){ return x*x + y*y + 2*x*y; } function square(num){ return num * num; } function validatePassword(password){ var encryption = password.split('').map(function(char) { //console.log(char.charCodeAt(0)); return char.charCodeAt(0); }); var checker = []; for (var i = 0; i < encryption.length; i++) { var a = encryption[i]; var b = fii(a); checker.push(b); } console.log(checker); if (key.length !== checker.length) { return false; } //console.log(checker.length); //console.log(key.length); for (var i = 0; i < key.length; i++) { if (key[i] !== checker[i]) { console.log(i," this is different"); return false; } } return true; } // create let myMap = new Map(); for(var i=32;i<126;i++){ myMap.set(fii(i),String.fromCharCode(i)); } //console.log(myMap); var str = ""; for(var i=0;i<key.length;i++){ str += myMap.get(key[i]); } console.log(str); var password = str; validatePassword(password); ``` 登入後就告訴你 flag 的形式 ![](https://hackmd.io/_uploads/S11NHbi8n.png) ## rev ### back-to-basics hint : 很簡單 ![](https://hackmd.io/_uploads/SJX5d-o83.png) ### brain-hurt hint : 將 source code 中的 expected_flag 解出來就好 題目給的 python: ```python= import sys def validate_flag(flag): encoded_flag = encode_flag(flag) expected_flag = 'ZT_YE\\0|akaY.LaLx0,aQR{"C' if encoded_flag == expected_flag: return True else: return False def encode_flag(flag): encoded_flag = "" for c in flag: encoded_char = chr((ord(c) ^ 0xFF) % 95 + 32) encoded_flag += encoded_char return encoded_flag def main(): if len(sys.argv) != 2: print("Usage: python main.py <flag>") sys.exit(1) input_flag = sys.argv[1] if validate_flag(input_flag): print("Correct flag!") else: print("Incorrect flag.") if __name__ == "__main__": main() ``` #### 解題思路 因為 ```encoded_char``` 跟我說了使用的 ascii 範圍是 32 ~ 127 (95+32), 所以先將這個範圍內加密後的值存起來,最後跟 ```expected_flag``` 做比對 ```python= import string result = dict() context = 'ZT_YE\\0|akaY.LaLx0,aQR{"C' encoded_flag = "" decoded = "" answer = "" for c in range(32, 127): #decoded = chr(c) #encoded_char = chr((c ^ 0xFF) % 95 + 32) #print(encoded_char) result[chr((c ^ 0xFF) % 95 + 32)] = chr(c) #print(decoded) #print(result) for i in context: answer += result[i] print(answer) ``` python 真的不熟啦,dictionary 弄了一陣子 ### mystery-methods hint: 看得懂 code XD 題目給的 java code ```java= import java.util.Base64; import java.util.Scanner; public class mysteryMethods{ public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Flag: "); String userInput = scanner.nextLine(); String encryptedInput = encryptInput(userInput); if (checkFlag(encryptedInput)) { System.out.println("Correct flag! Congratulations!"); } else { System.out.println("Incorrect flag! Please try again."); } } public static String encryptInput(String input) { String flag = input; flag = unknown2(flag, 345345345); flag = unknown1(flag); flag = unknown2(flag, 00000); flag = unknown(flag, 25); return flag; } public static boolean checkFlag(String encryptedInput) { return encryptedInput.equals("OS1QYj9VaEolaDgTSTXxSWj5Uj5JNVwRUT4vX290L1ondF1z"); } public static String unknown(String input, int something) { StringBuilder result = new StringBuilder(); for (char c : input.toCharArray()) { if (Character.isLetter(c)) { char base = Character.isUpperCase(c) ? 'A' : 'a'; int offset = (c - base + something) % 26; if (offset < 0) { offset += 26; } c = (char) (base + offset); } result.append(c); } return result.toString(); } public static String unknown1(String xyz) { return new StringBuilder(xyz).reverse().toString(); } public static String unknown2(String xyz, int integer) { return Base64.getEncoder().encodeToString(xyz.getBytes()); } } ``` 簡單來說,輸入會經過 base64、reverse、base64、rotation -1/27(或者說 ceaser -1/27) 然後跟 ```OS1QYj9VaEolaDgTSTXxSWj5Uj5JNVwRUT4vX290L1ondF1z``` 這一字串比對是否一樣 所以解密就是順序反過來,rotation 1、base64、rev、base64 ![](https://hackmd.io/_uploads/HkufhW3Lh.png) ![](https://hackmd.io/_uploads/Skq7hbnUh.png) ### keygen hint: 反組譯 #### 解題思路 丟到 IDA 後 ```c= int __cdecl main(int argc, const char **argv, const char **envp) { const char *v4; // [rsp+10h] [rbp-10h] const char *v5; // [rsp+18h] [rbp-8h] if ( argc == 2 && strlen(argv[1]) == 42 ) { puts("dfdfdf"); v5 = argv[1]; v4 = "lfkmq<8=?=>?l'==<2'<;=>'?l<i'<l<9<h9l::::w"; while ( *v5 ) { if ( (*v5 ^ 0xA) != *v4 ) goto LABEL_6; ++v5; ++v4; } puts("Correct"); return 0; } else { LABEL_6: puts("Wrong"); return 1; } } ``` 所以將 v4 做 ^ 0xA ```python= v4 = "lfkmq<8=?=>?l'==<2'<;=>'?l<i'<l<9<h9l::::w" str = "" for i in v4: str += chr(ord(i) ^ 0xA) print(str) ``` ### revrevrev hint: 看懂code #### 解題思路 題目給的程式 ```python= ins = "" while len(ins) != 20: ins = input("input a string of size 20: ") s = 0 a = 0 x = 0 y = 0 for c in ins: if c == 'r': # rev s += 1 elif c == 'L': # left a = (a + 1) % 4 elif c == 'R': # right a = (a + 3) % 4 else: print("this character is not necessary for the solution.") if a == 0: x += s elif a == 1: y += s elif a == 2: x -= s elif a == 3: y -= s print((x, y)) if x == 168 and y == 32: print("flag{" + ins + "}") else: print("incorrect sadly") ``` 因為 ```x == 168``` 和 ```y == 32``` ,所以 a 的值在過程中一定不會為 2 或 3,所以 ```LR``` 一定一起出現,s 只會增加不會減少,從 1 加到 18 為 175,所以 ```r``` 最多不會超過 18 個,那麼 ```LR``` 最多出現兩組,所以就是 ```'r'*16 + 'LR'*2``` ## cry ### double-trouble hint: Caesar ### really-small-algorithm 安裝了 RsaCtfTool 後,讓他跑一下 flag 直接就出來啦 ![](https://hackmd.io/_uploads/rJ-qIGo82.png) ### cupcakes hint: 丟上去 Vigenere 就出來了 ## Misc ### discord hint: 去 discord 的 Misc 找 ### intro-to-netcat hint: 跟著提示使用 nc 就會給 flag 了 ## Algo ### banana-queries 以下是題目: Banana Queries A monkey named Baljeet really likes bananas. He has a calendar that tells him how many bananas he can get each day. Baljeet doesn’t want to go collect bananas everyday because he is lazy but he also wants the maximum amount of bananas. He wants the biggest subarray of days where the sum of the bananas he collects is divisible by the length of the subsequence of days. Input: The first line contains N (1 <= N <= 1000) , the size of array a. The next line contains N integers (1 <= ai <= 1000), separated by spaces, where ai is the amount of bananas collected on day i. Output: Output the size of the biggest subarray of days in which the sum of its bananas is divisible by the number of days in the subarray. Sample Input/Output Input: 3 1 2 0 Output: 3 When looking at the set of numbers we can make different sub arrays. For example [1], [2,0], and [1,2,0], all of these subarrays are valid as their sums are divisible by their lengths. However when looking at the biggest subarray it has a length of 3, so we return 3. *HINT:Remember to not try to query for the sum of a sub array everytime you want to figure out if a subarray is valid as it will time out. Time complexity: N^2 #### 解題思路 因為 sum of bananas 要能被子集合的長度整除且時間複雜度為 n 平方,所以要找到最大的子集合。 dp 用來存第二行的所有 input,i 是當前的位置而 j 是 0 到 i-1。sum 用來計算 從 j 到 i 的子集合的總和 ```c++= #include <iostream> #include <vector> using namespace std; int max(int a, int b){ if (a > b) return a; else return b; } int main() { //input int N; cin >> N; vector<int> bananas(N); for (int i = 0; i < N; i++) { cin >> bananas[i]; } vector<int> dp(N, 0); dp[0] = 1; // 初始值,至少有一個元素 //divide int maxLength = 1; for (int i = 1; i < N; i++) { int sum = bananas[i]; int length = 1; for (int j = i - 1; j >= 0; j--) { sum += bananas[j]; if (sum % length == 0) { dp[i] = max(dp[i], dp[j] + 1); maxLength = max(maxLength, dp[i]); } length++; } } cout << maxLength << endl; return 0; } ``` ## 後記 等其他人的 writeup QQ 題目比較多程式相關的東西(因為解的幾乎是 reverse XD) ![](https://hackmd.io/_uploads/BJ-fJXev2.png) ### web 別人的 writeup -> https://github.com/Ramalingasamy012/HSCTF2023-writeups #### mogodb ```admin' || '``` 這樣就可以登入了,SQL injection... 只能說學藝不精,哭了 #### very-secure 原來要將 cookie 的內容 reverse 在用 admin 去編譯......學廢了學廢了 ### pwn #### ed https://ctftime.org/writeup/37157 結果卡 64,跟輸入 Q 來 return 0,導致卡在輸入那邊 orz #### doubler https://ctftime.org/writeup/37156 integer overflow 然後去抓溢出的值是多少