Everything old is new again

Everything old is new again

During the response to the incident in the national segment of the Internet, the CERT.BY team identified the activities of the Cloud Atlas APT group. In this article, we will not consider the full chain of compromise, but will present the vector of initial access to the organization.

In order to gain access to the organization, the classic “Phishing” method was used with an attachment, and a text document was used as a load. Despite the availability of security features and the relevance of updates, the text was delivered to the end user. At the time of writing, the antivirus solutions presented on the virustotal platform do not identify it as malicious:

Everything old is new again
Figure 1. The result of the analysis using antivirus solutions.

When opening a file in an isolated environment, there were request to a non-standard resource: https[:]/processmanagerpro[.]net?transparencia/ homeothermic.

In the file under investigation, the following line was found that was accessed:

Everything old is new again
Figure 2. A line in the file dumpp.

CERT.BY specialists conducted a detailed study of the file and identified a mechanism for abusing the MS Word function in order to gain initial access.

The study is based on the official documentation provided by Microsoft in the latest version:

https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/ 53989ce4-7b05-4f8d-829b-d08d6148375b – CFB format;

https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/ ccd7b486-7881-484c-a137-51170af7cc22 – DOC format.

Let’s omit the description of the methods for extracting the main data streams from CFB files, and proceed to the analysis of the doc document data streams, namely:

“WordDocument” contains the main headers of the file in the “doc” format and the data;

“xTables” contains the service data of a file in the “doc” format.

The malicious resource was found in the “1Table” stream in the service data of the file, which narrowed the search for potential exploitation techniques.

By parsing the internal structures of the file in the “doc” format, namely FibRgFcLcb97, which corresponds to the “doc 97-2003” format, the “SttbfAssoc” structure was found suitable for offsets:

Everything old is new again
Figure 3. Parsing the internal structures of the file.


The “SttbfAssoc” structure is responsible for file associations. According to the official documentation, the size should not be equal to 0. The “SttbfAssoc” structure consists of 18 special lines, each of which has its own purpose (see the official documentation). The file under study contains a line with the index 0x1, which corresponds to the document template.

Everything old is new again
Figure 4. Assignment of strings of the “SttbfAssoc” structure.


Thus, information was obtained about the approach used by the cyber group, as well as about the problems in the operation of modern protective equipment.

As a result, this cyber group uses as an opportunity a situation where a manufacturer supports working with “old” data formats, but this is not taken into account or supported by security mechanisms, which leads to problems of ensuring cybersecurity using standard tools of advertised manufacturers.

This issue was considered in the public space in 2023 however, no solution was proposed, nor was its integration carried out:

IOCs:

md5

006d454ab563ed82f7c5b9c7ce441316

1a761f40cd19ebded77173a8bd959869

sha1

b2f1467838de604023ec9e332a81dfad07658d68

5b7a92c00ba086b9fbdb9f454eb6aad0ebee83e9

sha256

1256f4faad88f8ce27922e8f1236fffb65a47c2873d8fdab4c486c83fbb852a1

6a13803593d72150319b91aafb5e47f0f87e447793e548b31add00d6db96e48a

ssdeep

192:M6ESgUNq5hDQkRKX2VZlyGgEP327AmE/RkwynyrSH9lLZaAHi6/6rrILd/Kf3HO6:5EmlKZYiP328RkRyWzgiSUR/8d3iTkj

192:IpEvFlPCul/VPlLq1IelLZaAQ6/6rrILd/Kf3HO8tfYb8i08pi5h:EA9CuNVPoGiSUR/8df3i0A

net

processmanagerpro[.]net

In this regard, CERT.BY specialists have developed a template detection script for old file formats based on the olefile library.

Application Code:

import olefile
import sys,struct

if __name__ == '__main__':  
    try:
        ole = olefile.OleFileIO(sys.argv[1])
        wordstream=bytearray(ole.openstream('WordDocument').read())
    except Exception as e:
        print('Warning! Can\'t get WordDocument stream, for file "{0}", reason {1}'.format(sys.argv[1],str(e)))
        exit(0)
    #https://msopenspecs.microsoft.com/files/MS-DOC/%5bMS-DOC%5d.pdf FIB info
    if len(wordstream) <= (32+2+28+2+88+2): #base_size + csw_size + csw*2 + cslw_size + cslw*4 + cbRgFcLcb_size
        print("Warning! Not correct size of 'WordDocument' stream")
        exit(0)
    
    #Check table name
    tablestream="0Table"
    flag=struct.unpack("<H",wordstream[10:12])[0]
    if flag &0x200:
        tablestream="1Table"
    try:
        tablestream=bytearray(ole.openstream(tablestream).read())
    except Exception as e:
        print("Warning! Can't get Table stream, reason {}".format(str(e)))
        exit(0)

    if len(tablestream)==0:
        print("Warning! Not correct size of Table Steam stream")
        exit(0)
    ole.close()
    # next version of FibRgFcLcb contains last, we need fcSttbfAssoc 0x100 lcbSttbfAssoc 0x104,
    # 0x9a offset to FibRgFcLcb
    fcSttbfAssoc=struct.unpack("<I",wordstream[0x9a+0x100:0x9a+0x100+0x4])[0]
    lcbSttbfAssoc=struct.unpack("<I",wordstream[0x9a+0x104:0x9a+0x104+0x4])[0]
    if (fcSttbfAssoc <=0 or fcSttbfAssoc>=len(tablestream)) or lcbSttbfAssoc <=0:
        print("Warning! Not valide fcSttbfAssoc")
        exit(0)
    
    if fcSttbfAssoc+lcbSttbfAssoc>len(tablestream):
        print("Warning! Not valide fcSttbfAssoc")
        exit(0)
    
    # https://msopenspecs.microsoft.com/files/MS-DOC/%5bMS-DOC%5d.pdf#%5B%7B%22num%22%3A905%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C69%2C329%2C0%5D
    # skip fExtend, cData, cbExtra, first item, and check size of template path
    cchData=struct.unpack("<H",tablestream[fcSttbfAssoc+0x8:fcSttbfAssoc+0x8+0x2])[0]
    if cchData!=0 and (cchData+fcSttbfAssoc+0x8) < len(tablestream): # it is unicode string, need to multiple size
        Data=tablestream[fcSttbfAssoc+0x8+0x2:fcSttbfAssoc+0x8+0x2+(cchData*2)]
        try:
            print(Data)
            Data=Data.decode('utf-16-le')         
        except:
            pass
        print('Template in file "{0}" found! location "{1}"'.format(sys.argv[1],Data))
    else:
        print('File "{0}" chechked, template not found'.format(sys.argv[1]))

If a threat is detected, please inform us by CERT.BY e-mail: support@cert.by.

For convenience and timely notification of news, subscribe to us on social networks: