基于HTTPS的中間人攻擊-BaseProxy

前言

在上一篇文章BaseProxy:異步http/https代理中,我介紹了自己的開源項目BaseProxy狞贱,這個項目的初衷其實是為了滲透測試刻获,抓包改包。在知識星球中瞎嬉,有很多朋友問我這個項目的原理及實現(xiàn)代碼蝎毡,本篇文章就講解一下和這個項目相關(guān)的HTTPS的中間人攻擊。

HTTPS隧道代理

HTTPS隧道代理簡單來說是基于TCP協(xié)議數(shù)據(jù)透明轉(zhuǎn)發(fā)佑颇,在RFC中顶掉,為這類代理給出了規(guī)范,Tunneling TCP based protocols through Web proxy servers挑胸。瀏覽器客戶端發(fā)送的原始 TCP 流量痒筒,代理發(fā)送給遠端服務(wù)器后,將接收到的 TCP 流量原封不動返回給瀏覽器。交互流程如下圖所示:

HTTP權(quán)威指南

以連接百度為例簿透,瀏覽器首先發(fā)起 CONNECT 請求:

CONNECT baidu.com:443 HTTP/1.1

代理收到這樣的請求后移袍,根據(jù) host 地址與服務(wù)器建立 TCP 連接,并返回給瀏覽器連接成功的HTTP 報文(沒有報文體):

HTTP/1.1 200 Connection Established

瀏覽器一旦收到這個響應(yīng)報文老充,就可認為與服務(wù)器的 TCP 連接已打通葡盗,后續(xù)可直接透傳。

在BaseProxy項目中啡浊,https=False是對于https實行透傳觅够。

HTTPS中間人攻擊

HTTPS 代理本質(zhì)上是隧道透傳,僅僅是轉(zhuǎn)發(fā) TCP 流量巷嚣,無法獲取其中的GET/POST請求的具體內(nèi)容喘先。這就很麻煩,現(xiàn)在 HTTPS 越來越普遍廷粒,做安全測試也就拿不到 HTTP 請求窘拯。那怎么做呢? 代理需要對 TCP 流量進行解密坝茎,然后對明文的HTTP請求進行分析涤姊,這樣的代理就稱為HTTPS中間人。

正常的HTTPS隧道

HTTPS隧道

在上圖中嗤放,隧道代理負責瀏覽器和服務(wù)器之間的TCP流量的轉(zhuǎn)發(fā)思喊。

HTTPS中間人

如果需要對TCP流量進行分析和修改,就要將上圖中的代理功能一分為二次酌,即代理既要當做TLS服務(wù)端搔涝,又要當做TLS客戶端,如下圖所示和措。

HTTPS中間人

在上圖中,用一個 TLS 服務(wù)器偽裝成遠端的真正的服務(wù)器蜕煌,接收瀏覽器的 TLS 流量派阱,解析成明文。這個時候可以對明文進行分析修改斜纪,然后用明文作為原始數(shù)據(jù)贫母,模擬 TLS 客戶端將原始數(shù)據(jù)向遠端服務(wù)器轉(zhuǎn)發(fā)。

CA證書問題

CA證書是我當時遇到的坑盒刚,之前沒接觸過腺劣。HTTPS傳輸是需要證書的,用來對HTTP明文請求進行加解密因块。一般正常網(wǎng)站的證書都是由合法的 CA 簽發(fā)橘原,則稱為合法證書。在上圖中,瀏覽器會驗證隧道代理中 TLS 服務(wù)器 的證書:

  1. 驗證是否是合法 CA 簽發(fā)趾断。
  2. 驗證該證書 CN 屬性是否是所請求的域名拒名。即若瀏覽器打開 www.baidu.com,則返回的證書 CN 屬性必須是 www.baidu.com芋酌。

對于第一點增显,合法的 CA 機構(gòu)不會給我們簽發(fā)證書的,否則HTTPS安全性形同虛設(shè)脐帝,因此我們需要自制CA證書同云,并導(dǎo)入到瀏覽器的信任區(qū)中。

對于第二點堵腹,我們由于需要對各個網(wǎng)站進行HTTPS攔截炸站,因此我們需要實時生成相應(yīng)域名的服務(wù)器證書,并使用自制的CA證書進行簽名秸滴。

BaseProxy源碼分析

通過以上的講解武契,HTTPS中間人的原理已經(jīng)基本清楚,下面簡要地說明一下BaseProxy源碼荡含。

HTTP服務(wù)器

代理其實就是一個HTTPS服務(wù)器咒唆,使用了Python中的HTTPServer類,為了增加異步特性释液,將其放到線程池中全释。

class MitmProxy(HTTPServer):

    def __init__(self,server_addr=('', 8788),RequestHandlerClass=ProxyHandle, bind_and_activate=True,https=True):
        HTTPServer.__init__(self,server_addr,RequestHandlerClass,bind_and_activate)
        logging.info('HTTPServer is running at address( %s , %d )......'%(server_addr[0],server_addr[1]))
        self.req_plugs = []##請求攔截插件列表
        self.rsp_plugs = []##響應(yīng)攔截插件列表
        self.ca = CAAuth(ca_file = "ca.pem", cert_file = 'ca.crt')
        self.https = https

    def register(self,intercept_plug):

        if not issubclass(intercept_plug, InterceptPlug):
            raise Exception('Expected type InterceptPlug got %s instead' % type(intercept_plug))

        if issubclass(intercept_plug,ReqIntercept):
            self.req_plugs.append(intercept_plug)

        if issubclass(intercept_plug,RspIntercept):
            self.rsp_plugs.append(intercept_plug)

class AsyncMitmProxy(ThreadingMixIn,MitmProxy):

    pass

HTTPS請求與響應(yīng)

對HTTP請求的解析與響應(yīng),關(guān)鍵在于ProxyHandle類误债,實現(xiàn)其中的do_CONNECT和do_GET方法浸船,并在do_CONNECT方法中判斷是使用透傳模式還是中間人模式。

class ProxyHandle(BaseHTTPRequestHandler):

    def __init__(self,request,client_addr,server):
        self.is_connected = False
        BaseHTTPRequestHandler.__init__(self,request,client_addr,server)

    def do_CONNECT(self):
        '''
        處理https連接請求
        :return:
        '''
        self.is_connected = True#用來標識是否之前經(jīng)歷過CONNECT
        if self.server.https:
            self.connect_intercept()
        else:
            self.connect_relay()

    def do_GET(self):
        '''
        處理GET請求
        :return:
        '''
      ......

    do_HEAD = do_GET
    do_POST = do_GET
    do_PUT = do_GET
    do_DELETE = do_GET
    do_OPTIONS = do_GET

CA證書生成以及代理證書的自簽名

與CA證書相關(guān)的內(nèi)容都放在了CAAuth類中寝蹈。生成CA證書代碼如下:

def _gen_ca(self,again=False):
        # Generate key
        #如果證書存在而且不是強制生成李命,直接返回證書信息
        if os.path.exists(self.ca_file_path) and os.path.exists(self.cert_file_path) and not again:
            self._read_ca(self.ca_file_path) #讀取證書信息
            return
        self.key = PKey()
        self.key.generate_key(TYPE_RSA, 2048)
        # Generate certificate
        self.cert = X509()
        self.cert.set_version(2)
        self.cert.set_serial_number(1)
        self.cert.get_subject().CN = 'baseproxy'
        self.cert.gmtime_adj_notBefore(0)
        self.cert.gmtime_adj_notAfter(315360000)
        self.cert.set_issuer(self.cert.get_subject())
        self.cert.set_pubkey(self.key)
        self.cert.add_extensions([
            X509Extension(b"basicConstraints", True, b"CA:TRUE, pathlen:0"),
            X509Extension(b"keyUsage", True, b"keyCertSign, cRLSign"),
            X509Extension(b"subjectKeyIdentifier", False, b"hash", subject=self.cert),
        ])
        self.cert.sign(self.key, "sha256")
        with open(self.ca_file_path, 'wb+') as f:
            f.write(dump_privatekey(FILETYPE_PEM, self.key))
            f.write(dump_certificate(FILETYPE_PEM, self.cert))

        with open(self.cert_file_path, 'wb+') as f:
            f.write(dump_certificate(FILETYPE_PEM, self.cert))

根據(jù)域名實時生成服務(wù)器證書,并對服務(wù)器證書進行自簽名。代碼如下:

def _sign_ca(self,cn,cnp):
        #使用合法的CA證書為代理程序生成服務(wù)器證書
        # create certificate
        try:

            key = PKey()
            key.generate_key(TYPE_RSA, 2048)

            # Generate CSR
            req = X509Req()
            req.get_subject().CN = cn
            req.set_pubkey(key)
            req.sign(key, 'sha256')

            # Sign CSR
            cert = X509()
            cert.set_version(2)
            cert.set_subject(req.get_subject())
            cert.set_serial_number(self.serial)
            cert.gmtime_adj_notBefore(0)
            cert.gmtime_adj_notAfter(31536000)
            cert.set_issuer(self.cert.get_subject())
            ss = ("DNS:%s" % cn).encode(encoding="utf-8")

            cert.add_extensions(
                [X509Extension(b"subjectAltName", False, ss)])

            cert.set_pubkey(req.get_pubkey())
            cert.sign(self.key, 'sha256')

            with open(cnp, 'wb+') as f:
                f.write(dump_privatekey(FILETYPE_PEM, key))
                f.write(dump_certificate(FILETYPE_PEM, cert))
        except Exception as e:
            raise Exception("generate CA fail:{}".format(str(e)))

最后

關(guān)注公眾號:七夜安全博客

image
  • 回復(fù)【1】:領(lǐng)取 Python數(shù)據(jù)分析 教程大禮包
  • 回復(fù)【2】:領(lǐng)取 Python Flask 全套教程
  • 回復(fù)【3】:領(lǐng)取 某學(xué)院 機器學(xué)習 教程
  • 回復(fù)【4】:領(lǐng)取 爬蟲 教程

知識星球已經(jīng)50多人了,隨著人數(shù)的增多扭吁,價格之后會上漲密末,越早關(guān)注越多優(yōu)惠。星球的福利有很多:

  • 比如上面的教程,已經(jīng)提前在知識星球中分享
  • 可以發(fā)表一些問題,大家一塊解決
  • 我之后寫的電子書,錄制的教學(xué)視頻笆制,對于知識星球的朋友都是優(yōu)惠的(基本上免費)
  • 一些節(jié)假日會給大家發(fā)個紅包或者贈書
image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市涣达,隨后出現(xiàn)的幾起案子在辆,更是在濱河造成了極大的恐慌证薇,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件开缎,死亡現(xiàn)場離奇詭異棕叫,居然都是意外死亡,警方通過查閱死者的電腦和手機奕删,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門俺泣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人完残,你說我怎么就攤上這事伏钠。” “怎么了谨设?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵熟掂,是天一觀的道長。 經(jīng)常有香客問我扎拣,道長赴肚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任二蓝,我火速辦了婚禮誉券,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刊愚。我一直安慰自己踊跟,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布鸥诽。 她就那樣靜靜地躺著商玫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牡借。 梳的紋絲不亂的頭發(fā)上拳昌,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音钠龙,去河邊找鬼地回。 笑死,一個胖子當著我的面吹牛俊鱼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播畅买,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼并闲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谷羞?” 一聲冷哼從身側(cè)響起帝火,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤溜徙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后犀填,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蠢壹,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年九巡,在試婚紗的時候發(fā)現(xiàn)自己被綠了图贸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡冕广,死狀恐怖疏日,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撒汉,我是刑警寧澤沟优,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站睬辐,受9級特大地震影響挠阁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜溯饵,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一侵俗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瓣喊,春花似錦坡慌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至棵帽,卻和暖如春熄求,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逗概。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工弟晚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逾苫。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓卿城,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铅搓。 傳聞我的和親對象是個殘疾皇子瑟押,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容