TSG-7682: 解析加密SNI异常导致SAPP重启
This commit is contained in:
BIN
demo/correct.txt
Normal file
BIN
demo/correct.txt
Normal file
Binary file not shown.
257
demo/demo.cpp
Normal file
257
demo/demo.cpp
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <langinfo.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "gquic_process.h"
|
||||||
|
#include "quic_analysis.h"
|
||||||
|
#include "parser_quic.h"
|
||||||
|
|
||||||
|
static int check_length(int last_len, int field_len)
|
||||||
|
{
|
||||||
|
return ((last_len-field_len>0) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int get_quic_tlv(char *start_pos, quic_tlv_t *tlv, int len, int type, int thread_seq)
|
||||||
|
{
|
||||||
|
if(tlv->value==NULL && len>0)
|
||||||
|
{
|
||||||
|
tlv->value=(char *)calloc(1, len+1);
|
||||||
|
memset(tlv->value, 0, len+1);
|
||||||
|
tlv->length=len;
|
||||||
|
tlv->type=type;
|
||||||
|
memcpy(tlv->value, start_pos, tlv->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_value(unsigned char *payload, int *offset, int len)
|
||||||
|
{
|
||||||
|
switch(len)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return (int)(payload[(*offset)++]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
(*offset)+=len;
|
||||||
|
return (int)ntohs(*(unsigned short *)(payload+*offset-len));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
(*offset)+=len;
|
||||||
|
return ((int)*(payload-2+*offset)<<16|
|
||||||
|
(int)*(payload-1+*offset)<<8|
|
||||||
|
(int)*(payload+*offset)<<0);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
(*offset)+=len;
|
||||||
|
return (int)ntohl(*(unsigned int *)(payload+*offset-len));
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
(*offset)+=len;
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_encrypt_parameter(struct _quic_stream *quic_stream, unsigned char *payload, int payload_len, int thread_seq)
|
||||||
|
{
|
||||||
|
int used_len=0,length=0;
|
||||||
|
while(payload_len>used_len)
|
||||||
|
{
|
||||||
|
if(payload[used_len]> 0x00 && payload[used_len]<=0x20)
|
||||||
|
{
|
||||||
|
get_value(payload, &used_len, 1); //type=1
|
||||||
|
length=get_value(payload, &used_len, 1); // length=1
|
||||||
|
used_len+=length;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((*(unsigned short *)(payload+used_len)) == htons(EXT_QUIC_PARAM_USER_AGENT))
|
||||||
|
{
|
||||||
|
quic_stream->ua_idx=quic_stream->ext_tag_num++;
|
||||||
|
get_value(payload, &used_len, 2); //type=2
|
||||||
|
length=get_value(payload, &used_len, 1); // length=1
|
||||||
|
get_quic_tlv((char *)payload+used_len, &(quic_stream->ext_tags[quic_stream->ua_idx]), length, EXT_QUIC_PARAM_USER_AGENT, thread_seq);
|
||||||
|
used_len+=length;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(*(unsigned int *)(payload+used_len) == htonl(EXT_QUIC_PARAM_QUIC_VERSION))
|
||||||
|
{
|
||||||
|
quic_stream->ver_idx=quic_stream->ext_tag_num++;
|
||||||
|
get_value(payload, &used_len, 4); //type=4
|
||||||
|
length=get_value(payload, &used_len, 1); // length=1
|
||||||
|
get_quic_tlv((char *)payload+used_len, &(quic_stream->ext_tags[quic_stream->ver_idx]), length, EXT_QUIC_PARAM_QUIC_VERSION, thread_seq);
|
||||||
|
*(unsigned int *)quic_stream->ext_tags[quic_stream->ver_idx].value=(unsigned int)htonl(*(unsigned int *)quic_stream->ext_tags[quic_stream->ver_idx].value);
|
||||||
|
used_len+=length;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((*(unsigned int *)(payload+used_len))== htonl(EXT_QUIC_PARAM_GREASE_HIGH4) && (*(unsigned int *)(payload+used_len+4))== htonl(EXT_QUIC_PARAM_GREASE_LOW4))
|
||||||
|
{
|
||||||
|
used_len+=8; //type=8
|
||||||
|
length=get_value(payload, &used_len, 1); // length=1
|
||||||
|
used_len+=length;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_encrypt_server_name(struct _quic_stream *quic_stream, unsigned char *payload, int payload_len, int thread_seq)
|
||||||
|
{
|
||||||
|
int ext_len=0,used_len=0;
|
||||||
|
|
||||||
|
quic_stream->sni_idx=quic_stream->ext_tag_num++;
|
||||||
|
get_value(payload, &used_len, 2); //Server Name List length
|
||||||
|
if(get_value(payload, &used_len, 1)==0) //Server Name type
|
||||||
|
{
|
||||||
|
ext_len=get_value(payload, &used_len, 2); //Server Name length
|
||||||
|
get_quic_tlv((char *)payload+used_len, &(quic_stream->ext_tags[quic_stream->sni_idx]), ext_len, EXTENSION_SERVER_NAME, thread_seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_encrypt_client_hello(void *pstream, struct _quic_stream *quic_stream, void *a_packet, unsigned char *payload, int payload_len)
|
||||||
|
{
|
||||||
|
int used_len=0;
|
||||||
|
int flags=0;
|
||||||
|
int skip_len=0,client_hello_len=0;
|
||||||
|
int ext_type=0, extension_total_len=0;
|
||||||
|
|
||||||
|
get_value(payload, &used_len, 1); //handshake type
|
||||||
|
client_hello_len=get_value(payload, &used_len, 3); //client hello length
|
||||||
|
get_value(payload, &used_len, 2); //ssl_version
|
||||||
|
|
||||||
|
get_value(payload, &used_len, 32); //Random
|
||||||
|
|
||||||
|
skip_len=(int)get_value(payload, &used_len, 1); //Session ID length
|
||||||
|
if(!check_length(payload_len-used_len, skip_len))
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
used_len+=skip_len;
|
||||||
|
|
||||||
|
skip_len=(int)get_value(payload, &used_len, 2); //Ciper Suites length
|
||||||
|
if(!check_length(payload_len-used_len, skip_len))
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
used_len+=skip_len;
|
||||||
|
|
||||||
|
skip_len=(int)get_value(payload, &used_len, 1); //Compression Methods
|
||||||
|
if(!check_length(payload_len-used_len, skip_len))
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
used_len+=skip_len;
|
||||||
|
|
||||||
|
extension_total_len=(int)get_value(payload, &used_len, 2); //Extension length
|
||||||
|
if(!check_length(payload_len-used_len, extension_total_len))
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
quic_stream->ext_tags=(quic_tlv_t *)calloc(1, sizeof(quic_tlv_t)*3);
|
||||||
|
memset(quic_stream->ext_tags, 0, sizeof(quic_tlv_t)*3);
|
||||||
|
|
||||||
|
while(extension_total_len>used_len)
|
||||||
|
{
|
||||||
|
ext_type=get_value(payload, &used_len, 2); //Extension type
|
||||||
|
skip_len=get_value(payload, &used_len, 2); //length
|
||||||
|
if(!check_length(payload_len-used_len, skip_len) || skip_len==0)
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(ext_type)
|
||||||
|
{
|
||||||
|
case EXTENSION_SERVER_NAME:
|
||||||
|
parse_encrypt_server_name(quic_stream, payload+used_len, skip_len, 0);
|
||||||
|
flags=1;
|
||||||
|
break;
|
||||||
|
case EXTENSION_QUIC_PARAM:
|
||||||
|
parse_encrypt_parameter(quic_stream, payload+used_len, skip_len, 0);
|
||||||
|
break;
|
||||||
|
case EXTENSION_SUPPORT_GROUP:
|
||||||
|
case EXTENSION_APP_PROT_NEGO:
|
||||||
|
case EXTENSION_SIG_ALGORITHM:
|
||||||
|
case EXTENSION_KEY_SHARE:
|
||||||
|
case EXTENSION_PSK_EXCHANGE:
|
||||||
|
case EXTENSION_SUPP_SSL_VER:
|
||||||
|
case EXTENSION_COMPRESS_CERT:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
used_len+=skip_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if(argc<1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
if (stat(argv[1], &statbuf) == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *payload=(unsigned char *)calloc(1, statbuf.st_size);
|
||||||
|
FILE *fp=fopen(argv[1], "rb");
|
||||||
|
if(fp)
|
||||||
|
{
|
||||||
|
int size=fread(payload, 1, 1315, fp);
|
||||||
|
assert(size==1315);
|
||||||
|
//assert(size==statbuf.st_size);
|
||||||
|
|
||||||
|
struct _quic_stream * quic_stream=(struct _quic_stream *)calloc(1, sizeof(struct _quic_stream));
|
||||||
|
parse_encrypt_client_hello(NULL, quic_stream, NULL, payload+4, 1314)-4;
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
fp=NULL;
|
||||||
|
|
||||||
|
free(quic_stream);
|
||||||
|
quic_stream=NULL;
|
||||||
|
|
||||||
|
free(payload);
|
||||||
|
payload=NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
demo/error.txt
Normal file
BIN
demo/error.txt
Normal file
Binary file not shown.
@@ -15,6 +15,7 @@
|
|||||||
#include "quic_analysis.h"
|
#include "quic_analysis.h"
|
||||||
#include "parser_quic.h"
|
#include "parser_quic.h"
|
||||||
|
|
||||||
|
|
||||||
int is_iquic(enum _QUIC_VERSION quic_version)
|
int is_iquic(enum _QUIC_VERSION quic_version)
|
||||||
{
|
{
|
||||||
switch(quic_version)
|
switch(quic_version)
|
||||||
@@ -707,7 +708,7 @@ int gquic_frame_type_stream(struct streaminfo *pstream, struct _quic_context* _c
|
|||||||
{
|
{
|
||||||
case GQUIC_VERSION_Q041:
|
case GQUIC_VERSION_Q041:
|
||||||
*used_len+=1; // unknown
|
*used_len+=1; // unknown
|
||||||
case GQUIC_VERSION_Q044:
|
//case GQUIC_VERSION_Q044:
|
||||||
message_tag=(unsigned int)ntohl(*(unsigned int *)(payload+*used_len));
|
message_tag=(unsigned int)ntohl(*(unsigned int *)(payload+*used_len));
|
||||||
*used_len+=4;
|
*used_len+=4;
|
||||||
|
|
||||||
@@ -1056,15 +1057,31 @@ int parse_encrypt_client_hello(struct streaminfo *pstream, struct _quic_stream *
|
|||||||
get_value(payload, &used_len, 32); //Random
|
get_value(payload, &used_len, 32); //Random
|
||||||
|
|
||||||
skip_len=(int)get_value(payload, &used_len, 1); //Session ID length
|
skip_len=(int)get_value(payload, &used_len, 1); //Session ID length
|
||||||
|
if(!check_length(payload_len-used_len, skip_len))
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
used_len+=skip_len;
|
used_len+=skip_len;
|
||||||
|
|
||||||
skip_len=(int)get_value(payload, &used_len, 2); //Ciper Suites length
|
skip_len=(int)get_value(payload, &used_len, 2); //Ciper Suites length
|
||||||
|
if(!check_length(payload_len-used_len, skip_len))
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
used_len+=skip_len;
|
used_len+=skip_len;
|
||||||
|
|
||||||
skip_len=(int)get_value(payload, &used_len, 1); //Compression Methods
|
skip_len=(int)get_value(payload, &used_len, 1); //Compression Methods
|
||||||
|
if(!check_length(payload_len-used_len, skip_len))
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
used_len+=skip_len;
|
used_len+=skip_len;
|
||||||
|
|
||||||
extension_total_len=(int)get_value(payload, &used_len, 2); //Extension length
|
extension_total_len=(int)get_value(payload, &used_len, 2); //Extension length
|
||||||
|
if(!check_length(payload_len-used_len, extension_total_len))
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
quic_stream->ext_tags=(quic_tlv_t *)dictator_malloc(pstream->threadnum, sizeof(quic_tlv_t)*3);
|
quic_stream->ext_tags=(quic_tlv_t *)dictator_malloc(pstream->threadnum, sizeof(quic_tlv_t)*3);
|
||||||
memset(quic_stream->ext_tags, 0, sizeof(quic_tlv_t)*3);
|
memset(quic_stream->ext_tags, 0, sizeof(quic_tlv_t)*3);
|
||||||
@@ -1073,6 +1090,11 @@ int parse_encrypt_client_hello(struct streaminfo *pstream, struct _quic_stream *
|
|||||||
{
|
{
|
||||||
ext_type=get_value(payload, &used_len, 2); //Extension type
|
ext_type=get_value(payload, &used_len, 2); //Extension type
|
||||||
skip_len=get_value(payload, &used_len, 2); //length
|
skip_len=get_value(payload, &used_len, 2); //length
|
||||||
|
if(!check_length(payload_len-used_len, skip_len))
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
switch(ext_type)
|
switch(ext_type)
|
||||||
{
|
{
|
||||||
case EXTENSION_SERVER_NAME:
|
case EXTENSION_SERVER_NAME:
|
||||||
@@ -1120,7 +1142,7 @@ int parse_decrypt_quic(struct streaminfo *pstream, struct _quic_context* _contex
|
|||||||
_context->quic_info.client_hello=(struct _quic_stream *)dictator_malloc(pstream->threadnum, sizeof(struct _quic_stream));
|
_context->quic_info.client_hello=(struct _quic_stream *)dictator_malloc(pstream->threadnum, sizeof(struct _quic_stream));
|
||||||
memset(_context->quic_info.client_hello, 0, sizeof(struct _quic_stream));
|
memset(_context->quic_info.client_hello, 0, sizeof(struct _quic_stream));
|
||||||
}
|
}
|
||||||
ret=parse_encrypt_client_hello(pstream, _context->quic_info.client_hello, a_packet, payload+*used_len, payload_len); //Frame Type=1, offset=1, length=2
|
ret=parse_encrypt_client_hello(pstream, _context->quic_info.client_hello, a_packet, payload+*used_len, payload_len-*used_len); //Frame Type=1, offset=1, length=2
|
||||||
if(ret>0 && _context->call_business)
|
if(ret>0 && _context->call_business)
|
||||||
{
|
{
|
||||||
state=quic_callPlugins(pstream, _context, (void *)(_context->quic_info.client_hello), sizeof(void *), QUIC_CLIENT_HELLO_MASK, a_packet);
|
state=quic_callPlugins(pstream, _context, (void *)(_context->quic_info.client_hello), sizeof(void *), QUIC_CLIENT_HELLO_MASK, a_packet);
|
||||||
@@ -1183,7 +1205,7 @@ int quic_process(struct streaminfo *pstream, struct _quic_context* _context, int
|
|||||||
ret=parse_gquic_Q046(pstream, _context, a_packet, (char *)udp_detail->pdata, udp_detail->datalen, &used_len);
|
ret=parse_gquic_Q046(pstream, _context, a_packet, (char *)udp_detail->pdata, udp_detail->datalen, &used_len);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if(is_gquic>=GQUIC_VERSION_Q001 && is_gquic<=GQUIC_VERSION_Q043)
|
if(is_gquic>=GQUIC_VERSION_Q001 && is_gquic<=GQUIC_VERSION_Q046)
|
||||||
{
|
{
|
||||||
ret=gquic_proc_unencrypt(pstream, _context, a_packet, (char *)udp_detail->pdata, udp_detail->datalen, &used_len);
|
ret=gquic_proc_unencrypt(pstream, _context, a_packet, (char *)udp_detail->pdata, udp_detail->datalen, &used_len);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user