mirror of
https://github.com/tuya-cloudcutter/tuya-cloudcutter.git
synced 2026-03-07 13:16:47 +01:00
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:
@@ -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!
|
||||
|
||||
@@ -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.."
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user