diff --git a/nmap-service-probes b/nmap-service-probes index 45c64095a..9af15e914 100644 --- a/nmap-service-probes +++ b/nmap-service-probes @@ -9000,7 +9000,7 @@ match http m|^HTTP/1\.1 400 Bad Request\r\nDate: Mon, 21 Feb 2011 17:38:00 GMT\r match http m|^HTTP/1\.1 307 Temporary Redirect\r\n.*Content-Length: 0\r\nConnection: keep-alive\r\nServer: AmazonS3\r\n\r\n$|s p/Amazon S3 httpd/ match http m|^HTTP/1\.1 200 OK\nServer: BO/([\w._-]+)\nDate: .*\nContent-type: text/html\nPublic: GET, POST\nConnection: keep-alive\n\n| p/BO2K built-in httpd/ v/$1/ match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello, non-Bayeux request\. Yet another one$| p/Node.js/ i/Faye Bayeux protocol/ cpe:/a:nodejs:node.js/ -match http m|^HTTP/1\.0 500 Internal Server Error\r\nCONTENT-TYPE: text/html\r\nDate: .*\r\nServer: IBM_CICS_Transaction_Server/([\w._-]+)\(zOS\)\r\n| p/IBM CICS Transaction Server/ v/$1/ o|z/OS| cpe:/o:ibm:z%2fos/ +match http m|^HTTP/1\.[01] \d\d\d [^\r\n]*\r\nCONTENT-TYPE: text/html\r.*\nServer: IBM_CICS_Transaction_Server/([\w._-]+)\(zOS\)\r\n|s p/IBM CICS Transaction Server/ v/$1/ o|z/OS| cpe:/o:ibm:z%2fos/ match http m|^HTTP/1\.1 200 OK\r\nServer: corehttp-([\w._-]+)\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n
| p/CoreHTTP/ v/$1/ i/directory listing/
 # http://code.google.com/p/webfinger/
 match http m|^HTTP/1\.1 400 Bad request\r\n\r\n$| p/WebFinger httpd/
diff --git a/scripts/tso-brute.nse b/scripts/tso-brute.nse
index d7faed45e..0b093c548 100644
--- a/scripts/tso-brute.nse
+++ b/scripts/tso-brute.nse
@@ -94,48 +94,65 @@ Driver = {
 
     local commands = self.options['key1']
     local always_logon = self.options['key2']
+    local skip = self.options['skip']
     stdnse.debug(2,"Getting to TSO")
     local run = stdnse.strsplit(";%s*", commands)
+    stdnse.verbose(2,"Trying User ID/Password: %s/%s", user, pass)
     for i = 1, #run do
-      stdnse.debug(2,"%s: Issuing Command (#%s of %s): %s",user , i, #run ,run[i])
-      self.tn3270:send_cursor(run[i])
+      stdnse.debug(2,"Issuing Command (#%s of %s): %s", i, #run ,run[i])
+      if i == #run and run[i]:upper():find("LOGON APPLID") and skip then
+        stdnse.debug(2,"Trying User ID: %s", user)
+        self.tn3270:send_cursor(run[i] .. " DATA(" .. user .. ")")
+      elseif i == #run and skip then
+        stdnse.debug(2,"Trying User ID: %s", user)
+        self.tn3270:send_cursor(run[i] .. " " .. user)
+      else
+        self.tn3270:send_cursor(run[i])
+      end
       self.tn3270:get_all_data()
     end
 
-    if ( self.tn3270:find('NO USER APPLID AVAILABLE') ) then
-      local err = brute.Error:new( "No APPLID Available" )
-        -- This error occurs on too many concurrent application requests
-        -- it should be temporary.
-      err:setRetry( true )
+    if self.tn3270:find("***") then -- For ACF2/TopSecret if required
+      self.tn3270:send_enter()
+      self.tn3270:get_all_data()
+    end
+
+    if not self.tn3270:find("ENTER USERID")
+       and not self.tn3270:find("TSO/E LOGON")
+       and not self.tn3270:find("IKJ56710I INVALID USERID") then
+      local err = brute.Error:new( "TSO Unavailable" )
+        -- This error occurs on too many concurrent application requests it
+        -- should be temporary. We use the new setReduce function.
+      err:setReduce(true)
+      stdnse.debug(1,"TSO Unavailable for UserID %s", pass )
       return false, err
     end
 
-    stdnse.verbose(2,"Trying User ID/Password: %s/%s", user, pass)
-    stdnse.debug(2,"Sending User ID: %s", user)
-
-    self.tn3270:send_cursor(user)
-    self.tn3270:get_all_data()
-    if self.tn3270:find("***") then
-      self.tn3270:send_enter() -- some systems require an enter after sending the user
+    if not skip then
+      stdnse.debug(2,"Trying User ID: %s", user)
+      self.tn3270:send_cursor(user)
       self.tn3270:get_all_data()
     end
 
+
     stdnse.debug(2,"Screen Recieved for User ID: %s", user)
     self.tn3270:get_screen_debug(2)
 
     if not self.tn3270:find('Enter LOGON parameters below') then
       stdnse.debug(2,"Screen Recieved for User ID: %s", user)
       self.tn3270:get_screen_debug(2)
-    -- Sometimes mainframes get overloaded
+        -- This error occurs on too many concurrent application requests it
+        -- should be temporary. We use the new setReduce function.
       local err = brute.Error:new( "Not at TSO" )
-      err:setRetry( true )
+      err:setReduce(true)
+      stdnse.debug(1,"TSO Unavailable for UserID %s", pass )
       return false, err
     end
 
     if self.tn3270:find('not authorized to use TSO') then -- invalid user ID
       stdnse.debug(2,"Got Message: IKJ56420I Userid %s not authorized to use TSO.", user)
       -- Store the invalid ID in the registry so we don't keep trying it with subsequent passwords
-      -- when using default brute library.
+      -- when using the brute library.
       register_invalid(user)
       return false,  brute.Error:new( "User ID not authorized to use TSO" )
     else
@@ -143,10 +160,10 @@ Driver = {
       stdnse.debug(2,"%s is a valid TSO User ID. Trying Password: %s", string.upper(user), pass)
       if always_logon then
         local writeable = self.tn3270:writeable()
-        -- This turns on
+        -- This turns on the 'reconnect' which may boot users off
         self.tn3270:send_locations({{writeable[1][1],pass},{writeable[11][1],"S"}})
       else
-          self.tn3270:send_cursor(pass)
+        self.tn3270:send_cursor(pass)
       end
 
       self.tn3270:get_all_data()
@@ -159,6 +176,7 @@ Driver = {
       self.tn3270:get_screen_debug(2)
 
       if not always_logon and self.tn3270:find("already logged on") then
+        -- IKJ56425I LOGON rejected User already logged on to system
         register_invalid(user)
         return true, creds.Account:new(user, "", "User logged on. Skipped.")
       elseif not (self.tn3270:find("IKJ56421I") or
@@ -176,14 +194,13 @@ Driver = {
         -- TSS7143E Accessor ID Has Been Inactive Too Long
         -- TSS7120E PASSWORD VIOLATION THRESHOLD EXCEEDED
 
-        stdnse.verbose(2,"Valid User/Pass" .. user .. "/" .. pass.. "MSG:" .. self.tn3270:get_screen():sub(1,80))
+        -- The 'MSG allows testers to discern any relevant messages they may get for the account'
+        stdnse.verbose(2,"Valid User/Pass: " .. user .. "/" .. pass)
+        stdnse.verbose(3,"Valid MSG for " .. user .. "/" .. pass .. ": " .. self.tn3270:get_screen():sub(1,80))
         return true, creds.Account:new(user, pass, creds.State.VALID)
       else
-        stdnse.verbose(self.tn3270:get_screen():sub(1,80))
         return false, brute.Error:new( "Incorrect password" )
       end
-
-      -- IKJ56425I LOGON rejected User already logged on to system
     end
   end
 }
@@ -194,34 +211,98 @@ Driver = {
 -- @param port port NSE object
 -- @param commands script-args of commands to use to get to TSO
 -- @return status true on success, false on failure
-
+-- @return name of security product installed
 local function tso_test( host, port, commands )
-  local tso = false -- initially we're not at TSO logon panel
   stdnse.debug("Checking for TSO")
   local tn = tn3270.Telnet:new()
+  local status, err = tn:initiate(host,port)
+  local tso = false -- initially we're not at TSO logon panel
+  local secprod = "RACF"
+  tn:get_screen_debug(2) -- prints TN3270 screen to debug
+  if not status then
+    stdnse.debug("Could not initiate TN3270: %s", err )
+    return tso, "Could not Initiate TN3270"
+  end
+  local run = stdnse.strsplit(";%s*", commands)
+  for i = 1, #run do
+    stdnse.debug(2,"Issuing Command (#%s of %s): %s", i, #run ,run[i])
+    tn:send_cursor(run[i])
+    tn:get_all_data()
+  end
+  tn:get_screen_debug(2)
+
+  if tn:find("***") then
+    secprod = "TopSecret/ACF2"
+  end
+
+  if tn:find("ENTER USERID") or tn:find("TSO/E LOGON")  then
+    tso = true
+    -- Patch OA44855 removed the ability to enumerate users
+    tn:send_cursor("notreal")
+    tn:get_all_data()
+    if tn:find("IKJ56476I ENTER PASSWORD") then
+      return false, secprod, "Enumeration is not possible. PASSWORDPREPROMPT is set to ON."
+    end
+  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."
+end
+
+--- Tests the target to see if we can speed up brute forcing
+-- VTAM/USSTable will sometimes allow you to put the userid
+-- in the command area either through data() or just adding
+-- the userid. This function will test for both
+--
+-- @param host host NSE object
+-- @param port port NSE object
+-- @param commands script-args of commands to use to get to TSO
+-- @return status true on success, false on failure
+local function tso_skip( host, port, commands )
+  stdnse.debug("Checking for IKJ56700A message skip")
+  local tn = tn3270.Telnet:new()
   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:")
   tn:get_screen_debug(2) -- prints TN3270 screen to debug
   if not status then
     stdnse.debug("Could not initiate TN3270: %s", err )
-    return tso
+    return false
   end
-  stdnse.debug("Getting to TSO")
+  -- We're connected now to test.
+  local data = false
+  if commands:upper():find('LOGON APPLID') then
+    stdnse.debug(2,"Using LOGON command (%s) trying DATA() command", commands )
+    data = true
+  else
+    stdnse.debug(2,"Not using LOGON command, testing adding userid to command" )
+  end
+
   local run = stdnse.strsplit(";%s*", commands)
   for i = 1, #run do
-    stdnse.debug(1,"Issuing Command (#%s of %s): %s", i, #run ,run[i])
-    tn:send_cursor(run[i])
+    stdnse.debug(2,"Issuing Command (#%s of %s): %s", i, #run ,run[i])
+    if i == #run then
+      if data then
+        stdnse.debug(2,"Sending "..run[i].." DATA(FAKEUSER)")
+        tn:send_cursor(run[i].." DATA(FAKEUSER)")
+      else
+        stdnse.debug(2,"Sending "..run[i].." FAKEUSER")
+        tn:send_cursor(run[i].." FAKEUSER")
+      end
+    else
+      tn:send_cursor(run[i])
+    end
     tn:get_all_data()
   end
   tn:get_screen_debug(2)
 
-  if tn:find("ENTER USERID") or
-    tn:find("TSO/E LOGON")  then
-    tso = true
+  if tn:find("IKJ56710I INVALID USERID")     or
+     tn:find("Enter LOGON parameters below") then
+    stdnse.debug('IKJ56700A message skip supported')
+    return true
+  else
+    return false
   end
-  tn:disconnect()
-  return tso
 end
 
 -- Filter iterator for unpwdb usernames
@@ -252,20 +333,20 @@ action = function( host, port )
   -- because a user is only allowed to logon from one location. If you turn always_logon on
   -- it will logon if it finds a valid username/password, kicking that user off
   local always_logon = stdnse.get_script_args(SCRIPT_NAME .. '.always_logon') or false
-
-  if tso_test(host, port, commands) then
+  local tsotst, secprod, err = tso_test(host, port, commands)
+  if tsotst then
     stdnse.debug("Starting TSO Brute Force")
-    local options = { key1 = commands, key2 = always_logon }
+    local options = { key1 = commands, key2 = always_logon, skip = tso_skip(host, port, commands) }
     local engine = brute.Engine:new(Driver, host, port, options)
-    -- TSO has username restrictions. This sets the iterator to use only valid TSO userids
+    -- TSO has username/password restrictions.
+    -- This sets the iterators to use only valid TSO userids/passwords
     engine:setUsernameIterator(unpwdb.filter_iterator(brute.usernames_iterator(),valid_name))
-    engine:setPasswordIterator(unpwdb.filter_iterator(brute.passwords_iterator(), valid_pass))
+    engine:setPasswordIterator(unpwdb.filter_iterator(brute.passwords_iterator(),valid_pass))
     engine.options.script_name = SCRIPT_NAME
-    engine.options:setOption("useraspass", false )
     engine.options:setTitle("TSO Accounts")
     status, result = engine:start()
+    return result
   else
-    return "Could not get to TSO. Try --script-args=tso-user-enum.commands='logon applid(tso)'. Aborting."
+   return err
   end
-  return result
 end
diff --git a/scripts/tso-enum.nse b/scripts/tso-enum.nse
index 6e349a447..dc8b57872 100644
--- a/scripts/tso-enum.nse
+++ b/scripts/tso-enum.nse
@@ -55,6 +55,7 @@ TSO user IDs have the following rules:
 -- 2015-07-04 - v0.1 - created by Soldier of Fortran
 -- 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
 
 
 author = "Philip Young aka Soldier of Fortran"
@@ -92,53 +93,59 @@ Driver = {
   login = function (self, user, pass)
   -- pass is actually the user id we want to try
     local commands = self.options['key1']
-    local cmd_DATA = false
+    local skip = self.options['skip']
     stdnse.debug(2,"Getting to TSO")
     local run = stdnse.strsplit(";%s*", commands)
     for i = 1, #run do
-      stdnse.debug(1,"Issuing Command (#%s of %s): %s", i, #run ,run[i])
-      if string.find(string.upper(run[i]),"logon applid") ~= nil then
+      stdnse.debug(2,"Issuing Command (#%s of %s): %s", i, #run ,run[i])
+      if i == #run and run[i]:upper():find("LOGON APPLID") and skip then
         stdnse.verbose(2,"Trying User ID: %s", pass)
         self.tn3270:send_cursor(run[i] .. " DATA(" .. pass .. ")")
-        cmd_DATA = true
+      elseif i == #run and skip then
+        stdnse.verbose(2,"Trying User ID: %s", pass)
+        self.tn3270:send_cursor(run[i] .. " " .. pass)
       else
         self.tn3270:send_cursor(run[i])
       end
       self.tn3270:get_all_data()
     end
 
-    self.tn3270:get_screen_debug(2)
+    if self.tn3270:find("***") then
+      self.tn3270:send_enter()
+      self.tn3270:get_all_data()
+    end
 
-    if not self.tn3270:find("ENTER USERID") and not self.tn3270:find("TSO/E LOGON")  then
-      local err = brute.Error:new( "TSO Unavailable" )
+    if not self.tn3270:find("ENTER USERID")
+       and not self.tn3270:find("TSO/E LOGON")
+       and not self.tn3270:find("IKJ56710I INVALID USERID") then
+      local err = brute.Error:new("Too many connections")
         -- This error occurs on too many concurrent application requests it
-        -- should be temporary. If not, the script errors and less threads should be used.
-      err:setRetry( true )
+        -- should be temporary. We use the new setReduce function.
+      err:setReduce(true)
+      stdnse.debug(1,"TSO Unavailable for UserID %s", pass )
       return false, err
     end
 
-    if not cmd_DATA then
+    if not skip then
       stdnse.verbose(2,"Trying User ID: %s", pass)
       self.tn3270:send_cursor(pass)
       self.tn3270:get_all_data()
       -- some systems require an enter after sending a valid user ID
-      if self.tn3270:find("***") then
-        self.tn3270:send_enter()
-        self.tn3270:get_all_data()
-      end
     end
 
-
     stdnse.debug(2,"Screen Recieved for User ID: %s", pass)
     self.tn3270:get_screen_debug(2)
-    if self.tn3270:find('not authorized to use TSO') then -- invalid user ID
+    if self.tn3270:find('not authorized to use TSO') or
+       self.tn3270:find('IKJ56710I INVALID USERID') then -- invalid user ID
       return false,  brute.Error:new( "Incorrect User ID" )
-    elseif ( self.tn3270:find('NO USER APPLID AVAILABLE') ) or
-            ( self.tn3270:isClear() ) or (not self.tn3270:find('TSO/E LOGON') ) then
-      local err = brute.Error:new( "TSO Unavailable" )
+    elseif self.tn3270:find('NO USER APPLID AVAILABLE') or self.tn3270:isClear()
+           or not (self.tn3270:find('TSO/E LOGON') or
+                   self.tn3270:find("IKJ56710I INVALID USERID")) then
         -- This error occurs on too many concurrent application requests it
-        -- should be temporary. If not, the script errors and less threads should be used.
-      err:setRetry( true )
+        -- should be temporary. We use the new setReduce function here to reduce number of connections.
+      local err = brute.Error:new( "TSO Unavailable" )
+      err:setReduce(true)
+      stdnse.debug(1,"TSO Unavailable for UserID %s", pass )
       return false, err
     else
       stdnse.verbose("Valid TSO User ID: %s", string.upper(pass))
@@ -165,35 +172,89 @@ local function tso_test( host, port, commands )
     stdnse.debug("Could not initiate TN3270: %s", err )
     return tso, "Could not Initiate TN3270"
   end
-  stdnse.debug("Getting to TSO")
   local run = stdnse.strsplit(";%s*", commands)
   for i = 1, #run do
-    stdnse.debug(1,"Issuing Command (#%s of %s): %s", i, #run ,run[i])
+    stdnse.debug(2,"Issuing Command (#%s of %s): %s", i, #run ,run[i])
     tn:send_cursor(run[i])
     tn:get_all_data()
   end
   tn:get_screen_debug(2)
 
-  if tn:find("ENTER USERID") or tn:find("TSO/E LOGON")  then
-    tso = true
-    -- Patch OA44855 removed the ability to enumerator users
-    -- we check for that here
-    tn:send_cursor("notreal")
-    tn:get_all_data()
-    if tn:find("IKJ56476I ENTER PASSWORD") then
-      return false, "Could not enumerate. PASSWORDPREPROMPT is set to ON."
-    end
-  end
-
   if tn:find("***") then
     secprod = "TopSecret/ACF2"
   end
 
+  if tn:find("ENTER USERID") or tn:find("TSO/E LOGON")  then
+    tso = true
+    -- Patch OA44855 removed the ability to enumerate users
+    tn:send_cursor("notreal")
+    tn:get_all_data()
+    if tn:find("IKJ56476I ENTER PASSWORD") then
+      return false, secprod, "Enumeration is not possible. PASSWORDPREPROMPT is set to ON."
+    end
+  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."
 end
 
+--- Tests the target to see if we can speed up brute forcing
+-- VTAM/USSTable will sometimes allow you to put the userid
+-- in the command area either through data() or just adding
+-- the userid. This function will test for both
+--
+-- @param host host NSE object
+-- @param port port NSE object
+-- @param commands script-args of commands to use to get to TSO
+-- @return status true on success, false on failure
+local function tso_skip( host, port, commands )
+  stdnse.debug("Checking for IKJ56700A message skip")
+  local tn = tn3270.Telnet:new()
+  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:")
+  tn:get_screen_debug(2) -- prints TN3270 screen to debug
+  if not status then
+    stdnse.debug("Could not initiate TN3270: %s", err )
+    return false
+  end
+  -- We're connected now to test.
+  local data = false
+  if commands:upper():find('LOGON APPLID') then
+    stdnse.debug(2,"Using LOGON command (%s) trying DATA() command", commands )
+    data = true
+  else
+    stdnse.debug(2,"Not using LOGON command, testing adding userid to command" )
+  end
+
+  local run = stdnse.strsplit(";%s*", commands)
+  for i = 1, #run do
+    stdnse.debug(2,"Issuing Command (#%s of %s): %s", i, #run ,run[i])
+    if i == #run then
+      if data then
+        stdnse.debug(2,"Sending "..run[i].." DATA(FAKEUSER)")
+        tn:send_cursor(run[i].." DATA(FAKEUSER)")
+      else
+        stdnse.debug(2,"Sending "..run[i].." FAKEUSER")
+        tn:send_cursor(run[i].." FAKEUSER")
+      end
+    else
+      tn:send_cursor(run[i])
+    end
+    tn:get_all_data()
+  end
+  tn:get_screen_debug(2)
+
+  if tn:find("IKJ56710I INVALID USERID")     or
+     tn:find("Enter LOGON parameters below") then
+    stdnse.debug('IKJ56700A message skip supported')
+    return true
+  else
+    return false
+  end
+end
+
+
 -- Filter iterator for unpwdb
 -- TSO is limited to 7 alpha numeric and @, #, $ and can't start with a number
 -- pattern:
@@ -207,7 +268,7 @@ action = function(host, port)
   local commands = stdnse.get_script_args(SCRIPT_NAME .. '.commands') or "tso"
   local tsotst, secprod, err = tso_test(host, port, commands)
   if tsotst then
-    local options = { key1 = commands }
+    local options = { key1 = commands, skip = tso_skip(host, port, commands) }
     stdnse.debug("Starting TSO User ID Enumeration")
     local engine = brute.Engine:new(Driver, host, port, options)
     engine.options.script_name = SCRIPT_NAME
diff --git a/scripts/vtam-enum.nse b/scripts/vtam-enum.nse
index 09ea71bc7..a02ca4313 100644
--- a/scripts/vtam-enum.nse
+++ b/scripts/vtam-enum.nse
@@ -54,6 +54,8 @@ found for application IDs.
 -- 2015-07-04 - v0.1 - created by Soldier of Fortran
 -- 2015-11-04 - v0.2 - significant upgrades and speed increases
 -- 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
 --
 
 author = "Philip Young aka Soldier of Fortran"
@@ -120,11 +122,13 @@ Driver = {
     local macros = self.options['key3']
     local cmdfmt = "logon applid(%s)"
     local type = "applid"
+    local threshold = 75
     -- instead of sending 'logon applid()' when macros=true
     -- we try to logon with just the command
     if macros then
       cmdfmt = "%s"
       type ="macro"
+      threshold = 90 -- sometimes the screen barely changes
     end
     stdnse.verbose(2,"Trying VTAM ID: %s", pass)
 
@@ -134,10 +138,9 @@ Driver = {
     self.tn3270:get_screen_debug(2)
     local current_screen = self.tn3270:get_screen_raw()
 
-    if (self.tn3270:find('UNABLE TO ESTABLISH SESSION')  or
-        self.tn3270:find('COMMAND UNRECOGNI[SZ]ED')         or
-        self.tn3270:find('UNABLE TO CONNECT TO THE REQUESTED APPLICATION')         or
-        self.tn3270:find('USSMSG0[1-4]')         or
+    if (self.tn3270:find('UNABLE TO ESTABLISH SESSION')  or -- thanks goes to Dominic White for creating these
+        self.tn3270:find('COMMAND UNRECOGNI[SZ]ED')      or
+        self.tn3270:find('USSMSG0[1-4]')                 or
         self.tn3270:find('SESSION NOT BOUND')            or
         self.tn3270:find('INVALID COMMAND')              or
         self.tn3270:find('PARAMETER OMITTED')            or
@@ -149,9 +152,11 @@ Driver = {
         self.tn3270:find('syntax invalid')               or
         self.tn3270:find('INVALID SYSTEM')               or
         self.tn3270:find('NOT VALID')                    or
-        self.tn3270:find('INVALID USERID, APPLID') )     or -- thanks goes to Domonic White for creating these
-      screen_diff(previous_screen, current_screen) > 75 then
+        self.tn3270:find('INVALID USERID, APPLID') )     or
+        self.tn3270:find('UNABLE TO CONNECT TO THE REQUESTED APPLICATION') or
+        screen_diff(previous_screen, current_screen) > threshold then
       -- Looks like an invalid APPLID.
+      stdnse.verbose(2,'Invalid Application ID: %s',string.upper(pass))
       return false,  brute.Error:new( "Invalid VTAM Application ID" )
     else
       stdnse.verbose(2,"Valid Application ID: %s",string.upper(pass))
@@ -200,10 +205,12 @@ local function vtam_test( host, port, commands, macros)
   tn:get_all_data()
   tn:get_screen_debug(2)
   local isVTAM = false
-  if tn:find('IBMECHO ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') then
+  if not macros and tn:find('IBMECHO ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') then
     stdnse.debug2("IBMTEST Returned: IBMECHO ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.")
     stdnse.debug1("VTAM Test Success!")
     isVTAM = true
+  elseif macros then
+    isVTAM = true
   end
 
   if not macros then
@@ -239,7 +246,6 @@ action = function(host, port)
   local macros = stdnse.get_script_args(SCRIPT_NAME .. '.macros') or false -- if set to true, doesn't prepend the commands with 'logon applid'
   local commands = stdnse.get_script_args(SCRIPT_NAME .. '.commands') -- Commands to send to get to VTAM
   local vtam_ids = {"tso", "CICS", "IMS", "NETVIEW", "TPX"} -- these are defaults usually seen
-
   vtam_id_file = ( (vtam_id_file and nmap.fetchfile(vtam_id_file)) or vtam_id_file ) or
   nmap.fetchfile("nselib/data/vhosts-default.lst")