Securing LDAP with LDAPS via Traefik

TLDR; To use the Unifi LTE Backup Pro with your own SIM card, edit the binary /usr/bin/uiwwand and bounce it.

Resources Consulted

  1. https://old.reddit.com/r/Ubiquiti/comments/1ddtzrr/using_ultebackup_pro_us_model_with_nonatt_sim/

NOTE: I’ve only tested this with T-mobile. YMMV

The Problem

The Unifi-LTE-Backup-Pro allows users to insert their own SIM card into the device. Unfortunately users are carrier-locked into using AT&T SIM cards, and I’m more of a T-Mobile guy. The Sierra Wireless RC7611 modem chip the LTE router uses doesn’t appear to be locked to a fixed carrier.

Gathering Information

Issuing AT-Commands

AT Commands are a special set of commands that a lot of telecom modems use.

RC76xx AT-Command Reference Manual: https://www.bipom.com/documents/sierra/AirPrime%20-%20RC76xx%20-%20AT%20Command%20Reference%20Guide%20-%20Rev4.0.pdf

If we SSH into the LTE Router, we can issue AT-commands to gleam some valuable info. The modem in our LTE-Backup-Pro is mounted on /dev/ttyUSB2 To read AT-command output and send AT-commands, run the following after SSH-ing into the LTE router: cat < /dev/ttyUSB2. In a separate SSH session run echo "ATI" > /dev/ttyUSB2. You should receive output similar to the following (the redactions are my own):

root@U-LTE-Pro:~# cat < /dev/ttyUSB2
ATI

Manufacturer: Sierra Wireless, Incorporated
Model: RC7611
Revision: SWI9X07H_00.08.20.00 d73df7 jenkins 2021/10/14 00:48:45
IMEI: [REDACTED]
IMEI SV: 18
FSN: [REDACTED]
+GCAP: +CGSM
OK

This printout confirms that the device uses an RC7611 modem chip and is running what appears to be generic firmware?

uiwwand

What is this binary?

The U-LTE-Backup-Pro runs a watchdog script that starts/checks/bounces the binary /usr/bin/uiwwand.

root@U-LTE-Pro:~# ps w | grep "watch"
 1403 root      1248 S    {modem_watchdog.} /bin/sh /etc/ltecfg/modem_watchdog.sh
13578 root      1264 R    sh /usr/etc/syswrapper.sh ssh-trace-cmd -c ps w | grep "watch" -n 4 -i
13580 root      1208 S    grep watch

If we search for strings in the /usr/bin/uiwwand binary, we can find SIM-related terms:

root@U-LTE-Pro:~# grep "SIM" /usr/bin/uiwwand
EVENT_SIM_READY
EVENT_SIM_REMOVED
STATE_SIM_ACTIVATION
SIM card removed
AUTO-SIM
uim: PIN '%s' is eligible to be verified for SIM with ICCID %s
uim: PIN '%s' was detected as incorrect previously for SIM with ICCID %s, refusing to verify
uim: PIN '%s' is eligible to be verified for SIM with ICCID %s (PIN1 retries = 3)
Incompatible SIM (MCC: %d, MNC: %d)
SIM powered off
SIM powered on
MNC length from SIM: %d
MCC %d, MNC %d from SIM
SIM data read successfully
SIM data could not be read
AUTO-SIM
ULTE-Pro-US: Canada SIM detected: mcc=%i mnc=%i (PRI=%s)
ULTE-Pro-US: %s'AT&T (US)' SIM detected:mcc=%i mnc=%i (PRI=%s)
ULTE-Pro-US: SIM is incompatible
Skip enable_autoconnect() because the SIM card is not compatible
Skip seq_set_profile() because the SIM card is not compatible

This binary is clearly important

SCP the binary

The U-LTE-Backup-Pro runs some sort of read-only RamFS Linux OS. In order to SCP files on and off the device, you will need to run SCP in legacy mode as SFTP is not available, ie:

scp -O root@192.168.0.137:/usr/bin/uiwwand .

Ghidra

We can run the binary thru Ghidra and search for relevant strings. Eventually, you’ll come across the following functions. I’ve gone ahead and annotated the function and variable names so we can see what’s going on.

void FUN_0041a7c4(void)

{
  int is_canadian_mcc;
  char is_mcc_canadian;
  undefined *prepend_str;
  char profile_to_load [20];

  if ((((DAT_00433e74 == '\0') || (DAT_00433e76 == '\0')) || (DAT_00433e77 == '\0')) ||
     (DAT_00433e75 == '\0')) {
    is_sim_readable = '\0';
  }
  else {
    is_sim_readable = '\x01';
  }
  if (is_sim_readable == '\0') {
    some_sort_of_print_function(1,3,"SIM data could not be read");
  }
  else {
    some_sort_of_print_function(1,6,"SIM data read successfully");
  }
  DAT_00433e79 = 1;
  is_sim_unsupported = '\0';
  if (DAT_00434189 != '\0') {
    is_mcc_canadian = func_is_mcc_canadian(mcc_value); // <- this function checks to see if the MCC (mobile country code) is equal to 302 (ie Canada)
    if (is_mcc_canadian == 0) {
      // MCC is not Canadian (302). `/usr/bin/uiwwand` assumes the SIM is American
      is_sim_ATT = FUN_0040c66c(mcc_value,mnc_value); // <- Checks to see if MMC/MNC combo is part of AT&T. Basically a tabular lookup
      is_sim_unsupported = is_sim_ATT == '\0'; // <- If SIM is not AT&T, then it is unsupported
      builtin_strncpy(profile_to_load,"ATT",4); // <- If the SIM is American, uiwwand assumes it's AT&T and loads the ATT profile
      if (is_sim_ATT == '\0') {
        prepend_str = &DAT_0041fa4c; // MNC/MCC combo is not AT&T, code will print non-AT&T (US) SIM detected
      }
      else {
        prepend_str = &DAT_0041f438; // MNC/MCC combo is AT&T, code will print AT&T (US) SIM detected
      }
      some_sort_of_print_function(1,6,"ULTE-Pro-US: %s\'AT&T (US)\' SIM detected:mcc=%i mnc=%i (PRI=%s)",prepend_str,
                   mcc_value,mnc_value,profile_to_load);
    }
    else {
      // MCC is 302. `uiwwand` assumes the SIM is Canadian
      is_sim_unsupported = '\0';
      builtin_strncpy(profile_to_load,"AUTO-SIM",9);
      some_sort_of_print_function(1,6,"ULTE-Pro-US: Canada SIM detected: mcc=%i mnc=%i (PRI=%s)",mcc_value,
                   mnc_value,profile_to_load);
    }
    if (is_sim_unsupported != '\0') { // watch out for the double-negative variable
      // If we enter the function, then the SIM is unsupported
      some_sort_of_print_function(1,6,"ULTE-Pro-US: SIM is incompatible");
    }
    is_currently_loaded_profile_equal_profile_to_load = strncmp(&currently_loaded_profile,profile_to_load,0x14);
    if (is_currently_loaded_profile_equal_profile_to_load != 0) {
      // Profile needs to be reloaded
      snprintf(&currently_loaded_profile,0x14,"%s",profile_to_load);
      DAT_00433fd4 = 0;
      FUN_00414db4(&PTR_FUN_004331e8);
    }
  }
  if (is_sim_unsupported != '\0') { // watch out for the double-negative variable
    // If we enter the function, then the SIM is unsupported
    FUN_0040c5c4(mcc_value,mnc_value);
  }
  FUN_0040c150(&DAT_00433d5c);
  if (is_sim_unsupported != '\x01') { // watch out for the double-negative variable
    // If we enter this function, then SIM is supported
    FUN_00406efc(6);
  }
  return;
}

If we ignore the cruft of prints and loading of profiles, we essentially just need to focus on the following logic:

void FUN_0041a7c4(void)

{
  /* A bunch of SIM readability checks */
  if (DAT_00434189 != '\0') {
    is_mcc_canadian = func_is_mcc_canadian(mcc_value); // <- this function checks to see if the MCC (mobile country code) is equal to 302 (ie Canada)
    if (is_mcc_canadian == 0) {
      /* If SIM is not Canadian AND is part of AT&T, load ATT Profile, else error out. */
    }
    else {
      /* IF SIM is Canadian, load AUTO-SIM Profile */
    }
    /* Check if SIM is supported, check if profile needs to be loaded, etc. */
  }
  /* Various printouts and checks related to SIM supportability */
  return;
}

Notice that if we can trick uiwwand into thinking the SIM card is Canadian, then it’ll load the “AUTO-SIM” profile which allows it to work w/ non-AT&T SIMs. If we disassemble the function func_is_mcc_canadian, the code is pretty straight-forward.

[ ... Start of function, a bunch of stack pointer stuff ...]
0040c944 24 02 01 2e     li         v0,0x12e            // Load 302 into v0
0040c948 14 62 00 04     bne        v1,v0,LAB_0040c95c  // If v1 != 302 (v0), branch to LAB_0040c95c
0040c94c 00 00 00 00     _nop
0040c950 24 02 00 01     li         v0,0x1              // Load 1 into v0
0040c954 10 00 00 02     b          LAB_0040c960        // Skip clearing v0
0040c958 00 00 00 00     _nop
                     LAB_0040c95c                                    XREF[1]:     0040c948(j)
0040c95c 00 00 10 21     clear      v0
                     LAB_0040c960                                    XREF[1]:     0040c954(j)
[ ... A bunch of stack pointer stuff, set v0 to return value, then jr ra ... ]

u/MrNerdHair points out in his post that change the binary 14 62 00 04 -> 14 62 00 01 sets bne’s jump offset to 1, so it basically becomes a no-op. The consequence of this is that the function will always return true, ie we always assume the SIM is Canadian and the load “AUTO-SIM” profile. This binary sequence is distinctive and only appears once in the file.

Conclusion

In order to get the U-LTE-Backup-Pro to run off of a non-AT&T SIM is to run the following script:

hexdump -v -e '/1 "%02x"' /usr/bin/uiwwand > /tmp/uiwwand.hex && \
grep -c "2402012e14620004" /tmp/uiwwand.hex && \
echo "Pattern found, starting patch..." && \
sed -i 's/2402012e14620004/2402012e14620001/' /tmp/uiwwand.hex && \
echo "File patched. Converting back to binary" && \
rm -f /tmp/uiwwand.bin && \
while read -n2 hex; do
    printf "\\x$hex" >> /tmp/uiwwand.bin
done < /tmp/uiwwand.hex && \
echo "Double-checking patch: " && \
hexdump /tmp/uiwwand.bin | grep "2402 012e 1462"  && \
echo "Replacing and bouncing..." && \
cp /tmp/uiwwand.bin /usr/bin/uiwwand && \
chmod +x /usr/bin/uiwwand && \
killall uiwwand && \
echo "done!"

Unfortunately the U-LTE-Backup-Pro’s filesystem is read-only and resets on reboot. You’ll need to run some sort of cron job on a separate host, maybe a UDM-Pro?