MENU

一次对OpenCFP的代码审计

September 8, 2016 • Security

前言

OpenCFP是一个开源的会议讨论系统,是用PHP写的。很多的会议都用这套系统,包括Immunity的渗透会议以及其他很多会议。

这个漏洞是OpenCFP使用的第三方身份认证框架Sentry造成的,该框架是由 Cartalyst开发的。尽管在新版本的OpenCFP中已经不用Sentry框架了,但是在实际环境中依然有许多OpenCFP系统中存在这个问题。

tumblr_inline_ocvgp3WmEN1t09cse_1280.png

功能

和大部分程序一样,OpenCFP也提供了一个“忘记密码”功能可以让用户通过邮箱来设置新密码。如以下页面:

tumblr_inline_ocvgo7ogUh1t09cse_540.png

点击“change mypassword”按钮的时候,会向服务器发送一个如下的POST请求包:

POST /updatepassword HTTP/1.1
Host: domain
Referer: http://domain/reset/2/vab9HtPIfIw2f6WLrzzidEbApaepDSGm9PkUKyKvZr
[...]

reset[_token]=x&reset[password][password]=passw0rd&reset[password][password2]=passw0rd&reset[user_id]=2&reset[reset_code]=vab9HtPIfIw2f6WLrzzidEbApaepDSGm9PkUKyKvZr

发送成功后,服务端会匹配参数reset[reset_code]的值和该用户在数据库中 reset_password_code字段的值。这个过程OpenCFP是通过Sentry框架完成这个身份认证。

漏洞

问题出现在Sentry框架如何执行这个认证的过程中。事实上,根据Sentry框架的数据库结构,如果用户没有重置密码的话,数据库中reset_password_code字段默认存储的是NULL:

3.png

除了一些基本的输入处理,Sentry框架并没有进一步验证输入是否由OpenCFP发送。而且,Sentry并没有在输入过滤方面提供相关建议。

下图是Sentry框架中用来验证的函数,主要用来验证请求包中的reset code是否和数据库中的相同。函数的返回很简单,就是true(匹配上了)和false(没匹配到)。

4.png

该检查函数在attemptResetPassword()函数中被调用。注意,用户的reset code会被修改回NULL,和新密码一起更新到数据库中:

5.png

这个漏洞本身非常简单。问题在于,如果NULL这个值可以传入checkResetPasswordCode()函数的话。那么该函数拿着NULL去数据库中匹配,那么,所有没有要修改密码的用户返回出的结果一定是True的。然后,我们用NULL的符号(%00)在请求包中构造这样的输入,把reset[reset_code] 的值设置成如下:

POST /updatepassword HTTP/1.1
Host: domain
Referer: http://domain/reset/2/vab9HtPIfIw2f6WLrzzidEbApaepDSGm9PkUKyKvZr
[...]

reset[_token]=x&reset[password][password]=passw0rd&reset[password][password2]=passw0rd&reset[user_id]=2&reset[reset_code]=%00

返回的结果自然是验证成功了。

6.png

这个漏洞可以用来重置任何OpenCFP用户的密码,只要他们的数据库中没有重设密码的令牌。不过攻击者要想登录的话,还是需要将用户ID和email关联起来才行,不过这也不是什么难题,因为通常1-5号用户就是管理。

结论

最后提醒下,其他用了Sentry框架的应用也可能存在同样的问题,尤其是现在基本上每个网站都会有“忘记密码”这个功能。

Archives QR Code
QR Code for this page
Tipping QR Code