added automatic latest firmware spoofing

This commit is contained in:
2026-07-13 20:15:28 +00:00
parent da29d1a032
commit fafb35cda4
6 changed files with 62 additions and 2 deletions
+25
View File
@@ -89,3 +89,28 @@ behavior that still needs manual testing.
state in the handoff. state in the handoff.
- Do not commit generated binaries, OpenWrt build output, runtime state, logs, or - Do not commit generated binaries, OpenWrt build output, runtime state, logs, or
captured protocol payloads. captured protocol payloads.
## Environment pitfalls and shared discoveries
- In this workspace, the Codex `apply_patch` helper is currently unusable. It
fails with `bwrap: No permissions to create a new namespace`, including when
invoked from an approved command outside the normal sandbox. Do not spend time
retrying it or tracing its symlink after seeing this known error.
- For repository edits, use `git apply --recount` with a focused unified diff as
the fallback. `--recount` avoids failures caused by manually miscounted hunk
lengths. Review the resulting `git diff` and run `git diff --check` afterward.
Preserve unrelated worktree changes exactly as with any other editing method.
- Ordinary read-only commands may fail with the same `bwrap` namespace error.
When the command is safe and required, rerun it using the environment's normal
escalation/approval mechanism instead of investigating the namespace setup.
Agents must preserve useful environment knowledge for later agents. When a new
pitfall or workaround is verified to be deterministic or repeatedly encountered,
add a short entry to this section before handoff. Keep these entries actionable
and specific: include the recognizable failure, the confirmed workaround, and
any validation required afterward.
Do not record transient command failures, guesses, lengthy logs, credentials,
auth keys, host secrets, or details that only apply to a single task. If a
discovery could weaken safety controls or requires broader permissions, report
it to the user instead of documenting a bypass.
+5
View File
@@ -8,6 +8,7 @@ Daemon that makes an OpenWrt router appear as a UniFi AP to UniFi Network contro
| --- | --- | --- | | --- | --- | --- |
| **L2 Discovery** | UDP broadcast + multicast every 10s | `announce.c` → port 10001 | | **L2 Discovery** | UDP broadcast + multicast every 10s | `announce.c` → port 10001 |
| **Adoption** | AES-128-CBC handshake with the controller | `inform.c``handle_response()` | | **Adoption** | AES-128-CBC handshake with the controller | `inform.c``handle_response()` |
| **Firmware spoofing** | Persists and reports the target version requested by an upgrade | `inform.c``handle_response()` |
| **WiFi Config** | Creates WiFi networks from the controller via UCI | `wlan.c``wlan_apply_config()` | | **WiFi Config** | Creates WiFi networks from the controller via UCI | `wlan.c``wlan_apply_config()` |
| **Band Steering** | 802.11k/v Neighbor Reports + BSS Transition | `wlan.c``apply_vap()` | | **Band Steering** | 802.11k/v Neighbor Reports + BSS Transition | `wlan.c``apply_vap()` |
| **Fast Roaming** | 802.11r FT with mobility_domain derived from MAC | `wlan.c``apply_vap()` | | **Fast Roaming** | 802.11r FT with mobility_domain derived from MAC | `wlan.c``apply_vap()` |
@@ -99,6 +100,10 @@ Daemon settings live in `/etc/openuf/openuf.conf`. Adoption and controller state
are persisted separately in `/etc/openuf/state.json`; do not copy that file are persisted separately in `/etc/openuf/state.json`; do not copy that file
between devices because it contains the device authentication key. between devices because it contains the device authentication key.
Because UniFi firmware cannot run on the OpenWrt host, an upgrade request does
not download or install its image. Instead, openUF saves the requested firmware
version in `state.json` and reports it in subsequent inform packets.
--- ---
## Glossary ## Glossary
+28 -1
View File
@@ -575,7 +575,12 @@ static char *build_payload(const openuf_state_t *st,
} }
char fw_version[64]; char fw_version[64];
snprintf(fw_version, sizeof(fw_version), "%s%s", m->fw_pre, m->fw_ver); if (st->firmware_version[0])
snprintf(fw_version, sizeof(fw_version), "%s",
st->firmware_version);
else
snprintf(fw_version, sizeof(fw_version), "%s%s",
m->fw_pre, m->fw_ver);
char inform_url_buf[256]; char inform_url_buf[256];
if (st->inform_url[0]) if (st->inform_url[0])
@@ -812,6 +817,28 @@ static void handle_response(openuf_state_t *st,
LOG("Handling response type: %s", type); LOG("Handling response type: %s", type);
/* An OpenWrt host cannot install UniFi firmware. Acknowledge the
* controller's request by reporting its target version from now on. */
if (!strcmp(type, "upgrade")) {
if (json_object_object_get_ex(resp, "version", &v) &&
json_object_is_type(v, json_type_string)) {
const char *version = json_object_get_string(v);
size_t len = strlen(version);
if (len > 0 && len < sizeof(st->firmware_version)) {
snprintf(st->firmware_version,
sizeof(st->firmware_version), "%s", version);
state_save(st);
LOG("Firmware upgrade spoofed; now reporting version=%s",
st->firmware_version);
strcpy(action_out, "upgrade-spoofed");
return;
}
}
LOG("Ignoring upgrade response without a valid version");
strcpy(action_out, "upgrade-invalid");
return;
}
/* ── noop ────────────────────────────────────────────────────── */ /* ── noop ────────────────────────────────────────────────────── */
if (!strcmp(type, "noop")) { if (!strcmp(type, "noop")) {
strcpy(action_out, "noop"); strcpy(action_out, "noop");
+1 -1
View File
@@ -28,7 +28,7 @@
void inform_set_debug_level(int level); void inform_set_debug_level(int level);
/* Send one inform cycle. /* Send one inform cycle.
* Updates *st in place (adopted flag, auth key, inform_url, cfgversion). * Updates *st in place (adoption, controller config, and spoofed firmware).
* Returns 0 on success, -1 on error (sets err_out[0..127]). */ * Returns 0 on success, -1 on error (sets err_out[0..127]). */
int inform_send(openuf_state_t *st, int inform_send(openuf_state_t *st,
const uf_model_t *model, const uf_model_t *model,
+2
View File
@@ -68,6 +68,7 @@ void state_load(openuf_state_t *st)
LOAD_STR (authkey, "authkey"); LOAD_STR (authkey, "authkey");
LOAD_STR (inform_url, "inform_url"); LOAD_STR (inform_url, "inform_url");
LOAD_STR (cfgversion, "cfgversion"); LOAD_STR (cfgversion, "cfgversion");
LOAD_STR (firmware_version, "firmware_version");
LOAD_BOOL(config_applied, "config_applied"); LOAD_BOOL(config_applied, "config_applied");
LOAD_INT (config_schema, "config_schema"); LOAD_INT (config_schema, "config_schema");
LOAD_BOOL(use_aes_gcm, "use_aes_gcm"); LOAD_BOOL(use_aes_gcm, "use_aes_gcm");
@@ -112,6 +113,7 @@ int state_save(const openuf_state_t *st)
json_object_object_add(root, "authkey", json_object_new_string(st->authkey)); json_object_object_add(root, "authkey", json_object_new_string(st->authkey));
json_object_object_add(root, "inform_url", json_object_new_string(st->inform_url)); json_object_object_add(root, "inform_url", json_object_new_string(st->inform_url));
json_object_object_add(root, "cfgversion", json_object_new_string(st->cfgversion)); json_object_object_add(root, "cfgversion", json_object_new_string(st->cfgversion));
json_object_object_add(root, "firmware_version", json_object_new_string(st->firmware_version));
json_object_object_add(root, "config_applied", json_object_new_boolean(st->config_applied)); json_object_object_add(root, "config_applied", json_object_new_boolean(st->config_applied));
json_object_object_add(root, "config_schema", json_object_new_int(st->config_schema)); json_object_object_add(root, "config_schema", json_object_new_int(st->config_schema));
json_object_object_add(root, "use_aes_gcm", json_object_new_boolean(st->use_aes_gcm)); json_object_object_add(root, "use_aes_gcm", json_object_new_boolean(st->use_aes_gcm));
+1
View File
@@ -10,6 +10,7 @@ typedef struct {
char authkey[64]; char authkey[64];
char inform_url[256]; char inform_url[256];
char cfgversion[32]; char cfgversion[32];
char firmware_version[64];
bool config_applied; bool config_applied;
int config_schema; int config_schema;
bool use_aes_gcm; bool use_aes_gcm;