CNVD 最近公开了 CNVD-2025-31165 (CVE-2024-2986) 漏洞, 漏洞描述为:
Tenda FH1202是腾达品牌推出的一款双频无线路由器,专为大户型家庭、小型办公室或商务休闲区域设计,旨在提供稳定的无线网络覆盖和高速传输。
Tenda FH1202存在堆栈缓冲区溢出漏洞,该漏洞源于/goform/SetSpeedWan文件的formSetSpeedWan方法的speed_dir参数未能正确验证输入数据的长度大小,攻击者可利用该漏洞在系统上执行任意代码或者导致拒绝服务。
软件版本 : AC15_V15.03.05.19
binwalk 分析 Squashfs 的系统文件
执行 binwalk -e -1 解压
根据 ./etc_ro/init.d/rcS 的脚本来创建环境目录
创建 br0 的虚拟网卡
根据 libCfm.so 的 GetValue 函数创建 Unix Domain Socket , 发送信息的格式如下 :
创建 Unix Domain Socket /var/cfm_socket 的 uds_server.py 脚本代码如下 :
default.ini 是 /webroot/default.cfg 转换的, 唯一的问题是程序客户端代码使用的是长连接的方式, 断开后还需要重启 uds_server.py 程序
此外, 还需要模拟 bcm_nvram_* 函数
HOOK NVRAM 的代码 hook_nvram.c 如下 :
/tmp/nvram_default.cfg 是 /webroot/nvram_default.cfg 复制过来的
查看 httpd 的 GCC 版本,使用的是 Buildroot ,C 标准库是 uClibc
根据 GITHUB 上的 buildroot 项目, 编译生成支持 ARMv7 + uClibc + soft-float 的 arm-linux-gcc
编译命令如下 :
生成的 hook_nvram.so ELF 信息文件如下 :
主机运行 python ./uds_server.py 创建 ./var/cfm_socket
sudo chroot . bin/sh 进入固件目录环境 ,再运行 LD_PRELOAD=/tmp/hook_nvram.so /bin/httpd 开启 HTTP 服务
访问 344K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5&6x3W2)9J5k6e0p5$3z5q4)9J5k6e0y4Q4x3X3f1K6i4K6u0r3L8r3!0Y4K9h3&6Q4x3X3g2Z5N6r3#2D9 页面如下

首先查看 httpd 开启的防护如下
使用调试模式启动 httpd , 然后在 0x0061998 BL sprintf 的溢出点下断点
gdb.txt 的加载脚本如下
python ./uds_server.py 重启uds , sudo chroot . qemu-arm-static -g 1234 -E LD_PRELOAD=/tmp/hook_nvram.so /bin/httpd 调试模式启动 , 然后 gdb-multiarch -x gdb.txt 程序就断在了 _start 开始处

然后使用 cyclic 生成测试字符串 , 使用 PYTHON POC 脚本的 HTTP 协议进行发送
程序正常断在 sprintf@plt 函数处

查看目的地址 0x407ff9f8 正常情况下的栈数据

sprintf 后的栈数据如下

单步运行到函数的结束处

可以看到最后是 r11 0x407ffa34 ◂— 0x61616a61 ('ajaa') 的数据被弹出到 PC 寄存器
偏移地址为 35 处
如果调用 system 函数, 需要首先设置 R0 的值指向需要执行的命令地址
但是 system 的函数汇编代码如下
system 运行结束后 ,弹出到 PC 寄存器的是 LR 的值 ,如果不控制 LR 的值程序就会崩溃,现在需要找一个 BL system 的指令来自动设置 LR 的值
libcommon.so 中的 doSystemCmd 函数正好符合, 然后 flush_dns_cache 的调用 doSystemCmd 代码如下
这段代码非常符合参数的设置,首先设置 R3 的地址指向需要执行的命令 telnetd -l /bin/sh
让弹出到 PC 寄存器的地址指向 libc.so.0 的 _exit 函数
使用 ROPgadget 查找设置 R3 值的指令
选取 0x00015c20 作为设置 R3 值的指令
结合 vmmap 和 info sharedlibrary 计算 libcommon.so 的基址为 0x40854000 , libc.so.0 的基址为 0x409EB000

栈的数据结构如下

开启 telnetd 服务还需要挂载 devpts , 不然程序会提示 telnetd: can't find free pty
运行 POC 后查看运行的进程信息
telnet 192.168.3.3 进行测试
POC 代码如下
int __fastcall formSetSpeedWan(_DWORD *a1)
{
_DWORD nptr[8];
_DWORD s[8];
void *v5;
char *nptr_1;
char *nptr_2;
int v8;
memset(s, 0, sizeof(s));
memset(nptr, 0, sizeof(nptr));
v8 = 0;
nptr_2 = (char *)sub_2BA8C((int)a1, (int)"speed_dir", (int)"0");
nptr_1 = (char *)sub_2BA8C((int)a1, (int)"ucloud_enable", (int)"0");
...
...
...
sprintf((char *)s, "{\"errCode\":%d,\"speed_dir\":%s}", v8, nptr_2);
return sub_9CCBC(a1, (const char *)s);
}
int __fastcall formSetSpeedWan(_DWORD *a1)
{
_DWORD nptr[8];
_DWORD s[8];
void *v5;
char *nptr_1;
char *nptr_2;
int v8;
memset(s, 0, sizeof(s));
memset(nptr, 0, sizeof(nptr));
v8 = 0;
nptr_2 = (char *)sub_2BA8C((int)a1, (int)"speed_dir", (int)"0");
nptr_1 = (char *)sub_2BA8C((int)a1, (int)"ucloud_enable", (int)"0");
...
...
...
sprintf((char *)s, "{\"errCode\":%d,\"speed_dir\":%s}", v8, nptr_2);
return sub_9CCBC(a1, (const char *)s);
}
binwalk US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
64 0x40 TRX firmware header, little endian, image size: 10629120 bytes, CRC32: 0xAB135998, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x1C9E58, rootfs offset: 0x0
92 0x5C LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4585280 bytes
1875608 0x1C9E98 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 8749996 bytes, 928 inodes, blocksize: 131072 bytes, created: 2017-05-26 02:03:03
binwalk US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
64 0x40 TRX firmware header, little endian, image size: 10629120 bytes, CRC32: 0xAB135998, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x1C9E58, rootfs offset: 0x0
92 0x5C LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4585280 bytes
1875608 0x1C9E98 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 8749996 bytes, 928 inodes, blocksize: 131072 bytes, created: 2017-05-26 02:03:03
binwalk -e -1 US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
64 0x40 TRX firmware header, little endian, image size: 10629120 bytes, CRC32: 0xAB135998, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x1C9E58, rootfs offset: 0x0
92 0x5C LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4585280 bytes
1875608 0x1C9E98 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 8749996 bytes, 928 inodes, blocksize: 131072 bytes, created: 2017-05-26 02:03:03
binwalk -e -1 US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
64 0x40 TRX firmware header, little endian, image size: 10629120 bytes, CRC32: 0xAB135998, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x1C9E58, rootfs offset: 0x0
92 0x5C LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4585280 bytes
1875608 0x1C9E98 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 8749996 bytes, 928 inodes, blocksize: 131072 bytes, created: 2017-05-26 02:03:03
mkdir -p /var/etc
mkdir -p /var/media
mkdir -p /var/webroot
mkdir -p /var/etc/iproute
mkdir -p /var/run
cp -rf /etc_ro/* /etc/
cp -rf /webroot_ro/* /webroot/
mkdir -p /var/etc/upan
mount -a
mount -t ramfs /dev
mkdir /dev/pts
mount -t devpts devpts /dev/pts
mount -t tmpfs none /var/etc/upan -o size=2M
mdev -s
mkdir /var/run
mkdir -p /var/etc
mkdir -p /var/media
mkdir -p /var/webroot
mkdir -p /var/etc/iproute
mkdir -p /var/run
cp -rf /etc_ro/* /etc/
cp -rf /webroot_ro/* /webroot/
mkdir -p /var/etc/upan
mount -a
mount -t ramfs /dev
mkdir /dev/pts
mount -t devpts devpts /dev/pts
mount -t tmpfs none /var/etc/upan -o size=2M
mdev -s
mkdir /var/run
ip link add name br0 type bridge
ip link set br0 up
ip addr add 192.168.3.3/24 dev br0
ip link add name br0 type bridge
ip link set br0 up
ip addr add 192.168.3.3/24 dev br0
00000000 struct CMDINFO
00000000 {
00000000
00000000 int cmd;
00000004 char name[512];
00000004
00000204 char value[1500];
00000204
000007E0 };
00000000 struct CMDINFO
00000000 {
00000000
00000000 int cmd;
00000004 char name[512];
00000004
00000204 char value[1500];
00000204
000007E0 };
import socket
import os
import time
import configparser
import struct
import hexdump
SOCKET_PATH = '/kctf/tenda/tendaac15/var/cfm_socket'
BUFFER_SIZE = 2016
config = configparser.RawConfigParser()
config.read("default.ini", encoding='utf-8')
def parse_cmdinfo(data):
if len(data) != 2016:
raise ValueError("数据包大小不正确,应为2016字节")
cmd, name_bytes, value_bytes = struct.unpack('<i512s1500s', data)
name_end = name_bytes.find(b'\x00')
if name_end != -1:
name_bytes = name_bytes[:name_end]
value_end = value_bytes.find(b'\x00')
if value_end != -1:
value_bytes = value_bytes[:value_end]
name = name_bytes.rstrip(b'\x00').decode('ascii', errors='ignore')
value = value_bytes.rstrip(b'\x00').decode('ascii', errors='ignore')
return cmd, name, value
def pack_cmdinfo(cmd, name, value):
name_bytes = name.encode('ascii')[:511]
value_bytes = value.encode('ascii')[:1499]
name_padded = name_bytes + b'\x00' * (512 - len(name_bytes))
value_padded = value_bytes + b'\x00' * (1500 - len(value_bytes))
packed = struct.pack('<i512s1500s', cmd, name_padded, value_padded)
return packed
def run_uds_server():
try:
if os.path.exists(SOCKET_PATH):
os.remove(SOCKET_PATH)
except OSError as e:
print(f"{e}")
return
server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
server_socket.bind(SOCKET_PATH)
server_socket.listen(5)
os.chmod(SOCKET_PATH, 0o666)
while True:
conn, addr = server_socket.accept()
try:
while True:
received_data = conn.recv(BUFFER_SIZE)
if received_data:
cmd, name, value = parse_cmdinfo(received_data)
print(f"received_data-> cmd: {cmd} name: {name} value: {value}")
if value != "":
config.set('DEFAULT', name, value)
cmd = cmd + 1
value = config['DEFAULT'].get(name, '')
response_data = pack_cmdinfo(cmd, name, value)
print(f"response_data-> cmd: {cmd} name: {name} value: {value}")
conn.sendall(response_data)
else:
time.sleep(1)
except Exception as e:
print(f"{e}")
finally:
conn.close()
except KeyboardInterrupt:
print("Ctrl+C...")
except Exception as e:
print(f"{e}")
finally:
with open('default.ini', 'w', encoding='utf-8') as configfile:
config.write(configfile)
server_socket.close()
if os.path.exists(SOCKET_PATH):
os.remove(SOCKET_PATH)
if __name__ == "__main__":
run_uds_server()
import socket
import os
import time
import configparser
import struct
import hexdump
SOCKET_PATH = '/kctf/tenda/tendaac15/var/cfm_socket'
BUFFER_SIZE = 2016
config = configparser.RawConfigParser()
config.read("default.ini", encoding='utf-8')
def parse_cmdinfo(data):
if len(data) != 2016:
raise ValueError("数据包大小不正确,应为2016字节")
cmd, name_bytes, value_bytes = struct.unpack('<i512s1500s', data)
name_end = name_bytes.find(b'\x00')
if name_end != -1:
name_bytes = name_bytes[:name_end]
value_end = value_bytes.find(b'\x00')
if value_end != -1:
value_bytes = value_bytes[:value_end]
name = name_bytes.rstrip(b'\x00').decode('ascii', errors='ignore')
value = value_bytes.rstrip(b'\x00').decode('ascii', errors='ignore')
return cmd, name, value
def pack_cmdinfo(cmd, name, value):
name_bytes = name.encode('ascii')[:511]
value_bytes = value.encode('ascii')[:1499]
name_padded = name_bytes + b'\x00' * (512 - len(name_bytes))
value_padded = value_bytes + b'\x00' * (1500 - len(value_bytes))
packed = struct.pack('<i512s1500s', cmd, name_padded, value_padded)
return packed
def run_uds_server():
try:
if os.path.exists(SOCKET_PATH):
os.remove(SOCKET_PATH)
except OSError as e:
print(f"{e}")
return
server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
server_socket.bind(SOCKET_PATH)
server_socket.listen(5)
os.chmod(SOCKET_PATH, 0o666)
while True:
conn, addr = server_socket.accept()
try:
while True:
received_data = conn.recv(BUFFER_SIZE)
if received_data:
cmd, name, value = parse_cmdinfo(received_data)
print(f"received_data-> cmd: {cmd} name: {name} value: {value}")
if value != "":
config.set('DEFAULT', name, value)
cmd = cmd + 1
value = config['DEFAULT'].get(name, '')
response_data = pack_cmdinfo(cmd, name, value)
print(f"response_data-> cmd: {cmd} name: {name} value: {value}")
conn.sendall(response_data)
else:
time.sleep(1)
except Exception as e:
print(f"{e}")
finally:
conn.close()
except KeyboardInterrupt:
print("Ctrl+C...")
except Exception as e:
print(f"{e}")
finally:
with open('default.ini', 'w', encoding='utf-8') as configfile:
config.write(configfile)
server_socket.close()
if os.path.exists(SOCKET_PATH):
os.remove(SOCKET_PATH)
if __name__ == "__main__":
run_uds_server()
001114D0 bcm_nvram_set .dynsym
001114EC bcm_nvram_match .dynsym
00111540 bcm_nvram_get .dynsym
00111660 bcm_nvram_commit .dynsym
001114D0 bcm_nvram_set .dynsym
001114EC bcm_nvram_match .dynsym
00111540 bcm_nvram_get .dynsym
00111660 bcm_nvram_commit .dynsym
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#define ROUTE_CONFIG_PATH "/tmp/nvram_default.cfg"
#define MAX_LINE_LENGTH 256
#define MAX_KEY_LENGTH 64
#define MAX_VALUE_LENGTH 128
#define isspace(c) my_isspace(c)
int my_isspace(int c) {
return (c == ' ' ||
c == '\t' ||
c == '\n' ||
c == '\r' ||
c == '\f' ||
c == '\v');
}
int bcm_nvram_set(const char *key, const char *value)
{
FILE *fp_read, *fp_write;
char line[MAX_LINE_LENGTH];
char config_key[MAX_KEY_LENGTH];
char config_value[MAX_VALUE_LENGTH];
char temp_file[] = "/tmp/nvram.ini.tmp";
int found = 0;
int result = 0;
if (!key || strlen(key) == 0 || !value) {
fprintf(stderr, "Error: Invalid key or value parameter\n");
return 1;
}
if (strchr(key, '=') != NULL || strchr(key, '\n') != NULL) {
fprintf(stderr, "Error: Key contains invalid characters\n");
return 1;
}
if (strchr(value, '\n') != NULL) {
fprintf(stderr, "Error: Value contains newline character\n");
return 1;
}
fp_read = fopen(ROUTE_CONFIG_PATH, "r");
fp_write = fopen(temp_file, "w");
if (fp_write == NULL) {
fprintf(stderr, "Error: Cannot create temp file: %s\n", strerror(errno));
if (fp_read) fclose(fp_read);
return 1;
}
if (fp_read != NULL) {
while (fgets(line, sizeof(line), fp_read) != NULL) {
char *trimmed_line = line;
bool is_comment_or_empty = false;
line[strcspn(line, "\n")] = '\0';
if (line[0] == '\0' || line[0] == '#') {
is_comment_or_empty = true;
}
while (isspace((unsigned char)*trimmed_line)) {
trimmed_line++;
}
if (*trimmed_line == '\0') {
is_comment_or_empty = true;
}
if (is_comment_or_empty) {
fprintf(fp_write, "%s\n", line);
continue;
}
if (sscanf(trimmed_line, "%63[^=]=%127[^\n]", config_key, config_value) >= 1) {
char *trimmed_key = config_key + strlen(config_key) - 1;
while (trimmed_key > config_key && isspace((unsigned char)*trimmed_key)) {
*trimmed_key = '\0';
trimmed_key--;
}
if (strcmp(config_key, key) == 0) {
fprintf(fp_write, "%s=%s\n", key, value);
found = 1;
} else {
fprintf(fp_write, "%s\n", line);
}
} else {
fprintf(fp_write, "%s\n", line);
}
}
fclose(fp_read);
}
if (!found) {
fprintf(fp_write, "%s=%s\n", key, value);
}
fclose(fp_write);
if (rename(temp_file, ROUTE_CONFIG_PATH) != 0) {
fprintf(stderr, "Error: Cannot replace config file: %s\n", strerror(errno));
remove(temp_file);
result = 1;
}
printf("[DEBUG] Setting config: %s = %s\n", key, value);
return result;
}
char *bcm_nvram_get(const char *key)
{
FILE *fp;
char line[MAX_LINE_LENGTH];
char config_key[MAX_KEY_LENGTH];
char config_value[MAX_VALUE_LENGTH];
char *result = NULL;
int found = 0;
if (!key || strlen(key) == 0) {
fprintf(stderr, "Error: Invalid key parameter\n");
return NULL;
}
fp = fopen(ROUTE_CONFIG_PATH, "r");
if (fp == NULL) {
fprintf(stderr, "Error: Cannot open config file %s: %s\n", ROUTE_CONFIG_PATH, strerror(errno));
return NULL;
}
while (fgets(line, sizeof(line), fp) != NULL) {
line[strcspn(line, "\n")] = '\0';
if (line[0] == '\0' || line[0] == '#') {
continue;
}
char *trimmed_line = line;
while (isspace((unsigned char)*trimmed_line)) {
trimmed_line++;
}
if (*trimmed_line == '\0') {
continue;
}
if (sscanf(trimmed_line, "%63[^=]=%127[^\n]", config_key, config_value) == 2) {
char *trimmed_key = config_key + strlen(config_key) - 1;
while (trimmed_key > config_key && isspace((unsigned char)*trimmed_key)) {
*trimmed_key = '\0';
trimmed_key--;
}
char *trimmed_value = config_value;
while (isspace((unsigned char)*trimmed_value)) {
trimmed_value++;
}
char *end_value = trimmed_value + strlen(trimmed_value) - 1;
while (end_value > trimmed_value && isspace((unsigned char)*end_value)) {
*end_value = '\0';
end_value--;
}
if (strcmp(config_key, key) == 0) {
result = malloc(strlen(trimmed_value) + 1);
if (result) {
strcpy(result, trimmed_value);
found = 1;
} else {
fprintf(stderr, "Error: Memory allocation failed\n");
}
break;
}
}
}
fclose(fp);
if (result) {
printf("[DEBUG] Getting config: %s = %s\n", key, result);
} else {
result = "";
printf("[DEBUG] Config not found: %s\n", key);
}
if (!found) {
result = "";
}
return result;
}
bool bcm_nvram_match(const char *key, const char *value)
{
bool result = false;
char *config_value = bcm_nvram_get(key);
if (config_value && value) {
result = (strcmp(config_value, value) == 0);
}
printf("[DEBUG] Match config: %s = %s, result: %s\n",
key, value, result ? "true" : "false");
if (config_value) {
free(config_value);
}
return result;
}
int bcm_nvram_commit(void)
{
#ifdef __linux__
sync();
#endif
printf("[DEBUG] Save config\n");
return 0;
}
int bcm_nvram_unset(const char *key)
{
FILE *fp_read, *fp_write;
char line[MAX_LINE_LENGTH];
char config_key[MAX_KEY_LENGTH];
char config_value[MAX_VALUE_LENGTH];
char temp_file[] = "/tmp/route.cfg.tmp";
int found = 0;
int result = 0;
if (!key || strlen(key) == 0) {
fprintf(stderr, "Error: Invalid key parameter\n");
return 1;
}
fp_read = fopen(ROUTE_CONFIG_PATH, "r");
if (fp_read == NULL) {
printf("[DEBUG] Unset config: %s (file not exists)\n", key);
return 0;
}
fp_write = fopen(temp_file, "w");
if (fp_write == NULL) {
fprintf(stderr, "Error: Cannot create temp file: %s\n", strerror(errno));
fclose(fp_read);
return 1;
}
while (fgets(line, sizeof(line), fp_read) != NULL) {
char *trimmed_line = line;
bool is_comment_or_empty = false;
line[strcspn(line, "\n")] = '\0';
if (line[0] == '\0' || line[0] == '#') {
is_comment_or_empty = true;
}
while (isspace((unsigned char)*trimmed_line)) {
trimmed_line++;
}
if (*trimmed_line == '\0') {
is_comment_or_empty = true;
}
if (is_comment_or_empty) {
fprintf(fp_write, "%s\n", line);
continue;
}
if (sscanf(trimmed_line, "%63[^=]=%127[^\n]", config_key, config_value) == 2) {
char *trimmed_key = config_key + strlen(config_key) - 1;
while (trimmed_key > config_key && isspace((unsigned char)*trimmed_key)) {
*trimmed_key = '\0';
trimmed_key--;
}
if (strcmp(config_key, key) == 0) {
found = 1;
continue;
} else {
fprintf(fp_write, "%s\n", line);
}
} else {
fprintf(fp_write, "%s\n", line);
}
}
fclose(fp_read);
fclose(fp_write);
if (rename(temp_file, ROUTE_CONFIG_PATH) != 0) {
fprintf(stderr, "Error: Cannot replace config file: %s\n", strerror(errno));
remove(temp_file);
result = 1;
}
printf("[DEBUG] Unset config: %s %s\n", key, found ? "deleted" : "not found");
return result;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#define ROUTE_CONFIG_PATH "/tmp/nvram_default.cfg"
#define MAX_LINE_LENGTH 256
#define MAX_KEY_LENGTH 64
#define MAX_VALUE_LENGTH 128
#define isspace(c) my_isspace(c)
int my_isspace(int c) {
return (c == ' ' ||
c == '\t' ||
c == '\n' ||
c == '\r' ||
c == '\f' ||
c == '\v');
}
int bcm_nvram_set(const char *key, const char *value)
{
FILE *fp_read, *fp_write;
char line[MAX_LINE_LENGTH];
char config_key[MAX_KEY_LENGTH];
char config_value[MAX_VALUE_LENGTH];
char temp_file[] = "/tmp/nvram.ini.tmp";
int found = 0;
int result = 0;
if (!key || strlen(key) == 0 || !value) {
fprintf(stderr, "Error: Invalid key or value parameter\n");
return 1;
}
if (strchr(key, '=') != NULL || strchr(key, '\n') != NULL) {
fprintf(stderr, "Error: Key contains invalid characters\n");
return 1;
}
if (strchr(value, '\n') != NULL) {
fprintf(stderr, "Error: Value contains newline character\n");
return 1;
}
fp_read = fopen(ROUTE_CONFIG_PATH, "r");
fp_write = fopen(temp_file, "w");
if (fp_write == NULL) {
fprintf(stderr, "Error: Cannot create temp file: %s\n", strerror(errno));
if (fp_read) fclose(fp_read);
return 1;
}
if (fp_read != NULL) {
while (fgets(line, sizeof(line), fp_read) != NULL) {
char *trimmed_line = line;
bool is_comment_or_empty = false;
line[strcspn(line, "\n")] = '\0';
if (line[0] == '\0' || line[0] == '#') {
is_comment_or_empty = true;
}
while (isspace((unsigned char)*trimmed_line)) {
trimmed_line++;
}
if (*trimmed_line == '\0') {
is_comment_or_empty = true;
}
if (is_comment_or_empty) {
fprintf(fp_write, "%s\n", line);
continue;
}
if (sscanf(trimmed_line, "%63[^=]=%127[^\n]", config_key, config_value) >= 1) {
char *trimmed_key = config_key + strlen(config_key) - 1;
while (trimmed_key > config_key && isspace((unsigned char)*trimmed_key)) {
*trimmed_key = '\0';
trimmed_key--;
}
if (strcmp(config_key, key) == 0) {
fprintf(fp_write, "%s=%s\n", key, value);
found = 1;
} else {
fprintf(fp_write, "%s\n", line);
}
} else {
fprintf(fp_write, "%s\n", line);
}
}
fclose(fp_read);
}
if (!found) {
fprintf(fp_write, "%s=%s\n", key, value);
}
fclose(fp_write);
if (rename(temp_file, ROUTE_CONFIG_PATH) != 0) {
fprintf(stderr, "Error: Cannot replace config file: %s\n", strerror(errno));
remove(temp_file);
result = 1;
}
printf("[DEBUG] Setting config: %s = %s\n", key, value);
return result;
}
char *bcm_nvram_get(const char *key)
{
FILE *fp;
char line[MAX_LINE_LENGTH];
char config_key[MAX_KEY_LENGTH];
char config_value[MAX_VALUE_LENGTH];
char *result = NULL;
int found = 0;
if (!key || strlen(key) == 0) {
fprintf(stderr, "Error: Invalid key parameter\n");
return NULL;
}
fp = fopen(ROUTE_CONFIG_PATH, "r");
if (fp == NULL) {
fprintf(stderr, "Error: Cannot open config file %s: %s\n", ROUTE_CONFIG_PATH, strerror(errno));
return NULL;
}
while (fgets(line, sizeof(line), fp) != NULL) {
line[strcspn(line, "\n")] = '\0';
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2026-1-4 13:51
被易之生生编辑
,原因: