
2.3 代理服务扫描器

代理服务扫描器的作用是判断一个端口上是否监听了代理服务,它的使用场景有以下几个。
● 定期扫描自己公司的服务器,排查是否有外网服务器开启了代理服务。如果运维或研发人员安全意识薄弱,为了方便在服务器中部署了代理软件,攻击者发现后就可以通过代理绕过网络边界设置的种种防御措施直接进入内网,渗透防御措施相对薄弱的内网服务器。
● 扫描并搜集公网上的代理,用来完善代理池。
● 批量检测一些代理网站上提供的免费代理是否有效。
2.3.1 HTTP/HTTPS代理检测模块
Go语言标准库的net/http包中有HTTP客户端和服务器的具体实现,利用此包可以很方便地编写HTTP客户端和服务器端的程序。例如,以下代码可以发起HTTP的GET请求:

在Sublime Text中执行后会返回笔者blog的内容,如图2-12所示。

●图2-12 Go的HTTP客户端测试
HTTP/HTTPS代理服务扫描的原理是利用代理发起HTTP请求,如果利用代理能访问目标网站,则该代理是有效的。
为上面HTTP请求增加使用代理的功能,自定义一个HTTP Client对象即可,详细的代码如下所示:


给HTTP Client增加使用代理的功能后再请求目标网站,返回的情况有以下两种。
● 代理的IP或端口无法访问,此时会返回connect:connection refused错误,说明代理是无效的。
● 如果测试的代理是Web服务,会返回此Web服务的内容,如果只判断是否有数据返回,会造成误报,如图2-13所示。

●图2-13 代理检测误报效果
解决误报的方式是给HTTP Client增加代理后,访问一个特定的网站,判断返回的内容中是否包含特定的关键字。例如,访问http://email.163.com/,如果返回的内容中包含“<title>网易免费邮箱”这几个关键字,说明测试的代理是有效的。
除了使用Go标准库中的net/http包外,也可以使用第三方包来实现HTTP代理测试的功能,如github.com/parnurzeal/gorequest、github.com/levigross/grequests包等。grequests包类似于Python语言中的requests库。
使用github.com/parnurzeal/gorequest包通过代理提交HTTP请求的示例代码如下所示:

可以看出,利用此包给HTTP请求增加使用代理的功能非常方便,只要调用gorequest对象的Proxy方法即可。运行结果如图2-14所示。

●图2-14 gorequest测试
使用github.com/levigross/grequests包通过代理提交HTTP请求的代码如下所示:

从以上示例可以得知,grequests库是通过grequests.RequestOptions来设置代理的。运行结果如图2-15所示。

●图2-15 grequests测试
前面介绍使用HTTP代表的3种不同的方式,为了减少对外部的依赖,笔者采用Go语言标准库的net/http包,并封装了一个HTTP代理检测函数,详细的代码如下所示:


以上代码中,变量HttpProxyProtocol与WebUrl的含义分别如下所述。
● HttpProxyProtocol中定义了检测的代理协议的种类,如HTTP代表与HTTPS代理。
● WebUrl表示用代理访问的Web,用于通过返回的内容判断代理是否有效。
2.3.2 SOCKS代理检测模块
Go语言的"golang.org/x/net/proxy"包支持给HTTP客户端增加SOCKS5代理。在使用这个包之前,需要通过go get-u golang.org/x/net/proxy命令进行安装。
如果无法访问golang.org这个域名,可以使用https://goproxy.io/或https://goproxy.cn/等Go模块代理。
golang.org/x/net/proxy包的使用方法如下。
1)创建一个dialer,它包含了SOCK5代理服务器的地址、用户名和密码。下面是创建dialer的代码,这里假设代理服务器的IP为sec.lu,端口为1080,用户名为xsec,密码为xsec,代码片断如下所示:

如果代理服务器并不需要用户名和密码,可以将proxy.SOCKS5函数的第三个参数设置为nil。
2)创建一个transport,它会利用刚才创建的dialer进行TCP连接,创建transport的代码如下所示:

3)创建一个HTTP Client就可使用SOCKS5代理了,代码如下所示:

完整的示例代码如下所示:

这个示例的作用是检测sec.lu:1080这个SOCKS5代理是否有效,可以在Sublime Text中通过GoSublime直接运行以上代码,结果如图2-16所示。

●图2-16 SOCKS5代理测试
另外还有一个第三方库h12.io/socks也可以实现使用SOCKS5代理的功能,它不仅支持SOCKS5,还支持SOCKS4, SOCKS4A。使用方法如下所示:


这里使用h12.io/socks包实现SOCKS代理检测模块。按照与HTTP代理检测模块相同的函数原型封装一个SOCKS代理检测函数,详细的代码如下所示:


SockProxyProtocol是需要检测协议的map,检测程序会测试这个map中的协议是否有效。
2.3.3 代理服务扫描模块任务执行功能的实现
前面已经实现了HTTP与SOCKS代理检测的功能,接下来实现扫描任务的调度功能。程序的执行流程如下。
1)从文件列表中读取待检测的代理服务器的IP、端口列表。
2)将待扫描的ipList分割为组,按组进行扫描。
1.读取待扫描的ipList的功能的实现
待扫描的ipList的文件格式为每行一个ip:port,如下所示:

定义一个ProxyAddr结构用来保存每条待扫描的记录,如下所示:

使用标准库中的bufio包逐行读取文件,然后用strings包进行分割,最后返回一个[]ProxyAddr即可,详细的代码如下所示:


2.扫描任务分割与调度的功能实现
扫描任务的组数是根据待扫描的ipList的总数与扫描的并发数计算出来的,每次最多扫描ScanNum个ipList,代码如下所示:


CheckProxy的作用是检测一组ipList,其中包括检测HTTP代表与SOCKS代理。HTTP代表有HTTP/HTTPS两种,SOCKS代理有SOCKS4、SOCKS4A与SOCKS5三种,详细的代码如下所示:

2.3.4 代理扫描器命令行的实现
最终实现的代理扫描器的命令行参数为./main scan-debug=true-scan_num=30-timeout=10-filename=iplist.txt,每个参数的含义如下。
● scan表示用默认参数启动代理服务器的扫描。
● --debug表示是否启用Debug模式,值为true和false。
● --scan_num表示扫描的并发数,默认值为100。
● --timeout表示每个扫描连接的超时时间,默认值为5s。
● --filename表示等待扫描的代理服务器的列表,默认值为iplist.txt。
前面多次介绍过github.com/urfave/cli包的用法,实现以上命令行参数只需要定义一个cli.Command即可,完整的代码如下所示:

实际的参数处理与任务调度入口在proxy.Scan的Action中,代码可以参考scanner/proxy-scanner/proxy/proxy.go文件。
2.3.5 代理扫描器测试
代理扫描器已经开发完成,其命令行参数如图2-17所示。

●图2-17 代理扫描器命令行参数
有一些网站提供了免费的代理服务器列表,如http://free-proxy.cz/en/网站,可以选择代理的类型、国家,这里选择China,单击Filter proxies可以过滤出国家为中国的代理服务器,最后单击Export IP:Port按钮即可将IP:Port复制出来,如图2-18所示。

●图2-18 免费代理网站
将IP:Port复制到代理扫描器当前目录中的iplist.txt中,然后通过以后命令启动扫描:

等待几秒钟后,扫描器输出了结果,用时大约5s,输出结果中有两个是SOCKS5代理,其他的全是SOCKS4或SOCKS4A代理,如图2-19所示。

●图2-19 代理验证效果
接下来用QQ自带的代理测试功能来测试扫描结果中SOCKS5代理的有效性,最终得出的结论是代理扫描器的检测结果是准确的,如图2-20所示。

●图2-20 QQ的代理测试效果