diff --git a/dist/scripts/rayhunter_daemon b/dist/scripts/rayhunter_daemon index 2befdad..666992b 100644 --- a/dist/scripts/rayhunter_daemon +++ b/dist/scripts/rayhunter_daemon @@ -5,6 +5,8 @@ set -e case "$1" in start) echo -n "Starting rayhunter: " + # Below line may be replaced by the installer with device-specific startup commands, such as mounting the SD card. + #RAYHUNTER-PRESTART start-stop-daemon -S -b --make-pidfile --pidfile /tmp/rayhunter.pid \ --startas /bin/sh -- -c "RUST_LOG=info exec /data/rayhunter/rayhunter-daemon /data/rayhunter/config.toml > /data/rayhunter/rayhunter.log 2>&1" echo "done" diff --git a/installer/src/main.rs b/installer/src/main.rs index bf7b839..1dd8a48 100644 --- a/installer/src/main.rs +++ b/installer/src/main.rs @@ -4,8 +4,8 @@ use clap::{Parser, Subcommand}; mod orbic; mod tplink; -pub static CONFIG_TOML: &[u8] = include_bytes!("../../dist/config.toml.example"); -pub static RAYHUNTER_DAEMON_INIT: &[u8] = include_bytes!("../../dist/scripts/rayhunter_daemon"); +pub static CONFIG_TOML: &str = include_str!("../../dist/config.toml.example"); +pub static RAYHUNTER_DAEMON_INIT: &str = include_str!("../../dist/scripts/rayhunter_daemon"); #[derive(Parser, Debug)] #[command(version, about)] @@ -42,13 +42,22 @@ struct InstallOrbic {} #[derive(Parser, Debug)] struct Util { #[command(subcommand)] - command: UntilSubCommand, + command: UtilSubCommand, } #[derive(Subcommand, Debug)] -enum UntilSubCommand { +enum UtilSubCommand { /// Send a serial command to the Orbic. Serial(Serial), + /// Root the tplink and launch telnetd. + TplinkStartTelnet(TplinkStartTelnet), +} + +#[derive(Parser, Debug)] +struct TplinkStartTelnet { + /// IP address for TP-Link admin interface, if custom. + #[arg(long, default_value = "192.168.0.1")] + admin_ip: String, } #[derive(Parser, Debug)] @@ -65,7 +74,7 @@ async fn run_function() -> Result<(), Error> { Command::Tplink(tplink) => tplink::main_tplink(tplink).await.context("Failed to install rayhunter on the TP-Link M7350. Make sure your computer is connected to the hotspot using USB tethering or WiFi.")?, Command::Orbic(_) => orbic::install().await.context("Failed to install rayhunter on the Orbic RC400L")?, Command::Util(subcommand) => match subcommand.command { - UntilSubCommand::Serial(serial_cmd) => { + UtilSubCommand::Serial(serial_cmd) => { if serial_cmd.root { if !serial_cmd.command.is_empty() { eprintln!("You cannot use --root and specify a command at the same time"); @@ -83,6 +92,9 @@ async fn run_function() -> Result<(), Error> { } } } + UtilSubCommand::TplinkStartTelnet(options) => { + tplink::start_telnet(&options.admin_ip).await?; + } } } diff --git a/installer/src/orbic.rs b/installer/src/orbic.rs index 3511ed2..c74c4ea 100644 --- a/installer/src/orbic.rs +++ b/installer/src/orbic.rs @@ -116,14 +116,14 @@ async fn setup_rayhunter( serial_interface, &mut adb_device, "/data/rayhunter/config.toml", - CONFIG_TOML, + CONFIG_TOML.as_bytes(), ) .await?; install_file( serial_interface, &mut adb_device, "/etc/init.d/rayhunter_daemon", - RAYHUNTER_DAEMON_INIT, + RAYHUNTER_DAEMON_INIT.as_bytes(), ) .await?; install_file( diff --git a/installer/src/tplink.rs b/installer/src/tplink.rs index d527324..046b8ed 100644 --- a/installer/src/tplink.rs +++ b/installer/src/tplink.rs @@ -23,20 +23,25 @@ use crate::InstallTpLink; type HttpProxyClient = hyper_util::client::legacy::Client; -pub async fn main_tplink(args: InstallTpLink) -> Result<(), Error> { - let InstallTpLink { +pub async fn main_tplink( + InstallTpLink { skip_sdcard, admin_ip, - } = args; + }: InstallTpLink, +) -> Result<(), Error> { + start_telnet(&admin_ip).await?; + tplink_run_install(skip_sdcard, admin_ip).await +} +#[derive(Deserialize)] +struct V3RootResponse { + result: u64, +} + +pub async fn start_telnet(admin_ip: &str) -> Result<(), Error> { let qcmap_web_cgi_endpoint = format!("http://{admin_ip}/cgi-bin/qcmap_web_cgi"); let client = reqwest::Client::new(); - #[derive(Deserialize)] - struct RootResponse { - result: u64, - } - println!("Launching telnet on the device"); // https://github.com/advisories/GHSA-ffwq-9r7p-3j6r @@ -48,9 +53,9 @@ pub async fn main_tplink(args: InstallTpLink) -> Result<(), Error> { if response.status() == 404 { println!("Got a 404 trying to run exploit for hardware revision v3, trying v5 exploit"); - tplink_launch_telnet_v5(admin_ip.clone()).await?; + tplink_launch_telnet_v5(admin_ip).await?; } else { - let RootResponse { result } = response.error_for_status()?.json().await?; + let V3RootResponse { result } = response.error_for_status()?.json().await?; if result != 0 { anyhow::bail!("Bad result code when trying to root device: {result}"); @@ -59,9 +64,10 @@ pub async fn main_tplink(args: InstallTpLink) -> Result<(), Error> { println!("Detected hardware revision v3"); } - println!("Succeeded in rooting the device!"); - - tplink_run_install(skip_sdcard, admin_ip).await + println!( + "Succeeded in rooting the device! Now you can use 'telnet {admin_ip}' to get a root shell. Use './installer util tplink-start-telnet' to root again without installing rayhunter." + ); + Ok(()) } async fn tplink_run_install(skip_sdcard: bool, admin_ip: String) -> Result<(), Error> { @@ -70,7 +76,14 @@ async fn tplink_run_install(skip_sdcard: bool, admin_ip: String) -> Result<(), E if !skip_sdcard { println!("Mounting sdcard"); - telnet_send_command(addr, "mount /dev/mmcblk0p1 /media/card", "exit code 0").await.context("Rayhunter needs a FAT-formatted SD card to function for more than a few minutes. Insert one and rerun this installer, or pass --skip-sdcard")?; + if telnet_send_command(addr, "mount | grep -q /media/card", "exit code 0") + .await + .is_err() + { + telnet_send_command(addr, "mount /dev/mmcblk0p1 /media/card", "exit code 0").await.context("Rayhunter needs a FAT-formatted SD card to function for more than a few minutes. Insert one and rerun this installer, or pass --skip-sdcard")?; + } else { + println!("sdcard already mounted"); + } } // there is too little space on the internal flash to store anything, but the initrd script @@ -79,7 +92,12 @@ async fn tplink_run_install(skip_sdcard: bool, admin_ip: String) -> Result<(), E telnet_send_command(addr, "mkdir -p /data", "exit code 0").await?; telnet_send_command(addr, "ln -sf /media/card /data/rayhunter", "exit code 0").await?; - telnet_send_file(addr, "/media/card/config.toml", crate::CONFIG_TOML).await?; + telnet_send_file( + addr, + "/media/card/config.toml", + crate::CONFIG_TOML.as_bytes(), + ) + .await?; #[cfg(feature = "vendor")] let rayhunter_daemon_bin = include_bytes!("../../rayhunter-daemon-tplink/rayhunter-daemon"); @@ -92,7 +110,7 @@ async fn tplink_run_install(skip_sdcard: bool, admin_ip: String) -> Result<(), E telnet_send_file( addr, "/etc/init.d/rayhunter_daemon", - crate::RAYHUNTER_DAEMON_INIT, + get_rayhunter_daemon().as_bytes(), ) .await?; @@ -260,7 +278,7 @@ async fn handler(state: State, mut req: Request) -> Result Result<(), Error> { +async fn tplink_launch_telnet_v5(admin_ip: &str) -> Result<(), Error> { let client: HttpProxyClient = hyper_util::client::legacy::Client::<(), ()>::builder(TokioExecutor::new()) .build(HttpConnector::new()); @@ -270,7 +288,7 @@ async fn tplink_launch_telnet_v5(admin_ip: String) -> Result<(), Error> { .route("/{*path}", any(handler)) .with_state(AppState { client, - admin_ip: admin_ip.clone(), + admin_ip: admin_ip.to_owned(), }); let listener = tokio::net::TcpListener::bind("127.0.0.1:4000") @@ -295,3 +313,21 @@ async fn tplink_launch_telnet_v5(admin_ip: String) -> Result<(), Error> { Ok(()) } + +fn get_rayhunter_daemon() -> String { + // Even though TP-Link eventually auto-mounts the SD card, it sometimes does so too late. And + // changing the order in which daemons are started up seems to not work reliably. + // + // This part of the daemon dynamically generated because we may have to eventually add logic + // specific to a particular hardware revision here. + crate::RAYHUNTER_DAEMON_INIT.replace( + "#RAYHUNTER-PRESTART", + "mount /dev/mmcblk0p1 /media/card || true", + ) +} + +#[test] +fn test_get_rayhunter_daemon() { + let s = get_rayhunter_daemon(); + assert!(s.contains("mount /dev/mmcblk0p1 /media/card")); +}