maxim-smirnov / gree-wifimodule-firmware

Gree AC WiFi Modules Firmwares
7 stars 5 forks source link

Interesting finds #2

Open KiralyCraft opened 6 months ago

KiralyCraft commented 6 months ago

I ran a strings on the U-WB05RT13V1.21-4307516678339185323.bin file, and I came across this:

../../../component/common/drivers/wlan/realtek/src/osdep/lwip_intf.c

Which is actually this file:

https://github.com/ambiot/ambd_sdk/blob/dev/component/common/drivers/wlan/realtek/src/osdep/lwip_intf.h

Might help with reverse engineering some of the code.

Found this as well: ../../../component/os/freertos/freertos_v10.0.1/Source/portable/GCC/ARM_RTL8710C/port.c As well as [I]: create_DeviceKey :%s - Which may indicate where the encryption key is generated (and how).

It looks like the device was (is) based on an AmebaZ2 RTL8710C, although the FCC pictures show an RTL8720CF: https://device.report/m/68a82c07c0a456cd60cb23c4e70f1e84d20fe8af49c3f36a73e89fd74cd5af95

That device has a Cortex M4 MCU, named KM4 MCU which is 32 bit, and likely little endian.

From here, Ghidra can help with ARM Aggresive Instruction finding, according to: https://blog.feabhas.com/2022/12/disassembling-a-cortex-m-raw-binary-file-with-ghidra/

maxim-smirnov commented 5 months ago

As I can see from the .bin file, the firmware itself developed using Ameba-ZII SDK (you can find AmebaZII by offset 0x290). So I think it can be RTL8720CM, RTL8720CF, RTL8720CN or RTL8710CX (got from Realtek docs).

KiralyCraft commented 5 months ago

Indeed, it looks like the SDK they use is a bit older, though. The file structure/path shown for the port.c resembles the structure of the vanilla FreeRTOS 10.0.1; I found a link somewhere with screenshots, and the SDK paths used to indeed look like that.

However, that was in a period when you needed an NDA with Realtek to have access to it. They've since noticed interest in the RTL87[XX] and released the SDK openly in that GitHub repo. I'm not sure if things might've also changed during this release, so what we have now may not be 1:1 with what was used to develop it. Nevertheless, useful info

maxim-smirnov commented 5 months ago

By offset 0x8140 we see AmebaZIIRTL8710C, so yep, it's RTL8710C.

maxim-smirnov commented 5 months ago

Got really interesting fw for some fw_code, it's an android boot img 250M in size, gonna upload it today.

maxim-smirnov commented 5 months ago

File is too large, I'll try to figure out how to upload it, the firmwareCode is 362001063066.

KiralyCraft commented 5 months ago

Have you looked into Git LFS? It's a Large File Storage extension which is designed for this situation specifically

maxim-smirnov commented 5 months ago

Yep, but I just never used it, so I decided to play around with fw first :)

KiralyCraft commented 5 months ago

I'll handle the git then - Do you happen to know if this fw also doesn't respond to bing queries?

It looks like you'll have to do this, since apparently GitHub doesn't support LFS on public forks according this this link. I get an error saying that @KiralyCraft can not upload new objects to public fork KiralyCraft/gree-wifimodule-firmware For this, you need to:

  1. Actually install git-lfs. On my system, I had to install the git-fs package
  2. Clone the repo, do git lfs install from within the repo
  3. Set up your everything (place the files, etc etc) and then do a git lfs track <name_of_the_img>. Make sure to actually be in the directory where the image is
  4. Run a git add . from the root of the cloned repo
  5. Commit & push
maxim-smirnov commented 5 months ago

File is too large, I'll try to figure out how to upload it, the firmwareCode is 362001063066.

Looks like it's for something like Smart Voice Control for AC. Can you give some strings from the packets are used in bind requests/responses? Like json keys, etc.

KiralyCraft commented 5 months ago

I documented some strings and protocols here:

https://github.com/cmroche/greeclimate/issues/70

This is just the scan, to which my device responds, but is then unable to bind. I disconnected my ACs from WiFi, in the hope that they don't update (and ruin our research). When binding, you have the bind keyword somewhere in there, from what I recall.

EDIT: Looks like my attempt was a bind attempt, so it looks about like that. The generic key is a3K8Bx%2r8Y7#xDh

maxim-smirnov commented 5 months ago

Here is the firmware I talked about

KiralyCraft commented 5 months ago

Tadaaa! The file is in the squashfs, at /etc/gree

Looks like the binary is not stripped, what a nice find!

> file gree
gree: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 4.4.0, not stripped
> grep "a3K8Bx%2r8Y7#xDh" . -iR                                                                                                                                                                                                       
grep: ./gree: binary file matches
maxim-smirnov commented 5 months ago

local_handle_recvdata looks interesting

KiralyCraft commented 5 months ago

Looks like it may be vulnerable to an SQL injection?

select * from device_data where MAC = \"%s\""

EDIT: Do you happen to know if this particular firmware works locally with available local control tools? Is this a jackpot?

maxim-smirnov commented 5 months ago

Looks like 👍 I'm a little confused for what kind of device this fw for...

maxim-smirnov commented 5 months ago

From local_reqJSON_udp found that key "i" in the json means does the device use locally generated AES key or default string "a3K8Bx%2r8Y7#xDh".

if (LOCAL_AESKEY_FLG == 0) {
  puVar4 = aes_device_key;
}
else {
  puVar4 = ecb_unify_key;
}
uVar7 = aes_ecb_encryption_t(puVar4,auStack_818);
memset(auStack_418,0,0x400);
base64_encode(auStack_418,0x400,auStack_818,uVar7);
uVar2 = cJSON_CreateObject();
uVar5 = cJSON_CreateString("pack");
cJSON_AddItemToObject(uVar2,"t",uVar5);
uVar5 = cJSON_CreateNumber(SUB84((double)(ulonglong)LOCAL_AESKEY_FLG,0));
cJSON_AddItemToObject(uVar2,"i",uVar5);
uVar5 = cJSON_CreateNumber(SUB84((double)(longlong)loginUid,0));
cJSON_AddItemToObject(uVar2,"uid",uVar5);
uVar5 = cJSON_CreateString(LOCAL_MAC);
cJSON_AddItemToObject(uVar2,"cid",uVar5);
uVar5 = cJSON_CreateString("");
cJSON_AddItemToObject(uVar2,"tcid",uVar5);
uVar5 = cJSON_CreateString(auStack_418);
cJSON_AddItemToObject(uVar2,"pack",uVar5);
local_reqJSON_out = (char *)cJSON_PrintUnformatted(uVar2);
KiralyCraft commented 5 months ago

I wonder, did you upload the wrong MD file for this new firmware? It seems that the version it gives me is 1.38, whereas the one you uploaded is 1.55

maxim-smirnov commented 5 months ago

Check the domains

http://test.grih.gree.com/wifiModule/Lastversion?firmwareCode=362001063066

{"CreateDate":"2022-12-06 01:00:21","commProtVer":"","desc":"","forcedUpgrade":0,"r":200,"url":"http://test.grih.gree.com/wifiModule/image/16988/254130648","ver":"1.55"}

http://grih.gree.com/wifiModule/Lastversion?firmwareCode=362001063066

{"CreateDate":"2022-05-31 08:23:21","commProtVer":"","desc":"优化已知功能","forcedUpgrade":0,"r":200,"url":"http://grih.gree.com/wifiModule/image/10463/193714648","ver":"1.38"}
KiralyCraft commented 5 months ago

Well, well, looks like I need glasses after all. I was looking at V1.38, and some things weren't lining up with your findings. Haha, they had it with debugging symbols since 1.38!

maxim-smirnov commented 5 months ago

Hey! Got ESP32 firmware

KiralyCraft commented 5 months ago

Really nice! Didn't get to looking into it just yet, but it's on the TODO list, as soon as possible. Did you find anything interesting in that one yet?

maxim-smirnov commented 5 months ago

Still investigating

KiralyCraft commented 5 months ago

I convinced the automatic LLM friend to generate a python script for us which checks the links you suggested. I've tested and debugged this, seems to work nicely:

import os
import sys
import json
import requests

def fetch_and_save_data(firmware_code, server):
    # Define the URL
    url = f'http://{server}/wifiModule/Lastversion?firmwareCode={firmware_code}'

    try:
        # Fetch JSON data using requests
        response = requests.get(url,verify=False)
        response.raise_for_status()  # Raise an exception for 4xx and 5xx status codes
        data = response.json()

        # Check if the response status is 405
        if data.get('r') == 405:
            print(f"Ignore firmware code {firmware_code} from {server} due to response status 405")
            return

        # Prepare filename
        version = data.get('ver')
        filename = f"downloaded/{firmware_code}_v{version}_{server}.md"

        # Write data to MD file
        with open(filename, 'w') as file:
            file.write(f"`{url}`\n\n")
            file.write(f"```json\n")
            json.dump(data, file, indent=2)
            file.write(f"```")

        print(f"Data saved for firmware code {firmware_code} from {server}")

    except requests.RequestException as e:
        print(f"Error fetching data for firmware code {firmware_code} from {server}: {str(e)}")
    except json.JSONDecodeError as e:
        print(f"Error decoding JSON response for firmware code {firmware_code} from {server}: {str(e)}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python script.py <firmware_code>")
        sys.exit(1)

    firmware_code = sys.argv[1]
    servers = [
        "grih.gree.com",
        "test.grih.gree.com",
        "ru.grih.gree.com",
        "na.grih.gree.com",
        "hk.grih.gree.com",
        "eu.grih.gree.com",
        "in.grih.gree.com",
        "sa.grih.gree.com"
    ]

    # Create the "downloaded" directory if it doesn't exist
    os.makedirs("downloaded", exist_ok=True)

    for server in servers:
        fetch_and_save_data(firmware_code, server)

If you find this useful, would you mind adding it? Also, we have a new firmware code contribution for 62001065280+U-WB05RT11V1.21.bin from https://github.com/home-assistant/core/issues/109230#issuecomment-2097199452

Would you mind adding that as well? I'm currently away from my broadband connection, and cloning the repo for a PR would eat my data. Still, that one seems to be yet again, 750 KB with the codename AmebaZIIRTL8710C. I wonder, the submission states that one of the devices is running firmware V1.16; I wonder, is there a link for downloading a specific version? Could we fetch V1.16 somehow, since we know it exists?

KiralyCraft commented 5 months ago

Managed to query the DNS requetss of my app. Seems it contacts:

eu12.as.gree.com, eugrih.gree.com

KiralyCraft commented 5 months ago

Looks like V1.53 does indeed fix fetching. It's only available on test.grih.gree.com, but it fixes these issues.

maxim-smirnov commented 5 months ago

Just to clarify XXgrih.gree.com and XX.grih.gree.com are always resolves to same IP. So eugrih.gree.com and eu.grih.gree.com are same servers. Kinda wondering is commProtVer means something like "communication protocol version"?

maxim-smirnov commented 5 months ago

Maybe someone can try to downgrade the firmware with DNS spoofing? I don't have any ACs with WiFi modules :)

wangeris commented 5 months ago

Maybe someone can try to downgrade the firmware with DNS spoofing? I don't have any ACs with WiFi modules :)

Hey, I could try it (have a unit), but not sure where to begin.

maxim-smirnov commented 5 months ago

@wangeris do you know your firmwareCode for your module?

wangeris commented 5 months ago

@wangeris do you know your firmwareCode for your module?

sadly I do not, tried to find out by intercepting dns queries, but no luck with that due to SSL i believe

maxim-smirnov commented 5 months ago

@wangeris is there any labels/stickers on your wifi module?

wangeris commented 5 months ago

I'll have to open up my AC unit to look when I'm home

KiralyCraft commented 5 months ago

2024-04-20-20-58-08-108

Here's a picture of mine if it helps. I attempted to upgrade my AC using DNS spoofing. You have to have the EWPE Smart / GREE+ app launched with a debugger attached, and then all regions map to the test server, and you can perform the upgrade (thanks to Urusus2 for this discovery) . I'm now running 1.53, which works with Home Assistant and existing tools.

If you simply change the default domain it's looking for to the IP of the testing server, you get the popup for a firmware upgrade but it never completes. There's no "upgrade firmware" button, it just shows it's available.

The app may have trouble with SSL for actually downloading the image (who knows?). Still, there is a way to make it ignore SSL certificates, and perform a MITM attack on it. If you run Windows, there's this web debugger called "Telerik Fiddler". It can decrypt HTTPs, but it requires that you install a CA certificate which simply trusts everything (which is also generates). Using this certificate, install it on the mobile device, and then connect the phone to a hotspot you control, where you can then play with routing (such as, with iptables) and with DNS queries as well. It seems that the installation of this all-trusting certificate spreads it's effects even in apps.

I haven't done this in a long while, but theory says it should still work. If anything, you can set a proxy on your device's wifi connection to point to Fiddler, and then you'll see all HTTPs traffic intercepted in there, including GREE traffic.

The device itself seems to have an SSL certificate embedded, for the "gree.com" domain, which expires in about 10 years. It was visible in the firmware I uploaded. If firmware update is done through the cloud, then I doubt downgrade would work with manually provided firmware, but it could work if the AC's DNS queries are poisoned to report a custom (locally-hosted) "image meta server", while still downloading the files from China cloud. The prompts for firmware data seem to go over HTTP (since the app worked with poisoned DNS).

Urusus2 commented 5 months ago

Maybe someone can try to downgrade the firmware with DNS spoofing? I don't have any ACs with WiFi modules :)

it doesn't work, it doesn't downgrade... I update from 1.20 to 1.21, and from 1.21 to 1.22.... I tried with charles proxy to change response: changed url to version 1.21, version to 1.30(example, need to be greater) and sent successfully to wifi module, but it not downgrade......when change response to update (1.21 to 1.22) it successfully updated....

maxim-smirnov commented 5 months ago

As I saw from different firmware files, looks like the app provides an fw link to the module, and module proceeds with the update by itself. May be try to change the link on that stage?

Urusus2 commented 5 months ago

I change link, if version is greater it update successfully, but if version is smaller it not downgrade... Yesterday I try to send request to module directly to update firmware from 1.53 to 1.21, module response with 200, but module not downgrade firmware

maxim-smirnov commented 4 months ago

Hey guys, got some fw codes :) Here are downloaded responses for them https://github.com/maxim-smirnov/gree-wifimodule-firmware/tree/main/downloaded_362001060000_362001069999

And here is the list of fw codes bruted starting from 362001060000 to 362001069999 https://github.com/maxim-smirnov/gree-wifimodule-firmware/blob/main/known_firmware_codes_362001060000_362001069999.txt

KiralyCraft commented 4 months ago

This is really cool - I wanted to try this as well, but then thought that it might make noise on GREE's servers. Given that you've taken the shot, would it be nice to try to also download all the firmware?

I'll build a script which also downloads the firmware themselves. Let's pray GREE won't take this down.

EDIT: I downloaded them, submitted a PR. There's still a few files missing, because they didn't exist on the servers. Only 4 of them are Android images, and are too big. They require LFS, so I can't PR with them. I've noted them in the PR

wangeris commented 4 months ago

@wangeris is there any labels/stickers on your wifi module?

sorry for a delay in my reply, on my wifi module it says it's wb05rt13v1.09

mkaluza commented 3 months ago

Hi guys! Since this thread seems alive, I'll add my 2c here - it you search for a string like TemSen inside the firmware, you'll find a lot of params that can be queried and/or set via this https://github.com/tomikaa87/gree-remote From more interesting ones I got access to more temp sensors:

KiralyCraft commented 3 months ago

Buzzer_ON_OFF [..] inhibit the beep

This alone makes this find amazing! I simply taped over my buzzers, but it's still really loud. Do you have the V2 encryption on your device, or the plain (standard) one?

mkaluza commented 3 months ago

V2 as in GCM? Yep - I do. As for that buzzer, b/c I played with it a bit more yesterday. It appears it works every other time :D I'll keep digging, but still - it cuts the frequency of beeps in half :D

Now I'm looking for a way to change minimal compressor freq, because the current one is too high and it can't hold stable temp - keeps cooling down and then turns off once it gets to 2C below set temp. I suspect the device itself can go lower (the range I have is 28-84, which matches data I found in some manuals pretty well), but since it looses efficiency at low and high range, it is probably artificially limited due to eco freaks... But I'm hoping that is just some register somewhere in the controller that can be modifier once found...

maxim-smirnov commented 2 months ago

So sorry was unable to review all kinda stuff that you guys have submitted, really had a lot of work.

mkaluza commented 2 months ago

Hi @maxim-smirnov No worries, no rush. Good you're still here. Have you done any interesting hacks on this stuff? like gree wire protocol (the way the AC talks to the outdoor unit) for example?

Btw do you know how to force firmware update/downgrade?

KiralyCraft commented 2 months ago

You may be able to sniff the HTTPs traffic when updating your devices using this:

https://github.com/nmatt0/mitmrouter