【小迪安全】Day40JAVA安全-JWT安全&预编译CASE注入
沙漠里的鲸
编辑于 2021年12月20日 01:02
收录于文集
共92篇

【小迪安全】Day40JAVA安全-JWT安全&预编译CASE注入

通过前期的 WEB 漏洞的学习,掌握了大部分的安全漏洞的原理及利用,但在各种脚本语言开发环境的差异下,会存在新的安全问题,其中脚本语言类型 PHP,Java,Python 等主流开发框架会有所差异。

Javaweb-SQL 注入攻击-预编译机制绕过

1.  SQL注入的防御

---防御 sql 注入:1.session2.参数绑定存储过程

 

#利用 session 防御

---session 内容正常情况下是用户无法修改的

---select * from users where user = "&#​39;" +session.getAttribute(&#​34;UserID") + &#​34;'&#​34;;(session在服务器内)

 

#参数绑定方式,利用了 sql 的预编译技术

---常用Statement、PreparedStatement  CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询 PreparedStatement 用于执行参数化查询,而 CallableStatement则是用于存储过程

---Statement 该对象用于执行静态的 SQL 语句,并且返回执行结果。 此处的SQL语句必须是完整的,有明确的数据指示。查的是哪条记录?改的是哪条记录?都要指示清楚。

---PreparedStatement ,SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。(这里是PreparedStatement不允许一个占位符(?)有多个值,注入的语句也就不会被执行)

---在使用参数化查询的情况下,数据库系统不会将参数的内容视为SQL指令的一部分来处理

---而是在数据库完成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 语句。(原理类似堆叠注入,一条语句出现多条命令)

#进入靶场

 

#数据包分析(发送的请求指向的是源代码中的server)

#源代码分析

---发现SQL语句,并且语句后面有order by(传递column参数),寻找传参column的地方

---抓包工具修改

---修改ip为1

---报错的时候存在order by,因此可以用case  when语句

---case when原理:普通的按某一个字段或者多个字段排序没办法满足我们的需求时,可以通过case when来排序

---构造语句(说实话我看不懂这个语句,应该是):

select id, hostname, ip, mac, status, description from servers  where status <> 'out of order&#​39; order by " + case when(select substr(ip,{0},1)='{1}&#​39; from servers where   hostname='webgoat-prd&#​39;)

---根据报错构造Python脚本(webgoat在虚拟机里,不能用Python脚本跑)

---设置请求数据的数据头,和cookie

---设置请求的代理,也就是先发送给抓包工具

---resp为request请求(目标URL,请求头(XML格式),cookie,代理)

---如果webgoat-acc字符串在返回的json数据的第一个元素的主机名,就认为是对的,就输出结果(emmmm感觉不太懂,只理解了如果是Java语句的SQL如果是预编译的的话,基本不能注入。除非存在order by,可以用order by case when 来构造查询语句(具体构造也不太会,突然感觉408和数据库的重要性),来进行数据库的查询。其实这个Python代码感觉挺有用的,但是代码看的似懂非懂)

Java的jwt令牌原理

#什么是JWT

---JSON Web Token(JSON Web 令牌)是一种跨域验证身份的方案。JWT 不加密传输的数据,但能够通过数字签名来验证数据未被篡改(但是做完下面的 WebGoat 练习后我对这一点表示怀疑)。

---JWT 分为三部分,头部(Header),声明(Claims),签名(Signature),三个部分以英文句号.隔开。

---JWT 的内容以 Base64URL 进行了编码。

#头部(Header):

{

"alg&#​34;:"HS256&#​34;,

"typ&#​34;:"JWT&#​34;

}

---alg是说明这个JWT 的签名使用的算法的参数,常见值用HS256(默认),HS512 等,也可以为None。HS256表示 HMAC SHA256。

---typ说明这个 token 的类型为 JWT

#声明(Claims):

{

"exp&#​34;: 1416471934,//到期时间

"user_name&#​34;: "user&#​34;,

"scope&#​34;: ["read&#​34;,"write&#​34;],

"authorities&#​34;: ["ROLE_ADMIN&#​34;,"ROLE_USER&#​34;],

"jti&#​34;: "9bc92a44-0b1a-4c5e-be70-da52075b9a84",//JWT标识

"client_id&#​34;: "my-client-with-secret&#​34;

}

#JWT 固定参数有:

---iss发行人

---exp到期时间

---sub主题

---aud用户

---nbf在此之前不可用

---iat发布时间

---jti:JWT ID 用于标识该 JWT

 

#签名(Signature)

---服务器有一个不会发送给客户端的密码(secret),用头部(header)中指定的算法对头部和声明的内容用此密码进行加密,生成的字符串就是 JWT 的签名。

下面是一个用 HS256 生成 JWT 的代码例子

HMACSHA256(base64UrlEncode(header) + ".&#​34; + base64UrlEncode(payload),secret)

Javaweb-身份验证攻击-JWT 修改伪造攻击

1.  进入靶场

---更改收到的令牌成为管理员用户,并且重置投票(管理员密码丢失)

---这里的操作和之前更改cookie的操作差不多,只是更改的数据是加密的数据

---点击Tom用户抓取数据包(传参用户和cookie,token为空白)

---点击重置投票(发现只有管理员才能重置),获取到一段cookie

#将cookie的acces_token数据解密:

---头部:”alg”:”HS512”(jwt使用算法的参数为HS512)

---声明:iat:发布时间(这里是时间戳形式);admin:是否是管理员,这里为布尔;user:用户名

---下面是签名,(网站只能识别前面的首部和声明,后面的签名需要输入密匙才行)

---将admin修改为Ture尝试(报错说签名不对,应为只改了声明,但是签名没有改,由于没有密匙,所以签名改变不了)

2.  修改首部和声明来绕过签名修改

#修改首部为None,声明为TRUE

---去除=添加到首部(注意由于首部为None,第三部分签名就为空,否则报错)

---注意:在HTTP传输中,base64编码中的=,+,/等等特殊符号通过URL解码容易产生歧义,因此产生了与URL兼容的base64 URL编码

---重置成功

3.  如果有密匙

---首部不需要更改

---更改admin为TRUE

---输入密匙

---修改数据包传输

 

Javaweb-身份验证攻击-jwt密匙爆破攻击

1.  进入靶场

---一旦找出密钥就就可以创建新令牌进行签名。因此,只要密钥足够强大,暴力破解字典就不可行

---通过以下令牌找出密钥,并将用户名改为WebGoat

---令牌解密(iat:开始时间,exp:失效时间)

2.  Python脚本分析(注意小迪视屏的脚本是Python2,这个字典在提示里面可以下载,我的环境是Python3运行不了)

#脚本逻辑

---若签名直接校验成功(原文为失败,猜测为作者手误),则 key_ 为有效密钥;

---若因数据部分预定义字段错误(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError,jwt.exceptions.InvalidIssuedAtError,jwt.exceptions.InvalidIssuedAtError,jwt.exceptions.ImmatureSignatureError)导致校验失败,说明并非密钥错误导致,则 key_ 也为有效密钥;(这不懂啊,总得来说就是通过比对字典的密匙加密后的签名,来获取密匙)

---若因密钥错误(jwt.exceptions.InvalidSignatureError)导致校验失败,则 key_ 为无效密钥;

---若为其他原因(如,JWT 字符串格式错误)导致校验失败,根本无法验证当前 key_ 是否有效

---得出答案:victory

#更改结束时间exp

#修改jwt

---exp和username

---输入密匙

---输入字典爆破的签名(爆破成功,爆破的前提条件:1.可用的字典2.获得完整的令牌的格式)

Javaweb-身份验证攻击-jwt修改伪造冒充

1.  进入靶场

---通过伪造让Tom进行支付

---查看日志进行分析(这里发现一个token传参使用的JWT令牌)

---解析jwt令牌(发现为Tom的数据包)

---修改exp,并且将首部改为None,去掉签名部分构造JWT

2.  点击checkout抓包(查找与token匹配的数据包)

---在授权这里写入jwt(有些疑惑为什么不加上前面的参数token)

---返回成功(这里没有检测签名失效时间的条件下)

Javaweb-身份验证攻击-jwt安全结合SQL注入

1.  进入靶场

#杰瑞相删去汤姆的账户,但是他只有自己的令牌

---点击汤姆的删除抓包分析

---将token传的参数解密(发现是杰瑞的JWT)

2.  源代码分析

---这里执行了一条SQL语句,传递的参数是kid

---这个函数传递的参数是token,如果token不为空,就创建一个令牌的对象,并且将令牌解密

--- .Jwts.parser().setSigningKeyResolver(自定义方法获取签名KEY).parseClaimsJws(token);//通过自定义方法获取签名key然后对token进行JWT解析

---kid为令牌的首部里面的kid(也就是说,这里没有预编译函数,所以存在注入点)

---利用sql inject,控制查询语句的查询值来控制JWT的密钥,从而伪造JWT,完成任务。

#数据库查询语句(说明jwt_keys表中有一个id的值是:“webgoat_key”):SELECT key FROM jwt_keys WHERE id = 'webgoat_key'

3.  构造注入语句

SELECT key FROM jwt_keys WHERE id = ‘y' and 1=2 union select id from jwt_keys where id ='webgoat_key&#​34;

---查询id为webgoat_key中的所有信息(主要是查看签名信息的密钥)

CTF-Node.js-前端JWT登录安全-伪造admin实现getflag

1.  进入靶场

#发现一个登录界面

---登录注册的用户123456抓包分析

---发现post传输的数据的格式为jwt令牌格式

---登录之后获取flag发现没有权限(应该是获取管理员,才能查看)

2.  查看nodejs框架

---nodejs简介(猜测是一个前端框架)

---通过/controllers/api.js查看框架信息(这里发现管理员帐号:admin,而且他的JWT验证在前端)

3.  修改JWT

---修改首部alg为none

---修改username为admin,密匙id的secreted改为空[]

---修改令牌和用户名

---发现返回成功

---放出数据包发现已经管理员登陆

---获取flag