コード例 #1
0
ファイル: cursor.c プロジェクト: haiwanxue/junkie
uint_least32_t cursor_peek_u24le(struct cursor *cursor, size_t offset)
{
    uint_least32_t a = cursor_peek_u8(cursor, offset);
    uint_least32_t b = cursor_peek_u8(cursor, offset + 1);
    uint_least32_t c = cursor_peek_u8(cursor, offset + 2);
    return a | (b << 8) | (c << 16);
}
コード例 #2
0
ファイル: tns.c プロジェクト: awesome-security/junkie
static bool is_query_valid(struct cursor *cursor, uint64_t potential_size, uint8_t offset)
{
    SLOG(LOG_DEBUG, "  Check query valid of potential size %"PRIu64" and offset %"PRIu8, potential_size, offset);
    // We check if last character is printable
    uint64_t last_char_pos = MIN(cursor->cap_len, potential_size) - 1;
    uint8_t last_char = cursor_peek_u8(cursor, last_char_pos);
    if (!is_print(last_char)) {
        SLOG(LOG_DEBUG, "  Last char 0x%02x at pos %"PRIu64" is not printable", last_char, last_char_pos);
        return false;
    }
    // We check if last character + 1 is not printable. If it is printable, size might be incorrect
    // We assume chunked string if size >= 0x40
    if (potential_size < MAX_OCI_CHUNK && potential_size < cursor->cap_len) {
        if (cursor->cap_len - 1 > potential_size)  {
            char next_char = cursor_peek_u8(cursor, potential_size + 1);
            if (is_print(next_char)) {
                SLOG(LOG_DEBUG, "  Char following last char 0x%02x is printable", next_char);
                return false;
            }
        }
    }
    // We check if first characters are printable
    if (PROTO_OK == is_range_print(cursor, MIN_QUERY_SIZE, offset)) {
        return true;
    }
    return false;
}
コード例 #3
0
ファイル: tns.c プロジェクト: awesome-security/junkie
/*
 * Check if query is valid.
 * @param cursor to read
 * @param candidate Filled with potential sql size candidate
 * @return True if a correct query has been found and cursor is positioned at the begin of the query
 */
static bool lookup_query(struct cursor *cursor, struct query_candidate *candidate)
{
    SLOG(LOG_DEBUG, "Start looking for query");
    uint8_t current;
    uint8_t next;

    while (cursor->cap_len > QUERY_WITH_SIZE) {
        current = cursor_peek_u8(cursor, 0);
        next = cursor_peek_u8(cursor, 1);
        if (current == TTC_ROW_DATA && next > 0 && next <= 4) {
            SLOG(LOG_DEBUG, " Looks like start of a data query row");
            return false;
        }
        if (current > 0 && current < 3 && next > MIN_QUERY_SIZE
                && candidate->num_candidate_size < NB_ELEMS(candidate->candidate_sizes)) {
            uint64_t buf = 0;
            // Copy cursor since we might have pattern like 0x01 Size Query
            struct cursor cursor_copy = *cursor;
            if (PROTO_OK == cursor_read_variable_int(&cursor_copy, &buf)) {
                SLOG(LOG_DEBUG, " Found a candidate size %"PRIu64, buf);
                insert_array_sorted(candidate, buf);
            }
        }
        if (candidate->num_candidate_size == 0 || current >= candidate->candidate_sizes[0]) {
            if (check_chuncked_query(cursor, current, next, candidate)) return true;
            if (check_fixed_query(cursor, current, next, candidate)) return true;
        } else {
            if (check_fixed_query(cursor, current, next, candidate)) return true;
            if (check_chuncked_query(cursor, current, next, candidate)) return true;
        }
        cursor_drop(cursor, 1);
    }
    return false;
}
コード例 #4
0
ファイル: tns.c プロジェクト: haiwanxue/junkie
static enum proto_parse_status tns_parse_sql_query_jdbc(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing a jdbc query");
    enum proto_parse_status status;

    DROP_FIX(cursor, 1);

    uint_least64_t sql_len;
    status = cursor_read_variable_int(cursor, &sql_len);
    if (status != PROTO_OK) return status;
    SLOG(LOG_DEBUG, "Size sql %zu", sql_len);

    DROP_FIX(cursor, 1);
    // We have a number of fields at the end of the query
    uint_least64_t end_len;
    status = cursor_read_variable_int(cursor, &end_len);
    if (status != PROTO_OK) return status;

    DROP_FIX(cursor, 2);
    DROP_VARS(cursor, 3);
    DROP_FIX(cursor, 1);
    DROP_VAR(cursor);
    DROP_FIX(cursor, 6);
    DROP_VAR(cursor);

    char *sql = "";
    if (sql_len > 0) {
        // Some unknown bytes
        while (cursor->cap_len > 1 && !isprint(cursor_peek_u8(cursor, 1))) {
            cursor_drop(cursor, 1);
        }

        CHECK(1);
        if (sql_len > 0xff && 0xff == cursor_peek_u8(cursor, 0)) {
            SLOG(LOG_DEBUG, "Looks like prefixed length chunks of size 0xff...");
            status = cursor_read_chunked_string(cursor, &sql, 0xff);
        } else if (sql_len > 0x40 && 0x40 == cursor_peek_u8(cursor, 1)) {
            SLOG(LOG_DEBUG, "Looks like prefixed length chunks of size 0x40...");
            cursor_drop(cursor, 1);
            status = cursor_read_chunked_string(cursor, &sql, 0x40);
        } else {
            if (!isprint(cursor_peek_u8(cursor, 0))) {
                cursor_drop(cursor, 1);
            }
            SLOG(LOG_DEBUG, "Looks like a fixed string of size %zu", sql_len);
            status = cursor_read_fixed_string(cursor, &sql, sql_len);
        }
        if (status != PROTO_OK) return status;
    }
    SLOG(LOG_DEBUG, "Sql parsed: %s", sql);
    sql_set_query(info, "%s", sql);

    SLOG(LOG_DEBUG, "Skipping %zu end variable fields", end_len);
    DROP_VARS(cursor, end_len);
    return PROTO_OK;
}
コード例 #5
0
ファイル: tns.c プロジェクト: awesome-security/junkie
/*
 * If oci, we will fallback on start query guesstimation
 * | 1 byte                        |
 * | Some flags (generally > 0x04) |
 *
 * If jdbc:
 * | 1 byte      | 0-4 bytes | 1 byte   | 0-4 bytes |
 * | Option size | Options   | Var size | Var value |
 */
static bool is_oci(struct cursor *cursor)
{
    CHECK(1);
    unsigned option_size = cursor_read_u8(cursor);
    CHECK(MAX(option_size, 2));
    if (option_size > 0x04 || cursor_peek_u8(cursor, 1) == 0x00) return true;
    cursor_drop(cursor, option_size);

    // Should be a var here
    CHECK(1);
    unsigned var_size = cursor_read_u8(cursor);
    CHECK(MAX(var_size, 2));
    if (var_size > 0x04 || cursor_peek_u8(cursor, 1) == 0x00) return true;
    cursor_drop(cursor, var_size);

    return false;
}
コード例 #6
0
ファイル: tns.c プロジェクト: haiwanxue/junkie
static enum proto_parse_status is_range_print(struct cursor *cursor, size_t size)
{
    CHECK(size);
    for (size_t i = 0; i < size; i++) {
        if (!isprint(cursor_peek_u8(cursor, i)))
            return PROTO_PARSE_ERR;
    }
    return PROTO_OK;
}
コード例 #7
0
ファイル: tns.c プロジェクト: awesome-security/junkie
static enum proto_parse_status is_range_print(struct cursor *cursor, size_t size, size_t offset)
{
    CHECK(size + offset);
    SLOG(LOG_DEBUG, "Check range print with size %zu, and offset %zu", size, offset);
    for (size_t i = 0; i < size; i++) {
        uint8_t chr = cursor_peek_u8(cursor, i + offset);
        if (!is_print(chr)) {
            SLOG(LOG_DEBUG, "Character 0x%"PRIx8" at %zu is not printable", chr, i);
            return PROTO_PARSE_ERR;
        }
    }
    return PROTO_OK;
}
コード例 #8
0
ファイル: cursor_check.c プロジェクト: gaolitao/junkie
static void cursor_check(void)
{
    struct cursor cursor;

    static uint8_t const data[] = { 1, 2 };
    cursor_ctor(&cursor, data, sizeof(data));
    assert(cursor_peek_u8(&cursor, 0) == 0x01U);
    assert(cursor_peek_u8(&cursor, 1) == 0x02U);
    assert(cursor_read_u8(&cursor) == 0x01U);
    assert(cursor_read_u8(&cursor) == 0x02U);

    static uint16_t const data16[] = { 0x0102, 0x0304 };
    cursor_ctor(&cursor, (uint8_t *)data16, sizeof(data16));
    assert(cursor_peek_u16le(&cursor, 0) == 0x0102U);
    assert(cursor_peek_u16le(&cursor, 2) == 0x0304U);
    assert(cursor_read_u16(&cursor) == 0x0102U);
    assert(cursor_read_u16(&cursor) == 0x0304U);

    static uint32_t const data32[] = { 0x01020304U, 0x05060708U };
    cursor_ctor(&cursor, (uint8_t *)data32, sizeof(data32));
    assert(cursor_peek_u32le(&cursor, 0) == 0x01020304U);
    assert(cursor_peek_u32le(&cursor, 4) == 0x05060708U);
    assert(cursor_read_u32(&cursor) == 0x01020304U);
    assert(cursor_read_u32(&cursor) == 0x05060708U);

    static uint64_t const data64[] = { 0x0102030405060708ULL };
    cursor_ctor(&cursor, (uint8_t *)data64, sizeof(data64));
    assert(cursor_peek_u64le(&cursor, 0) == 0x0102030405060708ULL);
    assert(cursor_read_u64(&cursor) == 0x0102030405060708ULL);

    static uint8_t const datan[] = { 1, 2, 3, 4 };
    cursor_ctor(&cursor, datan, sizeof(datan));
    assert(cursor_peek_u32n(&cursor, 0) == 0x01020304);
    assert(cursor_read_u32n(&cursor) == 0x01020304);

    cursor_ctor(&cursor, datan, sizeof(datan));
    assert(cursor_peek_u16n(&cursor, 0) == 0x0102);
    assert(cursor_read_u16n(&cursor) == 0x0102);
}
コード例 #9
0
ファイル: tns.c プロジェクト: haiwanxue/junkie
static bool lookup_query(struct cursor *cursor)
{
    #define MIN_QUERY_SIZE 10
    #define QUERY_WITH_SIZE 12
    while (cursor->cap_len > QUERY_WITH_SIZE) {
        uint8_t c;
        uint8_t potential_size = 0;
        do {
            c = cursor_peek_u8(cursor, 1);
            potential_size = cursor_read_u8(cursor);
        } while (cursor->cap_len > QUERY_WITH_SIZE && !isprint(c));
        SLOG(LOG_DEBUG, "Found potential size 0x%02x and first printable %c", potential_size, c);
        // Check on found size
        if (potential_size < MIN_QUERY_SIZE || potential_size > cursor->cap_len) continue;
        // We check if last character is printable
        if (!isprint(cursor_peek_u8(cursor, potential_size - 1))) continue;
        // We check if first characters are printable
        if (PROTO_OK == is_range_print(cursor, MIN_QUERY_SIZE)) {
            cursor_rollback(cursor, 1);
            return true;
        }
    }
    return false;
}
コード例 #10
0
ファイル: tds.c プロジェクト: iHaD/junkie
// | 1 byte      | 1 byte | 2 bytes | 4 bytes | 4 bytes | 4 bytes |
// | SMID (0x53) | Flag   | SID     | Length  | Seq num | Window  |
static enum proto_parse_status tds_parse_smp_header(struct cursor *cursor, struct smp_header *out_header)
{
#   define SMP_PKT_HDR_LEN 0x10
#   define SMP_SMID 0x53
    if (cursor_peek_u8(cursor, 0) == SMP_SMID) {
        CHECK_LEN(cursor, SMP_PKT_HDR_LEN, 0);
        cursor_drop(cursor, 1);
        out_header->flags   = cursor_read_u8(cursor);
        out_header->sid     = cursor_read_u16le(cursor);
        out_header->length  = cursor_read_u32le(cursor);
        out_header->seq_num = cursor_read_u32le(cursor);
        out_header->window  = cursor_read_u32le(cursor);
    }
    return PROTO_OK;
}
コード例 #11
0
ファイル: tns.c プロジェクト: awesome-security/junkie
/*
 * | 1 byte | 1 + 0-8 bytes | up to 5 vars |
 * | Flag   | Number column | unknown      |
 */
static enum proto_parse_status tns_parse_row_prefix(struct tns_parser *tns_parser,
        struct sql_proto_info *info, struct cursor *cursor)
{
    enum proto_parse_status status;
    SLOG(LOG_DEBUG, "Parsing Row prefix");

    DROP_FIX(cursor, 1);
    if (PROTO_OK != (status = read_field_count(tns_parser, info, cursor))) return status;
    for (unsigned i = 0; i < 5; i++) {
        CHECK(1);
        char c = cursor_peek_u8(cursor, 0);
        if (c == TTC_ROW_DATA || c == TTC_END_MESSAGE) return PROTO_OK;
        DROP_VAR(cursor);
    }
    return PROTO_OK;
}
コード例 #12
0
ファイル: tns.c プロジェクト: awesome-security/junkie
static enum proto_parse_status tns_parse_row_data(struct tns_parser *tns_parser, struct sql_proto_info *info, struct cursor *cursor)
{
    enum proto_parse_status status;
    SLOG(LOG_DEBUG, "Parsing row data with %u fields", tns_parser->nb_fields);

    /* A row data contains :
     * - 1 var for each fields
     */
    DROP_VAR_STRS(cursor, tns_parser->nb_fields);
    // Our nb fields might be incorrect
    CHECK(1);
    char c = cursor_peek_u8(cursor, 0);
    if (TTC_END_MESSAGE != c && TTC_ROW_RECAP != c) {
        DROP_VAR_STR(cursor);
        tns_parser->nb_fields++;
    }
    sql_set_field_count(info, tns_parser->nb_fields);
    return PROTO_OK;
}
コード例 #13
0
ファイル: cursor.c プロジェクト: haiwanxue/junkie
uint_least16_t cursor_peek_u16le(struct cursor *cursor, size_t offset)
{
    uint_least32_t a = cursor_peek_u8(cursor, offset);
    uint_least32_t b = cursor_peek_u8(cursor, offset + 1);
    return a | (b << 8);
}
コード例 #14
0
ファイル: cursor.c プロジェクト: haiwanxue/junkie
uint_least16_t cursor_peek_u16n(struct cursor *cursor, size_t offset)
{
    uint_least32_t a = cursor_peek_u8(cursor, offset);
    uint_least32_t b = cursor_peek_u8(cursor, offset + 1);
    return (a << 8) | b;
}
コード例 #15
0
ファイル: tns.c プロジェクト: awesome-security/junkie
static enum proto_parse_status tns_parse_sql_query_oci(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing an oci query");
    uint8_t query_size = parse_query_header(cursor);
    struct query_candidate candidate = {.num_candidate_size = 0};
    if (query_size > 0) {
        candidate.candidate_sizes[candidate.num_candidate_size++] = query_size;
    }

    bool has_query = lookup_query(cursor, &candidate);
    info->u.query.sql[0] = '\0';
    info->u.query.truncated = 0;
    if (has_query) {
        SLOG(LOG_DEBUG, "Found a query, parsing it");
        if (candidate.is_chunked) {
            cursor_read_chunked_string(cursor, info->u.query.sql, sizeof(info->u.query.sql), MAX_OCI_CHUNK);
        } else {
            cursor_read_fixed_string(cursor, info->u.query.sql, sizeof(info->u.query.sql), candidate.query_size);
        }
    }
    SLOG(LOG_DEBUG, "Sql parsed: %s", info->u.query.sql);
    info->set_values |= SQL_SQL;
    // Drop the rest
    if(cursor->cap_len > 0) cursor_drop(cursor, cursor->cap_len - 1);
    return PROTO_OK;
}

/*
 * | 1 byte | 1 + 0-8 bytes | 1 byte | 1 + 0-4 bytes  | Lots of unknown bytes | variable  |
 * | Unk    | Sql len       | Unk    | Num end fields | Unk                   | sql query |
 */
static enum proto_parse_status tns_parse_sql_query_jdbc(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing a jdbc query");
    enum proto_parse_status status = PROTO_OK;

    DROP_FIX(cursor, 1);

    uint_least64_t sql_len;
    if (PROTO_OK != (status = cursor_read_variable_int(cursor, &sql_len))) return status;
    SLOG(LOG_DEBUG, "Size sql %zu", sql_len);

    DROP_FIX(cursor, 1);
    DROP_VAR(cursor);
    DROP_FIX(cursor, 2);

    info->u.query.sql[0] = '\0';
    info->u.query.truncated = 0; // TODO Handle truncated
    if (sql_len > 0) {
        // Some unknown bytes
        while (cursor->cap_len > 1 && PROTO_OK != is_range_print(cursor, MIN(MIN_QUERY_SIZE, sql_len), 1)) {
            // TODO drop to the first non printable
            cursor_drop(cursor, 1);
        }
        CHECK(1);
        if (sql_len > 0xff && 0xff == cursor_peek_u8(cursor, 0)) {
            SLOG(LOG_DEBUG, "Looks like prefixed length chunks of size 0xff...");
            status = cursor_read_chunked_string(cursor, info->u.query.sql, sizeof(info->u.query.sql), 0xff);
        } else if (sql_len > MAX_OCI_CHUNK && MAX_OCI_CHUNK == cursor_peek_u8(cursor, 0)) {
            SLOG(LOG_DEBUG, "Looks like prefixed length chunks of size 0x40...");
            status = cursor_read_chunked_string(cursor, info->u.query.sql, sizeof(info->u.query.sql),
                    MAX_OCI_CHUNK);
        } else {
            // We don't care about the first non printable character
            cursor_drop(cursor, 1);
            CHECK(1);
            // In rare occurrence where sql_len == first character, we check the byte after the expected query,
            // if it's printable, the first character is probably the prefixed size.
            if (cursor_peek_u8(cursor, 0) == sql_len && sql_len < cursor->cap_len && is_print(cursor_peek_u8(cursor, sql_len)))
                cursor_drop(cursor, 1);
            SLOG(LOG_DEBUG, "Looks like a fixed string of size %zu", sql_len);
            int written_bytes = cursor_read_fixed_string(cursor, info->u.query.sql,
                    sizeof(info->u.query.sql), sql_len);
            if (written_bytes < 0) return PROTO_TOO_SHORT;
        }
        if (status != PROTO_OK) return status;
    }
    SLOG(LOG_DEBUG, "Sql parsed: %s", info->u.query.sql);
    info->set_values |= SQL_SQL;

    return PROTO_OK;
}
コード例 #16
0
ファイル: tns.c プロジェクト: awesome-security/junkie
static enum proto_parse_status tns_parse_end(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing tns end packet");
    enum proto_parse_status status;

    uint_least64_t var[6];
    for (unsigned i = 0; i < 6; i++) {
        if (PROTO_OK != (status = cursor_read_variable_int(cursor, var + i))) return status;
    }

    uint_least64_t nb_rows;
    uint_least64_t error_code;

    // let's use the double 0x00 to guess the position of row number and error code
    if (var[0] > 0 && var[4] == 0 && var[5] == 0) {
        // var[0] is unknown?
        // var[1] == sequence
        // var[2] == rows
        // var[3] == error code
        SLOG(LOG_DEBUG, "Unknown bits after ttc code");
        nb_rows = var[2];
        error_code = var[3];
        DROP_VAR(cursor);
    } else if (var[3] == 0 && var[4] == 0) {
        // var[0] == sequence
        // var[1] == rows
        // var[2] == error code
        nb_rows = var[1];
        error_code = var[2];
    } else {
        // var[0] == rows
        // var[1] == error code
        nb_rows = var[0];
        error_code = var[1];
    }

    if (info->msg_type == SQL_QUERY) {
        sql_set_row_count(info, nb_rows);
        SLOG(LOG_DEBUG, "Nb rows %d", info->u.query.nb_rows);
    }
    SLOG(LOG_DEBUG, "Error code is %zu", error_code);

    DROP_VARS(cursor, 1);
    DROP_FIX(cursor, 2);
    DROP_VARS(cursor, 2);
    DROP_FIX(cursor, 2);
    DROP_VARS(cursor, 2);
    DROP_FIX(cursor, 1);
    DROP_VARS(cursor, 3);

    if (error_code != 0) {
        SLOG(LOG_DEBUG, "Parsing error message");
        char *error_msg = tempstr();
        unsigned error_len;

        // Drop an unknown number of bytes here
        while(cursor->cap_len > 2 && (cursor_peek_u8(cursor, 0) == 0 || !is_print(cursor_peek_u8(cursor, 1)))){
            DROP_FIX(cursor, 1);
        }
        SLOG(LOG_DEBUG, "First printable char is %c", cursor_peek_u8(cursor, 1));
        status = cursor_read_variable_string(cursor, error_msg, TEMPSTR_SIZE, &error_len);
        if (status != PROTO_OK) return status;
        if (error_len < 12) return PROTO_PARSE_ERR;
        // Split "ORA-XXXX: msg"
        // Part before : is the error code
        // Part after is the localized message
        char *colon_pos = memchr(error_msg, ':', error_len);
        sql_set_request_status(info, SQL_REQUEST_ERROR);
        if (colon_pos) {
            // We extract the error code
            unsigned error_code_size = colon_pos - error_msg;
            int size_err = MIN(error_code_size, sizeof(info->error_code));
            memcpy(info->error_code, error_msg, size_err);
            info->error_code[size_err] = '\0';
            info->set_values |= SQL_ERROR_CODE;
            if (0 == strcmp("ORA-01403", info->error_code))
                info->request_status = SQL_REQUEST_COMPLETE;

            // We skip ':' in errror message
            char const *start_message = colon_pos + 1;
            // We skip spaces before errror message
            while (start_message < error_len + error_msg && *start_message == ' ')
                start_message++;

            copy_string(info->error_message, start_message, sizeof(info->error_message));
            info->set_values |= SQL_ERROR_MESSAGE;
        } else {
            copy_string(info->error_message, error_msg, sizeof(info->error_message));
            info->set_values |= SQL_ERROR_MESSAGE;
        }
    }

    return PROTO_OK;
}