背景

因为要将es接入ldap认证,而该功能是收费版本,所以我这里选择破解白金版

原理

license中有个signature字段,ES会根据这个字段判断License是否被篡改。只要取消ES的这个判断逻辑,就可以随便篡改License,达到激活的目的了。

下面的示例将会以官方 ES Docker 镜像 7.14.0 版本进行破解。原则上支持任意版本破解

破解流程

下载对应elasticsearch源文件

地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch

下载对应版本,之后会涉及到一些编译文件的操作,所以我这里的环境使用wsl+windows,理论上下载windows或者linux版本皆可

解压文件

我这里解压在D:\elasticsearch-7.14.0,主要修改jar包:D:/elasticsearch-7.14.0/modules/x-pack-core/x-pack-core-7.14.0.jar

取出源文件

接下来需要将几个jar包内的class文件反编译出来,这里使用Luyten

地址:https://github.com/deathmarine/Luyten/releases

注:这里exe程序有些问题,所以我们这里下载jar后缀文件

问题参考:https://github.com/deathmarine/Luyten/issues/273

启动命令:java -jar luyten-0.5.4.jar记得使用jdk1.8并且配置好JAVA_HOME

另存源文件

打开x-pack-core-7.14.0.jar这个文件:

定位到两个文件:然后点击File–Save As 另存为java源码文件,我这里将两个文件保存在D:\elasticsearch-7.14.0

org.elasticsearch.license/LicenseVerifier.class 另存为:LicenseVerifier.java

org.elasticsearch.xpack.core/XPackBuild.class另存为:XPackBuild.java

源码修改

LicenseVerifier.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package org.elasticsearch.license;

import java.nio.*;
import org.elasticsearch.common.bytes.*;
import java.security.*;
import java.util.*;
import org.elasticsearch.common.xcontent.*;
import org.apache.lucene.util.*;
import org.elasticsearch.core.internal.io.*;
import java.io.*;

public class LicenseVerifier
{
public static boolean verifyLicense(final License license, final byte[] publicKeyData) {
/* 注释掉这一大段
byte[] signedContent = null;
byte[] publicKeyFingerprint = null;
try {
final byte[] signatureBytes = Base64.getDecoder().decode(license.signature());
final ByteBuffer byteBuffer = ByteBuffer.wrap(signatureBytes);
final int version = byteBuffer.getInt();
final int magicLen = byteBuffer.getInt();
final byte[] magic = new byte[magicLen];
byteBuffer.get(magic);
final int hashLen = byteBuffer.getInt();
publicKeyFingerprint = new byte[hashLen];
byteBuffer.get(publicKeyFingerprint);
final int signedContentLen = byteBuffer.getInt();
signedContent = new byte[signedContentLen];
byteBuffer.get(signedContent);
final XContentBuilder contentBuilder = XContentFactory.contentBuilder(XContentType.JSON);
license.toXContent(contentBuilder, (ToXContent.Params)new ToXContent.MapParams((Map)Collections.singletonMap("license_spec_view", "true")));
final Signature rsa = Signature.getInstance("SHA512withRSA");
rsa.initVerify(CryptUtils.readPublicKey(publicKeyData));
final BytesRefIterator iterator = BytesReference.bytes(contentBuilder).iterator();
BytesRef ref;
while ((ref = iterator.next()) != null) {
rsa.update(ref.bytes, ref.offset, ref.length);
}
return rsa.verify(signedContent);
}
catch (IOException ex) {}
catch (NoSuchAlgorithmException ex2) {}
catch (SignatureException ex3) {}
catch (InvalidKeyException e) {
throw new IllegalStateException(e);
}
finally {
if (signedContent != null) {
Arrays.fill(signedContent, (byte)0);
}
}
*/
return true; // 增加这行
}

public static boolean verifyLicense(final License license) {
/* 注释掉这一大段
byte[] publicKeyBytes;
try {
final InputStream is = LicenseVerifier.class.getResourceAsStream("/public.key");
try {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
Streams.copy(is, (OutputStream)out);
publicKeyBytes = out.toByteArray();
if (is != null) {
is.close();
}
}
catch (Throwable t) {
if (is != null) {
try {
is.close();
}
catch (Throwable t2) {
t.addSuppressed(t2);
}
}
throw t;
}
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
return verifyLicense(license, publicKeyBytes);
*/
return true; // 增加这行
}
}

XPackBuild.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package org.elasticsearch.xpack.core;

import org.elasticsearch.common.io.*;
import java.net.*;
import org.elasticsearch.common.*;
import java.nio.file.*;
import java.io.*;
import java.util.jar.*;

public class XPackBuild
{
public static final XPackBuild CURRENT;
private String shortHash;
private String date;

@SuppressForbidden(reason = "looks up path of xpack.jar directly")
static Path getElasticsearchCodebase() {
final URL url = XPackBuild.class.getProtectionDomain().getCodeSource().getLocation();
try {
return PathUtils.get(url.toURI());
}
catch (URISyntaxException bogus) {
throw new RuntimeException(bogus);
}
}

XPackBuild(final String shortHash, final String date) {
this.shortHash = shortHash;
this.date = date;
}

public String shortHash() {
return this.shortHash;
}

public String date() {
return this.date;
}

static {
final Path path = getElasticsearchCodebase();
String shortHash = null;
String date = null;
Label_0109: {
/* 注释掉这一大段即可
if (path.toString().endsWith(".jar")) {
try {
final JarInputStream jar = new JarInputStream(Files.newInputStream(path, new OpenOption[0]));
try {
final Manifest manifest = jar.getManifest();
shortHash = manifest.getMainAttributes().getValue("Change");
date = manifest.getMainAttributes().getValue("Build-Date");
jar.close();
}
catch (Throwable t) {
try {
jar.close();
}
catch (Throwable t2) {
t.addSuppressed(t2);
}
throw t;
}
break Label_0109;
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
*/
shortHash = "Unknown";
date = "Unknown";
}
CURRENT = new XPackBuild(shortHash, date);
}
}

java源代码已经更改完毕,下面就是生成class文件,然后替换原来的class文件即可:

重新编译Class文件

注:

  1. 这里因为使用的是wsl环境,所以D:\elasticsearch-7.14.0挂载在/mnt/d/elasticsearch-7.14.0
  2. 使用elasticsearch7.x版本,java环境记住不要超过8,我使用17编译出来的class文件重新导入es后报错:java.lang.UnsupportedClassVersionError: org/elasticsearch/license/LicenseVerifier has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 60.0
  3. 以下命令参数按需修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 声明一些需要的临时变量
ES_HOME="/mnt/d/elasticsearch-7.14.0"
ES_JAR=$(cd $ES_HOME && ls lib/elasticsearch-[0-9]*.jar)
ESCORE_JAR=$(cd $ES_HOME && ls lib/elasticsearch-core-*.jar)
LUCENE_JAR=$(cd $ES_HOME && ls lib/lucene-core-*.jar)
XPACK_JAR=$(cd $ES_HOME && ls modules/x-pack-core/x-pack-core-*.jar)

cd ${ES_HOME}

# 编译2个Java类对应的class文件
javac -cp "${ES_HOME}/${ES_JAR}:${ES_HOME}/${LUCENE_JAR}:${ES_HOME}/${XPACK_JAR}:${ES_HOME}/${ESCORE_JAR}" LicenseVerifier.java
javac -cp "${ES_HOME}/${ES_JAR}:${ES_HOME}/${LUCENE_JAR}:${ES_HOME}/${XPACK_JAR}:${ES_HOME}/${ESCORE_JAR}" XPackBuild.java

# 解压重新替换并打包
mkdir -p /mnt/d/x-pack/
cp $ES_HOME/modules/x-pack-core/x-pack-core-7.14.0.jar /mnt/d/x-pack/
cd /mnt/d/x-pack/
jar -xvf x-pack-core-7.14.0.jar
cp ${ES_HOME}/XPackBuild.class ./org/elasticsearch/xpack/core/
cp ${ES_HOME}/LicenseVerifier.class ./org/elasticsearch/license/
rm -f x-pack-core-7.14.0.jar
jar cvf x-pack-core-7.14.0.jar .

申请License并修改过期时间

去官网申请License

这里会提示你下载哪个版本,我们选后者

我们将申请下来的License中的type改为platinum,将expiry_date_in_millis延长N年时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"license": {
"uid": "92c6b41e-59f9-4674-b227-77063c5fa8b0",
"type": "platinum",
"issue_date_in_millis": 1642291200000,
"expiry_date_in_millis": 3107746200000,
"max_nodes": 100,
"issued_to": "wang xiaowu (race)",
"issuer": "Web Form",
"signature": "AAAAAwAAAA0kxge9SLSAvWWnMgDEAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxaktJRVl5MUYvUWh3bHZVUTllbXNPbzBUemtnbWpBbmlWRmRZb25KNFlBR2x0TXc2K2p1Y1VtMG1UQU9TRGZVSGRwaEJGUjE3bXd3LzRqZ05iLzRteWFNekdxRGpIYlFwYkJiNUs0U1hTVlJKNVlXekMrSlVUdFIvV0FNeWdOYnlESDc3MWhlY3hSQmdKSjJ2ZTcvYlBFOHhPQlV3ZHdDQ0tHcG5uOElCaDJ4K1hob29xSG85N0kvTWV3THhlQk9NL01VMFRjNDZpZEVXeUtUMXIyMlIveFpJUkk2WUdveEZaME9XWitGUi9WNTZVQW1FMG1DenhZU0ZmeXlZakVEMjZFT2NvOWxpZGlqVmlHNC8rWVVUYzMwRGVySHpIdURzKzFiRDl4TmM1TUp2VTBOUlJZUlAyV0ZVL2kvVk10L0NsbXNFYVZwT3NSU082dFNNa2prQ0ZsclZ4NTltbU1CVE5lR09Bck93V2J1Y3c9PQAAAQBAD9GxJeiZQonVdEVrn5+frA3tMD18Stcp3fiHVGVdXRzbHQd3N23tTXSyXlqQo0lB/dDt1A4iKh8/Wotp38mFkYq/W/HbJC3hYkJaOQwBPO0aelWYTi4hAxw7c8HSjLf2S4J0dK7LYRW9vfuaK/YrCr42fOGsZ3GX+9WcwbBWT6ONnaJa2dMQRnDsrmcE599LiEz++8GvICWhzfGxjcHk4lsEGmFBC1FajDQsGf/d7oCI3EiNodgSMHtP3u6DZCt8h036wn4gyv5XdH3YauUltsKDmYqGFfD/Udy4kmiKR5qExX4i/K+7q+p4TVJ3GHqgVwtdXGkKiq32qXEqktj6",
"start_date_in_millis": 1642291200000
}
}

然后将其保存为并命名为license.json

接下来导入License,请确保以下配置为false

1
2
xpack.security.enabled: false
xpack.security.transport.ssl.enabled: false

等更新完升级为白金后再开启配置。

导入新的x-pack-core.jar

因为是使用docker的方式启动es,所以我这里使用挂载的方式进行配置

x-pack-core-7.14.0.jar放在/mydata/elasticsearch/modules/,然后挂载到/usr/share/elasticsearch/modules/x-pack-core/

1
2
3
4
......
volumes:
- /mydata/elasticsearch/modules/x-pack-core-7.14.0.jar:/usr/share/elasticsearch/modules/x-pack-core/x-pack-core-7.14.0.jar
......

导入证书

然后加载License到ES中:

1
2
$ curl -XPUT -u elastic:{密码} 'http://127.0.0.1:9200/_license?acknowledge=true' -H "Content-Type: application/json" -d @license.json
{"acknowledged":true,"license_status":"valid"} # 说明license写入成功

查看License:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ curl -XGET -u elastic:{密码} http://127.0.0.1:9200/_license
{
"license" : {
"status" : "active",
"uid" : "92c6b41e-59f9-4674-b227-77063c5fa8b0",
"type" : "platinum",
"issue_date" : "2019-11-29T00:00:00.000Z",
"issue_date_in_millis" : 1558051200000,
"expiry_date" : "2068-06-24T14:50:00.999Z",
"expiry_date_in_millis" : 2524579200999,
"max_nodes" : 1000,
"issued_to" : "pyker",
"issuer" : "Web Form",
"start_date_in_millis" : 1558051200000
}
}

最后,确保 elasticsearch 和 kibana均重启。单独elasticsearch,不重启kibana,会导致进入kibana时候提示 license无效。

注:需要注意的是elasticsearch一旦启用了xpack,则es服务必须是ssl的

kibana查看许可