Windows.Detection.MoveIt

This is an artifact to detect exploitation of a MoveIt critical vulnerability observed in the wild. CVE-2023-34362

The artifact enables detection via:

  • Yara: webshell, assembly and IIS logs
  • Evtx: IP ioc list and regex search

Last updated: 2023-06-05T06:46Z


name: Windows.Detection.MoveIt
author: Matt Green - @mgreen27
description: | 
   This is an artifact to detect exploitation of a MoveIt critical vulnerability 
   observed in the wild. CVE-2023-34362
   
   The artifact enables detection via:
   
   - Yara: webshell, assembly and IIS logs
   - Evtx: IP ioc list and regex search
   
   Last updated: 2023-06-05T06:46Z
   
reference:
  - https://www.rapid7.com/blog/post/2023/06/01/rapid7-observed-exploitation-of-critical-moveit-transfer-vulnerability/
  - https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-31May2023
  - https://github.com/Neo23x0/signature-base/blob/master/yara/vuln_moveit_0day_jun23.yar

type: CLIENT
resources:
  timeout: 1800

parameters:
  - name: EvtxGlob
    default: '%SystemRoot%\System32\Winevt\Logs\MOVEit.evtx'
  - name: IocRegex
    type: regex
    description: "IOC Regex in evtxHunt"
    default: 'a@b\.com'
  - name: IgnoreRegex
    description: "Regex of string to ignore in Evtxhunt"
    type: regex
  - name: IpEvtxIoc
    default: |
        104.194.222.107
        146.0.77.141
        146.0.77.155
        146.0.77.183
        162.244.34.26
        162.244.35.6
        179.60.150.143
        185.104.194.156
        185.104.194.24
        185.104.194.40
        185.117.88.17
        185.162.128.75
        185.174.100.215
        185.174.100.250
        185.181.229.240
        185.181.229.73
        185.183.32.122
        185.185.50.172
        188.241.58.244
        193.169.245.79
        194.33.40.103
        194.33.40.104
        194.33.40.164
        206.221.182.106
        209.127.116.122
        209.127.4.22
        45.227.253.133
        45.227.253.147
        45.227.253.50
        45.227.253.6
        45.227.253.82
        45.56.165.248
        5.149.248.68
        5.149.250.74
        5.149.250.92
        5.188.86.114
        5.188.86.250
        5.188.87.194
        5.188.87.226
        5.188.87.27
        5.34.180.205
        62.112.11.57
        62.182.82.19
        62.182.85.234
        66.85.26.215
        66.85.26.234
        66.85.26.248
        79.141.160.78
        79.141.160.83
        84.234.96.31
        89.39.104.118
        89.39.105.108
        91.202.4.76
        91.222.174.95
        91.229.76.187
        93.190.142.131
  - name: DateAfter
    type: timestamp
    default: 1685232000
    description: "Search for events or Modification time after this date. YYYY-MM-DDTmm:hh:ssZ"
  - name: DateBefore
    type: timestamp
    description: "Search for events or Modification time after this date. YYYY-MM-DDTmm:hh:ssZ"
  - name: AllDrives
    type: bool
    description: "By default we target yara at all drives"
    default: Y
  - name: DriveLetter
    description: "Target yara drive. Default is a C: if not AllDrives"
    default: "C:"
  - name: AspxYara
    default: |
        rule WEBSHELL_ASPX_MOVEit_Jun23_1 {
           meta:
              description = "Detects ASPX web shells as being used in MOVEit Transfer exploitation"
              author = "Florian Roth"
              reference = "https://www.rapid7.com/blog/post/2023/06/01/rapid7-observed-exploitation-of-critical-moveit-transfer-vulnerability/"
              date = "2023-06-01"
              score = 85
              hash1 = "2413b5d0750c23b07999ec33a5b4930be224b661aaf290a0118db803f31acbc5"
              hash2 = "48367d94ccb4411f15d7ef9c455c92125f3ad812f2363c4d2e949ce1b615429a"
              hash3 = "e8012a15b6f6b404a33f293205b602ece486d01337b8b3ec331cd99ccadb562e"
           strings:
              $s1 = "X-siLock-Comment" ascii fullword   
              $s2 = "]; string x = null;" ascii
              $s3 = ";  if (!String.Equals(pass, " ascii
           condition:
              filesize < 150KB and 2 of them
        }
  - name: DllYara
    default: |
        rule WEBSHELL_ASPX_DLL_MOVEit_Jun23_1 {
           meta:
              description = "Detects compiled ASPX web shells found being used in MOVEit Transfer exploitation"
              author = "Florian Roth"
              reference = "https://www.trustedsec.com/blog/critical-vulnerability-in-progress-moveit-transfer-technical-analysis-and-recommendations/?utm_content=251159938&utm_medium=social&utm_source=twitter&hss_channel=tw-403811306"
              date = "2023-06-01"
              score = 85
              hash1 = "6cbf38f5f27e6a3eaf32e2ac73ed02898cbb5961566bb445e3c511906e2da1fa"
           strings:
              $x1 = "human2_aspx" ascii fullword
              $x2 = "X-siLock-Comment" wide
              $x3 = "x-siLock-Step1" wide
        
              $a1 = "MOVEit.DMZ.Core.Data" ascii fullword
           condition:
              uint16(0) == 0x5a4d and
              filesize < 40KB and (
                 1 of ($x*) and $a1
              ) or all of them
        }
  - name: LogYara
    default: |
         rule LOG_EXPL_MOVEit_Exploitation_Indicator_Jun23_1 {
            meta:
               description = "Detects a potential compromise indicator found in MOVEit Transfer logs"
               author = "Florian Roth"
               reference = "https://www.huntress.com/blog/moveit-transfer-critical-vulnerability-rapid-response"
               date = "2023-06-01"
               score = 70
            strings:
               $x1 = "POST /moveitisapi/moveitisapi.dll action=m2 " ascii
               $x2 = " GET /human2.aspx - 443 " ascii
            condition:
               1 of them
         }

         rule LOG_EXPL_MOVEit_Exploitation_Indicator_Jun23_2 {
            meta:
               description = "Detects a potential compromise indicator found in MOVEit Transfer logs"
               author = "Florian Roth"
               reference = "https://www.huntress.com/blog/moveit-transfer-critical-vulnerability-rapid-response"
               date = "2023-06-03"
               score = 70
            strings:
               $a1 = "Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/105.0.5195.102+Safari/537.36" ascii
               
               $s1 = " POST /moveitisapi/moveitisapi.dll" ascii
               $s2 = " POST /guestaccess.aspx"
               $s3 = " POST /api/v1/folders/"

               $s4 = "/files uploadType=resumable&"
               $s5 = " action=m2 "
            condition:
               1 of ($a*) and 3 of ($s*)
               or all of ($s*)
         }
  - name: NumberOfHits
    description: THis artifact will stop by default at one hit. This setting allows additional hits
    default: 1
    type: int64
  - name: ContextBytes
    description: Include this amount of bytes around hit as context.
    default: 0
    type: int
  - name: UploadYaraHits
    type: bool

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    
    name: Yara
    query: |
      -- check which Yara to use
      LET yara_rules <= AspxYara + '\n' + DllYara + '\n' + LogYara

      -- first find all matching files mft
      LET files = SELECT OSPath, IsDir
        FROM Artifact.Windows.NTFS.MFT(MFTDrive=DriveLetter, AllDrives=AllDrives,
            FileRegex='\.aspx$|^App_Web_[0-9a-z]{8}\.dll$|^u_.+\.log$',
            PathRegex='MoveIt|Microsoft\.net|temp|inetpub' )
        WHERE NOT IsDir
            AND NOT OSPath =~ '''.:\\<Err>\\'''
            AND ((  FileName=~ '\.aspx$' AND OSPath =~ 'MoveIt' )
                OR (FileName=~ '^App_Web_[0-9a-z]{8}\.dll$' AND OSPath =~ 'Microsoft\.net|temp' )
                OR (FileName=~ '^u_.+\.log$' AND OSPath =~ 'inetpub' ))
            AND if(condition=DateAfter,
                then= LastRecordChange0x10 > DateAfter,
                else= True)
            AND if(condition=DateBefore,
                then= LastRecordChange0x10 < DateBefore,
                else= True)

      -- scan files and only report a single hit.
      LET hits = SELECT * FROM foreach(row=files,
            query={
                SELECT
                    FileName, OSPath,
                    File.Size AS Size,
                    File.ModTime AS ModTime,
                    Rule, Tags, Meta,
                    String.Name as YaraString,
                    String.Offset as HitOffset,
                    upload( accessor='scope', 
                            file='String.Data', 
                            name=format(format="%v-%v-%v", 
                            args=[
                                OSPath,
                                if(condition= String.Offset - ContextBytes < 0,
                                    then= 0,
                                    else= String.Offset - ContextBytes),
                                if(condition= String.Offset + ContextBytes > File.Size,
                                    then= File.Size,
                                    else= String.Offset + ContextBytes) ]
                            )) as HitContext
                FROM yara(rules=yara_rules, files=OSPath, context=ContextBytes,number=NumberOfHits)
            })

      -- upload files that have hit
      LET upload_hits=SELECT *,
            upload(file=OSPath) AS Upload
        FROM hits
        GROUP BY OSPath

      -- return rows
      SELECT * FROM if(condition=UploadYaraHits,
        then={ SELECT * FROM upload_hits},
        else={ SELECT * FROM hits})

  - name: Evtx
    query: |
      LET EvtxIPs <= SELECT _value as IP FROM foreach(row=split(string=IpEvtxIoc,sep='\\s')) WHERE _value
      LET EvtxHunterRegex = strip(string=join(array=EvtxIPs.IP + dict(Ioc=IocRegex).Ioc, sep='|'), suffix='|',prefix='|')
      LET Parse(X) = to_dict(
        item={
           SELECT split(sep=":", string=Column0)[0] AS _key,
                  regex_replace(re="^\\s+|\\s+$", replace="", source=split(sep=":", string=Column0)[1]) AS _value
           FROM split_records(accessor="data", filenames=X, regex="\r\n")
           WHERE Column0 =~ "^[a-zA-Z0-9]+:"
        }) +  parse_string_with_regex(regex="User '(?P<User>[^']+)'", string=X)

      SELECT EventTime,Computer,Channel,Provider,EventID,EventRecordID,
        Parse(X=split(string=EventData.Data[0],sep="\r\n\r\n")[1]) as EventData,
        split(string=EventData.Data[0],sep="\r\n\r\n")[0] as Message,
        FullPath,
        EventData.Data[0] as _RawData
      FROM Artifact.Windows.EventLogs.EvtxHunter(
                        IocRegex=EvtxHunterRegex,
                        WhitelistRegex=IgnoreRegex,
                        DateAfter=DateAfter,
                        DateBefore=DateBefore )
      WHERE EventData.IPAddress in EvtxIPs.IP OR _RawData =~ IocRegex

column_types:
  - name: HitContext
    type: preview_upload
  - name: ModTime
    type: timestamp
  - name: EventTime
    type: timestamp