在Unix世界中,可以很容易地给予信任。 假设用户在机器A和机器B上都有一个帐户,为了使两者之间的麻烦最小,就可以在它们之间建立全双工的信任关系。 在A的主目录中,创建一个 .rhosts 文件:
echo "hostB 用户名" > .rhosts
,在B的主目录中做同样的操作,现在,用户可以使用任何 r* 命令来操作另一台服务器,这些命令将只验证地址。
1994年圣诞节,Kevin Mitnick 攻击了下村勉的机器,主要用到了两种技术:IP源地址欺骗和TCP序列号预测
IP源地址欺骗:ip协议并不安全,其源地址可被随意修改,再配合另外一些操作就可以冒充其他主机与目的主机通信
TCP序列号预测:每个TCP报文都会带一个序列号,在数据传输过程中用于对报文段的确认,而在三次握手时需要序号来完成连接。TCP序列号是由网络协议栈生成,通过多次发送SYN包,从收到的SYN+ACK包中的序列号可以预测出对方序列号生成的规律(已被修复,现在初始序列号生成是随机的),这个操作必须在对方主机不活跃时进行,否则其他的TCP连接会扰乱这种规律,这也是为什么选在圣诞节攻击的原因。
这次攻击主要涉及四个对象:
首先来捋一遍攻击思路:
apollo 需要获得对 target 的访问,此时 x-terminal 已有对 target 的受信任连接,攻击者的目的是劫持这个连接。劫持这个连接,需要先控制 x-terminal,如何控制x-terminal呢?需要被它信任,如何被它信任?需要修改 .rhosts 文件,如何远程修改.rhosts文件呢?需要以它信任的机器的身份,建立一个TCP连接,在TCP连接中传递命令,所以最终是要冒充 server 与 x-terminal 建立连接
开始攻击:
1、为什么说ip传输协议是不可靠的?
2、详细描述一下TCP三次握手
3、port的作用是什么?
4、SYN/ACK/RST分别在什么时候使用?
5、进行 SYN flooding 以实现 DoS 的可信server的端口号是多少?为什么要选择这个端口?能不能换用一个其他端口?
6、在成功添加后门后,攻击者为何要发送RST释放可信Server中的TCP连接?
1、route(可加 -n 不解析名字而直接显示ip)
2、ping:发送 icmp 包来测试可达性
3、traceroute(路由跟踪)
4、nslookup:域名查询
5、whois
6、netstat
ARP用于通过目标机 ip 获取目标机 MAC 地址
arp 请求封装在数据链路层报文中:
在发送 arp 请求时,以太网首部的目的地址为全1,表示广播,而 arp 报文部分的目的mac为全0,因为还不知道目的主机的mac地址,正要通过以太网广播询问
有一种ARP包叫做 Gratuition ARP(无故ARP或者免费ARP),作用有:
每一台主机维护了一张 ARP 缓存表,其中存储着
在向外发包时,可以直接通过 ARP 缓存来获取目的 IP 和目的 MAC 的映射关系,而不用通过ARP协议来获取
向目标主机发送 ARP 响应报文,效果是,更新目标机器的 ARP 缓存,目标机器发送数据包到指定 ip 时,该包会被发送到我们指定的机器上
一个 IP 报文最长可以达到 65535 字节,但网络硬件限制了帧的大小,以太网限制为 1500 字节
IP分片中有 id 字段和 offset 以及 flag 字段,id 相同表示属于同一个IP报文,offset 则表示某分片在原 IP 报文中的偏移量,flag 则有是否分片、更多分片等标志位
可以使用微小碎片攻击,因为防火墙过滤包时,只会将过滤规则应用于初始片段,STD 5,RFC 791规定,每个互联网模块能够转发68B的最小数据报,不允许进一步分段,而IP首部长度最长可达到60B,这样,第一个片段只包含8B的内容,在TCP的情况下,这足以包含源和目标端口号,但它将强制TCP标志字段进入第二个片段,从而使得SYN包绕过过滤规则
重叠碎片攻击,即第一个片段标志位 SYN=0,ACK=1 且足够长,此时可以顺利通过过滤,第二个片段与第一个片段有重叠,且重叠部分含标志位为 SYN=1,如果主机使用后面覆盖前面的算法,此时就成功绕过
ICMP是网际报文控制协议,主要是因为IP协议有可能出现报文发送过程中的错误。譬如目标不可达,TTL过期等,需要通知发送方错误原因
有多种不同的ICMP报文,每种报文都有自己的格式,但是所有的ICMP报文都有三个共同的字段:type,code,checksum,不同类型的 icmp 报文通过 type 和 code 来区分:
一些常见的 icmp 报文:
其中,type=5 的重定向报文,是路由器用来向主机通知网络拓扑的变化,在以下情况下, 路由器会向主机发送重定向报文:
在上面的情况中,主机的IP地址是10.0.0.100,主机的路由表中默认网关是10.0.0.1,现在主机100要向 网络X 发送数据包。根据自己的默认网关,100将数据发给了G1。G1检查自己的路由表,发现要到达 网络X,需要经过G2,G1会将数据包转发给G2,同时发现数据包的源地址 10.0.0.100 和 G2 在同一个网段上,这时 G1 会向 Host 发送重定向报文,告诉它下次直接发给 G2 会快一点
主机总会假设路由器的信息更权威,路由器总是对的,所以它会更新自己的路由表,但无论是 icmp 报文还是封装它的 ip 报文都是可以伪造的,这就出现了安全问题
一种分布式拒绝服务攻击,冒充目标机 IP,向网络中广播 ICMP回送请求报文,那么目标机器就会收到大量的 ICMP回送应答报文,这使得受害者忙于处理 ping 包而无法正常工作
可以用以下方式防止攻击:
在同一个局域网下,攻击机 A 伪造成目标机的网关 B,对目标机器发送重定向报文,使得目标机器报文被重定向到指定主机上,从而可以达到无法上网或劫持流量的目的
可以不允许重定向来防止这样的攻击
主机会维持一个 TCP 半开连接队列,该队列大小有限,一旦满了,主机就会丢弃其他的 TCP 连接报文
那么 SYN flooding 攻击的本质就是用大量的虚假 IP,对目标机器发送 SYN 报文,使得对方的连接队列被占满,从而使得目标机无法处理新的TCP连接
防止:设置tcp_syncookie=1(这也是系统默认值)。在TCP三次握手过程中,在ACK报文未到达之前,主机不为此次连接分配内存空间,而用序列号识别三次握手
使用 nmap 扫描目标地址,它会首先一个个地尝试与一些端口的连接,如果端口有响应,并且能够建立起连接,那么nmap就能够判断得出来,这个端口是open的
nmap -sT <ip> # connect扫描, 发送SYN报文,若对方回复SYN+ACK,则会回复ACK建立连接
nmap -sS <ip> # 半开扫描,发送SYN报文,若对方回复SYN+ACK,则会回复RST中断连接, 需要sudo权限
向目标机发送 ACK 报文,若端口被过滤,则不会有任何响应,否则,无论端口是开放还是关闭,均会返回RST报文,可以检测端口是否被过滤
配合 nmap -sF 扫描(发送 FIN 报文,端口开放或被过滤不会有响应,端口关闭会收到 RST)就可以准确知道端口状态
通过僵尸主机(zombie)查看目标机器端口状态,zombie 需要满足攻击期间不会自己向外发送报文,且最好能与靶机具有信任关系
防火墙,置于内部网络和外部网络之间,内部网络与外部网络通信的必经之地
包过滤防火墙
也被成为无状态防火墙,静态防火墙。通过ACL规则控制数据流,最核心的五元素为:源IP,目的IP,源端口,目的端口,协议,在一次会话中,这五个元素是不会变化的,所以也叫静态防火墙
**优点:**实现简单,处理速度快
**缺点:**难以处理分片,不支持复杂协议,不能防止应用层的恶意攻击(因为它无法检测应用层数据)
应用级网关代理防火墙
客户端与服务器并不建立真正的连接,都是客户端通过访问代理服务器来访问服务器,通信时,数据包均要通过代理服务器的检测
**优点:**对数据的控制上升到应用层
**缺点:**每个服务均要实现一个特定的安全代理服务
状态防火墙
会话过程中的数据包并不独立,而是有前后连接状态的。在同一个会话中,五元组不会变化,但是各种状态标识、分片等都是可以变化的
例如:TCP三次握手的过程,若是突然收到一个ACK报文,那么该报文需要丢弃
网络地址转换,使得一个整体机构以一个公用 IP 地址出现在Internet上。因为IPv4地址不够用,才产生了NAT,即内网中的主机想要访问互联网时,会经过NAT路由器将源地址转化为公网ip后进行访问
正向代理
正向代理代理客户端,主要作用可以是kxsw之类。
反向代理
反向代理代理服务器,主要为了防止服务器的ip地址直接暴露,同时也可以在反向代理上实现请求过滤,在内网安全领域起到很大的作用
iptables/netfilter 是Linux内置的防火墙,通过一些表和规则链过滤包,每张表拥有的规则链数目不同,包进来时按顺序通过每张表上的某条链:
我们可以为每条链指定规则,常用参数有:
基本链管理:
-L, --list [chain]:列出规则;
-F, --flush [chain]:清空指定的规则链上的规则;
-P, --policy [chain] target 制定链表的策略 (ACCEPT|DROP|REJECT)
基本规则管理:
# 使用 -t 指定表,默认为 filter 表
# 指定 INPUT 链丢弃所有包
$ sudo iptables -P INPUT DROP
# 在 INPUT 链上添加规则,源端口为80的tcp报文可以进来
$ sudo iptables -A INPUT -p tcp --sport 80 -j ACCEPT
DAC
自主访问控制,即文件拥有者能够决定文件的访问权限
MAC
强制访问控制,在DAC之上构建的可选模块,DAC 验证通过且系统支持的话会验证 MAC
真实UID:当前登录的 uid
有效UID:当前进程以哪个用户ID来运行的
保存的UID:有效用户ID的一个副本,与SUID权限有关
SUID权限:若一个文件有SUID权限,那么若某用户对它有执行权限,那么执行时就以root权限执行
setuid(uid)
详解
ruid:
真实uid
euid:
有效uid
suid:
saveduid
- 用户具有超级用户权限的时候,
setuid(uid)
可将本进程的三个uid均置为uid
- 否则,仅当该 id为 ruid 或者 suid 时,该 id 对 euid 起效
- 若不满足上述情况,报错
seteuid(uid)
seteuid(uid)
只请求将euid
置为uid
举例:
若以 user身份登录系统并打开shell,然后在shell中打开一个文件,这个文件有s位(setuid),那么此时
ruid
为 user 的uid
,euid
应该为文件拥有者的 ruid
Rootkit中的root来自于unix。在unix主机系统管理员账号为root账号,该账号拥有最小的安全限制,完全控制主机并拥有了管理员权限被称为“root”了这台主机。然而,能够暂时性的控制住主机还不够,因为管理员可以采取一定的安全措施,譬如打补丁。因此Rootkit的作用在于“能维持root权限的一套工具”。它的目的是隐藏自己以及恶意程序,达到长期在目的主机存在并收集信息的目的。
系统调用过程
- 应用程序代码调用系统调用函数 xyz,该函数为一个包装系统调用的库函数
- 库函数xyz负责准备向内核传递的参数(系统调用号),并触发软中断切换到内核态
- 系统调用通过软中断80进入内核态,在 IDT(中段描述符表)中找到 80 对应的中断处理程序,即系统调用处理函数
- 系统调用处理函数通过传进来的系统调用号来调用系统调用服务例程。
那么在上述过程中,linux内核空间中存储着一张表叫做 系统调用表 (system_call_table),该表存储着 系统调用号对应的系统调用服务例程的地址
劫持过程
根据上述过程,那么如何劫持系统调用就很清晰明了。我们要做的就是修改系统调用表,使目标系统调用号对应的系统调用服务例程变为我们自己写的函数即可。
- 找到系统调用表(SCT)的首地址
- 修改SCT中目标系统调用服务例程的地址 (修改时需要关闭写保护,写完需要恢复写保护)
如何寻找SCT地址?
- 在旧版本的linux内核中,SCT是直接被导出的,也就是符号表中是存储了SCT符号的,这样内核程序就可以直接使用。
- 在2.6版本内核后,SCT就不再被直接导出了,于是另外一种查找SCT的方法是通过
/boot/System.map
来查找- 然而在更高版本中,
System.map
也没有了。这时可以通过/proc/kallsyms
来查询,这个文件中保存了所有linux内核符号。
虚拟文件系统(VFS)是这样一种特性的关键所在。它是 Linux 内核中的一个软件层,用于给用户空间的程序提供文件系统接口,同时,它也提供了内核中的一个抽象功能,允许不同的文件系统共存。
为了能够支持各种实际文件系统,VFS 定义了所有文件系统都支持的基本的、概念上的接口和数据 结构;同时实际文件系统也提供 VFS 所期望的抽象接口和数据结构,将自身的诸如文件、目录等概念在形式 上与VFS的定义保持一致。换句话说,一个实际的文件系统想要被 Linux 支持,就必须提供一个符合VFS标准 的接口,才能与 VFS 协同工作
因此,VFS 的意义如下:
因为计算机程序频繁地需要操作从文件、网络等各种来源读入的一段数据,因此程序经常需要分配一段有限大小的内存空间,以便将数据存储在其中。这段内存空间便成为缓冲区。缓冲区溢出就是写入到缓冲区或者从缓冲区读取的数据超出了缓冲区可以容纳的范围。
缓冲区溢出包括各种类型,如栈溢出、堆溢出等。我们主要关注的类型是栈缓冲区溢出。
当在一个函数中调用另一个函数,被调用的函数结束时需要返回原函数,接着执行调用语句的下一条指令,那么下一条指令的地址就是被调用函数的返回地址
通过修改指令指针寄存器eip实现跳转,用于实现子程序,具体来说,调用 call 时:
与之相配合的还有一个 ret 指令,用于在子程序中返回,调用时:
首先,把函数调用的参数压栈,然后eip(返回地址)压栈,ebp(栈底)压栈。接下来,更新ebp的值为esp的值(栈对齐),将esp减少一个特定的值(与调用函数内部申请空间相关),为调用函数获取一定的栈空间。
字符串存储时是从低地址向高地址增长,那么缓冲区溢出攻击的主要思路就是:将shellcode(生成shell的代码,执行它可以得到一个shell)置于内存的某处,然后通过缓冲区溢出覆盖将函数的返回地址指向shellcode,防范方法有: