SQL Injection 簡介
SQL Injection是一個非常基礎的Web 攻擊的一個技巧,這邊講一些理論或基礎知識的部分。SQL Injection是一種經典又經久不衰的漏洞,這種漏洞允許攻擊者可以改變應用程式的邏輯,對後方資料庫內的資料進行查詢。通常會用來竊取未經允許的資料內容,包含其他使用者的個人資料等等,在一些情況下,甚至可以達到RCE的效果。
在我們做滲透測試(PT)的時候通常會拿SQL Injection來達成三個目的。第一個是搜尋隱藏的數據,第二個是干擾網站應用程式的邏輯,第三個是從其他table中搜尋數據。
基本上我們要進行手動SQL Injection的話,通常會先從這四個地方下手:
- information_sechema
- schema_name
- table_name
- column_name
我們必須要先了解這個Database的Schema長什麼樣子。在很多DB裡面會預設自帶一個Information Schema用來儲存中繼資料,也就是Meta Data。它會存很多,例如說裡面有什麼樣的table、什麼樣的column,還有DB name等等的東西,所以我們通常會先從Information Schema這邊下手,去搜尋我們到底需要查詢那些感興趣的資料,例如說user name或是說user的資料庫等等的。
那在做SQL Injection的時候,我們通常會先從最基本最前面的東西開始,第一個第一步就是拿取資料庫名稱的列表,那我們會使用select的語法:
SELECT schema_name FROM information_schema.schema
這種方式可以讓我們得到一個資料庫的名稱,因為我們可能有不同的資料庫去管理不同的東西,所以我們會想要把他擁有的資料庫全部列舉出來,方便我們去做篩選。
第二個是拿取table名稱的列表,我們拿到這個資料庫名稱的列表之後,可能會選中某一個資料庫的名稱,下一步就可以拿取table的名稱列表。就是我們搜尋select table name from information schema裡面的table,然後從這個table schema是你剛剛指定的某一個資料庫的名稱。
SELECT table_name FROM information_schema.tables WHERE table_schema= “資料庫名稱”
第三步我們希望可以拿取column的名稱列表,跟剛剛類似,select column name就是我們想要知道的column name。然後從這個information schema中的column裡面,從你指定的table名稱裡面去搜尋:
SELECT column_name FROM information_schema.columns WHERE column_schema= “Table名稱”
最後一步就是我們搜尋我們特定欄位的資料,從我們剛剛拿到的某一個column的名稱搜尋可能是username password date、admin的一些數據,或是說其他有的沒的,然後就是資料庫名稱.table名稱,這樣子的方法去搜尋。
SELECT 欄位名稱 FROM 資料庫名稱.table名稱
我們現在了解到手動的SQL injection要怎麼進行,那我們就開始講我們可能會在Pentest中可能會對它做什麼樣的操作。第一個就是搜尋隱藏數據。例如說我們今天有一個購物平台,網址是insecurewebsite.com,然後products>categories>gift
https://insecure-website.com/products?category=Gifts
這代表後方資料庫它所使用的SQL搜尋語法可能是
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
這個星號它代表所有,我所有在這個地方的所有東西所有column什麼東西的我都要;product就是一個叫product table的這裡;WHERE category = 'Gifts'
代表其中category是gift的東西;然後release=1代表公開發布的商品。
所以這樣組成就變成是: 在category是gift的地方,而且是公開發布的商品裡面的product table裡面的所有細節。
這個category我們可以去做手動輸入的,我們可以選中我們自己的category,可以在這邊gift後面加一個A--代表註解,可以看到下面灰色這個部分註解之後的地方全部都不會被執行,因為查詢不再包含and release=1的判斷,導致SQL查詢將所有不同release的狀態,可顯示出來包含未上市的產品。也就是說假設它有三個狀態,一個是release=0 release=1 release=2,那原本我們只用release=1的東西才會顯示出來,現在把它註解掉之後,只要符合前面這些條件的release=0 release=1 release=2的產品,全部都會顯示出來。
SELECT * FROM products WHERE category = 'Gifts'A--' AND released = 1
第二個是干擾網站應用程式的邏輯。這個可能字面上比較難以直接去理解,我們可以用一個登入的功能去做一個說明。很多網站上都會有登入的功能,那這後面常常也常用是SQL來搜尋資料庫是否存在這個用戶,或是用戶的密碼是否正確等等。
例如說我們用戶發出以下的request:
POST /login.php HTTP/1.1
Host: example.com
...
username=bob&passwd=alice
而網站應用程式使用以下SQL語句進行查詢:
SELECT * FROM users WHERE username = 'bob' AND password = 'alice'
那我們攻擊者可以在具有username的情況下,用無密碼的方式通過這個身分驗證,只需要我們將where這個查詢子句中的password判斷註解掉即可。
例如說我們在username輸入admin單引號然後--,這時候SQL的查詢語句就會變成select星號from user where username=admin,然後就沒了。所以它只要username有一個叫做admin的人,不管後面password是不是正確的,都會讓你登入。也就是說我們可以在無密碼的狀況下,做一個登入的動作,其他的像我們最常聽的or 1=1其實也是一樣的例子。
SELECT * FROM users WHERE username = 'admin'--' AND passwd = ''
這時候如果我們寫成下面這樣,這個查詢語句會變成是where username=空值,空值一定是錯誤的,但是我們後面又加了一個判斷是or 1=1,那or 1=1一定是true,所以變成是username是false沒關係,因為後面還有一個true,那就可以查詢成功成功的bypass。
SELECT * FROM users WHERE username = '' OR 1=1 --' AND passwd = ''
那or 1=1怎麼知道是哪個帳號被登入,通常以我自己的經驗來說,通常是這個資料庫或是這個網站應用程式它的第一個使用者,也就是資料庫最前面,或是說最久第一個存在的這個資料庫裡面的使用者。那第一個通常是admin,所以這也是為什麼在一些例子上用or 1=1是可以直接進行admin的登入。
第三個,從其他table裡面搜尋數據。當我們應用程式存在SQL injection漏洞的時候,我們就可以用union這個語法來搜尋這個資料庫其他table的資料。union它會執行附加的select查詢,一起return結果。
例如說我們剛剛第一個例子中的購物網站,那應用程式用以下的SQL語法執行user輸入gift的查詢:
SELECT name, description FROM products WHERE category = 'Gifts'
而攻擊者就可以寫入這串SQL語法,使應用程式返回所有user table裡面的username、password以及product table中的name跟description。
' UNION SELECT username, password FROM users--
原始搜尋邏輯變成:
SELECT name, description FROM products WHERE category = '' UNION SELECT username, password FROM users--
我們可以看到說我們from這邊是table的名稱,那我們這次除了想要知道這個product這個table之外,我們還想要知道user這個table,所以我們寫入union select username, password from users。這個語法就導致原本的搜尋變成select name description from product where category=沒有東西,再來union select user name password from users --,也就是說它會先從這個product table中搜尋name跟description,而且是符合category=什麼東西都沒有,那這邊就會什麼東西都沒有,或是全部都顯示出來,再附加一個搜尋union select,然後在這個users這個table裡面去搜尋username跟password,然後後面註解掉。
這個在OSCP的考題裡面,曾經在應該在lab裡面遇過有一個sql injection的漏洞,這些方法讓我去搜尋到一組admin還是某個user的帳號密碼,然後我可以用這組,應該說它有一個帳號跟密碼的一個list,然後admin是不成功的,可是其他的帳號有某一個帳號可以成功,我就直接用那一組帳號進行ssh的登入,然後就可以進行後面的動作這樣子。
還有另外一種我遇過的情況是,它有兩個網站,一個是加在8080 port還是加在某個奇怪的port,然後我拿這個username跟password去8080 port的網站上也可以進行登入。然後裡面有一個我記得是可以拿到web shell的一個的漏洞的應用程式這樣子。但是因為它是要預驗證的,所以直接到8080 port的話你會發現它怎麼打都打不進去,因為你沒有使用者帳號密碼,你搜尋到的exploit全部都是要認證的。所以你就先到8080 port這邊,先拿到一組credential之後去try 8080的login,之後再進行利用這樣子。
那在某些場景下,SQL injection除了可以拿來撈取機敏資料外,也可以拿來做作為initial Foothold。initial Foothold的意思就是說建一個起始點,就是你可以拿到shell。這是因為SQL語法可以執行某些使檔案輸入或輸出的功能例如說in/out file跟load file。那這個可以怎麼利用呢?
例如說in/out file的話,最簡單的想法就是寫一個web shell,或是寫各種各樣的東西進去這個網站或是伺服器裡面,然後用load file的方法去看到某些credential,或是說ssh的rsa key之類的。
基本上這些SQL injection理論的方面到這裡結束,那SQL injection還有很多的利用手法,例如基於error的或是delay的,那我們這個章節只提供SQL injection的一些基本概念跟一些基本語法。OSCP裡面不會特別特別注重手寫的SQL injection的payload,而在真實場景中的滲透測試,依據環境跟防禦的不同,手法很多變。若是想深入了解SQL Injection,可以先熟悉SQL語法後,用Portswigger提供的Lab做練習。
這個是之前我之前看到一個作為Waf Bypass的一個方式,其實它就是把or換成兩個pipe,1=true,然後點點就維持原樣:
|| true –
SQL injection也不只一種類型,它還有基於time的、基於error的,或是像剛剛說的blind的,各種各樣的方式。OSCP不會考那麼細,它只會需要你就是有些基本的SQL injection的一些基本的攻擊手法就足夠了。裡面的SQL injection都不會到太複雜。