实验进度如下
利用漏洞完成后台登录利用漏洞修改管理员密码分析SQL注入原理,获取表名获取列名获取指定表中指定列的数据
漏洞利用
这个漏洞影响所有使用joomla 3.7版本及以下内容管理系统的网站,攻击者可以使用利用这个漏洞获取数据库的内容,包括用户数据和session数据。得到这些数据后,攻击者可以利用session登录网站后台,查看敏感信息,还可以修改原密码。
使用POC获取Session
- 在利用漏洞前,进入phpmyadmin后台,查看数据库中session表的内容,可以看到有两个session为游客,一个session为已经登录的用户,ID为748
-
session有时间限制,在运行脚本前,session已经更新过,利用SQL注入得到的session是最新的
更新后的session -
运行python脚本,传入的参数为joomla首页地址,获取到数据表lmbxc_session表的内容,其中记录了游客和已登录帐号的session,我们获取的session中包含了id,session值,用户名,说明了这个session是属于一个已登录帐号。
模拟登录
-
首先简单了解一下cookie和session。cookie用于弥补HTTP协议无状态的缺点,可以使用cookie来跟踪会话,cookie存储在浏览器中,当浏览器第一次访问服务器时,服务器为了追踪这个用户的状态,会给浏览器颁发一个cookie,通过在响应中加入Set-Cookie字段,告诉浏览器使用这个字段中的值来在本地建立Cookie。然后当浏览器再次访问同一网站时,会在HTTP报文头部携带这个cookie,服务器可以根据这个cookie来识别用户,提供基于用户身份的服务。
session对象用于服务器追踪用户状态的,与cookie不同的是session是以某种文件形式存放在服务器中的,当已登录的用户访问服务器时,可以使用session确认客户的身份。
cookie机制 -
所以拿到了session,我们可以在请求的cookie中带上session的值,伪造其他用户进行登录。在这里,我们使用burpsuite工具进行模拟登录,我们选择一个浏览器,设置浏览器的代理为burpsuite,使用浏览器请求joomla网站登录页面,在burpsuite中使用Proxy拦截请求,我们观察请求报文,发现在cookie中有一个以86dbc开头的key,我们在构造响应报文时需要这个key。我们将请求报文保存后,在burpsuite中设置拦截这条请求的响应报文。在服务器返回的响应报文的HTTP头部添加Set-Cookie字段,并构造Set-Cookie值为key:value的键值对,其中,key为请求报文cookie中的key,value为我们使用POC拿到的session值。刷新网站后,可以看到已经成功登录,可以成功进入网站后台。在整个过程中,我们并没有输入帐号或密码,就冒充其他用户进行了登录,网站的用户验证存在安全问题。
拦截到的请求报文
-
成功登录后,我们可以进入后台,修改管理员密码。可以看到,在资料修改界面,网站的安全验证不太安全,不需要输入旧密码就能直接修改密码。修改完成后,尝试原密码登录,已经失效,说明密码修改成功
修改密码界面
原理分析
这个漏洞出现在3.7.0新引入的一个组件“com_fields”,这个组件任何人都可以访问,无需登陆验证。由于对请求数据过滤不严导致sql 注入,sql注入对导致数据库中的敏感信息泄漏,例如用户的密码hash以及登陆后的用户的session(如果是获取到登陆后管理员的session,那么整个网站的后台系统可能被控制)。
注入原理
- 网站的注入点,访问此URL可以显示这个站点所有自定义字段的列表。程序会取list[fullording]后面的值,经过过滤后拼接到SQL语句中order by后,形成完整的查询语句,对自定义的字段进行排序。但由于过滤不严格,仅仅对一些特殊字符如",',\n,\r等进行了过滤,这就导致了注入。通过向这个参数传送特殊构造的SQL语句,可以执行我们想要的SQL查询,得到数据库中的内容
- order by 注入,在MySQL中,order by后面可以跟三种形式。其中,我们就是利用构造表达式来完成注。
- 列名(字符串,表中存在的列)
- 位置(整数,指定哪一列)
- 表达式(比如SQL的函数,如updatexml(),rand(),extravalue(),SLEEP()等)
- 基于时间的注入
- 构造以下语句,发现提交参数后,页面响应时间延长。证明构造的语句成功执行。后台程序没有对语句进行过滤转义等,我们可以进一步构造语句,获取数据
if(1=0,1,(SELECT(1)FROM(SELECT(SLEEP(10)))test))
- 基于报错的注入
- 构造以下语句。updatexml()函数是MYSQL对XML文档数据进行查询和修改的XPATH函数,这个函数的第一个参数指明lXML对象的名称,第二个参数为XPATH字符串,如果语法不对,就会保错,第三个参数为对XML进行更新的数据。我们就是要利用第二个参数,构造错误的XPATH字符串,通过报错信息来获取数据。可以看到,在保存信息中包含了user()函数的返回结果,注入成功
updatexml(1,concat(0x3a,user()0x3a),0)
报错信息-显示user()
- 其他会报错的函数,如extractvalue(),也是注入的一种手段,和updatexml()效果相同,都是因为回显错误信息,泄漏了数据
extractvalue(666,concat(0x3a,version(),0x3a))
报错信息-显示version()
获取表名
获取数据库表名,要构造攻击语句获取数据,我们首先要知道数据库中存储重要数据的表的名称。在MySQL中,information_schema数据库专门存储了MySQL自己数据库的元数据。我们可以利用这个数据库中的tables表来获取表名。因为查询结果中有多行会注入失败,所以只能一行一行的获取,这样手动获取表名太麻烦,我们可以使用burpsuite中的Intruder工具,Burp Intruder是一个强大的工具,用于自动对Web应用程序自定义的攻击,Burp Intruder 是高度可配置的,并被用来在广范围内进行自动化攻击。在本次实验中,我们用Intruder工具枚举参数值,不断修改LIMIT语句第一个参数值,即返回记录的偏移量,然后在response中设置正则匹配,提取表名及所属数据库。这样可以获取到所有表名
UpdateXML(2, concat(0x3a,(SELECT(concat(TABLE_SCHEMA,0x3a,TABLE_NAME))FROM(information_schema.tables) LIMIT 0,1),0x3a),1)
后台数据库中的表名数据
通过burpsuite获取到的表名
在获取数据库表名时,发现有一些不足。获取到的表名在返回时前缀被屏蔽,而且查询结果有长度的限制,只能看到16字节内的字符。我们先解决第一个问题,使用下列语句,使用hex()函数,将字符串转换为16进制的表示形式,在本地又重新转换为字符串,这样表名的前缀就不会被屏蔽,可以得到完整的表名
UpdateXML(2, concat(0x3a,(SELECT(HEX(TABLE_NAME))FROM(information_schema.tables) LIMIT 85,1),0x3a),1)
报错信息-16进制表示的表名
16进制表示的表名转换为字符串表名
获取列名
解决了获取表名不完整的问题后,我们可以尝试根据表名获取对应表的所有列名。由于程序屏蔽了单引号,我们不能使用常规的字符串条件如 TABLE_NAME='users' 进行判断。所以我们使用LIKE操作符代替,LIKE操作符支持16进制参数,可以将目标表名字符串转换为16进制数值,放在LIKE语句后充当条件,这样,查询结果就会返回目标表的所有列名。我们还是用burpsuite来循环参数来获取所有列名
UpdateXML(2,concat(0x3a,(SELECT(concat(COLUMN_NAME))FROM(information_schema.COLUMNS) WHERE TABLE_NAME LIKE 0x6c6d6278635f636f6e74656e7474 LIMIT 0,1),0x3a),1)
字符串表名转换为16进制表示
users表中的列名
报错信息-列名
通过burpsuite获取的列名
获取内容
获取目标表的特定列的值,在表名和列名都成功获取后,我们可以构造SQL语句,查询特定表的特定列的值。
下列语句获取lmbxc_content表中title列的一个值。
UpdateXML(2,%20concat(0x3a,(SELECT(concat(title))FROM(lmbxc_content)%20LIMIT%200,1),0x3a),1)
content表中title列的内容
报错信息-内容
网友评论