Redes Sociales

martes, 4 de diciembre de 2018

Explanation of CVE-2017-8464

table of Contents

[0x01].Introduction

[0x02].lnk file format parsing

[0x03]. Patch comparison

[0x04]. Vulnerability analysis

[0x05]. About detection

[0x06].Other

[0x01].Introduction

In June 2017, Microsoft's patch day announced two 0day vulnerabilities in the wild, CVE-2017-8464 (LNK vulnerability) and CVE-2017-8543 (vulnerability in Search service). This article will briefly analyze the causes and related uses of the LNK vulnerability.

For more information, please see:

  • https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2017-8464
  • http://www.securityfocus.com/bid/98818

Vulnerability affected version:

  • Microsoft Windows 10 3
  • Microsoft Windows 7 1
  • Microsoft Windows 8 1
  • Microsoft Windows 8.1 2
  • Microsoft Windows Server 2008 2
  • Microsoft Windows Server 2012 2
  • Microsoft Windows Server 2016

[0x02].lnk file format parsing

Lnk file is a relatively simple file format, but because the operation involves shell programming, COM related and other WINDOWS foundation, it is not very easy to fully understand the relevant information. It's not as clear and easy to parse as a complex PE file. A typical lnk file format is as follows:

              **********************.lnk file format ******************

              +---------------------------+
              | lnk file header |
              +---------------------------+ >------.
              | Shell Item Id List | |
              +---------------------------+ |
              | File location info | |
              +---------------------------+ |
              | Description string | |
              +---------------------------+ ---
              Relative path string | Not all of these sections must exist. If they exist, they should be arranged in this order.
              +---------------------------+ ---
              | Working directory string | |
              +---------------------------+ |
              | Command line string | |
              +---------------------------+ |
              | Icon filename string | |
              +---------------------------+ >------.
              | Extra stuff |
              +---------------------------+

The location related to this vulnerability is 1) Shell Item Id List , 2) Extra stuff. For a more detailed introduction to the LNK file format, please refer to this or this links.

Some security companies mentioned that this vulnerability is very similar to the CVE-2010-2568 used by Stuxnet. In fact, there are some truths, all related to the use of the control panel shortcut CPL loading, but there are a certain difference.

The main factor is still in the Shell Item Id List section, which describes the path portion of the shortcut. For example, if I have a c:\a\b\c\x.jpg file, then after creating a shortcut for this file, a shell Item ID list structure will be generated.

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000040 4B 01 14 00 K
00000050 1F 50 E0 4F D0 20 EA 3A 69 10 A2 D8 08 00 2B 30 P郞??i (20) +0
00000060 30 9D 19 00 2F 43 3A 5C 00 00 00 00 00 00 00 00 0 /C:\
00000070 00 00 00 00 00 00 00 00 00 00 00 00 44 00 31 00 00 D 1
00000080 00 00 00 E7 4A 01 73 10 00 61 00 34 00 09 00 04 鏙 s a 4
00000090 00 EF BE E7 4A 01 73 E7 4A 01 73 2E 00 00 00 7A 锞鏙 s鏙 s. z
000000A0 65 01 00 00 00 4F 02 00 00 00 00 00 00 00 00 00 00 e O
000000B0 00 00 00 00 00 DB B6 E5 00 61 00 00 00 10 00 44 鄱?a D
000000C0 00 31 00 00 00 00 00 E7 4A 01 73 10 00 62 00 34 1 鏙 s b 4
000000D0 00 09 00 04 00 EF BE E7 4A 01 73 E7 4A 01 73 2E 锞鏙 s鏙 s.
000000E0 00 00 00 D3 65 01 00 00 00 20 01 00 00 00 00 00 觘
000000F0 00 00 00 00 00 00 00 00 00 00 69 96 E5 00 62 00 00 i栧 b
00000100 00 10 00 44 00 31 00 00 00 00 00 E7 4A C3 73 10 D 1 鏙胹
00000110 00 63 00 34 00 09 00 04 00 EF BE E7 4A 01 73 E7 c 4 锞鏙 s?
00000120 4A C3 73 2E 00 00 00 E9 65 01 00 00 00 1F 00 00 J胹.
00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EC BF 2D 炜-
00000140 01 63 00 00 00 10 00 50 00 32 00 AE 3D 04 00 9F c P 2 ? ?
00000150 2F 00 80 20 00 78 2E 4A 50 47 00 3C 00 09 00 04 / € x.JPG <
00000160 00 EF BE E7 4A C1 73 E7 4A C1 73 2E 00 00 00 F2 velvet velvet.
00000170 65 01 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 e d
00000180 00 00 00 00 00 00 00 00 00

This part can be expressed as a structure

Struct ShellItem{
DWORD size;
SHITEMID item[1]; //The surface here is a variable section, which can grow with the increase of the directory.
}

Typedef struct _SHITEMID
      {
        Unsigned short int cb; //the length of this section
        Unsigned char abID[0]; //Define a mutable structure
      }SHITEMID, *LPSHITEMID;
    

Generally speaking, SHITEMID[0] represents my computer.

\x14\x00 (here is the length)
\x1F (type)
\x50 (unknown)
\xE0\x4F\xD0\x20\xEA\x3A\x69\x10\xA2\xD8\x08\x00\x2B\x30\x30\x9D(GUID)

The above GUID conversion is HKEY_CLASSES_ROOT\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}, you can query on your own computer.

The default upper directory of the shortcut is started from here. According to this structure, you can find the corresponding \a, \b, \c, x.jpg. SHITEMID[1], SHITEMID[2], SHITEMID[3], SHITEMID[4] structure information

Then, the shortcut for the control panel will be different, SHITEMID[0] - corresponding to my computer information,

SHITEMID[1] - Corresponds to the GUID information of the control panel, usually \x19\x00\x2F\x43\x3A\x5C\x00\x00\x00\x00\x00\x00
SHITEMID[2] - Corresponding to the specific control panel cpl program information, the standard control panel shortcut will add its own path information to the extra stuff structure.

For example, if you create a shortcut to Auto Play in the control panel, you can view the information of Extra data. You can see a GUID. You can find out which cpl function program is corresponding to the query. Auto Play:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ControlPanel\NameSpace\
{9C60DE1E-E5FC-40f4-A487-460851A8D915}

According to the previous experience of analyzing CVE-2010-2568, the SHITEMID[1] of the vulnerability fills the GUID information of the control panel {21EC2020-3AEA-1069-A2DD-08002B30309D} SHITEMID[2] adds the corresponding cpl program path. According to the analysis of CVE-2017-8464, simply adding the path information of the cpl program will not trigger the vulnerability, and a special Extra data message must be constructed.  

[0x03]. Patch comparison

   Analysis platform: win7 x86
   File: shell32.dll (pre-patch: 6.1.7601.1892, post-patch: 6.1.7600.23806)

   Through path diff, you can see that Microsoft has patched a lot of places this time. According to the last experience, it is natural to query the CPL_LoadCPLModule function to be called.

...
CAutoComplete::_OnSearchComplete(SearchResults *,_DPA *)                                                      738F086E CAutoComplete::_OnSearchComplete(SearchResults *,_DPA *)
Kfapi::CFolderRedirector::Redirect(_GUID const &,HWND__ *,ulong,ushort const *,_GUID const *,uint,ushort * *) 73B61419 kfapi::CFolderRedirector::Redirect(_GUID const &,HWND__ *,ulong,ushort Const *, _GUID const *, uint, ushort * *)
CopyStreamToFile(IStream *, ushort const *, unsigned __int64)                                                 73B16E33 CopyStreamToFile(IStream *, ushort const *, unsigned __int64)
CControlPanelFolder::_GetPidlFromAppletId(ushort const *,_ITEMID_CHILD * *)                                   73AF300B CControlPanelFolder::_GetPidlFromAppletId(ushort const *,_ITEMID_CHILD * *)
CAutoComplete::_AppendNext(int)                                                                               73AA4538 CAutoComplete::_AppendNext(int)
CAutoComplete::_AppendPrevious(int)                                                                           73AA460E CAutoComplete::_AppendPrevious(int)
CGrep::_InitializeChunkBuffer(void)                                                                           73B9066F CGrep::_InitializeChunkBuffer(void)
...

    Among them, the patch code of CControlPanelFolder::_GetPidlFromAppletId(ushort const *, _ITEMID_CHILD * *) is related to the CPL_LoadCPLModule function. A very important point is here:

  If ( SHExpandEnvironmentStringsW(&Start, &v17, 260) )
        {
          If ( CControlPanelFolder::_IsRegisteredCPLApplet(v11, &v17) ) // --- Added a checksum
          {
            V12 = 0;
            V7 = CPL_LoadCPLModule(&v17, 0);
            If ( v7 )
            {
              V13 = 0;
              Do
              {
                V8 = (struct _DSA *)*((_DWORD *)v7 + 135);
                If ( v6 >= *(_DWORD *)v8 )
                  Break;
                V9 = DSA_GetItemPtr(v8, v6);
                If ( v9 )
                {
                  V12 = IDControlCreate(
         ...
        }

    At this point, we already know that the key is how to trigger the path of this vulnerability, call CPL_LoadCPLModule, because if there is no problem there, then Microsoft will not use _IsRegisteredCPLApplet Filter because CVE-2010-2568 is also patched with this mechanism.

[0x04]. Vulnerability analysis

All the way back we can get a trigger path:

CControlPanelFolder::ParseDisplayName (The specific reference to CControlPanelFolder::ParseDisplayName cannot be seen in IDA)
--> CControlPanelFolder::_GetPidlFromAppletId
-->CPL_LoadCPLModule

Therefore, the call to CControlPanelFolder::ParseDisplayName should be implemented through a dynamic object virtual table function call. Setting breakpoints in CControlPanelFolder::ParseDisplayName, arbitrarily open the file directory where .lnk exists, it is difficult to get a trigger. Note that the usual lnk file is missing some information or the corresponding field is incorrect.

Using the sample of CVE-2010-2568 for testing, this breakpoint cannot be triggered. But when I printed the execution path letter, I saw some notable places, and the call will be executed according to the previous vulnerability.

_DecodeSpecialFolder. But this function is not actually executed completely.

46 101 [ 2] SHLWAPI!SHReadDataBlockList
    3 0 [ 3] SHLWAPI!__security_check_cookie eax = 0
   48 104 [ 2] SHLWAPI!SHReadDataBlockList eax = 0
  119 3446 [ 1] SHELL32!CShellLink::_LoadFromStream
   16 0 [ 2] SHELL32!CShellLink::_DecodeSpecialFolder
   11 0 [ 3] SHLWAPI!SHFindDataBlock eax = 0
   22 11 [ 2] SHELL32!CShellLink::_DecodeSpecialFolder
   11 0 [ 3] SHLWAPI!SHFindDataBlock eax = 0
   27 22 [ 2] SHELL32!CShellLink::_DecodeSpecialFolder
    5 0 [ 3] SHELL32!SHFree
    8 0 [ 3] ole32!CoTaskMemFree
    7 0 [ 4] ole32!CRetailMalloc_Free eax = 76b076bc
   10 7 [ 3] ole32!CoTaskMemFree eax = 76b076bc
   33 44 [ 2] SHELL32!CShellLink::_DecodeSpecialFolder
    3 0 [ 3] SHELL32!__security_check_cookie eax = 76b076bc



void __thiscall CShellLink::_DecodeSpecialFolder(CShellLink *this)
{
  CShellLink *v1; // esi@1
  int v2; // eax@1
  int v3; // edi@1
  signed int v4; // eax@3
  signed int v5; // eax@5
  int v6; // ecx@5
  char *v7; // edi@7
  unsigned int v8; // ebx@7
  int i; // eax@7
  bool v10; // zf@9
  const ITEMIDLIST *v11; // eax@13
  ITEMIDLIST *v12; // ebx@14
  ITEMIDLIST *v13; // edi@15
  int v14; // eax@15
  int v15; // esi@15
  int v16; // eax@15
  int v17; // eax@20
  int v18; // edi@20
  const struct _ITEMIDLIST_RELATIVE *pidl; // [sp+Ch] [bp-Ch]@7
  ITEMIDLIST *pidla; // [sp+Ch] [bp-Ch]@13
  LPITEMIDLIST v21; // [sp+10h] [bp-8h]@1

  v21 = 0;
  v1 = this;
  v2 = SHFindDataBlock(*((_DWORD *)this + 57), 0xA000000B); 
  v3 = v2;
  
  if ( v2 )  //If the search is successful, then it will continue, otherwise release ILFree (pidla);)
  {
    if ( !CShellLink::_ShouldDecodeSpecialFolder(v1, (const struct _GUID *)(v2 + 8)) )
      goto LABEL_19;
    v4 = 0x4000;
    if ( *((_DWORD *)v1 + 65) & 0x400000 )
      v4 = 20480;
    v5 = SHGetKnownFolderIDList(v3 + 8, v4, 0, &v21);
    v6 = *(_DWORD *)(v3 + 24);
  }
  else
  {
    v17 = SHFindDataBlock(*((_DWORD *)v1 + 57), 0xA0000005);
    v18 = v17;
    if ( !v17 )
      goto LABEL_19;
    v21 = SHCloneSpecialIDList(0, *(_DWORD *)(v17 + 8), 0);
    v6 = *(_DWORD *)(v18 + 12);
    v5 = v21 != 0 ? 0 : -2147024882;
  }

Obviously, SHFindDataBlock is a failure, because the DataBlock she is searching for is missing. See the LNK file manual to see that the Extra Data structure is mentioned. It involves a lot of structure and its signature information, showing that the parameter when called above is its signature information, 0xA0000005, 0xA000000B

 ConsoleDataBlock - 0xA0000002
 ConsoleFEDataBlock - 0xA0000004
 EnvironmentVariableDataBlock - 0xA0000001
 IconEnvironmentDataBlock - 0xA0000007
 PropertyStoreDataBlock - 0xA0000009
 SpecialFolderDataBlock - 0xA0000005 (parameters mentioned in the function above)
 KnownFolderDataBlock - 0xA000000B (parameters mentioned in the function above)

   Therefore, based on the CVE-2010-2568 sample, you can add an ExtraData data, and then add the SpecialFolderDataBlock and the KnownFolderDataBlock experiments.    Specific format:

   Struct KnownFolderDataBlock{
    DWORD BlockSize;
DWORD BlockSignature;
CHAR GUID[16];
DWORD offset;
   }

   
   KnownFolderDataBlock:
   \x1C\x00\x00\x00\x0B\x00\x00\xA0\x19\x27\x6D\x74\x59\x74\x6A\x2D\x63\x1A\x9D\x7f\x63\x24\x8C\x6D\x00 \x00x00\x03

The GUID in the middle specifically points to the special file ID. It does not seem to be clear in the document. Only some GUIDs that exist already exist, and the test is repeated. There is no success (if there is a friend who has successfully tested, please also give pointers).    

The structure of the SpecialFolderDataBlock is relatively simple:

   Struct SpecialFolderDataBlock{
    DWORD BlockSize;
DWORD BlockSignature;
DWORD ID;
DWORD offset;
   }


   Add these data: \x10\x00\x00\x00\x05\x00\x00\xA0\x03\x00\x00\x00\x28\x00\x00\x00\x00\x00

   Note that 0x28 inside is offset according to the manual.

A 32-bit, unsigned integer that specifies the location of the ItemID of the first child
Segment of the IDList specified by SpecialFolderID. This value is the offset, in bytes, into the

Link target IDList.

   Here I calculate the difference between SHITEMID[2] - SHITEMID[0].

   This way, you can see the call stackl where the .lnk file is loaded.

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000080 00 00 00 6A 00 00 00 00 00 00 05 01 05 01 46 00 j F
00000090 3A 00 5C 00 2E 00 5C 00 2E 00 5C 00 2E 00 5C 00:... \ \ \ \
000000A0 2E 00 5C 00 2E 00 74 00 65 00 73 00 74 00 2E 00. \. T e s t.
000000B0 63 00 70 00 6C 00 00 00 c p l

The final loading situation: SHExpandEnvironmentStringsW will get the system directory and the following directory spliced ​​into a system path SHELL32!CControlPanelFolder::_GetPidlFromAppletId+0x160:

75ec3da3 50 push eax
0:017> db eax
0583cde4 43 00 3a 00 5c 00 57 00-69 00 6e 00 64 00 6f 00 C.:.\.W.i.n.d.o.
0583cdf4 77 00 73 00 5c 00 73 00-79 00 73 00 74 00 65 00 w.s. \. S.y.s.t.e.
0583ce04 6d 00 33 00 32 00 5c 00-2e 00 74 00 65 00 73 00 m.3.2.\...t.e.s.
0583ce14 74 00 2e 00 63 00 70 00-6c 00 2c 00 00 00 15 09 t...c.p.l.,.....
0583ce24 38 ce 83 05 18 6f a0 76-d8 ed 15 09 70 f4 c6 75 8....o.v....p..u
0583ce34 53 00 6f 00 66 00 74 00-77 00 61 00 72 00 65 00 S.o.f.t.w.a.r.e.
0583ce44 5c 00 4d 00 69 00 63 00-72 00 6f 00 73 00 6f 00 \.M.i.c.r.o.s.o.
0583ce54 66 00 74 00 5c 00 57 00-69 00 6e 00 64 00 6f 00 f.t.\.W.i.n.d.o.

0:013> kv
ChildEBP RetAddr  Args to Child              
0499d114 75ec3f52 08ec3a48 0499d21c 75c70180 SHELL32!CControlPanelFolder::_GetPidlFromAppletId+0x181 (FPO: [Non-Fpo])
0499d140 75c5799b 08cabc38 00000000 08d5a800 SHELL32!CControlPanelFolder::ParseDisplayName+0x49 (FPO: [Non-Fpo])
0499d1c4 75c5f0af 090ddaac 00000000 08d5a800 SHELL32!CRegFolder::ParseDisplayName+0x93 (FPO: [Non-Fpo])
0499d238 75c606cd 090de3dc 00347098 75c7a660 SHELL32!ReparseRelativeIDList+0x137 (FPO: [Non-Fpo])
0499d27c 75c60715 00000000 00347070 08ead300 SHELL32!TranslateAliasWithEvent+0xa6 (FPO: [Non-Fpo])
0499d294 75c2e84e 00347070 08ead300 05c06258 SHELL32!TranslateAlias+0x15 (FPO: [Non-Fpo])
0499d2c0 75c2e5e3 00000000 00000000 05d5e0c8 SHELL32!CShellLink::_DecodeSpecialFolder+0xf9 (FPO: [Non-Fpo])
0499e584 75beca18 08f4c6d8 00000000 00000000 SHELL32!CShellLink::_LoadFromStream+0x39f (FPO: [Non-Fpo])
0499e7b4 75bec987 0499e840 00000000 0499e7f0 SHELL32!CShellLink::_LoadFromFile+0x90 (FPO: [Non-Fpo])
0499e7c4 75bec8dc 05d5e0dc 0499e840 00000000 SHELL32!CShellLink::Load+0x32 (FPO: [Non-Fpo])
0499e7f0 75bec933 05d5e0d0 0499e840 00000000 SHELL32!InitializeFileHandlerWithFile+0x6a (FPO: [Non-Fpo])
0499ea4c 75c38be0 00000000 0499eaa0 00000002 SHELL32!CFileSysItemString::HandlerCreateInstance+0x168 (FPO: [Non-Fpo])
0499eb04 75c42626 00000000 00000000 00000000 SHELL32!CFileSysItemString::LoadHandler+0x16b (FPO: [Non-Fpo])
0499efb4 75c42674 00000000 00000001 0499f00c SHELL32!CFSFolder::_BindHandler+0x1d1 (FPO: [Non-Fpo])
0499efd4 75bf9aa6 09058198 00000000 00000001 SHELL32!CFSFolder::GetUIObjectOf+0x21 (FPO: [Non-Fpo])
0499f490 75c3128d 09058180 05b4c0b8 05b4c0b8 SHELL32!CFSFolder::_GetPerceivedType+0x60 (FPO: [Non-Fpo])
0499f4b0 75c371fd 00000000 05b4c0b8 05b4c0b8 SHELL32!CFSFolder::_GetInnateDetailsFromHelper+0x47 (FPO: [Non-Fpo])
0499f4e0 75c31398 00000000 05b4c0b8 75c632ac SHELL32!CFSFolder::_GetInnateDetailsWithHandlerExceptions+0x61 (FPO: [Non-Fpo])
0499f4fc 75c3134c 05b4c0b8 75c632ac 0499f578 SHELL32!CFSFolder::_GetInnateDetails+0x18 (FPO: [Non-Fpo])
0499f538 75c312e9 05b4c0b8 75c632ac 0499f578 SHELL32!CFSFolder::_GetInnateDetailsAsVariant+0x41 (FPO: [Non-Fpo])
0499f580 75c0e197 09058198 05b4c0b8 75c632ac SHELL32!CFSFolder::GetDetailsEx+0x40 (FPO: [Non-Fpo])
0499f5b0 75c0ed8d 09058198 05b4c0b8 09056584 SHELL32!GetPerceivedType+0x44 (FPO: [Non-Fpo])
0499f5f8 75c0dc3a 0499f61c 08d6d4e0 09056584 SHELL32!GetFolderTypeFromItems+0xd8 (FPO: [Non-Fpo])
0499f63c 75c0d99c 08d6d4e0 09056584 09058198 SHELL32!CEnumTask::_CalculateFolderType+0x2f (FPO: [Non-Fpo])
0499f698 75c09e63 09058198 0499f6f0 0499f6ec SHELL32!CEnumTask::_IncrFillEnumToView+0x192 (FPO: [Non-Fpo])
0499f6f8 75c09fcd 09058198 00000001 755f81eb SHELL32!CEnumTask::_IncrEnumFolder+0x2b5 (FPO: [Non-Fpo])
0499f73c 75c38226 09056550 01000000 80000000 SHELL32!CEnumTask::InternalResumeRT+0x325 (FPO: [Non-Fpo])
0499f75c 75c761fb 09056564 7fffffff 05cb7708 SHELL32!CRunnableTask::Run+0xce (FPO: [Non-Fpo])
0499f778 75c78a8b 0499f7b4 00000000 090b62d8 SHELL32!CShellTask::TT_Run+0x167 (FPO: [Non-Fpo])
0499f7c0 75c78bbf 0499f7d8 7753b2b1 05cb7708 SHELL32!CShellTaskThread::ThreadProc+0xa3 (FPO: [Non-Fpo])
0499f7c8 7753b2b1 05cb7708 03de89a8 0499f84c SHELL32!CShellTaskThread::s_ThreadProc+0x1b (FPO: [Non-Fpo])
0499f7d8 773dd7c4 090b62d8 7322bf39 03de89a8 SHLWAPI!ExecuteWorkItemThreadProc+0xe (FPO: [Non-Fpo])   

Microsoft's repair method is to judge in _IsRegisteredCPLApplet, the incoming DLL path to be loaded and the default existing .cpl path match, if the input path does not exist in the whitelist, then return False, no DLLs are loaded.

For the patch function: He wants to determine if it exists in a list. Get the first element first, then compare them in order: 76f136bb ff30 push dword ptr [eax] ds:0023:067f1170=038bf1a0

0:002> db 038bf1a0
038bf1a0 43 00 3a 00 5c 00 50 00-52 00 4f 00 47 00 52 00 C.:.\.P.R.O.G.R.
038bf1b0 41 00 7e 00 31 00 5c 00-4d 00 49 00 46 00 35 00 A.~.1.\.M.I.F.5.
038bf1c0 42 00 41 00 7e 00 31 00-5c 00 4f 00 66 00 66 00 B.A.~.1.\.O.f.f.
038bf1d0 69 00 63 00 65 00 31 00-34 00 5c 00 4d 00 4c 00 i.c.e.1.4.\.M.L.
038bf1e0 43 00 46 00 47 00 33 00-32 00 2e 00 43 00 50 00 C.F.G.3.2...C.P.
038bf1f0 4c 00 00 00 00 00 05 10-66 a6 ec 53 ff ff 00 8c L.......f..S....
038bf200 57 00 69 00 6e 00 64 00-6f 00 77 00 73 00 20 00 W.i.n.d.o.w.s.
038bf210 77 00 69 00 6c 00 6c 00-20 00 69 00 6e 00 73 00 w.i.l.l. .i.n.s.

Second cycle
0:002> db 0692e7d0
0692e7d0 43 00 3a 00 5c 00 57 00-69 00 6e 00 64 00 6f 00 C.:.\.W.i.n.d.o.
0692e7e0 77 00 73 00 5c 00 73 00-79 00 73 00 74 00 65 00 w.s.\.s.y.s.t.e.
0692e7f0 6d 00 33 00 32 00 5c 00-77 00 73 00 63 00 75 00 m.3.2.\.w.s.c.u.
0692e800 69 00 2e 00 63 00 70 00-6c 00 00 00 6c 00 00 00 i...c.p.l...l...
0692e810 33 f7 4b 53 01 00 00 80-14 00 4c 00 45 00 33 00 3.KS...L.E.3.
0692e820 43 00 42 00 46 00 39 00-32 00 41 00 33 00 34 00 C.B.F.9.2.A.3.4.
0692e830 36 00 46 00 33 00 34 00-30 00 42 00 31 00 39 00 6.F.3.4.0.B.1.9.
0692e840 31 00 35 00 38 00 43 00-32 00 35 00 42 00 43 00 1.5.8.C.2.5.B.C.
the third time:
0:002> db poi(eax)
0384a9c0 43 00 3a 00 5c 00 57 00-69 00 6e 00 64 00 6f 00 C.:.\.W.i.n.d.o.
0384a9d0 77 00 73 00 5c 00 73 00-79 00 73 00 74 00 65 00 w.s.\.s.y.s.t.e.
0384a9e0 6d 00 33 00 32 00 5c 00-46 00 6c 00 61 00 73 00 m.3.2.\.F.l.a.s.
0384a9f0 68 00 50 00 6c 00 61 00-79 00 65 00 72 00 43 00 h.P.l.a.y.e.r.C.
0384aa00 50 00 4c 00 41 00 70 00-70 00 2e 00 63 00 70 00 P.L.A.p.p...c.p.
0384aa10 6c 00 00 00 00 00 6e 00-ea 57 13 53 73 00 00 80 l.....n..W.Ss...
0384aa20 32 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 00 2...............
0384aa30 01 00 00 00 00 00 00 00-a0 71 60 06 e8 95 a5 76 .........q`....v
  

This way, even if you try to load the path, it will be rejected because it is no longer in the whitelist.

[0x05]. About detection

    In order to detect this vulnerability generically, it needs to be an unconventional format detection for the shell item id list.

SHITEMID[0] = \x1F\x50\xE0\x4F\xD0\x20\xEA\x3A\x69\x10\xA2\xD8\x08\x00\x2B\x30\x9D
    SHITEMID[1] = \x2E\x1E\x20\x20\xEC\x21\xEA\x3A\x69\x10\xA2\xDD\x08\x00\x2B\x30\x30\x9D
    

In addition, for ExtraDtat:

ExtraData.BlockSize = 0x00000010
ExtraData.BlockSignature = 0xA0000005

The detection logic is as follows:

If(SHITEMID[0] == "\x1f \x50 ..." & SHITEMID[1] = "\x2e \x1f ...")
{
If(SHITEMID[2] in (".\\" || ".dll"))
{
If(ExtraData.BlockSize = 0x00000010 && ExtraData.BlockSignature = 0xA0000005)
{
Alert("Found CVE-2017-8464 Vulnerability\n");
}
}
}

[0x06].Other

Limited to the lack of understanding of KnownFolderDataBlock and a more in-depth analysis of related calls, it is not certain whether the KnownFolderDataBlock must be associated with this vulnerability, but apparently Microsoft. The way to fix this vulnerability is based on whitelist filtering. Even if the KnownFolderDataBlock can be triggered, it will not load successfully.

[references]

  • [MS-SHLLINK]-160714.pdf
  • Https://msdn.microsoft.com/en-us/library/dd891343.aspx

The looking glass service

Many internet agents offer (ISPs, IXPs, etc) what it's called the looking glass service . Only in this web site a full list of 1150 looking glass services are offered: http://www.bgplookingglass.com.

Looking glass services are a great way to understand if a target is up and how to reach it.

It's important to note that the Looking Glass service can be configured on Juniper, Cisco (IOS o IOS-XR), Extreme (NetIron), Quagga, BIRD, OpenBGPd, Vyatta/VyOS/EdgeOS, or FRRouting routers.

OTOH, it's necessary to install a Looking Glass client in a web server in order to be able to use the service. There exist a number of Looking Glass (often called LG) service software packages that can be installed in a web server. . In Ubuntu/Debian we can find:

# apt-cache search looking | grep glass
rancid-cgi - looking glass CGI based on rancid tools

However, the only documentation found about this package is this website, which means it's not widely used.

We al have MRLG (See http://mrlg.op-sec.us/), but it seems quite old...

Nevertheless, the typical LG software used is the standard version of Looking Glass obtained from Version6.net. A good source explaining how to install it in a CentOS distro is: http://amar-linux.blogspot.com/2013/06/setting-up-bgp-looking-glass-centos-6.html. It can also be found in its github reporsitory: https://github.com/version6net/lg.

All of them are services developed in Perl, however we can find other solutions developed in PHP, IE (https://github.com/respawner/looking-glass ...which is fully documented in https://looking-glass.readthedocs.io.

HOW DOES IT WORK

For the normal functioning of the Looking Glass service it's necessary to have access to router's ports 2601 and 2605. The software generally connects to these ports via telnet. Generally credentials for this telnet are stored in a config file and the LG software simply connects via telnet to the router and sends BGP commands, returning the output to the web service. The typical BGP commands are like "sh ip bgp".

Therefore it's necessary to open 2601 and 2605 ports in the firewalls before the LG service to work properly. It will also be necessary to have a working NGINX or Apache Web server. Finally the appropiate libraries will have to be installed (depending on each solution).

On the other hand the router will have to be prepared to wait allow BGP commands remotely. For instance configuration for a Juniper OS (at least for the PHP solution previously mentioned) is the following:https://looking-glass.readthedocs.io/en/latest/juniper/. A restricted user in the router is recommended for the telnet connection and BGP command execution.

Trying to get a Bro Cluster to work

After developing an analyzer as a plugin, I had to develop a number of bro scripts to make my tool work. Finally they were 4 bro scripts. We prepared a Cluster infrastructure with two worker nodes, each with two network interfaces, one of them listening in two different VLANs and the other in the monitoring network. And a manager&proxy node running in the monitoring network only.

The installation was quite smooth. Used a Debian Jessy 64bits and followed instructions found in the Bro website. After installation, both workers and the manager seemed to work fine. Not exactly.

Briefly, in Manager node, bro scripts should be copied in

[1] /usr/local/bro/share/bro/site

...broctl should be configured modifying values of:

[2] /usr/local/bro/etc/broctl.cfg

...logs would be copied to:

[3] /mnt/logs/current

...given the different and deep-named directories I decided to create some symbolic links to them in my /home/bro directory:

root@Manager:/home/bro# ls -l
total 8
drwxr-xr-x 6 bro  bro  4096 Jun  7 13:38 aux_files
drwxr-xr-x 3 bro  bro  4096 May 31 13:30 bro
lrwxrwxrwx 1 root root   29 Jun  8 13:51 broctl.cfg -> /usr/local/bro/etc/broctl.cfg
lrwxrwxrwx 1 root root   17 Jun  8 13:50 logs -> /mnt/logs/current
lrwxrwxrwx 1 root root   29 Jun  8 13:50 site -> /usr/local/bro/share/bro/site

These are the steps to follow when any change have to be introduced in the configuration or bro scripts:

  1. Modify bro scripts located in [1]
  2. Modify (if necessary) broctl options in [2]
  3. launch Bro Control (broctl)
  4. check
  5. deploy

All broctl commands, which can be looked up using the 'help' command, are actually python scripts located in:

[4] /usr/local/bro/lib/broctl/Brocontrol

It's not difficult to understand what they are doing if you know a little bit of Python. For instance, deploy and install commands are run with the control.py python script.

Basically, when a 'deploy' is sent in broctl, the scripts in [1] are copied to:

[5] /home/bro/bro/spoolinstalled-scripts-do-not-touch/site

...both in the manager and all worker nodes.

Why these directories?. Well, this is configured in the broctl.cfg [2] file. In this file there are a number of variables that will allow us to configure directories and bro-scripts like:

  1. PolicyDirSiteInstall: Directory where all bro scripts are copied both in workers and manager nodes. By default is ${SpoolDir}/installed-scripts-do-not-touch/site. In my case is the directory mentioned in [5].
  2. SitePolicyPath: Colon-separated list of Directories to search for site-specific policy (bro) files. For each such directory, all files and subdirectories are copied to PolicyDirSiteInstall during broctl ‘install’ or ‘deploy’. By default is /usr/local/bro/share/bro/site.
  3. SitePolicyWorker: Space-separated list of bro scripts that should be run in EVERY worker node of the cluster. By default its value is local-worker.bro, which is an empty bro script. They will be copied to ALL nodes of the cluster when a 'deploy' or 'install' commands are launched. If we want to run different scripts in both nodes, the process consist in installing the script for one worker (in all nodes) and then, modify nodes.cfg file, modify this variable and send an 'update' command in broctl.
  4. sitepolicyworker=my-prot-worker-main.bro my-prot-aux-functions.bro
    
  5. sitepolicymanager: Space-separated list of bro scripts that should be run in the manager node. In my case this script manages notices generated by workers.
  6. sitepolicymanager=manager_notice_policy.bro
    
  7. LogDir: Location of the log directory where log files will be archived each rotation interval.
    LogDir = /mnt/logs
    

Of course, there are many other interesting options that can be modified for BroControl. Have a llo to the BroControl documentation website.

Using a bro analyzer as a plugin in a Bro Cluster

One of the problem I faced was that all bro scripts running in the worker nodes, monitored a proprietary protocol for which I had developed an analyzer as a plugin. If run standalone both in the manager and worker nodes, the script and the proprietary analyzer worked fine. But if run as a cluster, the scripts seemed not to understand proprietary network traffic. After a couple of checks I found that the plugin was not being 'loaded' by bro in the workers. Then I realized that the problem should be the Plugin directory where Bro was looking for the plugin. Soon I found that there are two different options in broctl.cfg file that could help me:

  • Env_Vars:A comma-separated list of environment variables (e.g. env_vars=VAR1=123, VAR2=456) to set on all nodes immediately before starting Bro. Node-specific values (specified in the node configuration file) override these global values.
  • Env_Vars=BRO_PLUGIN_PATH=/home/bro/aux_files/plugin
    
  • SitePluginPath:Directories to search for custom plugins (i.e., plugins that are not included with broctl), separated by colons.
  • SitePluginPath=/home/bro/aux_files/plugin
    

...but somehow, after deploying, it didn't work.

A little bit of research led to the /usr/local/bro/share/broctl/scripts/run-bro script, where I found that apparently only three environment variables were set, regardless of what I wrote in the broctl.cfg file:

...
echo "PATH=${PATH}" >.env_vars
echo "BROPATH=${BROPATH}" >>.env_vars
echo "CLUSTER_NODE=${CLUSTER_NODE}" >>.env_vars
echo "BRO_PLUGIN_PATH=${BRO_PLUGIN_PATH}" >>.env_vars
...

I just added the last line, et voila!, my analyzer was working fine!... well, not so fine. Some thing didn't work. Somehow some events were not noticed to the manager node, some log file were not created in the manager LogDir, and a config file was not read.



The fact of cluster logging

My first move was to read the stderr.log and stdout.log files stored in the manager node BUT, somehow, even if I filled my scripts with 'print' instructions, the stdout.log was empty!...

...until I found the /usr/local/bro/share/bro/base/frameworks/cluster/nodes directory. Here you can find some redef options for the worker, manager and proxy nodes. And one of these options was:

## Don't do any local logging.
redef Log::enable_local_logging = F;

...setting this option to True and deploying again, made the workers to start logging locally in the following directory of the worker nodes:

[7] /home/bro/bro/spool/worker-n/

There I found the reporter.log, that showed me that there was a mistake in one of my bro scripts. Curiously, the reporter.log file in the manager was empty, which means that logs are NOT COPIED from the workers to the manager node. That's the reason why two of my log files did not appear in the manager node but they were in the workers' log [7] directory.

The fact is, log files correspond to the node where you find them. stdout.log of the worker nodes will show the standard output of the bro scripts running in the workers, while stdout.log in the manager correspond to what the manager policy script is sending to the standard output.

Regarding the protocol log files, we find some network traffic messages in the Manager but these are not all the network traffic messages received in the worker, only about 1 out of 10 messages received in the worker nodes are actually logged in the manager, and this is something we still have to understand.



Opening a config file in the workers

My third problem was related with a config file that should be opened both by the workers and the manager bro scripts. I was trying to open it with something like:

Input::add_event([$source="config.cfg", $reader=Input::READER_RAW, $name="conf", $fields=Val, $ev=config_entry]);

The solution came when I realized that I had to use the full file path, which should be the same both in the worker and the manager nodes:

Input::add_event([$source="/home/bro/bro/spool/installed-scripts-do-not-touch/site/config.cfg", $reader=Input::READER_RAW, $name="conf", $fields=Val, $ev=config_entry]);

This made it work.



The cluster bro processes structure

Now I had to be able to send my local worker log files to the manager. First of all I tried to understand the process structure.

In every worker node there are three bro processes:

root@Mario:/usr/local# ps -ef | grep bro
root      25139      1  0 Jun08 ?        00:00:00 /bin/bash /usr/local/bro/share/broctl/scripts/run-bro -1 -i eth0 -U .status -p broctl -p broctl-live -p local -p worker-1 local.bro broctl base/frameworks/cluster my-prot.bro broctl/auto
root      25145  25139 20 Jun08 ?        03:38:50 /usr/local/bro/bin/bro -i eth0 -U .status -p broctl -p broctl-live -p local -p worker-1 local.bro broctl base/frameworks/cluster my-prot.bro broctl/auto
root      25148  25145  0 Jun08 ?        00:00:09 /usr/local/bro/bin/bro -i eth0 -U .status -p broctl -p broctl-live -p local -p worker-1 local.bro broctl base/frameworks/cluster my-prot.bro broctl/auto

So the first was the parent of one of the bro processes which forked to the other one. Parameters where passed to the run-bro script mainly on run bro as nohup with all the same parameters:

...
mybro=${bro}
...
nohup "$mybro" "$@" &
...

So, the question was, why is the working bro process forking?. lsof command was very useful for that:

root@worker-1:/usr/local# lsof | grep 25145 | egrep -v "\.bro|\.so|pipe" | grep 25186
bro:      25145 25186        root  cwd       DIR                8,1      4096     656821 /home/bro/bro/spool/worker-1
bro:      25145 25186        root  rtd       DIR                8,1      4096          2 /
bro:      25145 25186        root  txt       REG                8,1 134900600    1315052 /usr/local/bro/bin/bro
bro:      25145 25186        root    1w      REG                8,1       188     655382 /home/bro/bro/spool/worker-1/stdout.log
bro:      25145 25186        root    2w      REG                8,1        97     655383 /home/bro/bro/spool/worker-1/stderr.log
bro:      25145 25186        root    4u     IPv4             152650       0t0        UDP 192.168.1.10:41394->192.168.1.5:domain 
bro:      25145 25186        root  202u     pack             152653       0t0        ALL type=SOCK_RAW
bro:      25145 25186        root  260r      REG                8,1       739     657284 /home/bro/bro/spool/installed-scripts-do-not-touch/site/config.cfg
bro:      25145 25186        root  272u     unix 0xffff88007a6abb80       0t0     152656 socket
bro:      25145 25186        root  273w      REG                8,1 283722769     656833 /home/bro/bro/spool/worker-1/my-prot.log
bro:      25145 25186        root  279w      REG                8,1    301214     656838 /home/bro/bro/spool/worker-1/communication.log
bro:      25145 25186        root  280w      REG                8,1     42471     656841 /home/bro/bro/spool/worker-1/weird.log
bro:      25145 25186        root  281w      REG                8,1       447     656844 /home/bro/bro/spool/worker-1/reporter.log



root@worker-1:/usr/local# lsof | grep 25148 | egrep -v "\.bro|\.so|pipe" 
bro       25148              root  cwd       DIR                8,1      4096     656821 /home/bro/bro/spool/worker-1
bro       25148              root  rtd       DIR                8,1      4096          2 /
bro       25148              root  txt       REG                8,1 134900600    1315052 /usr/local/bro/bin/bro
bro       25148              root    0u     IPv4             151842       0t0        TCP 192.168.1.10:35948->192.168.1.20:47762 (ESTABLISHED)
bro       25148              root    4u     IPv4             152650       0t0        UDP 192.168.1.10:41394->192.168.1.5:domain 
bro       25148              root  202u     pack             152653       0t0        ALL type=SOCK_RAW
bro       25148              root  260r      REG                8,1       739     657284 /home/bro/bro/spool/installed-scripts-do-not-touch/site/config.cfg
bro       25148              root  273u     unix 0xffff88007a6ab040       0t0     152657 socket
bro       25148              root  279u     IPv4             151845       0t0        TCP 192.168.1.10:57286->192.168.1.20:47761 (ESTABLISHED)
bro       25148              root  284u     IPv4             151850       0t0        TCP *:47763 (LISTEN)
bro       25148              root  285u     IPv6             151851       0t0        TCP *:47763 (LISTEN)

So, the first bro process (parent), is responsible for listening on the monitoring interface and writing local log files, while the second (child) is responsible for communicating with the manager (two different connections) and listening from the proxy.

In the manager node, there are two processes for the proxy and another two for the manager:

root@Manager:/home/bro/site# ps -ef | grep bro
root      38193      1  0 Jun08 ?        00:00:00 /bin/bash /usr/local/bro/share/broctl/scripts/run-bro -1 -U .status -p broctl -p broctl-live -p local -p manager local.bro broctl base/frameworks/cluster notice_policy.bro broctl/auto
root      38199  38193  3 Jun08 ?        00:37:58 /usr/local/bro/bin/bro -U .status -p broctl -p broctl-live -p local -p manager local.bro broctl base/frameworks/cluster notice_policy.bro broctl/auto
root      38201  38199  0 Jun08 ?        00:00:21 /usr/local/bro/bin/bro -U .status -p broctl -p broctl-live -p local -p manager local.bro broctl base/frameworks/cluster notice_policy.bro broctl/auto


root      38236      1  0 Jun08 ?        00:00:00 /bin/bash /usr/local/bro/share/broctl/scripts/run-bro -1 -U .status -p broctl -p broctl-live -p local -p proxy-1 local.bro broctl base/frameworks/cluster local-proxy broctl/auto
root      38242  38236  3 Jun08 ?        00:40:23 /usr/local/bro/bin/bro -U .status -p broctl -p broctl-live -p local -p proxy-1 local.bro broctl base/frameworks/cluster local-proxy broctl/auto
root      38243  38242  0 Jun08 ?        00:00:03 /usr/local/bro/bin/bro -U .status -p broctl -p broctl-live -p local -p proxy-1 local.bro broctl base/frameworks/cluster local-proxy broctl/auto

Netstat showed the open connections among them and the workers.

root@Manager:/home/bro/site# netstat -tupan | grep bro
tcp        0      0 0.0.0.0:47761           0.0.0.0:*               LISTEN      38201/bro       
tcp        0      0 0.0.0.0:47762           0.0.0.0:*               LISTEN      38243/bro       
tcp        0      0 192.168.1.20:47762         192.168.1.10:35948         ESTABLISHED 38243/bro       
tcp        0      0 192.168.1.20:47761         192.168.1.20:38523         ESTABLISHED 38201/bro       
tcp        0      0 192.168.1.20:38523         192.168.1.20:47761         ESTABLISHED 38243/bro       
tcp        0      0 192.168.1.20:47761         192.168.1.11:57110         ESTABLISHED 38201/bro       
tcp        0      0 192.168.1.20:47762         192.168.1.11:45263         ESTABLISHED 38243/bro       
tcp        0      0 192.168.1.20:47761         192.168.1.10:57286         ESTABLISHED 38201/bro       
tcp6       0      0 :::47761                :::*                    LISTEN      38201/bro       
tcp6       0      0 :::47762                :::*                    LISTEN      38243/bro       
udp        0      0 192.168.1.20:44445         192.168.1.5:53             ESTABLISHED 38242/bro       
udp        0      0 192.168.1.20:35156         192.168.1.5:53             ESTABLISHED 38199/bro  

So, to summarize:

  • The proxy (child process) has one connection to every worker node.
  • The manager (child process) has one connection to every worker node.
  • The proxy and the manager (boy child processes) are connected through a socket (ports 47761 to 38523, in this case)
  • Both proxy and manager parents are more interested in logging processes (they are resposible for writing logs to disk) and DNS resolution

Fine, but we still don't know where are those proprietary logs lost. Installed tcpdump and it quickly showed me that the proprietary protocol logs were being sent from the worker to the manager node and received by the manager child process.

So it seemed the problem should be in my manager policy script. It just implemented the hook of Notice::policy...

hook Notice::policy(not:Notice::Info)

lunes, 29 de octubre de 2018

The APX logic file format

The APX file format

The APX File has a variable size because it stores a lot of different information.

In general terms, the APX file is composed by a first section with general information of the strategy (timestamps, versions, owners, CRCs, the internal strategy password). This section is about 2000 bytes long.

The next section determines the number of Holding registers (MW%), register constants (KW%) and coils (M%) and store all the constant values. This section size depends on the number of constants specified.

The next section stores the error messages that could send the PLC to Unity Pro.

The next section stores network information (IPs, network masks, SNMP configuration, NTP configuration, SMTP configuration, etc.) of all the networking modules associated with the PLC.

The next section stores the strategy in binary format. It also stores chunks of bytes which are ZLIB compressed, when uncompressed they turned to be XML files with configuration information.

So the main structure is:

  1. General information section
  2. Registers and coils
  3. Error messages
  4. Network information
  5. Strategy in binary format


General information section

This section comprises of a number of subsections, separated by a “0E <16-bit CRC> 00 00” separator.

The first subsections are the following:

As we can see, these first blocks, start with 2 or 3 ASCII character (APX subsection, SD subsection, RT subsection…)

At the end of all these blocks, we can see the signature prevoiously mentioned (“E0 16-bit CRC 00 00”).

In the first block, bytes 11 to 15 are 4 byte CRC, in this case “00 C8 2D 0D” that will be repeated in 4 or 5 places of the first section.

We think that 16-bit-CRC values we find all along the APX file validate the starting subsection (they always appear in the beginning of a subsection (or the end of the previous one, which is the same).



Subsections This first subsection is two bytes smaller when it belongs to the TSX premium PLC. The two different bytes seem to be two “00” bytes before the end of the subsection (before “93E0”).

The second subsection is also four bytes smaller when it belongs to the TSX Premium PLC.

Strange enough, the third subsection (corresponding to “RT” ASCII characters) is much longer in the TSX PLC ( 110 vs 64 bytes).

This is because in a TSX PLC APX file, subsections 4 and 5 do not exist and its information is stored (in short) in those extra 56 bytes (prohect date and timestamp, versions, etc.).

This would be the fourth subsection:

This is the fifth subsection:

This subsection stores project information like timestamps, versions, etc, and it's accessed when a message “00 03 00” is sent to the PLC.

And this is the sixth subsection, it also stores some information about the project.

After the project version information come 1024 (0400h) blank bytes intended to store the project description.

And these are the following subsections. Note that Subsection 7 is sent when a “00 03 04” request is done.

However this is not the structure of this section if take into account how the file is read or write in the upload / download process:

The thin blue lines indicate how the bytes are read or write during the strategy upload/download process. It has nothing to do with the separators previously mentioned.

In the red boxes we can see the four-byte-CRC value which is repeated at least 4 time all along the APX file. The small red boxes are the 16-bit CRC values. These 16 bit CRC change on every regeneration of the project, even when the project has NOT changed at all.



Section 2. Registers and coils

In section 2 there are no subsections any more.

Starting in offset AA0h come a bunch of bytes that define the number of bytes stored by holding registers (MW%), constants (KW%, in offset B2 – FF 01 – 1023 bytes – 512 register constants) and coils (M%).

After B00 line come as many bytes as indicated in the KW% constant value (0b02h). In this case they are 01 FF (little endian) bytes. That means that the register constants will be stored in the APX file from 0B0Ch offset to 0D0Bh offset.



Section 3. Error messages

Starting on 0D0Ch, start a block with all error messages:

This block ends with four bytes set to FF (first black box):

It's important to note that both this section and the next start with four bytes “01 00 00 00”, and that subsections seem to start “01 00 A0 00”



Section 4. Network information

Following the end of the error message list, we can see two subsection separators and in the third subsection we can see, in the three coloured boxes the internal IPs of our PLC (in little endian format). In this case “CD 0D 1E 0A” stands for 10.30.13.205, “0A 1E 0D 01” stands for 10.30.13.1 (the gateway IP) and “00 FF FF FF” is the netmask, 255.255.255.0.

After more than 3000 “00” bytes come the next network information.

In offset 2350h (in our case) appear the SNMP configuration, with snmp keys (“public1, public2, public3”), SysLocation (“KKKKKKK...”) and SysContact data (“123456789...”), administration IP and other SNMP information.

Next comes SMTP information with the SMTP server IP address, port, username (“root”), password (“123456789012”). It also stores SMTP From, To and subject information.

This information could allow a malicious attacker to send emails on behalf the PLC.

Then it comes a section of repetitive characters:

In blue we can see the TSX automate APX File and in white the Modicon M340 one. There are some differences but it's a list of “DC053C” values.

From my point of view, this is a way of synchronization.

If the strategy has several cards configured, a “FF FF FF FF” separator comes and the process start again.



Section 5. Binary Strategy code

After the Network information come what we think is the strategy binary code and some blobs of ZLIB bytes.

This section is divided in “PK” subsections:

As we can see after the corresponding “?? E0 CRC16 00 00” signature, comes a “PK” signature an another PK, compressed subsection comes.

This occurs up to 9 times until the end of OUR strategy. Some of these PK files seem to be compressed data, other, with this aspect seem binary data:

The PK signatures appear in our strategy in offsets:

  • 000050F0h
  • 000052B0h
  • 00005C10h
  • 00005CF6h
  • 000064C4h
  • 000066A5h
  • 000066FAh
  • 000069C4h
  • 00006AEDh
  • 000010065h
  • 000010207h

As we can see, their size is small except for one, which is the only one with binary aspect.

Using the binwalk tool it found at least 5 ZLIB blobs, stored in two different PK subsections.

When decompressed these ZLIB blobs found that some of them were compressed XML files, while other stored ST functions:

On the big PK subsection we can even understand the binary code. We tried a simple program and tried to find it in the binary code. And we found it:

Written in our own assembler language we could understand where was it stored and how was it run.



Changes in the APX after a regeneration with no changes at all

We have detected that regenerating a project with no change in it, the APX file generated contains 36 different bytes.

The different bytes are the following:

APX subsection
00000Bh (4 bytes) → CRC32 value

SD Subsection
00003Ch (2 bytes) → SD subsection CRC16 value
000067h (4 bytes) → Copy of the CRC32 value
00006Eh (1 byte) → Regeneration Version number

RT Subsection
000164h (2 bytes) → RT subsection CRC16 value

000284h (2 bytes) → 4th Subsection CRC16 value
0002AFh (3 bytes) → Timestamp
0002B7h (3 bytes) → same timestamp
0002E5h (4 bytes) → Copy of the CRC32 value
0002EDh (4 bytes) → Copy of the CRC32 value
0002F1h (4 bytes) → Copy of the CRC32 value
000301h (1 byte) → Unknown
000355h (1 byte) → Version number
000359h (1 byte) → Unknown


Modification of the file

We were unable to upload an strategy to the PLC with ANY modification. And this must be caused by the CRC checks.

¿Why do we know it's a CRC value?

Running an strings command against the Firmware binary file we find the following function names (related with CRC):

It's clear that CRC16 flags and CRC 32 values are computed all along the APX File.

I tried to calculate the CRC 32 value of entire file, starting from byte 1 to byte 3000. None of them did the trick.

I also tried to calculate 16-bit CRC values of the different subsection. Couldn't get the correct CRC.

As mentioned earlier, we think that 16-bit-CRC values we find all along the APX file validate the starting subsection (they always appear in the beginning of a subsection (or the end of the previous one, which is the same).

sábado, 30 de junio de 2018

Snort rules for modbus and Unity traffic

Snort rules for industrial traffic are important to detect any strange behaviour. DigitalBond did already detect a number of rules which are included at the bottom of this post. However the number of snort rules detected here is much higher.

Taking a look at the Modbus specification and after my research in the Unity/UMAS protocol I have detected a total of 51 snort rules that detect anomalous Modbus and Unity traffic. This is the list of rules:

This is the whole listing:

Modbus protocol rules

RULE 1

Explanation: REQUEST MODBUS 0x01 --> BYTES 4-5 ==> Values should not be above 07D0h -->See Pag 12 modbus specification

alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|";
offset:2; depth:2; content:"|01|";offset:7; depth:1; byte_test: 2,>,0x07D0,10;
msg="Modbus TCP 0x01: Too many coils to read requested. Possible DoS";
classtype:attempted-dos; sid:987000001; rev:1; priority:1;)


RULE 2

Explanation: RESPONSE MODBUS 0x81 --> BYTE 2 (EXCEPTION CODE) ==> 0x01 o 0x02 o 0x03 o 0x04 --> See Pag 12 modbus specification

alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|81|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x01: Response exception code not allowed"; sid:987000002; rev:1; priority:1;)


RULE 3

Explanation: REQUEST MODBUS 0x02 --> BYTES 4-5 ==> Should not be higher than 07D0h -->See Pag 13 modbus specification

alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|02|"; offset:7; depth:1; byte_test: 2,>,0x07D0,10; msg="Modbus TCP 0x02: Too many inputs to read requested. Possible DoS"; classtype: attempted-dos; sid:987000003; rev:1; priority:1;)

RULE 4



Explanation --> RESPONSE MODBUS 0x82 --> BYTE 2 (EXCEPTION CODE) ==> 0x01 o 0x02 o 0x03 o 0x04 --> See Pag 13 modbus specification

alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|82|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x02: Response exception code not allowed"; sid:987000004; rev:1; priority:1;)



RULE 5

Explanation: REQUEST MODBUS 0x03 --> BYTES 4-5 ==> Should not be higher than 0x007D --> See Pag 15 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|03|"; offset:7; depth:1; -->  BYTE_test: 2,>,0x007D,10; msg="Modbus TCP 0x03: Too many holding registers to read requested. Possible DoS"; classtype: attempted-dos;sid:987000005; rev:1; priority:1;)




RULE 6

Explanation --> RESPONSE MODBUS 0x83 --> BYTE 2 (EXCEPTION CODE) ==> 0x01 o 0x02 o 0x03 o 0x04 --> See Pag 15 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|83|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x03: Response exception code not allowed"; sid:987000006; rev:1; priority:1;classtype: non-standard-protocol;)




RULE 7

Explanation: REQUEST MODBUS 0x04 --> BYTES 4-5 ==> Should not be higher than 0x007D --> See Pag 16 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|04|"; offset:7; depth:1; -->  BYTE_test: 2,>,0x007D,10; msg="Modbus TCP 0x04: Too many input registers to read requested. Possible DoS"; classtype: attempted-dos; sid:987000007; rev:1; priority:1;)




RULE 8

Explanation --> RESPONSE MODBUS 0x84 --> BYTE 2 (EXCEPTION CODE) ==> 0x01 o 0x02 o 0x03 o 0x04 --> See Pag 16 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|84|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x04: Response exception code not allowed"; classtype: non-standard-protocol; sid:987000008; rev:1; priority:3;)




RULE 9

Explanation: REQUEST MODBUS 0x05 --> BYTES 4-5 ==> Allowed values 0x0000 or 0xFF00 --> See Pag 17 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|05|"; offset:7; depth:1; content:"!|00 00|FF 00|"; offset:10, depth:2;  msg="Modbus TCP 0x05: Coil output value not allowed"; sid:987000009; rev:1; priority:1;)




RULE 10

Explanation --> RESPONSE MODBUS 0x05 --> BYTE 4-5 ==> Allowed values: 0x0000 or 0xFF00 --> See Pag 18 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|05|"; offset:7; depth:1; content:"!|00 00|FF 00|"; offset:10, depth:2;  msg="Modbus TCP 0x05: Coil value incorrect"; sid:987000010; rev:1; priority:1;)




RULE 11

Explanation --> RESPONSE MODBUS 0x85 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values 0x01 or 0x02 or 0x03 or 0x04 --> See Pag 18 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|85|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x05: Response exception code not allowed"; sid:987000011; rev:1; priority:3;classtype: non-standard-protocol;)




RULE 12

Explanation --> RESPONSE MODBUS 0x86 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 1 to 4 --> See Pag 19 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|86|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x06: Response exception code not allowed"; sid:987000012; rev:1; priority:3;classtype: non-standard-protocol;)




RULE 13

Explanation --> RESPONSE MODBUS 0x87 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 01 or 04 --> See Pag 20 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|87|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x07: Response exception code not allowed"; sid:987000013; rev:1; priority:3;classtype: non-standard-protocol;)




RULE 14

Explanation --> RESPONSE MODBUS 0x88 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 01 or 03 or 04 --> See Pag 22 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|88|"; offset:7; depth:1; content:!"|01|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x08: Response exception code not allowed"; sid:987000014; rev:1; priority:3;classtype: non-standard-protocol;)




RULE 15

Explanation --> RESPONSE MODBUS 0x8B --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 01 or 04 --> See Pag 25 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|8B|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x0B: Response exception code not allowed"; sid:987000015; rev:1; priority:1;)




RULE 16

Explanation --> RESPONSE MODBUS 0x8C --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 01 or 04 --> See Pag 27 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|8C|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x0C: Response exception code not allowed"; sid:987000016; rev:1; priority:3;classtype: non-standard-protocol;)




RULE 17

Explanation: REQUEST MODBUS 0x0F --> BYTES 4-5 ==> Should not be higher than 0x07B0 --> See Pag 29 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|0F|"; offset:7; depth:1; -->  BYTE_test: 2,>,0x07B0,10; msg="Modbus TCP 0x0F: Too many coils to write requested"; sid:987000017; rev:1; priority:1;)




RULE 18

Explanation --> RESPONSE MODBUS 0x0F --> BYTES 4-5 ==> Should not be higher than 0x07B0 --> See Pag 29 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|0F|"; offset:7; depth:1; -->  BYTE_test: 2,>,0x07B0,10; msg="Modbus TCP 0x0F: Too many coils responded"; sid:987000018; rev:1; priority:1;)




RULE 19

Explanation --> RESPONSE MODBUS 0x8F --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 --> See Pag 29 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|8F|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x0F: Response exception code not allowed"; sid:987000019; rev:1; priority:3;classtype: non-standard-protocol;)




RULE 20

Explanation: REQUEST MODBUS 0x10 --> BYTES 4-5 ==> Should not be over 0x007B --> See Pag 29 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|10|"; offset:7; depth:1; -->  BYTE_test: 2,>,0x007B,10; msg="Modbus TCP 0x10: Too many registers to write requested"; sid:987000020; rev:1; priority:1;)




RULE 21

Explanation --> RESPONSE MODBUS 0x10 --> BYTE 4-5 ==> Should not be higher than 0x007B --> See Pag 31 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|0F|"; offset:7; depth:1; -->  BYTE_test: 2,>,0x007B,10; msg="Modbus TCP 0x10: Too many registers responded"; sid:987000021; rev:1; priority:1;)




RULE 22

Explanation --> RESPONSE MODBUS 0x90 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 --> See Pag 31 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|90|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x10: Response exception code not allowed"; sid:987000022; rev:1; priority:3;classtype: non-standard-protocol;)




RULE 23

Explanation --> RESPONSE MODBUS 0x91 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 0x01 or 0x04 --> See Pag 32 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|91|"; offset:7; depth:1; content:!"|01|04|"; offset:8, depth:1;  msg="Modbus TCP 0x11: Response exception code not allowed"; sid:987000023; rev:1; priority:1;)




RULE 24

Explanation: REQUEST MODBUS 0x14 --> BYTE 2 ==> Allowed values: From 0x07 to 0xF5 --> See Pag 33 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|14|"; offset:7; depth:1; -->  BYTE_test: 1,>,0xF5,8; msg="Modbus TCP Request 0x14: message -->  BYTE count incorrect"; sid:987000124; rev:1; priority:1;)
alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|14|"; offset:7; depth:1; -->  BYTE_test: 1,<,0x07,8; msg="Modbus TCP Request 0x14: message -->  BYTE cunt incorrect"; sid:987000224; rev:1; priority:1;)




RULE 28

Explanation --> RESPONSE MODBUS 0x14 --> BYTE 2 ==> Allowed values: 0x07 to 0xF5 --> See Pag 33 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|14|"; offset:7; depth:1; -->  BYTE_test: 1,>,0xF5,8; msg="Modbus TCP Response 0x14: message -->  BYTE count incorrect"; sid:987000128; rev:1; priority:1;)
alert tcp $ $MODBUS_PLC 502 -> EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|14|"; offset:7; depth:1; -->  BYTE_test: 1,<,0x07,8; msg="Modbus TCP Response 0x14: message -->  BYTE cunt incorrect"; sid:987000228; rev:1; priority:1;)




RULE 31

Explanation --> RESPONSE MODBUS 0x94 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 or 0x08 --> See Pag 33 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|94|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x14: Response exception code not allowed"; sid:987000031; rev:1; priority:3;classtype: non-standard-protocol;)




RULE 32

Explanation: REQUEST MODBUS 0x15 --> BYTE 2 ==> Allowed values: 0x09 to 0xFB --> See Pag 34 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|15|"; offset:7; depth:1; -->  BYTE_test: 1,>,0xFB,8; msg="Modbus TCP Request 0x15: message -->  BYTE count incorrect"; sid:987000132; rev:1; priority:1;)
alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|15|"; offset:7; depth:1; -->  BYTE_test: 1,<,0x09,8; msg="Modbus TCP Request 0x15: message -->  BYTE cunt incorrect"; sid:987000232; rev:1; priority:1;)




RULE 36

Explanation --> RESPONSE MODBUS 0x15 --> BYTE 2 ==> Allowed values: 0x09 to 0xFB --> See Pag 35 modbus specification

 alert tcp $MODBUS_PLC 502 -> EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|15|"; offset:7; depth:1; -->  BYTE_test: 1,>,0xFB,8; msg="Modbus TCP Response 0x15: message -->  BYTE count incorrect"; sid:987000132; rev:1; priority:1;)
alert tcp $ $MODBUS_PLC 502 -> EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|15|"; offset:7; depth:1; -->  BYTE_test: 1,<,0x09,8; msg="Modbus TCP Response 0x15: message -->  BYTE cunt incorrect"; sid:987000232; rev:1; priority:1;)



RULE 40

Explanation --> RESPONSE MODBUS 0x95 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 or 0x08 --> See Pag 35 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|95|"; offset:7; depth:1; content:!"|01|02|03|04|08|"; offset:8, depth:1;  msg="Modbus TCP 0x15: Response exception code not allowed"; sid:987000040; rev:1; priority:3;classtype: non-standard-protocol;)




RULE 41

Explanation --> RESPONSE MODBUS 0x96 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 --> See Pag 37 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|96|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x16: Response exception code not allowed"; sid:987000041; rev:1; priority:3; classtype: non-standard-protocol;)




RULE 42

Explanation: REQUEST MODBUS 0x17 --> BYTES 4 and 5 ==> Allowed values: 0x0001 to 0x007D --> See Pag 38 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|17|"; offset:7; depth:1; -->  BYTE_test: 1,>,0x7D,10; msg="Modbus TCP Request 0x17: incorrect register count to read"; sid:987000142; rev:1; priority:1;)
alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|17|"; offset:7; depth:1; -->  BYTE_test: 1,<,0x01,10; msg="Modbus TCP Request 0x17: incorrect register count to read"; sid:987000242; rev:1; priority:1;)



RULE 43

Explanation: REQUEST MODBUS 0x17 --> BYTES 8 and 9 ==> Allowed values: 0x0001 a 0x0079 --> See Pag 38 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|17|"; offset:7; depth:1; -->  BYTE_test: 1,>,0x79,14; msg="Modbus TCP Request 0x17: incorrect register count to write"; sid:987000143; rev:1; priority:1;)
 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|17|"; offset:7; depth:1; -->  BYTE_test: 1,<,0x01,14; msg="Modbus TCP Request 0x17: incorrect register count to write"; sid:987000243; rev:1; priority:1;)




RULE 44

Explanation --> RESPONSE MODBUS 0x97 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 --> See Pag 39 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|97|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x17: Response exception code not allowed"; sid:987000044; rev:1; priority:3; classtype: non-standard-protocol;)




RULE 45

Explanation --> RESPONSE MODBUS 0x18 --> BYTES 2y3 ==> Allowed values: Should be lower than 0x0041 --> See Pag 41 modbus specification

 alert tcp $MODBUS_PLC 502 -> EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|18|"; offset:7; depth:1; -->  BYTE_test: 1,>,0x41,8; msg="Modbus TCP Response 0x18: message -->  BYTE count incorrect"; sid:987000145; rev:1; priority:1;)




RULE 46

Explanation --> RESPONSE MODBUS 0x18 --> BYTES 4y5 ==> Allowed values: 0x0001 to 0x001F --> See Pag 41 modbus specification

 alert tcp $MODBUS_PLC 502 -> EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|18|"; offset:7; depth:1; -->  BYTE_test: 1,>,0x1F,10; msg="Modbus TCP Response 0x18: message FIFO count incorrect"; sid:987000146; rev:1; priority:1;)
 alert tcp $ $MODBUS_PLC 502 -> EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|18|"; offset:7; depth:1; -->  BYTE_test: 1,<,0x01,10; msg="Modbus TCP Response 0x15: message FIFO count incorrect"; sid:987000246; rev:1; priority:1;)




RULE 47

Explanation --> RESPONSE MODBUS 0x98 --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 --> See Pag 41 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|98|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x18: Response exception code not allowed"; sid:987000047; rev:1; priority:3; classtype: non-standard-protocol;)




RULE 48

Explanation: REQUEST MODBUS 0x2B --> BYTE 2 ==> Allowed values: 0x0D or 0x0B --> See Pag 43 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|2B|"; offset:7; depth:1; content:"!|0D|0B|"; offset:8, depth:1;  msg="Modbus TCP Request 0x2B: Incorrect MEI Type"; sid:987000048; rev:1; priority:3; classtype: non-standard-protocol;)




RULE 49

Explanation --> RESPONSE MODBUS 0x2B --> BYTE 2 ==> Allowed values: 0x0D or 0x0B --> See Pag 43 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|2B|"; offset:7; depth:1; content:"!|0D|0B|"; offset:8, depth:1;  msg="Modbus TCP Response 0x2B: Incorrect MEI Type"; sid:987000049; rev:1; priority:3; classtype: non-standard-protocol;)




RULE 50

Explanation --> RESPONSE MODBUS 0xAB --> BYTE 2 (EXCEPTION CODE) ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 --> See Pag 44 modbus specification

 alert tcp $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|AB|"; offset:7; depth:1; content:!"|01|02|03|04|"; offset:8, depth:1;  msg="Modbus TCP 0x2B: Response exception code not allowed"; sid:987000050; rev:1; priority:3; classtype: non-standard-protocol;)




RULE 51

Explanation: REQUEST MODBUS 0x2B. --> BYTE 2=0x0E --> BYTE 3 ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 --> See Pag 44 modbus specification

 alert tcp $EXTERNAL any -> $MODBUS_PLC 502 (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|2B|"; offset:7; depth:1; content:"|0E|"; offset:8; depth:1; content:"!|01|02|03|04|"; offset:9, depth:1;  msg="Modbus TCP Request 0x2B: Incorrect Read Device ID Code"; sid:987000051; rev:1; priority:3; classtype: non-standard-protocol;)




RULE 52

Explanation --> RESPONSE MODBUS 0x2B. --> BYTE 2=0x0E --> BYTE 3 ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x04 --> See Pag 44 modbus specification

 alert tcp  $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|2B|"; offset:7; depth:1; content:"|0E|"; offset:8; depth:1; content:"!|01|02|03|04|"; offset:9, depth:1;  msg="Modbus TCP Response 0x2B: Incorrect Read Device ID Code"; sid:987000052; rev:1; priority:3; classtype: non-standard-protocol;)




RULE 53

Explanation --> RESPONSE MODBUS 0x2B. --> BYTE 2=0x0E --> BYTE 4 ==> Allowed values: 0x01 or 0x02 or 0x03 or 0x81 or 0x82 or 0x83 --> See Pag 44 modbus specification

 alert tcp  $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|2B|"; offset:7; depth:1; content:"|0E|"; offset:8; depth:1; content:"!|01|02|03|81|82|83|"; offset:10, depth:1;  msg="Modbus TCP Response 0x2B: Incorrect Conformity Level"; sid:987000053; rev:1; priority:3; classtype: non-standard-protocol;)


 


RULE 54

Explanation --> RESPONSE MODBUS 0x2B. --> BYTE 2=0x0E --> BYTE 5 ==> Allowed values: 0x00 o 0xFF --> See Pag 44 modbus specification

 alert tcp  $MODBUS_PLC 502 -> $EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; content:"|2B|"; offset:7; depth:1; content:"|0E|"; offset:8; depth:1; content:"!|00|FF|"; offset:11, depth:1;  msg="Modbus TCP Response 0x2B: Incorrect Conformity Level"; sid:987000054; rev:1; priority:3; classtype: non-standard-protocol;)




RULE 55

Explanation --> RESPONSE MODBUS MAYOR QUE 0x80 --> BYTE 2 ==> Allowed values: Should be lower than 0x0B --> See Pag 49 modbus specification

 alert tcp $MODBUS_PLC 502 -> EXTERNAL any (flow:from_client, established; content:"|00 00|"; offset:2; depth:2; -->  BYTE_test: 1,>,0x80,7; -->  BYTE_test: 1,<,0x0B,8; msg="Modbus TCP: Incorrect exception code"; sid:987000155; rev:1; priority:1;)

Unity protocol rules

RULE 56

Explanation: REQUEST MODBUS 0x5A --> BYTE 3=0x23 --> System Bit or Word Modification

 alert tcp any any -> any 502 (msg:"Schneider PLC UMAS Write System bit/word request";content:"|00 00|";offset:2;depth:2;content:"|5a|";offset:7;depth:1;content:"|23|";offset:9;depth:1;sid:987000110;)



RULE 57

Explanation: REQUEST MODBUS 0x5A --> BYTE 3=0x30 --> Strategy upload request

 alert tcp any any -> any 502 (msg:"Schneider PLC UMAS Initialize strategy upload request";content:"|00 00|";offset:2;depth:2;content:"|5a|";offset:7;depth:1;content:"|30|";offset:9;depth:1;sid:987000111;)



RULE 58

Explanation: REQUEST MODBUS 0x5A --> BYTE 3=0x33 --> Strategy upload start

 alert tcp any any -> any 502 (msg:"Schneider PLC UMAS Initialize strategy download request";content:"|00 00|";offset:2;depth:2;content:"|5a|";offset:7;depth:1;content:"|33|";offset:9;depth:1;sid:987000112;)



RULE 59

Explanation: REQUEST MODBUS 0x5A --> BYTE 3=0x36 --> BYTE4=0x02 --> Restore from backup request

 alert tcp any any -> any 502 (msg:"Schneider PLC UMAS strategy restore backup request";content:"|00 00|";offset:2;depth:2;content:"|5a|";offset:7;depth:1;content:"|36 02|";offset:9;depth:2;sid:987000113;)



RULE 60

Explanation: REQUEST MODBUS 0x5A --> BYTE 3=0x36 --> BYTE4=0x04 --> Remove backup request

 alert tcp any any -> any 502 (msg:"Schneider PLC UMAS strategy remove backup request";content:"|00 00|";offset:2;depth:2;content:"|5a|";offset:7;depth:1;content:"|36 04|";offset:9;depth:2;sid:987000114;)



RULE 61

Explanation: REQUEST MODBUS 0x5A. --> BYTE 3=0x40 --> PLC Start request

 alert tcp any any -> any 502 (msg:"Schneider PLC(Quantumn) uses function code 90 for communications the Unity pro software/Set PLC CPU STOP";content:"|00 00|";offset:2;depth:2;content:"|5a|";offset:7;depth:1;content:"|40|";offset:9;depth:1;sid:114;)



RULE 62

Explanation: REQUEST MODBUS 0x5A. --> BYTE 3=0x41 --> PLC Stop request

 alert tcp any any -> any 502 (msg:"Schneider PLC(Quantumn) uses function code 90 for communications the Unity pro software/Set PLC CPU Restart";content:"|00 00|";offset:2;depth:2;content:"|5a|";offset:7;depth:1;content:"|41|";offset:9;depth:1;sid:115;)

miércoles, 27 de junio de 2018

Story of a Linux server malware infection

On May 2016 I was asked to check a Linux Server that seemed to be functioning slowly and because of an alert from the communication services of the University that warned that the server could be serving malware and/or SPAM. This a report of what I found.

FORENSIC ANALYSIS REPORT

  • Date: 27th of May 2016
  • System name: SXXXXXXXa
  • Operative System: Ubuntu 14.04 LTS
  • Installation date: 12th of April, 2016
  • Usage of the System: Server used at the XXXXXXXXX department of the University of XXXXX for their daily operation. It's connected to the internal network of the University and at the same time it's directly accessible from internet through university's subdomain. XXXX.XXXXX.es (IP=193.XXX.XXX.X9). The system has, at the moment of the incident a SSH server accesible from internet. It does not offer any web site nor other available services.
  • Event:The department was informed from the communication services of the University that this server has been detected as the origin of malicious traffic and that probably it has a trojan running. To avoid further problem, the traffic was cut off from and to internet for the server.


Timeline of the problem

After accessing physically to the server the following facts were detected:

  1. The command 'ps' did not show any strange process running. The command netstat did not show any strange connection.
  2. In /root directory of the system, I found a file named XXXX.nano modified on April 21st, 2016 approximately at 12:00 AM in which I could see the line “PermitRootLogin yes”. This is a line for the SSH config file that allows SSH server to be accessed with root user. Checking the SSH config file, I can see it has the same date and time than the .nano file and therefore with that date, the system was allowed to be accessed by root. From my point of view this was not actually part of the attack but a very bad practice used by administrator some days after the system installation and days before the sttack started.
  3. That same day, and approximately same hour, it appears an SSH connection with a normal user (I'll use the fake username 'chris' to refer to it), and 5 minutes later an SSH connection with root user.
  4. Four days later we find that on April 25th, 09:03, an SSH connection with user 'chris' is started, but this time the connection comes from IP 183.3.202.187 (located in China).
  5. In command history of root user we find a total of 13 commands (From 71 to 83 in next image), where we can see that a file is downloaded from a server in China (IP 117.18.4.133), and it's give execution permissions and it's eventually executed... Finally on line 83 the root password is changed.

Unfortunately we cannot know the exact date and time of these commands, but we know that they happened before an SSH connection found on history line 86 (not shown) that happened on April 25th at 12:00 AM. So it seems the attack occurred on April 25th 9:03 and the server was owned a few minutes later.



Post Infection

The system was installed on April 12th, from the day after installation (April 13th, 2016) we can see SSH root Access attempts (which is normal as it had its own public IP address). Actually until the real attack, 42893 SSH connection attempts were found and in the whole life of the server more tan 131000 SSH Access attempts were found:

From IP 183.3.202.187 we can see five failed SSH connection attempts on April 25th at 09:03. The fifth attempt was the access with user 'chris' reported previously on point 3. From that moment on hundreds of connections from that IP were done (As seen in previous image). When asked user 'chris', he/she told me that the password was identical to the username. So the attacker simply tried a number of users with the same passwords and finally he could Access the system. Apparently user chris had sudo permissions and the attacked could easily Access the system as root.

After being owned the server was accessed successfully 61 times from 7 different IP addresses. This is a summary of the connections:

  • April 25th, 09:03. With user root from IP 183.3.202.187. This same day another 16 SSH connections were done with user root from IPs: 183.3.202.187, 185.103.252.14, 110.10.129.201 and 89.248.167.131.
  • The next day, April 26th a total of 15 connections are found from IPs 183.3.202.187 and 110.10.129.201, in these connections the history shows that a package named FakeRoot is installed.
  • The next day, April 27th, another 16 root connections are done from IPs 183.3.202.187, 222.186.21.100 and 110.10.129.201.
  • The next day, April 28th, another 12 root connections from IPs: 183.3.202.187 and 100.43.129.140.

From that day no root connections are done. Actually no new connections are performed from strange IPs.



Location of these IPs

  • 183.3.202.187 ==> Guangzhou, Guangdong, China.
  • 185.103.252.14 ==> Moscu, Russia. .
  • 110.10.129.201 ==> Seoul, South Korea.
  • 89.248.167.131 ==> ¿?, The netherlands.
  • 222.186.21.100 ==> Nanjing, Jiangsu Sheng, China.
  • 100.43.129.140 ==> Orange, California, USA.

All but the last one had been previously reported as malicious IPs.



Actions performed by attacker in the system

  1. In the directory /root the following malicious files were found:
    • 2016ttfacai, ELF executable, 1223123 bytes, with date April, 2nd.
    • heifacai, ELF executable, 1223123 bytes. With date April 3rd, but with the same MD5 hash, so it seems a copy of the former file.
    • conf.n, 73 bytes. Apparently a config file for the previous executables, although it's a binary file.

    All three files were sent to virustotal. The report of the two first executablescan be seen in this link.

  2. In /etc/init.d a script file named DbSecuritySpt was found that actually executed /root/heifacai previously mentioned. This script was executed at every system restart therefore ensuring its presence in memory.
  3. In /etc/init.d another two files kzgbqvo and mowepretrf were found with similar contents.
  4. In /bin at least three different commands 'ps', 'ss' and 'netstat' were found with modification dates between 25th and 28th of April. When running 'ps' no strange process where found and when running 'netstat' no strange connections where found, so it seems that the have the been substituted by a modified copy of the executable. When removed these files, they automatically appeared again:

    After downloading the 'ps' and 'netstat' executables from internet, the malicious heifacai process appeared.

  5. Other malicious files were also found in the /bin directory (ovqbbgzk, kzgbbqvo, kzgbbqvo.sh)


Measures taken

Although eventually the system seemed to be cleaned, a recommendation of formatting and reinstalling the system was given to the owner. A speech of cybersecurity concientization was given to the users and in the newly installed system secure password policies were included.

martes, 26 de junio de 2018

Making softflowd plugin to send correct netflow input_snmp values in a pfsense 2.4.2

As stated in this Netgate fórum post (https://forum.netgate.com/topic/66214/softflowd-source-and-destination-interface-id-as-0/3), the softflowd plugin for pfsense sends netflow packets always in-snmp and out_snmp field with value 0.

Therefore one cannot determine if a flow comes from an interface or the other.

As explained in that post, you should modify the source code of softflowd to fix the problem. As we were working in a production environment, we decided to test this solution and found that it's not exactly how it's explained in that post.

In this post we'll explain how did we prepare a test environment, how did we test the solution given in netgate's forum and how did we apply our solution to our production environment.

1.- Preparing the test environment

I needed a test environment of our pfsense firewall with capabilities to modify source code from the softflowd plugin. I first installed the pfsense in a virtual machine and started trying to install development packages (gcc, make and so on). Soon I realized that it was not so simple to install all necessary development packages to a pfsense environment and decided to start again. Removed this VM and created two different VMs. A first one that had a pfsense environment which had the same OS and plugin versions as in the production environment. A second VM was used to install BSD 11.1-RELEASE-p4, the same version used by our pfsense v2.4.2 (see table in https://www.netgate.com/docs/pfsense/releases/versions-of-pfsense-and-freebsd.html).

After that, the necessary packages for development were installed in this freebsd VM (roughly autoconf, automake, gcc, libtool and m4 packages).

Next step comprised to find out which softflowd version was installed in our production firewall. The pfsense plugin manager showed versión 1.1:

However I could not find version 1.1 anywhere, the last version which I could find the source code was 0.99 ( https://code.google.com/archive/p/softflowd/downloads), so I had to settle for this v0.99, so I downloaded it, compiled it in the freebsd VM and copied it (only the executables softflowd and softflowctl) in my testing environment. They worked fine... still sending netflow packets with the 'in_snmp' field set to zero (as expected).

So it was true, It worked and I had to fix the source code. After barely one hour Reading the code I could understand how does it work. Mainly the problem comes with the plugin expecting a network interface with names like eth:1 or enscp:0 and the number sent in the in_snmp value is that "0" or "1" that goes after the semicolon. This is the original source code that makes it:

OK, but, what if the network interface's name does not include a semicolon?. What if the interface's name does not include a number at all?. The solution given in that fórum Works fine. It calculates a number by adding up the ascii values of network interface name. This is how I left the same source code snipplet:

As it can be seen, al the strsep/strtok staff is out and simply the for loop that calculates a 5 digit number is included. With that done, and after compiling (./configure, make and make install) we tested that the netflow messages sent from the testing envionment included both in_snmp and out_snmp (normal as later an out_snmp=in_snmp is done) with a five digit value:

The final step was to copy the executables (only softflowd and softflowctl) to the production pfsense. It worked fine at the first try.