# External to limit 4.2 release evasions with mongbat to sane values

class HTTPPHPBBHighlight42Proc < Validator
  S_IPV4_FRAG = "ipv4_frag"
  S_TCP_SEG = "tcp_seg"
  
  S_IPV4_FRAG_EV = "ipv4_frag"
  S_IPV4_FRAG_SIZE = "IPv4 fragment size"  
  S_IPV4_ORDER_EV = "ipv4_order"
  S_IPV4_ORDER_ORDER = "IPv4 segment order"
  S_IPV4_OPT_EV = "ipv4_opt"
  S_IPV4_OPT_PROB = "Probability"
  S_IPV4_OPT_TYPE = "IPv4 option type"
  S_IPV4_OPT_PAYLOAD = "Duplicated payload"
  S_IPV6_FRAG_EV = "ipv6_frag"
  S_IPV6_FRAG_SIZE = "IPv6 fragment size"
  S_IPV6_DUMMYOPTHEADERS_EV = "ipv6_dummyoptheaders"
  S_IPV6_DUMMYOPTHEADERS_TYPE = "Header type"
  S_IPV6_DUMMYOPTHEADERS_SIZE = "Header size"
  S_IPV6_DUMMYOPTHEADERS_NUMBER = "Number of headers"
  S_IPV6_ORDER_EV = "ipv6_order"
  S_IPV6_ORDER_ORDER = "IPv6 segment order"
  S_TCP_SEG_EV = "tcp_seg"
  S_TCP_SEG_SIZE = "TCP segment size"
  S_TCP_ORDER_EV = "tcp_order"
  S_TCP_ORDER_ORDER = "TCP segment order"
  S_TCP_PAWS_EV = "tcp_paws"
  S_TCP_PAWS_PROB = "Probability"
  S_TCP_PAWS_OFFSET = "Timestamp offset"
  S_TCP_PAWS_DATA = "Payload data"
  S_TCP_OVERLAP_EV = "tcp_overlap"
  S_TCP_OVERLAP_AMOUNT = "Overlap amount"
  S_TCP_OVERLAP_DIRECTION = "Overlap direction"
  S_TCP_BOGUSSYNSENTACKS_EV = "tcp_bogussynsentacks"
  S_TCP_BOGUSSYNSENTACKS_OFFSET = "TCP ACK number offset"
  S_TCP_BOGUSSYNSENTACKS_DELAY = "Delay in milliseconds"
  S_TCP_BOGUSSYNSENTACKS_NUMBER = "Number of bogus ACKs"
  S_TCP_NUMRETRANS_EV = "tcp_numretrans"
  S_TCP_NUMRETRANS_NUMBER = "Number of segment retransmits"
  S_TCP_NUMRETRANS_TIMEOUT = "TCP user timeout in seconds"
  S_TCP_INITTSOPT_EV = "tcp_inittsopt"
  S_TCP_INITTSOPT_ENABLE = "Enable/Disable"
  S_TCP_TIMEWAIT_EV = "tcp_timewait"
  S_TCP_TIMEWAIT_NUMBER = "Number of decoy connections"
  S_TCP_TIMEWAIT_DATATYPE = "Type of decoy data"
  S_TCP_SEGVAR_EV = "tcp_segvar"
  S_TCP_MINIMUM_SEGMENT_SIZE = "TCP minimum segment size"
  S_TCP_MAXIMUM_SEGMENT_SIZE = "TCP maximum segment size"
  S_HTTP_URL_ENCODING_EV = "http_url_encoding"
  S_HTTP_URL_ENCODING_OPTION = "Encoding method"
  S_HTTP_REQUEST_METHOD_EV = "http_request_method"
  S_HTTP_REQUEST_METHOD_METHOD = "Method"
  S_HTTP_URL_DUMMYPATH_EV = "http_url_dummypath"
  S_HTTP_URL_DUMMYPATH_METHOD = "Encoding method"
  S_HTTP_URL_DUMMYPATH_LENGTH = "Dummy path length"
  S_TCP_PAYLOADACKSLEEP_EV = "tcp_payloadacksleep"
  S_TCP_PAYLOADACKSLEEP_NUMBER = "Delay in milliseconds"
  S_TCP_SYNSENTSLEEP_EV = "tcp_synsentsleep"
  S_TCP_SYNSENTSLEEP_NUMBER = "Delay in milliseconds"

  def initialize()
  end
  
  def valid_evasion?(evasion, option_hash)
    evasion_name = nil
        
    if evasion.instance_of? Predator4Module::StageEvasion
      evasion_name = evasion.base_name
    else
      evasion_name = evasion.name      
    end
  
    
    # Do general validation, does not depend on stages
    if evasion_name == S_IPV4_OPT_EV
      if option_hash[S_IPV4_OPT_TYPE] == "broken_rr"
        return false  # Not dropped on Linux hosts
      end 
    elsif evasion_name == S_IPV6_DUMMYOPTHEADERS_EV
      if option_hash[S_IPV6_DUMMYOPTHEADERS_SIZE] * option_hash[S_IPV6_DUMMYOPTHEADERS_NUMBER] > 128
        return false  # Discarded by linux ( note: exact limit not tested )
      end
    elsif evasion_name == S_TCP_OVERLAP_EV
      if option_hash[ S_TCP_OVERLAP_DIRECTION ] == "new"
        return false  # Non-linux tcp overlap style
      end
    elsif evasion_name == S_TCP_BOGUSSYNSENTACKS_EV
      if option_hash[S_TCP_BOGUSSYNSENTACKS_DELAY] * option_hash[S_TCP_BOGUSSYNSENTACKS_NUMBER] > 10000
        return false  # > 10s handshake, will timeout on windows
      end
    elsif evasion_name == S_TCP_NUMRETRANS_EV
      if option_hash[S_TCP_NUMRETRANS_NUMBER] > 10 or option_hash[S_TCP_NUMRETRANS_TIMEOUT] > 20
        return false  # Allow only slightly larger retransmission/timeout values
      end
    elsif evasion_name == S_TCP_TIMEWAIT_EV
      if option_hash[S_TCP_TIMEWAIT_NUMBER] > 16
        return false # Allow only a small number of timewait decoys
      end
    elsif evasion_name == S_TCP_PAWS_EV
      if option_hash[S_TCP_PAWS_OFFSET] == 1
        return false # Unreliable
      end
    elsif evasion_name == S_HTTP_URL_ENCODING_EV
      if option_hash[S_HTTP_URL_ENCODING_OPTION] == "escape_encoding"
        return false  # Breaks --obfuscate
      end
    elsif evasion_name == S_HTTP_REQUEST_METHOD_EV
      if option_hash[S_HTTP_REQUEST_METHOD_METHOD] == "head" or 
         option_hash[S_HTTP_REQUEST_METHOD_METHOD] == "trace" or
         option_hash[S_HTTP_REQUEST_METHOD_METHOD] == "connect"
        return false  # Does not execute PHP
      end
    elsif evasion_name == S_HTTP_URL_DUMMYPATH_EV
      if option_hash[S_HTTP_URL_DUMMYPATH_LENGTH] >= 5627
        return false  # Apache will discard too long URLs
      end
    elsif evasion_name == S_TCP_PAYLOADACKSLEEP_EV
      if option_hash[S_TCP_PAYLOADACKSLEEP_NUMBER] > 80000
        return false
      end
    elsif evasion_name == S_TCP_SYNSENTSLEEP_EV
      if option_hash[S_TCP_SYNSENTSLEEP_NUMBER] > 24000
        return false
      end
    elsif evasion_name == S_TCP_SEGVAR_EV
      if option_hash[S_TCP_MINIMUM_SEGMENT_SIZE] >= option_hash[S_TCP_MAXIMUM_SEGMENT_SIZE]
        return false
      end
    end
    
    return true
  end
  
  def valid_combo?(configured_evasions)
    if not super(configured_evasions)
      return false
    end
    evasion_by_name = {}
    configured_evasions.each_key do |evasion|
      if evasion.instance_of? Predator4Module::StageEvasion
        evasion_by_name[evasion.base_name] = evasion
      else
        evasion_by_name[evasion.name] = evasion
      end
    end
    
    # Extract evasions 
    ipv4_frag_size = ipv4_order = nil
    ipv6_frag_size = ipv6_order = nil
    tcp_seg_size = tcp_overlap = tcp_inittsopt = tcp_paws = tcp_order = nil
    smb_seg_size = nil 
    msrpc_seg_size = msrpc_group_sends = nil

    if evasion_by_name.has_key? S_IPV4_FRAG_EV
      ipv4_frag_size = configured_evasions[ evasion_by_name[ S_IPV4_FRAG_EV ] ].values.first
    end
    if evasion_by_name.has_key? S_IPV4_ORDER_EV
      ipv4_order = configured_evasions[ evasion_by_name[ S_IPV4_ORDER_EV ] ].values.first
    end
    
    if evasion_by_name.has_key? S_IPV6_FRAG_EV
      ipv6_frag_size = configured_evasions[ evasion_by_name[ S_IPV6_FRAG_EV ] ].values.first
    end
    if evasion_by_name.has_key? S_IPV6_ORDER_EV
      ipv4_order = configured_evasions[ evasion_by_name[ S_IPV6_ORDER_EV ] ].values.first
    end
        
    if evasion_by_name.has_key? S_TCP_SEG_EV
      tcp_seg_size = configured_evasions[ evasion_by_name[ S_TCP_SEG_EV ] ].values.first
    end
    if evasion_by_name.has_key? S_TCP_OVERLAP_EV
      tcp_overlap = configured_evasions[ evasion_by_name[ S_TCP_OVERLAP_EV ] ].values.first
    end
    if evasion_by_name.has_key? S_TCP_INITTSOPT_EV
      tcp_inittsopt = configured_evasions[ evasion_by_name[ S_TCP_INITTSOPT_EV ] ].values
    end    
    if evasion_by_name.has_key? S_TCP_PAWS_EV
      tcp_paws = configured_evasions[ evasion_by_name[ S_TCP_PAWS_EV ] ].values
    end
    if evasion_by_name.has_key? S_TCP_ORDER_EV
      tcp_order = configured_evasions[ evasion_by_name[ S_TCP_ORDER_EV ] ].values
    end
            
    if not ipv4_frag_size.nil?
      if not tcp_seg_size.nil?
        if ipv4_frag_size > tcp_seg_size + 32
          return false  # Larger IPv4 fragments than TCP packets
        end
      end
    elsif not ipv4_order.nil?
      # IPv4 not fragmented, no need to reorder
      return false
    end
    
    if not ipv6_frag_size.nil?
      if not tcp_seg_size.nil?
        if ipv6_frag_size > tcp_seg_size + 32
          return false  # Larger IPv6 fragments than TCP packets
        end
      end
    elsif not ipv6_order.nil?
      # IPv6 not fragmented, no need to reorder
    end
    
    if not tcp_overlap.nil?
      if not tcp_seg_size.nil?
        if tcp_overlap > tcp_seg_size
          return false
        end
      end
    end
    
    if not tcp_order.nil?
      if tcp_seg_size.nil?
        return false    # No point in changing TCP order without segmentation ( baseline attack doesn't segment )
      end
    end
    
    if not msrpc_group_sends.nil?
      if msrpc_seg_size.nil?
        return false    # Cannot group what does no exist
      else
        if msrpc_group_sends > ( 720 / msrpc_seg_size )
          return false # Not enough fragments to group
        end 
      end
    end
    
    if not tcp_paws.nil?
      if not tcp_inittsopt.nil?
        if( tcp_inittsopt[ 0 ] == "disable" )
          return false  # Cannot disable timestamps and do PAWS
        end
      end
    end        
    
    return true
  end

  def to_s
    return "#{__FILE__}: Validate HTTP phpBB highlight exploit against Apache/Ubuntu"
  end
end

register_external(HTTPPHPBBHighlight42Proc)
