前言
联合查询注入两个前提
- 存在数据显示位
- union左右列数相同
联合查询注入分三步
- order by/union select 猜解列数
- union select 寻找显示位
- 注入数据库查询攻击
联合查询注入
order by猜解列数
sqlmap采用二分法猜解列数.
- 以
10
为区间进行判断.设置highCols = 10 - 构造
ORDER BY highCols
,若与正常页面"不相似",进行以下操作,反之继续判断直到判断出不相似状态,若一直找不到(highCols>ORDER_BY_MAX),终止判断. - 在该区间内取中间值mid = highCols - (highCols - lowCols) // 2 (//为整数除法,舍去小数)
- 构造
ORDER BY mid
,发送请求,若与正常页面相似,则说明返回的页面是正常的,即列数是>=mid,这时令lowCols = mid
,否则令highCols = mid
。 - 进行以上循环,直到(highCols - lowCols) < 2,则确定lowCols为为列数。否则继续判断增加区间判断.
贴上源码便于理解.
上面还有一个问题就是相似的页面,sqlmap从以下几个维度来进行判断的.
- 正则搜索
(warning|error):
,order (by|clause)
,unknown column
,failed
在植入payload的请求中,不在正常请求中并且非heavilyDynamic(后续介绍)和相似度一致(后续介绍),满足以上条件返回true. - 正则搜索
data types cannot be compared or sorted
不在 植入payload的请求中,返回true.
同样贴出源码
return not any(re.search(_, page or "", re.I) and not re.search(_, kb.pageTemplate or "", re.I) for _ in ("(warning|error):", "order (by|clause)", "unknown column", "failed")) and not kb.heavilyDynamic and comparison(page, headers, code) or re.search(r"data types cannot be compared or sorted", page or "", re.I) is not None
Union猜解列数
总结来说,构造union all select NULL
, 每次增加NULL,判断返回页面,来确定列数.SQLmap会发送多个这种请求,记录该页面与原始页面的相似度,存入items的集合中.并寻找最大的相似度和最小的相似度.
接下来sqlmap通过两种方式对以上数据进行分析.从源码一步一步了解具体实现方式:
if not retVal:
#取出minItem,maxItem
for item in items:
if item[1] == min_:
minItem = item
elif item[1] == max_:
maxItem = item
#从`radios`去除最小值。
if min_ in ratios:
ratios.pop(ratios.index(min_))
#从`radios`去除最大值。
if max_ in ratios:
ratios.pop(ratios.index(max_))
# ratios所有元素都等于min_,且都不等于max_,判定列数为maxItem中count
if all(_ == min_ and _ != max_ for _ in ratios):
retVal = maxItem[0]
#ratios所有元素都等于max_,且都不等于min_,判定列数为minItem中count
elif all(_ != min_ and _ == max_ for _ in ratios):
retVal = minItem[0]
- 去除最大的radio和最小的radio
- 如果剩余的页面都完全相同,且与最小或最大的radio其中一个相同,不与另一个相同.
all(x)
:如果all(x)参数x对象的所有元素不为0、’’、False或者x为空对象,则返回True,否则返回False).
若上述未检测处理,则使用第二种方式(因为上述if处理方式一样,故归为一类)
elif abs(max_ - min_) >= MIN_STATISTICAL_RANGE:
# 通过stdev计算标准差
deviation = stdev(ratios)
if deviation is not None:
# 计算标准差区间[平均值-7*标准差,平均值+7*标准差]
lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation
if min_ < lower:
retVal = minItem[0]
if max_ > upper:
if retVal is None or abs(max_ - upper) > abs(min_ - lower):
retVal = maxItem[0]
首先了解下标准差的概念.
简单来说,标准差是一组数值自平均值分散开来的程度的一种测量观念。一个较大的标准差,代表大部分的数值和其平均值之间差异较大;一个较小的标准差,代表这些数值较接近平均值。例如,两组数的集合{0, 5, 9, 14}和{5, 6, 8, 9}其平均值都是7,但第二个集合具有较小的标准差。述“相差k个标准差”,即在 X̄ ± kS 的样本(Sample)范围内考量。标准差可以当作不确定性的一种测量。例如在物理科学中,做重复性测量时,测量数值集合的标准差代表这些测量的精确度。当要决定测量值是否符合预测值,测量值的标准差占有决定性重要角色:如果测量平均值与预测值相差太远(同时与标准差数值做比较),则认为测量值与预测值互相矛盾。这很容易理解,因为如果测量值都落在一定数值范围之外,可以合理推论预测值是否正确。
以下是一个正态分布图,上述计算的正常页面的相似度看作一个正太分布.
可以看到大部分的随机数据集中在中间。x轴为标准差.可以计算出来约有99.99%的数值落在7个标准差之间.换句话说,sqlmap保证正常的请求99.99%的概率落在这上面.超出该区间的99.99%不是正常请求.依此判断列数.
寻找输入点
为了方便分析,假定确认的列数为3.大概的思路是构造payloadUNION SELECT NULL, NULL, NULL
,依此将其中 NULL替换为随机字符串,从结果寻找随机字符串.
我在测试的时候也发现了一个坑,漏洞代码如下:
sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo "<font size='5' color= '#99FF00'>";
echo 'Your Login name:'. $row['username'];
echo "<br>";
echo 'Your Password:' .$row['password'];
echo "</font>";
}
返回数据限制成一行,如果按照上述思路,就会产生问题,不会回显第二个查询的数据.sqlmap针对这种将第一个查询设置为false,来保证第二个查询可以查询到数据.
总结
以上是关于自动化union检测的思路,能力有限可能存在错误.仅供参考.