lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <3FF9B685.8020304@science.org>
From: jasonc at science.org (Jason Coombs)
Subject: Show me the Virrii!

Richard Maudsley wrote:
> I recently finished a stable version of my little Virus-Scanner, LMS ( 
> http://www.mindblock.org/lms ).
> It currently detects 19 viruses. I need it to detect hundreds.
> 
> How do big Anti-Virus companies get their hands on new viruses, and how 
> can I?

Antivirus software is one of the biggest frauds going in the software 
industry. You really don't want to go there. Consider something useful 
instead:

(from http://www.windevnet.com)

Antivirus Software Turned Upside Down
by Jason Coombs (jasonc@...ence.org)

Antivirus software exists because viral code and malware exist. Malware 
signature databases coupled with antivirus software provide what I'll 
call "matter of fact, after the fact" security. It is a matter of fact 
that bytes matching an a/v vendor's malware signature must have 
malicious potential resembling a known virus, worm, Trojan, or other 
code analyzed in the past by the a/v software vendor and labeled as 
harmful. While false positives do occur in practice, a virus scanner 
wouldn't be useful if it constantly failed to distinguish between 
malware and user data or desireable code. Therefore, a/v software 
becomes the best proof, in practice, that particular bits are hostile. 
No jury is likely to reject forensic testimony designed to establish the 
presence of malware after seeing a forensic examiner employ a trusted 
brand-name a/v scanner to detect a virus or Trojan on a hard drive or 
other storage device. A commercial virus scanner makes a terrific 
exhibit in front of a jury. As a result, there is a distinct possibility 
that civilian security researchers may help to convict hackers (and 
other civilian security researchers) of computer crimes simply by adding 
definitions to a virus signature database. Law enforcement simply lack 
the resources necessary to assemble definitive lists of 
criminally-malicious bits, so we end up with an interesting, and 
uncomfortable, overlap between private sector business decisions and law 
enforcement investigations.

Antivirus software vendors make no effort to conceal the fact that they 
are in the business of selling virus signature updates. They sell 
content more than software, and it is content updates that drive their 
profits. Updates to virus definitions occur after the fact, so everyone 
is always out-of-date and must keep paying in order to feel protected. 
This makes for a good business, but it doesn't make for very good 
security. In fact, it's completely backwards. Think about it for a 
moment, why should anyone go through the expense and the trouble of 
keeping a running list of all bad code ever encountered? We can prove 
that something is good just as easily as we can prove that something is 
bad, but publishing a list of all known good software wouldn't be very 
profitable. Few people would ever have a need for the entire database. 
Most people would have no need for updates unless they planned to 
install more software. Restricting the execution of code by a CPU to a 
small list of known good programs that the owner of the computer chooses 
to trust would basically kill the antivirus industry. Such a deny-first 
security policy would give computer owners the kind of control over 
their boxes that the introduction of automobile ignition keys gave to 
early motorists. The fact is that today's computers are still designed 
to accomodate arbitrary drivers as though the absence of security is a 
feature demanded by the marketplace.

This brings to mind the purportedly-true story of the evolution of the 
automobile seatbelt. You've probably heard that consumers resented 
seatbelts initially, and manufacturers didn't want to install them, 
because they gave the impression that there was danger involved in 
driving a car. Compared to the seatbeltless horse or bicycle, a car with 
a seatbelt looked scary, and car manufacturers weren't in the fear 
business, they sold convenience and other delightful things. I'm not 
going to take the time to track down a definitive answer to the question 
of the authenticity of this historical tale, if it isn't true then 
perhaps it should have been. Rhetoric is always better when it's mostly 
true. The point is that computing can't continue without seatbelts. We 
simply cannot let our CPUs continue to execute whatever happens to come 
along each day. If you've ever tried to assemble a list of the processes 
normally executed by one of your Windows boxes, so that you might be 
able to spot a suspicious process that wasn't there before, you've 
probably realized that a malicious process can come and go faster than 
the Windows Task Manager will refresh. And Task Manager doesn't bother 
to keep a record of past program executions, so you'll only spot a 
suspicious process in this way if it happens to be long-lived or if you 
audit process execution at just the right time. Putting aside for a 
moment the obvious need for better low-level hardware-based controls 
over code execution, is there really any reason for Windows to allow 
programs to execute without first requiring the end-user or an 
administrator to authorize them in advance using a simple software control?

If we add even the simplest software seatbelt to the boxes we drive 
every day, we can turn antivirus software upside down, replacing it 
instead with anti-software software. Not unlike the anti-driver purpose 
served by automobile ignition keys, or the anti-death purpose served by 
seatbelts, we must redesign our infosec safety precautions around the 
idea that the bad things that can happen are worse than the protections 
we must have to guard against them. Nobody would accept an out-of-date 
list of ways in which one can die in an automobile in lieu of a 
seatbelt, so why do we accept that an out-of-date list of bad code is a 
viable way to protect ourselves while we drive a computer?

To complete what turned out to be a three-part-series on using hash 
algorithms for code authentication and containment, below I offer a 
working prototype of a software seatbelt for Windows. The last two 
articles in this newsletter laid the foundation for forensic hash sets 
produced using the MD5 or SHA-1 hash algorithms and comprehensive, 
full-file hash digests as a replacement for the Microsoft plan to some 
day make digital signatures work perfectly. Software vendors should 
communicate hash sets to users, and users should assemble and 
periodically verify hash sets of code modules installed on Windows 
boxes. But we also need runtime verification of hashes against our 
trusted hash sets. That's what the prototype below is designed to 
accomplish, albeit very crudely. The best way for runtime hash 
verification to occur is doubtless with the assistance of hardware 
enhancements to CPU and motherboard architecture. No add-on, 
after-the-fact technique to inject hash verification (or antivirus 
scanning, for that matter) into Windows will ever be as good as a simple 
kernel modification. Still, the code shown below isn't a complete waste 
of time. I've been using a precursor to this code for a while, without 
the hash verification feature, and feel that it gives a valuable log of 
executable modules that are invoked on a box, if nothing else. By adding 
hash code profiling and verification based on the work shown in the last 
two newsletter articles this code begins to look more promising. At 
least as a source of ideas and code.

#define WIN32_LEAN_AND_MEAN
#define ALGORITHM CALG_SHA1
//#define ALGORITHM CALG_MD5

#define _WIN32_WINNT 0x0500
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <stdlib.h>
#include <malloc.h>

#define BUFSIZE 2048

DWORD hashfile(HCRYPTPROV hcp, char * fname, char ** lphash);
void writehash(HCRYPTPROV hcp,HANDLE hProfile,char *sModulePath,char 
*sHash,DWORD hashlen);
BOOL validatehash(HANDLE hProfile,char *sHash,DWORD hashlen);
DWORD WINAPI BlockProcThread(LPVOID lpParameter);

HANDLE hBlockProcThread = 0;
UINT uiSP = 0;
UINT ui = 0;
DWORD dwBytes = 0;
char sProfileDirPath[MAX_PATH];
char sProfileModePath[MAX_PATH];
char sProfiledPath[MAX_PATH];
char sBlockedPath[MAX_PATH];
char sSystemPath[MAX_PATH];
char buf[MAX_PATH];
char sModulePath[MAX_PATH];


BOOL APIENTRY DllMain( HANDLE hModule,
  DWORD  ul_reason_for_call,
  LPVOID lpReserved)
{
  if(ul_reason_for_call == DLL_PROCESS_ATTACH)
{
  uiSP = GetSystemDirectory(sSystemPath,MAX_PATH);
  // appending 27 characters plus 0 "\AppInitProfile\ProfileMode"
  if(uiSP > 0 && (uiSP + 28) < MAX_PATH)
{
  if(lstrcpy(sProfileDirPath,sSystemPath) != 0)
{
  if(lstrcat(sProfileDirPath,"\\AppInitProfile\\") != 0)
{
  if(lstrcpy(sProfileModePath,sProfileDirPath) != 0)
{
  if(lstrcat(sProfileModePath,"ProfileMode") != 0)
{
  dwBytes = GetModuleFileName(0,buf,MAX_PATH);
  if(dwBytes > 0)
{
  lstrcpy(sModulePath,buf);
  // path may be UNC path "\\?\*" or local path "C:\*"
  // replace each backslash or colon with underscore
  for(ui = 0;ui < dwBytes;ui++)
{
  if(buf[ui] == '\\' || buf[ui] == ':')
{
  buf[ui] = '_';
}}
								 if(lstrcpy(sProfiledPath,sProfileDirPath) != 0)
{
  if(dwBytes + lstrlen(sProfiledPath) < MAX_PATH)
{
  if(lstrcat(sProfiledPath,buf) != 0)
{
  // "BLOCKED " = 8 characters plus NULL
  if(lstrlen(sProfileDirPath) + dwBytes + 9 < MAX_PATH)
{
  if(lstrcpy(sBlockedPath,sProfileDirPath) != 0)
{
  if(lstrcat(sBlockedPath,"BLOCKED ") != 0)
{
  if(lstrcat(sBlockedPath,buf) != 0)
{
  hBlockProcThread = CreateThread(0,0,BlockProcThread,0,0,0);
  if(hBlockProcThread) {
  SetThreadPriority(hBlockProcThread,THREAD_PRIORITY_TIME_CRITICAL);
}}}}}}}}}}}}}}}
  return TRUE;
}

DWORD WINAPI BlockProcThread(LPVOID lpParameter)
{
  BOOL bProfileMode = FALSE;
  BOOL bBlocked = FALSE;
  HANDLE hProfile = INVALID_HANDLE_VALUE;
  HCRYPTPROV hcp = 0;
  BOOL hashfiles = TRUE;
  FILE * f = 0;
  char * sHash = 0;
  DWORD a = 0;
  DWORD dwerr = 0;

if(!CryptAcquireContext(&hcp,0,MS_DEF_PROV,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT))
{ dwerr = GetLastError();
  hashfiles = FALSE; }
  hProfile = CreateFile(sProfileModePath,GENERIC_READ,
  FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
  if(hProfile != INVALID_HANDLE_VALUE)
{
  bProfileMode = TRUE;
  CloseHandle(hProfile);
  hProfile = INVALID_HANDLE_VALUE;
}
  hProfile = CreateFile(sBlockedPath,GENERIC_READ,
  FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
  if(hProfile != INVALID_HANDLE_VALUE)
{	// no need to hash if BLOCKED previously
  bBlocked = TRUE;
}
  if(!bBlocked)
{
  hProfile = CreateFile(sProfiledPath,GENERIC_READ,
  FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
  if(hProfile == INVALID_HANDLE_VALUE)
{
  if(bProfileMode)
{	// Profile Mode
  hProfile = CreateFile(sProfiledPath,GENERIC_WRITE,
  FILE_SHARE_READ,0,CREATE_NEW,0,0);
}
  else
{	// Protect Mode
  hProfile = CreateFile(sBlockedPath,GENERIC_WRITE,
  FILE_SHARE_READ,0,CREATE_NEW,0,0);
  if(hProfile != INVALID_HANDLE_VALUE)
{
  bBlocked = TRUE;
}}
  if(hProfile != INVALID_HANDLE_VALUE && hashfiles)
{	// first time module is encountered, hash it
  a = hashfile(hcp,sModulePath,&sHash);
  if(a)
{	// write the hash to file, BLOCKED or not
  writehash(hcp,hProfile,sModulePath,sHash,a);
}}}
  else if(!bProfileMode && hashfiles)
{ // when the module has been profiled before, and
  // protect mode is on, validate the authorized hash
  a = hashfile(hcp,sModulePath,&sHash);
  if(a)
{
  if(!validatehash(hProfile,sHash,a))
{
  bBlocked = TRUE;
}}}}
  CloseHandle(hProfile);
  if(hcp != 0) { CryptReleaseContext(hcp,0); }
  if(bBlocked && !bProfileMode)
{
  TerminateProcess(GetCurrentProcess(),0);
}
  return 0;
}

void writehash(HCRYPTPROV hcp,HANDLE hProfile,char *sModulePath,char 
*sHash,DWORD hashlen)
{
  DWORD a = 0;
  DWORD bufsize = 0;

  // WriteFile could fail, error handling needed here
  if(!WriteFile(hProfile,sHash,hashlen,&bufsize,0))
{
  a = GetLastError();
}
  free(sHash);
  if(!WriteFile(hProfile," *",2,&bufsize,0))
{
  a = GetLastError();
}
  if(!WriteFile(hProfile,sModulePath,lstrlen(sModulePath),&bufsize,0))
{
  a = GetLastError();
}
  if(!WriteFile(hProfile,"\r\n",2,&bufsize,0))
{
  a = GetLastError();
}}

BOOL validatehash(HANDLE hProfile,char *sHash,DWORD hashlen)
{
  char * buf;
  BOOL b = FALSE;
  DWORD dwread = 0;

  if(!sHash) { return FALSE; }
  buf = malloc(hashlen + 1);
  if(buf) {
  buf[hashlen] = 0;
  // read expected hash from file then compare to sHash
  if(ReadFile(hProfile,buf,hashlen,&dwread,0))
{
  if(dwread == hashlen)
{
  if(!strncmp(sHash,buf,hashlen)) {
  b = TRUE;
}}}
  free(buf);
}
  free(sHash);
  return b;
}

DWORD hashfile(HCRYPTPROV hcp, char * fname, char ** lphash)
{
  HCRYPTHASH hash = 0;
  HANDLE h = INVALID_HANDLE_VALUE;
  DWORD b = 0;
  FILE * f = 0;
  size_t sz = 0;
  DWORD bufsize = 0;
  BYTE buf[BUFSIZE];
  BYTE *pbuf = 0;
  DWORD a = 0;
  char * shash = 0;
  DWORD dwerr = 0;

  if(lphash == 0) { return 0; }

  if(!CryptCreateHash(hcp,ALGORITHM,0,0,&hash)) { dwerr = GetLastError();
  return 0; }

  h = CreateFile(fname,GENERIC_READ,
   FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
  if(h != INVALID_HANDLE_VALUE) {
	do {
  if(ReadFile(h,buf,BUFSIZE,&bufsize,0)) {
  if(bufsize > 0) {
  CryptHashData(hash,buf,bufsize,0);
}
}
  else {
  CryptDestroyHash(hash);
  hash = 0;
}
}
  while(bufsize > 0);
  if(hash) {
  CryptGetHashParam(hash,HP_HASHVAL,0,&bufsize,0);
  if(bufsize > 0) {
  pbuf = (BYTE *)malloc(bufsize);
  if(pbuf) {
  if(CryptGetHashParam(hash,HP_HASHVAL,pbuf,&bufsize,0)) {
  *lphash = (char *)malloc(bufsize * 2 + 1); // caller will free
  if(*lphash) { shash = *lphash;
  shash[bufsize * 2] = 0;
  b = bufsize * 2;
  for(a=0;a < bufsize;a++) {
  _snprintf(&(shash[a*2]),2,"%02x",pbuf[a]);
}}}}
  free(pbuf);
}}
  CloseHandle(h);
}
  if(hash) { CryptDestroyHash(hash); }
  return b;
}

The code shown here compiles into a DLL. If you prefer to use the MD5 
hash algorithm instead of SHA-1 simply adjust the value of the ALGORITHM 
#define as shown. Installation is done by placing a copy of the binary 
in the Windows System directory and editing the following Registry value 
located in the specified HKLM\Software Registry key. Type the name of 
the DLL without its path when you edit or create the "AppInit_DLLs" 
Registry value.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows 
NT\CurrentVersion\Windows\AppInit_DLLs

Next, create a folder under the Windows System directory named 
"AppInitProfile" such as:

c:\winnt\system32\AppInitProfile\

Finally, place an empty text file in the AppInitProfile directory named 
"ProfileMode". The presence of this file indicates that the code should 
operate in profile mode rather than protect mode. No process will be 
terminated while the ProfileMode file is present. Instead, each process 
that launches and that links either statically or dynamically with 
user32.dll will generate a corresponding text file in the AppInitProfile 
directory. The hash code of the executable module responsible for 
creating the process is stored, with winsha1sum-compatible formatting, 
for later use. Delete, or rename, the ProfileMode text file to switch to 
protect mode, where any executable module not previously profiled will 
be hashed, logged in a text file beginning with the word BLOCKED, and 
its process will be abruptly terminated. Although there are many 
limitations to the AppInit_DLLs feature of user32.dll (you can read more 
about them with a search through the Microsoft Knowledge Base) there is 
certainly nothing wrong with having a little more control over the code 
that executes under Windows. And having a record of code that executed 
in the past, or tried to execute and was blocked, is as basic to 
information security as any safety feature in a car is to driving, and 
arriving alive, when you're on the road.


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ