gipu/polictf/README.MD
2017-07-08 11:35:56 +02:00

423 lines
7.7 KiB
Markdown

# PoliCTF 2017 Challenge
This is the description displayed on the challenge page:
> We found this executable and we think it must have something in common with the baddies' infrastructure.
> We would be glad to understand what **`data`** they are hiding from us...
# How to deploy
The challenge can be deployed by running the `create_files.sh` helper script and by running the `server.elf` behind a proxy (xinetd). The server needs to access the files in the `res/` directory.
# Write-up
The client provided to the partecipants is just the VM running the code `encrypt.pstc` in the `asms/` directory:
```asm
def datastrlen:
###############
# r0 = offset of str in data
# retval (r0) = strlen
###############
push r1
push r2
push r3
movr s2, r0
movi s1, 0
lodr s0, s2
cmpb s0, 0
jpei exit
loop:
movi s2, 0
addi s1, 1
addr s2, s1
lodr s0, s2
cmpb s0, 0
jpni loop
exit:
movr r0, s1
poop r3
poop r2
poop r1
retn
def round: # round(uint16_t text[2])
#################
# r0 = offset of text[0] in data
# r1 = offset of text[1] in data
# r2 = text[0]
# r3 = text[1]
# retval = void
################
push r1
push r2
push r3
lodr r2, r0 # text[0]
lodr r3, r1 # text[1]
movi s0, 0 # i
movi s1, 0 # sum
loop:
push s0 # saving i
addi s1, 0x626f # sum += delta
push s1 # saving sum
# s0 and s1 will be used as tmps
#########
# calc v0
#########
movr s0, r3
shli s0, 4
addi s0, 0x7065 # s0 = (text[1] << 4) + k0
movr s1, r3
poop s3 # restoring sum in s3
#addr s1, s3 # s1 = text[1] + sum
push s3 # saving sum again
xorr s0, s1 # s0 = ((text[1] << 4) + k0) ^ (text[1] + sum)
push s0
movr s0, r3
shri s0, 5
addi s0, 0x7065 # s0 = (text[1] >> 5) + k1
poop s1
xorr s0, s1 # s0 = ((text[1] << 4) + k0) ^ (text[1] + sum) ^ ((text[1] >> 5) + k1)
addr r2, s0 # r2 += s0
#########
# calc v1
#########
movr s0, r2
shli s0, 4
addi s0, 0x7275 # s0 = (text[0] << 4) + k2
movr s1, r2
poop s3 # restoring sum in s3
#addr s1, s3 # s1 = text[0] + sum
push s3 # saving sum again
xorr s0, s1 # s0 = ((text[0] << 4) + k2) ^ (text[0] + sum)
push s0
movr s0, r2
shri s0, 5
addi s0, 0x6e73 # s0 = (text[0] >> 5) + k3
poop s1
xorr s0, s1 # s0 = ((text[0] << 4) + k2) ^ (text[0] + sum) ^ ((text[0] >> 5) + k3)
addr r3, s0 # r3 += s0
######
# end loop
#####
poop s1 # restoring sum
poop s0 # restoring i
addi s0, 1
cmpb s0, 127 # while (i < 128)
jpbi loop
# saving the values
strr r0, r2
strr r1, r3
poop r3
poop r2
poop r1
retn
def main:
grmn
movi r0, 0xadde
movi r1, 0x0bb0
stri 0, r0
stri 2, r1
movi r0, 0x0bb0
movi r1, 0xcefa
stri 0x4, r0
stri 0x6, r1
movi r0, 0
call datastrlen
movr r2, r0
movi s0, 0
encrypt:
push s0
movi r0, 0
movi r1, 2
addr r0, s0
addr r1, s0
call round
poop s0
addi s0, 4
cmpr s0, r2
jpbi encrypt
lodi r0, 0
lodi r1, 2
lodi r2, 4
lodi r3, 6
shit
```
Once the VM bytecode has been reversed, the partecipant had to write a custom assembler to **decrypt the server's VM data section** using the same algorithm shown above.
An example of a working decryption algorithm is `decrypt.pstc` in the `asms/` directory:
```asm
def datastrlen:
###############
# r0 = offset of str in data
# retval (r0) = strlen
###############
push r1
push r2
push r3
movr s2, r0
movi s1, 0
lodr s0, s2
cmpb s0, 0
jpei exit
loop:
movi s2, 0
addi s1, 1
addr s2, s1
lodr s0, s2
cmpb s0, 0
jpni loop
exit:
movr r0, s1
poop r3
poop r2
poop r1
retn
def round: # round(uint16_t text[2])
#################
# r0 = offset of text[0] in data
# r1 = offset of text[1] in data
# r2 = text[0]
# r3 = text[1]
# retval = void
################
push r1
push r2
push r3
lodr r2, r0 # text[0]
lodr r3, r1 # text[1]
movi s0, 0 # i
movi s1, 0 # sum
loop:
push s0 # saving i
# s0 and s1 will be used as tmps
#########
# calc v1
#########
movr s0, r2
shli s0, 4
addi s0, 0x7275 # s0 = (text[0] << 4) + k2
movr s1, r2
xorr s0, s1 # s0 = ((text[0] << 4) + k2) ^ text[0]
push s0
movr s0, r2
shri s0, 5
addi s0, 0x6e73 # s0 = (text[0] >> 5) + k3
poop s1
xorr s0, s1 # s0 = ((text[0] << 4) + k2) ^ text[0] ^ ((text[0] >> 5) + k3)
subr r3, s0 # r3 -= s0
#########
# calc v0
#########
movr s0, r3
shli s0, 4
addi s0, 0x7065 # s0 = (text[1] << 4) + k0
movr s1, r3
xorr s0, s1 # s0 = ((text[1] << 4) + k0) ^ text[1]
push s0
movr s0, r3
shri s0, 5
addi s0, 0x7065 # s0 = (text[1] >> 5) + k1
poop s1
xorr s0, s1 # s0 = ((text[1] << 4) + k0) ^ text[1] ^ ((text[1] >> 5) + k1)
subr r2, s0 # r2 -= s0
######
# end loop
#####
poop s0 # restoring i
addi s0, 1
cmpb s0, 127 # while (i < 128)
jpbi loop
# saving the values
strr r0, r2
strr r1, r3
poop r3
poop r2
poop r1
retn
def main:
movi r0, 0
call datastrlen
movr r2, r0
movi s0, 0
decrypt:
push s0
movi r0, 0
movi r1, 2
addr r0, s0
addr r1, s0
call round
poop s0
addi s0, 4
cmpr s0, r2
jpbi decrypt
shit
```
The python wrapper used to solve the challenge, `exploit-test.py`, can be found in the `server/` directory.
```python
from pwn import *
import subprocess
key_re = re.compile(".*\"(.*)\".*")
r = remote("pasticciotto.chall.polictf.it", 31337)
first = r.recv()
key = key_re.match(first).group(1)
print("Using key: {}".format(key))
subprocess.check_call(["python3", "../../assembler/assembler.py", "{}".format(key), "../asms/decrypt.pstc", "./out.pasticciotto"])
with open("./out.pasticciotto") as f:
data = f.read()
r.send("{}\n".format(len(data)))
print(r.recv())
r.send("{}\n".format(data))
print(r.recv(100000))
```
## Challenge output
```
$ python ./exploit-test.py
[+] Opening connection to pasticciotto.chall.polictf.it on port 31337: Done
Using key: 9XM6SvFPvN8qiLi
movi : 0x0->0x39
movr : 0x1->0x20
lodi : 0x2->0x9a
lodr : 0x3->0x1d
stri : 0x4->0xa9
strr : 0x5->0xd2
addi : 0x6->0x38
addr : 0x7->0x8f
subi : 0x8->0xd
subr : 0x9->0x64
andb : 0xa->0xa6
andw : 0xb->0x22
andr : 0xc->0x97
yorb : 0xd->0xda
yorw : 0xe->0x51
yorr : 0xf->0x48
xorb : 0x10->0x12
xorw : 0x11->0x70
xorr : 0x12->0xb6
notr : 0x13->0x37
muli : 0x14->0xa5
mulr : 0x15->0xc
divi : 0x16->0xd5
divr : 0x17->0xf4
shli : 0x18->0xdc
shlr : 0x19->0xc3
shri : 0x1a->0x6e
shrr : 0x1b->0xb5
push : 0x1c->0xe1
poop : 0x1d->0x88
cmpb : 0x1e->0x2c
cmpw : 0x1f->0x3a
cmpr : 0x20->0x35
jmpi : 0x21->0xbb
jmpr : 0x22->0x52
jpai : 0x23->0x4f
jpar : 0x24->0x90
jpbi : 0x25->0xd4
jpbr : 0x26->0x11
jpei : 0x27->0xe5
jper : 0x28->0x45
jpni : 0x29->0xbc
jpnr : 0x2a->0xbe
call : 0x2b->0x32
retn : 0x2c->0x7e
shit : 0x2d->0x6d
nope : 0x2e->0x6a
grmn : 0x2f->0xe6
FUNCTION main
0x0: movi r0, 0
0x4: call datastrlen
0x7: movr r2, r0
0x9: movi s0, 0
0xd: push s0
0xf: movi r0, 0
0x13: movi r1, 2
0x17: addr r0, s0
0x19: addr r1, s0
0x1b: call round
0x1e: poop s0
0x20: addi s0, 4
0x24: cmpr s0, r2
0x26: jpbi decrypt
0x29: shit
FUNCTION round
0x2a: push r1
0x2c: push r2
0x2e: push r3
0x30: lodr r2, r0
0x32: lodr r3, r1
0x34: movi s0, 0
0x38: movi s1, 0
0x3c: push s0
0x3e: movr s0, r2
0x40: shli s0, 4
0x44: addi s0, 0x7275
0x48: movr s1, r2
0x4a: xorr s0, s1
0x4c: push s0
0x4e: movr s0, r2
0x50: shri s0, 5
0x54: addi s0, 0x6e73
0x58: poop s1
0x5a: xorr s0, s1
0x5c: subr r3, s0
0x5e: movr s0, r3
0x60: shli s0, 4
0x64: addi s0, 0x7065
0x68: movr s1, r3
0x6a: xorr s0, s1
0x6c: push s0
0x6e: movr s0, r3
0x70: shri s0, 5
0x74: addi s0, 0x7065
0x78: poop s1
0x7a: xorr s0, s1
0x7c: subr r2, s0
0x7e: poop s0
0x80: addi s0, 1
0x84: cmpb s0, 127
0x87: jpbi loop
0x8a: strr r0, r2
0x8c: strr r1, r3
0x8e: poop r3
0x90: poop r2
0x92: poop r1
0x94: retn
FUNCTION datastrlen
0x95: push r1
0x97: push r2
0x99: push r3
0x9b: movr s2, r0
0x9d: movi s1, 0
0xa1: lodr s0, s2
0xa3: cmpb s0, 0
0xa6: jpei exit
0xa9: movi s2, 0
0xad: addi s1, 1
0xb1: addr s2, s1
0xb3: lodr s0, s2
0xb5: cmpb s0, 0
0xb8: jpni loop
0xbb: movr r0, s1
0xbd: poop r3
0xbf: poop r2
0xc1: poop r1
0xc3: retn
[main: size 0x2a, offset 0x0, round: size 0x6b, offset 0x2a, datastrlen: size 0x2f, offset 0x95]
Go ahead then!
Congratulations!
The flag is: flag{m4nc14t1b1_stu_bellu_p4sticci0tt0}
[*] Closed connection to pasticciotto.chall.polictf.it port 31337
```