Re: [Full-Disclosure] Show me the Virrii!

From: Donze, Erich (EDonze_at_miracosta.edu)
Date: 01/06/04

  • Next message: David Vincent: "RE: [Full-Disclosure] Ahh shucks!!"
    To: <full-disclosure@lists.netsys.com>
    Date: Tue, 6 Jan 2004 09:59:22 -0800
    
    

    I like the idea of scanning for valid software. There are some problems
    with it that would need to be overcome, though:

    1. Who makes the list, and keeps it updated? This would be a huge
    undertaking. Much larger than the list of malware. Probably to large
    to be stored locally, thus installation definition checking would have
    to be verified on an outside system that would need to be adeqately
    secured. This system would become the holy grail for hackers. There
    would be relatively significant potential for corruption with this
    organization as well, for instance vendor A offers member of
    organization all expense paid trip to Hawaii to expedite vendor A's
    software product approval and delay vendor B's as long as possible. Now
    nobody can install vendor B's wonderful new software package.

    2. Other vectors. Most virii aren't really installed as software, they
    are run as part of an email, webpage, or exploit some sort of bug in the
    os or some other piece of software. This is what makes the valid
    database really impossible. Not only must it contain a signature for
    every installable software product, but every javascript in every web
    page.

    3. Perhaps the worst is that if there was a virus whose authors got it
    on the list of 'good' software, it could spread completely unchecked.

    Just my 2 cents.
    Erich

    Message: 7
    Date: Mon, 05 Jan 2004 09:09:57 -1000
    From: Jason Coombs <jasonc@science.org>
    Reply-To: jasonc@science.org
    Organization: SCIENCE.ORG
    To: Richard Maudsley <r_i_c_h@btopenworld.com>
    CC: full-disclosure@lists.netsys.com
    Subject: Re: [Full-Disclosure] 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@science.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_VERIFYCON
    TEXT))
    { 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.

    --__--__--

    _______________________________________________
    Full-Disclosure - We believe in it.
    Charter: http://lists.netsys.com/full-disclosure-charter.html


  • Next message: David Vincent: "RE: [Full-Disclosure] Ahh shucks!!"

    Relevant Pages

    • Re: [Full-Disclosure] Show me the Virrii!
      ... audit process execution at just the right time. ... To complete what turned out to be a three-part-series on using hash ... DWORD WINAPI BlockProcThread; ... char sProfileModePath; ...
      (Full-Disclosure)
    • Re: What are my options for dictionary hashing?
      ... their more common words with the letter 'A,' their next-most common ... The first is your assumption of 10 levels for 1024 deep vocabularies, ... then the most frequently used words in each hash string ... lists are ordered randomly. ...
      (comp.lang.forth)
    • Re: My hash table is in need of peer review
      ... struct hash { ... of the two possible loop exit conditions caused the loop to ... You might want to consider keeping the lists sorted ... same value pointer the caller hands you. ...
      (comp.lang.c)
    • Re: combining hashes
      ... >> hash". ... c> sorting or filtering. ... Lisp has an especially rich set of data structures, ... c> lists, arrays, hash tables, sets, bags, vectors, trees, ...
      (comp.lang.perl.misc)
    • Re: Hash Code Compression
      ... all of the articles looking for new words. ... Although the hash table is currently Big 0. ... fact the hashTable is an array of LinkedLists is so that it handles ... My lists are currently smallish, i was just hoping someone would know a better hash Code function or a better compression method which would make each linked list have 1 element rather than up to 4 elements. ...
      (comp.lang.java.programmer)