# 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 ```