Skip to content

jle-quel/death

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

85 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Death

Disclaimer

This Virus was not designed as a means to exact vengance on anyone. I, Jefferson Le Quellec, take no responsibility for any damage that this software may cause. Please be responsible and friendly.

Introduction

This project is part of the Virology branch at 42.

The idea behind Death is to write a virus (more evolved than famine) that is not about executing malicious payload but only about propagating itself in an organic/natural way and obfuscate has much possible his infection routine.

Table of Contents

  • Architecture
    • Execution flow
  • Decryption routine
    • Residence
    • Anti-debugging
    • Polymorphism
  • Infection routine
    • Residence
    • Propagation
    • Anti-debugging
    • Obfuscation

Architecture

Execution flow
  • The entry point is on the decryption routine (1), it will decrypt the first encryption layer of the infection routine then jump on it (2).
  • The rip is now on the infection routine (2). There is still the second encryption layer. The infection routine will decrypt itself during runtime, function by function.
  • Jump on the original entry point (3) to give back the control flow to the host.
----------------------                       ] -> First layer of encryption (RC4)
|  header             | ------               ) -> Second layer of encryption (FLIP FLOP)
-----------------------      |               * -> Metamorphism layer
|  segment headers    |      |               + -> Polymorphism layer
----------------------- <------------ (3)
|                     |      |      |
|  text segment       |      |      |
|                     |      |      |
----------------------- <----- (1)  |
|  decryption routine | *           |
----------------------- -----       |
|                     |      |      |
|  data segment       |      |      |
|                     |      |      |
----------------------- <----- (2)  |
|                     | )   ]+      |
|  infection routine  | )   ]+      |
|                     | )   ]+      |
---------------------- --------------

Decryption routine

Residence

Segment text infection is used for the decryption routine, its size is 342 bytes. Since the code is less than 1 page (4096 bytes), we can then inject it between the TEXT_SEGMENT and DATA_SEGMENT.

Anti-debugging

To prevent debugging with software breakpoint, we generate the key for the RC4 to decrypt the first layer by making a hash of the decryption routine. Since the key is the hash of the function, if you place a breakpoint you will corrupt the memory and by translation try to decrypt something with the wrong key.

Polymorphism

To be able to achieve polymorphism, we modify the code with simple metamorphism. As seen in anti-debug the key to RC4 is different in every replication. So if we create some placeholder in the decryption routine code, and replace those at each replication with randomly chosen junk code. We achieve polymorphism.

Replication 1
decryption routine hash -> x1
layer of encryption hash -> x2

Replication 2
decryption routine hash -> y1
layer of encryption hash -> y2

Infection routine

Residence

PT_NOTE to PT_LOAD infection is used for the infection routine, the size of the routine is about 18000 bytes. We use the PT_NOTE segment which is not necessary at runtime; Convert it to PT_LOAD, change his offset and virtual memory to reside after the data segment. It gives us the advantage to not care about the size of the infection routine.

Propagation

Death will infect different directory if you run it as root or as a simple user, but the way it chooses its target will be the same.

First, it will count the number of entries inside the predefined directories. Now that we know how many executables are inside the directories, it will choose a random integer that is in range of the maximum entries and save one executable for each directory. Then select, again randomly, one of those executables and start the infection routine. This way every executable has as many chances to be chosen.

When a new binary is infected, the infector will execute it - which will start the whole (infection | propagation) routine again. The virus will stop his propagation if he chooses (randomly) a host that has already been infected or does not fit the criteria.

If you run it as a simple user, it will only target binary inside

  • /tmp/test/
  • /tmp/test2/

If you run is as root

  • /bin/
  • /sbin/
  • /usr/bin/
  • /usr/sbin/
Anti-debugging

To prevent the virus to run if being debugged we need to check if it is traced. To do that, we create a child that will try to trace his father (Death). If it fails we will know that we are being debugged. Otherwise, the child will detach itself from the father.

Obfuscation

The routine of infection as a very particular execution flow. All function has the same parameters struct s_host *host, struct s_keychain *keychain, enum e_context context. This is because the whole execution has to be linear, the functions can never return, they are all considered as ((no_return)).

They all receive a context to know what happened before, but will still continue to execute. The explanation behind that is because we encrypted every function with a different key, all stored inside keychain.

Example: main is the key to foo, and foo is the key to bar ...

The typical flow of execution will be like this:

You have one __entry and one __exit, both non-encrypted. __entry is only here to back up the register and save the stack frame. __exit is here to retrieve the register, realign the stack frame and jump to the original entry point.

The flip flop:

  • function1 is encrypted so __entry will generate a key with the signature of itself and decrypt function1.
  • rip is now on function1, function1 will generate a new key with itself and decrypt function2.

Now, this is interesting.

  • function2 will re-encrypt function1, and continue the whole generate, decrypt, re-encrypt function after.
  • So from a higher perspective, you will always have one function in clear. Functions only shows themselves when executed.

Just before the __exit, there is an autodestruction function, that will rewrite on the whole infection routine with random bytes.

__entry -> x -> x -> x -> __exit
__entry -> function1 -> x -> x -> __exit
__entry -> x -> function2 -> x -> __exit
__entry -> x -> x -> autodestruction -> __exit
__entry -> y -> y -> y -> __exit

Logger

By default (like Famine), Death is compiled in debug mode which means it will log on tmp/logger. You will be able to trace the path of the virus.

Author

Jefferson Le Quellec 🐜
Djelali Dinaut 🐜