diff --git a/CHANGELOG b/CHANGELOG index 8cb0c70f9..f448c4f37 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ #Nmap Changelog ($Id$); -*-text-*- +o [NSE][GH#1504] Updates TN3270.lua and adds argument to disable TN3270E + [Soldier of Fortran] + o [GH#1504] RMI parser could crash when encountering invalid input [Clément Notin] diff --git a/nselib/tn3270.lua b/nselib/tn3270.lua index a3ab14771..797921d81 100644 --- a/nselib/tn3270.lua +++ b/nselib/tn3270.lua @@ -61,12 +61,13 @@ Telnet = { EOR = "\239" }, + -- Thesse are the options we accept for telnet options = { BINARY = "\000", EOR = "\025", TTYPE = "\024", - --TN3270 = "\028", - TN3270 = "\040" -- Technically TN3270E + TN3270 = "\028", + TN3270E = "\040" }, command = { @@ -251,6 +252,7 @@ Telnet = { telnet_state = 0, -- same as TNS_DATA to begin with server_options = {}, client_options = {}, + unsupported_opts = {}, sb_options = '', connected_lu = '', connected_dtype= '', @@ -452,6 +454,7 @@ Telnet = { local TNS_DONT = 5 local TNS_SB = 6 local TNS_SB_IAC = 7 + local supported = false local DO_reply = self.commands.IAC .. self.commands.DO local DONT_reply = self.commands.IAC .. self.commands.DONT local WILL_reply = self.commands.IAC .. self.commands.WILL @@ -488,48 +491,72 @@ Telnet = { elseif data == self.commands.SB then self.telnet_state = TNS_SB end elseif self.telnet_state == TNS_WILL then - -- I know if could use a for loop here with ipairs() but i find this easier to read - if data == self.options.BINARY or data == self.options.EOR or - data == self.options.TTYPE or data == self.options.TN3270 then + stdnse.debug(3, "[TELNET] IAC WILL 0x%s?", stdnse.tohex(data)) + for _,v in pairs(self.options) do -- check to see if we support this sub option (SB) + if v == data then + stdnse.debug(3, "[TELNET] IAC DO 0x%s", stdnse.tohex(data)) + supported = true + break + end + end -- end of checking options + for _,v in pairs(self.unsupported_opts) do + if v == data then + stdnse.debug(3, "[TELNET] IAC DONT 0x%s (disabled)", stdnse.tohex(data)) + supported = false + end + end + if supported then if not self.server_options[data] then -- if we haven't already replied to this, let's reply self.server_options[data] = true self:send_data(DO_reply..data) - stdnse.debug(3, "Sent Will Reply: " .. data) + stdnse.debug(3, "[TELNET] Sent Will Reply: 0x%s", stdnse.tohex(data)) self:in3270() end else self:send_data(DONT_reply..data) - stdnse.debug(3, "Sent Don't Reply: " .. data) + stdnse.debug(3, "[TELNET] Sent Don't Reply: 0x%s", stdnse.tohex(data)) end self.telnet_state = TNS_DATA elseif self.telnet_state == TNS_WONT then if self.server_options[data] then self.server_options[data] = false self:send_data(DONT_reply..data) - stdnse.debug(3, "Sent Don't Reply: " .. data) + stdnse.debug(3, "[TELNET] Sent Don't Reply: 0x%s", stdnse.tohex(data)) self:in3270() end self.telnet_state = TNS_DATA elseif self.telnet_state == TNS_DO then - if data == self.options.BINARY or data == self.options.EOR or - data == self.options.TTYPE or data == self.options.TN3270 then - -- data == self.options.STARTTLS -- ssl encryption to be added later + stdnse.debug(3, "[TELNET] IAC DO 0x%s?", stdnse.tohex(data)) + for _,v in pairs(self.options) do -- check to see if we support this sub option (SB) + if v == data then + stdnse.debug(3, "[TELNET] IAC WILL 0x%s", stdnse.tohex(data)) + supported = true + break + end + end -- end of checking options + for _,v in pairs(self.unsupported_opts) do + if v == data then + stdnse.debug(3, "[TELNET] IAC WONT 0x%s (disabled)", stdnse.tohex(data)) + supported = false + end + end + if supported then if not self.client_options[data] then self.client_options[data] = true self:send_data(WILL_reply..data) - stdnse.debug(3, "Sent Do Reply: " .. data) + stdnse.debug(3, "[TELNET] Sent Do Reply: 0x%s" , stdnse.tohex(data)) self:in3270() end else self:send_data(WONT_reply..data) - stdnse.debug(3, "Got unsupported Do. Sent Won't Reply: " .. data .. " " .. self.telnet_data) + stdnse.debug(3, "[TELNET] Got unsupported Do. Sent Won't Reply: " .. data .. " " .. self.telnet_data) end self.telnet_state = TNS_DATA elseif self.telnet_state == TNS_DONT then if self.client_options[data] then self.client_options[data] = false self:send_data(WONT_reply .. data) - stdnse.debug(3, "Sent Wont Reply: " .. data) + stdnse.debug(3, "[TELNET] Sent Wont Reply: 0x%s", stdnse.tohex(data)) self:in3270() end self.telnet_state = TNS_DATA @@ -540,7 +567,7 @@ Telnet = { self.sb_options = self.sb_options .. data end elseif self.telnet_state == TNS_SB_IAC then - stdnse.debug(3, "Processing SB options") + stdnse.debug(3, "[TELNET] Processing SB options") -- self.sb_options = self.sb_options .. data -- looks like this is a bug? Why append F0 to the end? if data == self.commands.SE then self.telnet_state = TNS_DATA @@ -553,12 +580,13 @@ Telnet = { self.device_type .. self.commands.IAC .. self.commands.SE ) - elseif self.client_options[self.options.TN3270] and - self.sb_options:sub(1,1) == self.options.TN3270 then + elseif (self.client_options[self.options.TN3270] or self.client_options[self.options.TN3270E]) and + (self.sb_options:sub(1,1) == self.options.TN3270 or + self.sb_options:sub(1,1) == self.options.TN3270E) then if not self:negotiate_tn3270() then return false end - stdnse.debug(3, "Done Negotiating Options") + stdnse.debug(3, "[TELNET] Done Negotiating Options") else self.telnet_state = TNS_DATA end @@ -587,7 +615,7 @@ Telnet = { if self.connected_lu == '' then self:send_data(self.commands.IAC .. self.commands.SB .. - self.options.TN3270 .. + self.options.TN3270E .. self.tncommands.DEVICETYPE .. self.tncommands.REQUEST .. self.device_type .. @@ -597,7 +625,7 @@ Telnet = { stdnse.debug(3,"[TN3270] Sending LU: %s", self.connected_lu) self:send_data(self.commands.IAC .. self.commands.SB .. - self.options.TN3270 .. + self.options.TN3270E .. self.tncommands.DEVICETYPE .. self.tncommands.REQUEST .. self.device_type .. @@ -631,7 +659,7 @@ Telnet = { -- since We've connected lets send our options self:send_data(self.commands.IAC .. self.commands.SB .. - self.options.TN3270 .. + self.options.TN3270E .. self.tncommands.FUNCTIONS .. self.tncommands.REQUEST .. --self.tncommands.RESPONSES .. -- we'll only support basic 3270E mode @@ -649,7 +677,7 @@ Telnet = { -- functions really but we'll agree to whatever they want self:send_data(self.commands.IAC .. self.commands.SB .. - self.options.TN3270 .. + self.options.TN3270E .. self.tncommands.FUNCTIONS .. self.tncommands.IS .. self.sb_options:sub(4,4) .. @@ -664,29 +692,32 @@ Telnet = { --- Check to see if we're in TN3270 in3270 = function ( self ) - if self.client_options[self.options.TN3270] then + if self.client_options[self.options.TN3270E] then + stdnse.debug(3,"[in3270] In TN3270E mode") if self.negotiated then + stdnse.debug(3,"[in3270] TN3270E negotiated") self.state = self.TN3270E_DATA end - elseif self.server_options[self.options.EOR] and - self.server_options[self.options.BINARY] and - self.client_options[self.options.EOR] and + elseif self.client_options[self.options.EOR] and self.client_options[self.options.BINARY] and - self.client_options[self.options.TTYPE] then + self.client_options[self.options.EOR] and + self.client_options[self.options.BINARY] and + self.client_options[self.options.TTYPE] then + stdnse.debug(3,"[in3270] In TN3270 mode") self.state = self.TN3270_DATA end if self.state == self.TN3270_DATA or self.state == self.TN3270E_DATA then -- since we're in TN3270 mode, let's create an empty buffer - stdnse.debug(3, "Creating Empty IBM-3278-2 Buffer") + stdnse.debug(3, "[in3270] Creating Empty IBM-3278-2 Buffer") for i=0, 1920 do self.buffer[i] = "\0" self.fa_buffer[i] = "\0" self.overwrite_buf[i] = "\0" end - stdnse.debug(3, "Empty Buffer Created. Length: " .. #self.buffer) + stdnse.debug(3, "[in3270] Empty Buffer Created. Length: " .. #self.buffer) end - stdnse.debug(3,"Current State: "..self.word_state[self.state]) + stdnse.debug(3,"[in3270] Current State: "..self.word_state[self.state]) end, --- Also known as process_eor @@ -1220,7 +1251,7 @@ Telnet = { for j = i,#self.fa_buffer do -- find end of field if (self.fa_buffer[j]:byte(1) & 0x20) == 0x20 then - stdnse.debug(3,"Writeable Area: %d Row: %d Col: %d Length: %d", i + 1, self:BA_TO_ROW(i + 1), self:BA_TO_COL(i + 2), j-i-1) + stdnse.debug(3,"[WRITEABLE] Area: %d Row: %d Col: %d Length: %d", i + 1, self:BA_TO_ROW(i + 1), self:BA_TO_COL(i + 2), j-i-1) table.insert(writeable_list, {i + 1, j-i-1}) break end @@ -1240,13 +1271,13 @@ Telnet = { end end --local buff = self:get_screen() - stdnse.debug(3, "Looking for: " ..str) + stdnse.debug(3, "[FIND] Looking for: %s", tostring(str)) local i, j = string.find(buff, str) if i == nil then - stdnse.debug(3, "Couldn't find: " ..str) + stdnse.debug(3, "[FIND] Couldn't find: %s", tostring(str)) return false else - stdnse.debug(3, "Found String: " ..str) + stdnse.debug(3, "[FIND] Found String: %s", tostring(str)) return i , j end end, @@ -1262,10 +1293,10 @@ Telnet = { end local i, j = string.find(buff, '%w') if i ~= nil then - stdnse.debug(3, "Screen has text") + stdnse.debug(3, "[CLEAR] Screen has text") return false else - stdnse.debug(3, "Screen is Empty") + stdnse.debug(3, "[CLEAR] Screen is Empty") return true end end, @@ -1344,8 +1375,10 @@ Telnet = { get_lu = function ( self ) return self.connected_lu end, - - + disable_tn3270e = function ( self ) + stdnse.debug(3,"Disabling TN3270E") + table.insert(self.unsupported_opts,self.options.TN3270E) + end, overwrite_data = function ( self ) if not self:any_overwritten() then return false diff --git a/scripts/cics-enum.nse b/scripts/cics-enum.nse index b9866cb4e..b0deb65d4 100644 --- a/scripts/cics-enum.nse +++ b/scripts/cics-enum.nse @@ -57,6 +57,7 @@ found for CICS transaction IDs. -- 2015-07-04 - v0.1 - created by Soldier of Fortran -- 2015-11-14 - v0.2 - rewrote iterator -- 2017-01-22 - v0.3 - added authenticated CICS ID enumeration +-- 2019-02-01 - v0.4 - Removed TN3270E support (breaks location) -- -- @author Philip Young -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html @@ -90,6 +91,7 @@ Driver = { o.port = port o.options = options o.tn3270 = tn3270.Telnet:new() + o.tn3270:disable_tn3270e() return o end, connect = function( self ) @@ -263,6 +265,7 @@ Driver = { local function cics_test( host, port, commands, user, pass ) stdnse.debug("Checking for CICS") local tn = tn3270.Telnet:new() + tn:disable_tn3270e() local status, err = tn:initiate(host,port) local msg = 'Unable to get to CICS' local cics = false -- initially we're not at CICS diff --git a/scripts/cics-user-brute.nse b/scripts/cics-user-brute.nse index 49a617504..768813eff 100644 --- a/scripts/cics-user-brute.nse +++ b/scripts/cics-user-brute.nse @@ -34,6 +34,7 @@ CICS User ID brute forcing script for the CESL login screen. -- 2016-08-29 - v0.1 - created by Soldier of Fortran -- 2016-10-26 - v0.2 - Added RACF support -- 2017-01-23 - v0.3 - Rewrote script to use fields and skip enumeration to speed up testing +-- 2019-02-01 - v0.4 - Disabled new TN3270E support author = "Philip Young aka Soldier of Fortran" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" @@ -60,6 +61,7 @@ Driver = { o.port = port o.options = options o.tn3270 = tn3270.Telnet:new(brute.new_socket()) + o.tn3270:disable_tn3270e() return o end, connect = function( self ) @@ -120,9 +122,10 @@ Driver = { -- Ok we're good we're at CESL. Send the Userid and Password. local fields = self.tn3270:writeable() -- Get the writeable field areas - local user_loc = {fields[1][1],user} -- This is the 'UserID:' field - local pass_loc = {fields[3][1],pass} -- This is the 'Password:' field ([2] is a group ID) - stdnse.verbose('Trying CICS: ' .. user ..' : ' .. pass) + local user_loc = {fields[2][1],user} -- This is the 'UserID:' field + local pass_loc = {fields[4][1],pass} -- This is the 'Password:' field ([2] is a group ID) + stdnse.verbose('[BRUTE] Trying CICS: ' .. user ..' : ' .. pass) + stdnse.debug(3,"[BRUTE] Location:" .. fields[2][1] .. " x " .. fields[4][1]) self.tn3270:send_locations({user_loc,pass_loc}) self.tn3270:get_all_data() stdnse.debug(2,"Screen Received for User ID: %s/%s", user, pass) @@ -194,6 +197,7 @@ Driver = { local function cics_test( host, port, commands ) stdnse.verbose(2,"Checking for CICS Login Page") local tn = tn3270.Telnet:new() + tn:disable_tn3270e() local status, err = tn:initiate(host,port) local cesl = false -- initially we're not at CICS if not status then diff --git a/scripts/cics-user-enum.nse b/scripts/cics-user-enum.nse index e8917de4a..4124e1cb3 100644 --- a/scripts/cics-user-enum.nse +++ b/scripts/cics-user-enum.nse @@ -37,6 +37,7 @@ CICS User ID enumeration script for the CESL/CESN Login screen. -- @changelog -- 2016-08-29 - v0.1 - created by Soldier of Fortran -- 2016-12-19 - v0.2 - Added RACF support +-- 2019-02-01 - v0.3 - Disabled TN3270E support -- -- @author Philip Young -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html @@ -56,6 +57,7 @@ Driver = { o.port = port o.options = options o.tn3270 = tn3270.Telnet:new() + o.tn3270:disable_tn3270e() return o end, connect = function( self ) @@ -108,8 +110,8 @@ Driver = { end -- At this point we MUST be at CESL/CESN to try accounts. -- If we're not then we quit with an error - if not (self.tn3270:find('SIGN ON TO CICS') or self.tn3270:find("Signon to CICS")) then - local err = brute.Error:new( "Can't get to Transaction") + if not (self.tn3270:find('Type your userid and password')) then + local err = brute.Error:new( "Can't get to Transaction CESN") err:setRetry( true ) return false, err end @@ -158,6 +160,7 @@ Driver = { local function cics_test( host, port, commands, transaction ) stdnse.verbose(2,"Checking for CICS Login Page") local tn = tn3270.Telnet:new() + tn:disable_tn3270e() local status, err = tn:initiate(host,port) local cesl = false -- initially we're not at CICS if not status then @@ -176,7 +179,7 @@ local function cics_test( host, port, commands, transaction ) tn:get_all_data() tn:get_screen_debug(2) -- for debug purposes -- We should now be at CICS. Check if we're already at the logon screen - if tn:find('SIGN ON TO CICS') and tn:find("Signon to CICS") then + if tn:find('Type your userid and password') then stdnse.verbose(2,"At CICS Login Transaction") tn:disconnect() return true diff --git a/scripts/tn3270-screen.nse b/scripts/tn3270-screen.nse index 86133f7c4..59d8319c9 100644 --- a/scripts/tn3270-screen.nse +++ b/scripts/tn3270-screen.nse @@ -44,13 +44,15 @@ Hidden fields will be listed below the screen with (row, col) coordinates. -- -- @args tn3270-screen.commands a semi-colon separated list of commands you want to -- issue before printing the screen --- tn3270-screen.lu a logical unit you with to use fails if can't connect +-- tn3270-screen.lu specify a logical unit you with to use, fails if can't connect +-- tn3270-screen.disable_tn3270e disables TN3270 Enhanced mode -- -- -- @changelog -- 2015-05-30 - v0.1 - created by Soldier of Fortran -- 2015-11-14 - v0.2 - added commands argument -- 2018-09-07 - v0.3 - added support for Logical Units +-- 2019-02-01 - v0.4 - Added ability to disable TN3270E mode -- author = "Philip Young aka Soldier of Fortran" @@ -67,12 +69,17 @@ local hidden_field_mt = { action = function(host, port) local commands = stdnse.get_script_args(SCRIPT_NAME .. '.commands') + local disable_tn3270e = stdnse.get_script_args(SCRIPT_NAME .. '.disable_tn3270e') or false local lu = stdnse.get_script_args(SCRIPT_NAME .. '.lu') local t = tn3270.Telnet:new() - if lu then + if lu and not disable_tn3270e then stdnse.debug("Setting LU: %s", lu) t:set_lu(lu) end + + if disable_tn3270e then + t:disable_tn3270e() + end local status, err = t:initiate(host,port) if not status then stdnse.debug("Could not initiate TN3270: %s", err ) @@ -107,7 +114,9 @@ action = function(host, port) local out = stdnse.output_table() out.screen = t:get_screen() out["hidden fields"] = hidden - out["logical unit"]= t:get_lu() + if not disable_tn3270e then + out["logical unit"]= t:get_lu() + end return out end end diff --git a/scripts/tso-brute.nse b/scripts/tso-brute.nse index a1005e446..ff52c0ce5 100644 --- a/scripts/tso-brute.nse +++ b/scripts/tso-brute.nse @@ -75,6 +75,7 @@ Driver = { o.port = port o.options = options o.tn3270 = tn3270.Telnet:new(brute.new_socket()) + o.tn3270:disable_tn3270e() return o end, connect = function( self ) @@ -228,6 +229,7 @@ Driver = { local function tso_test( host, port, commands ) stdnse.debug("Checking for TSO") local tn = tn3270.Telnet:new() + tn:disable_tn3270e() local status, err = tn:initiate(host,port) local tso = false -- initially we're not at TSO logon panel local secprod = "RACF" @@ -259,7 +261,7 @@ local function tso_test( host, port, commands ) end tn:send_pf(3) tn:disconnect() - return tso, secprod, "Could not get to TSO. Try --script-args=tso-enum.commands='logon applid(tso)'. Aborting." + return tso, secprod, "Could not get to TSO. Try --script-args=tso-brute.commands='logon applid(tso)'. Aborting." end --- Tests the target to see if we can speed up brute forcing @@ -274,6 +276,7 @@ end local function tso_skip( host, port, commands ) stdnse.debug("Checking for IKJ56700A message skip") local tn = tn3270.Telnet:new() + tn:disable_tn3270e() stdnse.debug2("Connecting TN3270 to %s:%s", host.targetname or host.ip, port.number) local status, err = tn:initiate(host,port) stdnse.debug2("Displaying initial TN3270 Screen:") diff --git a/scripts/tso-enum.nse b/scripts/tso-enum.nse index bec164392..a73adbcbb 100644 --- a/scripts/tso-enum.nse +++ b/scripts/tso-enum.nse @@ -57,6 +57,7 @@ TSO user IDs have the following rules: -- 2015-10-30 - v0.2 - streamlined the code, relying on brute and unpwdb and -- renamed to tso-enum. -- 2017-1-13 - v0.3 - Fixed 'data' bug and added options checking to speedup +-- 2019-02-01 - v0.4 - Disabled TN3270 Enhanced support and fixed debug errors author = "Philip Young aka Soldier of Fortran" @@ -74,6 +75,7 @@ Driver = { o.port = port o.options = options o.tn3270 = tn3270.Telnet:new() + o.tn3270:disable_tn3270e() return o end, connect = function( self ) @@ -165,6 +167,7 @@ Driver = { local function tso_test( host, port, commands ) stdnse.debug("Checking for TSO") local tn = tn3270.Telnet:new() + tn:disable_tn3270e() local status, err = tn:initiate(host,port) local tso = false -- initially we're not at TSO logon panel local secprod = "RACF" @@ -211,6 +214,7 @@ end local function tso_skip( host, port, commands ) stdnse.debug("Checking for IKJ56700A message skip") local tn = tn3270.Telnet:new() + tn:disable_tn3270e() stdnse.debug2("Connecting TN3270 to %s:%s", host.targetname or host.ip, port.number) local status, err = tn:initiate(host,port) stdnse.debug2("Displaying initial TN3270 Screen:") diff --git a/scripts/vtam-enum.nse b/scripts/vtam-enum.nse index 8d8392c19..edf3cf8a4 100644 --- a/scripts/vtam-enum.nse +++ b/scripts/vtam-enum.nse @@ -57,7 +57,7 @@ found for application IDs. -- 2015-11-14 - v0.3 - rewrote iterator -- 2017-01-13 - v0.4 - Fixed 'macros' bug with default vtam screen and test -- and added threshold for macros screen checking --- +-- 2019-02-01 - v0.5 - Disabling Enhanced mode author = "Philip Young aka Soldier of Fortran" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" @@ -104,6 +104,7 @@ Driver = { o.port = port o.options = options o.tn3270 = tn3270.Telnet:new() + o.tn3270:disable_tn3270e() return o end, connect = function( self ) @@ -181,6 +182,7 @@ Driver = { -- @return status true on success, false on failure local function vtam_test( host, port, commands, macros) local tn = tn3270.Telnet:new() + tn:disable_tn3270e() local status, err = tn:initiate(host,port) stdnse.debug1("Testing if VTAM and 'logon applid' command supported") stdnse.debug2("Connecting TN3270 to %s:%s", host.targetname or host.ip, port.number)