Kaspersky Endpoint Security
Kaspersky is a well-known and widely-used vendor for antivirus products and as such,
provides a good baseline for testing antivirus protections on Linux systems.
Kaspersky’s Endpoint Security product, by default, enables real-time protection,
we'll disable this for now to more clearly demonstrate some foudational concepts.
We can turn this off using the kesl-control utility.
We'll use the --stop-t flag, which stops a specified task number.
The documentation indicates that real- time protection runs as task number “1”.
In a real-world scenario, we wouldn’t be able to turn off real-time protection
unless we had elevated privileges.
This makes it a bit easier to demonstrate the detection capability of Kaspersky
on some basic files.
If we don’t turn off real-time protection, our demonstration files will be
immediately deleted
on download or file access.
Moving forward, we’ll manually scan the files we want to check.
First, we’ll try the EICAR test file, which is used by antivirus vendors
to test the detection capabilities of their products.
All modern antivirus systems are trained on this and should detect it.
Let’s run a scan on the EICAR test file on the linuxvictim machine.
During testing, if the file is deleted and we want to reproduce the original
EICAR file on the VM, we can use gpg.
Note that it’s important to ensure real-time protection is turned off
when performing this step or the file will be deleted again.
The command decrypts the encrypted version of the EICAR file (with the password “lab”)
and copies it back to the eicar.txt file.
To perform the scan, we can run the kesl-control utility as before,
but this time with the --scan-file flag, which specifies a file to scan for viruses
Kaspersky detected the file,
and deleted it from the file system.
We can view the name of the detected infection
by querying Kaspersky’s event log.
To do this, we'll specify -E to review the event log,
and --query to list the detected items.
We can then grep “DetectName” to display the names of detected malware.
The resulting DetectName entry states that Kaspersky detected the EICAR test file,
which is what we were initially scanning.
This confirms Kaspersky is working properly and detecting malicious files.
Next, we’ll try scanning a Meterpreter payload.
Let’s generate an unencoded 64-bit Linux Meterpreter reverse TCP payload on Kali,
as an ELF file named met.elf.
Next, we’ll transfer it to the linuxvictim machine in the /tmp directory.
If we run a scan with Kaspersky on our met.elf file as we did with
our EICAR test file,
the file is detected as malware and automatically deleted.
The results show that our Meterpreter ELF file was categorized
as “Backdoor.Linux.Agent.ar”.
32-bit Meterpreter payloads are caught with or without an encoder set
when generating the Meterpreter ELF file.
However, a 64-bit Meterpreter payload encoded with the x64/zutto_dekiru encoder,
is not detected by the AV.
Let’s try a different approach and put our unencoded x64 payload
into a C program as shellcode instead.
This time, we’ll restore real-time protection to make things more realistic.
We can do this by again running kesl-control,
this time using the --start-t flag, which starts a task.
We’ll again specify the real-time protection task, "1".
Now that real-time protection is enabled, when we access or run a file,
Kaspersky will automatically scan it for viruses.
We can regenerate a 64-bit unencoded shellcode with msfvenom,
this time with an output type of “c”, on our Kali VM.
We will then insert it in a C program,
which will act as a wrapper to load and run the shellcode.
We haven’t covered C programming in this course, so let’s take a moment to review
each part of the code individually.
The first three lines are include statements.
They allow us access to functions included in the libraries that are defined
by the C programming language standard.
The next section is an unsigned character array variable called buf,
that contains our shellcode output in C format from msfvenom.
The final section is the main function.
This contains the content of our program and is run when our program starts.
The main function takes two arguments, an integer called argc,
which stores how many arguments are passed to the program
and one called argv
which is an array of strings
containing the actual values of the arguments passed to the program.
Inside our main function we have two lines of code that can seem a bit complicated.
The C language supports pointers.
A pointer variable
(indicated by a * between the variable type and the variable name) just stores
the address of a place in memory that points to a value of the type we specify.
Let’s take a look at a quick example.
In this code, we create an integer variable called myvalue,
which has a value of “10”.
In the second line, we create an integer pointer called myptr
as indicated by the asterisk after int.
This points to a place in memory that stores an integer value, in this case,
the value of the myvalue variable we created in the previous line.
The address of the myvalue variable is retrieved by using an ampersand
character before the variable name.
In the final line, we use the dereference operator,
to get the value stored at the address in myptr,
and save it in the myothervalue variable.
If we ran code to print the contents of all three variables,
we would receive output something like this.
The myvalue output is “10”
because we’re printing out the value of the variable itself.
The myptr value is the value stored by the pointer.
As we know, pointers store memory addresses, so this value is the memory address
where the myvalue variable is being stored.
The myothervalue variable is retrieving the data stored at the location pointed to
by our myptr value.
Because myptr is storing the location of our first variable myvalue, and we’re
retrieving the information stored there, we get an output of “10”.
This is because myothervalue is accessing the same data
as what is stored in myvalue by using a pointer.
Now that we’ve covered how pointers work, we can examine the last two lines in our
shellcode encoder’s main function.
In the first line, we are defining a function pointer called ret.
In our code, the ret function takes in no arguments as indicated by the empty
parentheses to the left of the equals sign.
The int on the left indicates that our function returns an integer value.
On the right of the equals sign, we have the name of our shellcode variable, buf,
but with some elements within parentheses before it:
The parentheses and their contents just indicate that we’re casting our
buf variable to be a function pointer.
Normally, character array variables are just pointers to a set of characters
in memory, so it’s already a pointer.
In this case, we’re casting it to be a function pointer specifically.
This allows us to call our buf shellcode like any other function.
A thorough coverage of function pointers and how they work is outside the scope
of this course.
At a high-level, they work the same way as a pointer to other types of objects
in memory,
except they point to a place in memory where function code is stored.
The last line of our main function just takes the function pointer we’ve created
(called ret) and calls the function it points to, which is our shellcode.
With our wrapper program written, we'll set up a listener in Metasploit
matching our shellcode type.
Then we’ll compile our code with the Gnu C Compiler.
Our buf variable is a local variable and as such, is stored on the stack.
Our shellcode execution would normally be blocked as the stack is marked
as non-executable for binaries compiled by modern versions of gcc.
We can explicitly allow it with the -z execstack parameter.
We’ll provide an output file with the -o parameter,
and a source code file.
Next, we can run our shellcode wrapper
On the Metasploit side,
we receive our shell.
We know our shellcode wrapper program works even though Kapersky
real-time scanning is enabled,
but let’s try explicitly scanning it with Kaspersky just to find out what happens.
Surprisingly, we can bypass Kaspersky by simply wrapping our shellcode
in a C program.
Kaspersky was fairly easy to bypass.
However, not all antivirus products are the same, so let’s try an alternative.