Rapid7
Threat Research

Rapid7 Analysis: SolarWinds Orion Platform Unauthenticated RCE (CVE-2021-25274)

|Last updated on Jun 16, 2026|4 min read

Description

On February 3, 2021, Trustwave security researcher Martin Rakhmanov published a blog post detailing vulnerabilities they discovered in the SolarWinds Orion Platform and Serv-U FTP Server products. These vulnerabilities are tracked as CVE-2021-25274, CVE-2021-25275, and CVE-2021-25276.

Of particular significance is CVE-2021-25274, an unauthenticated remote code execution (RCE) vulnerability in SolarWinds Orion Platform versions prior to 2020.2.4. The vulnerability stems from unauthenticated Microsoft Message Queuing (MSMQ) queues exposed on TCP port 1801. Data sent to these queues is deserialized by the SolarWinds Collector, leading to RCE as the LocalSystem account. Trustwave stated that they intend to release a proof-of-concept (PoC) on February 9, 2021.

SolarWinds has released Orion Platform 2020.2.4 to patch CVE-2021-25274. Rapid7 urges SolarWinds customers to update immediately, as CVE-2021-25274 is considered an impending threat due to ease of exploitation.

Affected products

  • SolarWinds Orion Platform versions prior to 2020.2.4

Rapid7 analysis

The following code snippets are from patched version 2020.2.4. According to Rakhmanov:

After the patch is applied, there is a digital signature validation step performed on arrived messages so that messages having no signature or not signed with a per-installation certificate are not further processed.

This behavior can be seen in the decompiled .NET code.

Exhibit A: SolarWinds.Collector.Queue.MsmqQueueController

The MsmqQueueController class controls MSMQ queues with prefix .\\Private$\\SolarWinds/Collector/ProcessingQueue/. Its OpenQueue() method sets CompressedMessageFormatter as the formatter for serialization and deserialization operations.

[snip]
  private MessageQueue OpenQueue()
  {
    if (queue != null)
    {
      return queue;
    }
    lock (SyncRoot)
    {
      if (queue != null)
      {
        return queue;
      }
      if (!MessageQueue.Exists(QueueName))
      {
        log.DebugFormat("Creating MSMQ [{0}]", QueueName);
        MessageQueue val = MessageQueue.Create(QueueName);
        try
        {
          ConfigureUsers(val);
        }
        finally
        {
          ((IDisposable)val)?.Dispose();
        }
      }
      log.DebugFormat("Opening MSMQ [{0}]", QueueName);
      MessageQueue val2 = null;
      try
      {
        bool messageSigningEnabled = CollectorSettings.Instance.MessageSigningEnabled;
        val2 = new MessageQueue(QueueName);
        val2.set_Formatter((IMessageFormatter)(object)new CompressedMessageFormatter((IMessageFormatter)new BinaryMessageFormatter(), messageSigningEnabled ? new DataSignature() : null));
        val2.get_MessageReadPropertyFilter().set_ArrivedTime(true);
        if (messageSigningEnabled)
        {
          val2.get_MessageReadPropertyFilter().set_Extension(true);
        }
        if (val2.get_MaximumQueueSize() != maximumQueueSize)
        {
          val2.set_MaximumQueueSize(maximumQueueSize);
        }
        queue = val2;
      }
      catch (Exception exception)
      {
        log.Error("Failed to initialize queue " + QueueName, exception);
        if (val2 != null)
        {
          ((Component)val2).Dispose();
        }
        throw;
      }
      return val2;
    }
  }
[snip]

CompressedMessageFormatter itself uses BinaryMessageFormatter as its formatter. Message signing is optionally enabled in the patched version.

Exhibit B: SolarWinds.Collector.Queue.CompressedMessageFormatter

CompressedMessageFormatter.Read() calls baseFormatter.Read(), where baseFormatter is an instance of BinaryMessageFormatter.

[snip]
  public object Read(Message message)
  {
    if (message == null)
    {
      throw new ArgumentNullException("message");
    }
    if (dataSignature != null)
    {
      message.get_BodyStream().Position = 0L;
      if (!dataSignature.VerifyData(message.get_BodyStream(), message.get_Extension()))
      {
        throw new VerificationException("Invalid data signature");
      }
    }
    byte[] array = new byte[compressHeader.Length];
    message.get_BodyStream().Position = 0L;
    message.get_BodyStream().Read(array, 0, array.Length);
    if (compressHeader.SequenceEqual(array))
    {
      message.get_BodyStream().Seek(array.LongLength, SeekOrigin.Begin);
      DeflateStream val = new DeflateStream(message.get_BodyStream(), (CompressionMode)0);
      try
      {
        Message val2 = new Message();
        val2.set_BodyType(message.get_BodyType());
        ((Stream)(object)val).CopyTo(val2.get_BodyStream());
        message.set_BodyStream(val2.get_BodyStream());
      }
      finally
      {
        ((IDisposable)val)?.Dispose();
      }
    }
    message.get_BodyStream().Position = 0L;
    return baseFormatter.Read(message);
  }
[snip]

Data signature verification is performed in the patched version. Presumably, this behavior does not exist in unpatched versions.

Exhibit C:

The following code snippet is from Microsoft’s reference source.

[snip]
        /// <include file='doc\BinaryMessageFormatter.uex' path='docs/doc[@for="BinaryMessageFormatter.Read"]/*' />
        /// <devdoc>
        ///    This method is used to read the contents from the given message
        ///     and create an object.
        /// </devdoc>
        public object Read(Message message)
        {
            if (message == null)
                throw new ArgumentNullException("message");

            int variantType = message.BodyType;
            if (variantType == VT_BINARY_OBJECT)
            {
                Stream stream = message.BodyStream;
                return formatter.Deserialize(stream);
            }

            throw new InvalidOperationException(Res.GetString(Res.InvalidTypeDeserialization));
        }
[snip]

Finally, BinaryMessageFormatter.Read() directly deserializes its input stream. In a vulnerable version, RCE should be as straightforward as sending a .NET gadget chain over the MSMQ protocol to TCP port 1801. An independent security researcher has verified this.

Guidance

SolarWinds Orion Platform customers should update to version 2020.2.4 immediately. Furthermore, it is advised to limit access to TCP port 1801 on the Orion Platform host, as the patch leaves the MSMQ queues unauthenticated.

References

  • https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/full-system-control-with-new-solarwinds-orion-based-and-serv-u-ftp-vulnerabilities/
  • https://documentation.solarwinds.com/en/Success_Center/orionplatform/content/release_notes/orion_platform_2020-2-4_release_notes.htm
LinkedInFacebookXBluesky