CVE-2023-42820-JumpServer伪随机数导致任意密码重置
2023-10-16 / 共计3062 字
CVE-2023-42820-JumpServer伪随机数导致任意密码重置
错过了夕阳就别错过晚霞!
If miss the sunset, don’t miss the sunset!
前几日在学习分析 CVE-2023-42442-JumpServer身份绕过漏洞时,在官网上又看见出了个随机种子导致任意密码重置(Random seed leakage results in the user password being reset)的漏洞,不来活了,这不得分析分析这个新漏洞,听起来挺有意思的,随机种子和任意密码重置怎么结合起来的。 经过这几天的代码分析,该漏洞产生的主要是因为伪随机数而导致的。最终实现了自动化利用。
(Exp可在Exp-CVE-2023-42820,有问题可扫最下方二维码添加WeChat咨询 或留下评论)
前置知识
先给出该漏洞利用的整个流程:
1. 获取算数计算图形验证码1;
2. 批量重放验证码1的url,从而固定seed;
3. 获取算数计算图形验证码2,根据固定的seed进行预测,通过算数计算图形验证码2;
4. 再次批量重放验证码1的url,再次固定seed;
5. 获取邮件验证码,通过固定的seed进行预测,从而通过邮件验证码;
6. 进行密码重置。
在分析该漏洞前,需要先了解一下伪随机数,我先在python中获取两次随机数,每次获取4个随机数,可以看到当每当播种的seed不同,每次获取的随机数是不同的: 但是,当每当播种seed都是一样的时候,那又会是什么样呢?如下可以看出当我们设置seed都为11时,两次获取随机数是完全一样的。不仅如此random.seed播种之后,无论做什么操作,只要没有重新播种,那么还是使用的先前播种的seed,这就说明了播种seed是作用于整个进程的: 在JumpServer重置密码过程中中存在一个邮箱验证码,通过翻阅代码证明该验证码就是通过伪随机生成的: 从上面的实验中就可以预知,CVE-2023-42820-JumpServer伪随机数导致任意密码重置,主要就是通过得知播种相同seed去预测之后产生的随机验证码,从而进行密码重置。
漏洞分析
在官方的修复中,在random_string开头添加了random.seed(None),这就使得每次在使用random_string获取随机数的时候使用新的seed,从而阻断预测产生的随机数: 在JumpServer密码重置的流程如下:
1. 进入重置密码页面,获取算数图形验证码,输入结果通过算数图形验证码;
2. 输入用户邮箱,获取邮箱验证码;
3. 提交邮箱验证码,通过则进入密码修改页面。
首先跟进看第一个步骤,“通过算数计算的图形验证码”。通过定位api找到对应的View为UserForgotPasswordPreviewingView:
可以看到GET请求UserForgotPasswordPreviewingView实际是进行渲染users/forgot_password_previewing.html并返回页面:
在实际请求http数据包中,可以看到每次请求的验证码数值都不一样,由此可以推断出在渲染过程中调用了某些代码生成了验证码的url:
跟进发现url的api发现是使用第三方验证码库django-simple-captcha。image后面的字符串是由captcha/fields.py下BaseCaptchaTextInput类中的image_url进行渲染:
那么self._key又是从哪里获取的呢?接着跟进,发现在渲染开始就调用了fetch_captcha_store函数:
在fetch_captcha_store函数中就对self._key进行了赋值:
在Jumpserver配置中并没有CAPTCHA_GET_FROM_POOL,只有下面4个配置:
并且三方验证码库django-simple-captcha中CAPTCHA_GET_FROM_POOL默认为False,因此则使用CaptchaStore.generate_key(generator)进行获取key:
在generate_key中可以看到challenge字段,很容易想到这是就是验证码图片渲染前的实际值,challenge是通过get_challenge进行获取的,获取challenge之后创建store,返回challenge的hsahkey:
在get_challenge代码中实际调用的是CAPTCHA_CHALLENGE_FUNCT
所配置的方法,还记得上面看JumpServer配置时有一个CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge'
吗,可以得知是通过captcha.helpers.math_challenge方法获取的图形验证码实际值了:
找到captcha.helpers.math_challenge方法如下,直接复制到之后的EXP进行使用即可:
好,现在算数计算图形验证码生成方式已经弄清楚,但是还是没有看到可以获取seed的地方。别急,我们接着按照流程进行,获取到图形验证码的url之后,接着需要请求验证码图片了,之前看到过验证码的url形式是这样的/core/auth/captcha/image/f2ea3c452a08ccc9799d7355feef8294b817add9/
,在JumpServer的路由中可知这是调用的第三方验证码库django-simple-captcha的urls:
在第三方验证码库django-simple-captcha的urls又对接了views.captcha_image:
在captcha_image中可以看到,generate_key生成的hashkey作为key传入,从而生成图形验证(看到了吗,这里有一个random.seed(key),这不就是随机数播种吗,而且key是通过url请求传进去的,并且是可控的,这不就是我们在找的播种seed吗):
既然这里已经找到了可以播种seed的地方,那么不就可以通过请求图形验证码进行播种seed,控制之后生成的随机验证码,从而预测验证码吗?
先别慌,我们并没有完成密码重置的所有流程,继续完成重置密码的流程。接下来到了获取邮件验证码,在获取邮件验证码时,请求的是/api/v1/authentication/password/reset-code/
:
在JumpServer中找到/api/v1/authentication/password/reset-code/
对应的view为UserResetPasswordSendCodeApi
:
在UserResetPasswordSendCodeApi中的create方法中,邮件验证码就是使用的开头提到官方修复的random_string进行生成的:
直接复制random_string
(修复之前没有random.seed(None)的)到之后的EXP中使用即可:
这下好了,找到了算数技术图形验证码的生成方式和邮件验证码的生成方式,还有了可以设置seed的地方,这下就可以按照文章开头所给出的流程进行漏洞利用了。 (虽然还有部分小坑,但已经描述的很清楚了,稍微再分析下就可以完成整个漏洞的利用了,具体细节可前往Exp-CVE-2023-42820查看Exp)
注:自动化利用需要解决csrftoken。在提交邮件验证码时,需要等待几秒钟,等验证码导入内存
如有问题,可扫下方二维码添加WeChat 或留下评论
文笔垃圾,技术欠缺,欢迎各位大师傅请斧正,非常感谢!
如果文章对您有帮助
部分文章会发布公众号!
感谢您的支持!