目录
参考:Java代码审计入门:WebGoat8(再会) - FreeBuf网络安全行业门户
由于笔者个人水平有限,行文如有不当,还请各位师傅评论指正,非常感谢
(一)什么是预编译
JDBC编程之预编译SQL与防注入 - 加了冰的才叫可乐 - 博客园
在博客园找了篇不错的文章,当然也可以看看我之前整理的文章,我先简单进行总结SQL Injection 防御——预编译__Cyber的博客-CSDN博客
1 sqLIniection(mitigation)
2防御sq1注入,其实就是session,参数绑定,存储过程这样的注入。
3//利用session防御,session内容正常情况下是用户无法修改的select *
from users where user=""+session.getAttribute("UserID"
+"!";
4//参数绑定方式,利用了sq1的预编译技术
5 String query = "SELECT * FROM users WHERE last name =?";
6 Preparedstatement statement =
connection.prepareStatement(query);
7 statement.setstring(1, accountName);
8 ResultSet results =statement.executeQuery();
9上面说的方式也不是能够绝对的进行sq1注入防御,只是减轻。
1、如何绕过
上述参数指定方式可以通过如下绕过:
通过使用case when语句可以将order by后的orderExpression表达式中添加select语句
BP抓包看响应报文里面有没有用到order by
我们把column的字段值修改为i,发现order by语句刚好拼接上去了,case when语法.
(二)JWT安全
1、什么是JWT
JSON Web Token(JSON Web令牌)是一种跨域验证身份的方案。JWT不加密传输的数据,但是能够通过数字签名来验证数据未被篡改。
JWT分为三部分,头部(Header),声明(Claims),签名(Signature),三个部分以英文句号.隔开,JWT的内容以Base64URL进行了编码。如图一

头部
头部(Header) I
{
"alg":"Hs256",
"typ":"JWT"
}
alg是说明这个JwT的签名使用的算法的参数,常见值用Hs256(默认),H8512等,也
可以为None。HS256表示HMAC SHA256
typ说明这个token的类型为JWT
声明
JWT固定参数有:
- iss:发行人
- exp:到期时间
- sub:主题
- aud:用户
- nbf:在此之前不可用
- iat:发布时间
- jc:JWT ID用于标识该JWT
签名
服务器有一个不会发送给客户端的密码(secret),用头部中指定的算法对头部和声明的内容用此密码进行加密,生成的字符串就是JWT的签名。
下面是一个用H8256生成JWT的代码例子
HMACSHA256(base64UrlEncode(header)+"." base64UrlEncode(payload)secret)
1、用户端登录,用户名和密码在请求中被发往服务器
2、(确认登录信息正确后)服务器生成JSON头部和声明,将登录信息写入JSON的声明中(通常不应写入密码,因为JWT是不加密的),并用secret用指定算法进行加密,生成该用户的JWT。此时,服务器并没有保存登录状态信息。
3.服务器将JWT(通过响应)返回给客户端
4、用户下次会话时,客户端会自动将JWT写在HTTP请求头部的Authrzretion字段中
5、服务器对JWT进行验证,若验证成功,则确认此用户的登录状态
6、服务器返回响应
2、javaweb-身份验证攻击-JWT修改伪造攻击
我们知道JWT的声明内容变了,因此签名需要重新生成,生成签名又需要密码,我们尝试把Header改为None,实现身份绕过。
案例分析:
我这里是把webgoat放在kali里面,配置比较简单安装看这个,直接打开此关卡即可
抓到Tom的数据包
把access_token的值复制到JSON Web Tokens - jwt.io
进行解密,看数据包内容
我们要明白alg的value值最终决定了数字签名使用的何种算法,我们如果说把它改为None,就相当于明文,它这一关的要求就得到管理员权限,我们把admin的value改为true就可以了。
改none
改true
在HTTP传输过程中,Base64编码的"=","+","/"等特殊符号通过URL编码通常容易产生歧义,因此产生了与URL兼容的Base64 URL编码,所以我们把它们去掉即可。
发送数据:
3、javaweb-身份验证攻击-JWT密匙爆破攻击
如果签名算法,加密后的载荷和最终得到的加密字符串都已知,且密钥不够复杂的话,使用暴力破解便有可能将密钥解出,可以看看这位大佬写的好文章全程带阻:记一次授权网络攻防演练(上) - FreeBuf网络安全行业门户
0x01、 先进去看看描述
0x02、把它给我们的令牌进行解密
0x03、找到密匙
理清一下思路,我们要找到这个令牌的密匙,现在知道使用密匙加密后的数据,我们用python进行爆破,用github上相应的字典。
import jwt
import termcolor
if __name__ == "__main__":
jwt_str = R'eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1Njk3MjI2NDQsImFkbWluIjoiZmFsc2UiLCJ1c2VyIjoiVG9tIn0.Y2WgbXt9wjv4p4BdM_tA9f05sG-_n1ugojijOZMXx2_Gld_Ip4dOazj9K3iWVC68W_7_HEyu2_c0qSjtqDC0Vg'
with open('/YOUR-PATH/Top1000.txt') as f:
for line in f:
key_ = line.strip()
try:
jwt.decode(jwt_str, verify=True, key=key_)
print('\r', '\bbingo! found key -->', termcolor.colored(key_, 'green'), '<--')
break
except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):
print('\r', '\bbingo! found key -->', termcolor.colored(key_, 'green'), '<--')
break
except jwt.exceptions.InvalidSignatureError:
print('\r', ' ' * 64, '\r\btry', key_, end='', flush=True)
continue
else:
print('\r', '\bsorry! no key be found.')
0x04、重新签名
找到了密钥我们就要按照要求把用户名Tom改为WebGoat,同时修改它对对应的时间戳。
0x05 总结
想要爆破成功有二个因素
- 正确高效的字典
- 已经得到它的令牌
防御:
- 开发人员不应在JWT中暴露敏感信息,可使用工具将截获的JWT解析查看是否包含敏感信息。
- JWT弱口令爆破可以离线进行。
- JWT的安全性非常依赖密钥的长度及复杂度,建议密钥设置为32位及以上长度的随机字符。
4、javaweb-身份验证攻击-JWT修改伪造冒充
我认为这是逻辑漏洞的一种方式,其中包含了JWT令牌的相关技术,这一关目的是修改令牌,让Tom进行付款。JWT Refresh Token Manipulation – Mikail's Blog
前置知识:
就如同session会有存活时长一样,JWT的access_token也是有相类似的机制。session失活后,系统会要求用户再次身份验证,通过则重新颁发session;JWT则可使用refresh token去刷新access token而无需再次身份验证。
WebGoat中提到:
应在服务器端存储足够的信息,以验证用户是否仍然受信任。您可以考虑的事情有很多,比如存储IP地址,跟踪使用refresh token的次数(在access token的有效时间窗口中多次使用刷新令牌可能表示奇怪的行为,您可以撤销所有token,让用户再次进行身份验证)。还要跟踪哪个access token属于哪个refresh token,否则攻击者可能会使用攻击者的refresh token为其他用户获取新的access token
这段话中关键信息是,服务器中可能存在:未校验access token和refresh token是否属于同一个用户,导致A用户可使用自己的refresh token去刷新B用户的access token。
0x01、进入靶场
先用BP抓包,我们可以看到刚开始Authorization的value值为Bearer null,我们的目的是把它改为Tom的令牌
0x02、打开日志
可以看到有一条token,和一些与refresh相关的url信息。拿token去https://jwt.io/#debugger,可以看到:
是属于Tom,exp的时间是2018年(已过期)
我们查看有问题的代码:
//仅校验是否存在user和refreshToken,未校验两者对应关系,存在漏洞
if (user == null || refreshToken == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} else if (validRefreshTokens.contains(refreshToken)) {
validRefreshTokens.remove(refreshToken);
//返回JWT user的新token
return ResponseEntity.ok(createNewTokens(user));
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
思路:
- 从logfile中获取到Tom到过期JWT
- 利用账号密码:Jerry/bm5nhSkxCXZkKRy4 拿到Jerry账号的refresh token
- 利用Jerry的refresh token 和Tom的过期access token去刷新一下
- 拿到刷新后的token 结账
- 从logfile中获取到Tom到过期JWT
0x03、 拿到refresh token
利用账号密码:Jerry/bm5nhSkxCXZkKRy4 拿到refresh token
账号密码从源码中可得
0x04 刷新Token
0x05 拿到刷新后的access_token 结账
这个签名我们是不知道的,改为nono,再修改时间戳,同上
0x06 总结
当使用refresh_token机制时,服务器端存储足够的信息,以验证用户是否仍然受信任。(存储IP地址,跟踪使用refresh token的次数及是否在access_token过期后使用等等的信息)
当存在JWT泄漏和越权刷新JWT漏洞时,漏洞发生。
5、javaweb-身份验证攻击-JWT安全结合SQL注入
本关相对于把SQL注入和JWT认证相结合,实现身份验证失效。
0x01 先用BP抓到Tom下方的Delete数据包
把JWT数据放到https://jwt.io/#debugger解析一下:
这个题要有一定代码审计的能力,鄙人说不清楚,感兴趣的可以看看下面文章。