The current RG2100 firmware did not ship with a ready-to-run SSH daemon. The goal here was a small, signed OTA that installs Dropbear and a systemd service, without flashing boot, modem, or recovery partitions.
Borrowing Dropbear from older firmware
An older R717 firmware included Dropbear and a compatible glibc runtime. The useful files were copied from:
R717F21.FR.1311_ota_update_all_sm/ota_update_all/system
The working runtime set:
dropbearmulti
dropbear_rsa_host_key
ld-2.22.so
libc-2.22.so
libcrypt-2.22.so
libnsl-2.22.so
libnss_compat-2.22.so
libnss_files-2.22.so
libutil-2.22.so
libz.so.1.2.8
Required symlinks at runtime:
dropbear -> dropbearmulti
dropbearkey -> dropbearmulti
ld-linux.so.3 -> ld-2.22.so
libc.so.6 -> libc-2.22.so
libcrypt.so.1 -> libcrypt-2.22.so
libnsl.so.1 -> libnsl-2.22.so
libnss_compat.so.2 -> libnss_compat-2.22.so
libnss_files.so.2 -> libnss_files-2.22.so
libutil.so.1 -> libutil-2.22.so
libz.so.1 -> libz.so.1.2.8
Manually, this worked as a systemd service. Modern OpenSSH rejects old ssh-rsa host keys by default, so the client needs:
ssh -o HostKeyAlgorithms=+ssh-rsa [email protected]
The original R717 root password hash cracked to frk9x07. For the final neutral package below, I used a different password:
franklinroot
The SHA-256 crypt hash used in /etc/shadow:
$5$FwRoot9x07$PSqLxmZNQiVergpuBSwjjOsBpsgjoMzVpw1rQjHvsg7
Package layout
The first test package used obviously lab-specific names. Once the mechanism was proven, I built a cleaner version with no lab references in paths, filenames, scripts, UI messages, or package strings.
Final neutral package paths:
/data/dropbear
/etc/systemd/system/dropbear.service
/tmp/dropbear-postinst.sh
/tmp/dropbear-remove.sh
/etc/shadow.fw-pre-ssh
The updater-script
The install updater-script is intentionally minimal:
ui_print("Installing remote access service");
mount("ubifs", "UBI", "system", "/system", "");
mount("ubifs", "UBI", "usrfs", "/data", "");
package_extract_file("tmp/dropbear-postinst.sh", "/tmp/dropbear-postinst.sh");
package_extract_dir("system", "/system");
package_extract_dir("data", "/data");
set_perm(0, 0, 0755, "/tmp/dropbear-postinst.sh");
run_program("/bin/sh", "/tmp/dropbear-postinst.sh");
ui_print("Remote access service installed");
unmount("/data");
unmount("/system");
The package contains only:
META-INF/com/android/metadata
META-INF/com/google/android/update-binary
META-INF/com/google/android/updater-script
data/dropbear/dropbear_rsa_host_key
data/dropbear/dropbearmulti
data/dropbear/ld-2.22.so
data/dropbear/libc-2.22.so
data/dropbear/libcrypt-2.22.so
data/dropbear/libnsl-2.22.so
data/dropbear/libnss_compat-2.22.so
data/dropbear/libnss_files-2.22.so
data/dropbear/libutil-2.22.so
data/dropbear/libz.so.1.2.8
system/etc/systemd/system/dropbear.service
tmp/dropbear-postinst.sh
There are no destructive updater commands:
write_raw_image
format
delete
delete_recursive
apply_patch
block_image_update
Post-install script behaviour
The postinstall script:
- backs up
/system/etc/shadowto/system/etc/shadow.fw-pre-ssh - changes only the
rootpassword hash - creates Dropbear symlinks under
/data/dropbear - enables
/system/etc/systemd/system/dropbear.service - removes older generic
*dropbear.serviceunits and old/data/*/dropbearruntime directories to avoid port conflicts - runs
sync
It does not flash boot, modem, recovery, firmware, or any raw partition.
The AppleDouble trap
The first signed test package passed web UI validation and rebooted into recovery, but the update failed before the updater-script ran.
Recovery logs showed:
._full_update_all.zip
full_update_all.zip
._fti_sw_ver
fti_sw_ver
._filesize
filesize
._zip_sign.bin
zip_sign.bin
There is no decrypted zip file. Exit PrepareRecovery.
macOS had added AppleDouble sidecar files to the inner tar. Franklin’s recovery script does roughly:
zipfile=`find /cache -name "*.zip"`
if [[ -f "$zipfile" ]]; then
...
fi
With both ._full_update_all.zip and full_update_all.zip present, zipfile becomes a multi-line value and the -f test fails.
The fix was to disable resource-fork/AppleDouble generation during packaging:
export COPYFILE_DISABLE=1
xattr -cr "$workdir"
After rebuilding, the decrypted inner tar contained only:
full_update_all.zip
fti_sw_ver
filesize
zip_sign.bin
Final packages
The final neutral install PAC:
analysis/franklin-remote-access-ota/RG2100.FR.SVC.0001.pac
The removal PAC:
analysis/franklin-remote-access-ota-remove/RG2100.FR.SVC.REMOVE.0001.pac
SHA-256 hashes:
bf45fcd18786bfd189316b21a35cc158863d8e46f4bcf06eab2342d9517b9817 RG2100.FR.SVC.0001.pac
33943626b6d41811b24fb3edeb84526a1533358817d6268220e1fd1208df7977 RG2100.FR.SVC.REMOVE.0001.pac
Audit checks before flashing:
old-name-scan-install: no matches
old-name-scan-remove: no matches
destructive-check: no risky updater commands
enc signature: Verified OK
zip signature: OK
Successful install
The proof-of-concept package using the same install mechanism applied successfully. The device rebooted into recovery, showed the progress bar, rebooted back into normal mode, and came up with Dropbear active:
dropbear.service enabled
dropbear.service active
tcp 0.0.0.0:22 LISTEN
Connect with:
ssh -o HostKeyAlgorithms=+ssh-rsa [email protected]
Password:
franklinroot
What actually changed
The successful recovery log from the proof-of-concept package showed extraction of only the intended files. The final neutral package uses different names and paths, but the same narrow update mechanism. The audited file set:
/system/etc/systemd/system/dropbear.service
/data/dropbear/dropbear_rsa_host_key
/data/dropbear/dropbearmulti
/data/dropbear/ld-2.22.so
/data/dropbear/libc-2.22.so
/data/dropbear/libcrypt-2.22.so
/data/dropbear/libnsl-2.22.so
/data/dropbear/libnss_compat-2.22.so
/data/dropbear/libnss_files-2.22.so
/data/dropbear/libutil-2.22.so
/data/dropbear/libz.so.1.2.8
The web UI can be overdramatic about update status. The important evidence is the recovery log and the resulting filesystem state. In this case, the package did not replace the full system, it merged a small number of files into the existing rootfs and usrfs volumes.
Continue to Part 7: Security and Wrap-Up.