Commit 6b0bc7a9 authored by bol-van's avatar bol-van
Browse files

nfqws: tls mod set sni

parent 93bdfdb6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -476,3 +476,4 @@ v70.6

nfqws: detect Discord Voice IP discovery packets
nfqws: detect STUN message packets
nfqws: change SNI to specified value tls mod : --dpi-desync-fake-tls-mod sni=<sni>
+2 −1
Original line number Diff line number Diff line
@@ -174,7 +174,7 @@ nfqws takes the following parameters:
 --dpi-desync-any-protocol=0|1                  ; 0(default)=desync only http and tls  1=desync any nonempty data packet
 --dpi-desync-fake-http=<filename>|0xHEX        ; file containing fake http request
 --dpi-desync-fake-tls=<filename>|0xHEX         ; file containing fake TLS ClientHello (for https)
 --dpi-desync-fake-tls-mod=mod[,mod]            ; comma separated list of TLS fake mods. available mods : none,rnd,rndsni,dupsid,padencap
 --dpi-desync-fake-tls-mod=mod[,mod]            ; comma separated list of TLS fake mods. available mods : none,rnd,rndsni,sni=<sni>,dupsid,padencap
 --dpi-desync-fake-unknown=<filename>|0xHEX     ; file containing unknown protocol fake payload
 --dpi-desync-fake-syndata=<filename>|0xHEX     ; file containing SYN data payload
 --dpi-desync-fake-quic=<filename>|0xHEX        ; file containing fake QUIC Initial
@@ -285,6 +285,7 @@ It's possible to use TLS Client Hello with any fingerprint and any SNI.
 * `rnd`. Randomize `random` and `session id` fields. Applied on every request.
 * `rndsni`. Randomize SNI. If SNI >=7 symbols random SLD is applied with known TLD. Otherwise filled with random symbols. Applied only once at startup.
 * `dupsid`. Copy `session ID` from original TLS Client Hello. Takes precedence over `rnd`. Applied on every request.
 * `sni=<sni>`. Set specified SNI value. Changes TLS fake length, fixes lengths in TLS structure. Applied once at startup before `rndsni`.
 * `padencap`. Padding extension is extended by original TLS Client Hello size (including multi packet variation with kyber). Padding extension is added to the end if not present, otherwise it must be the last extension. All lengths are increased. Fake size is not changed. Can be useful if DPI does not analyze sequence numbers properly. Applied on every request.

By default if custom fake is not defined `rnd,rndsni,dupsid` mods are applied. If defined - `none`.
+3 −2
Original line number Diff line number Diff line
# zapret v70.5
# zapret v70.6

# ВНИМАНИЕ, остерегайтесь мошенников

@@ -196,7 +196,7 @@ dvtws, собираемый из тех же исходников (см. [док
--dpi-desync-any-protocol=0|1                      ; 0(default)=работать только по http request и tls clienthello  1=по всем непустым пакетам данных
--dpi-desync-fake-http=<filename>|0xHEX	           ; файл, содержащий фейковый http запрос для dpi-desync=fake, на замену стандартному www.iana.org
--dpi-desync-fake-tls=<filename>|0xHEX             ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному
--dpi-desync-fake-tls-mod=mod[,mod]                ; список через запятую режимов runtime модификации фейков : none,rnd,rndsni,dupsid,padencap
--dpi-desync-fake-tls-mod=mod[,mod]                ; список через запятую режимов runtime модификации фейков : none,rnd,rndsni,sni=<sni>,dupsid,padencap
--dpi-desync-fake-unknown=<filename>|0xHEX         ; файл, содержащий фейковый пейлоад неизвестного протокола для dpi-desync=fake, на замену стандартным нулям 256 байт
--dpi-desync-fake-syndata=<filename>|0xHEX         ; файл, содержащий фейковый пейлоад пакета SYN для режима десинхронизации syndata
--dpi-desync-fake-quic=<filename>|0xHEX            ; файл, содержащий фейковый QUIC Initial
@@ -340,6 +340,7 @@ dvtws, собираемый из тех же исходников (см. [док
 * `rnd`. Рандомизировать поля `random` и `session id`. Выполняется на каждый запрос.
 * `dupsid`. Копировать `session ID` из передаваемого TLS Client Hello. Имеет приоритет над `rnd`. Выполняется на каждый запрос.
 * `rndsni`. Рандомизировать SNI. Если SNI >=7 символов, применяется случайный домен 2 уровня с известным TLD, иначе заполняется случайными символами без точки. Выполняется один раз при старте.
 * `sni=<sni>`. Заменить sni на указанное значение. Макс длина SNI - 63 байта. Общая длина TLS фейка и длины в структуре TLS Client Hello меняются. Выполняется один раз при старте. Если сочетается с `rndsni`, выполняется до него.
 * `padencap`. Расширяется padding extension на размер передаваемого TLS Client Hello (включая многопакетный вариант с kyber). Если padding отсутствует, он добавляется в конец. Если присутствует - требуется, чтобы padding шел последним extension. Правятся все длины, чтобы создать видимость включения передаваемого TLS Client Hello в padding extension. Размер фейка не изменяется. Расчет идет на DPI, который не анализирует sequence numbers должным образом. Выполняется на каждый запрос.

По умолчанию если не задан собственный фейк для TLS используются модификации `rnd,rndsni,dupsid`. Если фейк задан, используется `none`.
+4 −1
Original line number Diff line number Diff line
@@ -609,7 +609,7 @@ static uint16_t IP4_IP_ID_FIX(const struct ip *ip)
// fake_mod buffer must at least sizeof(desync_profile->fake_tls)
// size does not change
// return : true - altered, false - not altered
static bool runtime_tls_mod(int fake_n,const struct fake_tls_mod_cache *modcache, uint8_t fake_tls_mod, const uint8_t *fake_data, size_t fake_data_size, const uint8_t *payload, size_t payload_len, uint8_t *fake_mod)
static bool runtime_tls_mod(int fake_n,const struct fake_tls_mod_cache *modcache, uint32_t fake_tls_mod, const uint8_t *fake_data, size_t fake_data_size, const uint8_t *payload, size_t payload_len, uint8_t *fake_mod)
{
	bool b=false;
	if (modcache) // it's filled only if it's TLS
@@ -630,6 +630,7 @@ static bool runtime_tls_mod(int fake_n,const struct fake_tls_mod_cache *modcache
				phton16(fake_mod+modcache->extlen_offset,(uint16_t)sz_ext);
				phton16(fake_mod+modcache->padlen_offset,(uint16_t)sz_pad);
				b=true;
				DLOG("fake[%d] applied padencap tls mod. sizes increased by %zu bytes.\n", fake_n, payload_len);
			}
		}
		if (fake_tls_mod & FAKE_TLS_MOD_RND)
@@ -638,6 +639,7 @@ static bool runtime_tls_mod(int fake_n,const struct fake_tls_mod_cache *modcache
			fill_random_bytes(fake_mod+11,32); // random
			fill_random_bytes(fake_mod+44,fake_mod[43]); // session id
			b=true;
			DLOG("fake[%d] applied rnd tls mod\n", fake_n);
		}
		if (fake_tls_mod & FAKE_TLS_MOD_DUP_SID)
		{
@@ -650,6 +652,7 @@ static bool runtime_tls_mod(int fake_n,const struct fake_tls_mod_cache *modcache
				if (!b)	memcpy(fake_mod,fake_data,fake_data_size);
				memcpy(fake_mod+44,payload+44,fake_mod[43]); // session id
				b=true;
				DLOG("fake[%d] applied dupsid tls mod\n", fake_n);
			}
		}
	}
+111 −59
Original line number Diff line number Diff line
@@ -950,35 +950,57 @@ static bool parse_ip_list(char *opt, ipset *pp)
	return true;
}

static bool parse_tlsmod_list(char *opt, uint8_t *mod)
static bool parse_tlsmod_list(char *opt, uint32_t *mod, char *sni, size_t sni_buf_len)
{
	char *e,*p,c;
	char *e,*e2,*p,c,c2;

	*mod &= FAKE_TLS_MOD_SAVE_MASK;
	*mod |= FAKE_TLS_MOD_SET;
	for (p=opt ; p ; )
	{
		if ((e = strchr(p,',')))
		for (e2=p ; *e2 && *e2!=',' && *e2!='=' ; e2++);

		if ((e = strchr(e2,',')))
		{
			c=*e;
			*e=0;
		}

		if (*e2=='=')
		{
			c2=*e2;
			*e2=0;
		}
		else
			e2=NULL;

		if (!strcmp(p,"rnd"))
			*mod |= FAKE_TLS_MOD_RND;
		else if (!strcmp(p,"rndsni"))
			*mod |= FAKE_TLS_MOD_RND_SNI;
		else if (!strcmp(p,"sni"))
		{
			*mod |= FAKE_TLS_MOD_SNI;
			if (!e2 || !e2[1] || e2[1]==',') goto err;
			strncpy(sni,e2+1,sni_buf_len-1);
			sni[sni_buf_len-1]=0;
		}
		else if (!strcmp(p,"padencap"))
			*mod |= FAKE_TLS_MOD_PADENCAP;
		else if (!strcmp(p,"dupsid"))
			*mod |= FAKE_TLS_MOD_DUP_SID;
		else if (strcmp(p,"none"))
			return false;
			goto err;

		if (e2) *e2=c2;
		if (e) *e++=c;
		p = e;
	}
	return true;
err:
	if (e2) *e2=c2;
	if (e) *e++=c;
	return false;
}


@@ -1012,13 +1034,13 @@ static void SplitDebug(void)

static const char * tld[]={"com","org","net","edu","gov","biz"};

static bool onetime_tls_mod_blob(int profile_n, int fake_n, uint8_t fake_tls_mod, uint8_t *fake_tls, size_t *fake_tls_size, size_t fake_tls_buf_size, struct fake_tls_mod_cache *modcache)
static bool onetime_tls_mod_blob(int profile_n, int fake_n, uint32_t fake_tls_mod, const char *fake_tls_sni, uint8_t *fake_tls, size_t *fake_tls_size, size_t fake_tls_buf_size, struct fake_tls_mod_cache *modcache)
{
	const uint8_t *ext;
	size_t extlen, slen;
	size_t extlen;

	modcache->extlen_offset = modcache->padlen_offset = 0;
	if (fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
	if (fake_tls_mod & (FAKE_TLS_MOD_PADENCAP|FAKE_TLS_MOD_SNI))
	{
		if (!TLSFindExtLen(fake_tls,*fake_tls_size,&modcache->extlen_offset))
		{
@@ -1026,52 +1048,51 @@ static bool onetime_tls_mod_blob(int profile_n, int fake_n, uint8_t fake_tls_mod
			return false;
		}
		DLOG("profile %d fake[%d] tls extensions length offset : %zu\n", profile_n, fake_n, modcache->extlen_offset);
		if (TLSFindExt(fake_tls,*fake_tls_size,21,&ext,&extlen,false))
		{
			if ((ext-fake_tls+extlen)!=*fake_tls_size)
		size_t slen;
		if (!TLSFindExt(fake_tls,*fake_tls_size,0,&ext,&extlen,false))
		{
				DLOG_ERR("profile %d fake[%d] tls padding ext is present but it's not at the end. padding ext offset %zu, padding ext size %zu, fake size %zu\n", profile_n, fake_n, ext-fake_tls, extlen, *fake_tls_size);
			DLOG_ERR("profile %d fake[%d] sni mod is set but tls fake does not have SNI\n", profile_n, fake_n);
			return false;
		}
			modcache->padlen_offset = ext-fake_tls-2;
			DLOG("profile %d fake[%d] tls padding ext is present, padding length offset %zu\n", profile_n, fake_n, modcache->padlen_offset);
		uint8_t *sniext = fake_tls + (ext - fake_tls);
		if (!TLSAdvanceToHostInSNI(&ext,&extlen,&slen))
		{
			DLOG_ERR("profile %d fake[%d] sni set but tls fake has invalid SNI structure\n", profile_n, fake_n);
			return false;
		}
		else
		uint8_t *sni = fake_tls + (ext - fake_tls);

		if (fake_tls_mod & FAKE_TLS_MOD_SNI)
		{
			if ((*fake_tls_size+4)>fake_tls_buf_size)
			size_t slen_new = strlen(fake_tls_sni);
			ssize_t slen_delta = slen_new-slen;
			if (slen_delta)
			{
				DLOG_ERR("profile %d fake[%d] tls padding is absent and there's no space to add it\n", profile_n, fake_n);
				if ((*fake_tls_size+slen_delta)>fake_tls_buf_size)
				{
					DLOG_ERR("profile %d fake[%d] not enough space for new SNI\n", profile_n, fake_n);
					return false;
				}
			phton16(fake_tls+*fake_tls_size,21);
			*fake_tls_size+=2;
			modcache->padlen_offset=*fake_tls_size;
			phton16(fake_tls+*fake_tls_size,0);
			*fake_tls_size+=2;
			phton16(fake_tls+modcache->extlen_offset,pntoh16(fake_tls+modcache->extlen_offset)+4);
			phton16(fake_tls+3,pntoh16(fake_tls+3)+4); // increase tls record len
			phton24(fake_tls+6,pntoh24(fake_tls+6)+4); // increase tls handshake len
			DLOG("profile %d fake[%d] tls padding is absent. added. padding length offset %zu\n", profile_n, fake_n, modcache->padlen_offset);
				memmove(sni+slen_new,sni+slen,fake_tls+*fake_tls_size-(sni+slen));
				phton16(fake_tls+3,(uint16_t)(pntoh16(fake_tls+3)+slen_delta));
				phton24(fake_tls+6,(uint32_t)(pntoh24(fake_tls+6)+slen_delta));
				phton16(fake_tls+modcache->extlen_offset,(uint16_t)(pntoh16(fake_tls+modcache->extlen_offset)+slen_delta));
				phton16(sniext-2,(uint16_t)(pntoh16(sniext-2)+slen_delta));
				phton16(sniext,(uint16_t)(pntoh16(sniext)+slen_delta));
				phton16(sni-2,(uint16_t)(pntoh16(sni-2)+slen_delta));
				*fake_tls_size+=slen_delta;
			}
			DLOG_ERR("profile %d fake[%d] change sni to %s size_delta=%zd\n", profile_n, fake_n, fake_tls_sni,slen_delta);
			memcpy(sni,fake_tls_sni,slen_new);
			slen = slen_new;
		}
		if (fake_tls_mod & FAKE_TLS_MOD_RND_SNI)
		{
		if (!TLSFindExt(fake_tls,*fake_tls_size,0,&ext,&extlen,false))
		{
			DLOG_ERR("profile %d fake[%d] rndsni set but tls fake does not have SNI\n", profile_n, fake_n);
			return false;
		}
		if (!TLSAdvanceToHostInSNI(&ext,&extlen,&slen))
		{
			DLOG_ERR("profile %d fake[%d] rndsni set but tls fake has invalid SNI structure\n", profile_n, fake_n);
			return false;
		}
			if (!slen)
			{
				DLOG_ERR("profile %d fake[%d] rndsni set but tls fake has zero sized SNI\n", profile_n, fake_n);
				return false;
			}
		uint8_t *sni = fake_tls + (ext - fake_tls);

			char *s1=NULL, *s2=NULL;
			if (params.debug)
@@ -1102,6 +1123,37 @@ static bool onetime_tls_mod_blob(int profile_n, int fake_n, uint8_t fake_tls_mod
				free(s1); free(s2);
			}
		}
	}
	if (fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
	{
		if (TLSFindExt(fake_tls,*fake_tls_size,21,&ext,&extlen,false))
		{
			if ((ext-fake_tls+extlen)!=*fake_tls_size)
			{
				DLOG_ERR("profile %d fake[%d] tls padding ext is present but it's not at the end. padding ext offset %zu, padding ext size %zu, fake size %zu\n", profile_n, fake_n, ext-fake_tls, extlen, *fake_tls_size);
				return false;
			}
			modcache->padlen_offset = ext-fake_tls-2;
			DLOG("profile %d fake[%d] tls padding ext is present, padding length offset %zu\n", profile_n, fake_n, modcache->padlen_offset);
		}
		else
		{
			if ((*fake_tls_size+4)>fake_tls_buf_size)
			{
				DLOG_ERR("profile %d fake[%d] tls padding is absent and there's no space to add it\n", profile_n, fake_n);
				return false;
			}
			phton16(fake_tls+*fake_tls_size,21);
			*fake_tls_size+=2;
			modcache->padlen_offset=*fake_tls_size;
			phton16(fake_tls+*fake_tls_size,0);
			*fake_tls_size+=2;
			phton16(fake_tls+modcache->extlen_offset,pntoh16(fake_tls+modcache->extlen_offset)+4);
			phton16(fake_tls+3,pntoh16(fake_tls+3)+4); // increase tls record len
			phton24(fake_tls+6,pntoh24(fake_tls+6)+4); // increase tls handshake len
			DLOG("profile %d fake[%d] tls padding is absent. added. padding length offset %zu\n", profile_n, fake_n, modcache->padlen_offset);
		}
	}
	return true;
}
static bool onetime_tls_mod(struct desync_profile *dp)
@@ -1130,7 +1182,7 @@ static bool onetime_tls_mod(struct desync_profile *dp)
			fake_tls->extra = malloc(sizeof(struct fake_tls_mod_cache));
			if (!fake_tls->extra) return false;
		}
		if (!onetime_tls_mod_blob(dp->n,n,dp->fake_tls_mod,fake_tls->data,&fake_tls->size,fake_tls->size_buf,(struct fake_tls_mod_cache*)fake_tls->extra))
		if (!onetime_tls_mod_blob(dp->n,n,dp->fake_tls_mod,dp->fake_tls_sni,fake_tls->data,&fake_tls->size,fake_tls->size_buf,(struct fake_tls_mod_cache*)fake_tls->extra))
			return false;
	}
	if (!bMod)
@@ -1378,7 +1430,7 @@ static void exithelp(void)
		" --dpi-desync-any-protocol=0|1\t\t\t; 0(default)=desync only http and tls  1=desync any nonempty data packet\n"
		" --dpi-desync-fake-http=<filename>|0xHEX\t; file containing fake http request\n"
		" --dpi-desync-fake-tls=<filename>|0xHEX\t\t; file containing fake TLS ClientHello (for https)\n"
		" --dpi-desync-fake-tls-mod=mod[,mod]\t\t; comma separated list of TLS fake mods. available mods : none,rnd,rndsni,dupsid,padencap\n"
		" --dpi-desync-fake-tls-mod=mod[,mod]\t\t; comma separated list of TLS fake mods. available mods : none,rnd,rndsni,sni=<sni>,dupsid,padencap\n"
		" --dpi-desync-fake-unknown=<filename>|0xHEX\t; file containing unknown protocol fake payload\n"
		" --dpi-desync-fake-syndata=<filename>|0xHEX\t; file containing SYN data payload\n"
		" --dpi-desync-fake-quic=<filename>|0xHEX\t; file containing fake QUIC Initial\n"
@@ -2051,11 +2103,11 @@ int main(int argc, char **argv)
			load_blob_to_collection(optarg, &dp->fake_http, FAKE_MAX_TCP,0);
			break;
		case 39: /* dpi-desync-fake-tls */
			load_blob_to_collection(optarg, &dp->fake_tls, FAKE_MAX_TCP,4);
			load_blob_to_collection(optarg, &dp->fake_tls, FAKE_MAX_TCP,4+sizeof(dp->fake_tls_sni));
			dp->fake_tls_mod |= FAKE_TLS_MOD_CUSTOM_FAKE;
			break;
		case 40: /* dpi-desync-fake-tls-mod */
			if (!parse_tlsmod_list(optarg,&dp->fake_tls_mod))
			if (!parse_tlsmod_list(optarg,&dp->fake_tls_mod,dp->fake_tls_sni,sizeof(dp->fake_tls_sni)))
			{
				DLOG_ERR("Invalid tls mod : %s\n",optarg);
				exit_clean(1);
Loading