Skip to content

TechBlog

It's about tech

Note: This post is the third in a three-part series. You may want to read Part I: McAfee Host Intrusion Prevention Should Die and Part II: McAfee Assassin before continuing with this post.

McAfee Host Intrusion Prevention DialogAfter programming McAfee Assassin Rev 1 and distributing it for testing I was relatively pleased with the outcome. It effectively dismissed the annoying McAfee Host Intrusion Prevention dialogs and allowed me to work without constantly clicking them. However, I found some issues with McAfee Assassin that I felt decreased its user experience:

  • Using the Win32 API to simulate keyboard commands was actually slower than I had expected. It took one to two seconds for a given dialog to be dismissed. With two seconds of application sleep between polls, it could take up to four seconds for a dialog to be dismissed.
  • If more than one McAfee Host Intrusion Prevention dialog is generated at the same time, a set of “Next” and “Previous” buttons are added to each dialog. McAfee Assassin Rev 1 was unable to handle this and would not dismiss such dialogs.

I decided to focus on the performance issue first. While four seconds doesn’t seem like a really big deal, it’s long enough for the user to start moving the cursor toward the dialog, expecting to click the “Allow” button. It further posed problems when running automated JUnit tests through Eclipse, as Eclipse creates some application hooks and tries to open a connection to localhost. The McAfee Host Intrusion Prevention dialogs force the JUnit test runner to be terminated immediately, and the only way I’ve found to run them is to check the “Create a rule for this connection” checkbox on the McAfee HIPS dialog, then re-run the JUnit tests in the next 60 seconds.

Remembering something from C++ classes in college, I did some research and found that the Win32 API provides functions for interacting with GUI window elements (including buttons and other controls) regardless of the process that launched the window itself. I used WinID to inspect the McAfee HIPS dialogs and locate the individual components in which I was interested, intending to use the information to programmatically access and click the required buttons.

It was not as easy as I had thought it would be. I couldn’t locate a Win32 API function to find a specific control if I didn’t already have a handle to its parent control. It turns out the GUI layout of the McAfee HIPS controls includes some invisible container components that have to be located before the buttons and checkboxes can be clicked or manipulated.

Eventually I figured out the appropriate control hierarchies and set to work. McAfee Assassin Rev 2 still polls for McAfee HIPS dialogs in a loop but now uses the Win32 API to manipulate a dialog’s controls. It’s able to determine which type of McAfee HIPS dialog it’s looking at and changes its behavior accordingly. As a result, McAfee Assassin Rev 2 is much faster than Rev 1 and works more accurately because it doesn’t rely on series of keyboard commands.

C++ Code:

//============================================================================
// Name : McAfeeAssassin.cpp
// Version : 1.1
// Copyright : No copyright
// Description : McAfee Host Intrusion Prevention dialog automatic Allow clicker.
// Usage : McAfeeAssassin.exe -sleep milliseconds -title windowtitle
// -sleep milliseconds to sleep between checks for new dialogs
// -title the dialog title for which to check
//============================================================================
#define _WIN32_WINNT 0x0501 // Windows XP = 0501H
#define NOCOMM // Exclude unnecessary dlls
#define _WINNETWK_H
#define _WINREG_H
#define _WINSVC_H

#include
#include
#include
#include
#include

using namespace std;

const string SLEEP_PARAM = "sleep";
const string DIALOG_TITLE_PARAM = "title";

void loop() {
while(1) {
HWND windowHandle;

windowHandle = FindWindow(NULL, DIALOG_TITLE.c_str());
if(windowHandle != NULL) {
ShowWindow(windowHandle, SW_SHOW);
SetActiveWindow(windowHandle);
SetForegroundWindow(windowHandle);

// If the window has more than one tab, it's probably a firewall dialog. We'll need to select the "Connection Information" tab and click the
// "Create firewall application rule for all ports and protocols" checkbox
HWND tabHandle;
tabHandle = FindWindowEx(windowHandle, 0, "SysTabControl32", "Tab1");
if(tabHandle != NULL) {
// Sadly, the Allow button and firewall checkbox are children of anonymous layout controls, so we have to locate a handle to the
// layout control first
HWND buttonLayoutHandle;
buttonLayoutHandle = FindWindowEx(windowHandle, NULL, NULL, "");

// If this is a firewall dialog, there will be a second tab. If -1 is returned when selecting the tab, we know there
// is only one tab available.
if(-1 != TabCtrl_SetCurSel(tabHandle, 1)) {
// We're now on the Connection Information tab
// Look for the "create firewall application rule" checkbox
// Sadly, the Allow button and firewall checkbox are children of an anonymous layout control, so we have to locate a handle to the
// layout control first
HWND layoutHandle;
layoutHandle = FindWindowEx(windowHandle, NULL, NULL, "");

HWND checkboxHandle;
checkboxHandle = FindWindowEx(layoutHandle, 0, "Button", "&Create a firewall application rule for all ports and protocols");
if(checkboxHandle != NULL) {
// Select the checkbox
SendMessage (checkboxHandle, BM_CLICK, 0 , 0);
}

// If the firewall dialog is displayed, the Connection Information tab's layout window is before the Application Info's in the Z-order
buttonLayoutHandle = FindWindowEx(windowHandle, layoutHandle, "#32770", "");
}

// Locate the Allow button and click it
HWND buttonHandle;
buttonHandle = FindWindowEx(buttonLayoutHandle, NULL, "Button", "&Allow");
SendMessage (buttonHandle, BM_CLICK, 0 , 0);
}
}
Sleep(SLEEP_MS);
}

int main(int argcount, char *argvalues[]) {
cout << "|----------------------------------------------|\n";
cout << "| McAfeeAssassin v1.1 |\n";
cout << "| Author Unknown |\n";
cout << "| Trolls for McAfee Host Intrusion Prevention |\n";
cout << "| dialogs and simulates \"allow\" action. |\n";
cout << "| Works on \"Network Application Alert\" and |\n";
cout << "| \"Application Creation Alert\" dialogs. |\n";
cout << "| Found necessary because these dialogs do not |\n";
cout << "| adhere to the application and firewall rules |\n";
cout << "| set in the McAfee Host Intrusion Prevention |\n";
cout << "| editor. |\n";
cout << "| |\n";
cout << "| USAGE: |\n";
cout << "| McAfeeAssassin -sleep milliseconds -title |\n";
cout << "| windowtitle |\n";
cout << "| |\n";
cout << "| -sleep: milliseconds to sleep between checks |\n";
cout << "| -title: the dialog title for which to poll |\n";
cout << "|----------------------------------------------|\n";

// Hide this window
HWND console = GetConsoleWindow();
ShowWindow(console, SW_HIDE);
parseArgs(argcount, argvalues);

// init();
loop();

return 0;
}

Requires the following DLLs in your Windows path (can just be copied to %SYSTEMROOT%\system32):

  • cygwin1.dll
  • cygcc_s-1.dll
  • cygstdc++-6.dll

Binary download: McAfeeAssassin.exe

Note: This post is the second in a three-part series. You may want to read Part I: McAfee Host Intrusion Prevention Should Die before continuing with this post.

A casual study of McAfee Host Intrusion Prevention’s constant stream of dialogs showed an extreme level of intrusion into the user’s productivity. When I’m coding, I highly dislike being irritated by focus-stealing dialogs that never end.

In order to reclaim some of my lost productivity (and exact a bit of childish revenge), I started analyzing the McAfee Host Intrusion Prevention dialogs with the intent to write McAfee Assassin, a program to automate the dismissal of these dialogs. I realized they had accelerator keys and would respond to Alt-A (“Allow”). Unfortunately, there are a number of different kinds of McAfee Host Intrusion Prevention dialogs. The most common I found were:

  • Application Creation Alerts
  • Application Hook Alerts
  • Network Application Alerts (firewall alerts)
McAfee Host Intrusion Prevention Application Creation Alert Dialog

McAfee HIPS Application Creation Alert Dialog

McAfee HIPS Network Application Alert Dialog

McAfee HIPS Network Application Alert Dialog

An Application Creation Alert is shown whenever a new process is started. An Application Hook Alert is shown whenever a program attempts to use the Windows API (e.g. to show the files on a drive). A Network Application Alert is displayed when any program tries to access a local network or the Internet.

While the first two can be quickly dismissed using Alt-A, the Network Application Alert dialog requires an extra step. It has a second “Connection Information” tab that contains a “Create a rule for this connection” checkbox that, if checked, automatically allows subsequent connection attempts for the next 60 seconds. In addition, if more than one HIPS dialog is generated at the same time, each dialog is given a set of “Next” and “Previous” buttons – which screw up the dialog’s regular tab order.

After analyzing these dialogs I tested and confirmed that the following keyboard command sequences would simulate clicking the Allow button:

  • Application Creation and Hook alerts: Alt-A (“Allow”)
  • Network Application Alerts: Tab (selects the tab control at the top), Right Arrow (selects Connection Information tab), Alt-C (checks checkbox), Shift-Tab (selects tab control at the top), Alt-A (“Allow”)

McAfee Assassin Rev 1 is very simple, polling continuously to determine whether a McAfee Host Intrusion Prevention dialog is open, then blindly simulating the keyboard commands to dismiss it. I looked into using Image File Execution Options to launch McAfee Assassin when a McAfee HIPS dialog was created, but each dialog is created in a queue as part of a single continuously-running background process (which incidentally reduces HIPS performance).

McAfee Assassin was coded using C++ using the Win32 API. It’s also very small at 494 KB.

McAfee Host Intrusion Prevention

McAfee Host Intrusion Prevention

McAfee Host Intrusion Prevention (HIPS) is a security product that claims to “guard desktops against rampant complex threats” and “manage centrally”. It’s a program that runs in the background and prevents applications from running or connecting to the Internet. Administrators can install it on all the desktops in their environment and list the applications that are allowed to run and create network connections. If an application not on this list attempts to perform one of these actions, a “learning” dialog is presented to the desktop user so they can make the decision to allow or deny the application.

Unfortunately, all is not well. In keeping with traditions like bad software updates [1, 2], which my company was negatively affected by, McAfee wrote the Host Intrusion Prevention system to poll the network’s Host Intrusion Prevention server every 60 seconds to download the definitions of applications allowed by the system administrator. This is not a bad thing in itself – it ensures administrator configuration changes are propagated quickly – but when the download occurs, it wipes out EVERY local definition of “allowed” applications.

McAfee Host Intrusion Prevention Dialog

McAfee Host Intrusion Prevention Dialog

In a large enterprise it is likely that different users have different application needs. For example, customer service personnel usually need only the software that’s been pre-installed on their machines. Developers typically need all sorts of apps that no other role requires, such as Eclipse, Notepad++, and Tortoise. But when these tools are used with McAfee Host Intrusion Prevention Protection installed, a LOT of pop-up dialogs are displayed. HIPS gets antsy when any new process is started and when a network connection is attempted – and the user sees a pop-up dialog EVERY TIME. For example, Chrome spawns a new process for each new tab you open, and McAfee Host Intrusion Prevention constantly alerts the user that the possibly threatening Chrome is launching. In addition, Chrome checks for application updates every 60 seconds, and each check is faithfully shown in a McAfee Host Intrusion Prevention dialog.

A casual productivity loss test was conducted to determine how much time is devoted to clicking the “Allow” button on the Host Intrusion Prevention dialogs. In the first five minutes, 17 McAfee HIPS dialogs were displayed, stealing window focus and interrupting the user’s work.

It’s understandable to have software learn the user’s preferences, such as the way ZoneAlarm pops a dialog asking the user what it should do when a specific application tries connecting to the Internet. The difference I see between ZoneAlarm and McAfee Host Intrusion Prevention, however, is that ZoneAlarm actually remembers which applications are permitted and doesn’t ask again.

So does McAfee Host Intrusion Prevention just have a memory problem? Is it an aged, Alzheimeric product due for the elephant graveyard? I don’t know, but perhaps it’s time to consider the nursing home.

As I’m moving to another state (700 miles away) this week it became a priority to get a new cell phone. My current service through Cricket is as spotty as the price is cheap, and there’s a 3-second delay on the line that often results in accidental interruptions. To rationalize further, Cricket’s service in my new city is really, really bad – after 45 minutes on hold with Expedia (during a major snowstorm event on the east coast), the representative said there was too much static on the line and hung up on me.

Sony Ericcson W518

Sony Ericcson W518

My first foray into the expensive world of mainstream cell phones and service plans started out with the Sony Ericcson W518, which was great except for the terribly low earpiece volume (and the wacko proprietary accessory ports…phone-specific headsets are so out of style). After researching AT&T phone options online I went to the local AT&T store and exchanged the W518 for an LG CF360, which has a much more acceptable speaker volume (and more industry-standard ports).

LG CF360

LG CF360

Since I’m moving to West Virginia I’m trying to sublet my apartment and thought it’d be a great idea to take pictures of the place with my first camera phone. Discounting my bad photography skills, the photos came out okay even with the CF360′s low-quality camera. Now to just transfer them to my PC…oops. Without the extra $20 text messaging or data plan on my AT&T account I couldn’t just text the pictures to myself, and all of my Google search results assumed I already had a USB A to USB 5-pin connector cable in hand…which wasn’t in my box o’ cables. Whatever could I do?

The CF360 has Bluetooth capability and I have a USB Bluetooth adapter dongle. After extensive searching I found this post with a photo of an attractive blonde that describes how to send a file to the LG Vu via Bluetooth. I followed the instructions and ka-BAM! File sent (droid.mp3 :D )! But now how to send pictures from the CF360 to my PC over Bluetooth?

Through analytical thinking (and blindly clicking until I hit the right menu) I found a way:

  1. On your PC, click the Bluetooth icon in your system tray
  2. Select the ‘Receive a file’ option
  3. On your LG CF360′s home screen, go to Options > Bluetooth > Search New Device. When your PC’s Bluetooth adapter shows up, pair it with your phone
  4. Locate the file you wish to send to your PC. Select the file and go to Options > Send > Via Bluetooth and select your PC’s Bluetooth adapter
  5. Your PC will prompt you to save the file

Note: I’m on Windows 7. You may have a different experience on other operating systems.

It took a long time to find this procedure and it’s annoying that the available CF360 support makes no mention of it. Indeed, every source except the one mentioned in this post indicated that the USB cable is mandatory if you want to transfer files from your CF360 to your PC or vice versa. The Bluetooth support is noted as nothing more than a mechanism for connecting a hands-free headset.

A bit of code that’s been in production for almost a year now came into question recently and I had an opportunity to examine more closely. It was very simple code to get the GMT time and use it to generate an authorization code. The problem was that the GMT time generated on the server and the GMT time generated on the client did not match.

Yes, I too was baffled. How could this make sense? Time calculated for a specific time zone should be the same when calculated simultaneously from two points, regardless of where those points may be. For a moment I thought I’d discovered a rent in the very fabric of time, which might possibly lead to spectacular adventures in history-hopping (and maybe even next year’s stock market status – I could make billions! [but who needs them when you can buy food and eat it, then go back in time so you never bought it?]).

It turns out not all GMT is created equal, at least according to Java’s java.util.Calendar object. A seemingly-simple method for creating a Calendar with the GMT time zone in fact results in the current time zone being used instead!

Calendar calendar = Calendar.getInstance();
// We want only the current year, month, day, and hour;
// Use GMT so it's standard across all time zones
calendar.set(Calendar.ZONE_OFFSET, 0);
calendar.setTimeZone(TimeZone.getTimeZone("GMT"));

Using this code while your computer’s clock is set to CST will result in a CST Calendar being generated! Instead the following snippet can be used to get an actual GMT Calendar object:

Calendar calendar = Calendar.getInstance(new SimpleTimeZone(0, "GMT"));

It seems that passing the expected time zone in as a constructor argument is much more accurate than using the setter methods after the object has already been created.

I’ve spent a bit less than a week attempting to configure Apache to work with Active Directory authentication for Trac on Windows 2003 Server.  Getting the LDAP syntax correct was an absolute horror and took three times forever. Here I’ve documented my findings.

Final {Apache root}/conf/httpd.conf configuration excerpt:

  <Directory /projects/>
  #Trac setup for mod_python
  SetHandler mod_python
  PythonInterpreter main_interpreter
  PythonHandler trac.web.modpython_frontend
  PythonOption TracEnv C:/Trac/projects/
  PythonOption TracUriRoot /projects

  #Apache authentication setup
  Order allow,deny
  Allow from all
  AuthType Basic
  AuthName "Trac"
  AuthBasicProvider ldap
  AuthLDAPBindDN "anActiveDirectoryUsername@yourdomainname.com"
  AuthLDAPBindPassword passwordForAboveUser
  AuthzLDAPAuthoritative Off

  AuthLDAPURL "ldap://adservernamehere:3268/DC=domainname,DC=com?sAMAccountName?sub"

  require group CN=authorizedGroupNameGoesHere
  </Directory>

Notes:

1. The AuthName directive specifies the text that displays on the login dialog in the form “The server at AuthName requires a username and password”

2. The AuthBasicProvider directive determines which Apache module to load. Using “ldap” as shown here requires that the following LoadModule statements be in your httpd.conf:

LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
LoadModule ldap_module modules/mod_ldap.so

3. The AuthLDAPBindDN directive refers to a user account that has read access to the Active Directory tree structure. This account is used by Apache to determine whether the user logging in is valid.

4. The AuthzLDAPAuthoritative directive essentially tells Apache to pass information about the authentication to other Apache modules when set to “On”

5. The Require directive determines which users are considered authorized. The syntax shown here tells Apache to only authorize users who are in the group called “authorizedGroupNameGoesHere”.  There are several options for this directive that range from hardcoding authorized usernames to specifying complicated LDAP queries. You can find out more by reading the mod_authnz_ldap documentation.

6. The biggest problem I had was getting the AuthzLDAPURL syntax right. This directive tells Apache how to contact Active Directory and where to look in the tree structure for users. Tip: If you use port 3268 as shown here instead of port 389, your query will be executed against a “flattened” tree structure and therefore is more likely to work – it’s a pisser to get the syntax correct otherwise. Further note that “referrals” or “referral chasing” (I don’t know much about Active Directory, obviously) in your AD tree structure WILL cause problems with the AuthzLDAPURL directive – and there’s no way to turn it off (see Apache Bug 42557). Apparently Apache attempts to reconnect to Active Directory for each referral, but doesn’t reauthenticate using the credentials from the original authentication. My solution was to use a query that excluded tree nodes that were referrals – not intentionally, it just worked out that way.

You will also want to be careful with how specific you are with the AuthzLDAPURL query. In my case

DC=domainname,DC=com

was as specific as I could get before running into referral problems. It was much easier to compose the query after downloading Softerra LDAP Administrator because it showed me in real-time which query arguments caused problems. It includes an option to turn off referral chasing, which was also useful to determine which nodes contained referrals and should therefore be excluded.

Errors

The following errors in {Apache root}/logs/error.log will occur when the account specified by AuthLDAPBindDN does not have permission to read the Active Directory tree structure (most likely the password specified by AuthLDAPBindPassword is wrong or you need to use the Active Directory Distinguished Name for the user):

auth_ldap authenticate: user spork authentication failed; URI /projects/ [LDAP: ldap_simple_bind_s() failed][Invalid Credentials]
user spork: authentication failure for "/projects/": Password Mismatch

The obvious solution is to check the credentials and try again.

The following errors in the error log will occur when the AuthzLDAPURL query is too restrictive to find the user who tried to log in:

auth_ldap authenticate: user spork authentication failed; URI /projects/ [User not found][No Such Object]
auth_ldap authenticate: user spork authentication failed; URI /projects/FieldPlus [ldap_search_ext_s() for user failed][No Such Object]

The solution I found was to change the AuthzLDAPURL query to least restrictive and work towards more specific until it found my user account (using Softerra LDAP Administrator).

The following error in the error log will occur when the AuthzLDAPURL query results in referral chasing:

auth_ldap authenticate: user spork authentication failed; URI /projects/FieldPlus [ldap_search_ext_s() for user failed][Operations Error]

The solution I found was to again change the AuthzLDAPURL query until no more referrals were included.