int mysql_parser(MolochSession_t *session, void *uw, const unsigned char *data, int len) { Info_t *info = uw; if (session->which != 0) { return 0; } if (len < 37 || data[1] != 0 || data[2] != 0 || data[3] != 1) { moloch_parsers_unregister(session, info); return 0; } unsigned char *ptr = (unsigned char*)data + 36; unsigned char *end = (unsigned char*)data + len; while (ptr < end) { if (*ptr == 0) break; if (!isprint(*ptr)) { moloch_parsers_unregister(session, info); return 0; } ptr++; } moloch_nids_add_protocol(session, "mysql"); moloch_field_string_add(versionField, session, info->version, info->versionLen, FALSE); info->version = 0; char *lower = g_ascii_strdown((char *)data+36, ptr - (data + 36)); moloch_field_string_add(userField, session, lower, ptr - (data + 36), FALSE); moloch_parsers_unregister(session, info); return 0; }
int http_parse(MolochSession_t *session, void *uw, const unsigned char *data, int remaining, int which) { HTTPInfo_t *http = uw; http->which = which; #ifdef HTTPDEBUG LOG("HTTPDEBUG: enter %d - %d %.*s", http->which, remaining, remaining, data); #endif if ((http->wParsers & (1 << http->which)) == 0) { return 0; } while (remaining > 0) { int len = http_parser_execute(&http->parsers[http->which], &parserSettings, (char *)data, remaining); #ifdef HTTPDEBUG LOG("HTTPDEBUG: parse result: %d input: %d errno: %d", len, remaining, http->parsers[http->which].http_errno); #endif if (len <= 0) { http->wParsers &= ~(1 << http->which); if (http->wParsers) { moloch_parsers_unregister(session, uw); } break; } data += len; remaining -= len; } return 0; }
LOCAL int tds_parser(MolochSession_t *session, void *uw, const unsigned char *data, int remaining, int which) { TDSInfo_t *tds = uw; remaining = MIN(remaining, TDS_MAX_SIZE - tds->pos[which]); memcpy(tds->data[which] + tds->pos[which], data, remaining); tds->pos[which] += remaining; // Lots of info from http://www.freetds.org/tds.html if (tds->pos[0] > 598) { #if 0 LOG("host:%.*s user:%.*s pass:%.*s process:%.*s app:%.*s server:%.*s lib:%.*s", tds->data[0][38], tds->data[0] + 8, tds->data[0][69], tds->data[0] + 39, tds->data[0][100], tds->data[0] + 70, tds->data[0][131], tds->data[0] + 101, tds->data[0][178], tds->data[0] + 148, tds->data[0][209], tds->data[0] + 179, tds->data[0][480], tds->data[0] + 470 ); #endif moloch_field_string_add_lower(userField, session, (const char *)tds->data[0] + 39, tds->data[0][69]); moloch_parsers_unregister(session, uw); } return 0; }
LOCAL int mysql_parser(MolochSession_t *session, void *uw, const unsigned char *data, int len, int which) { Info_t *info = uw; if (which != 0) { return 0; } if (info->ssl) { moloch_parsers_classify_tcp(session, data, len, which); moloch_parsers_unregister(session, info); return 0; } if (len < 35 || data[1] != 0 || data[2] != 0 || data[3] > 2) { moloch_parsers_unregister(session, info); return 0; } unsigned char *ptr = (unsigned char*)data + 36; unsigned char *end = (unsigned char*)data + len; while (ptr < end) { if (*ptr == 0) break; if (!isprint(*ptr)) { moloch_parsers_unregister(session, info); return 0; } ptr++; } moloch_session_add_protocol(session, "mysql"); moloch_field_string_add(versionField, session, info->version, info->versionLen, FALSE); info->version = 0; if (ptr > data + 36) { moloch_field_string_add_lower(userField, session, (char*)data+36, ptr - (data + 36)); } if (data[5] & 0x08) { //CLIENT_SSL info->ssl = 1; } else { moloch_parsers_unregister(session, info); } return 0; }
int tls_parser(MolochSession_t *session, void *uw, const unsigned char *data, int remaining, int which) { TLSInfo_t *tls = uw; // If not the server half ignore if (which != tls->which) return 0; // Copy the data we have memcpy(tls->buf + tls->len, data, MIN(remaining, (int)sizeof(tls->buf)-tls->len)); tls->len += MIN(remaining, (int)sizeof(tls->buf)-tls->len); // Make sure we have header if (tls->len < 5) return 0; // Not handshake protocol, stop looking if (tls->buf[0] != 0x16) { tls->len = 0; moloch_parsers_unregister(session, uw); return 0; } // Need the whole record int need = ((tls->buf[3] << 8) | tls->buf[4]) + 5; if (need > tls->len) return 0; if (tls_process_server_handshake_record(session, tls->buf + 5, need - 5)) { tls->len = 0; moloch_parsers_unregister(session, uw); return 0; } tls->len -= need; // Still more data to process if (tls->len) { memmove(tls->buf, tls->buf+need, tls->len); return 0; } return 0; }
int dns_tcp_parser(MolochSession_t *session, void *uw, const unsigned char *data, int len, int which) { DNSInfo_t *info = uw; while (len >= 2) { // First packet of request if (info->len[which] == 0) { int dnslength = ((data[0]&0xff) << 8) | (data[1] & 0xff); if (dnslength < 18) { moloch_parsers_unregister(session, uw); return 0; } if (info->size[which] == 0) { info->size[which] = MAX(1024,dnslength); info->data[which] = malloc(info->size[which]); } else if (info->size[which] < dnslength) { free(info->data[which]); info->data[which] = malloc(dnslength); info->size[which] = dnslength; } // Have all the data in this first packet, just parse it if (dnslength <= len-2) { dns_parser(session, data+2, dnslength); data += 2 + dnslength; len -= 2 + dnslength; } else { memcpy(info->data[which], data+2, len-2); info->len[which] = dnslength; info->pos[which] = len-2; return 0; } } else { int rem = info->len[which] - info->pos[which]; if (rem <= len) { memcpy(info->data[which] + info->pos[which], data, rem); len -= rem; data += rem; dns_parser(session, info->data[which], info->len[which]); info->len[which] = 0; } else { memcpy(info->data[which] + info->pos[which], data, len); info->pos[which] += len; return 0; } } } return 0; }
int socks4_parser(MolochSession_t *session, void *uw, const unsigned char *data, int remaining) { SocksInfo_t *socks = uw; switch(socks->state4) { case SOCKS4_STATE_REPLY: if (session->which == socks->which) return 0; if (remaining >= 8 && data[0] == 0 && data[1] >= 0x5a && data[1] <= 0x5d) { if (socks->ip) moloch_field_int_add(ipField, session, socks->ip); moloch_field_int_add(portField, session, socks->port); moloch_nids_add_tag(session, "protocol:socks"); moloch_nids_add_protocol(session, "socks"); if (socks->user) { if (!moloch_field_string_add(userField, session, socks->user, socks->userlen, FALSE)) { g_free(socks->user); } socks->user = 0; } if (socks->host) { if (!moloch_field_string_add(hostField, session, socks->host, socks->hostlen, FALSE)) { g_free(socks->host); } socks->host = 0; } moloch_parsers_classify_tcp(session, data+8, remaining-8); socks->state4 = SOCKS4_STATE_DATA; return 8; } break; case SOCKS4_STATE_DATA: if (session->which != socks->which) return 0; moloch_parsers_classify_tcp(session, data, remaining); moloch_parsers_unregister(session, uw); break; } return 0; }
int molua_parsers_cb(MolochSession_t *session, void *uw, const unsigned char *data, int remaining, int which) { lua_State *L = Ls[session->thread]; lua_rawgeti(L, LUA_REGISTRYINDEX, (long)uw); molua_pushMolochSession(L, session); lua_pushlstring(L, (char *)data, remaining); lua_pushnumber(L, which); if (lua_pcall(L, 3, 1, 0) != 0) { LOG("error running parser function %s", lua_tostring(L, -1)); exit(0); } int num = lua_tointeger(L, -1); if (num == -1) moloch_parsers_unregister(session, uw); lua_pop(L, 1); return 0; }
// SSH Parsing currently assumes the parts we want from a SSH Packet will be // in a single TCP packet. Kind of sucks. int ssh_parser(MolochSession_t *session, void *uw, const unsigned char *data, int remaining, int which) { SSHInfo_t *ssh = uw; if (memcmp("SSH", data, 3) == 0) { unsigned char *n = memchr(data, 0x0a, remaining); if (n && *(n-1) == 0x0d) n--; if (n) { int len = (n - data); char *str = g_ascii_strdown((char *)data, len); if (!moloch_field_string_add(verField, session, str, len, FALSE)) { g_free(str); } } return 0; } if (which != 1) return 0; BSB bsb; BSB_INIT(bsb, data, remaining); while (BSB_REMAINING(bsb) > 6) { uint32_t loopRemaining = BSB_REMAINING(bsb); // If 0 looking for a ssh packet, otherwise in the middle of ssh packet if (ssh->sshLen == 0) { BSB_IMPORT_u32(bsb, ssh->sshLen); // Can't have a ssh packet > 35000 bytes. if (ssh->sshLen >= 35000) { moloch_parsers_unregister(session, uw); return 0; } ssh->sshLen += 4; uint8_t sshCode = 0; BSB_IMPORT_skip(bsb, 1); // padding length BSB_IMPORT_u08(bsb, sshCode); if (sshCode == 33) { moloch_parsers_unregister(session, uw); uint32_t keyLen = 0; BSB_IMPORT_u32(bsb, keyLen); if (!BSB_IS_ERROR(bsb) && BSB_REMAINING(bsb) >= keyLen) { char *str = g_base64_encode(BSB_WORK_PTR(bsb), keyLen); if (!moloch_field_string_add(keyField, session, str, (keyLen/3+1)*4, FALSE)) { g_free(str); } } break; } } if (loopRemaining > ssh->sshLen) { // Processed all, looking for another packet BSB_IMPORT_skip(bsb, loopRemaining); ssh->sshLen = 0; continue; } else { // Waiting on more data then in this callback ssh->sshLen -= loopRemaining; break; } } return 0; }
int socks5_parser(MolochSession_t *session, void *uw, const unsigned char *data, int remaining, int which) { SocksInfo_t *socks = uw; int consumed; //LOG("%d %d %d", which, socks->which, socks->state5[which]); //moloch_print_hex_string(data, remaining); switch(socks->state5[which]) { case SOCKS5_STATE_VER_REQUEST: if (data[2] == 0) { socks->state5[which] = SOCKS5_STATE_CONN_REQUEST; } else { socks->state5[which] = SOCKS5_STATE_USER_REQUEST; } socks->state5[(which+1)%2] = SOCKS5_STATE_VER_REPLY; break; case SOCKS5_STATE_VER_REPLY: if (remaining != 2 || data[0] != 5 || data[1] > 2) { moloch_parsers_unregister(session, uw); return 0; } moloch_nids_add_protocol(session, "socks"); if (socks->state5[socks->which] == SOCKS5_STATE_CONN_DATA) { // Other side of connection already in data state socks->state5[which] = SOCKS5_STATE_CONN_REPLY; } else if (data[1] == 0) { socks->state5[socks->which] = SOCKS5_STATE_CONN_REQUEST; socks->state5[which] = SOCKS5_STATE_CONN_REPLY; } else if (data[1] == 2) { socks->state5[socks->which] = SOCKS5_STATE_USER_REQUEST; socks->state5[which] = SOCKS5_STATE_USER_REPLY; } else { // We don't handle other auth methods moloch_parsers_unregister(session, uw); } return 2; case SOCKS5_STATE_USER_REQUEST: if ((2 + data[1] > (int)remaining) || (2 + data[1] + 1 + data[data[1]+2] > (int)remaining)) { moloch_parsers_unregister(session, uw); return 0; } moloch_field_string_add(userField, session, (char *)data + 2, data[1], TRUE); moloch_nids_add_tag(session, "socks:password"); socks->state5[which] = SOCKS5_STATE_CONN_REQUEST; return data[1] + 1 + data[data[1]+2]; case SOCKS5_STATE_USER_REPLY: socks->state5[which] = SOCKS5_STATE_CONN_REPLY; return 2; case SOCKS5_STATE_CONN_REQUEST: if (remaining < 6 || data[0] != 5 || data[1] != 1 || data[2] != 0) { moloch_parsers_unregister(session, uw); return 0; } socks->state5[which] = SOCKS5_STATE_CONN_DATA; if (data[3] == 1) { // IPV4 socks->port = (data[8]&0xff) << 8 | (data[9]&0xff); memcpy(&socks->ip, data+4, 4); moloch_field_int_add(ipField, session, socks->ip); moloch_field_int_add(portField, session, socks->port); consumed = 4 + 4 + 2; } else if (data[3] == 3) { // Domain Name socks->port = (data[5+data[4]]&0xff) << 8 | (data[6+data[4]]&0xff); char *lower = g_ascii_strdown((char*)data+5, data[4]); if (!moloch_field_string_add(hostField, session, lower, data[4], FALSE)) { g_free(lower); } moloch_field_int_add(portField, session, socks->port); consumed = 4 + 1 + data[4] + 2; } else if (data[3] == 4) { // IPV6 consumed = 4 + 16 + 2; } else { break; } moloch_parsers_classify_tcp(session, data+consumed, remaining-consumed, which); return consumed; case SOCKS5_STATE_CONN_REPLY: { if (remaining < 6) { moloch_parsers_unregister(session, uw); return 0; } socks->state5[which] = SOCKS5_STATE_CONN_DATA; if (data[3] == 1) { // IPV4 consumed = 4 + 4 + 2; } else if (data[3] == 3) { // Domain Name consumed = 4 + 1 + data[4] + 2; } else if (data[3] == 4) { // IPV6 consumed = 4 + 16 + 2; } else { break; } moloch_parsers_classify_tcp(session, data+consumed, remaining-consumed, which); return consumed; } case SOCKS5_STATE_CONN_DATA: moloch_parsers_classify_tcp(session, data, remaining, which); moloch_parsers_unregister(session, uw); return 0; default: moloch_parsers_unregister(session, uw); } return 0; }
LOCAL int quic_udp_parser(MolochSession_t *session, void *UNUSED(uw), const unsigned char *data, int len, int UNUSED(which)) { int version = -1; int offset = 1; // PUBLIC_FLAG_RESET if (data[0] & 0x02) { return 0; } // CID if (data[0] & 0x08) { offset += 8; } // Get version if (data[0] & 0x01 && data[offset] == 'Q') { version = (data[offset+1] - '0') * 100 + (data[offset+2] - '0') * 10 + (data[offset+3] - '0'); offset += 4; } // Unsupported version if (version < 24) { moloch_parsers_unregister(session, uw); return 0; } // Diversification only is from server to client, so we can ignore // Packet number size if ((data[0] & 0x30) == 0) { offset++; } else { offset += ((data[0] & 0x30) >> 4) * 2; } // Hash offset += 12; // Private Flags if (version < 34) offset++; if (offset > len) return 0; BSB bsb; BSB_INIT(bsb, data+offset, len-offset); while (!BSB_IS_ERROR(bsb) && BSB_REMAINING(bsb)) { uint8_t type = 0; BSB_LIMPORT_u08(bsb, type); //1fdooossB if ((type & 0x80) == 0) { return 0; } int offsetLen = 0; if (type & 0x1C) { offsetLen = ((type & 0x1C) >> 2) + 1; } int streamLen = (type & 0x03) + 1; BSB_LIMPORT_skip(bsb, streamLen + offsetLen); int dataLen = BSB_REMAINING(bsb); if (type & 0x20) { BSB_LIMPORT_u16(bsb, dataLen); } if (BSB_IS_ERROR(bsb)) return 0; BSB dbsb; BSB_INIT(dbsb, BSB_WORK_PTR(bsb), MIN(dataLen, BSB_REMAINING(bsb))); BSB_IMPORT_skip(bsb, dataLen); quic_chlo_parser(session,dbsb); moloch_parsers_unregister(session, uw); return 0; }