利用powershell script每个月定期从microsoft download网站上抓补丁

This artical will be published in English also: http://www.cnblogs.com/LarryAtCNBlog/p/4026695.html

本人所在的公司对于安全性要求较高,除了平时各种内网加密外网firewall之外,对于server所使用的OS也要求更新到最新的security级别的补丁。

但是样本数量一多就总有些是打不上补丁的,这可能由于各种各样如update配置错误,SCCM/WSUS抽风,加上第3方扫描补丁软件的2X机制和security team的压力,不得不把缺失的补丁一个个打上。这样的话就导致了经常性的要把KB或MS号贴在google里,然后找到链接,再找补丁下载。机器一多就乱的要死,重复下载就是经常发生的事~

于是为了让生活轻松一些就做了这样一个script,从security RSS里抓出MS号,然后从MS的链接里抓出KB号,再抓出补丁下载链接把其下载到本地。所以只需要每月schedule一次或多次运行就可以把所有补丁放在一个固定的共享中了。microsoft每个月第二个星期二release当月的补丁(米国的周二,亚太大概就是周三)。

Security RSS: https://technet.microsoft.com/en-us/security/rss/bulletin

流程:读取RSS内容 -> 脚本抓出MS号和链接 -> 循环读MS号链接内容,取出所有KB -> 使用一些条件filter掉不要的KB,比如我只管server,我就不想下载非server的补丁 -> 脚本抓出KB的下载链接 -> 再从KB下载链接内容中抓取具体的下载路径 -> 下载补丁到本地

下面一步步分解该脚本

$Url = ‘https://technet.microsoft.com/en-us/security/rss/bulletin‘
$ExcludeProducts = ‘lync|Itanium|for mac‘
$IncludeProducts = ‘server‘

$ExcludePatches = ‘-IA64|Windows6\.0|-RT-|ServiceBusServer‘

$PatchStoreTo = ‘.\‘

在上面的几行中,定义了几个变量,

$Url 当然就是RSS的链接;

$ExcludeProducts 就是抓出MS号网页内容之后根据提供的正则表达式过滤掉不想要的product,比如lync,安腾cpu相关的补丁;

$IncludeProducts 就是经过上面exclude过滤后留下来的KB再过滤一次,而这次就是滤出包涵server信息的KB;

$ExcludePatches 是另一个过滤,在取到具体的补丁下载链接后对补丁的名字进行匹配,过滤掉安腾补丁之类的安装包(因为有些KB信息里没有直接写明安腾cpu,所以加了该过滤从文件名来判断);

$PatchStoreTo 就是把补丁存在哪个地方,当然,要有写权限才行。

$WebClient = New-Object System.Net.WebClient
$WebClient.Encoding = [System.Text.Encoding]::UTF8

以上建立Webclient类并指定编码

do
{
    $RSSContent = $WebClient.DownloadString($Url)
}
while(
    $(if(!$?)
    {
        Write-Host ‘Failed to get RSS‘ -ForegroundColor Red
        Start-Sleep -Seconds 600
        $true
    })
)

上面就是从RSS链接中取到RSS的内容,如果不成功的话就等10分钟再试一次。

([xml]$RSSContent).rss.channel.Item | Sort-Object link | %{...}

把RSS内容转换为xml对象,然后就可以方便的从xml中读节点数据了

    $MSRC_URL = $_.link
    Write-Host "Processing: [$MSRC_URL]" -ForegroundColor Yellow
    $MSRC = ([regex]::Match($MSRC_URL, ‘(?i)MS\d+-\d+$‘)).Value
    Write-Host "MS number: [$MSRC]" -ForegroundColor Green
    if(!(Test-Path -LiteralPath "$PatchStoreTo\$MSRC"))
    {
        do
        {
            New-Item -Path "$PatchStoreTo\$MSRC" -ItemType Directory | Out-Null
        }
        while(
            $(if(!$?)
            {
                Write-Host ‘Failed to create MSRC folder‘ -ForegroundColor Red
                Start-Sleep 300
                $true
            })
        )
    }

首先,把MS号的链接放在了$MSRC_URL变量中,然后用黄字输出到屏幕上,利用正则匹配到MS号存在$MSRC中,同样输出到屏幕。后面write-host这种输出信息的就不描述了。随后是创建以MS号为名称的文件夹,方便之后用来存放补丁文件。

    Write-Host "Trying to capture KBs from MSRC URL" -ForegroundColor Yellow
    do
    {
        $MSContent = $null
        $MSContent = $WebClient.DownloadString($MSRC_URL)
    }
    while(
        $(if(!$?)
        {
            Write-Host ‘Failed to capture MSRC content‘ -ForegroundColor Red
            Start-Sleep 300
            $true
        })
    )

上面的代码则是抓取MS号的链接内容存在$MSContent中。

MS链接例如 https://technet.microsoft.com/en-us/library/security/MS14-063,MSContent就是该网页的后台代码。

[regex]::Matches($MSContent, ‘(?i)<tr>[\s\S]+?<a href="(http://www.microsoft.com/downloads/details.aspx\?FamilyID=[\w\-]+?)">[\s\S]+?\((\d{7})\)‘) | %{...}

上面就是从MS的网页内容中抓到具体的KB信息,如KB的链接,KB号。

它匹配到的内容如下,图片中所有的内容都会被匹配出来。

        Write-Host "KB: [$($_.Groups[2].Value)]" -NoNewline -ForegroundColor Green
        if($_.Value -imatch $ExcludeProducts)
        {
            Write-Host "   --- Excluded: [$($Matches[0])]" -ForegroundColor Red
        }
        else
        {
            if($_.Value -notmatch $IncludeProducts)
            {
                Write-Host "   --- Excluded: Not match [$IncludeProducts]" -ForegroundColor Red
                return
            }
            $KBNumber = "KB$($_.Groups[2].Value)"       Write-Host "`nDownload URL: [$($_.Groups[1].Value)]" -ForegroundColor Gray

上面的内容以KB的内容排除了$excludeProducts中描述的产品名称,然后通过的KB又会经过$IncludeProducts的过滤,最终都通过的话,KB号存放于$KBNumber中,并打印出下载链接在屏幕上。

            do
            {
                $KBContent = $null
                $KBContent = $WebClient.DownloadString($_.Groups[1].Value)
            }while(
                $(if(!$?)
                {
                    Write-Host ‘Failed to capture KB content‘ -ForegroundColor Red
                    Start-Sleep 300
                    $true
                })
            )

以上代码则是从已抓出的KB链接中取KB的网页内容放于$KBContent中。

KB链接在MSContent中长这样:http://www.microsoft.com/downloads/details.aspx?familyid=8a59fc6d-cbad-4905-842b-e5aa1fc6fedf

但是访问它后会跳转成:http://www.microsoft.com/en-us/download/details.aspx?id=44400

当然,这是Web server自动的,不需要我们手动在代码中做什么,该网页并不包涵补丁下载信息,它只是让我们确认一下语言,还有告诉我们KB具体信息而已,截图如下。

因此,我们还要接着抓该网页后台的字符串信息,找到下载链接confirmation.aspx,当把鼠标放于"Download"按钮上时,可以在状态栏看到confirmation.aspx的链接。

            $KBConfirm = ([regex]::Match($KBContent, ‘(?i)href="(confirmation.aspx\?id=\d+)"‘)).Groups[1].Value
            $KBConfirm = "http://www.microsoft.com/en-us/download/$KBConfirm"
            Write-Host "KB confirm URL: [$KBConfirm]" -ForegroundColor Gray
            do
            {
                $KBContent = $null
                $KBContent = $WebClient.DownloadString($KBConfirm)
            }while(
                $(if(!$?)
                {
                    Write-Host ‘Failed to capture KB download content‘ -ForegroundColor Red
                    Start-Sleep 300
                    $true
                })
            )

以上是从$KBContent中抓到confirmation.aspx链接,并会抓出该confirmation.aspx中的内容,其实confirmation.aspx后面跟的id好像和KB details.aspx后面的id是一样的,但是为了保险一点,我还是选择了从网页内容中抓confirmation.aspx的信息。

            $KBLinks = @()
            $KBLinks = [regex]::Matches($KBContent, ‘(?i)<a href="(http://download.microsoft.com/download/.+?)".+?>Click here</span>‘) | %{
                $_.Groups[1].Value
            }
            $KBLinks = @($KBLinks | Sort-Object -Unique)
            Write-Host "The KB contains updates: [$($KBLinks.Count)]" -ForegroundColor Green

在抓到KB的confirmation.aspx内容之后,然后从内容中用正则匹配具体的下载链接,最后做一个排序并去除重复的条目,完成后$KBLinks中就包涵了该KB中所有补丁的下载链接。

当在浏览器中打开confirmation.aspx后其实会自动弹出下载,但这是游览器的行为,代码是不会自动下载的,但是我们可以看到一个"click here"就是下载链接了。

要做的也就是分析它后面的网页代码了,依然还是用正则。

            $KBLinks | %{
                $FileName = $null
                $FileName = $_.Split(‘/‘)[-1]
                if($FileName -imatch $ExcludePatches)
                {
                    Write-Host "Patch excluded: [$($Matches[0])]" -ForegroundColor Red
                    return
                }

既然有了补丁的具体下载链接,剩下的就是下载了,但在下载之前又对补丁的名称做了一次过滤,之前也提到了KB的信息有时候是不完整的,因此要做这里的过滤。

                if(Test-Path -Path $FilePath)
                {
                    Write-Host ‘File already exists, skip!‘ -ForegroundColor Gray
                }
                else
                {
                    do
                    {
                        $WebClient.DownloadFile($_, $FilePath)
                    }while(
                        $(if(!$?)
                        {
                            Write-Host ‘Download file failed!‘ -ForegroundColor Red
                            Start-Sleep -Seconds 300
                            $true
                        })
                    )
                }

上面就是下载补丁的代码了,当然,如果补丁已经存在,脚本不会重复下载。

以上就是脚本的分析和介绍了,最后贴张运行图还有完整的script。

完整脚本如下,

$Url = ‘https://technet.microsoft.com/en-us/security/rss/bulletin‘
$ExcludeProducts = ‘lync|Itanium|for mac‘
$IncludeProducts = ‘server‘

$ExcludePatches = ‘-IA64|Windows6\.0|-RT-|ServiceBusServer‘

$PatchStoreTo = ‘.\‘

$WebClient = New-Object System.Net.WebClient
$WebClient.Encoding = [System.Text.Encoding]::UTF8

do
{
    $RSSContent = $WebClient.DownloadString($Url)
}
while(
    $(if(!$?)
    {
        Write-Host ‘Failed to get RSS‘ -ForegroundColor Red
        Start-Sleep -Seconds 600
        $true
    })
)

([xml]$RSSContent).rss.channel.Item | Sort-Object link | %{
    $MSRC_URL = $_.link
    Write-Host "Processing: [$MSRC_URL]" -ForegroundColor Yellow
    $MSRC = ([regex]::Match($MSRC_URL, ‘(?i)MS\d+-\d+$‘)).Value
    Write-Host "MS number: [$MSRC]" -ForegroundColor Green
    if(!(Test-Path -LiteralPath "$PatchStoreTo\$MSRC"))
    {
        do
        {
            New-Item -Path "$PatchStoreTo\$MSRC" -ItemType Directory | Out-Null
        }
        while(
            $(if(!$?)
            {
                Write-Host ‘Failed to create MSRC folder‘ -ForegroundColor Red
                Start-Sleep 300
                $true
            })
        )
    }
    Write-Host "Trying to capture KBs from MSRC URL" -ForegroundColor Yellow
    do
    {
        $MSContent = $null
        $MSContent = $WebClient.DownloadString($MSRC_URL)
    }
    while(
        $(if(!$?)
        {
            Write-Host ‘Failed to capture MSRC content‘ -ForegroundColor Red
            Start-Sleep 300
            $true
        })
    )

    [regex]::Matches($MSContent, ‘(?i)<tr>[\s\S]+?<a href="(http://www.microsoft.com/downloads/details.aspx\?FamilyID=[\w\-]+?)">[\s\S]+?\((\d{7})\)‘) | %{
        Write-Host "KB: [$($_.Groups[2].Value)]" -NoNewline -ForegroundColor Green
        if($_.Value -imatch $ExcludeProducts)
        {
            Write-Host "   --- Excluded: [$($Matches[0])]" -ForegroundColor Red
        }
        else
        {
            if($_.Value -notmatch $IncludeProducts)
            {
                Write-Host "   --- Excluded: Not match [$IncludeProducts]" -ForegroundColor Red
                return
            }
            $KBNumber = "KB$($_.Groups[2].Value)"
            Write-Host "`nDownload URL: [$($_.Groups[1].Value)]" -ForegroundColor Gray
<#
            if(!(Test-Path -Path "$MSRC\$KBNumber"))
            {
                do
                {
                    New-Item -Name "$MSRC\$KBNumber" -ItemType Directory | Out-Null
                }
                while(
                    $(if(!$?)
                    {
                        Write-Host ‘Failed to create KB folder‘ -ForegroundColor Red
                        Start-Sleep 300
                        $true
                    })
                )
            }
#>
            do
            {
                $KBContent = $null
                $KBContent = $WebClient.DownloadString($_.Groups[1].Value)
            }while(
                $(if(!$?)
                {
                    Write-Host ‘Failed to capture KB content‘ -ForegroundColor Red
                    Start-Sleep 300
                    $true
                })
            )

            $KBConfirm = ([regex]::Match($KBContent, ‘(?i)href="(confirmation.aspx\?id=\d+)"‘)).Groups[1].Value
            $KBConfirm = "http://www.microsoft.com/en-us/download/$KBConfirm"
            Write-Host "KB confirm URL: [$KBConfirm]" -ForegroundColor Gray
            do
            {
                $KBContent = $null
                $KBContent = $WebClient.DownloadString($KBConfirm)
            }while(
                $(if(!$?)
                {
                    Write-Host ‘Failed to capture KB download content‘ -ForegroundColor Red
                    Start-Sleep 300
                    $true
                })
            )

            $KBLinks = @()
            $KBLinks = [regex]::Matches($KBContent, ‘(?i)<a href="(http://download.microsoft.com/download/.+?)".+?>Click here</span>‘) | %{
                $_.Groups[1].Value
            }
            $KBLinks = @($KBLinks | Sort-Object -Unique)
            Write-Host "The KB contains updates: [$($KBLinks.Count)]" -ForegroundColor Green
            $KBLinks | %{
                $FileName = $null
                $FileName = $_.Split(‘/‘)[-1]
                if($FileName -imatch $ExcludePatches)
                {
                    Write-Host "Patch excluded: [$($Matches[0])]" -ForegroundColor Red
                    return
                }
                $FilePath = $null
                $FilePath = "$MSRC\$FileName"
                Write-Host "Going to download file: [$FilePath]" -ForegroundColor Gray
                $FilePath = "$PatchStoreTo\$FilePath"
                if(Test-Path -Path $FilePath)
                {
                    Write-Host ‘File already exists, skip!‘ -ForegroundColor Gray
                }
                else
                {
                    do
                    {
                        $WebClient.DownloadFile($_, $FilePath)
                    }while(
                        $(if(!$?)
                        {
                            Write-Host ‘Download file failed!‘ -ForegroundColor Red
                            Start-Sleep -Seconds 300
                            $true
                        })
                    )
                }
            }
        }
    }
}

附,关于proxy,WebClient类会自动使用IE里所设置的proxy,所以如果要用proxy的话,把IE设置好就行了。

时间: 2024-08-20 12:44:27

利用powershell script每个月定期从microsoft download网站上抓补丁的相关文章

利用Powershell自动部署asp.net mvc网站项目 (一)

这一篇中我们会写一些关于自动化部署的代码.我们会使用 Powershell 书写这类代码. 你将发现这篇文章中涉及的东西非常具体,有的要求甚至相当苛刻且可能不具有通用性.这是因为部署从来都是跟环境打交道,部署过程中协作的组建太多,相互之间的交集不可能太大.可能唯一能够通用的是自动化部署的基本原则(只是这篇文章的基本原则): 每一次自动化部署结束之后,应用程序都会有相同的初始状态. 自动化部署的机器非常干净,只有相应的 Windows Server 系统和 .NET Framework.尤其是,不

C# 从需要登录的网站上抓取数据

[转] C# 从需要登录的网站上抓取数据 背景:昨天一个学金融的同学让我帮她从一个网站上抓取数据,然后导出到excel,粗略看了下有1000+条记录,人工统计的话确实不可能.虽说不会,但作为一个学计算机的,我还是厚着脸皮答应了. . 刚开始想的是直接发送GET请求,然后再解析返回的html不就可以获取需要的信息吗?的确,如果是不需要登录的网站,这样可行,但对于这个网站就行不通.所以首先我们需要做的就是抓包,即分析用户登录时浏览器向服务器发送的POST请求.许多浏览器都自带抓包工具,但我还是更喜欢

Use powershell script against password hacking over OWA

In my previous company Exchange OWA isn't published to internet, so this blog described my first time encountering hacker trying to hack my new company's Active directory passwords. Based on it, I wrote a powershell script running on each CAS servers

Microsoft Azure Web Sites应用与实践【4】—— Microsoft Azure网站的“后门”

Microsoft Azure Web Sites应用与实践 系列: [1]—— 打造你的第一个Microsoft Azure Website [2]—— 通过本地IIS 远程管理Microsoft Azure Web Site [3]—— 通过Visual Studio Online在线编辑Microsoft Azure 网站 [4]—— Microsoft Azure网站的“后门” Microsoft Azure网站的“后门” 从我们之前的博文可以看到,对Microsoft Azure 网站

SharePoint2013 Powershell script to get site Title, Site Owner, Site user count and usage

Powershell script to get site Title, Site Owner, Site user count and usage Add-PSSnapin microsoft.sharepoint.powershell -ErrorAction SilentlyContinue $wburl = Read-Host "Enter Web application URL " $webApp = Get-SPWebApplication $wburl $outputPa

[已解决]从微软合作伙伴资源和MSDN下载系统和软件Microsoft download Manager无效

有个itellyou,更新了所有MSDN的软件包.如果自己有微软的注册账户,还是从微软官网下载比较好.而且对自己账户里的系统和itellyou里的做了对比.发现SHA1码不相同,估计官方分配的序列号也不一定可以使用到itellyou下载的软件包里. 本着这样的执念.开始折腾从官网下载.速度超级慢,一个Windows10都要下载好几天才可以完成.在国内算是不可能的任务了吗.我记得在国外的时候,完全没有这个问题的. 官方的提示的Microsoft Download Manager在IE11后,就完全

利用htmlunit登陆带验证码图片的网站

http://htsoft.org/html/y2011/822_using-htmlunit-landing-site-with-captcha-image.html 利用htmlunit登陆带验证码图片的网站 2011年09月15日 ⁄ 编程语言 ⁄ 共 1266字 ⁄ 字号 小 中 大 ⁄ 暂无评论 ⁄ 阅读 7,088 次 以百度统计为例,说明下如何用htmlunit登陆带验证码的网站 //baidu统计登陆代码 try { WebClient client = new WebClien

Send email alert from Performance Monitor using PowerShell script (检测windows服务器的cpu 硬盘 服务等性能,发email的方法) -摘自网络

I have created an alert in Performance Monitor (Windows Server 2008 R2) that should be triggered whenever \Processor(_Total)\% Processor Time is Above 10 (a small value just to guarantee that the condition for sending the alert is always met). You ca

利用Powershell SSH-Session 工具管理 linux或网络设备

首先我们需要下载这个工具 下载地址 http://www.powershelladmin.com/wiki/SSH_from_PowerShell_using_the_SSH.NET_library#Downloads 1 下载的文件内容,请注意针对你使用的powershell 版本下载相应的的版本,有支持powershell2.0以及支持powershell3.0和4.0. 2检测一下你的系统 powershell模块放置的目录 3 所以将下载的压缩文件解压到这两个目录随意一个都可以: C:\