Day 14 了!!不知不覺過了兩週!!!
說實話,沒想到自己可以走那麼遠 xddd
每天幾乎都會遇到卡關,但為了順利交出鐵人賽,必須要努力解決><從中真的是大進步!!!
Web 漏洞部分,因為時間比較緊湊,目前只有實作到 SQL Injection 跟 Command Injection QQ。但對於滲透測試的流程、思路、Linux 提權等等,過了兩週,功力真的大進步!!!
其他常見的 Web 漏洞像 IDOR、XSS、LFI、RFI、SSRF、CSRF、File Upload等雖然很重要,但 CPTS 考試還有 AD、Windows 靶機這些更複雜的區塊也是重點,所以其他 Web 漏洞,我們就等之後有時間再一起練習!
Day 12 的方法二,我們是利用資料庫的欄位資訊,把未上架的產品列出,來成功闖關。
當時雖然成功解題了
但:information_schema 真的懂了嗎?
……老實說,沒有 xddd
比較像是當下有一些想法,再搭配 AI 一起摸索,最後是有把題目解出來沒錯,但其實自己沒有真的搞懂 xddd
如果現在問我:
「資料庫裡到底有哪些 Table?我要怎麼知道?」
我可能還是會先:恩......讓我想想
所以今天就趁這個機會,把 information_schema 好好搞懂。
不只是知道「這樣下 SQL 可以解題」,而是要能靠自己做到:
資料庫裡有哪些資料表? → 查 INFORMATION_SCHEMA.TABLES
這張資料表有哪些欄位? → 查 INFORMATION_SCHEMA.COLUMNS
知道資料表名跟欄位名 → SELECT 撈出資料
這樣下次再遇到類似的 SQL Injection,才不會又變成:
AI:你可以這樣下
我:喔喔好 🤣
而需要達到這樣的功力,不能靠背的,必須要真的理解!
在開始練習,強化實作能力之前,先把幾個基礎觀念強化一下!
要搞懂 SQL Injection,以及要怎麼利用 information_schema 找到想要的資料,得先知道資料庫裡面的東西是怎麼放的~
一起想像一下:資料庫就像是 Excel。
用一個假設的學校系統來看整個資料庫長什麼樣子:
┌──────────────────────────────────────────────────┐
│ 📁 學校資料庫 │
│ │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ 📋 通訊錄 │ │ 📋 成績單 │ │
│ │ 存學生的聯絡方式│ │ 存每個人的考試成績│ │
│ ├───────────────┤ ├───────────────┤ │
│ │ id │ │ id │ │
│ │ 姓名 │ │ 學生名 │ │
│ │ 電話 │ │ 科目 │ │
│ │ Email │ │ 分數 │ │
│ └───────────────┘ └───────────────┘ │
│ │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ 📋 教師名單 │ │ 📋 帳號密碼 │ ⬅ 是寶藏!!! │
│ │ 存老師跟教哪科 │ │ 存登入用的帳密 │ │
│ ├───────────────┤ ├───────────────┤ │
│ │ id │ │ id │ │
│ │ 老師名 │ │ username │ │
│ │ 科目 │ │ password │ │
│ └───────────────┘ └───────────────┘ │
└──────────────────────────────────────────────────┘
一個資料庫裡面可以有好幾張資料表(Table),每張資料表裡面又可以有好多欄位(Column)。
SQL Injection 的目標就是:
先找到資料庫裡有哪些資料表 → 找到目標資料表 → 找出裡面的欄位 → 再把想看的資料撈出來。
把其中一張資料表打開來看,就像 Excel 工作表:
通訊錄 這張「工作表」:
欄位 → id | 姓名 | 電話 | Email
資料 → 1 | Kitty | 0912-345-678 | kitty@example.com
資料 → 2 | Kuromi | 0922-111-222 | kuromi@example.com
通訊錄、欄位有 姓名 跟 Email,就能寫 SELECT 姓名, Email FROM 通訊錄 把資料撈出來。
但問題是——我們一開始連「有一張資料表叫通訊錄」都不知道。所以要從更外面的層級開始問起。
資料庫(Database)——裝很多張資料表的容器
就像上面那張圖,一個資料庫裡面會有好幾張資料表——通訊錄、成績單、教師名單、帳號密碼⋯⋯我們要做的就是找出有哪些資料表,然後鎖定最香的那張(像帳號密碼)。
伺服器(Server)——跑資料庫的那台機器
一台伺服器上可以跑好幾個資料庫。除了應用程式自己的資料庫,系統也會有自己用的。就像一台電腦上有好多個 Excel 檔案——有的是你自己的,有的是系統預裝的。
SQL Injection 的時候,我們要先確認 「我現在在哪個資料庫裡」 ,才知道該從哪裡開始查資料表。
Schema——可以先把它想成資料庫裡的「資料夾」
Schema 可以先把它想成資料庫內部的「資料夾」,用來分類、管理不同的資料表。
MSSQL 的預設 schema 叫 dbo——很多使用者自己建的資料表幾乎都放在這裡。等一下查資料表的時候,我們會加 WHERE TABLE_SCHEMA='dbo' 來過濾,先把範圍縮小到 dbo 裡的資料表,避免系統相關的雜訊太多。
(對照 Day 12:PostgreSQL 的預設 schema 叫 public,所以當時查資料表時會用 WHERE table_schema='public')
整個結構從大到小:
伺服器 → 「我在哪台機器上?」
└── 資料庫 → 「這台機器上有哪些資料庫?我在哪個裡面?」
└── Schema → 「這個資料庫裡有哪些資料夾?應用程式的資料表在 dbo 裡」
└── 資料表 → 「dbo 裡有哪些資料表?admin_users!(完全是寶藏呀!有帳密的話就更好了!!!)」
└── 欄位 → 「admin_users 有哪些欄位?有 username 跟 password_hash」
└── 資料 → 「SELECT username, password_hash → 帳密到手」
所以 SQL Injection 就是照這個順序,一層一層往下挖:
我在哪個資料庫? → 有哪些資料表? → 資料表裡有哪些欄位? → 撈出資料
「一層一層往下問」,但問誰?我們連資料庫裡有什麼資料表都不知道,總不能用猜的吧~
其實資料庫自己就有一份「目錄」,記錄了裡面所有的結構——有哪些資料表、每張資料表有哪些欄位。這個目錄叫 INFORMATION_SCHEMA。
想像一下:你走進一個完全陌生的圖書館,不知道有什麼書、放在哪裡。你可以一個書架一個書架翻——但更聰明的做法是先去看目錄。目錄會告訴你:這間圖書館有哪些區、每區有哪些書。
INFORMATION_SCHEMA 就是資料庫的目錄。裡面有很多可以查資料庫結構資訊的內容,我們這次最常查的是這三個:
| 我想知道什麼 | 查哪張目錄 | 撈哪個欄位 |
|---|---|---|
| 有哪些 Schema(資料夾)? | INFORMATION_SCHEMA.SCHEMATA |
SCHEMA_NAME |
| 資料庫裡有哪些「資料表」? | INFORMATION_SCHEMA.TABLES |
TABLE_NAME |
| 某張資料表裡有哪些「欄位」? | INFORMATION_SCHEMA.COLUMNS |
COLUMN_NAME |
基礎觀念搞清楚了,來實際操作。
今天的實作素材,是跟Claude討論一起製作出的 SQL Injection 練習靶機 - BaijiaShop敗家線上商城
這個靶機的後端是 MSSQL,跟 Day 12 的 PostgreSQL 不一樣——但information_schema兩種資料庫都支援
那我們就來走一遍完整流程,把上面說的觀念都串起來~
情境: 我們在測試敗家線上商城,發現搜尋功能有 SQL Injection。
目標: 取得敗家線上商城的管理者帳密!!!(超美好)
注入點在商品搜尋框,後端把我們的輸入直接拼進這句 SQL:
SELECT id, name, price, description FROM products WHERE name LIKE '%[我們的輸入]%'
以這個靶機(MSSQL)為例,資料庫結構長這樣(先不准偷看資料庫結構!!!)
BaijiaShop(資料庫)
├── dbo(即Postgresql的public)
│ ├── products(商品)── 6 筆商品
│ ├── categories(分類)
│ ├── customers(會員帳密)
│ ├── orders(訂單)
│ ├── order_items(訂單明細)
│ ├── coupons(折價券)
│ ├── admin_users(管理員帳密)⬅ 寶藏在這!!!
│ └── app_config(系統設定)⬅ 藏了一堆金鑰,喜歡!
└──另一個 schema,放系統相關資料表
UNION 兩邊欄位數必須一樣,所以第一步要先確定原本查詢有幾欄。
怎麼找?用 ORDER BY n。ORDER BY 1 就是「按第 1 欄排序」,ORDER BY 2 就是「按第 2 欄排序」⋯⋯如果你 ORDER BY 的數字超過實際欄位數,資料庫就會報錯。所以慢慢加上去,加到報錯的前一個數字就是答案。
嘗試 ORDER BY 1-- -:

嘗試ORDER BY 5-- -
嘗試ORDER BY 4-- -

ORDER BY 5 報錯,ORDER BY 4 可以正常執行——所以 products 這張資料表是 4 個欄位
UNION SELECT 前後對應位置的欄位,資料型態要能對得上。所以從第 1 欄開始,一欄一欄把字串輪流塞進去測試,其他欄位全部用 NULL 佔位。
測試第一個欄位是否為文字型態:
' union select 'hi',null,null,null -- -

會出現錯誤:
Conversion failed when converting the varchar value 'hi' to data type int.
(第 1 欄是 int,不能放非數字字串——這就是為什麼要用 NULL 佔位、只把字串放進「字串型別」的欄位)
這代表 UNION SELECT 前面原本查詢(products)的第一欄位的資料型態不是字串,我們想知道的所有字串型資訊(資料表名、欄位名、帳密⋯⋯)不能放在union select後的第一欄位。
測試第二個欄位是否為文字型態:
' union select null,'hi',null,null -- -

不會出錯!而且頁面上出現了 hi。
這代表目前這個位置可以接受字串型態的值——型態對得上,所以不會報錯。
等等我們想知道的所有字串型資訊(資料表名、欄位名、帳密⋯⋯)都可以放在第二個欄位。 其他欄位我們用 NULL 佔位就好。
拿到注入點之後,不急著撈資料——先搞清楚自己在什麼環境裡。MSSQL 有幾個內建函式可以直接問:
我在哪個資料庫?
' union select null,DB_NAME(),null,null -- -

DB_NAME() 回傳目前連線所在的資料庫——BaijiaShop,這就是應用程式自己的資料庫,帳密幾乎都在這。
這台跑的是什麼資料庫、什麼版本?
' union select null,@@version,null,null -- -

@@version 一次告訴我們三件事:是 MSSQL(不是 MySQL、不是 PostgreSQL)、版本是 2019、架構是 64 位元。版本資訊可以作為後續漏洞研究的線索(相關操作可以參考Day 5 — 從版本號到 Shell:第一次用 Metasploit 拿下目標)。
目前用什麼帳號連進來的?
' union select null,system_user,null,null -- -

SYSTEM_USER 回傳目前的 SQL Server Login 名稱。如果這裡顯示的是 sa,那我們就可以執行很多事,所以遇到資料庫跟拿到shell一樣。要先偵查環境與拿到的帳號,這裡是 baijia_app,是一般的應用程式帳號。
小整理——MSSQL 環境偵察常用函式:
| 我想知道什麼 | 用什麼 | 結果 |
|---|---|---|
| 目前在哪個資料庫 | DB_NAME() |
BaijiaShop |
| 資料庫類型和版本 | @@version |
Microsoft SQL Server 2019... |
| 目前的 Login 帳號 | SYSTEM_USER |
baijia_app |
這些資訊看起來不像帳密那麼香,但在真正的滲透測試裡很重要——知道版本,可以作為後續漏洞研究的線索!知道帳號,則可以進一步判斷權限,information gathering非常重要!我們不能放過!!!
WHERE TABLE_SCHEMA='dbo' 就是「只看 dbo 這個資料夾裡的資料表」,把其他資料夾裡的資料表過濾掉:
ss' union select null,TABLE_NAME,null,null from information_schema.tables where table_schema='dbo'-- -
故意用ss這種查不到任何商品的字,這樣頁面上就只會剩下我們要的table name(比較乾淨><
頁面出現:

看到 admin_users——管理員帳密很可能在這裡,先查這張。
(對照 PostgreSQL:過濾條件是 WHERE table_schema='public',因為 PostgreSQL 預設 schema 叫 public,不叫 dbo。)
ss' union select null,COLUMN_NAME,null,null from information_schema.columns where table_name='admin_users'-- -
頁面出現:

找到了—— username 跟 password_hash 就是我們要的。
我們剛剛只有先找「一個」文字型態的欄位,但想同時撈 username 跟 password_hash 兩欄,用 : 把兩欄拼在 union select後的第二個欄位輸出:
ss' union select null,username+':'+password_hash,null,null from admin_users-- -
頁面出現:
帳密出來了——雖然密碼不是明文,但我們可以拿密碼的hash值,使用John the Ripper去破解看看
確認union select前的資料表欄位數(ORDER BY)
→ 找可以顯示出文字型態資料的欄位(UNION SELECT 塞字串測試)
→ DB_NAME() 確認在哪個資料庫
→ INFORMATION_SCHEMA.TABLES 找有哪些資料表
→ INFORMATION_SCHEMA.COLUMNS 找有哪些欄位
→ SELECT 撈出目標資料
走完這一遍,對Day 12 的方法二不再心虛(變身為自己的information_schema AI><
終於不只是知道怎麼解,請AI協助,而是真的知道要取得什麼資訊,必須要怎麼做哩!
明天開始就要正式進入 CPTS 考試的 AD 部分準備哩!!!
完全陌生的領域,有點緊張 ><
但鐵人賽都已經進行到一半了,沒有不繼續走下去的道理吧 xddd
先來把靶機跟教材找好,趕快鑽研一下 ><
明天就直接開始我們的 AD 之路!!!
不知道會不會瘋狂卡關 xddd
但就一邊查、一邊學,繼續一起往 CPTS 前進囉!
我們明天見~~~