iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 2
1
Security

CTF 的三十道陰影系列 第 2

Day2: [Crypto] CTF 題型分類

前言


昨天大致介紹了 CTF 的賽制,也順便帶到了打卡題,今天就來正式介紹 CTF 的題目類型,之後的幾天大家就可以根據標題寫的題目類型來決定要不要看文章了 (X)

CTF 六大系


跟 <獵人> 的念能力系統一樣,CTF 的題目類型也大致可以分成六個種類,如下圖:

1. Pwn

  • 唯一目的就是要達成 RCE (Remote Code Execution) 後讀取 flag
  • 最典型的解題方式會是要利用 binary 和組合語言的特性,讓程式執行 system("/bin/sh")
    • e.g. 題目是用 C 寫的有 stack overflow 程式,利用方式會是將程式 stack 上的 ret address 覆蓋後,跳的 input 上執行 shellcode 來執行 shell,俗稱 shell out
  • 隨著 CTF 發展迅速,pwn 題型現在還多出了像是 sandbox escape、privilege escalation 的出題方式
  • 可以說是 CTF 題目裡面最 hardcore 的類型,因此類比於 強化系

2. Web

  • 通常題目會架設網站給參賽者,flag 有可能放置在各處,像是 database 內、admin 密碼、source code 內、server config,甚至內網的其他服務上
  • 可能會用到 SQLi、path traversal、XSS、SSRF 等滲透時常見的技術
  • 有分黑箱題和白箱題,但近年來的出題趨勢是白箱題,讓參賽者根據程式語言或 protocol 的特性解題
  • 由於 web 開發日新月異,導致 web 題也千變萬化,有如 變化系

3. Reverse

  • 題目會參考各種 obfuscation 的技術對程式進行混淆
  • 有些題目會參考現實 Malware 的機制,甚至直接拿樣本當成題目,解題時建議在 VM 內進行
  • 最考驗耐心的類型,解題時需要一點猜測和想像 source code 的樣子,類似 具現化系 要想像武器

4. Forensic

  • 跟資料鑑識有關的題目,像是封包分析、資料救援、隱寫術 等等
  • 通常需要操作大量工具來分析題目,因此分類規於 操作系

5. Crypto

  • 基於密碼學出的題目歸類於此類型
  • 出題方式大致可分為兩類:
    • 不正確的使用演算法,導致有機會被解出明文或 private key
    • 自己發明演算法,讓參賽者找出演算法的問題
  • 近代密碼學 == 數學,需要一定程度的數學知識才有辦法做 crypto 的題目
  • 密碼學類型的題目不應該有任何需要參賽者猜測的要素,source 必須是題目的一部分

    某 crypto 大神曾經說過: 沒有 source 的 crypto 題目都是來鬧的

  • 放出系 其實沒什麼關聯,但是我每次都解到想放棄,所以歸在這邊 (逃

6. Misc

  • hacker 多半是隨興又不按照規則來的生物 (?),因此經常會產生一些奇思妙想並出成題目,通常不屬於上述五類的題目都歸在這邊
  • 近期流行的 machine learning 或前陣子流行的 SDN 題目,也會歸在此類
  • 因為比較沒有既定的出題模式,偶爾會出現需要猜出題者心思才能解的題目,俗稱 通靈
  • 特質系 一樣亂七八糟又沒什麼規則,解 Misc 題需要懂很多領域的知識

上面雖然歸納了六個類型,但題目和分類並不是 1-to-1 的關係,經常有題目是橫跨多個領域,像是:有些題目要先透過 reverse 看懂 crypto 的演算法才能解題、或是需要透過 web 手段才能觸發 overflow 等等,另外 CTF 進行時主辦方經常也會根據自己的特色而不一定按照上述的分類方式,像是 3XC3 CTF 就經常出 browser exploit 類型的題目,就會專門為他們開一個分類,近期 pwn 題也多出一些環繞於 shellcode 出題的題目,因此會幫題目分出 shellcode 的類型。

0x01: Bulls & Cows

猜數字是個歷久不衰的經典遊戲,也應該算是我第一次解的 CTF 題目,同時也是交大某一年程式安全的第一個作業,由於當時原始題目和解題環境已經隨著時間流逝而不可考,因此我簡單描述一下題目和當時的解法 XD

程式碼大概是這樣:

int main()
{
  srand(time());
  int ans = rand() % 10000;

  int n = 0;
  for (int i = 0; i < 3; i++) {
    printf("give me a number:")
    scanf("%d", &n);
    if (n == ans) {
      printf("%s", get_flag());
      return 0;    
    }
    print_XAXB(num, ans):
  }
	return 0;
}

這題其實現在來看真的是超級簡單 XD 但對當時剛接觸的我還是想了很久,這題的問題在於透過 time() 來當 random seed 是有機會被預測的,但當時因為上課學的亂數寫法的是這樣寫,因此第一時間並沒有看出有什麼不對,不過跟一起修課的同學討論了一陣子,並透過 google 大神的指點之後,確定基於 time 來亂數產生方式不夠安全,因此有機會被利用,但對當時還是新手的我們來說,即使看出了這點還是不知道該怎麼解這題,最後想到的做法是:

  1. 兩個人同時連線上題目
  2. 犧牲一個人的 session 來猜數字
  3. 沒猜到就重新連一次 QQ
  4. 猜對就拿到一個人的 flag,等等再用另一個人的帳號猜 XD
    • 當時的題目平台有設計成會根據每個學生產生不同的 flag,因此不能直接拿別人得到的 flag 來送

雖然這題看起來簡單,但如今工作了一陣子,才知道還真的很多人會用這樣的方式來產生 random number....,即使不是用 time 來當 seed,也有些 RD 會 call 的程式語言內建的 random function,但卻沒搞清楚使用情境而採用了不夠安全的 PRNG

最後補充一下,如果要安全的拿到亂數,Linux 上唯一不可預測的亂數只有透過 /dev/urandom 取得,但由於讀取的 cost 相對大,通常會只拿個 4 byte 當做 srand() 的 seed,真正需要亂數時再用 rand() 得到亂數,細節在下一篇文章說明


上一篇
Day1: [Misc] What is CTF ?
下一篇
Day3: [Crypto] Pseudo-Random Number Generator
系列文
CTF 的三十道陰影31

尚未有邦友留言

立即登入留言