Reverse Engineering Network Protocols - Jack Hacks
在过去的几周里,我终于找到了时间翻阅我的书库,阅读一些我从去年开始就想读的书。其中一本真正引起我注意的书是《攻击网络协议》,作者是James Forshaw。
这本书涵盖了什么内容呢?根据网站的描述,这本书:
《攻击网络协议》 是一本深入探讨网络协议安全的书,作者是全球领先的漏洞猎人之一 James Forshaw。这本全面的指南从攻击者的角度看待网络,帮助你发现、利用并最终保护漏洞。
总体来说,这本书非常值得一读!尽管书中涵盖的许多内容对我来说是复习,但它仍然教会了我很多新知识。书中讨论了动态协议分析、常见协议结构、密码学和协议安全。接着,它将重点转向寻找和利用网络协议中的漏洞,并概述了常见的漏洞类别、模糊测试、调试和耗尽攻击。
引言
那么什么是协议逆向工程,为什么我们应该学习它?
总体而言,协议逆向工程是提取客户端 - 服务器或应用程序所使用的应用程序/网络级协议的过程。如今,逆向工程协议的任务对网络安全变得非常重要。了解应用程序和网络级协议格式对许多安全工程师、安全顾问甚至开发人员来说都是必不可少的。
但是为什么?它为什么重要?
这有很多原因。假设你是一名安全顾问,被雇佣来测试一个将在华尔街用于交易/购买/出售股票及其他众多事务的交易系统的底层协议。不幸的是,客户只提供了一个客户端,而没有提供服务器,甚至没有提供应用程序的源代码……那么你该怎么办?
我们知道这个交易系统将是基于客户端 - 服务器的,因此需要有一个底层的 TCP 协议用于在客户端和服务器之间传输数据。由于我们可以访问客户端但无法访问服务器,因此我们可以轻松(好吧……希望能轻松)拦截流量,逆向协议,了解它的结构、如何传递数据,然后寻找协议中的漏洞,这通常会导致代码中的漏洞。
那么……现在我们知道了协议逆向工程的重要性,让我们开始学习如何逆向工程一个协议。
但首先……我们需要一些工具!
那么我们需要什么工具来完成这个任务呢?这实际上取决于具体的参与情况或我们可以访问的内容。如果你真的想了解更多关于特定捕获技术、漏洞和协议分析的信息,我强烈建议你去购买《攻击网络协议》。
但通常我们会使用一些工具,比如用于中间人攻击的代理、数据包嗅探器,以及用于编写套接字代码的编程语言。
一些可能的工具包括:
-
Wireshark -
Bettercap -
BinProxy -
Netzob -
CANAPE -
IDA Pro <– 如果我们可以访问客户端或服务器 -
Python 或 Ruby -
还有更多!
捕获协议流量
好的,现在我们有了工具,让我们深入实际捕获网络协议流量,以便稍后进行分析。
在这篇文章中,我将使用SuperFunkyChat应用程序,这是一个C#二进制协议应用程序,可以用于学习协议逆向工程。这个应用程序最初是由James Forshaw 构建的,用于教授 CANAPE。
该应用程序可以在 Windows、Linux 和 OS X 上运行。你可以从GitHub下载最新的预构建应用程序和源代码。只需确保选择适合你平台的发布二进制文件。
我不会详细解释如何让这个应用程序运行,因为这非常简单且不言自明。不过,你需要为你的操作系统安装.NET Core,以便该应用程序能够正常工作。
一旦你安装并解压了所有内容,你应该在你的 SuperFunkyChat 文件夹中看到以下文件。
kkb@kkb-ubuntu:~/SuperFunkyChat$ ls
ChatClient.deps.json ChatServer.dll
ChatClient.dll ChatServer.pdb
ChatClient.pdb ChatServer.runtimeconfig.json
ChatClient.runtimeconfig.json LICENSE
ChatProtocol.dll Microsoft.Extensions.CommandLineUtils.dll
ChatProtocol.pdb README.md
ChatServer.deps.json
在我们开始捕获数据包之前,让我们启动聊天服务器。由于我在 Linux 上,我将使用dotnet
前缀我的命令,以便应用程序知道使用.NET Core 来运行该应用程序。
kkb@kkb-ubuntu:~/SuperFunkyChat$ dotnet ChatServer.dll
ChatServer (c) 2017 James Forshaw
WARNING: Don't use this for a real chat system!!!
Running server on port 12345 Global Bind False
好的,现在我们已经启动了服务器,让我们在启动客户端之前先启动 Wireshark,以便开始捕获应用程序协议流量。
由于我在我的 Linux 虚拟机上进行这一切,我将开始在我的本地主机以太网适配器Loopback:lo
上嗅探流量。请双击你的本地以太网适配器,确保你正在捕获数据包。
太好了,现在我们已经准备好,可以在另一个控制台中启动我们的聊天客户端,并指定我们想要使用的用户名以及我们想要连接的主机名。
在这种情况下,我的用户名将是“test”,我将连接到我的“localhost”实例,因为我的聊天服务器就位于那里。
kkb@kkb-ubuntu:~/SuperFunkyChat$ dotnet ChatClient.dll test localhost
ChatClient (c) 2017 James Forshaw
WARNING: Don't use this for a real chat system!!!
Connecting to localhost:12345
>
如果操作正确,您应该在运行聊天服务器的控制台中看到类似的输出。
Connection from 127.0.0.1:46251 to
Received packet ChatProtocol.HelloProtocolPacket
Hello Packet for User: test HostName: kkb-ubuntu
现在客户端成功连接到我们的服务器,我们应该能够在 Wireshark 中看到一些网络流量。
太好了!我们成功捕获了连接流量,并且应该能够看到客户端如何连接和认证到服务器。为了确保我们有足够的数据进行分析,让我们发送一些聊天消息,然后输入/quit
以断开连接并捕获断开连接的流量。
> This is a test message!
> Hello - testing!
> /quit
Server> : Don't let the door hit you on the way out!
与我们发送的消息以及退出会话的操作相一致,您应该在聊天服务器控制台中看到类似以下输出的内容。
Received packet ChatProtocol.MessageProtocolPacket
Received packet ChatProtocol.MessageProtocolPacket
Received packet ChatProtocol.GoodbyeProtocolPacket
Closing Client
现在我们通过应用程序生成了一些网络流量,让我们停止在 Wireshark 中捕获数据包,以便开始分析应用程序的协议流量。
分析协议流量
让我们看看我们生成的流量,以便对客户端和服务器之间发生的通信有一个总体概述。
为此,右键单击第一个数据包,然后选择“跟踪 TCP 流”。这将为我们提供流量的 HEX/ASCII 表示。
跟踪 TCP 流后,您应该会看到一些文本,包括我们设备的名称、我们发送的消息以及一些服务器的响应。颜色表示流量的方向,红色表示客户端 -> 服务器,蓝色表示服务器 -> 客户端。
这很好,因为我们可以看到网络协议的 ASCII 表示,但这并不是我们所需要的。由于我们要逆向工程应用程序的协议,因此我们需要查看客户端和服务器发送的数据包中包含的所有内容。
您可能会想 - 为什么?
好吧,看看 TCP 流。您注意到流中的.
吗?这个单个点字符是 Wireshark 无法表示为 ASCII 的数据的替代。这清楚地表明我们正在查看的网络协议并不是一个专门的基于文本的协议。
我们之所以能在这个协议中看到文本,是因为这是一个聊天应用程序 - 主要用于发送…没错,您猜对了 - 文本!
因此,总的来说,这意味着在数据包头或主体中发送的控制信息可能是十六进制的。既然我们知道这一点,我们就可以开始深入研究协议了!
*注意*
请注意,这种二进制协议是相对容易区分的。有很多协议也完全作为基于文本的协议工作,还有一些则完全是二进制/十六进制的。
您可能会想 - “如果协议是加密的,我该怎么办?”
好吧,在那种情况下,需要做大量工作,这可能会变得非常艰巨。在某些情况下,甚至可能无法破解。当然,如果是自制的加密(不要自己实现加密!),您可能可以尝试规避加密,但这仍然需要大量的时间和经验。
这只是一个教程,我们从学习基础知识开始。如果您想要更多的练习,我建议您从一个已经有良好文档的协议开始。这可能看起来很傻,但这是获得一些经验并有文档可以依赖的好方法。
如果您对基于套接字的软件开发不熟悉,我建议您从一个良好文档的明文协议开始,例如 IRC 协议(RFC1459)。
首先 - 让我们开始将我们的对话隔离到仅客户端。请记住,服务器在 TCP 12345 上监听,因此我们要选择发送流量到该端口的对话。
您可以通过单击“整个对话(190 字节)”下拉菜单,选择我们适当的方向来隔离对话。
一旦对话被隔离,您应该只看到红色,这将是我们的客户端 -> 服务器流量。
在我们隔离了对话后,现在我们可以开始深入研究我们的数据包。为了能够成功逆向工程协议,我们需要能够看到协议发送的所有字节。
为此,在“显示并保存数据为”下拉菜单中,单击并选择“十六进制转储”。
这样做将使我们能够同时获得流量的十六进制和 ASCII 表示,以及每个发送的数据包。这将使我们至少能够了解协议数据包的结构、使用的命令等。
确定协议结构
好的 - 现在我们知道这个协议是文本/二进制的,我们可以通过查看数据包/十六进制来最终确定协议的结构。
让我们开始查看前几个数据包。
00000000 42 49 4e 58 BINX
00000004 00 00 00 12 ....
00000008 00 00 05 d6 ....
0000000C 00 .
0000000D 04 74 65 73 74 0a 6b 6b 62 2d 75 62 75 6e 74 75 .test.kk b-ubuntu
0000001D 00 .
0000001E 00 00 00 1e ....
00000022 00 00 09 f9 ....
00000026 03 .
00000027 04 74 65 73 74 17 54 68 69 73 20 69 73 20 61 20 .test.Th is is a
00000037 74 65 73 74 20 6d 65 73 73 61 67 65 21 test mes sage!
查看第一个数据块,我们看到它的长度为 4 字节,并以四个字符BINX开头。似乎这四个字符在其余的数据包中没有重复,因此我们可以假设这是在客户端连接到服务器时用于区分有效连接的命令。
接下来的两个数据块也都是 4 字节长,后面跟着一个 1 字节大小的数据块,最后是一个包含大部分可读文本的更大数据块。
似乎下一个数据块是一个 1 字节大小的数据块——这是一个空字节x00
……但这似乎也是文本块的一部分。这可能是发送 Hello 数据包时的字符串终止符。
知道这些后,我可以将十六进制转储分成 3 个独立的数据包:连接数据包、Hello 数据包和聊天数据包。
----------------------------------------------------------------------------- <- Connection Packet
00000000 42 49 4e 58 BINX
----------------------------------------------------------------------------- <- Hello Packet
00000004 00 00 00 12 ....
00000008 00 00 05 d6 ....
0000000C 00 .
0000000D 04 74 65 73 74 0a 6b 6b 62 2d 75 62 75 6e 74 75 .test.kk b-ubuntu
0000001D 00 .
----------------------------------------------------------------------------- <- Chat Packet
0000001E 00 00 00 1e ....
00000022 00 00 09 f9 ....
00000026 03 .
00000027 04 74 65 73 74 17 54 68 69 73 20 69 73 20 61 20 .test.Th is is a
00000037 74 65 73 74 20 6d 65 73 73 61 67 65 21 test mes sage!
在可视化之后,似乎该协议发送的每个数据包都有四个部分,除了仅仅是连接数据包的那个。
既然我们现在已经将其分开,并对结构有了一些基本了解,那么让我们开始深入研究每个块,弄清楚它们代表什么。
让我们先看看位于 00000004
的四字节块。
我们看到 3 个字节都是空的,所以里面实际上没有数据。最后我们有十六进制值 0x12
。根据 Wireshark 放置的 .
字符来看,这意味着这不是 ASCII 数据,否则我们会看到 符号。
所以如果它不是 ASCII 数据,我们下一个最好的选择就是十进制。将 0x12
转换为十进制得到 18
。嗯……18?这可能代表什么呢?
为了更好地理解这一点,我们首先需要了解 TLV 或类型 - 长度 - 值。它也被称为标签、长度值。在 TCP 协议中,TLV 是一种用于某些协议中可选信息元素的编码方案。
在像 TCP 这样的流式协议中,这是必要的,因为它允许应用程序或客户端和服务器知道需要从连接中读取多少数据以处理协议。
类型和长度的大小是固定的(通常为 1-4 字节),而值字段的大小是可变的。这些字段的用途如下:
-
类型: 一个二进制代码,通常是字母数字的,表示消息的这一部分所代表的字段类型。 -
长度: 值字段的大小,通常以字节为单位。 -
值: 一系列可变大小的字节,包含该消息部分的数据。
好的,所以似乎数据包的第一个块指定了数据长度……但是如果我们将数据块 0000000D
和块 0000001D
中的数据相加,那么我们总共有 17 个字符/字节,这与数据长度不符。
有时查看这些协议和数据时,当数据不匹配时会让人感到困惑。因此,为了更好地理解为什么我们只有 17 个字节的数据而不是 18 个字节,我们需要了解可变长度缓冲区是如何在栈上分配的。
对于可变长度缓冲区,应用程序为存储的数据分配正确大小的缓冲区。通常在 TCP 协议中,这样做是为了避免固定缓冲区,这可能导致缓冲区溢出,如果提供给缓冲区的数据过多。
然而,如果应用程序错误地计算了缓冲区大小,也可能发生缓冲区溢出。因此,回到协议 - 该块分配了 18 个字节的数据,17 个字节用于我们传递的数据,以及另一个字节,通常是空终止符,以防止缓冲区读取超出分配的数据 - 从而防止缓冲区溢出发生。
请注意,这只是一个假设。目前我们并不是在寻找协议中的漏洞,我们只是想弄清楚它的结构。也许我错误地假设字符串的末尾有一个空终止符……谁知道呢。我们在测试之前不会知道 - 但不幸的是,这超出了本博客文章的范围 =)
如果您想了解更多关于网络协议中的漏洞,请务必购买 James Forshaw 的书籍攻击网络协议。
好的,假设第一个块是基于 TLV 的长度,我也可以假设文本部分是值。
00000004 00 00 00 12 .... <- Length
00000008 00 00 05 d6 .... <- ?
0000000C 00 . <- ?
0000000D 04 74 65 73 74 0a 6b 6b 62 2d 75 62 75 6e 74 75 .test.kk b-ubuntu <- Value
0000001D 00
那么其他两个块是什么呢?我们可以假设,由于第一个块是十进制的,那么第二个和第三个块也应该是十进制表示,因为 Wireshark 用.
符号替换了它们,因此没有 ASCII 表示。
如果我们将第二个块的数据0x05D6
转换为十进制,那么我们得到1494
。这……1494?这到底与什么有关?
另外,如果我们查看下一个块,我们看到0x00
,这只是0
。
好吧……现在我完全搞不清楚这些块与什么相关。此时我们需要做的是通过编写一个解析器来比较每个数据包,这个解析器应该解析我们的数据包并输出它的值,以便我们可以将它们与流中的其他数据包进行比较。
这同时也将有助于巩固我们对数据包中其他块的假设,并进一步帮助我们理解其他部分。
解析协议
在这一部分,我将使用 Python 来读取和解析 Wireshark 的 PCAP 文件。但在此之前,我们需要将所有数据包字节导出到一个文件中。
为此,在我们流窗口的“显示并保存数据为”部分,点击下拉菜单并选择“原始”。
现在你应该只看到网络协议的原始二进制数据。
完成后,只需点击“另存为…”并将文件保存为“outbound.bin”。
在你保存文件后,让我们打开一个终端控制台,使用xxd工具检查我们是否成功导出了所需的数据。
kkb@kkb-ubuntu:~$ xxd outbound.bin
00000000: 4249 4e58 0000 0012 0000 05d6 0004 7465 BINX..........te
00000010: 7374 0a6b 6b62 2d75 6275 6e74 7500 0000 st.kkb-ubuntu...
00000020: 001e 0000 09f9 0304 7465 7374 1754 6869 ........test.Thi
00000030: 7320 6973 2061 2074 6573 7420 6d65 7373 s is a test mess
00000040: 6167 6521 0000 0017 0000 0757 0304 7465 age!.......W..te
00000050: 7374 1048 656c 6c6f 202d 2074 6573 7469 st.Hello - testi
00000060: 6e67 2100 0000 1500 0006 8d02 1349 276d ng!..........I'm
00000070: 2067 6f69 6e67 2061 7761 7920 6e6f 7721 going away now!
很好,我们成功地保存了所有来自 pcap 的协议流量。现在我们可以开始编写我们的 Python 脚本,该脚本将读取我们的 bin 文件,并以网络字节顺序读取二进制数据。
请注意,“网络字节顺序”是大端字节序,与 x86 和 x64 进程中使用的小端字节序不同。
我们的 Python 脚本将类似于下面的代码:
from struct import unpack
import sys
import os
# Read a fixed number of bytes, dictated by "l"
def read_bytes(f, l):
bytes = f.read(l)
if len(bytes) != l:
raise Exception("Not enough bytes in stream")
return bytes
# Unpack a 4 byte integer in network byte order
def read_int(f):
return unpack("!i", read_bytes(f, 4))[0]
# Read a single byte
def read_byte(f):
return ord(read_bytes(f, 1))
filename = sys.argv[1]
file_size = os.path.getsize(filename)
f = open(filename, "rb")
print ("Connection: %s" % read_bytes(f, 4))
# Keep reading until file is empty.
while f.tell() < file_size:
length = read_int(f)
val1 = read_int(f)
val2 = read_byte(f)
data = read_bytes(f, length - 1)
print("Len: %d, Val1: %d, Val2: %d, Data: %s" % (length, val1, val2, data))
这个脚本应该是自解释的,但让我们快速分析一下,以便更好地理解发生了什么。
首先,我们创建三个新的定义:
-
read_bytes:接受两个参数;要读取字节的文件,以及要读取的字节数,这个数由“l”传递过来。 -
read_int:解包四个字节并将其读取为一个整数。 -
read_byte:简单地读取一个字节的数据。
之后,我们将“filename”设置为用户从命令行传递的文件名,并将其赋值给“f”。
一旦文件打开,我们读取前 4 个字节,这应该是我们的连接命令“BINX”,然后将其打印到控制台。
接下来,我们遍历文件,直到文件为空,没有更多的字节可以读取。在遍历文件时,我们做了几件事情;
-
以网络字节顺序解包数据包的前四个字节,将其读取为一个整数并将其设置为长度。这在技术上应该是数据包的第一个块,指示数据长度,就像我们在结构分析中假设的那样。 -
解包数据包的下四个字节,将其读取为一个整数并将其设置为“Val1”,这将是我们的第一个未知值。 -
读取下一个块,即一个字节,并将其设置为“Val2”,这将是我们的第二个未知值。 -
使用我们在第一块中找到的长度减去一个字节来读取其余的数据包数据。请记住,我们需要减去那个额外的字节,我们假设它是空字符串终止符。 -
在一行中打印出长度、未知值 1 和 2,以及数据,这将表示一个单独的数据包。
总体而言,这个脚本相当简单,可以让我们快速解析数据包中的所有数据,然后我们可以用这些数据与其他数据包进行比较。
所以让我们继续运行 Python 脚本,并在命令中包含我们的 bin 文件的名称。我建议您也使用 Python v3,因为它在打印字节数据方面比 Python v2 表现得更好。
kkb@kkb-ubuntu:~$ python3 protocol_parse.py outbound.bin
Connection: b'BINX'
Len: 18, Val1: 1494, Val2: 0, Data: b'x04testnkkb-ubuntux00'
Len: 30, Val1: 2553, Val2: 3, Data: b'x04testx17This is a test message!'
Len: 23, Val1: 1879, Val2: 3, Data: b'x04testx10Hello - testing!'
Len: 21, Val1: 1677, Val2: 2, Data: b"x13I'm going away now!"
太棒了!正如您所看到的,我们已经以易于阅读的格式打印出了每个数据包的长度、未知值和数据。现在我们可以继续确定协议的结构。
确定协议结构
现在我们已经解析了协议数据,让我们回过头来看看我们发送的第一个连接数据包:
Len: 18, Val1: 1494, Val2: 0, Data: b'x04testnkkb-ubuntux00'
我们的假设是正确的,关于第一个数据块以及它提供了我们发送的数据长度加 1(用于空终止符)!只需注意,不要将数据包中的 x00
混淆为空终止符,因为它并不是!空终止符是由服务器在接收到数据包后提供的。
我们还可以看到,Wireshark 中的不可打印数据现在通过我们的脚本以十六进制形式显示给我们。这使我们更好地了解协议的结构以及它传递的数据。
如果我们仔细观察数据包中的“数据”部分,我们会看到三个十六进制字节:x04
、n
和 x00
。让我们注意这些字节的位置。如果将 x04
转换为十进制,则等于 4
,这正好是我们用户名的长度!n
只是一个换行符,而 x00
是字符串的终止符——在这种情况下,是连接到聊天服务器的用户名和设备的终止符。
因此,根据这一点,似乎数据中的第一个字节是用户名的长度。我们可以通过查看我们发送的下一个数据包来验证这一点,该数据包是我们的聊天消息。
Len: 30, Val1: 2553, Val2: 3, Data: b'x04testx17This is a test message!'
如您所见,x04
再次出现,因为我们的用户名“test”是 4 个字符长!进一步观察,我们还看到另一个十六进制字节,这次是x17
,转换为十进制是23
,这正好是我们发送的消息的长度。
太棒了!现在我们知道数据包的数据部分也有自己的 TLV,这让服务器知道需要为用户名和消息分配多少数据,以便在堆栈上适配——防止缓冲区溢出。
好吧,现在我们知道这些提供了数据的长度,让我们做一些数学运算,看看这是否与长度值显示的内容相符。用户名的长度是 4 个字符,数据的长度是 23,所以4 + 23 = 27
。等等,27?但是长度包显示的是 30……我们遗漏了什么?
长度是否也在加上Val2的值?这很有道理,因为4 + 23 + 3 = 30
。没错,现在我们知道长度的计算方式是:Val2 + 用户名长度 + 消息长度 = 数据长度
。
那么我们的问题是,Val2代表什么呢?让我们回顾一下我们解析的数据,并比较每个数据包的Val2值。
kkb@kkb-ubuntu:~$ python3 read_protocol.py outbound.bin
Connection: b'BINX'
Len: 18, Val1: 1494, Val2: 0, Data: b'x04testnkkb-ubuntux00'
Len: 30, Val1: 2553, Val2: 3, Data: b'x04testx17This is a test message!'
Len: 23, Val1: 1879, Val2: 3, Data: b'x04testx10Hello - testing!'
Len: 21, Val1: 1677, Val2: 2, Data: b"x13I'm going away now!"
嗯 - 这很有趣!看看数据包 2 和 3。Val2的值是相同的 - 它们都显示为3
。而其他数据包则显示其他值,但它们似乎在0-9
的范围内。
数据包 1 和 2 都是聊天消息,我相信我们可以安全地假设Val2是客户端/服务器正在执行的命令的表示。3 表示聊天消息,0 可能表示连接命令,而 2 则是退出命令!
命令编号 | 方向 | 描述 |
---|---|---|
0 | 出站 | 客户端与服务器的连接 |
2 | 双向 | 当使用/quit 命令时,服务器/客户端都会发送 |
3 | 双向 | 当消息发送到服务器并被客户端接收时发送 |
好的 - 我们已经确定了大约 90% 的协议结构,剩下的就是弄清楚Val1是什么。
经过一番思考,我问自己 - 这个值是校验和的可能性有多大? 这可能是因为我们正在发送聊天消息,必须有某种校验和来验证从客户端发送到服务器的数据没有被修改或损坏……至少我希望如此!
在继续之前,让我们先谈谈校验和。
正如我之前所说,校验和通常用于确保通过网络协议传递的数据的完整性。这是服务器防止中间人攻击的好方法,如果协议是明文的……就像这个一样。
现在,如果这个协议使用 SSL,那么我们可能会立刻放弃,除非我们找到某种填充 Oracle 攻击,这将允许我们传递加密或明文数据并获得相反的结果,从而让我们看到加密是如何工作的。
现在,校验和技术上应该使用安全的哈希算法,如 SHA1 或 SHA2……如果你在考虑 MD5……真可惜!
但是,如果校验和使用安全的哈希算法和复杂的输入,那么试图拦截数据并更改它,甚至制作我们自己的数据包将几乎是不可能的。但是,如果网络协议使用易于猜测或自定义的校验和,那么如果我们弄清楚它是如何工作的,保护措施实际上将是不存在的。
例如,让我们来看以下两个例子:
使用复杂输入的安全哈希算法的校验和:
假设一个银行系统为每个交易使用校验和。它通过从用户会话中获取以下内容并将其附加到字符串中来计算每个数据包的校验和:用户名;主机名;时间
一旦提供了这些值,它会生成该字符串的 SHA1 哈希,结果为:fd8f04e9c574b265fa07a69d164f3cd8c2665f84
。
祝你好运,试图弄清楚这个校验和,甚至试图破解它!这应该是校验和的计算方式。
使用自定义算法的校验和:
假设银行系统认为使用安全校验和会消耗过多资源……所以他们决定选择自己的自定义校验和生成器……这真是个坏主意!
这个校验和简单地接受消息中的每个字节,将字节转换为十进制,最后将它们相加。
假设传递的消息是test
,其十六进制表示为74 65 73 74
。将其转换为十进制,我们得到116 + 101 + 115 + 116 = 448
这似乎很容易猜测,因为这个数字是一个整数。因此,作为攻击者,如果我们知道这一点,那么数据的完整性将不安全,因为我们可以轻松编辑或创建带有有效校验和的自定义数据包!
好的……现在我们详细解释了这一点,你可能会想知道为什么第二个“自定义算法”看起来与聊天协议中的Val1值非常相似。
那么,聊天应用程序使用我们刚刚详细描述的相同校验和的可能性有多大?我不知道……但让我们找出答案!
让我们回顾一下我们解析的数据包,特别是连接数据包,因为它更小,更容易测试。
Len: 18, Val1: 1494, Val2: 0, Data: b'x04testnkkb-ubuntux00'
Val1的值是1494
。所以,让我们将数据的十六进制表示转换为十进制。以下是我们数据的十六进制表示:
04 74 65 73 74 0a 6b 6b 62 2d 75 62 75 6e 74 75 00
现在让我们将其转换为十进制表示,结果应如下所示:
4 116 101 115 116 10 107 107 98 45 117 98 117 110 116 117 0
将这些整数相加,我们得到1494
,这正是Val1的值!太棒了,因此我们验证了我们的假设,即Val1是校验和,它是通过取每个字节的十进制值并将其相加来计算的……这并不像我希望的那样安全。
协议结构图
好吧,现在我们已经弄清楚了协议的结构,我们可以创建一个简单的图表,作为网络协议数据包结构的视觉辅助。
太棒了!经过大量的工作,我们终于对聊天应用程序的出站数据包的结构及其包含的内容有了相当不错的了解。我们可以假设它们与入站数据包相似。
我将留给你去练习,尝试找出入站数据包是否相似——_提示提示_你需要进行一些代码更改,但不多!
从这一点开始,我们可以开始拦截数据包,并开始寻找协议中的漏洞,例如缓冲区溢出、整数溢出、命令注入等。
原文始发于微信公众号(securitainment):网络协议逆向工程
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论