### super guesser

pyc, so the logical approach is ofc to run pycdc on it

eyo actually worked flawlessly coz its python 3.8 apparently

reading the src tells us that we need to give it 4 chunks of the flag each of 5 chars long and matches the partial hash given

i dont think hashcat nor john the ripper accepts partial hashes out of the box, and i dont think we need that powerful of a bruteforcer to test anyway so i just coded one in python

turns out pwnlib has mbruteforce thats pretty fast lol

flage `cvctf{hashisnotguessy}`
```py
import hashlib
import re

import pwnlib.util.iters as iters
import string

hashes = [
    'd.0.....f5...5.6.7.1.30.6c.d9..0',
    '1b.8.1.c........09.30.....64aa9.',
    'c.d.1.53..66.4.43bd.......59...8',
    '.d.d.076........eae.3.6.85.a2...']


for i in range(len(hashes)):
    match = re.compile('^' + hashes[i].replace('.', '[0-9a-f]') + '$')
    print (iters.mbruteforce(lambda t:match.match(hashlib.md5(t.encode()).hexdigest()), string.ascii_lowercase, 6, 'upto'))
```


### warmup 4

[@Ray](https://maplebacon.org/authors/Ray/) was originally working on it and i picked up trying to see if theres a pattern in the different width unicode chars they used to encode things

but then [@kever](https://maplebacon.org/authors/vEvergarden/) was like watch me solve it in 5 mins

AND HE ACTUALLY GUESSED WHAT THE STEGO ALGO IS RIGHT FROM THE HINT LOL 

guess what tho i sniped him by decoding using the site faster than he can :sunglasses:

logic is https://github.com/holloway/steg-of-the-dump/blob/master/steg-of-the-dump.js

`cvctf{secretsaretobeh1dd3n}`

### french

watching [@kever](https://maplebacon.org/authors/vEvergarden/) going insane speedrunning the challs in like sub 5 mins made me wanna try too but im just too slow :turtle:

anyway its clear from the decompilation that it decrypts the flag using rc4 and then checks char by char if it matches or not

its obviously instruction countable but dude the flag is literally in memory alr so just grab it after breakpointing

`cvctf{rC4<->3nC0d3d>-<}`

### baby cuda

this was kinda fun ngl i love blackboxing challs and not doing it the intended way

judging from name it was probably supposed to be a shader chall and i can see communication to and from the gpu too

but man im not about to learn how to reverse shaders

so i looked at the checking part directly

and i realized they are checking chunks of 4 characters at once for 4 times which means the flag is 16 bytes

so i started looking at the transformation to the input after the cuda operations and seeing if theres any patterns i can see

good ol 'aaaaaaabaaacaaad' tells me that there does seem to be a pattern in that they are always of the same distance from each other as long as we change only a character for each chunk even though the 4 bytes are intertwined which means we cant char by char bruteforce / instruction count feasibly

eventually i mapped out the differences between each char:
```text
aaaaaaabaaac

00 90 17 45 00 E0 35 45  00 90 78 45 00 60 66 45  ...E.à5E..xE.`fE
00 50 18 45 00 B0 36 45  00 C0 79 45 00 50 67 45  .P.E.°6E.ÀyE.PgE
00 10 19 45 00 80 37 45  00 F0 7A 45 00 40 68 45  ...E.€7E.ðzE.@hE

little endian, (0xC0, 0xD0, 0x130, 0xF0) on middle 2 bytes


aaaaaabaaacaaada

00 90 17 45 00 E0 35 45  00 90 78 45 00 60 66 45
00 10 18 45 00 70 36 45  00 40 79 45 00 10 67 45
00 90 18 45 00 00 37 45  00 F0 79 45 00 C0 67 45
00 10 19 45 00 90 37 45  00 A0 7A 45 00 70 68 45

little endian, (0x80, 0x90, 0xB0, 0xB0) on middle 2 bytes


aaaaabaaacaaadaa

00 90 17 45 00 E0 35 45  00 90 78 45 00 60 66 45
00 D0 17 45 00 50 36 45  00 20 79 45 00 D0 66 45
00 10 18 45 00 C0 36 45  00 B0 79 45 00 40 67 45
00 50 18 45 00 30 37 45  00 40 7A 45 00 B0 67 45

little endian, (0x40, 0x70, 0x90, 0x70) on middle 2 bytes


aaaabaaacaaadaaa

00 90 17 45 00 E0 35 45  00 90 78 45 00 60 66 45
00 A0 17 45 00 F0 35 45  00 B0 78 45 00 B0 66 45
00 B0 17 45 00 00 36 45  00 D0 78 45 00 00 67 45
00 C0 17 45 00 10 36 45  00 F0 78 45 00 50 67 45

little endian, (0x10, 0x10, 0x20, 0x50) on middle 2 bytes
```

which also compounds up which is a very good sign coz it means we can just do multiplication on it
```text
aaaaaabb

00 90 17 45 00 E0 35 45  00 90 78 45 00 60 66 45
00 D0 18 45 00 40 37 45  00 70 7A 45 00 00 68 45

+0x140  (0xC0+0x80)
+0x160  (0xD0+0x90)
+0x1E0  (0x130+0xB0)
+0x1A0  (0xF0+0xB0)
```

now that i got a pretty consistent result so i started looking at the expected result it compares in memory

they convert the float into an int to compare to expected result so i tried converting the expected result to float since i found a pattern in the float representation in memory
```c
#include <stdio.h>
#include <xmmintrin.h>

int main()
{
    char d[] = {0xC3, 0x0A, 0x00, 0x00, 0xFC, 0x0C, 0x00, 0x00, 0xC9, 0x11, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00
    ,0xE6, 0x09, 0x00, 0x00, 0x0F, 0x0C, 0x00, 0x00, 0xAF, 0x10, 0x00, 0x00, 0x17, 0x0F, 0x00, 0x00
    ,0x24, 0x07, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0x57, 0x0B, 0x00, 0x00, 0xB3, 0x0A, 0x00, 0x00
    ,0x84, 0x09, 0x00, 0x00, 0x0E, 0x0B, 0x00, 0x00, 0x56, 0x0F, 0x00, 0x00, 0xA2, 0x0D, 0x00, 0x00};
    
    int* data = (int*)d;
    
    __m128 buf;
    for(int i = 0; i < 16; i++) {
        int in = data[i];
        __m128 out = __builtin_ia32_cvtsi2ss(buf, in);
        
        char * bytearray = (char *) &out;
        for(int i = 0; i < 4; i++) printf("%02hhx", bytearray[i]);
        printf("\n");
    }

    return 0;
}
```

and then its time to try out z3

but while writing that i realized somehow some of the expected values arent even divisible by the amount of offset i added (last hexit is 8 not 0) so the representation is probably not that accurate due to how float is implemented

and z3 is just giving me unsats everywhere or a ton of wrong solutions otherwise

so i went back to verify if my theory is correct or not

at one point i got so confused about the pattern in the float representation coz it straight up aint even aligned anymore so i gave up on it

and then i thought why not try the integer representation instead

turns out the pattern is way easier to recognize so i just wrote a script to grab the differences automatically to do what i did manually for the floats
```py
raw = """
00 90 17 45 00 E0 35 45  00 90 78 45 00 60 66 45
00 A0 17 45 00 F0 35 45  00 B0 78 45 00 B0 66 45
00 B0 17 45 00 00 36 45  00 D0 78 45 00 00 67 45
00 C0 17 45 00 10 36 45  00 F0 78 45 00 50 67 45
"""

r = raw.replace('  ', ' ').replace('\n', ' ').strip().split(' ')
chunks = [struct.unpack('f', bytes([int(v, 16) for v in r[i:i + 4]]))[0] for i in range(0, len(r), 4)]

for i in range(4):
    print([j-i for i, j in zip(chunks[i::4][:-1], chunks[i::4][1:])])
```

but it is still different from the expected values somehow aside from the first value

turns out i was coding the difference obtaining automation script too much that i got it mixed with how to get expected values LOL

i parsed the expected values as columns instead of rows ofc its gonna be wrong for anything aside from the first value

with the fixed script we can grab the chunks out now and obtain the actual flag: `cvctf{CuD4_B@@M}`
```py
from z3 import *

#not working float comparison impl (inconsistent differences due to how float is implemented)

# s = Solver()

# b = [BitVec("b1", 8),BitVec("b2", 8),BitVec("b3", 8),BitVec("b4", 8)]

# # s.add(Int2BV(0x1790 + BV2Int(BV2Int(b[0]*0)xC0) + BV2Int(BV2Int(b[1]*0)x80) + BV2Int(BV2Int(b[2]*0)x40) + BV2Int(BV2Int(b[3]*0)x10), 16) == 0x2c30)
# # s.add(Int2BV(0x35e0 + BV2Int(BV2Int(b[0]*0)xD0) + BV2Int(BV2Int(b[1]*0)x90) + BV2Int(BV2Int(b[2]*0)x70) + BV2Int(BV2Int(b[3]*0)x10), 16) == 0x4fc0)
# # s.add(Int2BV(0x7890 + BV2Int(BV2Int(b[0]*0)x130) + BV2Int(BV2Int(b[1]*0)xB0) + BV2Int(BV2Int(b[2]*0)x90) + BV2Int(BV2Int(b[3]*0)x20), 16) == 0x8e40) #8e48
# # s.add(Int2BV(0x6660 + BV2Int(BV2Int(b[0]*0)xF0) + BV2Int(BV2Int(b[1]*0)xB0) + BV2Int(BV2Int(b[2]*0)x70) + BV2Int(BV2Int(b[3]*0)x50), 16) == 0x81b0)

# s.add((BV2Int(b[0]*0)xC0 + BV2Int(b[1]*0)x80 + BV2Int(b[2]*0)x40 + BV2Int(b[3]*0)x10) == 0x451920)
# s.add((BV2Int(b[0]*0)xD0 + BV2Int(b[1]*0)x90 + BV2Int(b[2]*0)x70 + BV2Int(b[3]*0)x10) == 0x4537c0)
# s.add((BV2Int(b[0]*0)x130 + BV2Int(b[1]*0)xB0 + BV2Int(b[2]*0)x90 + BV2Int(b[3]*0)x20) == 0x457b20) #8e48
# s.add((BV2Int(b[0]*0)xF0 + BV2Int(b[1]*0)xB0 + BV2Int(b[2]*0)x70 + BV2Int(b[3]*0)x50) == 0x4568c0)

# for c in b:
#     s.add(c >= 97)
#     s.add(c <= 122)

# print(s.check())

# while str(s.check()) == 'sat':
#     print("".join([chr(s.model()[c].as_long()) for c in b]))
#     s.add(Or([c != s.model()[c] for c in b]))


#sanity check for float

# b = b'aaas'[::-1]

# print(b[0], b'h'[0], hex(0x130*b'h'[0]))

# print(hex(0x448000 + BV2Int(b[0]*0)xC0 + BV2Int(b[1]*0)x80 + BV2Int(b[2]*0)x40 + BV2Int(b[3]*0)x10))
# print(hex(0x448000 + BV2Int(b[0]*0)xD0 + BV2Int(b[1]*0)x90 + BV2Int(b[2]*0)x70 + BV2Int(b[3]*0)x10))
# print(hex(0x448000 + (BV2Int(b[0]*0)x130 if b[0] <= b'h'[0] else ((0x130*b'h'[0]) + (b[0]-b'h'[0])*0x98)) + BV2Int(b[1]*0)xB0 + BV2Int(b[2]*0)x90 + BV2Int(b[3]*0)x20))
# print(hex(0x448000 + BV2Int(b[0]*0)xF0 + BV2Int(b[1]*0)xB0 + BV2Int(b[2]*0)x70 + BV2Int(b[3]*0)x50))

# print(chr(b's'[0] - 0xB))


import struct

# from c cvtsi2ss coz i just realized python struct unpack works exactly like cvtss2si

#sanity check for unpack to see if values match

# vals = [00, 0x90, 0x17, 0x45, 0x00, 0xE0, 0x35, 0x45, 0x00, 0x90, 0x78, 0x45, 0x00, 0x60, 0x66, 0x45,
# 00, 0x10, 0x18, 0x45, 0x00, 0x70, 0x36, 0x45, 0x00, 0x40, 0x79, 0x45, 0x00, 0x10, 0x67, 0x45,
# 00, 0x90, 0x18, 0x45, 0x00, 0x00, 0x37, 0x45, 0x00, 0xF0, 0x79, 0x45, 0x00, 0xC0, 0x67, 0x45,
# 00, 0x10, 0x19, 0x45, 0x00, 0x90, 0x37, 0x45, 0x00, 0xA0, 0x7A, 0x45, 0x00, 0x70, 0x68, 0x45]

#print([struct.unpack('f', bytes(vals[i:i + 4])) for i in range(0, len(vals), 16)])

# expected vals from float from cvtsi2ss

vals = [ 0x00302c45, 0x00c04f45, 0x00488e45, 0x00b08145
, 0x00601e45, 0x00f04045, 0x00788545, 0x00707145
, 0x0080e444, 0x00100645, 0x00703545, 0x00302b45
, 0x00401845, 0x00e03045, 0x00607545, 0x00205a45]

expected = [int(struct.unpack('f', v.to_bytes(4, byteorder='big'))[0]) for v in vals][:4]

# orig expected vals in memory

vals = [0xC3, 0x0A, 0x00, 0x00, 0xFC, 0x0C, 0x00, 0x00, 0xC9, 0x11, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00
    ,0xE6, 0x09, 0x00, 0x00, 0x0F, 0x0C, 0x00, 0x00, 0xAF, 0x10, 0x00, 0x00, 0x17, 0x0F, 0x00, 0x00
    ,0x24, 0x07, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0x57, 0x0B, 0x00, 0x00, 0xB3, 0x0A, 0x00, 0x00
    ,0x84, 0x09, 0x00, 0x00, 0x0E, 0x0B, 0x00, 0x00, 0x56, 0x0F, 0x00, 0x00, 0xA2, 0x0D, 0x00, 0x00]

expected = [int.from_bytes(bytes(vals[i:i + 4]), byteorder='little') for i in range(0, len(vals), 4)][12:]

print(expected)


#sanity check
# b = b'cvct'

# print((b[0]*1 + b[1]*4 + b[2]*8 + b[3]*12))
# print((b[0]*1 + b[1]*7 + b[2]*9 + b[3]*13))
# print((b[0]*2 + b[1]*9 + b[2]*11 + b[3]*19))
# print((b[0]*5 + b[1]*7 + b[2]*11 + b[3]*15))


#finally a solution that works with consistent differences
s = Solver()

b = [BitVec("b1", 8),BitVec("b2", 8),BitVec("b3", 8),BitVec("b4", 8)]

s.add((BV2Int(b[0])*1 + BV2Int(b[1])*4 + BV2Int(b[2])*8 + BV2Int(b[3])*12) == expected[0])
s.add((BV2Int(b[0])*1 + BV2Int(b[1])*7 + BV2Int(b[2])*9 + BV2Int(b[3])*13) == expected[1])
s.add((BV2Int(b[0])*2 + BV2Int(b[1])*9 + BV2Int(b[2])*11 + BV2Int(b[3])*19) == expected[2])
s.add((BV2Int(b[0])*5 + BV2Int(b[1])*7 + BV2Int(b[2])*11 + BV2Int(b[3])*15) == expected[3])


print(s.check())


while str(s.check()) == 'sat':
    print("".join([chr(s.model()[c].as_long()) for c in b]))
    s.add(Or([c != s.model()[c] for c in b]))
```

### boost game

now that we are nearly full solving i gotta try harder so its time to look at the other less solved challs

code looks kinda scary ngl but LOL turns out its way too simple for what it does

in the mess of repeating codes theres an early exit right after a check, and otherwise we increment a counter until 5 which congrats us for getting the flag

then why not just breakpoint at the early exit and see what it checks then change our input to match that

its also not modifying our input at all either it turns out so we can just dynamically obtain whats expected and send that instead

after doing that 5 times we literally just get the flag lol `cvctf{2326651332123730010604561282900}` i was so surprised it worked 

solved in like 20 mins too lmao

### my online voucher

movuscated binary :skull:

literally did not want to touch it at all but we have to in order to full solve

but it also turned out to be pretty straightforward logically LOL

the obfuscation is pretty intersting too in how it uses signal handlers to trigger calls to functions and such

i only realized it when straced it and realized theres too many segfaults yet the program still ran fine then i remembered the sigaction calls thats the only non mov instructions in the binary

now that its much clearer what the program is doing with being able to map the function calls, i can check a bit after the strlen func call which is likely the flag length check for most flag checkers (it also had "invalid" printf not far after the check too)

and im right - 0x14 was the flag length and now we can go to the next stages

i see a valid call thats never triggered with segfaults (i breakpointed at all `mov [eax], eax` after printfs coz those are the trigger points for the function calls) and an invalid that does after multiple SIGILLs at the end of the entire block of mov instructions

wait ok multiple times? but whats the pattern for that

turns out its literally a char by char check LOL the amount of SIGILLs are the amount of correct inputs until it terminates early after printing "invalid"

this means its time for our good ol pal instruction counting to do its thing

with `handle SIGNAL nostop` we can easily count how many times the char checks have been run without intervention needed too so its a pretty simple script

with this we have flage `cvctf{M0V3c0nfu51ng}`
```py
from pwn import *
import string
import io

context.log_level = 'ERROR'

flag = b'cvctf{'

while len(flag) < 0x14:

    for c in string.printable:
        p = process(['/usr/bin/gdb', '-q', './voucher'], stdin=PIPE)
        p.sendline(b'handle SIGSEGV nostop')
        p.recvuntil(b'(gdb)')
        p.sendline(b'handle SIGILL nostop')
        p.recvuntil(b'(gdb)')
        p.sendline(b'start')
        p.recvuntil(b'(gdb)')

        p.recvuntil(b'SIGSEGV') #we cant recvuntil "Enter your code: " coz its never flushed yet

        print(flag + c.encode() + (b'a'*(0x14-len(flag)-1)))
        p.sendline(flag + c.encode() + (b'a'*(0x14-len(flag)-1)))

        lines = p.recvuntil(b'(gdb)').split(b'SIGILL')
        print(len(lines), len(flag))
        if len(lines) > len(flag) + 1:
            flag += c.encode()

        p.send('quit')
        p.close()
```

### cheney-on-the-mta

chall desc mentioned a recent ctf, and opening it up we see chicken which was also in sekaictf

pain i have no idea how to even run this but i have to solve it coz its the last chall and [@kever](https://maplebacon.org/authors/vEvergarden/)s nearly done with his last crypto chall

turns out `libchicken.so.11` is an actual library oops lmfao but still i cant run it coz string-utils extension is missing and idk how to get it

and now [@kever](https://maplebacon.org/authors/vEvergarden/) is challenging me to a race to see who solves the chall first so i gotta be quick :skull:

so i just asked other teammates to try figuring out how to run it so i can probably instruction count it (coz strings tells me its another flag checker)

meanwhile ill just look at the data section for the blob of data that basically every flag checker checks against

this binary looks pretty simple in the data section: we have a few strings that has tags in front of it and then a data blob that looks like an array of sth which are all referenced in `C_toplevel` which seems to initialize the globals

looking closer we can see that the strings have 5 bytes as tags, which is quite similar to the array of chunks that the data blob has

splitting it into easier readable lines we get:
```txt
fe03000002feff0100000060
fe03000002feff0100000073
fe03000002feff0100000060
fe03000002feff0100000071
fe03000002feff0100000063
fe03000002feff0100000078
fe03000002feff0100000032
fe03000002feff0100000060
fe03000002feff0100000065
fe03000002feff0100000030
fe03000002feff010000006a
fe03000002feff0100000030
fe03000002feff010000005c
fe03000002feff010000005f
fe03000002feff0100000031
fe03000002feff010000005f
fe03000002feff0100000076
fe03000002feff010000005b
fe03000002feff010000005b
fe03000002feff010000007a
feff0e00
```

which is all repeating aside from the last byte, and looks supsiciously like ascii

also first and third byte are the same?? **c**v**c**tf????

fifth byte is also 3 away from first and third, which is also the difference between ascii `c` and `f`

now im basically convinced this is a direct representation of the flag just slightly shifted

and yep LOL flage `cvctf{5ch3m3_b4by^^}`
```py
data = [0x60, 0x73, 0x60, 0x71, 0x63, 0x78, 0x32, 0x60, 0x65, 0x30, 0x6a, 0x30, 0x5c, 0x5f, 0x31, 0x5f, 0x76, 0x5b, 0x5b, 0x7a]

print(bytes([d + 3 for d in data]))
```

insane how nobody solved this before me coz it was literally right there

guess everyone just got scared by the language like me LOL

[@kever](https://maplebacon.org/authors/vEvergarden/) solved his last crypto 2 mins before i solved this sadge lost the race

but hey with this it means we full cleared the entire ctf lmaooo very nice

i also eventually realized the hint 2 thats released a bit before i solved it (even tho i didnt even realize it was there) said something about "a simple cipher" LOL sure rot3 definitely is simple :skull: