nmap安装完成后,nmap-service-probes文件(默认路径为:/usr/local/share/nmap)中包含了大量的probe请求以及响应的对应关系。
文件中附带标记####NEXT PROBE####的部分,既为nmap中预先构造好的banner识别请求。
在扫描中附加--version-trace --version-all选项,可以看到nmap主动发送这些请求。
但nmap中内置指纹库不可能那么齐全,因此即便对于某些常见的HTTP服务,邮件服务等,都会存在误报或者无法识别的情况。
对于这种情况,需要在nmap-service-probes文件手工添加match记录,并添加自定义的服务描述,让nmap具备对未知指纹的识别,以及后续扫描脚本的执行功能。
对于上图的143端口,已知其是imap服务,并返回可用于识别的字符串OK Hermes (ver-1.0.0-2018-01-25) Thu,22 Feb 2018 11:05:56 +0800 ..
因此只需要将可识别的字符串,通用化成正则表达式加入nmap-service-probes文件中,即可。
因为imap服务在不需要payload的情况下,便会返回该字符串,因此将用于匹配该指纹的
规则记录match imap m|^\* OK Hermes| p/Hermes imap/
添加到nmap-service-probes文件中的Probe TCP NULL项目下即可。
这样原先不可被识别的imap服务,现在已经变成可识别的了,同时扫描时间大大缩短。
My Staff
2018年2月21日星期三
2016年11月10日星期四
Python2中奇葩的编码问题整理
写这篇文章的目的很简单,之前写HTTP批量获取网站Title的时候,就遇到或这种奇葩问题:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd6 in position 3: ordinal not in range(128)
此外,把代码从osx搬到win上执行的时候,也遇到这种问题。
今天在处理eml文件的时候,同样的代码在osx上执行没问题,在win上执行的时候又报这种让人一脸懵b的问题。
OS之间编码环境的差异
#获得Python的默认编码ascii
import sys
sys.getdefaultencoding()
#获得当前系统中文件系统使用的默认编码
import sys
sys.getfilesystemencoding()
以下是若干个环境测试结果
windows xp(默认语言环境GBK)
windows 2003 (默认语言环境US)
windows 2008 (默认语言环境GBK)
OSX10.9(默认语言环境UTF-8)
Windows下比较奇葩,全都返回mbcs,既多字节字符编码。直接拿这个去decode,Python解释器可不乐意。我们需要得到更精准的描述(cmd中执行chcp可得到当前系统所使用的编码,在控制面板的区域中可以的得到,只不过里面给的并不是特定的编码,而是地区/国家)。
如何通过py代码获得系统默认编码呢?
import locale
import codecs
print locale.getpreferredencoding();
print codecs.lookup(locale.getpreferredencoding()).name
这段代码在Windows 中文系统中返回gbk。
在Linux,OSX中返回utf-8。
到此编码环境确定了,Windows下的编码会经常变化,而Linux系统则不会。
Python程序对编码的处理
Python的代码,强烈建议将所有输入都转为unicode进行处理(根据输入的来源,进行decode处理)在根据需求,将unicode转换为特定的编码进行保存。不然你的python程序同时在linux,win上执行时,可能会出现很奇葩的行为。
将输入来源进行分类:
- 当前系统:locale获取当前系统默认编码
- 外部系统:根据来源系统,进行特定的编码处理
- 其他操作系统:Linux默认为UTF-8,windows由local获得或cmd中执行chcp
- http:Content-Type头,还有html页面中的meta标签都可能包含编码信息。
- 数据库:
- Mysql:
- Oracle:
- MSSQL:
赠送一个unicode查询网站,在赠送一个ppt,也是介绍编码的。
还有一个有编码信息的地方,py文件的头部
#coding=gbk
这段代码定义了当前py文件中的字符串的编码。
既当前文本中的字符串在转换为unicode,只能通过decode,coding所指定的编码获得。
建议设置一个全局变量,用来控制当前os中用户的输入。
奇葩问题集
输出
- 输出到终端:Unicode类型可以直接输出到终端
- 持久化:(需要进行特定编码后才可保存)
- 数据库
- 文本:
- ascii编码:open打开的文本,只能进行ascii读写
- 非ascii编码:codecs.open,指定encoding类型,进行读写
来自不同终端的输入
来自终端的输入,都是带有特定编码的。比如中文的win下,默认编码是gbk。OSX下,默认是utf-8。为了避免出现UnicodeDecodeError的问题。需要根据当前OS的默认编码对输入进行decode处理。raw_input得到的输入,需要decode之后再用。但是不同安装语言的win下,默认编码不同。因此,raw_input().decode的参数需要通过locale的getprederredencoding获得。
import locale
default_os_encoding = locale.getpreferredencoding();
raw_input().decode(default_os_encoding)
os.walk的问题
这玩意儿根据传入的参数类型(str,unicode)不同,返回的dirpath,dirnames,files类型也不同(传str,返回str。传unicode,返回unicode)。官方文档里也没这描述,只有os.listdir有对此行为进行描述。
os.walk以在osx系统与windows中的行为存在差异:
osx上,以str类型作为输入,在pycharm中的调试结果
windows上,以str类型作为输入,在pycharm中的调试结果(这些都是什么鬼)
Windows 2008 R2,E文
Windows 2008 R2,毛子文
Windows 2008 R2,中文
以unicode类型作为os.walk的输入,得到一组unicode类型的返回,在win下与osx在结构都正常。
因此,os.walk比较稳妥的用法:
for dirpath, dirnames, files in os.walk(raw_input.decode(locale.getpreferredencoding()))
2016年10月26日星期三
SSL/TLS手册
什么是SSL
SSL(Secure
Socket Layer),TLS(Transport
Layer Security)用加密算法来确保双方得意安全通信的协议。
版本
- SSL 1.0 (never released)
- SSL 2.0 (by Netscape communications)
- SSL 3.0
(转交给IETF之后变更为TLS,
SSL1.0~3.0版本个协议基本上已经被现代浏览器废弃,不再支持)
- TLS 1.0 (SSL3.1, released in 1999)
- TLS 1.1 (SSL3.2, released in 2006)
- TLS 1.2 (SSL3.3, released in 2008)
各个平台上的SSL/TLS支持
- Windows:
- 的SSL/TLS协议支持,由SCHANNEL组件提供,既系统层面提供。
SSL中的几个概念
- SSL握手(协商协议版本)
- Cipher Suite(加密通信)
- encryption protocol(DES,RC4,AES)
- encryption key length(48,50,128bit)
- hash algorithm(SHA,MD5)
- SSL证书(基于数字证书来做身份识别)
- Issuer:数字证书签发机构(CA)。
- expired:有效期。
- Subject:站点的名称。
- SNI:请求证书时,服务器并不知道客户端要访问哪个域名(虚拟主机),SSLv3/TLSv1及其以后的版本,支持在请求证书时附带主机名称。
Language Binding
不同的language都有自己的openssl依赖,openssl版本不同,可能导致访问目标的ssl
based serivce出现问题。
- GoLang
- Python
SSL Checking
-
握手阶段完成
-
Weak ciphers(劫持攻击)
-
检测目标的ssl/tls版本(溢出攻击)
- //TODO
-
-
证书有效性
-
名称匹配
-
过期
-
Trusted CA
-
- owasp给的参考标准
- 扫描器检测:
- nmap
- nessus
- openssl检测
- openssl s_client -connect host:port -ssl2
- openssl s_client -connect host:port -ssl2
- openssl s_client -connect host:port -tls1
- openssl s_client -connect host:port -tls1_1
- openssl s_client -connect host:port -tls1_2
基本上这些本地的扫描工具都依赖本地安装的openssl,确保使用最新版本的openssl。
openssl版本的选用可以参考官方给出的建议 。
- Version 1.1.0 支持到 2018-08-31。
- Version 1.0.2 支持到 2019-12-31 (LTS)。
- Version 1.0.1 支持到 2016-12-31。
- Version 1.0.0 , 0.9.8 已放弃支持。
建议使用1.0.0以上版本。
传输层加密
劫持攻击
//TODO
2016年4月19日星期二
Tinc, Proxy-arp(TODO)
首先需要明确一下什么是Proxy ARP,它有什么用?
给两个参考链接,解释的挺到位的。
Cisco关于Proxy ARP的解释
某人对于Proxy ARP的见解
其实这个工作模式的tinc就是我一直想要的场景,目标内网机器反向vpn连回vps,我可以直接在vps打内网。省得再拖着一堆工具进去。有时候不小心留了个exe在里面,又不巧碰上弱扫和杀软定时检查。那就懵b了。况且在这种配置下工作,也不会把你暴露到目标网络里,相对隐蔽。
目标:搭建一个反向vpn服务,可以直接访问目标网络里的机器(tinc里给出的配置)
工具:tinc
工作模式:route/switch
所需权限:Host1(root)
这种模式的配置,相比 Tinc,Route Mode 而言需要的权限不要那么大。不需要动到路由器。动静也小的多。
网络结构如下:
TODO
给两个参考链接,解释的挺到位的。
Cisco关于Proxy ARP的解释
某人对于Proxy ARP的见解
其实这个工作模式的tinc就是我一直想要的场景,目标内网机器反向vpn连回vps,我可以直接在vps打内网。省得再拖着一堆工具进去。有时候不小心留了个exe在里面,又不巧碰上弱扫和杀软定时检查。那就懵b了。况且在这种配置下工作,也不会把你暴露到目标网络里,相对隐蔽。
目标:搭建一个反向vpn服务,可以直接访问目标网络里的机器(tinc里给出的配置)
工具:tinc
工作模式:route/switch
所需权限:Host1(root)
这种模式的配置,相比 Tinc,Route Mode 而言需要的权限不要那么大。不需要动到路由器。动静也小的多。
网络结构如下:
TODO
2016年4月14日星期四
php_screw在未知密钥情况下的破解
php_screw模块是一个依赖与zlib与流式加密的php代码加密模块。
主要构成为:
密钥模块:php_screw-1.5/my_screw.h
加密模块:php_screw-1.5/tools/screw.c
解密模块:php_screw-1.5/php_screw.c
具体的加密细节可以参考文件中的实现。
大体加解密的过程如下:
加密过程:
PHP文件(明文) -> zencode ->流式加密(my_screw.h秘钥)-> PHP文件(密文)
解密过程(可逆):
PHP文件(密文)-> 流式解密(my_screw.h秘钥)-> zdecode -> PHP文件(明文)
整个加密模块的弱点在于,在每次加密的过程中都使用相同的密钥,且没有用到IV。因此基于xor运算的特性,在已知一段未知密文,一段明文与密文的对照之后。可以破解未知密文多对照的明文。
破解只需要关注加密部分,解密部分完全可以忽略,因为解密部分依赖的密钥又不在我们手上= =!
下面来分解一下加密的过程:
输入:(PHP文件(明文)-> zencode)->
加密:流式加密(my_screw.h秘钥)->
输出:PHP文件(密文)
撇开加解密过程中zlib的参与(既zencode方法),仅关注中间的流式加密部分。
可以把经过zencode处理的php文件理解为流式加密过程中的明文。
经过流式加密处理的内容既为密文。
因此要破解一段密文a,我们还缺少一分明文与密文的对照。
密文b很简单,直接用对方的screw模块处理一下就能获得。
明文b也和简单,将一个PHP文件中的内容交给zencode处理一下就行了。
接下来将
(密文a ) xor (密文b) xor (明文b) 既可得到明文a
只不过这里得到的明文a是经过zencode处理的。
得到明文a之后调用zdecode一下即可得到最终解密的php代码。
以上情况基于密钥每次都相同的情况。
但实际情况下密钥并不是每次都相同的,但是密钥是有一定规律的,其规律与输入的明文长度有关。来看screw.c中的加密代码。可以知道密钥基于 pm9screw_mycryptkey短整型数组,在实际使用的时,并不是从0元素开始到循环尾元素的,而是基于明文的长度来选择一个起始位置,然后向 前迭代。如果两段明文的长度之间遵守一定规律,是可以构造出相同密钥加密后的密文的。
规律很简单,因为是流式加密,且没有带入任何影响密文长度的内容,因此密文的长度就是明文的长度。
假如密钥的长度为len(key), 对于一段长度为n的密文,其明文的长度也为n。
密钥的起始位置应该为(n-i) % len(key),又因为i默认为0,因此起始位置为:
n % len(key)。
为了构造起始位置相同的密钥,其明文的长度k必须大于n并且满足
k % len(key) == n % len(key)。
构造出此明文,经过screw加密一下得到对应的密文。在加上未知的密文。
这三者拿去xor一下,把xor得到的结果丢给zdecode处理,最终可获得未知密文对应的明文信息。
本场景适用于仅对密钥内容进行了修改, 未对 php_screw的原始加密算法进行过修改。
如果密钥长度修改过的话,就自己按
“明文的长度k必须大于n并且满足k % len(key) == n % len(key)”的规则去创建明文,然后一个个匹配结果吧!
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "../php_screw.h"
#include "../my_screw.h"
char *CryptBinaryData(char *pData, int nDataLen, int *pNewDataLen)
{
char *pRet=NULL;
*pNewDataLen = 0;
pRet = zencode(pData, nDataLen, pNewDataLen);
int cryptkey_len = sizeof pm9screw_mycryptkey / 2;
printf("cryptkey_len: %d\r\nfirst_key_elemnt:%d\r\n",cryptkey_len,pm9screw_mycryptkey[(*pNewDataLen - 0) % cryptkey_len]);
int i=0;
for(i=0; i<*pNewDataLen; i++) {
pRet[i] = (char)pm9screw_mycryptkey[(*pNewDataLen - i) % cryptkey_len] ^ (~(pRet[i]));
}
return pRet;
}
void printf_string_on_element(char *pString, int nLen)
{
int i=0;
for(i=0; i<nLen; ++i)
{
printf("%d ",pString[i]);
}
printf("\r\n");
}
int main(int argc, char **argv)
{
/*
默认key长度为5
"<? echo '123'?>";//zencode后的长度为23
"<? echo '12345678'?>";//zencode后的长度为28
满足
23 % 5 == 28 % 5 && 28 > 25
*/
char *pSourceText1="<? echo '123'?>";
int nSourceText1Len = strlen(pSourceText1);
char *pSourceText2="<? echo '12345678'?>";
int nSourceText2Len = strlen(pSourceText2);
//构造pSourceText2对应的“明文”
int nPlainText2Len = 0;
char *pPlainText2=zencode(pSourceText2,nSourceText2Len, &nPlainText2Len);
printf("%s encoded by zencode:%d\r\n%sr\n",pSourceText2, nPlainText2Len, pPlainText2);
printf_string_on_element(pPlainText2, nPlainText2Len);
//构造pSourceText1的密文
int nCipherText1Len = 0;
char *pCipherText1 = CryptBinaryData(pSourceText1,nSourceText1Len,&nCipherText1Len);
printf("%s Cryptyed %d:\r\n%s\r\n",pSourceText1, nCipherText1Len, pCipherText1);
printf_string_on_element(pCipherText1, nCipherText1Len);
//构造pSourceText2的密文
int nCipherText2Len = 0;
char *pCipherText2 = CryptBinaryData(pSourceText2,nSourceText2Len,&nCipherText2Len);
printf("%s Cryptyed %d:\r\n%s\r\n",pSourceText2, nCipherText2Len, pCipherText2);
printf_string_on_element(pCipherText2, nCipherText2Len);
//xor
int i =0;
char plaintextBuffer[1024]={0};
for(i=0; i<nCipherText1Len; ++i)
{
//plaintextBuffer[i] =pCipherText2[i] ^ pCipherText1[i] ^ (~pPlainText2[i]);
//plaintextBuffer[i] = ~plaintextBuffer[i];
//上面两句可以简化为下面这一句,因为两个非运算后,还是得到原始结果
plaintextBuffer[i] =pCipherText2[i] ^ pCipherText1[i] ^ (pPlainText2[i]);
}
printf("%s\r\n",plaintextBuffer);
printf_string_on_element(plaintextBuffer, nCipherText1Len);
int nPlainTextResultLen = 0;
char *pPlainTextResult = zdecode(plaintextBuffer, nCipherText1Len, &nPlainTextResultLen);
printf("(%d)%s\r\n",nPlainTextResultLen, pPlainTextResult);
return 0;
}
主要构成为:
密钥模块:php_screw-1.5/my_screw.h
加密模块:php_screw-1.5/tools/screw.c
解密模块:php_screw-1.5/php_screw.c
具体的加密细节可以参考文件中的实现。
大体加解密的过程如下:
加密过程:
PHP文件(明文) -> zencode ->流式加密(my_screw.h秘钥)-> PHP文件(密文)
解密过程(可逆):
PHP文件(密文)-> 流式解密(my_screw.h秘钥)-> zdecode -> PHP文件(明文)
整个加密模块的弱点在于,在每次加密的过程中都使用相同的密钥,且没有用到IV。因此基于xor运算的特性,在已知一段未知密文,一段明文与密文的对照之后。可以破解未知密文多对照的明文。
破解只需要关注加密部分,解密部分完全可以忽略,因为解密部分依赖的密钥又不在我们手上= =!
下面来分解一下加密的过程:
输入:(PHP文件(明文)-> zencode)->
加密:流式加密(my_screw.h秘钥)->
输出:PHP文件(密文)
撇开加解密过程中zlib的参与(既zencode方法),仅关注中间的流式加密部分。
可以把经过zencode处理的php文件理解为流式加密过程中的明文。
经过流式加密处理的内容既为密文。
因此要破解一段密文a,我们还缺少一分明文与密文的对照。
密文b很简单,直接用对方的screw模块处理一下就能获得。
明文b也和简单,将一个PHP文件中的内容交给zencode处理一下就行了。
接下来将
(密文a ) xor (密文b) xor (明文b) 既可得到明文a
只不过这里得到的明文a是经过zencode处理的。
得到明文a之后调用zdecode一下即可得到最终解密的php代码。
以上情况基于密钥每次都相同的情况。
但实际情况下密钥并不是每次都相同的,但是密钥是有一定规律的,其规律与输入的明文长度有关。来看screw.c中的加密代码。可以知道密钥基于 pm9screw_mycryptkey短整型数组,在实际使用的时,并不是从0元素开始到循环尾元素的,而是基于明文的长度来选择一个起始位置,然后向 前迭代。如果两段明文的长度之间遵守一定规律,是可以构造出相同密钥加密后的密文的。
规律很简单,因为是流式加密,且没有带入任何影响密文长度的内容,因此密文的长度就是明文的长度。
假如密钥的长度为len(key), 对于一段长度为n的密文,其明文的长度也为n。
密钥的起始位置应该为(n-i) % len(key),又因为i默认为0,因此起始位置为:
n % len(key)。
为了构造起始位置相同的密钥,其明文的长度k必须大于n并且满足
k % len(key) == n % len(key)。
构造出此明文,经过screw加密一下得到对应的密文。在加上未知的密文。
这三者拿去xor一下,把xor得到的结果丢给zdecode处理,最终可获得未知密文对应的明文信息。
本场景适用于仅对密钥内容进行了修改, 未对 php_screw的原始加密算法进行过修改。
如果密钥长度修改过的话,就自己按
“明文的长度k必须大于n并且满足k % len(key) == n % len(key)”的规则去创建明文,然后一个个匹配结果吧!
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "../php_screw.h"
#include "../my_screw.h"
char *CryptBinaryData(char *pData, int nDataLen, int *pNewDataLen)
{
char *pRet=NULL;
*pNewDataLen = 0;
pRet = zencode(pData, nDataLen, pNewDataLen);
int cryptkey_len = sizeof pm9screw_mycryptkey / 2;
printf("cryptkey_len: %d\r\nfirst_key_elemnt:%d\r\n",cryptkey_len,pm9screw_mycryptkey[(*pNewDataLen - 0) % cryptkey_len]);
int i=0;
for(i=0; i<*pNewDataLen; i++) {
pRet[i] = (char)pm9screw_mycryptkey[(*pNewDataLen - i) % cryptkey_len] ^ (~(pRet[i]));
}
return pRet;
}
void printf_string_on_element(char *pString, int nLen)
{
int i=0;
for(i=0; i<nLen; ++i)
{
printf("%d ",pString[i]);
}
printf("\r\n");
}
int main(int argc, char **argv)
{
/*
默认key长度为5
"<? echo '123'?>";//zencode后的长度为23
"<? echo '12345678'?>";//zencode后的长度为28
满足
23 % 5 == 28 % 5 && 28 > 25
*/
char *pSourceText1="<? echo '123'?>";
int nSourceText1Len = strlen(pSourceText1);
char *pSourceText2="<? echo '12345678'?>";
int nSourceText2Len = strlen(pSourceText2);
//构造pSourceText2对应的“明文”
int nPlainText2Len = 0;
char *pPlainText2=zencode(pSourceText2,nSourceText2Len, &nPlainText2Len);
printf("%s encoded by zencode:%d\r\n%sr\n",pSourceText2, nPlainText2Len, pPlainText2);
printf_string_on_element(pPlainText2, nPlainText2Len);
//构造pSourceText1的密文
int nCipherText1Len = 0;
char *pCipherText1 = CryptBinaryData(pSourceText1,nSourceText1Len,&nCipherText1Len);
printf("%s Cryptyed %d:\r\n%s\r\n",pSourceText1, nCipherText1Len, pCipherText1);
printf_string_on_element(pCipherText1, nCipherText1Len);
//构造pSourceText2的密文
int nCipherText2Len = 0;
char *pCipherText2 = CryptBinaryData(pSourceText2,nSourceText2Len,&nCipherText2Len);
printf("%s Cryptyed %d:\r\n%s\r\n",pSourceText2, nCipherText2Len, pCipherText2);
printf_string_on_element(pCipherText2, nCipherText2Len);
//xor
int i =0;
char plaintextBuffer[1024]={0};
for(i=0; i<nCipherText1Len; ++i)
{
//plaintextBuffer[i] =pCipherText2[i] ^ pCipherText1[i] ^ (~pPlainText2[i]);
//plaintextBuffer[i] = ~plaintextBuffer[i];
//上面两句可以简化为下面这一句,因为两个非运算后,还是得到原始结果
plaintextBuffer[i] =pCipherText2[i] ^ pCipherText1[i] ^ (pPlainText2[i]);
}
printf("%s\r\n",plaintextBuffer);
printf_string_on_element(plaintextBuffer, nCipherText1Len);
int nPlainTextResultLen = 0;
char *pPlainTextResult = zdecode(plaintextBuffer, nCipherText1Len, &nPlainTextResultLen);
printf("(%d)%s\r\n",nPlainTextResultLen, pPlainTextResult);
return 0;
}
Tinc,Route Mode
目标:搭建一个反向vpn服务。
工具:tinc
工作模式:route
所需权限:Host1(root),Route/Firewall(root)
网络结构如下:
当前权限位于Route/Firewall内的LAN中,网段为:192.168.1.0/24, 主机为192.168.1.121(下面图有错。)
VPN所在的虚拟网段为10.0.0.0/8
希望在Host1与vps server之间开一条隧道,通过vps server连入192.168.1.0/24。
配置可以参考官网给的windows下的配置:http://tinc-vpn.org/examples/windows-install/
Host1上的配置文件:
%TINC_HOME%/to_vps/tinc.conf
Name = local_host
ConnectTO = vps
Interface = vpn
%TINC_HOME%/to_vps/hosts/local_host
Subnet = 10.0.0.3/32
Subnet = 192.168.1.0/24
附加Key后拷贝到vps server上的 %TINC_HOME%/vps/hosts/ 目录下
Host1上的网卡配置:
vps server上的配置文件
%TINC_HOME%/vps/tinc.conf
Name = vps
Interface = vpn
%TINC_HOME%/vps/hosts/vps
Subnet = 10.0.0.2/32
附加Key后拷贝到Host1上的 %TINC_HOME%/to_vps/hosts/ 目录下
vps server上的网卡配置
配置好之后两台机器可以互通
现在需要增加配置,让vps server可以访问目标的192.168.1.0/24网段。
tinc启动后,vps server上的路由表是这样的
里面并没有到192.168.1.0/24的路由,需要手工添加。
Route add 192.168.1.0 mask 255.255.255.0 10.0.0.3 metric 1 if 14
解释一下这条路由:
目标网络
192.168.1.0 mask 255.255.255.0 对应的就是192.168.1.0/24
网关
10.0.0.3,对应的就是Host1那台主机
接口
这里14是tinc所使用的虚拟网卡的网卡ID
刚开始以为只要配置了这条路由之后,就能访问内部的192.168.1.0/24网络了。
但是ping的时候却出问题了。
然后我就想这是什么毛病。。。想了很久想不明白,什么书都翻出来看了也没头绪,头都要炸了。算了干脆tcpdump抓个包看看。
这不是有东西么。。
在看看目标网络上的包情况。
这不是响应了么。。。
但是我在vps server为什么收不到?
看下包的详细信息吧。
下面那条192.168.1.111 > 10.0.0.2,响应的
在看看Host1上的arp table。
arp源地址 (ac:xx:xx:xx:xx:xx)对应的是192.168.1.111,这条没错。
arp 目标地址(50:xx:xx:xx:xx:xx)对应的是192.168.1.1 ,这尼玛是目标网段(192.168.1.0/24)的网关啊。
ICMP Reply没往Host1(192.168.1.121)上发,再由经tinc转回vps server,而是发到网关去了,难怪收不到。
怎么办?如果我在192.168.1.111上加一条arp记录,直接指定10.0.0.2的位置行不行?
看来不行 =,=
那加一条路由记录指定一下10.0.0.0/8网段的路由是不是就ok了?
往路由表里塞一条记录试试看。
加上了!!回去ping一下看看!
卧槽!通了。
抓包看看!
还是下面那条 192.168.1.111 > 10.0.0.2
arp源地址 (ac:xx:xx:xx:xx:xx)对应的是192.168.1.111,这条没错。
arp 目标地址(00:xx:xx:xx:xx:xx)对应的是Host1(192.168.1.121) ,这是Host1的mac地址,这下没错了。
那我现在要让192.168.1.0/24的所有机器都能和vps server互通,是不是要一台台机器去加路由=,=。
这不科学。。。
然后我就想,既然10.0.0.2被路由到了192.168.1.1。那我直接在192.168.1.1上加个路由是不是就ok了?
先把192.168.1.111的路由删了,然后上192.168.1.1去试试。
删了。然后不通了。。。
上192.168.1.1去加
加上了
好激动。。在回去ping试试看。
都通了!!
这里有几个概念没搞明白,导致问题折腾这么久。
首先vpn所处的网段10.0.0.0/8和目标网段192.168.1.0/24是不同的网段。
需要路由的介入两个网段才能完成通信。
虽然在vps server上添加了到达192.168.1.0/24的路由记录。
数据包从vps server上的tinc服务到达Host1上的tinc服务,并由Host1送达目标网络上的主机流程如下。
Ping 192.168.1.111后
ICMP Echo数据包首先通过vps server上的路由记录
送达tinc所管理的虚拟网卡(10.0.0.2)上,vps server上的tinc服务将该数据包转发给Host1上的tinc服务
因为目标地址192.168.1.111与Host1(192.168.1.121)处在相同网络,通过路由表+arp表。
可以找到192.168.1.111的位置。(Host1开启了ip转发功能)
(
Windows上同样需要开启IP转发功能
比较蛋疼,需要重启
)
接下来ICMP Echo数据包会到达192.168.1.111。
此时数据包的源地址为10.0.0.2。
问题就出在这!192.168.1.111在回应ICMP Reply数据包的时候会检查自己的路由表,查找10.0.0.2这个IP地址的下一站在哪。
而没有经过配置的192.168.1.111的路由表是没有10.0.0.0/8这个网络下一条的地址的。
于是ICMP Reply被送往默认网关(路由表中的第一条记录)
很明显,网关也没经过配置,自然也不知道10.0.0.0/8的下一跳在哪,所以将该ICMP Reply丢弃。
这就导致了在vps server虽然有了通往192.168.1.0/24网段的路由,但是却无法ping通192.168.1.111的问题。
网络间的互通不仅需要路由的介入,还需要每台主机上的路由表介入。
这下192.168.1.0/24上的主机,也能直接与10.0.0.2通信了。
订阅:
博文 (Atom)