漏洞情报
CVE-2024-53375:TP-Link Archer系列路由器存在一个已认证的远程代码执行(RCE)漏洞。该漏洞存在于TP-Link提供的HomeShield功能中的"tmp_get_sites"函数内。即使未启用HomeShield功能,此漏洞仍可被利用。
本文采用Archer AXE75(EU)_V1_1.2.2 Build 20240827固件进行分析、复现和挖掘
信息收集
拿到固件,binwalk解包,进入squashfs-root目录下
看到是ARM架构32位
我们找到web服务器usr/sbin/uhttpd
uhttpd分析
main函数
找到uhttpd的main
函数。该函数用于启动一个轻量级Web服务器
以下按代码逻辑顺序分析各部分功能和实现细节,跳过不需要部分,命令行参数解析部分重点展开。
命令行参数解析
while ( 1 )
{
v11 = getopt(argc, (char *const *)argv, "fSDRC:K:N:E:I:p:s:h:c:l:L:d:r:m:n:x:i:t:T:A:u:U:");
v12 = v11;
if ( v11 <= 0 )
break;
switch ( v11 )
{
case 'A': ... case 'C': ... case 'D': ... /* 其他选项 */
default:
v37 = "Usage: %s -p [addr:]port [-h docroot]
" /* 使用说明 */
v36 = *argv;
goto LABEL_91;
}
}
- 功能:解析命令行参数,配置服务器的运行参数。
- 逻辑:
- 使用
getopt
循环处理选项,选项字符串为"fSDRC:K:N:E:I:p:s:h:c:l:L:d:r:m:n:x:i:t:T:A:u:U:"
。 - 将当前选项存入
v11
(并复制到v12
),若v11 <= 0
,退出循环。 - 使用
switch
根据选项字符执行对应逻辑。 - 无效选项打印使用说明并退出。
- 使用
- 选项字符串:
- 无参数选项:
f
,S
,D
,R
,l
,L
。 - 带参数选项:
C:
,K:
,N:
,E:
,I:
,p:
,s:
,h:
,c:
,d:
,r:
,m:
,n:
,x:
,i:
,t:
,T:
,A:
,u:
,U:
。 optarg
存储带参数选项的值。
- 无参数选项:
各选项详细分析
以下部分分析switch
中的选项逻辑和功能:
-A
:设置最大活动连接数
case 'A':
v46 = atoi((const char *)optarg);
p_tm_isdst = &filename[93].tm_isdst;
goto LABEL_108;
LABEL_108:
*p_tm_isdst = v46;
continue;
- 功能:设置服务器的最大活动连接数。
- 逻辑:将
optarg
转换为整数(atoi
),存储在filename[93].tm_isdst
。 - 细节:影响服务器的并发处理能力,复用
struct tm
字段存储配置。
-R
:启用RFC1918过滤
case 'R':
p_tm_year = &filename[93].tm_yday;
goto LABEL_104;
- 功能:启用RFC1918私有地址过滤。
- 逻辑:设置
filename[93].tm_yday=1
。 - 细节:限制私有IP访问。
-T
:设置网络超时
case 'T':
v46 = atoi((const char *)optarg);
p_tm_isdst = &filename[93].tm_wday;
goto LABEL_108;
- 功能:设置网络超时时间(秒)。
- 逻辑:将
optarg
转换为整数,存储在filename[93].tm_wday
。 - 细节:默认30秒。
-U
:设置ubus socket路径
case 'U':
v48 = (int)optarg;
p_tm_min = (struct tm *)&filename[94].tm_min;
goto LABEL_110;
LABEL_110:
p_tm_min->tm_sec = v48;
continue;
- 功能:覆盖ubus socket路径。
- 逻辑:将
optarg
存储在filename[94].tm_min
。 - 细节:默认路径为
/var Rats/ubus.sock
。
-h
:设置文档根目录
case 'h':
if ( realpath((const char *)optarg, (char *)filename) )
continue;
v39 = (const char *)optarg;
v40 = (FILE *)stderr;
v41 = _errno_location();
v42 = strerror(*v41);
fprintf(v40, "Error: Invalid directory %s: %s
", v39, v42);
goto LABEL_82;
- 功能:指定文档根目录。
- 逻辑:
- 使用
realpath
解析optarg
到filename
,验证路径。 - 如果无效,打印错误并退出。
- 使用
- 细节:默认根目录为
.
。
-n
:设置最大并发请求数
case 'n':
v46 = atoi((const char *)optarg);
p_tm_isdst = &filename[93].tm_gmtoff;
goto LABEL_108;
- 功能:设置最大并发请求数。
- 逻辑:将
optarg
转换为整数,存储在filename[93].tm_gmtoff
。 - 细节:默认值8。
-p
、-s
:绑定端口(普通或TLS)
case 'p':
case 's':
memset(v107, 0, sizeof(v107));
v16 = (const char *)optarg;
v17 = strrchr((const char *)optarg, 58);
if ( v17 )
{
v18 = v17 - v16;
v19 = *(unsigned __int8 *)v16 == 91;
if ( v16 >= v17 )
v19 = 0;
if ( v19 && *(v17 - 1) == 93 )
{
v18 -= 2;
v20 = v16 + 1;
if ( v18 >= 0x80 )
v18 = 128;
}
else
{
v20 = v16;
if ( v18 >= 0x80 )
v18 = 128;
}
v16 = v17 + 1;
memcpy(v107, v20, v18);
}
if ( v12 != 115 )
goto LABEL_38;
if ( sub_149F8(filename) )
{
fprintf((FILE *)stderr, "Notice: TLS support is disabled, ignoring '-s %s'
", optarg);
continue;
}
v98 = 1;
LABEL_38:
v101 = 1;
if ( v107[0] )
v21 = v107;
else
v21 = 0;
pai[0] = 0;
v22 = getaddrinfo(v21, v16, &req, pai);
/* 后续网络初始化逻辑 */
- 功能:
-p
:绑定普通HTTP端口。-s
:绑定HTTPS端口(需TLS支持)。
- 逻辑:
- 解析
optarg
格式为[addr]:port
(支持IPv6)。 - 分割地址(存入
v107
)和端口(存入v16
)。 - 对于
-s
,检查TLS支持,若不支持则忽略并设置v98=1
。 - 调用
getaddrinfo
解析地址,创建套接字,绑定端口。
- 解析
- 细节:
- 支持IPv4/IPv6,地址为空时绑定所有接口。
- TLS端口需
-C
和-K
配合。
-r
:设置认证领域
case 'r':
v48 = optarg;
p_tm_min = (struct tm *)&filename[93].tm_min;
goto LABEL_110;
- 功能:设置基本认证的领域名称。
- 逻辑:将
optarg
存储在filename[93].tm_min
。 - 细节:默认
"Protected Area"
。
-t
:设置脚本超时
case 't':
v46 = atoi((const char *)optarg);
p_tm_isdst = &filename[94].tm_wday;
goto LABEL_108;
- 功能:设置CGI/Lua/ubus脚本超时时间(秒)。
- 逻辑:将
optarg
转换为整数,存储在filename[94].tm_wday
。 - 细节:默认60秒。
-x
:设置CGI处理URL前缀
case 'x':
v48 = optarg;
p_tm_min = (struct tm *)&filename[93].tm_zone;
goto LABEL_110;
- 功能:指定CGI处理器的URL前缀。
- 逻辑:将
optarg
存储在filename[93].tm_zone
。 - 细节:默认
/cgi-bin
。
默认情况
default:
v37 = "Usage: %s -p [addr:]port [-h docroot]
" /* 详细使用说明 */
v36 = *argv;
goto LABEL_91;
LABEL_91:
fprintf(v38, v37, v36);
goto LABEL_82;
- 功能:处理无效选项,打印使用说明并退出。
- 逻辑:输出程序用法,设置退出状态
v35=1
。
参数验证
if ( v9 <= 1 )
v13 = v98;
else
v13 = 0;
if ( v13 )
{
v14 = "Error: Missing private key or certificate file
";
v15 = (FILE *)stderr;
goto LABEL_81;
}
if ( !v96 )
{
v14 = "Error: No sockets bound, unable to continue
";
v15 = (FILE *)stderr;
goto LABEL_81;
}
- 功能:验证命令行参数完整性。
- 逻辑:
- 如果启用了
-s
(v98=1
)且v9<=1
(缺少证书或私钥),报错退出。 - 如果
v96=0
(无成功绑定的套接字),报错退出。
- 如果启用了
- 细节:
v9
确保TLS配置完整(需-C
和-K
)。v96
记录绑定的套接字数量。
网络初始化
v25 = pai[0];
v97 = 0;
while ( 2 )
{
if ( v25 )
{
v26 = socket(v25->ai_family, v25->ai_socktype, v25->ai_protocol);
v27 = v26;
if ( setsockopt(v26, 1, 2, &v101, 4u) )
goto LABEL_53;
if ( filename[93].tm_isdst > 0 )
{
v104 = 3;
tm_isdst = filename[93].tm_isdst;
optval = 1;
if ( setsockopt(v27, 1, 9, &v101, 4u)
|| setsockopt(v27, 6, 4, &optval, 4u)
|| setsockopt(v27, 6, 5, &tm_isdst, 4u)
|| setsockopt(v27, 6, 6, &v104, 4u) )
{
v29 = (FILE *)stderr;
v30 = _errno_location();
v31 = strerror(*v30);
fprintf(v29, "Notice: Unable to enable TCP keep-alive: %s
", v31);
}
}
if ( v25->ai_family == 10 && setsockopt(v27, 41, 26, &v101, 4u) == -1 )
goto LABEL_53;
if ( bind(v27, v25->ai_addr, v25->ai_addrlen) == -1 )
goto LABEL_54;
if ( listen(v27, 64) == -1 )
goto LABEL_54;
v32 = uh_listener_add(v27, filename);
if ( v32 )
{
if ( v12 == 115 )
p_tm_mon = &filename[94].tm_mon;
else
p_tm_mon = 0;
if ( v12 == 115 )
p_tm_mon = (int *)p_tm_mon[6];
*(_DWORD *)(v32 + 52) = p_tm_mon;
v34 = fcntl(v27, 1);
fcntl(v27, 2, v34 | 1);
uh_ufd_add(v32, sub_14858, 65);
++v97;
}
else
{
fputs("uh_listener_add(): Failed to allocate memory
", (FILE *)stderr);
}
if ( v27 > 0 )
close(v27);
LABEL_51:
v25 = v25->ai_next;
continue;
}
break;
}
freeaddrinfo(pai[0]);
v96 += v97;
- 功能:创建TCP套接字,绑定地址和端口,启动监听。
- 逻辑:
- 使用
getaddrinfo
解析地址(从-p
或-s
)。 - 为每个地址创建套接字,设置
SO_REUSEADDR
和TCP keep-alive(若启用)。 - 调用
bind
和listen
,最大连接数64。 - 使用
uh_listener_add
注册监听器到事件循环。 - 增加
v97
(成功绑定计数),释放地址信息。 - 更新
v96
(总绑定计数)。
- 使用
- 细节:
- 支持IPv4/IPv6,TCP keep-alive增强连接稳定性。
uh_ufd_add
将套接字加入事件循环,回调函数为sub_14858
,后续如果想进一步分析http服务从这里入手。
服务器配置
uh_get_operation_mode(&dword_2B45C);
uh_get_local_addr(&dword_2B45C);
uh_get_admin_config(&dword_2B45C);
if ( filename[93].tm_gmtoff <= 0 )
filename[93].tm_gmtoff = 8;
if ( filename[93].tm_wday <= 0 )
filename[93].tm_wday = 30;
if ( !uh_index_files )
{
if ( byte_2B41C[0] )
{
memset(pai, 0, 0x80u);
sprintf((char *)pai, "index.%shtml", byte_2B41C);
uh_index_add(pai);
}
uh_index_add("index.html");
uh_index_add("index.htm");
uh_index_add("default.html");
uh_index_add("default.htm");
}
if ( filename[94].tm_wday <= 0 )
filename[94].tm_wday = 60;
if ( !filename[93].tm_zone )
filename[93].tm_zone = "/cgi-bin";
- 功能:设置服务器运行模式、地址和默认配置。
-
逻辑: