From ff6b5b65a8b7af5dd283306c1a228461b30a0ba8 Mon Sep 17 00:00:00 2001 From: dmiller Date: Sat, 22 Sep 2018 05:19:24 +0000 Subject: [PATCH] Remove more bin.lua packings. --- nselib/coap.lua | 106 +++++++++++++------------------------------ nselib/dhcp.lua | 77 ++++++++++++++++---------------- nselib/mqtt.lua | 3 +- nselib/mysql.lua | 114 ++++++++++++++++++----------------------------- 4 files changed, 116 insertions(+), 184 deletions(-) diff --git a/nselib/coap.lua b/nselib/coap.lua index 636b67b38..49e0fc508 100644 --- a/nselib/coap.lua +++ b/nselib/coap.lua @@ -1,4 +1,3 @@ -local bin = require "bin" local comm = require "comm" local json = require "json" local lpeg = require "lpeg" @@ -329,22 +328,23 @@ COAP.header.build = function(options) code = COAP.header.codes.build(code) -- Build the fixed portion of the header. - local pkt = "" ver = ver << 6 mtype = mtype << 4 - pkt = pkt .. bin.pack("C", ver | mtype | tkl) - pkt = pkt .. code - pkt = pkt .. bin.pack(">S", id) - pkt = pkt .. token + local pkt = { + string.pack("B", ver | mtype | tkl), + code, + string.pack(">I2", id), + token, + } -- Include optional portions of the header. if options["options"] then - pkt = pkt .. COAP.header.options.build(options.options) + pkt[#pkt+1] = COAP.header.options.build(options.options) end - return pkt + return table.concat(pkt) end --- Parses a CoAP message header. @@ -361,9 +361,6 @@ end -- string containing the error message on failure. COAP.header.parse = function(buf, pos) assert(type(buf) == "string") - if #buf < 4 then - return false, "Cannot parse a string of less than four bytes." - end if not pos or pos == 0 then pos = 1 @@ -371,14 +368,11 @@ COAP.header.parse = function(buf, pos) assert(type(pos) == "number") assert(pos <= #buf) - if pos + 4 - 1 > #buf then + if #buf - pos + 1 < 4 then return false, "Fixed header extends past end of buffer." end - local pos, ver_type_tkl, code, id = bin.unpack("CA>S", buf, pos) - if not pos then - return false, "Failed to parse fixed header." - end + local ver_type_tkl, code, id, pos = string.unpack(">Bc1I2", buf, pos) -- Parse the fixed header. local hdr = {} @@ -486,7 +480,7 @@ COAP.header.codes.build = function(name) class = class << 5 - return bin.pack("C", class | detail) + return string.pack("B", class | detail) end --- Parses a CoAP request or response code. @@ -511,7 +505,7 @@ COAP.header.codes.parse = function(buf, pos) assert(type(pos) == "number") assert(pos <= #buf) - local pos, id = bin.unpack("C", buf, pos) + local id, pos = string.unpack("B", buf, pos) if not pos then return false, id end @@ -1462,20 +1456,8 @@ COAP.header.options.value.uint.build = function(val) if val == 0 then return "" end - - if val <= 255 then - return bin.pack("C", val) - end - - if val <= 65535 then - return bin.pack(">S", val) - end - - if val <= 16777215 then - return bin.pack(">I", val):sub(2, 5) - end - - return bin.pack(">I", val) + -- strip leading null bytes to use smallest space + return string.pack(">I16", val):gsub("^\0*","") end --- Parses a CoAP message Uint header option value. @@ -1490,25 +1472,15 @@ end COAP.header.options.value.uint.parse = function(buf) assert(type(buf) == "string") assert(#buf >= 0) - assert(#buf <= 4) + assert(#buf <= 16) if #buf == 0 then return 0 end - local val, pos - if #buf == 1 then - pos, val = bin.unpack("C", buf) - elseif #buf == 2 then - pos, val = bin.unpack(">S", buf) - elseif #buf == 3 then - pos, val = bin.unpack(">I", string.char(0x00) .. buf) - else - pos, val = bin.unpack(">I", buf) - end + local val = string.unpack(">I" .. #buf, buf) -- There should be no way for this to fail. - assert(pos) assert(val) assert(type(val) == "number") @@ -1567,19 +1539,18 @@ COAP.header.options.delta_length.build = function(delta, length) end if num <= 268 then - return 13, bin.pack("C", num - 13) + return 13, string.pack("B", num - 13) end - return 14, bin.pack(">S", num - 269) + return 14, string.pack(">I2", num - 269) end local d1, d2 = build(delta) local l1, l2 = build(length) d1 = d1 << 4 - bin.pack("C", d1 | l1) - return bin.pack("C", d1 | l1) .. d2 .. l2 + return string.pack("B", d1 | l1) .. d2 .. l2 end --- Parse the variable-length option delta and length field. @@ -1613,7 +1584,7 @@ COAP.header.options.delta_length.parse = function(buf, pos) assert(type(pos) == "number") assert(pos <= #buf) - local pos, delta_and_length = bin.unpack("C", buf, pos) + local delta_and_length, pos = string.unpack("B", buf, pos) if not pos then return false, nil, nil, delta_and_length end @@ -1636,20 +1607,20 @@ COAP.header.options.delta_length.parse = function(buf, pos) if delta == 13 then required_bytes = required_bytes + 1 - dspec = "C" + dspec = "B" elseif delta == 14 then required_bytes = required_bytes + 2 delta = 269 - dspec = ">S" + dspec = ">I2" end if length == 13 then required_bytes = required_bytes + 1 - lspec = "C" + lspec = "B" elseif length == 14 then required_bytes = required_bytes + 2 length = 269 - lspec = ">S" + lspec = ">I2" end if pos + required_bytes - 1 > #buf then @@ -1659,7 +1630,7 @@ COAP.header.options.delta_length.parse = function(buf, pos) -- Extract the remaining bytes of each field. if dspec then local num - pos, num = bin.unpack(dspec, buf, pos) + num, pos = string.unpack(dspec, buf, pos) if not pos then return false, nil, nil, num end @@ -1668,7 +1639,7 @@ COAP.header.options.delta_length.parse = function(buf, pos) if lspec then local num - pos, num = bin.unpack(lspec, buf, pos) + num, pos = string.unpack(lspec, buf, pos) if not pos then return false, nil, nil, num end @@ -2426,7 +2397,7 @@ local tests = { { {["name"] = "etag", ["value"] = "ETAGETAG"}, }, - bin.pack("CA", 0x48, "ETAGETAG") + "\x48ETAGETAG" }, { -- Before @@ -2450,7 +2421,7 @@ local tests = { {["name"] = "uri_path", ["value"] = "foo"}, {["name"] = "max_age", ["value"] = 0}, }, - bin.pack("CAC", 0xB3, "foo", 0x30) + "\xB3foo\x30" }, { -- Before @@ -2463,7 +2434,7 @@ local tests = { {["name"] = "uri_path", ["value"] = ".well-known"}, {["name"] = "uri_path", ["value"] = "core"}, }, - bin.pack("CACA", 0xBB, ".well-known", 0x04, "core") + "\xBB.well-known\x04core" }, { -- Before @@ -2482,14 +2453,7 @@ local tests = { {["name"] = "uri_path", ["value"] = "core"}, {["name"] = "max_age", ["value"] = 0}, }, - bin.pack( - "CACCACAC", - 0x48, "ETAGETAG", -- ID: 4, Delta: 4 - 0x10, -- ID: 5, Delta: 1 - 0x6B, ".well-known", -- ID: 11, Delta: 6 - 0x04, "core", -- ID: 11, Delta: 0 - 0x30 -- ID: 14, Delta: 3 - ) + "\x48ETAGETAG\x10\x6B.well-known\x04core\x30" }, } @@ -2537,15 +2501,7 @@ local tests = { {["name"] = "uri_path", ["value"] = "core"}, }, }, - bin.pack( - "CC>SACACA", - 0x48, - 0x01, - 0x1234, - "nmapcoap", - 0xBB, ".well-known", - 0x04, "core" - ) + "\x48\x01\x12\x34nmapcoap\xBB.well-known\x04core" }, } diff --git a/nselib/dhcp.lua b/nselib/dhcp.lua index 575c508dc..bffa0cae0 100644 --- a/nselib/dhcp.lua +++ b/nselib/dhcp.lua @@ -14,7 +14,6 @@ -- o Added possibility to override transaction id -- o Added WPAD action -local bin = require "bin" local datetime = require "datetime" local ipOps = require "ipOps" local math = require "math" @@ -67,7 +66,7 @@ local function read_ip(data, pos, length) local results = {} for i=1, length, 4 do local value - pos, value = bin.unpack(">I", data, pos) + value, pos = string.unpack(">I4", data, pos) table.insert(results, ipOps.fromdword(value)) end @@ -75,7 +74,7 @@ local function read_ip(data, pos, length) end else local value - pos, value = bin.unpack(">I", data, pos) + value, pos = string.unpack(">I4", data, pos) return pos, ipOps.fromdword(value) end @@ -89,7 +88,8 @@ end --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_string(data, pos, length) - return bin.unpack(string.format("A%d", length), data, pos) + local value, pos = string.unpack(("c%d"):format(length), data, pos) + return pos, value end ---Read a single byte. Print an error if the length isn't 1. @@ -105,7 +105,8 @@ local function read_1_byte(data, pos, length) pos = pos + length return pos, nil end - return bin.unpack("C", data, pos) + local value, pos = string.unpack("B", data, pos) + return pos, value end ---Read a message type. This is a single-byte value that's looked up in the request_types_str @@ -163,7 +164,8 @@ local function read_2_bytes(data, pos, length) pos = pos + length return pos, nil end - return bin.unpack(">S", data, pos) + local value, pos = string.unpack(">I2", data, pos) + return pos, value end @@ -185,7 +187,7 @@ local function read_2_bytes_list(data, pos, length) local results = {} for i=1, length, 2 do local value - pos, value = bin.unpack(">S", data, pos) + value, pos = string.unpack(">I2", data, pos) table.insert(results, value) end @@ -207,7 +209,8 @@ local function read_4_bytes(data, pos, length) pos = pos + length return pos, nil end - return bin.unpack(">I", data, pos) + local value, pos = string.unpack(">I4", data, pos) + return pos, value end ---Read a 4-byte unsigned little endian value, and interpret it as a time offset value. Print an @@ -225,7 +228,7 @@ local function read_time(data, pos, length) pos = pos + length return pos, nil end - pos, result = bin.unpack(">I", data, pos) + result, pos = string.unpack(">I4", data, pos) return pos, datetime.format_time(result) end @@ -426,34 +429,32 @@ function dhcp_build(request_type, ip_address, mac_address, options, request_opti end -- Header - packet = packet .. bin.pack(">CCCC", overrides['op'] or 1, overrides['htype'] or 1, overrides['hlen'] or 6, overrides['hops'] or 0) -- BOOTREQUEST, 10mb ethernet, 6 bytes long, 0 hops + packet = packet .. string.pack(">BBBB", overrides['op'] or 1, overrides['htype'] or 1, overrides['hlen'] or 6, overrides['hops'] or 0) -- BOOTREQUEST, 10mb ethernet, 6 bytes long, 0 hops packet = packet .. ( overrides['xid'] or transaction_id ) -- Transaction ID = - packet = packet .. bin.pack(">SS", overrides['secs'] or 0, overrides['flags'] or 0x0000) -- Secs, flags + packet = packet .. string.pack(">I2I2", overrides['secs'] or 0, overrides['flags'] or 0x0000) -- Secs, flags packet = packet .. ip_address -- Client address - packet = packet .. bin.pack("I", overrides['cookie'] or 0x63825363) -- Magic cookie + packet = packet .. string.pack(">I4", overrides['cookie'] or 0x63825363) -- Magic cookie -- Options - packet = packet .. bin.pack(">CCC", 0x35, 1, request_type) -- Request type + packet = packet .. string.pack(">BBB", 0x35, 1, request_type) -- Request type for _, option in ipairs(options or {}) do - packet = packet .. bin.pack(">C", option.number) - if ( "string" == option.type ) then - packet = packet .. bin.pack("p", option.value) - elseif( "ip" == option.type ) then - packet = packet .. bin.pack(">CI", 4, option.value) + packet = packet .. string.pack(">B", option.number) + if ( "string" == option.type or "ip" == option.type ) then + packet = packet .. string.pack("s1", option.value) end end - packet = packet .. bin.pack(">CCA", 0x37, #request_options, request_options) -- Request options - packet = packet .. bin.pack(">CCI", 0x33, 4, lease_time or 1) -- Lease time + packet = packet .. string.pack(">Bs1", 0x37, request_options) -- Request options + packet = packet .. string.pack(">BBI4", 0x33, 4, lease_time or 1) -- Lease time - packet = packet .. bin.pack(">C", 0xFF) -- Termination + packet = packet .. "\xFF" -- Termination return true, strbuf.dump(packet) end @@ -473,26 +474,26 @@ function dhcp_parse(data, transaction_id) local result = {} -- Receive the first bit and make sure we got the correct operation back - pos, result['op'], result['htype'], result['hlen'], result['hops'] = bin.unpack(">CCCC", data, pos) + result.op, result.htype, result.hlen, result.hops, pos = string.unpack(">BBBB", data, pos) if(result['op'] ~= 2) then return false, string.format("DHCP server returned invalid reply ('op' wasn't BOOTREPLY (it was 0x%02x))", result['op']) end -- Confirm the transaction id - pos, result['xid'] = bin.unpack("A4", data, pos) + result.xid, pos = string.unpack("c4", data, pos) if(result['xid'] ~= transaction_id) then return false, string.format("DHCP server returned invalid reply (transaction id didn't match (%s != %s))", result['xid'], transaction_id) end -- Unpack the secs, flags, addresses, sname, and file - pos, result['secs'], result['flags'] = bin.unpack(">SS", data, pos) - pos, result['ciaddr'] = bin.unpack(">I", data, pos) - pos, result['yiaddr'] = bin.unpack(">I", data, pos) - pos, result['siaddr'] = bin.unpack(">I", data, pos) - pos, result['giaddr'] = bin.unpack(">I", data, pos) - pos, result['chaddr'] = bin.unpack("A16", data, pos) - pos, result['sname'] = bin.unpack("A64", data, pos) - pos, result['file'] = bin.unpack("A128", data, pos) + result.secs, result.flags, + result.ciaddr, + result.yiaddr, + result.siaddr, + result.giaddr, + result.chaddr, + result.sname, + result.file, pos = string.unpack(">I2I2 I4I4I4I4 c16 c64 c128", data, pos) -- Convert the addresses to strings result['ciaddr_str'] = ipOps.fromdword(result['ciaddr']) @@ -501,7 +502,7 @@ function dhcp_parse(data, transaction_id) result['giaddr_str'] = ipOps.fromdword(result['giaddr']) -- Confirm the cookie - pos, result['cookie'] = bin.unpack(">I", data, pos) + result.cookie, pos = string.unpack(">I4", data, pos) if(result['cookie'] ~= 0x63825363) then return false, "DHCP server returned invalid reply (the magic cookie was invalid)" end @@ -515,7 +516,7 @@ function dhcp_parse(data, transaction_id) end local option, length - pos, option, length = bin.unpack(">CC", data, pos) + option, length, pos = string.unpack(">BB", data, pos) -- Check for termination condition if(option == 0xFF) then @@ -611,10 +612,10 @@ end --@return The parsed response, as a table. function make_request(target, request_type, ip_address, mac_address, options, request_options, overrides, lease_time) -- A unique id that identifies this particular session (and lets us filter out what we don't want to see) - local transaction_id = overrides and overrides['xid'] or bin.pack("I", ipOps.todword(ip_address)), mac_address, options, request_options, overrides, lease_time, transaction_id) + local status, packet = dhcp_build(request_type, ipOps.ip_to_str(ip_address), mac_address, options, request_options, overrides, lease_time, transaction_id) if(not(status)) then stdnse.debug1("dhcp: Couldn't build packet: " .. packet) return false, "Couldn't build packet: " .. packet diff --git a/nselib/mqtt.lua b/nselib/mqtt.lua index 6e20ed9e7..822ef680a 100644 --- a/nselib/mqtt.lua +++ b/nselib/mqtt.lua @@ -920,7 +920,8 @@ MQTT.utf8_parse = function(buf, pos) return false, ("Buffer at position %d has no space for a %d-byte UTF-8 string."):format(pos, str_length) end - return string.unpack(">s2", buf, pos) + local value, pos = string.unpack(">s2", buf, pos) + return pos, value end --- Prefix the body of an MQTT packet with a fixed header. diff --git a/nselib/mysql.lua b/nselib/mysql.lua index 2c2143d28..2e84431b5 100644 --- a/nselib/mysql.lua +++ b/nselib/mysql.lua @@ -7,7 +7,6 @@ -- -- @author Patrik Karlsson -local bin = require "bin" local nmap = require "nmap" local stdnse = require "stdnse" local string = require "string" @@ -91,7 +90,7 @@ local function decodeHeader( data, pos ) local response = {} local pos, tmp = pos or 1, 0 - pos, tmp = bin.unpack( "> 24 ) @@ -121,42 +120,38 @@ function receiveGreeting( socket ) end local is_error - pos, is_error = bin.unpack( "C", data, pos ) + is_error, pos = string.unpack("B", data, pos) if ( is_error == 0xff ) then - pos, response.errorcode = bin.unpack( " 0 then - pos, tmp, _ = bin.unpack("A" .. (math.max(13, auth_plugin_len - 8) - 1) .. "x", data, pos) + tmp, pos = string.unpack("c" .. (math.max(13, auth_plugin_len - 8) - 1) .. "x", data, pos) response.salt = response.salt .. tmp end if response.extcapabilities & ExtCapabilities.SupportsAuthPlugins > 0 then - response.auth_plugin_name = bin.unpack("z", data, pos) + response.auth_plugin_name = string.unpack("z", data, pos) end end elseif response.proto == 9 then - pos, response.auth_plugin_data = bin.unpack( "z", data, pos ) + response.auth_plugin_data, pos = string.unpack( "z", data, pos ) else stdnse.debug2("Unknown MySQL protocol version: %d", response.proto) end @@ -189,8 +184,8 @@ local function createLoginHash(pass, salt) hash_stage3 = openssl.sha1( salt .. hash_stage2 ) for pos=1, hash_stage1:len() do - _, b1 = bin.unpack( "C", hash_stage1, pos ) - _, b2 = bin.unpack( "C", hash_stage3, pos ) + b1 = string.unpack( "B", hash_stage1, pos ) + b2 = string.unpack( "B", hash_stage3, pos ) reply[pos] = string.char( b2 ~ b1 ) end @@ -244,7 +239,7 @@ function loginRequest( socket, params, username, password, salt ) hash = createLoginHash( password, salt ) end - local packet = bin.pack( " 0 then - pos, response.errorcode = bin.unpack( "length and type function decodeField( data, pos ) - local header, len - local def, _ + local _ local field = {} - pos, len = bin.unpack( "C", data, pos ) - pos, field.catalog = bin.unpack( "A" .. len, data, pos ) - - pos, len = bin.unpack( "C", data, pos ) - pos, field.database = bin.unpack( "A" .. len, data, pos ) - - pos, len = bin.unpack( "C", data, pos ) - pos, field.table = bin.unpack( "A" .. len, data, pos ) - - pos, len = bin.unpack( "C", data, pos ) - pos, field.orig_table = bin.unpack( "A" .. len, data, pos ) - - pos, len = bin.unpack( "C", data, pos ) - pos, field.name = bin.unpack( "A" .. len, data, pos ) - - pos, len = bin.unpack( "C", data, pos ) - pos, field.orig_name = bin.unpack( "A" .. len, data, pos ) - - -- should be 0x0C - pos, _ = bin.unpack( "C", data, pos ) - - -- charset, in my case 0x0800 - pos, _ = bin.unpack( " 0 then - local _, b = bin.unpack("C", data, pos ) + local b = string.unpack("B", data, pos ) -- Is this the EOF packet? if b == EOF_MARKER then @@ -465,13 +441,11 @@ end -- @return number containing the amount of fields function decodeResultSetHeader( data ) - local _, fields - if data:len() ~= HEADER_SIZE + 1 then return false, "Result set header was incorrect" end - _, fields = bin.unpack( "C", data, HEADER_SIZE + 1 ) + local fields = string.unpack( "B", data, HEADER_SIZE + 1 ) return true, fields end @@ -486,16 +460,16 @@ end -- @return rows table containing row tables function decodeDataPackets( data, count ) - local len, pos = 0, 1, 1 - local header, row, rows = {}, {}, {} + local pos = 1 + local rows = {} - while pos < data:len() do - row = {} + while pos <= data:len() do + local row = {} + local header pos, header = decodeHeader( data, pos ) for i=1, count do - pos, len = bin.unpack("C", data, pos ) - pos, row[i] = bin.unpack("A" .. len, data, pos) + row[i], pos = string.unpack("s1", data, pos) end table.insert( rows, row ) @@ -521,7 +495,7 @@ function sqlQuery( socket, query ) local packet, packet_len, pos, header local status, fields, field_count, rows, rs - packet = bin.pack("