Coverage Report

Created: 2026-08-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/util-file-decompression.c
Line
Count
Source
1
/* Copyright (C) 2017 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/** \file
19
 *
20
 * \author Giuseppe Longo <giuseppe@glongo.it>
21
 *
22
 * \brief Decompress files transferred via HTTP corresponding to file_data
23
 * keyword.
24
 *
25
 */
26
27
#include "suricata-common.h"
28
#include "suricata.h"
29
30
#include "detect-engine.h"
31
#include "app-layer-htp.h"
32
33
#include "util-file-decompression.h"
34
#include "util-file-swf-decompression.h"
35
#include "util-misc.h"
36
#include "util-print.h"
37
38
3
#define SWF_ZLIB_MIN_VERSION    0x06
39
0
#define SWF_LZMA_MIN_VERSION    0x0D
40
41
int FileIsSwfFile(const uint8_t *buffer, uint32_t buffer_len)
42
15.4k
{
43
15.4k
    if (buffer_len >= 3 && buffer[1] == 'W' && buffer[2] == 'S') {
44
9
        if (buffer[0] == 'F')
45
0
            return FILE_SWF_NO_COMPRESSION;
46
9
        else if (buffer[0] == 'C')
47
6
            return FILE_SWF_ZLIB_COMPRESSION;
48
3
        else if (buffer[0] == 'Z')
49
0
            return FILE_SWF_LZMA_COMPRESSION;
50
3
        else
51
3
            return FILE_IS_NOT_SWF;
52
9
    }
53
54
15.3k
    return FILE_IS_NOT_SWF;
55
15.4k
}
56
57
/**
58
 * \brief This function decompresses a buffer with zlib/lzma algorithm
59
 *
60
 * \param buffer compressed buffer
61
 * \param buffer_len compressed buffer length
62
 * \param decompressed_buffer buffer that store decompressed data
63
 * \param decompressed_buffer_len decompressed data length
64
 * \param swf_type decompression algorithm to use
65
 * \param decompress_depth how much decompressed data we want to store
66
 * \param compress_depth how much compressed data we want to decompress
67
 *
68
 * \retval 1 if decompression works
69
 * \retval 0 an error occurred, and event set
70
 */
71
int FileSwfDecompression(const uint8_t *buffer, uint32_t buffer_len,
72
                         DetectEngineThreadCtx *det_ctx,
73
                         InspectionBuffer *out_buffer,
74
                         int swf_type,
75
                         uint32_t decompress_depth,
76
                         uint32_t compress_depth)
77
3
{
78
3
    int r = 0;
79
80
3
    int compression_type = FileIsSwfFile(buffer, buffer_len);
81
3
    if (compression_type == FILE_SWF_NO_COMPRESSION) {
82
0
        return 0;
83
0
    }
84
85
3
    uint32_t offset = 0;
86
3
    if (compression_type == FILE_SWF_ZLIB_COMPRESSION) {
87
        /* compressed data start from the 4th bytes */
88
3
        offset = 8;
89
3
    } else if (compression_type == FILE_SWF_LZMA_COMPRESSION) {
90
        /* compressed data start from the 17th bytes */
91
0
        offset = 17;
92
0
    }
93
94
3
    if (buffer_len <= offset) {
95
0
        DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_INVALID_SWF_LENGTH);
96
0
        return 0;
97
0
    }
98
99
3
    uint32_t compressed_data_len = 0;
100
3
    if (compress_depth > 0 && compress_depth <= buffer_len - offset) {
101
0
        compressed_data_len = compress_depth;
102
3
    } else {
103
3
        compressed_data_len = buffer_len - offset;
104
3
    }
105
106
    /* get swf version */
107
3
    uint8_t swf_version = FileGetSwfVersion(buffer, buffer_len);
108
3
    if (compression_type == FILE_SWF_ZLIB_COMPRESSION &&
109
3
        swf_version < SWF_ZLIB_MIN_VERSION)
110
0
    {
111
0
        DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_INVALID_SWF_VERSION);
112
0
        return 0;
113
0
    }
114
3
    if (compression_type == FILE_SWF_LZMA_COMPRESSION &&
115
0
        swf_version < SWF_LZMA_MIN_VERSION)
116
0
    {
117
0
        DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_INVALID_SWF_VERSION);
118
0
        return 0;
119
0
    }
120
121
    /* get flash decompressed file length */
122
3
    uint32_t decompressed_swf_len = FileGetSwfDecompressedLen(buffer, buffer_len);
123
3
    if (decompressed_swf_len == 0) {
124
0
        decompressed_swf_len = MIN_SWF_LEN;
125
0
    }
126
127
    /* if decompress_depth is 0, keep the flash file length */
128
3
    uint32_t decompressed_data_len = decompressed_swf_len;
129
    // only restrict the decompressed_data_len, a too big decompress_depth does not make us more
130
    // than we need
131
3
    if (decompress_depth > 0 && decompress_depth < decompressed_data_len)
132
0
        decompressed_data_len = decompress_depth;
133
134
    // cannot overflow as decompressed_swf_len maxed out at MAX_SWF_DECOMPRESSED_LEN 50000000
135
3
    decompressed_data_len += 8;
136
137
    /* make sure the inspection buffer has enough space */
138
3
    InspectionBufferCheckAndExpand(out_buffer, decompressed_data_len);
139
3
    if (out_buffer->size < decompressed_data_len) {
140
0
        DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_NO_MEM);
141
0
        return 0;
142
0
    }
143
3
    out_buffer->len = decompressed_data_len;
144
145
    /*
146
     * FWS format
147
     * | 4 bytes         | 4 bytes    | n bytes |
148
     * | 'FWS' + version | script len | data    |
149
     */
150
3
    out_buffer->buf[0] = 'F';
151
3
    out_buffer->buf[1] = 'W';
152
3
    out_buffer->buf[2] = 'S';
153
3
    out_buffer->buf[3] = swf_version;
154
3
    memcpy(out_buffer->buf + 4, &decompressed_swf_len, 4);
155
3
    memset(out_buffer->buf + 8, 0, decompressed_data_len - 8);
156
157
3
    if ((swf_type == HTTP_SWF_COMPRESSION_ZLIB || swf_type == HTTP_SWF_COMPRESSION_BOTH) &&
158
3
            compression_type == FILE_SWF_ZLIB_COMPRESSION)
159
3
    {
160
        /* the first 8 bytes represents the fws header, see 'FWS format' above.
161
         * data will start from 8th bytes
162
         */
163
3
        r = FileSwfZlibDecompression(det_ctx,
164
3
                                     (uint8_t *)buffer + offset, compressed_data_len,
165
3
                                     out_buffer->buf + 8, out_buffer->len - 8);
166
3
        if (r == 0)
167
1
            goto error;
168
169
3
    } else if ((swf_type == HTTP_SWF_COMPRESSION_LZMA || swf_type == HTTP_SWF_COMPRESSION_BOTH) &&
170
0
               compression_type == FILE_SWF_LZMA_COMPRESSION)
171
0
    {
172
        /* we need to setup the lzma header */
173
        /*
174
         * | 5 bytes         | 8 bytes             | n bytes         |
175
         * | LZMA properties | Uncompressed length | Compressed data |
176
         */
177
0
        compressed_data_len += 13;
178
0
        uint8_t *compressed_data = SCCalloc(1, compressed_data_len);
179
0
        if (compressed_data == NULL) {
180
0
            goto error;
181
0
        }
182
        /* put lzma properties */
183
0
        memcpy(compressed_data, buffer + 12, 5);
184
        /* put lzma end marker */
185
0
        memset(compressed_data + 5, 0xFF, 8);
186
        /* put compressed data */
187
0
        memcpy(compressed_data + 13, buffer + offset, compressed_data_len - 13);
188
189
        /* the first 8 bytes represents the fws header, see 'FWS format' above.
190
         * data will start from 8th bytes
191
         */
192
0
        r = FileSwfLzmaDecompression(det_ctx,
193
0
                                     compressed_data, compressed_data_len,
194
0
                                     out_buffer->buf + 8, out_buffer->len - 8);
195
0
        SCFree(compressed_data);
196
0
        if (r == 0)
197
0
            goto error;
198
0
    } else {
199
0
        goto error;
200
0
    }
201
202
    /* all went well so switch the buffer's inspect pointer/size
203
     * to use the new data. */
204
2
    out_buffer->inspect = out_buffer->buf;
205
2
    out_buffer->inspect_len = out_buffer->len;
206
207
2
    return 1;
208
209
1
error:
210
1
    return 0;
211
3
}