第一部分:医院域环境架构

一、医院企业管理架构

某医院为根域,本部医院坐落在某某某地,下设子域附属子医院坐落在某某某地,与医院本部有交易来往的是南京市的某某医疗设备售卖公司,医院本部与公司之间建立起来双向信任关系。

1.1 医院本部架构

image.png

根域下设四个组织单元,分别为放在DMZ区的web服务器、用户、工作站及域控,由于电脑内存有限,所以服务器、域控都设置在一台电脑上运行,其中web服务器搭建医院的门户网站系统。

image.png

在用户组织单元下设置院长对各个部门的医生进行管理,还设置了人力资源部门、门诊部门、医疗科技部门、临床部门、护理部门、保安部门和财务部门,在医疗科技部门下又设置药剂科和放射科两个组织单元进行管理。

image.png
image.png

1.2 附属子医院和医疗设备售卖公司

附属子医院和医疗设备售卖公司都只设置了基本的组织单元域控、服务器与用户。

image.png

1.3域信任关系

医院的主域与医疗设备售卖公司建立信任关系,医院本部与附属医院建立父子域关系

image.png

1.4管理关系分配

l 院长管理所有员工

l 人力资源部门设置主任和员工,其余部门都设置主任和医生

image.png

l 院长组织单元内设置两个用户

image.png

l 人力资源部门设置主任和员工

l 门诊部门设置主任和医生

l 医疗科技部门设置药剂科与放射科以及主任

l 药剂科分主任和医生

l 放射科分主任和医生

l 临床部门分主任和医生

l 护理部门分护士长和护士

l 保安部门分队长和员工

l 财务部门分主任和员工

二、域上设置密码、锁定、单点登录策略

2.1安全需求

l 密码策略:

l 密码必须符合复杂性要求

l 密码长度最小值6

l 密码最短使用期限0天(为了方便可以随时更改密码)

l 密码最长使用期限60天(为了安全性强制用户每两个月更换一次密码)

l 记录五个历史密码

l 可以用还原的加密来存储密码

l 用户无操作30分钟后锁定账户

l 五次无效登陆后锁定用户

l 30分钟之后重置账户锁定计数器

l 服务票据最长寿命300分钟

l 时钟同步的最大容差5分钟

l 强制用户登录限制

l 用户票证最长续订最长寿命10天

l 用户票证最长寿命10小时

2.2安全实现

计算机配置->安全设置->账户策略->密码策略

image.png

计算机配置->安全设置->账户策略->账户锁定策略

image.png

计算机配置->安全设置->账户策略->Kerberos策略

image.png

三、域策略设置为强制

3.1安全需求

l 为了防止下层管理员组织域全局策略,需要设置策略为强制

3.2安全实现

image.png

四、规划管理用户

4.1安全需求

l 允许医生、护士、员工周一到周五登录用户

l 允许院长、主任、护士长周一到周日登录

l 允许保安队队长、员工周一到周日登录

l 添加各个部门的用户到对应的全局组

l 将全局组添加到域本地组(采用AGDLP原则)

l 让院长对所有域本地组委派权限

l 让各个主任对所管理的部门用户所在的域本地组委派权限实现统筹管理

4.2安全实现

其他成员截图类似

人力资源主任设置

image.png

张医生设置

image.png

将财务部门用户添加到财务部门全局组

image.png

创建域本地组

image.png

将全局组添加到域本地组中

image.png

成功设置AGDLP规则(A表示用户账号,G表示全局组,U表示通用组,DL表示域本地组,P表示资源权限。A-G-DL-P策略是将用户账号添加到全局组中,将全局组添加到域本地组中,然后为域本地组分配资源权限。

假设,你有两个域,A和B,A中的5个财务人员和B中的3个财务人员都需要访问B中的“FINA”文件夹,这时,你可以在B中建一个DL,因为DL的成员可以来自所有的域,然后把这8个人都加入这个DL,并把FINA的访问权赋给DL。这样做的坏处是什么呢?因为DL是在B域中,所以管理权也在B域,如果A域中的5个人变成6个人,那只能A域管理员通知B域管理员,将DL的成员做一下修改,B域的管理员太累了。

这时候,我们改变一下,在A和B域中都各建立一个全局组(G),然后在B域中建立一个DL,把这两个G都加入B域中的DL中,然后把FINA的访问权赋给DL。这下两个G组都有权访问FINA文件夹了,是吗?组嵌套造成权限继承嘛!这时候,两个G分布在A和B域中,也就是A和B的管理员都可以自己管理自己的G啦,只要把那5个人和3个人加入G中,就可以了!以后有任何修改,都可以自己做了,不用麻烦B域的管理员了。这就是AGDLP。)

image.png
对部门主任设置为财务部门域本地组的管理者实现了P->DL(权限分配到域本地组)

image.png

五、医院官网部署及用户组织单元的统一设置

5.1安全需求

l 搭建一个某医院的主页部署到医院购买的云服务器(http://***)

l 对医院内所有电脑设置IE浏览器默认主页为某医院的主页***

l 医院内所有用户都使用某壁纸

l 为医院内所有用户分发软件

l 为医院内所有用户配置登录注销脚本

5.2 某医院主页搭建在阿里云服务器上(ip:********)**

image.png

简单配置容器

image.png

编写前端代码,部署网页应用

前端代码文件架构H5开发

image.png

image.png

前端展示地址:url栏中输入***********即可,因为是公网ip,门户主页网站,所以所有人都可以浏览

5.3下发用户组织单元的组策略

创建链接到用户组织单元的组策略

image.png

编辑组策略禁止更改主页设置

image.png

修改url为http://****

image.png

应用组策略,使用客户机登录用户组织单元内任一用户,打开ie浏览器主页都是某医院的主页

image.png

设置壁纸组策略

image.png

登录客户机验证

image.png

配置软件分发组策略

image.png

设置部署内容

image.png

部署成功(一定不要忘记更新组策略重启)

image.png

设置登录脚本

image.png

六、Exchange server

6.1 安全需求

l 使用exchange server作为邮件服务器在医院内部部署

6.2 安装exchange server

6.2.1**安装.Net Framework 4.**7.2

image.png

6.2.2安装.Net Framework 4.8

下载链接:https://dotnet.microsoft.com/download/dotnet-framework/net48

image.png

6.2.3以管理员身份运行Windows Powershell,安装必需的 Windows组件

Install-WindowsFeature NET-Framework-45-Features, Server-Media-Foundation, RPC-over-HTTP-proxy, RSAT-Clustering, RSAT-Clustering-CmdInterface, RSAT-Clustering-Mgmt, RSAT-Clustering-PowerShell, WAS-Process-Model, Web-Asp-Net45, Web-Basic-Auth, Web-Client-Auth, Web-Digest-Auth, Web-Dir-Browsing, Web-Dyn-Compression, Web-Http-Errors, Web-Http-Logging, Web-Http-Redirect, Web-Http-Tracing, Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Lgcy-Mgmt-Console, Web-Metabase, Web-Mgmt-Console, Web-Mgmt-Service, Web-Net-Ext45, Web-Request-Monitor, Web-Server, Web-Stat-Compression, Web-Static-Content, Web-Windows-Auth, Web-WMI, Windows-Identity-Foundation, RSAT-ADDS

image.png

6.2.4安装C++组件

安装Visual C++ Redistributable Package for Visual Studio 2012

网址:https://www.microsoft.com/en-US/download/details.aspx?id=30679

image.png
安装

image.png

安装Visual C++ Redistributable Package for Visual Studio 2013

网址: https://www.microsoft.com/en-us/download/details.aspx?id=40784

image.png

6.2.5安装API

安装Microsoft统一通信托管API 4.0 核心运行时(64 位)

地址:Download 统一通信托管 API 4.0 运行时 from Official Microsoft Download Center

image.png
安装完成后重启服务器

安装exchange server 2016 CU14,下载地址为:

https://www.microsoft.com/en-us/download/details.aspx?id=100302

下载后是一个镜像文件,打开里面的.exe安装即可,文件有点大

image.png

image.png

复制文件

image.png

6.3部署exchange server

服务器角色选择

image.png

升级包地址:

Microsoft Update Catalog

image.png

升级后重启然后就能安装exchange server了

image.png

邮箱管理登录页面:https://localhost/ecp

个人邮箱用户登录页面:https://localhost/owa

创建 Internet 发送连接器

image.png

image.png

image.png
登陆后进行邮件收发测试,发现可以正常收发邮件

image.png

image.png

image.png

成功部署

七、规划管理保安部门

7.1安全需求

l 由于保安部门的特殊性,保安部门的用户的电脑主要要用来监控录像,所以需要对保安部门设置强硬的软件限制策略,并设置为强制

7.2安全实现

在安保部门组织单元链接一个组策略

image.png

设置为强制

image.png

编辑组策略,做软件限制策略

确保系统安全的第一步,就是保证自身不受威胁仿冒,为了保护系统关键进程

进行如下策略配置:

C:\WINDOWS\system32\csrss.exe 不受限的

C:\WINDOWS\system32\ctfmon.exe 不受限的

C:\WINDOWS\system32\lsass.exe 不受限的

C:\WINDOWS\system32\rundll32.exe 不受限的

C:\WINDOWS\system32\services.exe 不受限的

C:\WINDOWS\system32\smss.exe 不受限的

C:\WINDOWS\system32\spoolsv.exe 不受限的

C:\WINDOWS\system32\svchost.exe 不受限的

C:\WINDOWS\system32\winlogon.exe 不受限的

image.png

进而再屏蔽掉其他路径下仿冒进程

csrss.* 不允许的 (.* 表示任意后缀名,这样就涵盖了 bat com 等 等可执行的后缀) ctfm?n.* 不允许的

lass.* 不允许的

lssas.* 不允许的

rund*.* 不允许的

services.* 不允许的

smss.* 不允许的

sp???sv.* 不允许的

s??h?st.* 不允许的

s?vch?st.* 不允许的

win??g?n.* 不允许的

image.png

防止伪装进程 一些病毒和恶意软件通常喜欢伪装为我们常见的进程来迷惑用户,例如 Windows 常见进程 svchost.exe 就是常见的已被伪装进程。

image.png

浏览器安全

浏览网页中毒的主要途径是通过网页的页面缓存,通过缓存的文件,病毒与***会将自身进 行复制、转移等操作,由此,对浏览器缓存文件进行限制,并且限制 IE 对系统敏感位置进 行操作就成为保障浏览器安全环节中的重要一环。我们这里使用的方法就是禁止 IE 在系统 敏感位置创建文件。

创建如下规则:

%ProgramFiles%\Internet Explorer\iexplore.exe 基本用户

%UserProfile%\Local Settings\Temporary Internet Files\*.* 不允许的

%UserProfile%\Local Settings\Temporary Internet Files\* 不允许的

%UserProfile%\Local Settings\Temporary Internet Files\ 不允许的

%UserProfile%\Local Settings\Temporary Internet Files 不允许的

image.png

免疫流氓软件与恶意插件可以利用软件限制策略进一步对上网安全进行优化,例如,为了免疫流氓软件与恶意插件,

我们可以配置如下限制规则:

3721.*

不允许的

CNNIC.*

不允许的

*Bar.*

不允许的

image.png

禁止从回收站和备份文件夹执行文件

?:\Recycler\**\*.*

不允许的

?:\System Volume Information\**\*.*

不允许的

image.png

防范移动存储设备携毒

将系统中可分配给移动设备的盘符通通进行限制

?:\autorun.inf

不允许的

image.png
根据需要准确限制目录位置

例如我们需要限制

C: 盘下的程序文件夹下的程序运行,可能会创建如下规则:

C:\Program Files

不允许的

C:\Program Files\*\

不受限的

登陆客户机验证

image.png

八、备份策略

8.1安全需求

l 有些医院独立研发的药品的研究信息,药品成分等信息需要定期备份防止丢失

8.2安全实现

image.png

打开任务计划程序->创建任务

image.png

设置任务

image.png

成功部署

image.png

九、部署FTP服务器

9.1安全需求

l 有些医院独立研发的药品的研究信息,药品成分等信息需要存放到FTP服务器上以保证信息不被丢失

9.2安全实现

设置好用户为***,密码为************

image.png
指派权限为下载上传和新建目录

image.png

使用客户机链接此ftp服务器,并成功传输文件

image.png

十、PKI设置

10.1安全需求

l 医院内部提供证书申请服务,域中用户可以从域控申请数字证书,从而完成安全CA认证,防止中间人攻击

10.2安全实现

打开 mmc 管理器,添加证书

image.png

选择 EFS 恢复代理

image.png

至此证书申请成功,根 ca 证书

image.png

web 页面客户机申请添加证书

打开客户机访问根域的 http://********/certsrv

image.png

安装证书

image.png
组策略结合 CA 服务器来完成证书的自动注册申请,利用这种方式,我们可以对整个域的用户或某个组织的用户,只要用户在开机后以域用户身份登录,就可以自动完成证书的注册申请MMC 中添加证书模板单元

image.png

复制模板

image.png

配置组策略

image.png

SSL 使用,IIS 中添加签名证书

image.png

申请证书

image.png

发现成功创建

image.png

客户机登录证书 http://****************/certsrv

image.png

IIS 管理器,点击完成证书申请成功添加证书

image.png

然后我们重新绑定一致的网站

image.png

十一、医院与附属子医院实现IPSec通信

11.1安全需求

l 医院本部与附属医院之间两台域控之间实现加密会话

11.2安全实现

编辑组策略,创建 ip 安全策略

image.png

设置名字,添加筛选表

image.png

设置源 ip

image.png

设置筛选器向导,设置加密

image.png

image.png

设置身份验证方法为预共享密钥

image.png

分配

image.png

Ps:记得在两台域控都要设置

在策略指派前与主机不能 ping 通

image.png

在两端策略指派后与主机能 ping 通

image.png

十二、部署战略性蜜罐系统

12.1安全需求

l 假如医院成为了护网行动的首要攻击对象,上级教育部要求部署一定的溯源防御蜜罐系统

12.2蜜罐

HFish由控制端和节点端组成,控制端用来生成和管理节点端,并接收、分析和展示节点端回传的数据,节点端接受控制端的控制并负责构建蜜罐服务。

image.png

十三、常见内网攻击的防御(模拟护网蓝队)

13.1安全需求

l 假如医院域内某台主机已经被黑客操控,现在内网已经很不安全,要求蓝队人员对域控服务器加固,防止黑客进一步横向移动渗透,防止黑客窃取凭据,从而威胁整个医院的安全

13.2攻击机与域模拟真实分配部署

模拟内网渗透,所以直接将攻击机kali(192.168.111.130)加入内网,并为客户机上线一台cs马,来模拟黑客已经攻陷一台内网电脑

image.png

域控192.168.111.131

子域控192.168.111.133

13.3模拟内网信息搜集

使用hashdump获取凭据

image.png

Mimikatz

image.png

13.4模拟黄金票据攻击

黄金票据是伪造票据授予票据(TGT),也被称为认证票据。如下图所示,与域控制器没有AS-REQ或AS-REP(步骤1和2)通信。由于黄金票据是伪造的TGT,它作为TGS-REQ的一部分被发送到域控制器以获得服务票据

image.png

Kerberos黄金票据是有效的TGT Kerberos票据,因为它是由域Kerberos帐户(KRBTGT)加密和签名的 。TGT仅用于向域控制器上的KDC服务证明用户已被其他域控制器认证。TGT被KRBTGT密码散列加密并且可以被域中的任何KDC服务解密的。

黄金票据的条件要求:

1.域名称[AD PowerShell模块:(Get-ADDomain).DNSRoot]

2.域的SID 值[AD PowerShell模块:(Get-ADDomain).DomainSID.Value]

3.域的KRBTGT账户NTLM密码哈希

4.伪造用户名

一旦攻击者拥有管理员访问域控制器的权限,就可以使用Mimikatz来提取KRBTGT帐户密码哈希值。

黄金票据条件充足

13.5模拟蓝队针对攻击做出防御

此时蓝队防御者通过查看票据发现异常,对此做出防御

1.限制域管理员登录到除域控制器和少数管理服务器以外的任何其他计算机(不要让其他管理员登录到这些服务器)将所有其他权限委派给自定义管理员组。这大大降低了攻击者访问域控制器的Active Directory的ntds.dit。如果攻击者无法访问AD数据库(ntds.dit文件),则无法获取到KRBTGT帐户密码。

2. 禁用KRBTGT帐户,并保存当前的密码以及以前的密码。KRBTGT密码哈希用于在Kerberos票据上签署PAC并对TGT(身份验证票据)进行加密。如果使用不同的密钥(密码)对证书进行签名和加密,则DC(KDC)通过检查KRBTGT以前的密码来验证。

3.建议定期更改KRBTGT密码(毕竟这是一个管理员帐户)。更改一次,然后让AD备份,并在12到24小时后再次更改它。这个过程应该对系统环境没有影响。这个过程应该是确保KRBTGT密码每年至少更改一次的标准方法。

4.一旦攻击者获得了KRBTGT帐号密码哈希的访问权限,就可以随意创建黄金票据。通过快速更改KRBTGT密码两次,使任何现有的黄金票据(以及所有活动的Kerberos票据)失效。这将使所有Kerberos票据无效,并消除攻击者使用其KRBTGT创建有效金票的能力。

我这里尝试快速更改KRBTGT帐号密码2次

image.png

防御成功:

image.png

实现的安全需求汇总

l 密码策略:

l 密码必须符合复杂性要求

l 密码长度最小值6

l 密码最短使用期限0天(为了方便可以随时更改密码)

l 密码最长使用期限60天(为了安全性强制用户每两个月更换一次密码)

l 记录五个历史密码

l 可以用还原的加密来存储密码

l 用户无操作30分钟后锁定账户

l 五次无效登陆后锁定用户

l 30分钟之后重置账户锁定计数器

l 服务票据最长寿命300分钟

l 时钟同步的最大容差5分钟

l 强制用户登录限制

l 用户票证最长续订最长寿命10天

l 用户票证最长寿命10小时

l 为了防止下层管理员组织域全局策略,需要设置策略为强制

l 允许医生、护士、员工周一到周五登录用户

l 允许院长、主任、护士长周一到周日登录

l 允许保安队队长、员工周一到周日登录

l 添加各个部门的用户到对应的全局组

l 将全局组添加到域本地组(采用AGDLP原则)

l 让院长对所有域本地组委派权限

l 让各个主任对所管理的部门用户所在的域本地组委派权限实现统筹管理

l 对医院内所有电脑设置IE浏览器默认主页为医院的主页

l 医院内所有用户都使用矿大壁纸

l 为医院内所有用户分发软件

l 为医院内所有用户配置登录注销脚本

l 搭建一个医院的主页部署到医院购买的云服务器

l 为医院内所有用户配置登录注销脚本

l 使用exchange server作为邮件服务器在医院内部部署

l 由于保安部门的特殊性,保安部门的用户的电脑主要要用来监控录像,所以需要对保安部门设置强硬的软件限制策略,并设置为强制

l 有些医院独立研发的药品的研究信息,药品成分等信息需要存放到FTP服务器上以保证信息不被丢失

l 医院内部提供证书申请服务,域中用户可以从域控申请数字证书,从而完成安全CA认证,防止中间人攻击

l 医院本部与附属医院之间两台域控之间实现加密会话

l 假如医院成为了护网行动的首要攻击对象,上级教育部要求部署一定的溯源防御蜜罐系统

l 假如医院域内某台主机已经被黑客操控,现在内网已经很不安全,要求蓝队人员对域控服务器加固,防止黑客进一步横向移动渗透,防止黑客窃取凭据,从而威胁整个医院的安全

第二部分:设计实现VPN

一、工作流程

模拟一个网页请求流程

1、Firefox在笔记本发出一个请求。

2、内核使用默认路由发送这个请求的IP包。

3、因为默认路由的设备设置的是tun0,所以被tunnel程序捕获。

4、tunnel程序读取ip包后,用icmp封装,发送到远程vps。

5、icmp无障碍地通过cmwap网络,发送到远程vps上。

6、远程vps收到后,被服务器端的tunnel程序捕获(tunnel程序捕获所有的icmp数据包)。

7、tunnel程序读取icmp包,获取里面的ip包,写入到本地网络中。

8、因为通过iptables设置了nat,所以该ip包的源地址被改为vps ip后,发送到了所请求的服务器上。

9、一个IP包从请求的服务器上返回到vps,经过nat后,进入tunnel程序建立的网络,被tunnel程序捕获。

10、tunnel程序读取ip包后,用icmp封装,发送到笔记本。

11、icmp回应包无障碍地通过cmwap网络,发送到笔记本上。

12、笔记本接收到该icmp包,被笔记本上的tunnel程序捕获。

13、tunnel程序读取icmp包,获取里面的ip包,写入到本地网络中。

14、内核得到这个ip包,通知指定的应用程序响应。

15、Firefox收到了回应。

关键需要解决的是封装ip包和解除封装

二、要解决的问题

2.1如何捕获与发送icmp包

socket的RAW模式即原始套接字

2.2如何不影响vps上正常的ping回应

给icmp里的code字段设置一个固定值,默认是0,这个值可以随便设置。例如86。这样我们只捕获与发送code值为86的icmp数据包。跟普通的ping区别开来,互不影响。

同时,避免vps的内核回应我们的icmp包。添加下面的iptables规则。使用到--icmp-type type/code选项。type的值中,8是ping请求,0是ping响应,所以只针对响应包屏蔽。但是为了让服务器端的tunnel程序的icmp能发出去,服务器端在发送的时候,可以把code+1,也就变为87,发送出去。

image.png

2.3 MTU问题

因为IP包被封装到ICMP里之后,体积肯定会变大,如果超出网络的MTU,内核就会用两个IP包来装。导致第一个IP包装满了,第二个IP包可能只有几十个字节。十分浪费。为了避免这种现象,可以设置虚拟网卡的mtu为1000或更少

2.4 Python处理ICMP

Python 的 stdlib 没有任何用于支持 ICMP 数据包的包含库,,但ICMP数据包是常见和有用的:它们既用于跟踪路线,也用于平公用设施。因此,它们可用于控制网络诊断。

数据包类是用于创建和读取ICMP数据包的内容。要创建一个数据包,请将类瞬间化,设置头和数据字段,然后调用创建()方法,该方法将字符串表示可以传递到插座。

要读取数据包,请使用"parse()类模组",该分类将返回已填写字段的包实例。

为了显示其使用,您还可以看到包含的 ping()方法。只需将代码用作脚本,并将地址传递给ping。

image.png

三、运行

Kali运行vpn

image.png

ip route show

image.png

乌班图运行vpn

image.png

成功

image.png

到目前为止,已经在两台机器之间通过icmp建立了点对点隧道

构建vpn

先在kali服务器端设置NAT

iptables -t nat -A POSTROUTING -s 10.1.2.0/24 -j SNAT --to-source 192.168.111.130

image.png

再在本地设置路由表,让默认网关为新创建的t0

image.png

成功建立vpn

image.png

四、实现的功能

1)支持多人同时使用这个VPN。每个客户端通过外网IP与ICMP里的ID的组合来决定。所以,即使多个使用者在同一个局域网下,也不会相互使用。参见代码中key的计算。

2)使用密码登录以限制他人访问。初次连接服务器要求密码才能使用该VPN。默认为10分钟收不到来自客户端的数据包就删除会话。

3)服务器端和客户端共用一个tunnel程序。通过参数来指定工作模式。

附录

#!/usr/bin/env python

import os, sys

import hashlib

import getopt

import fcntl

import icmp

import time

import struct

import socket, select

SHARED\_PASSWORD = hashlib.md5("password").digest()

TUNSETIFF = 0x400454ca

IFF\_TUN   = 0x0001

MODE = 0

DEBUG = 0

PORT = 0

IFACE\_IP = "10.0.0.1"

MTU = 1500

CODE = 86

TIMEOUT = 60\*10 # seconds

class Tunnel():

  def create(self):

    self.tfd = os.open("/dev/net/tun", os.O\_RDWR)

    ifs = fcntl.ioctl(self.tfd, TUNSETIFF, struct.pack("16sH", "t%d", IFF\_TUN))

    self.tname = ifs\[:16\].strip("\\x00")

  def close(self):

    os.close(self.tfd)

  def config(self, ip):

    os.system("ip link set %s up" % (self.tname))

    os.system("ip link set %s mtu 1000" % (self.tname))

    os.system("ip addr add %s dev %s" % (ip, self.tname))

  def run(self):

    self.icmpfd = socket.socket(socket.AF\_INET, socket.SOCK\_RAW, socket.getprotobyname("icmp"))

    self.clients = {}

    packet = icmp.ICMPPacket()

    self.client\_seqno = 1

    while True:

      rset = select.select(\[self.icmpfd, self.tfd\], \[\], \[\])\[0\]

      for r in rset:

        if r == self.tfd:

          if DEBUG: os.write(1, ">")

          data = os.read(self.tfd, MTU)

          if MODE == 1: # Server

            for key in self.clients:

              buf = packet.create(0, CODE+1, self.clients\[key\]\["id"\], self.clients\[key\]\["seqno"\], data)

              self.clients\[key\]\["seqno"\] += 1

              self.icmpfd.sendto(buf, (self.clients\[key\]\["ip"\], 22))

            # Remove timeout clients

            curTime = time.time()

            for key in self.clients.keys():

              if curTime - self.clients\[key\]\["aliveTime"\] > TIMEOUT:

                print "Remove timeout client", self.clients\[key\]\["ip"\]

                del self.clients\[key\]

          else: # Client

            buf = packet.create(8, CODE, PORT, self.client\_seqno, data)

            self.client\_seqno += 1

            self.icmpfd.sendto(buf, (IP, 22))

        elif r == self.icmpfd:

          if DEBUG: os.write(1, "<")

          buf = self.icmpfd.recv(icmp.BUFFER\_SIZE)

          data = packet.parse(buf, DEBUG)

          ip = socket.inet\_ntoa(packet.src)

          if packet.code in (CODE, CODE+1):

            if MODE == 1: # Server

              key = struct.pack("4sH", packet.src, packet.id)

              if key not in self.clients:

                # New client comes

                if data == SHARED\_PASSWORD:

                  self.clients\[key\] = {"aliveTime": time.time(),

                            "ip": ip,

                            "id": packet.id,

                            "seqno": packet.seqno}

                  print "New Client from %s:%d" % (ip, packet.id)

                else:

                  print "Wrong password from %s:%d" % (ip, packet.id)

                  buf = packet.create(0, CODE+1, packet.id, packet.seqno, "PASSWORD"\*10)

                  self.icmpfd.sendto(buf, (ip, 22))

              else:

                # Simply write the packet to local or forward them to other clients ???

                os.write(self.tfd, data)

                self.clients\[key\]\["aliveTime"\] = time.time()

            else: # Client

              if data.startswith("PASSWORD"):

                # Do login

                buf = packet.create(8, CODE, packet.id, self.client\_seqno, SHARED\_PASSWORD)

                self.client\_seqno += 1

                self.icmpfd.sendto(buf, (ip, 22))

              else:

                os.write(self.tfd, data)

def usage(status = 0):

  print "Usage: icmptun \[-s code|-c serverip,code,id\] \[-hd\] \[-l localip\]"

  sys.exit(status)

if \_\_name\_\_=="\_\_main\_\_":

  opts = getopt.getopt(sys.argv\[1:\],"s:c:l:hd")

  for opt,optarg in opts\[0\]:

    if opt == "-h":

      usage()

    elif opt == "-d":

      DEBUG += 1

    elif opt == "-s":

      MODE = 1

      CODE = int(optarg)

    elif opt == "-c":

      MODE = 2

      IP,CODE,PORT = optarg.split(",")

      CODE = int(CODE)

      PORT = int(PORT)

    elif opt == "-l":

      IFACE\_IP = optarg

  if MODE == 0 or CODE == 0:

    usage(1)

  tun = Tunnel()

  tun.create()

  print "Allocated interface %s" % (tun.tname)

  tun.config(IFACE\_IP)

  try:

    tun.run()

  except KeyboardInterrupt:

    tun.close()

sys.exit(0)

icmp.py

#!/usr/bin/env python

import socket

import binascii

import struct

import ctypes

BUFFER\_SIZE = 8192

class IPPacket():

    def \_checksum(self, data):

        if len(data) % 2:

            odd\_byte = ord(data\[-1\])

            data = data\[:-1\]

        else:

            odd\_byte = 0

        words = struct.unpack("!%sH" %(len(data)/2), data)

        total = 0

        for word in words:

            total += word

        else:

            total += odd\_byte

        total = (total>>16) + (total & 0xffff)

        total += total>>16

        return ctypes.c\_ushort(~total).value

    def parse(self, buf, debug = True):

        self.ttl, self.proto, self.chksum = struct.unpack("!BBH", buf\[8:12\])

        self.src, self.dst = buf\[12:16\], buf\[16:20\]

        if debug:

            print "parse IP ttl=", self.ttl, "proto=", self.proto, "src=", socket.inet\_ntoa(self.src), \\

                "dst=", socket.inet\_ntoa(self.dst)

class ICMPPacket(IPPacket):

    def parse(self, buf, debug = True):

        IPPacket.parse(self, buf, debug)

        self.type, self.code, self.chksum, self.id, self.seqno = struct.unpack("!BBHHH", buf\[20:28\])

        if debug:

            print "parse ICMP type=", self.type, "code=", self.code, "id=", self.id, "seqno=", self.seqno

        return buf\[28:\]

    def create(self, type\_, code, id\_, seqno, data):

        packfmt = "!BBHHH%ss" % (len(data))

        args = \[type\_, code, 0, id\_, seqno, data\]

        args\[2\] = IPPacket.\_checksum(self, struct.pack(packfmt, \*args))

        return struct.pack(packfmt, \*args)