From 92f4216aabfc55f94e880c31ea2a8e98dee58a61 Mon Sep 17 00:00:00 2001 From: Kasmar Date: Sun, 15 May 2022 13:15:20 -0400 Subject: [PATCH 1/2] Support pre-defined commands --- README.md | 13 +++-- config.example.json | 15 +++++- listener.js | 115 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 111 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 2c0efe7..ae23885 100644 --- a/README.md +++ b/README.md @@ -39,15 +39,14 @@ "manual_list" = True/False for creating your own JSON list of devices as device.json. See device.example.json. "manual_ip" = True/False for adding the IP to the device.json or to allow the script to find IPs. "use_ios_deploy" = True/False for using ios-deploy instead of cfgutil for querying devices (disables the profile command). +"allow_cmds" = True/False for allowing the execution of local commands that are defined in "cmds". +"cmds" = An array of objects that contain the "alias" (required), "command" (required), and "description" (optional). + "alias" is the name that you can send to the listener to execute a command without typing the full command. + "command" is a string used to hold the command and its parameters. Note that you should use nohup if the command's error output is larger than 200kb like Redux. + "description" is a string that you can use to keep notes about your command. + Note that passing parameters or using sudo are not supported as these open your system to attacks. ``` -## Android support -If a mitm client sends their status to RDM we can use the same logic to trigger reboots via the `rebootMonitorURL` endpoint. Currently you must use the `manual_list: true` to supply a custom script via the `reboot_cmd` option to reboot devices. All other endpoints (`reopenMonitorURL`, `reapplySAMMonitorURL`, `brightnessMonitorURL`) are not supported since they are iOS specific. Power relays can be triggered via http calls, custom API methods, and are unique per vendor as such this is a batteries not included option. - -It is recommended that you setup multiple DCMRL instances if running iOS and Android on the same host machine. This allows you to keep the automatic iOS device list detection plus reopen, reapply sam, and brightness commands can all point to one instance. Then Android devices will only receive the reboot commands keeping for clean logs. - -For iOS devices please leave `reboot_cmd` empty or delete the line! - ## Troubleshooting If the cfgutil from AC2 does not list any devices when you use the `cfgutil list` command, then you may need to upgrade AC2 and reinstall the automation tools. diff --git a/config.example.json b/config.example.json index b42644c..f481a0a 100644 --- a/config.example.json +++ b/config.example.json @@ -4,5 +4,18 @@ "domain": "http://YOUR-DOMAIN.TLD", "manual_list": false, "manual_ip": false, - "use_ios_deploy": false + "use_ios_deploy": false, + "allow_cmds": false, + "cmds": [ + { + "alias": "1", + "command": "pwd", + "description": "The pwd command prints the working directory" + }, + { + "alias": "redux_1", + "command": "cd ~/redux/ && nohup ./redux.js -f", + "description": "This command changes to the Redux directory and executes Redux with the full auto parameter. Output is sent to nohup.out so the listener can avoid buffer overflows" + } + ] } diff --git a/listener.js b/listener.js index cc868a0..1007125 100644 --- a/listener.js +++ b/listener.js @@ -8,6 +8,7 @@ const Request = require("request"); const express = require("express"); const { exec } = require("child_process"); const config = require("./config.json"); +const cmds = config.cmds; // DEFINE THE EXPRESS SERVER var server = express().use(express.json({ limit: "1mb" })); @@ -40,12 +41,13 @@ else if (config.use_ios_deploy) { server.post("/", (payload, res) => { console.log("[DCM] [listener.js] [" + getTime("log") + "] Received a Payload:", payload.body); let target = payload.body; - // GO THROUGH DEVICE ARRAY TO FIND A MATCH - devices.forEach(async (device, i) => { - switch (target.type) { + // CHECK IF THE PAYLOAD IS FOR DEVICES OR COMMANDS + // IF NOT A COMMAND, GO THROUGH DEVICE ARRAY TO FIND A MATCH + switch (target.type) { - // RESTART A DEVICE - case "restart": + // RESTART A DEVICE + case "restart": + devices.forEach(async (device, i) => { if (device.name == target.device) { if (device.reboot_cmd) { var command = device.reboot_cmd @@ -75,10 +77,12 @@ server.post("/", (payload, res) => { }); } } - break; + }); + break; - // REOPEN THE GAME - case "reopen": + // REOPEN THE GAME + case "reopen": + devices.forEach(async (device, i) => { if (device.name == target.device) { var ipaddr = ''; if (config.manual_ip) { @@ -127,14 +131,26 @@ server.post("/", (payload, res) => { }); } } - break; + }); + break; - // REAPPLY THE SAM PROFILE - case "profile": - if (!config.use_ios_deploy && device.name == target.device) { + // REAPPLY THE SAM PROFILE + case "profile": + if (config.use_ios_deploy) { + res.json({ + status: 'error', + node: config.name, + error: 'Listener set to only use iOS_Deploy.' + }); + break; + } + devices.forEach(async (device, i) => { + if (device.name == target.device) { // REMOVE THE SAM PROFILE + var errored = false; var profile = await cli_exec("cfgutil -e " + device.ecid + " -K org.der -C org.crt remove-profile com.apple.configurator.singleappmode", "device_command"); if (profile.hasError) { + errored = true; console.error("[DCM] [listener.js] [" + getTime("log") + "] Failed to remove the SAM1 profile from " + device.name + " : " + device.uuid + ".", profile.error); res.json({ status: 'error', @@ -146,6 +162,7 @@ server.post("/", (payload, res) => { // APPLY THE CLOCK PROFILE TO FORCE THE GAME CLOSED profile = await cli_exec("cfgutil -e " + device.ecid + " -K org.der -C org.crt install-profile sam_clock.mobileconfig", "device_command"); if (profile.hasError) { + errored = true; console.error("[DCM] [listener.js] [" + getTime("log") + "] Failed to add the SAM_CLOCK profile to " + device.name + " : " + device.uuid + ".", profile.error); res.json({ status: 'error', @@ -157,6 +174,7 @@ server.post("/", (payload, res) => { // REMOVE THE SAM PROFILE AGAIN profile = await cli_exec("cfgutil -e " + device.ecid + " -K org.der -C org.crt remove-profile com.apple.configurator.singleappmode", "device_command"); if (profile.hasError) { + errored = true; console.error("[DCM] [listener.js] [" + getTime("log") + "] Failed to remove the SAM2 profile from " + device.name + " : " + device.uuid + ".", profile.error); res.json({ status: 'error', @@ -168,26 +186,30 @@ server.post("/", (payload, res) => { // APPLY THE POGO PROFILE TO RELAUNCH THE GAME profile = await cli_exec("cfgutil -e " + device.ecid + " -K org.der -C org.crt install-profile sam_pogo.mobileconfig", "device_command"); if (profile.hasError) { + errored = true; console.error("[DCM] [listener.js] [" + getTime("log") + "] Failed to add the SAM_CALC profile to " + device.name + " : " + device.uuid + ".", profile.error); res.json({ status: 'error', node: config.name, error: 'Failed to add the SAM_POGO profile.' }); - break; } - // REAPPLICATION WAS SUCCESSFUL - console.log("[DCM] [listener.js] [" + getTime("log") + "] Reapplied the SAM profile to " + device.name + " : " + device.uuid + "."); - // SEND CONFIRMATION TO DCM - res.json({ - status: 'ok' - }); + if (!errored) { + // REAPPLICATION WAS SUCCESSFUL + console.log("[DCM] [listener.js] [" + getTime("log") + "] Reapplied the SAM profile to " + device.name + " : " + device.uuid + "."); + // SEND CONFIRMATION TO DCM + res.json({ + status: 'ok' + }); + } } - break; + }); + break; - // CHANGE DEVICE BRIGHTNESS - case "brightness": + // CHANGE DEVICE BRIGHTNESS + case "brightness": + devices.forEach(async (device, i) => { if (device.name == target.device) { let ipaddr = ''; if (config.manual_ip) { @@ -237,9 +259,51 @@ server.post("/", (payload, res) => { }); } } + }); + break; + + // INCOMING COMMAND + case "cmd": + if(!config.allow_cmds) { + res.json({ + status: 'error', + node: config.name, + error: 'Listener is not configured to execute local commands.' + }); break; - } - }); + } + // CHECK IF COMMAND IS IN THE CONFIG + cmds.forEach(async (cmd, i) => { + if(cmd.alias == target.cmdID) { + var cmd_string = cmd.command.toString(); + var cmd_results = await cli_exec(cmd_string, 'local_command'); + if(cmd_results.hasError) { + res.json({ + status: 'error', + node: config.name, + message: 'Failed to execute command: ' + cmd.alias + '\n' + cmd_results.error + }); + } + else { + res.json({ + status: 'ok', + node: config.name, + message: 'Listener ran command `'+cmd.alias+'` and output the following:\n'+cmd_results.result + }); + } + } + }); + break; + + // DEFAULT + default: + res.json({ + status: 'error', + node: config.name, + error: 'Listener received an unhandled request type: ' + target.type + }); + break; + } }); //------------------------------------------------------------------------------ @@ -343,6 +407,9 @@ function cli_exec(command, type) { } return resolve(ipaddr); + // BLOCK IN CASE WE NEED DIFFERENT INFO FOR LOCAL COMMANDS + case 'local_command': + // GENERAL IDEVICEDIAGNOSTICS COMMAND case 'device_command': response.hasError = false; From 32b2c650b6d976f2b2c642c828b468e9d68f0c68 Mon Sep 17 00:00:00 2001 From: Kasmar Date: Sun, 15 May 2022 13:22:53 -0400 Subject: [PATCH 2/2] Merge README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index ae23885..4a486aa 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,16 @@ "command" is a string used to hold the command and its parameters. Note that you should use nohup if the command's error output is larger than 200kb like Redux. "description" is a string that you can use to keep notes about your command. Note that passing parameters or using sudo are not supported as these open your system to attacks. + ``` +## Android support +If a mitm client sends their status to RDM we can use the same logic to trigger reboots via the `rebootMonitorURL` endpoint. Currently you must use the `manual_list: true` to supply a custom script via the `reboot_cmd` option to reboot devices. All other endpoints (`reopenMonitorURL`, `reapplySAMMonitorURL`, `brightnessMonitorURL`) are not supported since they are iOS specific. Power relays can be triggered via http calls, custom API methods, and are unique per vendor as such this is a batteries not included option. + +It is recommended that you setup multiple DCMRL instances if running iOS and Android on the same host machine. This allows you to keep the automatic iOS device list detection plus reopen, reapply sam, and brightness commands can all point to one instance. Then Android devices will only receive the reboot commands keeping for clean logs. + +For iOS devices please leave `reboot_cmd` empty or delete the line! + ## Troubleshooting If the cfgutil from AC2 does not list any devices when you use the `cfgutil list` command, then you may need to upgrade AC2 and reinstall the automation tools.