MSSQL Maggie Rules and My Thoughts

Hey folks,

I wanted to share the process that I used for generating rules for the MSSQL ‘maggie’ backdoor. Before I proceed any further, these rules would not have been possible without the research published by DCSO_CyTec. Additionally, we asked their researchers for pcaps and boy did they ever deliver. So, personal thanks to @jaydinbas, and @botlabsDev for writing the research, and supplying us with pcaps.

There are countless rules that we put out in a given day, week, month or year, but I’ve never encountered a backdoor of this nature before. To me it represents a novel threat, because I’ve only really ever seen mysql backdoors discussed in theory, using trigger functions available in most DBMS suites. While this story isn’t exactly that, it is interesting in that threat actors were using native functionality in MSSQL to establish persistence. So, I wanted to take a little bit of time to discuss how I generated rules based on DCSO’s research.

I can’t really do the research article justice, but to summarize it:

  • maggie is an MSSQL backdoor delivered as a DLL
  • Uses MSSQL’s Extended Stored Procedures functionality
    • An adversary drops the DLL to disk on the target system
    • Logs into to the MSSQL interface
    • Instructs the MSSQL server to load the DLL and register it with the extended stored procedures functionality
    • Utilizes the functions the DLL provides to perform a variety of different tasks (e.g., information gathering, pivoting, etc.)

There were a couple of small details the researchers at DCSO shared with us, regarding the backdoor:

  • The DLL export name and the stored procedure name are related. This means, at least for this iteration of this backdoor, and this particular campaign, that since maggie is the only DLL export, that the stored procedure name for accessing the backdoor function will always be maggie.

  • I wasn’t initially aware that MSSQL has to be configured to listen for database connections on port 1433/tcp. This is a configuration that most database admins employ to enable remote connections, however there are other cases in which perhaps the application and MSSQL database may be hosted on the same system and remote access to the database isn’t required or enabled. This means that theoretically there may be hosts in which threat actors could have backdoored the MSSQL server, but do not employ its remote capabilities. Improbable, but not impossible, I suppose.

The pcaps I got from DCSO recorded them performing a variety of actions against an MSSQL server:

  • Installing the backdoor
  • Running the following commands:
    • AccessAll
    • ls
    • listip
    • sysinfo
    • executing the system command, whoami

Using these pcaps, I created six rules related to the implant itself:

  • 2039182 ET MALWARE MSSQL maggie backdoor Accessall Query Observed
  • 2039183 ET MALWARE MSSQL maggie backdoor ListIP Query Observed
  • 2039184 ET MALWARE MSSQL maggie backdoor ls Query Observed
  • 2039185 ET MALWARE MSSQL maggie backdoor sysinfo Query Observed
  • 2039186 ET MALWARE MSSQL maggie backdoor whoami Query Observed
  • 2039187 ET MALWARE MSSQL maggie backdoor sp_addextendedproc Command Observed

and two additional informational/hunting rules that weren’t already in the ruleset, and I thought were ‘nice-to-haves’:

  • 2039181 ET INFO MSSQL SELECT SPID Query Observed
  • 2039188 ET INFO MSSQL sp_addextendedproc Command Observed

The six rules above are enough to spot most of the common tasks associated with the maggie backdoor, but not all of them. You’ll be able to spot a lot of the initial access vetting commands, as well as the sp_addextendedproc command that is used to register the maggie DLL, and the functions associated with the backdoor.

I’ll be adding another rule soon that spots generic calls to the other functions that maggie provides, but we’ll come back to that in a minute.

In addition to those rules, the ET INFO rules spot users attempting to query the session ID of the current process (SELECT @@SPID), as well as users attempting to register other extended stored procedure DLLs (sp_addextendedproc) – the idea being to catch other threat actors attempting to utilize the sp_addextendedproc the functionality to install their own backdoors.

Let’s take a look at one of the pcaps in question:

As you can see in the image here, the SQL statement SELECT @@SPID; is seen being executed prior to doing anything related to the backdoor. I’m not sure if this is an artifact from trying to utilize the functions available via the maggie extended stored procedure functions or not, but wrote a rule looking for this query specifically, because I look at it the same way I would look at a standard user suddenly executing whoami in some cases, its probably not worrisome. But in most, its not typical behavior.

Next up, the next query runs exec maggie 'sysinfo';. We’re calling the maggie extended stored procedure provided by the DLL registered from the sp_addextendedproc command, and the 'sysinfo' function it provides. The blue portion of the screencap is a small portion of the data returned by this function.

You may be noticing that in the screen capture, that there are . symbols between each of the letters in the various commands and outputs. This is because the input and output for the application protocol used to interact with MSSQL servers (TDS-Tabular Data Stream) is formatted with UTF-16.

All of the pcaps I have follow the same format:

  • Run SELECT @@SPID;
  • Run exec maggie '[function name]'

I mentioned earlier that I missed a pretty obvious rule (that will be included in today’s rule release) to catch more generic calls to the maggie extended stored procedure functions by matching on the content exec maggie ' (with the single quote). So look forward to that with today’s rule release.

Well, those are my thoughts on these rules. Again, I feel like this represents a novel case of native DBMS functionality being used to establish persistence. But if you think I’m wrong, or have observed other cases where DBMS functions were used to establish persistence, I’d love to hear more about them. This is a platform for discouse, so please share your thoughts.

1 Like


I would say the SELECT @@SPID; can be surely ignored - it is the question for the MSSQL Session ID: Execution characteristics of extended stored procedures - SQL Server | Microsoft Learn

SPs are callable in different ways:

  • exec <name> <param1> ...
  • execute <name> <param1> ...
  • exec master..<name> <param1> ...
  • execute master..<name> <param1> ...

Maybe it would be worth like this?

content: "e|00|x|00|e|00|c"; nocase; content:"m|00|a|00|g|00|g|00|i|00|e|002000|"; nocase;
1 Like

Hey there,

I appreciate your response to this post. I don’t have much experience maintaining databases in my IT career, and even less experience with MSSQL in particular so sharing your awareness of these different execution methods is very helpful.

I’m going to use this information to modify the 7 rules in place meant to detect the backdoor.

In addition to the rule content changes you’ve recommended, I think a distance:0 and a within:200 modifier for all the rules on the second content match would make this rule even better.

The distance:0 modifier on the second content match would ensure that snort/suricata interpret the packet data as "the exec statement (no matter what format the threat actor uses) should always come before the call to the maggie stored procedure (and/or any specific functions it is requesting.

Using within:200 modifier on the second content match establishes a boundary for snort/suricata that says “if the content match m|00|a|00|g|00|g|00|i|00|e|00|00 20 00| isn’t found within the next 200 bytes of the payload after the e|00|x|00|e|00|c content match, then this rule isn’t a match.”

so, this is what the rule to capture generic calls to the maggie SP functions would look like:

content:"e|00|x|00|e|00|c"; nocase; content:"m|00|a|00|g|00|g|00|i|00|e|00 20 00|"; fast_pattern; nocase; distance:0; within:200;

edit: just realized after reviewing the suricata docs that distance and within don’t work with endswith;, so, for those cases, we’ll be splitting the exec content match (as originally suggested), but we’ll only really be able to use the distance and within check on the generic call to the maggie stored procedure.

thanks for the awesome information.