Files
cam-mitm/iotc/iotc_bridge.c
sssnake 800052acc2 Initial commit — SetecSuite Camera MITM Framework
Original tooling from the Camhak research project (camera teardown of a
rebranded UBIA / Javiscam IP camera). PyQt6 GUI on top of a curses TUI on
top of a service controller; per-service start/stop, intruder detection,
protocol fingerprinting, OAM HMAC signing, CVE verifiers, OTA bucket
probe, firmware fetcher, fuzzer, packet injection.

Tabs: Dashboard, Live Log, Intruders, Cloud API, Fuzzer, Inject, CVEs,
Config, Help. Real-time per-packet protocol detection, conntrack-based
original-destination lookup, log rotation at 1 GiB.

See SECURITY_PAPER.md for the full writeup, site/index.html for the
public report, README.md for usage. Run with:
    sudo /usr/bin/python3 gui.py

Co-authored by Setec Labs.
2026-04-09 08:14:18 -07:00

270 lines
7.7 KiB
C

/*
* IOTC Bridge — ARM32 process that loads the TUTK library and
* communicates with the Python TUI over stdin/stdout JSON.
*
* Compile: arm-linux-gnueabi-gcc -o iotc_bridge iotc_bridge.c -L../lib -lIOTCAPIs_ALL -lpthread -ldl
* Run: qemu-arm-static -L /usr/arm-linux-gnueabi ./iotc_bridge
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
/* IOTC API declarations (from IOTCAPIs.h) */
extern int IOTC_Initialize2(int udp_port);
extern int IOTC_DeInitialize(void);
extern int IOTC_Get_SessionID(void);
extern int IOTC_Connect_ByUID_Parallel(const char *UID, int SID);
extern int IOTC_Session_Close(int SID);
extern int IOTC_Session_Check(int SID, void *info);
extern int IOTC_Lan_Search2(void *result, int max, int timeout_ms);
/* AV API declarations (from AVAPIs.h) */
extern int avInitialize(int max_channels);
extern int avDeInitialize(void);
extern int avClientStart(int SID, const char *user, const char *pass,
int timeout, unsigned int *srvtype, int channel);
extern void avClientStop(int avIndex);
extern int avSendIOCtrl(int avIndex, int type, const char *data, int len);
extern int avRecvIOCtrl(int avIndex, int *type, char *data, int len, int timeout_ms);
/* Simple JSON output helpers */
static void json_ok(const char *key, int value) {
printf("{\"ok\":true,\"%s\":%d}\n", key, value);
fflush(stdout);
}
static void json_ok_str(const char *key, const char *value) {
printf("{\"ok\":true,\"%s\":\"%s\"}\n", key, value);
fflush(stdout);
}
static void json_err(const char *msg) {
printf("{\"ok\":false,\"error\":\"%s\"}\n", msg);
fflush(stdout);
}
static void json_data(const char *cmd, const unsigned char *data, int len) {
printf("{\"ok\":true,\"cmd\":\"%s\",\"len\":%d,\"hex\":\"", cmd, len);
for (int i = 0; i < len && i < 4096; i++)
printf("%02x", data[i]);
printf("\"}\n");
fflush(stdout);
}
/* State */
static int g_sid = -1;
static int g_av_index = -1;
static int g_initialized = 0;
static int do_init(void) {
if (g_initialized) {
json_err("already initialized");
return -1;
}
int ret = IOTC_Initialize2(0);
if (ret < 0) {
char buf[64];
snprintf(buf, sizeof(buf), "IOTC_Initialize2 failed: %d", ret);
json_err(buf);
return ret;
}
ret = avInitialize(16);
if (ret < 0) {
char buf[64];
snprintf(buf, sizeof(buf), "avInitialize failed: %d", ret);
json_err(buf);
return ret;
}
g_initialized = 1;
json_ok("init", 0);
return 0;
}
static int do_connect(const char *uid) {
if (!g_initialized) {
json_err("not initialized");
return -1;
}
int sid = IOTC_Get_SessionID();
if (sid < 0) {
char buf[64];
snprintf(buf, sizeof(buf), "IOTC_Get_SessionID failed: %d", sid);
json_err(buf);
return sid;
}
g_sid = IOTC_Connect_ByUID_Parallel(uid, sid);
if (g_sid < 0) {
char buf[64];
snprintf(buf, sizeof(buf), "connect failed: %d", g_sid);
json_err(buf);
return g_sid;
}
json_ok("sid", g_sid);
return g_sid;
}
static int do_login(const char *user, const char *pass) {
if (g_sid < 0) {
json_err("not connected");
return -1;
}
unsigned int srvtype = 0;
g_av_index = avClientStart(g_sid, user, pass, 20, &srvtype, 0);
if (g_av_index < 0) {
char buf[64];
snprintf(buf, sizeof(buf), "avClientStart failed: %d", g_av_index);
json_err(buf);
return g_av_index;
}
json_ok("av_index", g_av_index);
return g_av_index;
}
static int do_ioctrl(int cmd_type, const char *hex_data) {
if (g_av_index < 0) {
json_err("not logged in");
return -1;
}
/* Parse hex data */
int hex_len = strlen(hex_data);
int data_len = hex_len / 2;
unsigned char *data = calloc(1, data_len + 1);
for (int i = 0; i < data_len; i++) {
unsigned int byte;
sscanf(hex_data + i * 2, "%2x", &byte);
data[i] = (unsigned char)byte;
}
int ret = avSendIOCtrl(g_av_index, cmd_type, (const char *)data, data_len);
free(data);
if (ret < 0) {
char buf[64];
snprintf(buf, sizeof(buf), "avSendIOCtrl failed: %d", ret);
json_err(buf);
return ret;
}
/* Try to receive response */
unsigned char resp[8192];
int resp_type = 0;
int resp_len = avRecvIOCtrl(g_av_index, &resp_type, (char *)resp, sizeof(resp), 5000);
if (resp_len > 0) {
json_data("ioctrl_resp", resp, resp_len);
} else {
json_ok("sent", cmd_type);
}
return 0;
}
static int do_lan_search(void) {
if (!g_initialized) {
json_err("not initialized");
return -1;
}
/* Search result struct: 84 bytes per entry (UID[20] + IP[16] + ...)] */
unsigned char results[84 * 16];
memset(results, 0, sizeof(results));
int count = IOTC_Lan_Search2(results, 16, 3000);
if (count < 0) {
char buf[64];
snprintf(buf, sizeof(buf), "lan_search failed: %d", count);
json_err(buf);
return count;
}
printf("{\"ok\":true,\"count\":%d,\"devices\":[", count);
for (int i = 0; i < count; i++) {
unsigned char *entry = results + (i * 84);
char uid[21] = {0};
memcpy(uid, entry, 20);
if (i > 0) printf(",");
printf("\"%s\"", uid);
}
printf("]}\n");
fflush(stdout);
return count;
}
static void do_disconnect(void) {
if (g_av_index >= 0) {
avClientStop(g_av_index);
g_av_index = -1;
}
if (g_sid >= 0) {
IOTC_Session_Close(g_sid);
g_sid = -1;
}
json_ok("disconnected", 1);
}
static void do_deinit(void) {
do_disconnect();
if (g_initialized) {
avDeInitialize();
IOTC_DeInitialize();
g_initialized = 0;
}
json_ok("deinit", 1);
}
int main(int argc, char *argv[]) {
char line[8192];
fprintf(stderr, "IOTC Bridge started. Send JSON commands on stdin.\n");
while (fgets(line, sizeof(line), stdin)) {
/* Strip newline */
line[strcspn(line, "\r\n")] = 0;
if (strlen(line) == 0) continue;
/* Very simple command parsing: cmd arg1 arg2 ... */
char cmd[64] = {0};
char arg1[256] = {0};
char arg2[256] = {0};
char arg3[8192] = {0};
sscanf(line, "%63s %255s %255s %8191s", cmd, arg1, arg2, arg3);
if (strcmp(cmd, "init") == 0) {
do_init();
} else if (strcmp(cmd, "connect") == 0) {
if (strlen(arg1) == 0) {
json_err("usage: connect <UID>");
} else {
do_connect(arg1);
}
} else if (strcmp(cmd, "login") == 0) {
if (strlen(arg1) == 0 || strlen(arg2) == 0) {
json_err("usage: login <user> <pass>");
} else {
do_login(arg1, arg2);
}
} else if (strcmp(cmd, "ioctrl") == 0) {
if (strlen(arg1) == 0) {
json_err("usage: ioctrl <cmd_id> [hex_data]");
} else {
int cmd_id = atoi(arg1);
do_ioctrl(cmd_id, strlen(arg2) > 0 ? arg2 : "");
}
} else if (strcmp(cmd, "search") == 0) {
do_lan_search();
} else if (strcmp(cmd, "disconnect") == 0) {
do_disconnect();
} else if (strcmp(cmd, "quit") == 0 || strcmp(cmd, "exit") == 0) {
do_deinit();
break;
} else if (strcmp(cmd, "help") == 0) {
printf("{\"commands\":[\"init\",\"connect <UID>\",\"login <user> <pass>\",\"ioctrl <cmd_id> [hex]\",\"search\",\"disconnect\",\"quit\"]}\n");
fflush(stdout);
} else {
json_err("unknown command");
}
}
do_deinit();
return 0;
}