summaryrefslogtreecommitdiffstats
path: root/scripts/cics-info.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/cics-info.nse')
-rw-r--r--scripts/cics-info.nse409
1 files changed, 409 insertions, 0 deletions
diff --git a/scripts/cics-info.nse b/scripts/cics-info.nse
new file mode 100644
index 0000000..44e41c2
--- /dev/null
+++ b/scripts/cics-info.nse
@@ -0,0 +1,409 @@
+local stdnse = require "stdnse"
+local shortport = require "shortport"
+local stringaux = require "stringaux"
+local tn3270 = require "tn3270"
+local table = require "table"
+
+
+description = [[
+Using the CICS transaction CEMT, this script attempts to gather information
+about the current CICS transaction server region. It gathers OS information,
+Datasets (files), transactions and user ids. Based on CICSpwn script by
+Ayoub ELAASSAL.
+]]
+
+---
+-- @args cics-info.commands Command used to access cics. Default is <code>cics</code>
+-- @args cics-info.cemt CICS Transaction ID to be used. Default is <code>CEMT</code>
+-- @args cics-info.trans Instead of gathering all transaction IDs supplying a name here
+-- will make the script only look up one transaction ID
+-- @args cics-info.user Username to use if access to CEMT requires authentication
+-- @args cics-info.pass Password to use if access to CEMT requires authentication
+--
+-- @usage
+-- nmap --script=cics-info -p 23 <targets>
+--
+-- nmap --script=cics-info --script-args cics-info.commands='logon applid(coolcics)',
+-- cics-info.user=test,cics-info.pass=test,cics-info.cemt='ZEMT',
+-- cics-info.trans=CICA -p 23 <targets>
+--
+-- @output
+-- PORT STATE SERVICE VERSION
+-- 23/tcp open tn3270 IBM Telnet TN3270 (TN3270E)
+-- | cics-info:
+-- | Security: Disabled
+-- | System:
+-- | z/OS Version: 02.01.00
+-- | CICS Version: 05.02.00
+-- | System ID: CICS
+-- | Application ID: CICSFAKE
+-- | Default User: USERCICS
+-- | Datasets:
+-- | CICS.FILEA
+-- | HLQ123.CICS.DFHCSD
+-- | HLQ123.CICS.DFHLRQ
+-- | Libraries:
+-- | HLQ123.CICS.SDFHLOAD
+-- | Users:
+-- | USERCICS
+-- | Transaction / Program:
+-- | AADD / DFH$AALL
+-- | ABRW / DFH$ABRW
+-- | AINQ / DFH$AALL
+-- | AMNU / DFH$AMNU
+-- | AORD / DFH$AREN
+-- | AORQ / DFH$ACOM
+-- | AREP / DFH$AREP
+-- | AUPD / DFH$AALL
+-- | CADP / DFHDPLU
+-- ...
+-- | CEDX / DFHEDFP
+-- | CEGN / DFHCEGN
+-- | CEHP / DFHCHS
+-- | CEHS / DFHCHS
+-- | CEJR / DFHEJITL
+-- | CEMN / DFHCEMNA
+-- | CEMT / DFHEMTP
+-- | CEOT / DFHEOTP
+-- | CXRT / DFHCRT
+-- | DSNC / DFHD2CM1
+
+-- @changelog
+-- 2017-01-30 - v0.1 - created by Soldier of Fortran
+
+author = "Philip Young aka Soldier of Fortran"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {"discovery", "safe"}
+portrule = shortport.port_or_service({23,992}, "tn3270")
+
+
+
+--- Gathers CICS transaction server information
+--
+-- @param host host NSE object
+-- @param port port NSE object
+-- @param user CICS userID
+-- @param pass CICS userID password
+-- @param commands optional script-args of commands to use to get to CICS
+-- @param cemt transaction ID to use instead of CEMT
+-- @param trans transaction ID to check instead of gathering all
+-- @return Status boolean true if CICS was detected.
+-- @return Table of information or error message
+
+local function cics_info( host, port, commands, user, pass, cemt, trans )
+ stdnse.debug("Checking for CICS")
+ local tn = tn3270.Telnet:new()
+ local status, err = tn:initiate(host,port)
+ local msg = 'Unable to get to CICS'
+ local more = true
+ local count = 1
+ local results = stdnse.output_table()
+ if not status then
+ stdnse.debug("Could not initiate TN3270: %s", err )
+ return false, msg
+ end
+ tn:get_screen_debug(2) -- prints TN3270 screen to debug
+ stdnse.debug("Getting to CICS")
+ local run = stringaux.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])
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ end
+ tn:get_all_data()
+ tn:get_screen_debug(2) -- for debug purposes
+ -- we should technically be at CICS. So we send:
+ -- * F3 to exit the CICS program
+ -- * CLEAR (a tn3270 command) to clear the screen.
+ -- (you need to clear before sending a transaction ID)
+ -- * a known default CICS transaction ID with predictable outcome
+ -- (CESF with 'Sign-off is complete.' as the result)
+ -- to confirm that we were in CICS. If so we return true
+ -- otherwise we return false
+ local count = 1
+ while not tn:isClear() and count < 6 do
+ -- some systems will just kick you off others are slow in responding
+ -- this loop continues to try getting out of CESL 6 times. If it can't
+ -- then we probably weren't in CICS to begin with.
+ if tn:find("Signon") then
+ stdnse.debug(2,"Found CESL/CESN 'Signon' sending PF3")
+ tn:send_pf(3)
+ tn:get_all_data()
+ end
+ tn:get_all_data()
+ stdnse.debug(2,"Clearing the Screen")
+ tn:send_clear()
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ count = count + 1
+ end
+ if count == 6 then
+ return false, msg
+ end
+ stdnse.debug(2,"Sending CESF (CICS Default Sign-off)")
+ tn:send_cursor('CESF')
+ tn:get_all_data()
+ if tn:isClear() then
+ tn:get_all_data(1000)
+ end
+ tn:get_screen_debug(2)
+
+ if not tn:find('off is complete.') then
+ return false, 'Unable to get to CICS. Try --script-args cics-info.commands="logon applid(<applid>)"'
+ end
+
+
+ if user and pass then -- We're doing authenticated CICS testing now baby!
+ stdnse.verbose(2,'Logging in with %s / %s for auth testing', user, pass)
+ tn:send_clear()
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ tn:send_cursor('CESN')
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ local fields = tn: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: %s : %s', user, pass)
+ tn:send_locations({user_loc,pass_loc})
+ tn:get_all_data()
+ stdnse.debug(2,"Screen Received for User ID: %s / %s", user, pass)
+ tn:get_screen_debug(2)
+ count = 1
+ while not tn:find('DFHCE3549') and count < 6 do
+ tn:get_all_data(1000) -- loop for 6 seconds
+ tn:get_screen_debug(2)
+ count = count + 1
+ end
+ if not tn:find('DFHCE3549') then
+ msg = 'Unable to access CICS with User: '..user..' / Pass: '..pass
+ return false, msg
+ end
+ end
+ -- By now it's time to start trying to gather information
+ tn:send_clear()
+ tn:get_all_data()
+ tn:send_cursor('CESN')
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+
+ results["Security"] = tn:find('DFHCE3547') and "Enabled" or "Disabled"
+ stdnse.debug(2,"Sending F3")
+ tn:send_pf(3)
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.debug(2,"Sending 'Clear Screen'")
+ tn:send_clear()
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.verbose(2,"Sending 'CEMT INQUIRE SYSTEM'")
+ tn:send_cursor('CEMT INQUIRE SYSTEM')
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ if tn:find('DFHAC2002') then
+ results["Error"] = 'CEMT Access Denied.'
+ return true, results
+ elseif tn:find('NOT AUTHORIZED') then
+ results["System"] = "CEMT 'INQUIRE SYSTEM' Access Denied."
+ else
+ local sysresults = stdnse.output_table()
+ local l1, l2 = tn:find('Oslevel')
+ local oslevel = tn:get_screen_raw():sub(l2+2,l2+7)
+ l1, l2 = tn:find('Cicstslevel')
+ local cicstslevel = tn:get_screen_raw():sub(l2+2,l2+7)
+ l1, l2 = tn:find('Dfltuser')
+ local Dfltuser = tn:get_screen_raw():sub(l2+2,l2+10)
+ local Dfltuser_len = Dfltuser:find(')')
+ l1, l2 = tn:find('Db2conn')
+ local Db2conn = tn:get_screen_raw():sub(l2+2,l2+10)
+ local Db2conn_len = Db2conn:find(')')
+ l1, l2 = tn:find('Mqconn')
+ local Mqconn = tn:get_screen_raw():sub(l2+2,l2+10)
+ local Mqconn_len = Mqconn:find(')')
+ l1, l2 = tn:find('SYSID')
+ local SYSID = tn:get_screen_raw():sub(l2+2,l2+10)
+ local SYSID_len = SYSID:find('\00')
+ l1, l2 = tn:find('APPLID')
+ local APPLID = tn:get_screen_raw():sub(l2+2,l2+10)
+ local APPLID_len = APPLID:find('\00')
+ sysresults["z/OS Version"] = ("%s.%s.%s"):format( oslevel:sub(1,2),oslevel:sub(3,4),oslevel:sub(5,6) )
+ sysresults["CICS Version"] = ("%s.%s.%s"):format( cicstslevel:sub(1,2),cicstslevel:sub(3,4),cicstslevel:sub(5,6) )
+ sysresults["System ID"] = SYSID:sub(1,SYSID_len-1)
+ sysresults["Application ID"] = APPLID:sub(1,APPLID_len-1)
+ sysresults["Default User"] = Dfltuser:sub(1,Dfltuser_len-1)
+ if Db2conn_len > 1 then
+ sysresults["DB2 Connection"] = Db2conn:sub(1,Db2conn_len-1)
+ end
+ if Mqconn_len > 1 then
+ sysresults["MQ Connection"] = Mqconn:sub(1,Mqconn_len-1)
+ end
+ results["System"] = sysresults
+ end -- Done with INQUIRE SYSTEM
+
+ stdnse.debug(2,"Sending F3")
+ tn:send_pf(3)
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.debug(2,"Sending 'Clear Screen'")
+ tn:send_clear()
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.verbose(2,"Sending 'CEMT INQUIRE DSNAME'")
+ tn:send_cursor('CEMT INQUIRE DSNAME')
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+
+ if tn:find('NOT AUTHORIZED') then
+ results["Datasets"] = "CEMT 'INQUIRE DSNAME' Access Denied."
+ else
+ local datasets = {}
+ while more do
+ more = false
+ for line in tn:get_screen():gmatch("[^\r\n]+") do
+ if line:find('Dsn') then
+ table.insert(datasets,line:sub(line:find('%(')+1, line:find(')')-1):match( "(.-)%s*$" ))
+ if count >= 9 and line:find('+') then
+ more = true
+ count = 1
+ stdnse.debug(2,"Sending F11")
+ tn:send_pf(11)
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ else
+ count = count + 1
+ end
+ end
+ end
+ end
+ results["Datasets"] = datasets
+ end -- Done with DSNAME
+
+ stdnse.debug(2,"Sending F3")
+ tn:send_pf(3)
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.debug(2,"Sending 'Clear Screen'")
+ tn:send_clear()
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.verbose(2,"Sending 'CEMT INQUIRE LIBRARY'")
+ tn:send_cursor('CEMT INQUIRE LIBRARY')
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+
+ if tn:find('NOT AUTHORIZED') then
+ results["Libraries"] = "CEMT 'INQUIRE LIBRARY' Access Denied."
+ else
+ local libraries = {}
+ for line in tn:get_screen():gmatch("[^\r\n]+") do
+ if line:find('Dsname') then
+ table.insert(libraries,line:sub(line:find('%(')+1, line:find(')')-1):match( "(.-)%s*$" ))
+ end
+ end
+ results["Libraries"] = libraries
+ end -- Done with Library
+
+ stdnse.debug(2,"Sending F3")
+ tn:send_pf(3)
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.debug(2,"Sending 'Clear Screen'")
+ tn:send_clear()
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.verbose(2,"Sending 'CEMT INQUIRE TASK'")
+ tn:send_cursor('CEMT INQUIRE TASK')
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+
+ if tn:find('NOT AUTHORIZED') then
+ results["Users"] = "CEMT 'INQUIRE TASK' Access Denied."
+ else
+ count = 1
+ more = true
+ local users = {}
+ while more do
+ more = false
+ for line in tn:get_screen():gmatch("[^\r\n]+") do
+ if line:find('Use') then
+ table.insert(users,line:sub(line:find('Use')+4, line:find(')',line:find('Use'))-1):match( "(.-)%s*$" ))
+ if count >= 9 and line:find('+') then
+ more = true
+ count = 1
+ stdnse.debug(2,"Sending F11")
+ tn:send_pf(11)
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ else
+ count = count + 1
+ end
+ end
+ end
+ end
+ results["Users"] = users
+ end -- End of TASK
+
+ stdnse.debug(2,"Sending F3")
+ tn:send_pf(3)
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.debug(2,"Sending 'Clear Screen'")
+ tn:send_clear()
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ stdnse.verbose(2,"Sending 'CEMT INQUIRE TRANSACTION(".. trans ..") en'")
+ tn:send_cursor('CEMT INQUIRE TRANSACTION('.. trans ..') en')
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+
+ if tn:find('NOT AUTHORIZED') then
+ results["Transaction / Program"] = "CEMT 'INQUIRE TRANSACION' Access Denied."
+ else
+ local transactions = {}
+ count = 1
+ more = true
+ local tra, pro = ''
+ while more do
+ more = false
+ for line in tn:get_screen():gmatch("[^\r\n]+") do
+ if line:find('Tra%(') then
+ tra = line:sub(line:find('%(')+1,line:find(')')-1)
+ pro = line:sub(line:find('Pro%(')+4,line:find(')',line:find('Pro%('))-1)
+ table.insert(transactions,tra..' / '..pro)
+ if count >= 9 and line:find('+') then
+ more = true
+ count = 1
+ stdnse.debug(2,"Sending F11")
+ tn:send_pf(11)
+ tn:get_all_data()
+ tn:get_screen_debug(2)
+ else
+ count = count + 1
+ end
+ end
+ end
+ end
+ results["Transaction / Program"] = transactions
+ end -- Done with Transaction IDs
+ tn:disconnect()
+ return true, results
+end
+
+
+action = function(host, port)
+ local commands = stdnse.get_script_args(SCRIPT_NAME .. '.commands') or 'cics'-- VTAM commands/macros to get to CICS
+ local username = stdnse.get_script_args(SCRIPT_NAME .. '.user') or nil
+ local password = stdnse.get_script_args(SCRIPT_NAME .. '.pass') or nil
+ if (username or password) and not (username and password) then
+ stdnse.verbose1("Both 'user' and 'pass' are required for CICS auth")
+ end
+ local CEMT = stdnse.get_script_args(SCRIPT_NAME .. '.cemt') or 'cemt' -- to supply a different transaction ID if they've changed it
+ local transaction = stdnse.get_script_args(SCRIPT_NAME .. '.trans') or '*'
+ local status, results = cics_info(host, port, commands, username, password, CEMT, transaction)
+ -- Report results. Only report an error if
+ -- script args were set or the service is definitely TN3270
+ if status or username or password or port.service == "tn3270" then
+ return results
+ end
+end