Added automation step to make devices connect to cloudcutter-flash access point without need for a Tuya app on your phone

This commit is contained in:
Tom Clement
2022-03-30 00:11:32 +02:00
parent 6459ce1fcd
commit b27726ea19
3 changed files with 80 additions and 26 deletions

View File

@@ -10,12 +10,12 @@ Here we describe how to use tuya-cloudcutter to jailbreak Tuya IoT devices by re
* Download or git clone this repository
* Open a terminal and `cd` into the repository to make it your working directory
* Run `./run_detach.sh <SSID> <SSID password> [wifi adapter name]`, where SSID/password is the name of the access point you want the Tuya device to join, and wifi adapter is optional (if not set, it will use the first detected adapter in your computer)
* When instructed, put your Tuya device in _AP Mode_ by toggling it off and on again 6 times, with around 1 second in between each toggle
* When instructed, put your Tuya device in _AP Mode_ by toggling it off and on again 6 times, with around 1 second in between each toggle. If it's a light bulb, it will blink _slowly_. If it blinks _quickly_, power cycle it 3 more times.
* The script will automatically connect to your light (assuming it creates a "SmarLife-*" SSID. If not, let us know.) and run the exploit that replaces the security keys (now it can't connect to the cloud anymore)
* The exploit freezes the light. It will reboot back into AP mode if left alone, and you can speed this up by power cycling it yourself one time
* The script will start up an access point of its own called "cloudcutter-flash", using your WiFi adapter
* Use the phone app to make the Tuya device connect to "cloudcutter-flash" (we're automating this step at the moment, so you won't need an app anymore soon)
* Once the device connects, the script will set up your device's local access keys, and configure it to join the SSID you passed as an argument to `run_detach.sh`
* Turn the device off and on again once. It will enter AP mode again. If it doesn't, power cycle it 6 times to enter AP mode. The script will now make the device connect to our "cloudcutter-flash" AP.
* Once the device connects (can take up to half a minute), the script will set up your device's local access keys, and configure it to join the SSID you passed as an argument to `run_detach.sh`
* Your Tuya device should now be completely cut off from the cloud, and be locally controllable on your network using e.g. `tinytuya`
* The randomly generated keys you need to connect to your device can be found in the `configured-devices` folder
* Enjoy!

View File

@@ -1,6 +1,10 @@
#!/usr/bin/env bash
CLOUDCUTTER_AP_PREAMBLE="A-"
TUYA_AP_PREAMBLE="SmartLife-"
IHOME_AP_PREAMBLE="iHome-"
COMBINED_AP_PREAMBLE="(${CLOUDCUTTER_AP_PREAMBLE})|(${TUYA_AP_PREAMBLE})|(${IHOME_AP_PREAMBLE})"
AP_MATCHED_NAME=""
FIRST_WIFI=$(nmcli device status | grep " wifi " | head -n1 | awk -F ' ' '{print $1}')
@@ -9,27 +13,42 @@ SSID_PASS=${2:-$SSID_PASS}
WIFI_ADAPTER=${3:-$FIRST_WIFI}
wifi_connect () {
AP_SEARCH_STRING=${1:-$TUYA_AP_PREAMBLE}
AP_SEARCH_STRING=${1:-$COMBINED_AP_PREAMBLE}
AP_PASS=${2:-""}
# Turn on WiFi, and wait for SSID to show up
nmcli radio wifi on
while [ "${AP_MATCHED_NAME}" == "" ]
for i in {1..5}
do
echo "Scanning for "${TUYA_AP_PREAMBLE}" SSID..."
AP_MATCHED_NAME=$(nmcli dev wifi list --rescan yes | grep "${TUYA_AP_PREAMBLE}" | cut -c 2- | awk -F ' ' '{print $2}')
AP_MATCHED_NAME=""
# Turn on WiFi, and wait for SSID to show up
nmcli device set ${WIFI_ADAPTER} managed yes # Make sure we turn on managed mode again in case we didn't recover it in the trap below
nmcli radio wifi off
sleep 1
nmcli radio wifi on
while [ "${AP_MATCHED_NAME}" == "" ]
do
echo "Scanning for "${AP_SEARCH_STRING}" SSID..."
AP_MATCHED_NAME=$(nmcli dev wifi list --rescan yes | grep -E "${AP_SEARCH_STRING}" | cut -c 2- | awk -F ' ' '{print $2}')
done
echo "Found access point name: ${AP_MATCHED_NAME}, trying to connect.."
nmcli dev wifi connect ${AP_MATCHED_NAME} ${AP_PASS}
# Check if successfully connected
AP_STATUS=$(nmcli -f GENERAL.STATE con show "${AP_MATCHED_NAME}" | awk -F ' ' '{print $2}')
if [ "${AP_STATUS}" != "activated" ]; then
if [[ "${i}" == "5" ]]; then
echo "Error, could not connect to SSID."
return 1
fi
else
break
fi
sleep 1
done
echo "Found access point name: ${AP_MATCHED_NAME}, trying to connect now."
nmcli dev wifi connect ${AP_MATCHED_NAME}
# Check if successfully connected
AP_STATUS=$(nmcli -f GENERAL.STATE con show "${AP_MATCHED_NAME}" | awk -F ' ' '{print $2}')
if [ "${AP_STATUS}" != "activated" ]; then
echo "Error, could not connect to SSID."
return 1
fi
echo "Connected to access point."
return 0
}
@@ -57,11 +76,8 @@ PROFILE=$(cat profile.txt)
rm -f profile.txt
# Connect to Tuya device's WiFi
echo "==> Toggle Tuya device's power off and on again 6 times, with ~1 sec pauses in between, to enable AP mode. Repeat if ${TUYA_AP_PREAMBLE} SSID doesn't show up within ~30 seconds."
nmcli device set ${WIFI_ADAPTER} managed yes # Make sure we turn on managed mode again in case we didn't recover it in the trap below
nmcli radio wifi off
nmcli radio wifi on
wifi_connect ${TUYA_AP_PREAMBLE}
echo "==> Toggle Tuya device's power off and on again 6 times, with ~1 sec pauses in between, to enable AP mode. Repeat if your device's SSID doesn't show up within ~30 seconds."
wifi_connect ${COMBINED_AP_PREAMBLE}
if [ ! $? -eq 0 ]; then
echo "Failed to connect, please run this script again"
exit 1
@@ -82,11 +98,29 @@ OUTPUT_DIR=$(echo "${OUTPUT}" | grep "output=" | awk -F '=' '{print $2}' | sed -
echo "Saved device config in ${OUTPUT_DIR}"
# Connect to Tuya device's WiFi again, to make it connect to our hostapd AP later
echo "==> Turn the device off and on again once. Repeat 6 more times if your device's SSID doesn't show up within ~5 seconds."
sleep 1
wifi_connect ${COMBINED_AP_PREAMBLE}
if [ ! $? -eq 0 ]; then
echo "Failed to connect, please run this script again"
exit 1
fi
OUTPUT=$(run_in_docker pipenv run python3 -m cloudcutter configure_wifi "cloudcutter-flash" "")
RESULT=$?
echo "${OUTPUT}"
if [ ! $RESULT -eq 0 ]; then
echo "Oh no, something went wrong with making the device connect to our hostapd AP! Try again I guess.."
exit 1
fi
echo "Device is connecting to 'cloudcutter-flash' access point"
# Cutting device from cloud, allowing local-tuya access still
echo "Cutting device off from cloud.."
echo "==> Wait for 20-30 seconds for the device to connect to 'cloudcutter-flash'. This script will then show the activation requests sent by the device, and tell you whether local activation was successful."
nmcli device set ${WIFI_ADAPTER} managed no
trap "nmcli device set ${WIFI_ADAPTER} managed yes" EXIT # Set WiFi adapter back to managed when the script exits
echo "==> Connect your phone to 'cloudcutter-flash' WiFi, and follow AP Mode instructions in Tuya phone app."
run_in_docker bash -c "bash /src/setup_apmode.sh ${WIFI_ADAPTER} && pipenv run python3 -m cloudcutter configure_local_device --ssid \"${SSID}\" --password \"${SSID_PASS}\" \"/src/cloudcutter/device-profiles/${PROFILE}\" \"${OUTPUT_DIR}\""
if [ ! $? -eq 0 ]; then
echo "Oh no, something went wrong with detaching from the cloud! Try again I guess.."

View File

@@ -11,7 +11,7 @@ import tornado.web
from .crypto.pskcontext import PSKContext
from .device import DEFAULT_AUTH_KEY, DEVICE_PROFILE_FILE_NAME, DeviceConfig
from .exploit import exploit_device_with_config
from .exploit import exploit_device_with_config, build_network_config_packet, send_network_config_datagram
from .protocol.handlers import DetachHandler, GetURLHandler
from .protocol.transformers import ResponseTransformer
@@ -136,6 +136,18 @@ def __exploit_device(args):
print(f"output={output_path}")
def __configure_wifi(args):
SSID = args.SSID
password = args.password
payload = '{"ssid":"' + SSID + '","passwd":"' + password + '","token":"AAAAAAAA"}'
print(f"{payload=}")
datagram = build_network_config_packet(payload.encode('ascii'))
send_network_config_datagram(datagram)
print(f"Configured device to connect to '{SSID}'")
def parse_args():
parser = argparse.ArgumentParser(
prog="cloudcutter",
@@ -191,6 +203,14 @@ def parse_args():
)
parser_exploit_device.set_defaults(handler=__exploit_device)
parser_configure_wifi = subparsers.add_parser(
"configure_wifi",
help="Makes a device to which you're connected via its AP mode join a given WiFi network"
)
parser_configure_wifi.add_argument("SSID", help="WiFi access point name to make the device join")
parser_configure_wifi.add_argument("password", help="WiFi access point password")
parser_configure_wifi.set_defaults(handler=__configure_wifi)
return parser.parse_args()