在几个月前,我有幸被邀请参加了HackerOne上的一个私人项目。该项目受到一系列IDOR漏洞的影响,通过对这些漏洞的利用,我成功接管了他们的一个应用。由于保密协议的限制,因此在本文中将不会出现任何真实名称或与公司相关的信息。但我会最大限度的分享我从普通用户升级为管理用户的详细过程。

侦察

这个阶段的任务非常简单:该项目将所有站点和子域都包含在了测试范围内,幸运的是数量并不巨大。经过一番查看,有两个子域引起了我的注意app.site.com和admin.site.com,下面我们就以这两个子站为切入点。

知己知彼百战不殆

在对任何的应用测试之前,我都会先按照目标应用的正常流程走一遍。在此期间,我会在后台运行burp拦截请求和接收的数据,以便我更好的分析应用的执行过程。

核心应用(app.site.com)非常的小,经过几个小时的折腾,我几乎一无所获……说实话,我都有点想放弃了~

第二次尝试

核心应用上的一无所获着实令我有些失望,还好还有一个管理站点(admin.site.com)。那么,有没有什么办法可以访问到管理面板呢?经过几次尝试,页面都只向我返回了一个“access denied”拒绝访问的提示信息,根本无法获取到任何的页面内容。就当我快要放弃的时候,我惊奇的发现当使用VPN进行连接时,拒绝访问提示竟然消失了,并且页面内容也能看到。我不知道为什么我的VPN提供商,允许我访问其内容(具体原因我也没有调查过,至少目前对我来说是非常有利的)

我访问了管理站点,并被提示输入登录凭证。此外,这里还有一个下拉菜单提供了:1.登录 2.注册 两个选项。

我尝试了注册选项,API为我返回了一个“注册失败”的错误提示。这也在我的意料之中,让我们通过burp来查看下请求数据。

连环利用

注册表单向管理站点上的/api/register端点发出请求,之前在核心应用上请求的也是这个端点。但这里丢失了一部分信息:管理站点上的原始请求如下:

POST /api/register HTTP/1.1 Host: admin.site.com

{"firstName":null,"lastName":null,"login":"email-address@site.com","email":"email-address@site.com","password":"hunter2"}

让我们将其与核心应用的请求进行比较:

POST /api/register HTTP/1.1 Host: app.site.com  {"firstName":null,"lastName":null,"login":"email-address@site.com","email":"email-address@site.com","password":"hunter2","securityQuestions":[{"questionId":"1","answer":"test1"},{"questionId":"2","answer":"test2"}]}

通过对比可以看到,管理站点的请求缺少了securityQuestions。如果我们将该缺失部分添加上去,我们就能够在管理站点上成功注册帐户。但这里有一个问题,管理站点要求使用2FA进行身份验证。这种情况我们可以尝试烧过,例如给它一个空或随机值。这里我随便填了串数字123456作为我的身份验证码,如我预期的那我成功通过了验证。(由于我并没有2AF设置,因此填写任何数字都应该是有效的)。到这里,问题仍没有完全解决。由于我的用户权限较低,因此只有对一些页面可怜的只读权限,包括配置文件和一些其它的文件。但不得不说这是一个非常好的开始!

步步为营

管理站点有一个设置菜单,允许用户修改他们的电子邮件地址,密码和2FA设置。当我尝试更改我的信息时,页面会返回“Something went wrong”的提示,但由于我的帐户中没有设置2FA,因此我可以通过发送空POST请求来进行修改:

POST /api/users/newAuthenticationCode/46774 HTTP/1.1 Host: admin.site.com

返回一个键值:

{"newKey":"YNFTHSAJVOR3RS6B"}

当页面加载时,这里还有另一个到/api/users/authenticationCode/YNFTHSAJVOR3RS6B的请求加载我的QR码。你可能已经猜到,我能够替换我的user_id (46774) 为任意用户创建新的身份验证码,并使用任意的2FA工具(如Duo/Google Authenticator)在我的设备上使用QR码注册它。

获取用户数据库

我唯一访问的一个页面是一个空的“users”页面,该页面并没有为我返回网站上任何的用户信息,但它允许我们进行搜索。GET请求如下:

/api/users/search?page=&size=20&ascending=true&orderBy=Login&searchString=My_SEARCH_STRING&userRole=

如果我们删除了searchString,size和page的值,那么将会获取到整个用户数据库,其中包括UID,email,login_id,profile以及其他一些细节信息。

眼见不一定为实

现在我已获取到了足够的信息,我想尝试看看是否能获取到更高的访问权限。当用户尝试在管理站点内更改其用户详细信息时,应用程序将发出以下请求:

PUT /api/users HTTP/1.1 Host: admin.site.com {"id":46774,"login":"email-address@site.com","firstName":null,"lastName":null,"email":"email-address@site.com","activated":true,"locked":false,"failedLoginAttempts":0,"authorities":["ROLE_USER"],"authenticationKey":"BHUVCONYHGLMUSAM"}

我尝试将我的id替换为其它用户id,并试图修改他们的用户数据,但这并没有成功。这使我意识到,可能“Something went wrong”的错误提示只是一个假象,实际上帐户信息早已被成功更改。换而言之就是:即使应用程序成功进行了更改,也照样会返回错误提示。当我提交报告时,我请求厂商允许我更改其中一个管理员帐户的信息,并且允许我使用特定的用户ID测试帐户。

连接漏洞

现在我们已经确认,可以通过更改id值来创建新的身份验证码和更改其他用户帐户。我尝试使用获准的特定用户ID user_id (user_id:3)进行测试。我首先为2FA创建了一个新的QR码,并导入到了Google authenticator中,然后我更改了电子邮件地址并重置了其密码,并最终成功登录到了该帐户!

升级为管理员权限后,我查看了该站点的用户角色,主要有管理员,普通用户,开发人员等。有趣的是,一些API端点不仅管理员可以访问,普通用户如果知道路由的话其实也可以访问。

意外收获

几天后,我重新访问了该应用。当我使用我的凭据访问管理站点后,我意外的发现我仍然可以以只读权限访问管理页面。以下是我使用该帐户发出的请求:

POST /api/account/updateAdmin
Host: admin.site.com

{"id":3,"login":"admin-email-address@site.com","firstName":null,"lastName":null,"email":"admin-email-addres@site.com","activated":true,"locked":false,"failedLoginAttempts":0,"authorities":["ROLE_ADMIN"],"passwordLastModifiedOn":1523930764000,"address":{"id":3,"state":null,"city":null,"streetOne":null,"streetTwo":null,"unitNumber":null,"phoneNumber":null,"zip":null,"latitude":null,"longitude":null}}

这与之前/api/users的请求非常相似,在这个端点我能够更改“authorities”的值。通过更改用户ID,我再次将只读帐户升级为了管理员帐户。

{"id":46754,"login":"email-address2@site.com","firstName":null,"lastName":null,"email":"email-address2@site.com","activated":true,"locked":false,"failedLoginAttempts":0,"authorities":["ROLE_ADMIN"],"passwordLastModifiedOn":1523930764000,"address":{"id":46754,"state":null,"city":null,"streetOne":null,"streetTwo":null,"unitNumber":null,"phoneNumber":null,"zip":null,"latitude":null,"longitude":null}}

总结

1.侦察不仅仅只是收集子域和文件/目录列表,也应了解目标应用的执行流程和工作方式。

2.不要太过依赖于返回的提示信息,你看到的不一定就是真实的情况。

3.碰到2FA并不意味着你就一定会失败,是人都会犯错误。试着站在开发者的角度思考问题。

4.如果你的第一次尝试失败,请不要轻易放弃。你可以尝试去放松一下或睡上一觉,再回过头来看说不定就会收获惊喜。

*参考来源:nahamsec,FB小编 secist 编译,转自FreeBuf