Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A VB.NET application to fix Visual Basic 6 OCX security issues

0.00/5 (No votes)
2 Nov 2011 1  
A threaded application to identify if system VB6 OCX files are exploitable and generate a batch file to register updated OCX controls.

Introduction

I developed a threaded Visual Basic 2010 application that verifies if a Microsoft security issue (MS08-070: Vulnerabilities in Visual Basic 6.0 Runtime Extended Files (ActiveX Controls) could allow remote code execution) exists on a computer.

The application checks both registered ActiveX controls and ActiveX controls that exist in your file system (but not registered). Microsoft states you need to contact the software developer (that installed these exploitable ActiveX controls) and request an updated software package (that will install updated ActiveX controls). How do you know which software installed the ActiveX controls? Why can’t the individual files just be updated?

My software provides an easy method to check your systems and fix the security issues without having to do all this research and major software upgrades.

Verify_VB6_OCX.jpg

  1. This application shows you how to determine which ActiveX (OCX) files are registered on your (32/64 bit OS) computer, by reading the system Registry.
  2. Provides a utility to resolve a potential security issue that exists on many computers.
  3. Demonstrates how to use threading to read Registry entries and check files (via System.Management) at the same time.
  4. Demonstrates how to allow threads to update a progress bar and DataGridView on the main form.
  5. Demonstrates how to apply a style to reformat DataGridView entries (row color) as they are added, based upon data entry.
  6. Demonstrates how to embed files into your compiled application and extract files as required.

What problem does this solution solve?

This Visual Basic (2010) utility allows a system administrator to check their systems for exploitable VB6 ActiveX controls and generate a fix action [a batch file that unregisters the exploitable OCX file, renames it, copies an updated {Microsoft signed} OCX file {that was extracted out of this utility}, and registers the new OCX file]. No need to guess which application installed the outdated OCX files or upgrade existing software.

How does the code work?

This application starts two threads. The first thread scans the Registry for registered OCX files.

' Read registry thread
Private Sub Read_Reg()
  ' Give a name to identify the thread that creates the User Interface
  Thread.CurrentThread.Name = "Read_Registry"

  Dim x As Integer = 1  'starting count

  Dim myAL As New ArrayList() ' holds initial raw registry data
  myAL.Clear()  'clear initial raw registry data

  Dim strProgID As String = String.Empty
  Dim strInprocServer32 As String = String.Empty
  Dim strVersion As String = String.Empty

  Dim defaultValue As Array

  If My.Computer.FileSystem.DirectoryExists("C:\Windows\SysWOW64") Then
    'this is a 64 bit OS
    'Get all key names
    defaultValue = Registry.ClassesRoot.OpenSubKey("Wow6432Node\CLSID").GetSubKeyNames()  
  Else
    'this is a 32 bit OS
    'Get all key names
    defaultValue = Registry.ClassesRoot.OpenSubKey("CLSID").GetSubKeyNames()
  End If

  Dim EntryCount As Integer = defaultValue.Length  ' number of items to loop thru

Delegates are added to initialize the main form progress bar based upon Registry count and advance it while reading entries.

' Delegate to initialize registry process bar (sets maximum)
Private Delegate Sub DelInitializePB(ByVal pMin As Integer, _
        ByVal pMax As Integer, ByVal pValue As Integer)

  ' Delegate to advance registry process bar
  Private Delegate Sub MethodInvoker(ByVal pVal As Integer)

  ' Initialize Progressbar (for Registry)
  Private Sub InitializePB(ByVal pMin As Integer, _
          ByVal pMax As Integer, ByVal pValue As Integer)
    progressBar1.Minimum = pMin
    progressBar1.Maximum = pMax
    progressBar1.Value = pValue
End Sub

' Increment Progressbar (for Registry)
Private Sub IncrementPB(ByVal PVal As Integer)
    If Me.progressBar1.InvokeRequired Then
      Me.progressBar1.Invoke(New MethodInvoker(AddressOf IncrementPB), _
                             New Object() {PVal})
    Else
      progressBar1.Value = PVal
    End If
End Sub

The second thread scans for OCX files on all hard drives.

' Read files thread
Private Sub Check_for_OCX_on_HD()
  ' Give a name to identify the thread that creates the User Interface
  Thread.CurrentThread.Name = "Check_Drives_for_OCX"

  'find local drive letters
  Dim t2 As New System.Management.SelectQuery(_
     "Select DeviceID from Win32_LogicalDisk Where drivetype = '3' ")
  Dim r2 As New System.Management.ManagementObjectSearcher(t2)
  Dim Drv_Name As System.Management.ManagementObject
  Dim Drv_Ltrs As System.Management.ManagementObjectCollection = r2.Get()
  Dim Drv_Ltrs_Count As Integer = Drv_Ltrs.Count
  Dim DrvLtrArray As New ArrayList
  For Each Drv_Name In Drv_Ltrs
    DrvLtrArray.Add("Drive='" & _
      Drv_Name("DeviceID").ToString & "'").ToString()
  Next Drv_Name

  ' Display querying files  
  Count_File_Entry("(Querying Files)")

  'create multiple drive letter str for next WMI query (if multiple drive letters)
  Dim myArr4 As String() = CType(DrvLtrArray.ToArray(GetType(String)), String())
  Dim DrvSearch As String = String.Join("  Or  ", myArr4)
  Dim q As New System.Management.SelectQuery("Select * from " & _ 
    "CIM_DataFile Where Extension = 'ocx' and (" & DrvSearch & ")")
  Dim s As New System.Management.ManagementObjectSearcher(q)
  Dim OCX_Lst As System.Management.ManagementObjectCollection = s.Get()

  Dim OCX_Count As Integer = OCX_Lst.Count

  ' display file count
  Count_File_Entry("(Processing " & OCX_Count & " Files)")

Delegates are added to allow the threads to add a row to the DataGridViews on the main form.

' Add row to Reg_DataGridView (for Reg)
Private Sub AddRow_2_Reg_DataGridView(ByVal This_Key As String, _
        ByVal OCX_Ver As String, ByRef OCXPath As String)
    If Me.Reg_DataGridView.InvokeRequired Then
      Me.Reg_DataGridView.Invoke(New add_2_Reg_DataGridView(AddressOf _
         AddRow_2_Reg_DataGridView), New Object() {This_Key, OCX_Ver, OCXPath})
    Else
      Reg_index = Reg_DataGridView.Rows.Add(This_Key, OCX_Ver, OCXPath)
      'Scroll to the last row.
      Me.Reg_DataGridView.FirstDisplayedScrollingRowIndex = Me.Reg_DataGridView.RowCount - 1
      Me.Reg_DataGridView.Update()
    End If
End Sub

Changes in this revision

2 Nov 2010

The Read_Reg subroutine was modified (old lines 316 - 341).

I now check for a Registry entry and test if a null entry exists before trying to read Registry values. Many null exceptions were being thrown in the old code. I also now close the Registry entry after reading it.

Old code:

For Each subKeyName As String In defaultValue 'regKey.GetSubKeyNames()
  strProgID = String.Empty
  Try
    If My.Computer.FileSystem.DirectoryExists("C:\Windows\SysWOW64") Then
      'this is a 64 bit OS
      strProgID = Registry.ClassesRoot.OpenSubKey("Wow6432Node\CLSID\" & _
         subKeyName & "\ProgID").GetValue("").ToString()
    Else
      'this is a 32 bit OS
      strProgID = Registry.ClassesRoot.OpenSubKey("CLSID\" & _
         subKeyName & "\ProgID").GetValue("").ToString()
    End If
  Catch ex As Exception
  End Try
 
  strInprocServer32 = String.Empty
  Dim InprocServer32_Path As String = String.Empty
  Try
    If My.Computer.FileSystem.DirectoryExists("C:\Windows\SysWOW64") Then
      'this is a 64 bit OS
      InprocServer32_Path = Registry.ClassesRoot.OpenSubKey("Wow6432Node\CLSID\" & _
             subKeyName & "\InprocServer32").GetValue("").ToString()
    Else
      'this is a 32 bit OS
      InprocServer32_Path = Registry.ClassesRoot.OpenSubKey("CLSID\" & _
              subKeyName & "\InprocServer32").GetValue("").ToString()
    End If
    strInprocServer32 = System.IO.Path.GetFullPath(InprocServer32_Path)
    'Expand any short names
  Catch ex As Exception
  End Try

New code:

For Each subKeyName As String In defaultValue
  strProgID = String.Empty
  Try
    If My.Computer.FileSystem.DirectoryExists("C:\Windows\SysWOW64") Then
      'this is a 64 bit OS
      ' Check the registry entry before trying to read the value (entry could be a null)
      Using key As RegistryKey = _
        Registry.ClassesRoot.OpenSubKey("Wow6432Node\CLSID\" & _
        subKeyName & "\ProgID")
        ' if null returned, don't try to get the value
        If key IsNot Nothing Then
          ' get registry value and close the registry entry
          strProgID = key.GetValue("").ToString()
          key.Close()
        End If
      End Using
    Else
      'this is a 32 bit OS
      ' Check the registry entry before trying to read the value (entry could be a null)
       Using key As RegistryKey = Registry.ClassesRoot.OpenSubKey("CLSID\" & subKeyName & "\ProgID")
         ' if null returned, don't try to get the value
         If key IsNot Nothing Then
           ' get registry value and close the registry entry
           strProgID = key.GetValue("").ToString()
           key.Close()
         End If
       End Using
     End If
   Catch ex As Exception
   End Try

Points of interest

I learned a lot while developing this application and always welcome comments to improve my code. This was my first threaded application. Also my first application that embedded resources (the new OCX files) and extracted them as required.

The first use of applying styles to format DataGridView rows (colors rows based on not a VB6 OCX, exploitable OCX, current OCX).

I used this utility to check at least 80 computers and most had a few VB6 OCX files that required updating. Read the PDF documents at the top of this article to see how this software is used. The compiled utility is only about 8 megs even though it has 26 VB6 OCX files embedded as resources. The compiled utility is not included with the source code due to posting size limitations.

History

  • Version 1 posted 23 Oct 2011.
  • Version 2 posted 2 Nov 2011.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here