Lucene search

K
nmapPatrik KarlssonNMAP:DAAP-GET-LIBRARY.NSE
HistoryJan 25, 2010 - 11:51 p.m.

daap-get-library NSE Script

2010-01-2523:51:44
Patrik Karlsson
nmap.org
87

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C

0.973 High

EPSS

Percentile

99.8%

Retrieves a list of music from a DAAP server. The list includes artist names and album and song titles.

Output will be capped to 100 items if not otherwise specified in the daap_item_limit script argument. A daap_item_limit below zero outputs the complete contents of the DAAP library.

Based on documentation found here: <http://www.tapjam.net/daap/&gt;.

Script Arguments

daap_item_limit

Changes the output limit from 100 songs. If set to a negative value, no limit is enforced.

slaxml.debug

See the documentation for the slaxml library.

http.host, http.max-body-size, http.max-cache-size, http.max-pipeline, http.pipeline, http.truncated-ok, http.useragent

See the documentation for the http library.

smbdomain, smbhash, smbnoguest, smbpassword, smbtype, smbusername

See the documentation for the smbauth library.

Example Usage

nmap -sV --script=daap-get-library &lt;target&gt;

Script Output

| daap-get-library:
|   BUBBA|TWO
|     Fever Ray
|       Fever Ray (Deluxe Edition)
|         Concrete Walls
|         I'm Not Done
|         Here Before
|         Now's The Only Time I Know
|         Stranger Than Kindness
|         Dry And Dusty
|         Keep The Streets Empty For Me
|         Triangle Walks
|         If I Had A Heart
|         Seven
|         When I Grow Up
|_        Coconut

Requires


local http = require "http"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"

description = [[
Retrieves a list of music from a DAAP server. The list includes artist
names and album and song titles.

Output will be capped to 100 items if not otherwise specified in the
<code>daap_item_limit</code> script argument. A
<code>daap_item_limit</code> below zero outputs the complete contents of
the DAAP library.

Based on documentation found here:
http://www.tapjam.net/daap/.
]]

---
-- @args daap_item_limit Changes the output limit from 100 songs. If set to a negative value, no limit is enforced.
--
-- @output
-- | daap-get-library:
-- |   BUBBA|TWO
-- |     Fever Ray
-- |       Fever Ray (Deluxe Edition)
-- |         Concrete Walls
-- |         I'm Not Done
-- |         Here Before
-- |         Now's The Only Time I Know
-- |         Stranger Than Kindness
-- |         Dry And Dusty
-- |         Keep The Streets Empty For Me
-- |         Triangle Walks
-- |         If I Had A Heart
-- |         Seven
-- |         When I Grow Up
-- |_        Coconut

author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}


-- Version 0.2
-- Created 01/14/2010 - v0.1 - created by Patrik Karlsson
-- Revised 01/23/2010 - v0.2 - changed to port_or_service, added link to documentation, limited output to 100 songs or to daap_item_limit script argument.

portrule = shortport.port_or_service(3689, "daap")

--- Gets the name of the library from the server
--
-- @param host table containing an ip field.
-- @param port table containing number and protocol fields.
-- @return string containing the name of the library
function getLibraryName( host, port )
  local libname, pos
  local url = "daap://" .. host.ip .. "/server-info"
  local response = http.get( host, port, url, nil, nil, nil)

  if response == nil or response.body == nil or response.body=="" then
    return
  end

  pos = string.find(response.body, "minm")

  if pos > 0 then
    pos = pos + 4
    libname, pos = string.unpack( ">s4", response.body, pos )
  end

  return libname
end

--- Reads the first item value specified by name
--
-- @param data string containing the unparsed item
-- @param name string containing the name of the value to read
-- @return number
local function getAttributeAsInt( data, name )

  local pos = string.find(data, name)
  local attrib

  if pos and pos > 0 then
    pos = pos + 4
    local len
    len, pos = string.unpack( ">I4", data, pos )

    if ( len ~= 4 ) then
      stdnse.debug1("Unexpected length returned: %d", len )
      return
    end

    attrib, pos = string.unpack( ">I4", data, pos )
  end

  return attrib

end

--- Gets the revision number for the library
--
-- @param host table containing an ip field.
-- @param port table containing number and protocol fields.
-- @return number containing the session identity received from the server
function getSessionId( host, port )

  local sessionid
  local response = http.get( host, port, "/login", nil, nil, nil )

  if response ~= nil then
    sessionid = getAttributeAsInt( response.body, "mlid")
  end

  return sessionid
end

--- Gets the revision number for the library
--
-- @param host table containing an ip field.
-- @param port table containing number and protocol fields.
-- @param sessionid number containing session identifier from <code>getSessionId</code>
-- @return number containing the revision number for the library
function getRevisionNumber( host, port, sessionid )
  local url = "/update?session-id=" .. sessionid .. "&revision-number=1"
  local revision
  local response = http.get( host, port, url, nil, nil, nil )

  if response ~= nil then
    revision = getAttributeAsInt( response.body, "musr")
  end

  return revision
end

--- Gets the database identity for the library
--
-- @param host table containing an ip field.
-- @param port table containing number and protocol fields.
-- @param sessionid number containing session identifier from <code>getSessionId</code>
-- @param revid number containing the revision id as retrieved from <code>getRevisionNumber</code>
function getDatabaseId( host, port, sessionid, revid )
  local url = "/databases?session-id=" .. sessionid .. "&revision-number=" .. revid
  local response = http.get( host, port, url, nil, nil, nil )
  local miid

  if response ~= nil then
    miid = getAttributeAsInt( response.body, "miid")
  end

  return miid
end

--- Gets a string item type from data
--
-- @param data string starting with the 4-bytes of length
-- @param pos number containing offset into data
-- @return pos number containing new position after reading string
-- @return value string containing the string item that was read
local function getStringItem( data, pos )
  local item, pos = string.unpack(">s4", data, pos)
  return pos, item
end

local itemFetcher = {}

itemFetcher["mikd"] = function( data, pos )  return getStringItem( data, pos ) end
itemFetcher["miid"] = itemFetcher["mikd"]
itemFetcher["minm"] = itemFetcher["mikd"]
itemFetcher["asal"] = itemFetcher["mikd"]
itemFetcher["asar"] = itemFetcher["mikd"]

--- Parses a single item (mlit)
--
-- @param data string containing the unparsed item starting at the first available tag
-- @param len number containing the length of the item
-- @return item table containing <code>mikd</code>, <code>miid</code>, <code>minm</code>,
-- <code>asal</code> and <code>asar</code> when available
parseItem = function( data, len )

  local pos, name, value = 1, nil, nil
  local item = {}

  while( len - pos > 0 ) do
    name, pos = string.unpack( "c4", data, pos )

    if itemFetcher[name] then
      pos, item[name] = itemFetcher[name](data, pos )
    else
      stdnse.debug1("No itemfetcher for: %s", name)
      break
    end

  end

  return item

end

--- Request and process all music items
--
-- @param host table containing an ip field.
-- @param port table containing number and protocol fields.
-- @param sessionid number containing session identifier from <code>getSessionId</code>
-- @param dbid number containing database id from <code>getDatabaseId</code>
-- @param limit number containing the maximum amount of songs to return
-- @return table containing the following structure [artist][album][songs]
function getItems( host, port, sessionid, revid, dbid, limit )
  local meta = "dmap.itemid,dmap.itemname,dmap.itemkind,daap.songalbum,daap.songartist"
  local url = "/databases/" .. dbid .. "/items?type=music&meta=" .. meta .. "&session-id=" .. sessionid .. "&revision-number=" .. revid
  local response = http.get( host, port, url, nil, nil, nil )
  local item, data, pos, len
  local items = {}
  local limit = limit or -1

  if response == nil then
    return
  end

  -- get our position to the list of items
  pos = string.find(response.body, "mlcl")
  pos = pos + 4

  while ( pos > 0 and pos + 8 < response.body:len() ) do

    -- find the next single item
    pos = string.find(response.body, "mlit", pos)
    pos = pos + 4

    len, pos = string.unpack( ">I4", response.body, pos )

    if ( pos < response.body:len() and pos + len < response.body:len() ) then
      data, pos = string.unpack( "c" .. len, response.body, pos )
    else
      break
    end

    -- parse a single item
    item = parseItem( data, len )

    local album = item.asal or "unknown"
    local artist= item.asar or "unknown"
    local song  = item.minm or ""

    if items[artist] == nil then
      items[artist] = {}
    end

    if items[artist][album] == nil then
      items[artist][album] = {}
    end

    if limit == 0 then
      break
    elseif limit > 0 then
      limit = limit - 1
    end

    table.insert( items[artist][album], song )

  end


  return items

end


action = function(host, port)

  local limit = tonumber(nmap.registry.args.daap_item_limit) or 100
  local libname = getLibraryName( host, port )

  if libname == nil then
    return
  end

  local sessionid = getSessionId( host, port )

  if sessionid == nil then
    return stdnse.format_output(true, "Libname: " .. libname)
  end

  local revid = getRevisionNumber( host, port, sessionid )

  if revid == nil then
    return stdnse.format_output(true, "Libname: " .. libname)
  end

  local dbid = getDatabaseId( host, port, sessionid, revid )

  if dbid == nil then
    return
  end

  local items = getItems( host, port, sessionid, revid, dbid, limit )

  if items == nil then
    return
  end

  local albums, songs, artists, results = {}, {}, {}, {}

  table.insert( results, libname )

  for artist, v in pairs(items) do
    albums = {}
    for album, v2 in pairs(v) do
      songs = {}
      for _, song in pairs( v2 ) do
        table.insert( songs, song )
      end
      table.insert( albums, album )
      table.insert( albums, songs )
    end
    table.insert( artists, artist )
    table.insert( artists, albums )
  end

  table.insert( results, artists )
  local output = stdnse.format_output( true, results )

  if limit > 0 then
    output = output .. string.format("\n\nOutput limited to %d items", limit )
  end

  return output

end

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C

0.973 High

EPSS

Percentile

99.8%

Related for NMAP:DAAP-GET-LIBRARY.NSE