先来一段itchat的官方介绍吧
itchat是一个开源的微信个人号接口,使用python调用微信从未如此简单。
使用不到三十行的代码,你就可以完成一个能够处理所有信息的微信机器人。
当然,该api的使用远不止一个机器人,更多的功能等着你来发现,比如这些。
该接口与公众号接口itchatmp共享类似的操作方式,学习一次掌握两个工具。
如今微信已经成为了个人社交的很大一部分,希望这个项目能够帮助你扩展你的个人的微信号、方便自己的生活。
实际上,itchat是对微信网页端的爬虫,所以,网页端可以实现的功能都有,那么,我想要的定时群发微信消息,自然不在话下!
安装
1 | pip install itchat |
一个简单实例:实现给文件传输助手发送消息
1 | import itchat |
这个的实现需要注册msg_register,逻辑很简单,当收到指定群里的指定消息时,将消息转发到另一个群。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40import itchat
from datetime import datetime
import time
import re
import threading
from itchat.content import TEXT
from itchat.content import *
from apscheduler.schedulers.blocking import BlockingScheduler
@itchat.msg_register([TEXT], isFriendChat=True, isGroupChat=True, isMpChat=True)
def getContent(msg):
global g_msg
groups = itchat.get_chatrooms(update = True)
for g in groups:
#print(g['NickName'])
if g['NickName'] == '被转发的群名':
from_group = g['UserName']
if '每日安全简讯' in msg['Content']:
print("get message from " + msg['FromUserName'])
if msg['FromUserName'] == from_group:
g_msg = msg['Content']
print('成功获得群消息,等待转发')
print(int(time.strftime("%H%M%S")))
while(1):
if int(time.strftime("%H%M%S")) > 80000:
SendMessage(g_msg,'发送的对象群名')
g_msg = ''
break
def SendMessage(context,gname):
itchat.get_chatrooms(update = True)
users = itchat.search_chatrooms(name=gname)
userName = users[0]['UserName']
itchat.send_msg(context,toUserName=userName)
print("\n发送时间: " + datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "\n" "发送到:" + gname + "\n" + "发送内容:" + context + "\n")
print("*********************************************************************************")
if __name__ == '__main__':
itchat.auto_login(hotReload=True,enableCmdQR=2)
itchat.run(blockThread=False)
据说每三十分钟发送一次消息可防止网页端微信掉线~~1
2
3
4
5
6
7
8
9
10
11
12def loop_send():
nowTime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
context = '现在是北京时间 :\n'+ nowTime +'\n\n我们还活着'
itchat.get_chatrooms(update = True)
users = itchat.search_friends(name=u'chengkun')
userName = users[0]['UserName']
itchat.send_msg(context,toUserName=userName)
if __name__ == '__main__':
sched = BlockingScheduler()
sched.add_job(loop_send,'interval',minutes=30)
sched.start()
我是在腾讯云有个服务器,因为自己的电脑不可能时时刻刻开机,所以就放在服务器上,方法是:1
sudo nohup python -u auto_Send.py >> auto_Send.log 2>&1 &
这里有两个线程,一个是定时转发,一个是循环发送,因此要设置为itchat.run(blockThread=False)以及sched = BlockingScheduler()否则会卡在某个方法。
这是因为users = itchat.search_chatrooms(name=gname),在搜索的是你保存到通讯录的群组。
itchat.auto_login(hotReload=True,enableCmdQR=2),需要设置为2
]]>小米自己改了个打包解包固件的工具,基于 trx 改的(本质上还是 trx 格式),加了 RSA 验证和解包功能,路由系统里自带:1
2
3
4Usage:
mkxqimg [-o outfile] [-p private_key] [-f file] [-f file [-f file [-f file ]]]
[-x file]
[-I]
固件工具mkxqimage完成对固件的解包,在解包前先检查Checksum是否正确,然后利用RSA公钥/usr/share/xiaoqiang/public.pem检查RSA签名,这两个步骤通过后,根据[0x0C]的固件类型,以及[0x10]、[0x14]、[0x18]和[0x1C]的4个偏移量拆分固件。
小米官方在打包固件时用RSA私钥计算出固件的RSA签名,小米路由器下载固件后用RSA公钥来验证RSA签名,有效地防止固件被篡改。
路由固件的格式,基本是基于 openwrt 的 trx 这个简单的二进制文件格式1
2
348 44 52 30 63 D4 11 03 FE 3D 1A FD 05 00 02 00
20 00 00 00 20 00 FE 00 00 00 00 00 00 00 00 00
FF 04 00 EA 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5
第1~4字节:ASCII字符串“HDR0”,作为固件的标识;
第5~8字节:4字节整型数0x0311D464,表示固件的大小:51500132字节;
第9~12字节:固件的检查和;
第13~14字节:0x0005,表示固件中包含哪些部分;
第15~16字节:0x0002,表示固件格式版本号;
第17~20字节:0x00000020,表示固件第一部分在整个固件中的偏移量,0.4.85固件的第一部分是brcm4709_nor.bin,也就是Flash中除0xfe0000-0xff0000的board_data外的全镜像;
第21~24字节:0x00FE0020,表示固件第二部分在整个固件中的偏移量,0.4.85固件的第二部分是root.ext4.lzma,也就是硬盘中128M固件的压缩包;
第33字节开始是固件的正式内容开始。
使用mkxqimage解包
(现在会提示秘钥不存在)1
2error fopen public key
Image verify failed, not formal image
如果能解包应该可以得到脚本文件upsetting.sh
1 | #!/bin/sh |
执行脚本文件upsetting.sh后,将ssh_en设置为1,同时设置了flag_init_root_pwd项。当正式启动时,/usr/sbin/boot_check脚本检测到flag_init_root_pwd=1时,自动修改root用户密码,具体脚本为:1
2
3
4
5
6
7flg_init_pwd=`nvram get flag_init_root_pwd`
if [ "$flg_init_pwd" = "1" ]; then
init_pwd=`mkxqimage -I`
(echo $init_pwd; sleep 1; echo $init_pwd) | passwd root
nvram unset flag_init_root_pwd
nvram commit
fi
初始密码是mkxqimage -I的结果,实际是根据路由器的序列号计算得到。路由器的序列号印在底盖上,12位数字,如:561000088888
初始密码计算算法为:
substr(md5(SN+"A2E371B0-B34B-48A5-8C40-A7133F3B5D88"), 0, 8)
A2E371B0-B34B-48A5-8C40-A7133F3B5D88 为分析mkxqimage得到的salt
]]>DENGTA_META.xml—IMEI:867179032952446
databases/2685371834.db——数据库文件
明文msg_t 密文msg_Data key:IMEI
msg_t = msg_Data[i]^IMEI[i%15]
1 | import sqlite3 |
论文来源:USENIX SECURITY 2018:Off-Path TCP Exploit: How Wireless Routers Can Jeopardize Your Secrets
下载:
原文pdf
中文slides
香农信息论
什么是信息? 用来减少随机不确定的东西
什么是加密? 类似于加噪声,增加随机不确定性
“从密码分析者来看,一个保密系统几乎就是一个通信系统。待传的消息是统计事件,加密所用的密钥按概率选出,加密结果为密报,这是分析者可以利用的,类似于受扰信号。”
侧信道随之出现 越过加密算法增加的随机不定性,从其他的渠道获取数据标签,确定信息内容。
侧信道攻击的流程 第一个就是侧信道泄露的截取,第二个是信息的恢复。
“指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。”
通信线路之外,攻击者看不到双方的消息,没办法截获和发送通信包。智能伪造成一方给另一方发消息。
- 客户端通过向服务器端发送一个SYN来创建一个主动打开,作为三路握手的一部分。客户端把这段连接的序号设定为随机数A。
- 服务器端应当为一个合法的SYN回送一个SYN/ACK。ACK的确认码应为A+1,SYN/ACK包本身又有一个随机产生的序号B。
- 最后,客户端再发送一个ACK。当服务端收到这个ACK的时候,就完成了三路握手,并进入了连接创建状态。此时包的序号被设定为收到的确认号A+1,而响应号则为B+1。
通过三次握手,确定对方不是非中间人
TCP序列号的问题
1985 | 1995 | 2001 | 2004 | 2007 | 2012 | 2012 | 2016 |
---|---|---|---|---|---|---|---|
Morris | Mitnik | Zalewsky | Waston | kLM | Herzberg | 作者 | 作者 |
初始序列可预测 | 真实利用 | 漏洞仍在 | BGP DoS | Windows攻击 | Puppet-assisted | Malware-assisted | off-path attack |
攻击模型:
给受害者安装一个无特权的应用程序(仅能网络连接),这个程序跟非中间人的攻击者里应外合,劫持手机上所有的TCP连接。
如何劫持TCP
需要的信息:Facebook的连接IP地址和端口号,由此可以知道TCP连接的序列号,利用序列号伪装成Facebook给手机发消息。
使用netstat命令获取:
任务:由于TCP的序列号通常连续,所以要精确猜到它的下一个序列号。
攻击过程: TCP三次握手之后产生A和B,将来传输的包序列号必须跟A和B很接近,否则,防火墙会丢弃这个包。因此只有猜对了序列号,包才能到达手机端。到达手机端后,后台的恶意软件可以帮助我们判断手机是否接受了这个数据包。
具体侧信道方案: CPU资源使用率(噪音很大)——>TCP计数器(后台软件运行制造噪音)——>低噪音计数器:包被丢掉时,一个相应的错误计数器。
解决方法: 关闭防火墙检查序列号的功能
具体侧信道方案:跟TCP业务逻辑有关的计数器——收到的TCP包序列号小于期望时增加,大于时不变。二分查找搜索正确的序列号。
影响范围:Android、Linux、MacOS、FreeBSD
不植入恶意软件,劫持任意两台机器的TCP连接:首先确定是否建立TCP连接,然后推测其序列号A和B。
USENIX 2016 : Off-Path TCP Exploits: Global Rate Limit Considered Dangerous
侧信道: 所有的侧信道,本质上就是攻击者和受害者之间共享着某些资源,如之前的全局TCP计数器。这里使用的侧信道是 服务器上 的共享资源,限速器(RFC 5961)限制某一种包的发送速率(默认100p/s)
如何利用共享限速器:
先判断是否建立了连接。然后伪造TCP包,需要猜测源端口,如果猜测正确,服务器会返回一个challenge,攻击者不断触发,一共可以收到99个(还有一个发给了客户端);如果猜测错误,则一共可以收到100个challenge。
评估: 是否建立了连接:<10s ; Seq:30s ; ACK:<10s
解决方案: 1. 加噪音,100变成150、200;2. 限速器做成局部的
USENIX 2018 : Off-Path TCP Exploit: How Wireless Routers
Can Jeopardize Your Secrets
之前的漏洞无论是计数器还是限速器都属于软件,很好更正,但这篇文章的漏洞利用无法修复。
TCP收包的原理: 通常TCP收包要看这个包是否匹配了当前的某一个连接。如果连接匹配上了,就会去看这个包的序列号;如果序列号不对,会触发一个回复,说明这个序列号存在问题;如果序列号正确,但反向序列号不对,也会丢包。当连接匹配、序列号和反向序列号正确时,就会返回一个数据包。
侧信道: 攻击者伪装成服务器给客户端发包,正确的序列号会有回复,错误则没有。但回复时发送给服务器的,有没有回复攻击者并不知道。那么如何去判断有没有回复,利用无线网络的 半双工 传输。
让有回包和没有回包的时间差异放大。
判断流程: 客户端和路由器之间wifi通信。攻击者依次发送三个数据包,第一个包用来测试正常的RTT。第2个包是伪装成服务器发送的,如果第2个包猜对了,客户端会向服务器返回数据包,这会导致占用更长时间的wifi信道,从而会使第3个包的RTT更长。
评估: 在本地环境下,如果发送40个包,就有20ms的RTT差别。
攻击应用:
1. 攻击模型: 受害者访问了我们的钓鱼网站,这时javascript(傀儡)会在后台执行,主动建立到攻击者的连接(规避NAT或防火墙造成的不可抵达问题),这时攻击者就可以从外网测试RTT。
与理想情况的不同:客户端通常在NAT或防火墙之后;操作系统不一定严格遵守TCP收包的原则
Attacker -------wire----------| Router ---------wireless-------Victim (client)Server -------wire----------|
2. 攻击目标: 推断出客户端和服务器是否建立了连接;合计连接中交换的字节数或强制中断连接;注入恶意payload到连接(不失一般性的关注web缓存投毒)。前两个不需要傀儡初始化连接,第三个不一定需要,但攻击者控制了时序,能够简化攻击。
3. 攻击过程: 假设傀儡已经建立了连接,攻击者可以劫持并替换任何不加密的网站(如武汉大学),并在浏览器缓存。这是因为当浏览器请求相同的ip地址时,会复用之前的TCP连接。这意味着恶意网站中的傀儡可以通过重复HTML元素来建立到目标域名的单个持久连接。然后,路径外攻击者可以进行侧信道攻击,以推断目标连接中使用的端口号和序列号,然后注入虚假的http响应,并要求浏览器不重新检查对象的新鲜度,从而达到持续性的缓存投毒。
4. 细节:
TCP劫持: 通过劫持傀儡初始化的连接,可以简化web缓存投毒的过程。三个os在ACK验证上都不符合规范,所以各自处理情况也不同——windows:客户端必须持续发送请求以防止ACK接收窗口仅为一个字节,这要求攻击者必须能准确预期下一个序列号并解决大量流量带来的噪声。
因此,作者设计了一种新策略,该策略利用处理重叠数据的TCP行为和处理损坏的HTTP响应的浏览器行为——在Windows主机上缓冲的攻击者注入数据可能会破坏来自服务器的真实HTTP响应。 (1)注入,傀儡不断从服务器上请求脚本,而攻击者发送2^23/|wnd|个欺骗性数据包,这些包的窗口序列号与RCV.NXT加上偏移量相匹配,其中|wnd|为ack接收窗口大小,第i个数据包的ACK号为i*|wnd|,payload为
1 | websocket.send(|wnd|*i) |
因此,这些数据包中包含有效ACK号的一个包将被缓冲,并破坏真实的HTTP响应头。浏览器执行注入的脚本时,它将通过websocket发送猜测的ACK号,提供有效的窗口内ACK号。
(2)利用,由于客户端已经接受了额外的欺骗payload,推进了其预期的序列号,因此客户端和服务器实际上已经被去同步。攻击者现在可以简单地发送欺骗性响应(知道预期的序列号和有效的ACK号)。如果我们只想执行一次性注入,只需用恶意脚本替换第一步中的payload就足够了。
此外,针对Windows的注入步骤存在更加通用的替代策略,不依赖于浏览器行为。 具体来说,由于HTTP响应的前几个字节是可预先确定的(即HTTP),不破坏真实的响应,而是覆盖标题和正文以形成合法但恶意的响应。 在这种情况下,浏览器将完全忘记注入的存在。 这表明一旦序列号泄露,就存在各种方法来有效地将数据注入浏览器,而不用进行基于时间信道的慢得多的ACK号推断。
时序侧信道来自无线网络的半双工性质。由于无线协议中固有的冲突和回退,它被进一步放大。正如我们的测试路由器所证实的那样,现代无线路由器都支持CSMA / CA和RTS / CTS,因为它是802.11标准的一部分,并且该原则不太可能很快改变。
虽然作者只讨论威胁模型,其中来自受害客户端的连接是针对性的,但攻击实际上也适用于源自通过同一无线路由器连接的其他客户端的连接。这是因为所有这些客户端(例如,在相同NAT之后)共享了相同的冲突域并因此遭受相同的定时信道。通过探测数据包在任何客户端上触发的响应将有效地延迟探测后查询。在这种情况下,受害者连接(通过傀儡打开)只是为远程攻击者提供了测量碰撞的机会。
此外,我们可以扩展威胁模型以考虑无线连接的服务器,例如物联网设备。已经证明,通过公共IP地址和开放端口可以访问数百万个物联网设备。在这种情况下,可以针对此类IoT设备上的连接启动完全偏离路径的攻击。例如,计算在连接上交换的字节,终止与另一主机的连接,在正在进行的telnet连接上注入恶意命令。
这个漏洞触发的原因是,tcpdump在处理特殊的pcap包的时候,由于对数据包传输数据长度没有进行严格的控制,导致在连续读取数据包中内容超过一定长度后,会读取到无效的内存空间,从而导致拒绝服务的发生。对于这个漏洞,首先要对pcap包的结构进行一定的分析,才能够最后分析出漏洞的成因,下面对这个漏洞进行复现。
1 | 1.# apt-get install libpcap-dev |
1 | # Exploit Title: tcpdump 4.5.1 Access Violation Crash |
首先来分析一下pcap包的格式,首先是pcap文件头的内容,在.h有所定义,这里将结构体以及对应变量含义都列出来。1
2
3
4
5
6
7
8
9struct pcap_file_header {
bpf_u_int32 magic;
u_short version_major;
u_short version_minor;
bpf_int32 thiszone; /* gmt to local correction */
bpf_u_int32 sigfigs; /* accuracy of timestamps */
bpf_u_int32 snaplen; /* max length saved portion of each pkt */
bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */
};
看一下各字段的含义:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 magic: 4字节 pcap文件标识 目前为“d4 c3 b2 a1”
major: 2字节 主版本号 #define PCAP_VERSION_MAJOR 2
minor: 2字节 次版本号 #define PCAP_VERSION_MINOR 4
thiszone:4字节 时区修正 并未使用,目前全为0
sigfigs: 4字节 精确时间戳 并未使用,目前全为0
snaplen: 4字节 抓包最大长度 如果要抓全,设为0x0000ffff(65535),
tcpdump -s 0就是设置这个参数,缺省为68字节
linktype:4字节 链路类型 一般都是1:ethernet
struct pcap_pkthdr {
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
struct timeval {
long tv_sec; /* seconds (XXX should be time_t) */
suseconds_t tv_usec; /* and microseconds */
};
ts: 8字节 抓包时间 4字节表示秒数,4字节表示微秒数
caplen:4字节 保存下来的包长度(最多是snaplen,比如68字节)
len: 4字节 数据包的真实长度,如果文件中保存的不是完整数据包,可能比caplen大
其中len变量是值得关注的,因为在crash文件中,对应len变量的值为00 3C 9C 37
这是一个很大的值,读取出来就是379C3C00,数非常大,实际上在wireshark中打开这个crash文件,就会报错,会提示这个数据包的长度已经超过了范围,而换算出来的长度就是379C3C00,这是触发漏洞的关键。
首先通过gdb运行tcpdump,用-r参数打开poc生成的crash,tcp崩溃,到达漏洞触发位置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
361.Program received signal SIGSEGV, Segmentation fault.
2.[----------------------------------registers-----------------------------------]
3.EAX: 0x1
4.EBX: 0x81e33bd --> 0x0
5.ECX: 0x2e ('.')
6.EDX: 0x0
7.ESI: 0xbfffe201 ('.' <repeats 14 times>)
8.EDI: 0xbfffe1db --> 0x30303000 ('')
9.EBP: 0x10621
10.ESP: 0xbfffe1ac --> 0x8053caa (<hex_and_ascii_print_with_offset+170>: mov ecx,DWORD PTR [esp+0xc])
11.EIP: 0x8053c6a (<hex_and_ascii_print_with_offset+106>: movzx edx,BYTE PTR [ebx+ebp*2+0x1])
12.EFLAGS: 0x10296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
13.[-------------------------------------code-------------------------------------]
14. 0x8053c5d <hex_and_ascii_print_with_offset+93>: je 0x8053d40 <hex_and_ascii_print_with_offset+320>
15. 0x8053c63 <hex_and_ascii_print_with_offset+99>: mov ebx,DWORD PTR [esp+0x18]
16. 0x8053c67 <hex_and_ascii_print_with_offset+103>: sub esp,0x4
17.=> 0x8053c6a <hex_and_ascii_print_with_offset+106>: movzx edx,BYTE PTR [ebx+ebp*2+0x1]
18. 0x8053c6f <hex_and_ascii_print_with_offset+111>: movzx ecx,BYTE PTR [ebx+ebp*2]
19. 0x8053c73 <hex_and_ascii_print_with_offset+115>: push edx
20. 0x8053c74 <hex_and_ascii_print_with_offset+116>: mov ebx,edx
21. 0x8053c76 <hex_and_ascii_print_with_offset+118>: mov DWORD PTR [esp+0x18],edx
22.[------------------------------------stack-------------------------------------]
23.0000| 0xbfffe1ac --> 0x8053caa (<hex_and_ascii_print_with_offset+170>: mov ecx,DWORD PTR [esp+0xc])
24.0004| 0xbfffe1b0 --> 0xb7fff000 --> 0x23f3c
25.0008| 0xbfffe1b4 --> 0x1
26.0012| 0xbfffe1b8 --> 0x2f5967 ('gY/')
27.0016| 0xbfffe1bc --> 0x0
28.0020| 0xbfffe1c0 --> 0x0
29.0024| 0xbfffe1c4 --> 0x7ffffff9
30.0028| 0xbfffe1c8 --> 0x81e33bd --> 0x0
31.[------------------------------------------------------------------------------]
32.Legend: code, data, rodata, value
33.Stopped reason: SIGSEGV
34.hex_and_ascii_print_with_offset (ident=0x80c04af "\n\t", cp=0x8204000 <error: Cannot access memory at address 0x8204000>,
35. length=0xfffffff3, oset=0x20c40) at ./print-ascii.c:91
36.91 s2 = *cp++;
从崩溃信息来看,出错位置为s2 = cp++;崩溃原因为SIGSEGV,即进程执行了一段无效的内存引用或发生段错误。可以看到,问题出现在./print-ascii.c:91,而且此时指针读取[ebx+ebp2+0x1]的内容,可能是越界读取造成的崩溃。
再结合源码信息可知,指针cp在自加的过程中访问到了一个没有权限访问的地址,因为这是写在一个while循环里,也就是是说nshorts的值偏大,再看nshorts怎么来的,由此nshorts = length / sizeof(u_short);可知,可能是函数传入的参数length没有控制大小导致,因此目标就是追踪length是如何传入的。
我们通过bt回溯一下调用情况。1
2
3
4
5
6
7
8
9
10
11
12
131.gdb-peda$ bt
2.#0 hex_and_ascii_print_with_offset (ident=0x80c04af "\n\t", cp=0x8204000 <error: Cannot access memory at address 0x8204000>,
3. length=0xfffffff3, oset=0x20c40) at ./print-ascii.c:91
4.#1 0x08053e26 in hex_and_ascii_print (ident=0x80c04af "\n\t", cp=0x81e33bd "", length=0xfffffff3) at ./print-ascii.c:127
5.#2 0x08051e7d in ieee802_15_4_if_print (ndo=0x81e1320 <Gndo>, h=0xbfffe40c, p=<optimized out>) at ./print-802_15_4.c:180
6.#3 0x080a0aea in print_packet (user=0xbfffe4dc " \023\036\b\300\034\005\b\001", h=0xbfffe40c, sp=0x81e33a8 "@\377")
7. at ./tcpdump.c:1950
8.#4 0xb7fa3468 in ?? () from /usr/lib/i386-linux-gnu/libpcap.so.0.8
9.#5 0xb7f940e3 in pcap_loop () from /usr/lib/i386-linux-gnu/libpcap.so.0.8
10.#6 0x0804b3dd in main (argc=0x3, argv=0xbffff6c4) at ./tcpdump.c:1569
11.#7 0xb7de9637 in __libc_start_main (main=0x804a4c0 <main>, argc=0x3, argv=0xbffff6c4, init=0x80b1230 <__libc_csu_init>,
12. fini=0x80b1290 <__libc_csu_fini>, rtld_fini=0xb7fea880 <_dl_fini>, stack_end=0xbffff6bc) at ../csu/libc-start.c:291
13.#8 0x0804c245 in _start ()
函数调用流程1
2
3
4pcap_loop
|----print_packet
|-----hex_and_ascii_print
|-------- hex_and_ascii_print_with_offset
由此可见,从main函数开始了一连串函数调用,git源码下来看看。
tcpdump.c找到pcap_loop调用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
171. do {
2. status = pcap_loop(pd, cnt, callback, pcap_userdata);
3. if (WFileName == NULL) {
4. /*
5. * We're printing packets. Flush the printed output,
6. * so it doesn't get intermingled with error output.
7. */
8. if (status == -2) {
9. /*
10. * We got interrupted, so perhaps we didn't
11. * manage to finish a line we were printing.
12. * Print an extra newline, just in case.
13. */
14. putchar('n');
15. }
16. (void)fflush(stdout);
17. }
设置断点之后查看一下该函数的执行结果
pcap_loop通过callback指向print_packet,来看一下它的源码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
241.static void
2.print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
3.{
4. struct print_info *print_info;
5. u_int hdrlen;
6. ++packets_captured;
7. ++infodelay;
8. ts_print(&h->ts);
9. print_info = (struct print_info *)user;
10. /*
11. * Some printers want to check that they're not walking off the
12. * end of the packet.
13. * Rather than pass it all the way down, we set this global.
14. */
15. snapend = sp + h->caplen;
16. if(print_info->ndo_type) {
17. hdrlen = (*print_info->p.ndo_printer)(print_info->ndo, h, sp);<====
18. } else {
19. hdrlen = (*print_info->p.printer)(h, sp);
20. }
21. putchar('n');
22. --infodelay;
23. if (infoprint)
24. info(0);}
同样设置断点看该函数是如何调用执行的
这是我们可以根据call的信息,计算出调用的函数名
其中(*print_info->p.ndo_printer)(print_info->ndo,h,sp)指向ieee802_15_4_if_print
1 | 25.u_int |
传入的第二个值是struct pcap_pkthdr *h结构体,函数使用的参数caplen就是结构体中的caplen,不难看出,caplen进行一些加减操作后,没有判断正负,直接丢给了下一个函数使用。
直接跟进函数,看看最后赋值情况
从源码和调试信息可以看到libpcap在处理不正常包时不严谨,导致包的头长度hdrlen竟然大于捕获包长度caplen,并且在处理时又没有相关的判断。hdrlen和caplen都是非负整数,导致caplen==0xfffffff3过长。
继续跟进hex_and_asciii_print(ndo_default_print)
1 | 1.void |
但数据包数据没有这么长,导致了crash。
仔细分析之后发现,通过len判断的这个长度并没有进行控制,如果是自己构造的一个超长len的数据包,则会连续读取到不可估计的值。
通过查看epx的值来看一下这个内存到底开辟到什么位置1
2
3
41.gdb-peda$ x/10000000x 0x81e33bd
2.0x8203fdd: 0x00000000 0x00000000 0x00000000 0x00000000
3.0x8203fed: 0x00000000 0x00000000 0x00000000 0x00000000
4.0x8203ffd: Cannot access memory at address 0x8204000
可以看到,到达0x 8204000附近的时候,就是无法读取的无效地址了,那么初始值为0x 81e33bd,用两个值相减。0x 8204000-0x 81e33bd = 0x 20c40,因为ebx+ebp*2+0x1一次读取两个字节,那么循环计数器就要除以2,最后结果为0x 10620。
来看一下到达拒绝服务位置读取的长度:EBX: 0x81e33bd –> 0x0;EBP: 0x10621;
EBP刚好为10621。正是不可读取内存空间的地址,因此造成拒绝服务。
总结一下整个漏洞触发过程,首先tcpdump会读取恶意构造的pcap包,在构造pcap包的时候,设置一个超长的数据包长度,tcpdump会根据len的长度去读取保存在内存空间数据包的内容,当引用到不可读取内存位置时,会由于引用不可读指针,造成拒绝服务漏洞。
Libpcap依然是apt安装的默认版本,tcpdump使用4.7 .0-bp版本
在hex_and_ascii_print_with_offset中增加对caplength的判断1
2
3
4
5
6
7
8
91.caplength = (ndo->ndo_snapend >= cp) ? ndo->ndo_snapend - cp : 0;
2.if (length > caplength)
3. length = caplength;
4.nshorts = length / sizeof(u_short);
5.i = 0;
6.hsp = hexstuff; asp = asciistuff;
7.while (--nshorts >= 0) {
8. ...
9.}
可以看到执行完caplength = (ndo->ndo_snapend >= cp) ? ndo->ndo_snapend - cp : 0;,caplength为0,继续执行,可以推出length同样为0,到这里已经不会发生错误了。
所以,作者希望提出一种减少对数据集和人工依赖的注释方式。本文的方法主要基于两个事实,第一个Figure 1是制造商通常会将相关信息硬编码到IOT设备,第二个Figure 2是有许多网站(如产品测评)会描述设备产品。从第一个事实,我们可以从应用层数据包获取关键词,然后根据这些关键词依据第二个事实进行网页爬虫,以获取网页上的相关描述,然后对这些描述进行自然语言处理和数据挖掘,从而建立起基于规则的映射。
Rule Miner由三个部分构成,Transaction set是一对由应用层数据和相关网页组成的文本单元,它生成了一种规则: ,其中A是从应用层数据包中提取的一些特征,B是从相关网页抓取的设备描述;Device entity recognition结合了基于语料库的NER和基于规则的NER(命名实体识别),前者解决了设备类型和供应商名,后者使用正则表达式识别出产品型号。但是由于一个不相干的网页也可能包含设备类型的关键词(如switch),以及一个短语可能因为满足正则表达式而被认为是型号所以表现并不好,但好在实体与实体之间具有很高的依赖性,这三个元素常常一起出现。数据挖掘算法Apriori algorithm用于从Transaction中学习“关系”。
完整的ARE除了核心Rule Miner之外,还有Transaction Collection用于收集响应数据和网络爬虫,Rule Library用于存储生成的规则,Planner用于更新规则。
作者主要将ARE应用于三个方面,一是互联网范围的设备测量统计,二是对受损设备进行检测,三是对易受攻击的设备进行分析。
之后对ARE的效果与Nmap进行比较和评估,从产生规则的数量、规则的准确率和覆盖率、动态学习规则的能力以及时间代价,ARE都要优于Nmap。
小米智能插座:192.168.31.197 网关:192.168.31.147(控制它的手机ip)
sudo ettercap -i ens33 -T -q -M ARP:remote /192.168.31.197// /192.168.31.147//
从图中可以看出,设备的命令控制包为UDP传输,既然是UDP协议传输,那么是否可以通过命令包重放攻击来对设备进行控制?
了解到在homeassistant中可实现对小米设备的集成,并在其中对设备进行管理和操作。Homeassistant,主要以Python语言开发,既然它能操控小米设备,那它底层肯定有相关的函数调用库。
为了可以消除对专有软件(米家app)的依赖,并能控制自己的设备,所以出现了MiIo。设备和米家app在同一局域网下使用的加密专有网络协议我们称之为MiIo协议。
Miio库支持的设备有:
在同一局域网中,小米设备可以使用专有的加密UDP网络协议进行通信控制。在网络可达的前提下,向小米设备发送hello bytes就可以获得含有token的结构体数据。之后,构造相应的结构体,并且以同样的方式发送给设备即可完成控制。具体流程如下:
小米设备的token获取有三种途径:miio获取、从米家app获取、从数据库获取
在ubuntu下,先安装miio,然后发现设备:
npminstall -g miio
miiodiscover
但是很可惜,很多设备隐藏了token,使用该方法可能无法获取到token或获取到的token不正确。
这种方法需要的mijia app版本较老,且只对部分设备有效。
这种方法仅在Mi Home 5.0.19之前的版本可用。
该方法是读取手机中米家的app中的数据记录来获取设备的token,具体步骤如下:
首先随意发送hellobytes获得时间和设备ID,token我们自己设置;然后构造发送的数据结构msg,cmd中的method包括:set_power(控制开关)、get_prop(获取状态),控制的params是[‘on’]/ [‘off’],获取状态的params是[‘power’, ‘temperature’]
如果获得了token,就能对小米的设备进行操作,如图下面是返回的信息。
从目前的智能家居市场来看,用户不会只使用单个智能设备厂商的设备,所以对于厂商来说,通过开放接口给用户一些局域网的控制“自由”,实现不同厂商设备的联动是一个不错的选择。
从另外一个角度,本文中体现的安全问题我们也不容忽视。如果在局域网中不经过认证就能获取物联网设备的访问凭证,并进而进行控制,无形中给入侵者留了一扇门。例如,攻击者可经过扫描互联网发现家庭路由器,并利用弱口令或设备漏洞获得路由器的shell权限,接下来就可按照文中步骤就可以获得设备token进而控制。好在小米已经在最新的miio版本中修复了这一漏洞,大大提高了攻击者获取token的难度。
具有行为能力的主体
不具有行为能力的客体
可以执行的命令:读、写、执行
对行为的控制策略
系统行为所依赖的环境
行为对系统产生的效果
完整性的威胁就是一个子系统在初始时刻认为不正常的修改行为;
来源:内部&外部;
类型:直接&间接
外部的直接 | 外部的间接 | 内部的直接 | 内部的间接 |
---|---|---|---|
外部系统恶意地篡改另一个系统的数据或程序 | 一个外部系统插入恶意的子程序 | 修改自己的代码 | 修改自己的指针 |
完整性级别高的实体对完整性低的实体具有完全的支配性,反之如果一个实体对另一个实体具有完全的控制权,说明前者完整性级别更高,这里的实体既可以是主体也可以是客体。
完整性级别和可信度有密切的关系,完整级别越高,意味着可信度越高。
写规则控制
当且仅当主体S的完整性级别大于或等于客体O的完整性级别时,主体S可以写客体O,一般称之为上写。
执行操作控制
当且仅当主体S2的完整性级别高于或等于S1,主体S1可以执行主体S2。
低水标模型
任意主体可以读任意完整性级别的客体,但是如果主体读完整性级别比自己低的客体时,主体的完整性级别将为客体完整性级别,否则,主体的完整性级别保持不变。
环模型
不管完整性级别如何,任何主体都可以读任何客体
严格完整性模型
这个模型对读操作是根据主客体的完整性级别严格控制的,即只有完整性级别低或相等的主体才可以读完整性级别高的客体,称为下读
一般都是指毕巴严格完整性模型,总结来说是上写、下读
核对用户输入的账户密码与存储的是否匹配
若正确,判断是管理员还是普通用户,并跳转相应的界面
1 | def checkPass(self): |
管理员可以对用户进行增、删、查的操作
- 获取管理员输入的用户名、密码和用户等级
- 将明文密码转换为md5值
- 判断输入的账户是否已经存在以及是否为空
- 如果没有问题,将其存入passwd.txt的末尾
1 | def adduser(self): |
从passwd.txt中逐行读出
1 | def readuser(self): |
从passwd.txt中逐行读出用户名,并与待删除用户比较,如果相同,则删除该行
1 | def rmuser(self): |
普通用户可以完成对合法权限文件的读取、增加内容(上写下读)以及创建文件的操作
双击文件名
获取选中文件和当前用户的完整性级别
如果用户的级别低于文件,则读取文件内容
1 | def readfile(self): |
双击文件名
获取选中文件和当前用户的完整性级别
如果用户的级别高于文件,则写入文件内容
1 | def writefile(self): |
获取当前用户名和输入的文件名
在当前路径下创建名为用户名的文件
并对新创建的文件与用户等级建立字典,新文件路径为key,用户等级为value
这个字典方便读写时判断等级高低
1 | def touchfile(self): |