[Ttssh2-commit] [7020] SSH ペイロードの長さをきちんとチェックするようにした。

scmno****@osdn***** scmno****@osdn*****
2018年 1月 11日 (木) 22:20:04 JST


Revision: 7020
          http://sourceforge.jp/projects/ttssh2/scm/svn/commits/7020
Author:   doda
Date:     2018-01-11 22:20:04 +0900 (Thu, 11 Jan 2018)
Log Message:
-----------
SSH ペイロードの長さをきちんとチェックするようにした。

これも方針決定の試験実装の為、とりあえず handle_SSH2_kexinit() のみ。

SSH1 専用や SSH1/2 共用のパケットハンドラ関数では大体問題は無いのだが、
SSH2 専用のパケットハンドラ関数ではペイロードの残りの長さを確認せずに
アクセスする事を多用している。この為、ちょっとした問題によってすぐに
確保したメモリ領域を超えてアクセスして Application Fault によって落ちる
という事が起きてしまう。

ちょっとした問題の例:
・何らかの問題でパケットの復号に失敗した
・同じ番号に割り当てられた他のメッセージを処理しようとした
・サーバからのパケットが壊れていた (バグや悪意のあるサーバの攻撃等)

してはいけないメモリアクセスによって Application Fault で落ちるのは
セキュリティ的にも望ましくないので、必要な量のデータが残っていなかった
場合は警告メッセージを表示して切断する。

基本的には、SSH ペイロードからデータを取り出す場合は、grab_payload()
によって必要な長さのペイロードが残っている事を確認してからアクセスする。

Modified Paths:
--------------
    trunk/ttssh2/ttxssh/ssh.c

-------------- next part --------------
Modified: trunk/ttssh2/ttxssh/ssh.c
===================================================================
--- trunk/ttssh2/ttxssh/ssh.c	2018-01-11 13:20:00 UTC (rev 7019)
+++ trunk/ttssh2/ttxssh/ssh.c	2018-01-11 13:20:04 UTC (rev 7020)
@@ -4833,7 +4833,6 @@
 	char buf[1024];
 	char *data;
 	int len, size;
-	int offset = 0;
 	char *msg = NULL;
 	char tmp[1024+512];
 	char str_keytype[20];
@@ -4853,7 +4852,7 @@
 	data = remained_payload(pvar);
 	len = remained_payloadlen(pvar);
 
-	// KEX \x82̍Ō\xE3\x82\xC5 hash (session-id) \x82\xF0\x8Cv\x8EZ\x82\xB7\x82\xE9\x82̂Ɏg\x82\xA4\x82̂ŕۑ\xB6\x82\xB5\x82Ă\xA8\x82\xAD
+	// KEX \x82̍Ō\xE3\x82\xC5 exchange-hash (session-id) \x82\xF0\x8Cv\x8EZ\x82\xB7\x82\xE9\x82̂Ɏg\x82\xA4\x82̂ŕۑ\xB6\x82\xB5\x82Ă\xA8\x82\xAD
 	if (pvar->peer_kex != NULL) {
 		// already allocated
 		buffer_clear(pvar->peer_kex);
@@ -4867,33 +4866,35 @@
 	}
 	buffer_append(pvar->peer_kex, data, len);
 
-	// TODO: buffer overrun check
-
 	push_memdump("KEXINIT", "exchange algorithm list: receiving", data, len);
 
-	if (offset + 20 >= len) {
-		msg = "payload size too small @ handle_SSH2_kexinit()";
+	// cookie; \x82\xB1\x82\xB1\x82ł͎g\x82\xED\x82Ȃ\xA2\x82̂œǂݔ\xF2\x82΂\xB7
+	if (! grab_payload(pvar, SSH2_COOKIE_LENGTH)) {
+		msg = __FUNCTION__ ": truncated packet (cookie)";
 		goto error;
 	}
 
 	// get rid of Cookie length
-	offset += SSH2_COOKIE_LENGTH;
+	data += SSH2_COOKIE_LENGTH;
 
+	// \x8Ae\x97v\x91f(\x8C\xAE\x8C\xF0\x8A\xB7,\x88Í\x86\x89\xBB\x93\x99)\x82Ŏg\x97p\x82\xB7\x82\xE9\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80\x82̌\x88\x92\xE8\x81B
+	// \x83T\x81[\x83o\x82\xA9\x82\xE7\x82̓J\x83\x93\x83}\x8B\xE6\x90؂\xE8\x82ł̃\x8A\x83X\x83g\x82\xAA\x91\x97\x82\xE7\x82\xEA\x82ė\x88\x82\xE9\x81B
+	// \x83N\x83\x89\x83C\x83A\x83\x93\x83g\x82ƃT\x81[\x83o\x97\xBC\x95\xFB\x82\xAA\x83T\x83|\x81[\x83g\x82\xB5\x82Ă\xA2\x82镨\x82̂\xA4\x82\xBF\x81A
+	// \x83N\x83\x89\x83C\x83A\x83\x93\x83g\x91\xA4\x82ōł\xE0\x91O\x82Ɏw\x92肵\x82\xBD\x95\xA8\x82\xAA\x8Eg\x82\xED\x82\xEA\x82\xE9\x81B
 
-	// KEX\x82̌\x88\x92\xE8\x81B\x94\xBB\x92菇\x82\xF0myproposal[PROPOSAL_KEX_ALGS]\x82̕\xC0\x82тƍ\x87\x82킹\x82邱\x82ƁB
-	// \x83T\x81[\x83o\x82́A\x83N\x83\x89\x83C\x83A\x83\x93\x83g\x82\xA9\x82瑗\x82\xE7\x82\xEA\x82Ă\xAB\x82\xBD myproposal[PROPOSAL_KEX_ALGS] \x82̃J\x83\x93\x83}\x95\xB6\x8E\x9A\x97\xF1\x82̂\xA4\x82\xBF\x81A
-	// \x90擪\x82\xA9\x82玩\x95\xAA\x82\xCC myproposal[] \x82Ɣ\xE4\x8Ar\x82\xF0\x8Ds\x82\xA2\x81A\x8Dŏ\x89\x82Ƀ}\x83b\x83`\x82\xB5\x82\xBD\x82\xE0\x82̂\xAAKEX\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80\x82Ƃ\xB5\x82\xC4
-	// \x91I\x91\xF0\x82\xB3\x82\xEA\x82\xE9\x81B(2004.10.30 yutaka)
+	// \x8C\xAE\x8C\xF0\x8A\xB7\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80
+	if (!grab_payload(pvar, 4)
+	 || !grab_payload(pvar, size = get_uint32(data))) {
+		msg = __FUNCTION__ ": truncated packet (kex algorithms)";
+		goto error;
+	}
+	data += 4;
 
-	// \x83L\x81[\x8C\xF0\x8A\xB7\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80\x83`\x83F\x83b\x83N
-	size = get_payload_uint32(pvar, offset);
-	offset += 4;
-
 	if (size >= sizeof(buf)) {
 		logputs(LOG_LEVEL_WARNING, __FUNCTION__ ": server proposed kex algorithms is too long.");
 	}
-	strncpy_s(buf, sizeof(buf), data+offset, _TRUNCATE);
-	offset += size;
+	strncpy_s(buf, sizeof(buf), data, _TRUNCATE);
+	data += size;
 
 	logprintf(LOG_LEVEL_VERBOSE, "server proposal: KEX algorithm: %s", buf);
 
@@ -4905,16 +4906,19 @@
 		goto error;
 	}
 
+	// \x83z\x83X\x83g\x8C\xAE\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80
+	if (!grab_payload(pvar, 4)
+	 || !grab_payload(pvar, size = get_uint32(data))) {
+		msg = __FUNCTION__ ": truncated packet (hostkey algorithms)";
+		goto error;
+	}
+	data += 4;
 
-	// \x83z\x83X\x83g\x83L\x81[\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80\x83`\x83F\x83b\x83N
-	size = get_payload_uint32(pvar, offset);
-	offset += 4;
-
 	if (size >= sizeof(buf)) {
 		logputs(LOG_LEVEL_WARNING, __FUNCTION__ ": server proposed hostkey algorithms is too long.");
 	}
-	strncpy_s(buf, sizeof(buf), data+offset, _TRUNCATE);
-	offset += size;
+	strncpy_s(buf, sizeof(buf), data, _TRUNCATE);
+	data += size;
 
 	logprintf(LOG_LEVEL_VERBOSE, "server proposal: server host key algorithm: %s", buf);
 
@@ -4934,63 +4938,72 @@
 		goto error;
 	}
 
+	// \x88Í\x86\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80(\x83N\x83\x89\x83C\x83A\x83\x93\x83g -> \x83T\x81[\x83o)
+	if (!grab_payload(pvar, 4)
+	 || !grab_payload(pvar, size = get_uint32(data))) {
+		msg = __FUNCTION__ ": truncated packet (encryption algorithms client to server)";
+		goto error;
+	}
+	data += 4;
 
-	// \x83N\x83\x89\x83C\x83A\x83\x93\x83g -> \x83T\x81[\x83o\x88Í\x86\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80\x83`\x83F\x83b\x83N
-	size = get_payload_uint32(pvar, offset);
-	offset += 4;
-
 	if (size >= sizeof(buf)) {
 		logputs(LOG_LEVEL_WARNING, __FUNCTION__ ": server proposed encryption algorithms (client to server) is too long.");
 	}
-	strncpy_s(buf, sizeof(buf), data+offset, _TRUNCATE);
-	offset += size;
+	strncpy_s(buf, sizeof(buf), data, _TRUNCATE);
+	data += size;
 
 	logprintf(LOG_LEVEL_VERBOSE, "server proposal: encryption algorithm client to server: %s", buf);
 
 	pvar->ciphers[MODE_OUT] = choose_SSH2_cipher_algorithm(buf, myproposal[PROPOSAL_ENC_ALGS_CTOS]);
-	if (pvar->ciphers[MODE_OUT] == NULL) {
-		strncpy_s(tmp, sizeof(tmp), "unknown Encrypt algorithm(ctos): ", _TRUNCATE);
+	if (pvar->ciphers[MODE_OUT]->id == SSH_CIPHER_NONE) {
+		strncpy_s(tmp, sizeof(tmp), "unknown Encrypt algorithm(client to server): ", _TRUNCATE);
 		strncat_s(tmp, sizeof(tmp), buf, _TRUNCATE);
 		msg = tmp;
 		goto error;
 	}
 
+	// \x88Í\x86\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80(\x83T\x81[\x83o -> \x83N\x83\x89\x83C\x83A\x83\x93\x83g)
+	if (!grab_payload(pvar, 4)
+	 || !grab_payload(pvar, size = get_uint32(data))) {
+		msg = __FUNCTION__ ": truncated packet (encryption algorithms server to client)";
+		goto error;
+	}
+	data += 4;
 
-	// \x83T\x81[\x83o -> \x83N\x83\x89\x83C\x83A\x83\x93\x83g\x88Í\x86\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80\x83`\x83F\x83b\x83N
-	size = get_payload_uint32(pvar, offset);
-	offset += 4;
-
 	if (size >= sizeof(buf)) {
 		logputs(LOG_LEVEL_WARNING, __FUNCTION__ ": server proposed encryption algorithms (server to client) is too long.");
 	}
-	strncpy_s(buf, sizeof(buf), data+offset, _TRUNCATE);
-	offset += size;
+	strncpy_s(buf, sizeof(buf), data, _TRUNCATE);
+	data += size;
 
 	logprintf(LOG_LEVEL_VERBOSE, "server proposal: encryption algorithm server to client: %s", buf);
 
 	pvar->ciphers[MODE_IN] = choose_SSH2_cipher_algorithm(buf, myproposal[PROPOSAL_ENC_ALGS_STOC]);
-	if (pvar->ciphers[MODE_IN] == NULL) {
-		strncpy_s(tmp, sizeof(tmp), "unknown Encrypt algorithm(stoc): ", _TRUNCATE);
+	if (pvar->ciphers[MODE_IN]->id == SSH_CIPHER_NONE) {
+		strncpy_s(tmp, sizeof(tmp), "unknown Encrypt algorithm(server to client): ", _TRUNCATE);
 		strncat_s(tmp, sizeof(tmp), buf, _TRUNCATE);
 		msg = tmp;
 		goto error;
 	}
 
+	// MAC\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80(\x83N\x83\x89\x83C\x83A\x83\x93\x83g -> \x83T\x81[\x83o)
+	if (!grab_payload(pvar, 4)
+	 || !grab_payload(pvar, size = get_uint32(data))) {
+		msg = __FUNCTION__ ": truncated packet (MAC algorithms client to server)";
+		goto error;
+	}
+	data += 4;
 
-	// MAC(Message Authentication Code)\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80\x82̌\x88\x92\xE8 (2004.12.17 yutaka)
-	size = get_payload_uint32(pvar, offset);
-	offset += 4;
-
 	if (size >= sizeof(buf)) {
 		logputs(LOG_LEVEL_WARNING, __FUNCTION__ ": server proposed MAC algorithms (client to server) is too long.");
 	}
-	strncpy_s(buf, sizeof(buf), data+offset, _TRUNCATE);
-	offset += size;
+	strncpy_s(buf, sizeof(buf), data, _TRUNCATE);
+	data += size;
 
 	logprintf(LOG_LEVEL_VERBOSE, "server proposal: MAC algorithm client to server: %s", buf);
 
 	if (pvar->ciphers[MODE_OUT]->auth_len > 0) {
-		logputs(LOG_LEVEL_VERBOSE, "AEAD cipher is selected, ignoring MAC algorithms. (c2s)");
+		logputs(LOG_LEVEL_VERBOSE, "AEAD cipher is selected, ignoring MAC algorithms. (client to server)");
 		pvar->macs[MODE_OUT] = get_ssh2_mac(HMAC_IMPLICIT);
 	}
 	else {
@@ -5003,19 +5016,24 @@
 		}
 	}
 
-	size = get_payload_uint32(pvar, offset);
-	offset += 4;
+	// MAC\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80(\x83T\x81[\x83o -> \x83N\x83\x89\x83C\x83A\x83\x93\x83g)
+	if (!grab_payload(pvar, 4)
+	 || !grab_payload(pvar, size = get_uint32(data))) {
+		msg = __FUNCTION__ ": truncated packet (MAC algorithms server to client)";
+		goto error;
+	}
+	data += 4;
 
 	if (size >= sizeof(buf)) {
 		logputs(LOG_LEVEL_WARNING, __FUNCTION__ ": server proposed MAC algorithms (server to client) is too long.");
 	}
-	strncpy_s(buf, sizeof(buf), data+offset, _TRUNCATE);
-	offset += size;
+	strncpy_s(buf, sizeof(buf), data, _TRUNCATE);
+	data += size;
 
 	logprintf(LOG_LEVEL_VERBOSE, "server proposal: MAC algorithm server to client: %s", buf);
 
 	if (pvar->ciphers[MODE_IN]->auth_len > 0) {
-		logputs(LOG_LEVEL_VERBOSE, "AEAD cipher is selected, ignoring MAC algorithms. (s2c)");
+		logputs(LOG_LEVEL_VERBOSE, "AEAD cipher is selected, ignoring MAC algorithms. (server to client)");
 		pvar->macs[MODE_IN] = get_ssh2_mac(HMAC_IMPLICIT);
 	}
 	else {
@@ -5028,17 +5046,19 @@
 		}
 	}
 
-	// \x88\xB3\x8Fk\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80\x82̌\x88\x92\xE8
-	// pvar->ssh_state.compressing = FALSE; \x82Ƃ\xB5\x82ĉ\xBA\x8BL\x83\x81\x83\x93\x83o\x82\xF0\x8Eg\x97p\x82\xB7\x82\xE9\x81B
-	// (2005.7.9 yutaka)
-	size = get_payload_uint32(pvar, offset);
-	offset += 4;
+	// \x88\xB3\x8Fk\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80(\x83N\x83\x89\x83C\x83A\x83\x93\x83g -> \x83T\x81[\x83o)
+	if (!grab_payload(pvar, 4)
+	 || !grab_payload(pvar, size = get_uint32(data))) {
+		msg = __FUNCTION__ ": truncated packet (compression algorithms client to server)";
+		goto error;
+	}
+	data += 4;
 
 	if (size >= sizeof(buf)) {
 		logputs(LOG_LEVEL_WARNING, __FUNCTION__ ": server proposed compression algorithms (client to server) is too long.");
 	}
-	strncpy_s(buf, sizeof(buf), data+offset, _TRUNCATE);
-	offset += size;
+	strncpy_s(buf, sizeof(buf), data, _TRUNCATE);
+	data += size;
 
 	logprintf(LOG_LEVEL_VERBOSE, "server proposal: compression algorithm client to server: %s", buf);
 
@@ -5050,15 +5070,19 @@
 		goto error;
 	}
 
+	// \x88\xB3\x8Fk\x83A\x83\x8B\x83S\x83\x8A\x83Y\x83\x80(\x83T\x81[\x83o -> \x83N\x83\x89\x83C\x83A\x83\x93\x83g)
+	if (!grab_payload(pvar, 4)
+	 || !grab_payload(pvar, size = get_uint32(data))) {
+		msg = __FUNCTION__ ": truncated packet (compression algorithms server to client)";
+		goto error;
+	}
+	data += 4;
 
-	size = get_payload_uint32(pvar, offset);
-	offset += 4;
-
 	if (size >= sizeof(buf)) {
 		logputs(LOG_LEVEL_WARNING, __FUNCTION__ ": server proposed compression algorithms (server to client) is too long.");
 	}
-	strncpy_s(buf, sizeof(buf), data+offset, _TRUNCATE);
-	offset += size;
+	strncpy_s(buf, sizeof(buf), data, _TRUNCATE);
+	data += size;
 
 	logprintf(LOG_LEVEL_VERBOSE, "server proposal: compression algorithm server to client: %s", buf);
 
@@ -5070,40 +5094,32 @@
 		goto error;
 	}
 
-
-	// \x8C\x88\x92\xE8
+skip:
+	// \x8C\x88\x92肵\x82\xBD\x95\xFB\x8E\xAE\x82\xF0\x83\x8D\x83O\x82ɏo\x97\xCD
 	logprintf(LOG_LEVEL_VERBOSE, "KEX algorithm: %s",
 		get_kex_algorithm_name(pvar->kex_type));
 
-	logprintf(LOG_LEVEL_VERBOSE,
-		"server host key algorithm: %s",
+	logprintf(LOG_LEVEL_VERBOSE, "server host key algorithm: %s",
 		get_ssh_keytype_name(pvar->hostkey_type));
 
-	logprintf(LOG_LEVEL_VERBOSE,
-		"encryption algorithm client to server: %s",
+	logprintf(LOG_LEVEL_VERBOSE, "encryption algorithm client to server: %s",
 		get_cipher_string(pvar->ciphers[MODE_OUT]));
 
-	logprintf(LOG_LEVEL_VERBOSE,
-		"encryption algorithm server to client: %s",
+	logprintf(LOG_LEVEL_VERBOSE, "encryption algorithm server to client: %s",
 		get_cipher_string(pvar->ciphers[MODE_IN]));
 
-	logprintf(LOG_LEVEL_VERBOSE,
-		"MAC algorithm client to server: %s",
+	logprintf(LOG_LEVEL_VERBOSE, "MAC algorithm client to server: %s",
 		get_ssh2_mac_name(pvar->macs[MODE_OUT]));
 
-	logprintf(LOG_LEVEL_VERBOSE,
-		"MAC algorithm server to client: %s",
+	logprintf(LOG_LEVEL_VERBOSE, "MAC algorithm server to client: %s",
 		get_ssh2_mac_name(pvar->macs[MODE_IN]));
 
-	logprintf(LOG_LEVEL_VERBOSE,
-		"compression algorithm client to server: %s",
+	logprintf(LOG_LEVEL_VERBOSE, "compression algorithm client to server: %s",
 		get_ssh2_comp_name(pvar->ctos_compression));
 
-	logprintf(LOG_LEVEL_VERBOSE,
-		"compression algorithm server to client: %s",
+	logprintf(LOG_LEVEL_VERBOSE, "compression algorithm server to client: %s",
 		get_ssh2_comp_name(pvar->stoc_compression));
 
-
 	// we_need\x82̌\x88\x92\xE8 (2004.11.6 yutaka)
 	// \x83L\x81[\x8Dč쐬\x82̏ꍇ\x82̓X\x83L\x83b\x83v\x82\xB7\x82\xE9\x81B
 	if (pvar->rekeying == 0) {



Ttssh2-commit メーリングリストの案内