解题准备知识:
查询通解的思路:
1.同一个模块内,代码段和数据段之间的距离确定,不受随机化影响
2.同一动态库内,每个函数在动态库内部的偏移量是确定的
3.只要泄露出动态库中某个函数的地址,就可以知道该函数在动态库中的偏移。
4.不同动态库中相同函数的偏移量是不同的,那就可以通过这个泄露的偏移量确定该程序使用的动态库的版本。
5.计算出动态库的基址:动态库的基址=泄露的函数的地址 - 该函数在动态库中的偏移量
6.计算出system函数的地址:system函数的地址= 动态库的基址 + system函数在动态库中的偏移量
7.找到 /bin/sh 这个字符串的所在位置,一般动态库里有这个字符串
8.如果没有这个字符串就使用能写入的函数,将这个字符串读写到可写入的区域,这就需要构造ROP链,pop pop pop ret 。
参考博文:https://blog.csdn.net/qq_43394612/article/details/85323020
首先检测程序开启的保护
hu@ubuntu:~/Desktop/stack over$ checksec pwn3
[*] '/home/hu/Desktop/stack over/pwn3'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
可以看出,源程序为 64 位,开启了 NX和ASLR 保护。
利用 IDA 来查看源码
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-40h]
puts("Welcome to the stack training_3");
puts("Now play your game: ");
read(0, &buf, 0x150uLL);
return 0;
}
可以看到 buf 这个字符串数组只有0x40的大小,但是却可以 read 0x150个字节,多出来的 0x150-0x80 就会造成溢出.
因为程序中没有可以直接利用的代码,也不能自己填写代码来获得 shell,所以我们利用程序中的 gadgets 来获得 shell
首先绕过ASLR(地址随机化),泄露出libc的基址libc_base,然后利用构造ROP链绕过NX,这两步要在一次运行完成,不然因为地址随机化的缘故,在下一次运行时libc基址又将改变。
查看共享库:
运行三次,每次加载位置都不同,即不可通过简单的ldd命令查看共享库将libc的加载地址给获取。
把libc.so.6复制到当前目录下,可见它偏移量:
hu@ubuntu:~/Desktop/stackstack$ one_gadget libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints: rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints: [rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints: [rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints: [rsp+0x70] == NULL
在IDA中可以找到main函数入口地址为:
.text:0000000000400566 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000400566 public main
.text:0000000000400566 main proc near ; DATA XREF: _start+1D↑o
.text:0000000000400566
.text:0000000000400566
.text:0000000000400566 buf = byte ptr -40h
.text:0000000000400566
.text:0000000000400566 ; __unwind {
.text:0000000000400566 push rbp
.text:0000000000400567 mov rbp, rsp
.text:000000000040056A sub rsp, 40h
.text:000000000040056E mov edi, offset aWelcomeToTheSt ; "Welcome to the stack training_3"
.text:0000000000400573 call _puts
.text:0000000000400578 mov edi, offset aNowPlayYourGam ; "Now play your game: "
.text:000000000040057D call _puts
.text:0000000000400582 lea rax, [rbp+buf]
.text:0000000000400586 mov edx, 150h ; nbytes
.text:000000000040058B mov rsi, rax ; buf
.text:000000000040058E mov edi, 0 ; fd
.text:0000000000400593 call _read
.text:0000000000400598 mov eax, 0
.text:000000000040059D leave
.text:000000000040059E retn
.text:000000000040059E ; } // starts at 400566
.text:000000000040059E main endp
put与read在got表中的地址为:
.got.plt:0000000000601018 off_601018 dq offset puts ; DATA XREF: _puts↑r
.got.plt:0000000000601020 off_601020 dq offset read ; DATA XREF: _read↑r
put在plt表中的地址为:
.plt:0000000000400430 ;