[HITCON CTF 2018] EV3
| import json | |
| import operator | |
| ''' | |
| Have to read documentation and see how opUI_DRAW worked | |
| 840501xxxxyyyyff | |
| opcode text black xcord ycord char | |
| Communication happens from localhost ethernet -> ev3 | |
| ''' | |
| # Taken from https://stackoverflow.com/questions/10664856/make-dictionary-with-duplicate-keys-in-python | |
| class DictList(dict): | |
| def __setitem__(self, key, value): | |
| try: | |
| # Assumes there is a list on the key | |
| self[key].append(value) | |
| except KeyError: # if fails because there is no key | |
| super(DictList, self).__setitem__(key, value) | |
| except AttributeError: # if fails because it is not a list | |
| super(DictList, self).__setitem__(key, [self[key], value]) | |
| blk1="" | |
| blk2="" | |
| blk3="" | |
| blk4="" | |
| dict1 = DictList() | |
| dict2 = DictList() | |
| dict3 = DictList() | |
| dict4 = DictList() | |
| # btrfcomm && packetlogger.type==0x02 && data | |
| with open('data_ev3_1.1') as f: | |
| data = json.load(f) | |
| for j in xrange(0, 4): | |
| data_dump="" | |
| stack="" | |
| for i in xrange(0, len(data)): | |
| tmp = "".join(data[i]["_source"]["layers"]["data"]["data.data"].split(":")) | |
| if j ==0: | |
| #print "[+] Retrieving block 1" | |
| if '2884' in tmp: | |
| meh = tmp.find('2884') | |
| beep = tmp[meh+4:][:2] | |
| xcord = tmp[meh-4:][:4] | |
| if '00' in xcord[:2]: | |
| xcord = tmp[meh-6:][:6] | |
| xcord_int = int(xcord, 16) | |
| beep_nice = beep.decode('hex') | |
| if beep_nice in stack: | |
| beep_nice = beep_nice+"#" | |
| stack +=beep_nice | |
| dict1[beep_nice] = xcord_int | |
| #blk1 += beep_nice | |
| #print data_dump | |
| elif j == 1: | |
| #print "[+] Retrieving block 2" | |
| if '3684' in tmp: | |
| meh = tmp.find('3684') | |
| beep = tmp[meh+4:][:2] | |
| xcord = tmp[meh-4:][:4] | |
| if '00' in xcord[:2]: | |
| xcord = tmp[meh-6:][:6] | |
| xcord_int = int(xcord, 16) | |
| beep_nice = beep.decode('hex') | |
| if beep_nice in stack: | |
| beep_nice = beep_nice+"#" | |
| stack +=beep_nice | |
| dict2[beep_nice] = xcord_int | |
| #print data_dump | |
| elif j == 2: | |
| #print "[+] Retrieving block 3" | |
| if '4484' in tmp: | |
| meh = tmp.find('4484') | |
| beep = tmp[meh+4:][:2] | |
| xcord = tmp[meh-4:][:4] | |
| if '00' in xcord[:2]: | |
| xcord = tmp[meh-6:][:6] | |
| xcord_int = int(xcord, 16) | |
| beep_nice = beep.decode('hex') | |
| if beep_nice in stack: | |
| beep_nice = beep_nice+"#" | |
| count +=1 | |
| stack +=beep_nice | |
| dict3[beep_nice] = xcord_int | |
| #print data_dump | |
| elif j == 3: | |
| #print "[+] Retrieving block 4" | |
| if '5284' in tmp: | |
| meh = tmp.find('5284') | |
| beep = tmp[meh+4:][:2] | |
| xcord = tmp[meh-4:][:4] | |
| if '00' in xcord[:2]: | |
| xcord = tmp[meh-6:][:6] | |
| xcord_int = int(xcord, 16) | |
| beep_nice = beep.decode('hex') | |
| if beep_nice in stack: | |
| beep_nice = beep_nice+"#" | |
| stack +=beep_nice | |
| dict4[beep_nice] = xcord_int | |
| #print data_dump | |
| print dict1 | |
| sorted_dict1 = sorted(dict1.items(), key=operator.itemgetter(1)) | |
| for i in xrange(0, len(sorted_dict1)): | |
| blk1 += sorted_dict1[i][0] | |
| sorted_dict2 = sorted(dict2.items(), key=operator.itemgetter(1)) | |
| for i in xrange(0, len(sorted_dict2)): | |
| blk2 += sorted_dict2[i][0] | |
| sorted_dict3 = sorted(dict3.items(), key=operator.itemgetter(1)) | |
| for i in xrange(0, len(sorted_dict3)): | |
| blk3 += sorted_dict3[i][0] | |
| sorted_dict4 = sorted(dict4.items(), key=operator.itemgetter(1)) | |
| for i in xrange(0, len(sorted_dict4)): | |
| blk4 += sorted_dict4[i][0] | |
| x = blk1.replace("#", '') | |
| y = blk2.replace("#", '') | |
| z = blk3.replace("#", '') | |
| zz = blk4.replace("#", '') | |
| print x+y+z+zz | |
| #hitcon{m1nd5t0rm_communication_and_firmware_developer_kit} | |
| #Human interaction was to include 'e' in firmwar due to 3 duplication issue. Didn't have time during CTF to optimize solver |
| import json | |
| ''' | |
| Instruction opInput_Device (CMD, …) | |
| Opcode 0x99 | |
| Now here we are looking at the read values from the sensor sent from ev3 to localhost ethernet | |
| It seems like # for black and <space> for white will recreate the image | |
| Example payload: 99 1d 00 02 00 02 01 60 | |
| length = 8 Bytes | |
| 99 = Opcode | |
| 1d = READY_SI | |
| 00 = Layer number 0 | |
| 02 = Port Number of Sensor | |
| 00 = Type (default) | |
| 02 = Mode (default) | |
| 01 = Returned values (eh?) | |
| Seems this payload is send as a request to read the values from sensor I think and the ev3 will respond it. | |
| For reading response we can load the pklg into wireshark and use the following filter: | |
| btrfcomm && packetlogger.type==0x03 && data | |
| Select all packets (CTRL+SHIFT+M) and dump it using 'Export Packet Dissection -> As JSON -> ev3-scanner.json' | |
| Hence, the communication weill be from LegoSyst -> localhost ethernet this case as values taken from robot will be sent of. | |
| The response data variantions are minute : | |
| 00 c0 80 | |
| 00 80 3f | |
| + + 45 (1st + is faster, 2nd is increment) | |
| 00 c0 80 | |
| 00 80 3f | |
| - - 45 | |
| 00 c0 40 | |
| 00 80 3f | |
| + + 45 (1st + is faster, 2nd is increment) | |
| This looks like the ++45 means robot is making 180 degree U-Turn towards Right hand side | |
| - - 45 is to nullify I think, making 180 degree U-Turn towards Left hand side | |
| and c0 80 , c0 40 means White color read | |
| 80 3f means black color read | |
| >>> int('c0', 16), int('80',16) | |
| (192, 128) | |
| >>> int('80', 16), int('3f',16) | |
| (128, 63) | |
| So, if the color is black then seems sensor value will be down, where intensity of reflected light will be higher on white surface. | |
| ----> 1 | |
| <----- 2 | |
| -------> 3 | |
| ............ | |
| --------> 11 | |
| So, robot will traverse 11 times on the mattress. | |
| ''' | |
| with open('ev3-scanner.json') as f: | |
| data = json.load(f) | |
| alert = 0 | |
| total_turn = 0 | |
| first_round = "" | |
| second_round = "" | |
| third_round = "" | |
| fourth_round ="" | |
| fifth_round = "" | |
| six_round ="" | |
| seven_round="" | |
| eight_round = "" | |
| nine_round = "" | |
| ten_round = "" | |
| eleven_round = "" | |
| for i in xrange(0, len(data)): | |
| tmp = "".join(data[i]["_source"]["layers"]["data"]["data.data"].split(":")) | |
| if len(tmp) == 18: | |
| i = 1 | |
| identifier1 = tmp[12:][0:2] | |
| identifier2 = tmp[12:][2:4] | |
| identifier3 = tmp[12:][4:6] | |
| if identifier3 == '45': # Likely 180 U Turn | |
| alert = 1 | |
| # break # start of the fucking turn | |
| continue | |
| elif identifier2 == 'c0' or identifier3 == '40' and identifier1 == '00': # Likely white | |
| if alert == 1: # Turn has been taken | |
| print "[+] Turn was taken" | |
| alert = 0 | |
| total_turn += 1 | |
| if total_turn == 0: | |
| first_round += " " | |
| elif total_turn == 1: | |
| second_round += " " | |
| elif total_turn == 2: | |
| third_round += " " | |
| elif total_turn == 3: | |
| fourth_round += " " | |
| elif total_turn == 4: | |
| fifth_round += " " | |
| elif total_turn == 5: | |
| six_round += " " | |
| elif total_turn == 6: | |
| seven_round += " " | |
| elif total_turn == 7: | |
| eight_round += " " | |
| elif total_turn == 8: | |
| nine_round += " " | |
| elif total_turn == 9: | |
| ten_round += " " | |
| elif total_turn == 10: | |
| eleven_round += " " | |
| if total_turn == 0: | |
| first_round += " " | |
| elif total_turn == 1: | |
| second_round += " " | |
| elif total_turn == 2: | |
| third_round += " " | |
| elif total_turn == 3: | |
| fourth_round += " " | |
| elif total_turn == 4: | |
| fifth_round += " " | |
| elif total_turn == 5: | |
| six_round += " " | |
| elif total_turn == 6: | |
| seven_round += " " | |
| elif total_turn == 7: | |
| eight_round += " " | |
| elif total_turn == 8: | |
| nine_round += " " | |
| elif total_turn == 9: | |
| ten_round += " " | |
| elif total_turn == 10: | |
| eleven_round += " " | |
| elif identifier2 == '80' and identifier1 == '00': # Likely black | |
| if alert == 1: | |
| print "[+] Turn was taken" | |
| alert = 0 | |
| total_turn += 1 | |
| if total_turn == 0: | |
| first_round += "#" | |
| elif total_turn == 1: | |
| second_round += "#" | |
| elif total_turn == 2: | |
| third_round += "#" | |
| elif total_turn == 3: | |
| fourth_round += "#" | |
| elif total_turn == 4: | |
| fifth_round += "#" | |
| elif total_turn == 5: | |
| six_round += "#" | |
| elif total_turn == 6: | |
| seven_round += "#" | |
| elif total_turn == 7: | |
| eight_round += "#" | |
| elif total_turn == 8: | |
| nine_round += "#" | |
| elif total_turn == 9: | |
| ten_round += "#" | |
| elif total_turn == 10: | |
| eleven_round += " " | |
| if total_turn == 0: | |
| first_round += "#" | |
| elif total_turn == 1: | |
| second_round += "#" | |
| elif total_turn == 2: | |
| third_round += "#" | |
| elif total_turn == 3: | |
| fourth_round += "#" | |
| elif total_turn == 4: | |
| fifth_round += "#" | |
| elif total_turn == 5: | |
| six_round += "#" | |
| elif total_turn == 6: | |
| seven_round += "#" | |
| elif total_turn == 7: | |
| eight_round += "#" | |
| elif total_turn == 8: | |
| nine_round += "#" | |
| elif total_turn == 9: | |
| ten_round += "#" | |
| elif total_turn == 10: | |
| eleven_round +="#" | |
| print "[+] Total turn taken was "+str(total_turn) | |
| print first_round | |
| print second_round[::-1] | |
| print third_round | |
| print fourth_round[::-1] | |
| print fifth_round | |
| print six_round[::-1] | |
| print seven_round | |
| print eight_round[::-1] | |
| print nine_round | |
| print ten_round[::-1] | |
| print eleven_round | |
| print "*************************************************************************************" | |
| print len(first_round) | |
| print len(second_round) | |
| print len(third_round) | |
| print len(fourth_round) | |
| print len(fifth_round) | |
| print len(six_round) | |
| print len(seven_round) | |
| print len(eight_round) | |
| print len(nine_round) | |
| print len(ten_round) | |
| print len(eleven_round) | |
| #hitcon{EV3GYROSUCKS} | |
| #The ascii art isn't that proper but readable. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment