喵星之旅-狂奔的兔子-linux开放端口校验

场景

发布高危端口,各服务器逐一验证是否有高危端口使用。由于端口超100个,服务器超40,逐一验证困难度过大,编写校验程序。

代码

文件“high-hazard” 无扩展名,每一行一个端口号。

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
import java.io.*;
import java.util.*;

public class SystemCommandPortDetector {

public static Set<String> getListeningPorts() throws IOException {
Set<String> ports = new HashSet<>();
String os = System.getProperty("os.name").toLowerCase();
Process process;


// Linux/Unix系统使用ss命令
process = Runtime.getRuntime().exec("ss -tuln");


// 读取命令输出
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
// 解析端口号

// Linux输出格式解析
if (line.startsWith("tcp") || line.startsWith("udp")) {
String[] parts = line.split("\\s+");
if (parts.length >= 5) {
String[] pps = parts[4].split(":");
String portPart = pps[pps.length - 1];
ports.add(portPart);
}
}

}
}

return ports;
}

/**
* 入参1 服务器信息(可以使用端口号,用于生成文件名称。否则名称无差别)
* 同级别创建文件“high-hazard” 无扩展名 ,linux环境下默认创建即可,内容为排查端口,一个端口一行
* @param args
*/
public static void main(String[] args) {
Set<String> taget = readFile();
if (taget == null) {
return;
}
try {
Set<String> ports = getListeningPorts();

// 计算交集(注意:retainAll()会修改原集合,先创建副本)
Set<String> intersection = new HashSet<>(taget); // 复制set1
intersection.retainAll(ports); // 保留与set2共有的元素

// 转换为List并使用自定义比较器排序
List<String> sortedList1 = new ArrayList<>(taget);
sortedList1.sort(new StringNumberComparator());

List<String> sortedList2 = new ArrayList<>(ports);
sortedList2.sort(new StringNumberComparator());

List<String> sortedList3 = new ArrayList<>(intersection);
sortedList3.sort(new StringNumberComparator());

PrintWriter pw = null;
try {
File fout = null;
if (args.length < 1) {
System.out.println("无入参,采用默认名称。");
fout = new File( "portresult");
} else {
fout = new File(args[0] + "portresult");
}

if (fout.exists()) {
fout.delete();
}
pw = new PrintWriter(fout);

System.out.println("结果文件:" + fout.getAbsolutePath());
} catch (FileNotFoundException e) {
e.printStackTrace();
}

pw.println("高危端口: " + sortedList1);
pw.println("本机监听端口: " + sortedList2);
pw.println("本机高危端口(交集): " + sortedList3);
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 自定义比较器:可转数字的按数值排序,不可转的放最后且按字符串自然顺序排序
static class StringNumberComparator implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
// 尝试将两个字符串转换为数字
Long num1 = parseNumber(s1);
Long num2 = parseNumber(s2);

// 情况1:两个都能转成数字 - 按数值比较
if (num1 != null && num2 != null) {
return Long.compare(num1, num2);
}

// 情况2:只有s1能转成数字 - s1排在前面
if (num1 != null) {
return -1;
}

// 情况3:只有s2能转成数字 - s2排在前面
if (num2 != null) {
return 1;
}

// 情况4:两个都不能转成数字 - 按字符串自然顺序排序
return s1.compareTo(s2);
}

// 辅助方法:尝试将字符串转换为Long,失败则返回null
private Long parseNumber(String s) {
try {
return Long.parseLong(s);
// 使用Long而非Integer避免大数字溢出
} catch (NumberFormatException e) {
return null;
}
}
}
public static Set<String> readFile() {
Set<String> taget = new HashSet<>();
InputStreamReader fr = null;
BufferedReader br = null;
try {
File f = new File("high-hazard");
if (!f.exists()) {
System.out.println("high-hazard文件未找到。同级别创建文件“high-hazard” 无扩展名 ,linux环境下默认创建即可,内容为排查端口,一个端口一行。");
System.out.println(f.getAbsolutePath());
return null;
}
FileInputStream fis = new FileInputStream(f);
// 指定编码格式
fr = new InputStreamReader(fis, "utf-8");
br = new BufferedReader(fr);
String line = null;
while ((line = br.readLine()) != null) {
taget.add(line.trim());
}

} catch (IOException e) {
try {
br.close();
fr.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
return taget;
}
}

使用

编译好的class文件2个“SystemCommandPortDetector”“SystemCommandPortDetector$StringNumberComparator”、 端口文件“high-hazard”,放到同一个路下,环境配置好jdk,可以执行java命令。

运行命令

1
java SystemCommandPortDetector 参数(一般为服务器地址)

结果存在“参数+portresult”文件中,在程序的同级目录。

结果类似:

1
2
3
4
高危端口: [21, 22, 23, 69, 110, 111, 135, 137, 138, 139, 143, 161, 389, 427, 443, 445, 502, 554, 587, 636, 873, 1080, 1099, 1433, 1521, 1883, 2049, 2123, 2152, 2181, 2222, 2375, 2379, 2888, 3000, 3128, 3306, 3386, 3389, 3690, 3888, 4000, 4040, 4369, 4440, 4848, 4899, 5000, 5005, 5037, 5432, 5601, 5631, 5632, 5673, 5900, 5984, 6123, 6379, 7001, 7051, 7077, 7180, 7182, 7848, 8000, 8009, 8019, 8020, 8042, 8048, 8051, 8069, 8080, 8081, 8083, 8086, 8088, 8123, 8161, 8443, 8649, 8848, 8880, 8883, 8888, 8999, 9000, 9001, 9004, 9042, 9043, 9083, 9092, 9100, 9200, 9300, 9876, 9990, 10000, 10909, 10911, 11000, 11111, 11211, 11434, 18080, 19888, 20880, 25000, 25010, 27017, 27018, 50000, 50030, 50070, 50090, 60000, 60010, 60030, 61616]
本机监听端口: [22, 631, 1716, 4001, 4301, 4310, 5353, 5657, 6188, 6189, 9210, 10631, 14008, 16067, 35600, 35692, 37473, 40468, 40882, 40995, 45649, 47264, 48705, 49313, 50730, 52211, 57212, 60675, 60828, 63342]
本机高危端口(交集): [22]

文章目录
  1. 场景
  2. 代码
  3. 使用
|