diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index 5407c50..3a436a8 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -5,6 +5,8 @@ Pasticciotto uses the Harvard Architecture meaning its code is separated from it There are 8 general purpose registers (`R0` to `S3`) with `S0 -> S3` being "scratch" ones. There is a `RP` register (Return Pointer) and obviously the `IP` (Instruction Pointer). +Every instruction varies from `2` to `4` bytes long: the opcode has a fixed size (`1` byte). + # Opcode encryption The VM needs a decryption key to run: the opcodes are "encrypted" with the key by the assembler. The encryption algorithm is the `RC4` key scheduling shuffle. Once the values are shuffled, the `opcodes` are assigned according to their definition order. @@ -22,6 +24,25 @@ for i, o in enumerate(ops): o.set_value(arr[i]) ``` +# Addressing modes +## Absolute +``` +JMPI 0x200 # jumps to code[0x200] +CALL foo # jumps to the foo() function +``` +## Register direct +Every opcode ending with `R` uses this addressing mode. E.g: +``` +MOVR R0, R1 # R0 = R1 +MULR R0, R1 # R0 *= R1 +``` + +## Immediate +Every opcode ending with `I` uses this addressing mode. E.g: +``` +MOVI R0, 0x2 # R0 = 0x2 +MULI R0, 0x2 # R0 *= 0x2 +``` # Assembling, labels and functions The enclosed assembler recognizes **labels** and **functions**. The **main** function has to be defined. Here is an example: diff --git a/README.md b/README.md index a4ac0f5..68be964 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ void foo() { # What about the challenge? -You can find the client and the server under the `polictf/` directory. I do not want to spoil the challenge for those that haven't completed it yet so you won't find the "specifics" of it. Check out some write-up online! +You can find the client and the server under the `polictf/` directory. I have also written a small writeup. Check it out! # Implementation details Check out the file [IMPLEMENTATION.MD](IMPL) to understand how the VM works and which operations it can do! Watch out for some spoilers if you haven't completed the challenge though! diff --git a/polictf/README.MD b/polictf/README.MD index 27a0b97..6fc78fa 100644 --- a/polictf/README.MD +++ b/polictf/README.MD @@ -1,10 +1,423 @@ # PoliCTF 2017 Challenge -This is the hint displayed on the challenge page: +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... +> 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. \ No newline at end of file +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 +``` \ No newline at end of file diff --git a/polictf/asms/README.MD b/polictf/asms/README.MD new file mode 100644 index 0000000..9a9ef7d --- /dev/null +++ b/polictf/asms/README.MD @@ -0,0 +1,2 @@ +# C versions +You can find the C version of these algorithms in the `tea_cversion`. \ No newline at end of file diff --git a/polictf/res/flag.txt b/polictf/res/flag.txt index 86337a8..5591df2 100644 --- a/polictf/res/flag.txt +++ b/polictf/res/flag.txt @@ -1 +1 @@ -PoliCTF17{DajeFunziona} \ No newline at end of file +flag{m4nc14t1b1_stu_bellu_p4sticci0tt0} \ No newline at end of file diff --git a/polictf/server/exploit-test.py b/polictf/server/exploit-test.py index 0099371..c38aa3f 100644 --- a/polictf/server/exploit-test.py +++ b/polictf/server/exploit-test.py @@ -2,7 +2,7 @@ from pwn import * import subprocess key_re = re.compile(".*\"(.*)\".*") -r = remote("127.0.0.1", 8888) +r = remote("pasticciotto.chall.polictf.it", 31337) first = r.recv() key = key_re.match(first).group(1) @@ -13,4 +13,4 @@ with open("./out.pasticciotto") as f: r.send("{}\n".format(len(data))) print(r.recv()) r.send("{}\n".format(data)) -print(r.recv(100000)) \ No newline at end of file +print(r.recv(100000))