Pwntools
Pwntools是一个用于漏洞利用和二进制分析的 Python 库
,广泛应用于安全研究、渗透测试和竞争性编程(如 CTF,Capture The Flag)中。它为用户提供了一套强大的工具和功能,以简化与二进制文件的交互
、网络通信
以及各种常见任务的执行
。
我们启动靶机后会得到一个靶机地址,把靶机地址的前缀和端口分别输入在remote
里面,ip
用''
包裹起来用逗号隔开端口
,这样就实现了远程连接。
# 用于连接远程服务器,并把连接到的进程命名为p,后续的函数都围绕这个p进程展开
p = remote("ip",port)
#比如:p = remote("node5.buuoj.cn",5555)
当然偶尔也有题目需要用ssh来连接靶机的:
# 通过使用ssh来连接靶机
p = ssh(host='192.168.xx.xxx', user='xidp', port=6666, password='88888888')
发送消息
**************************************************
p.send(payload) # 直接发送payload
**************************************************
p.sendline(payload) # 发送payload,但是结尾会有一个\n
**************************************************
p.sendafter("string", payload) # 接收到 string (这里说的string可以替换成任何信息) 之后会发送payload,但是如果没有接收到string,那么就会导致脚本一直卡在这里不动
**************************************************
p.sendlineafer("string", payload) # 接收到 string 之后会发送payload 并且在payload最后添加\n
一般常用的发送就这几种,此时可能会有一个疑问,p.send
和 p.sendline
就差一个\n
有什么区别?
有区别,比如gets()
和scanf()
这类函数它们会以\n
作为结束符号,如果我们没有发送\n
,它们就会一直卡着等待输入,所以遇到这类输入函数就必须要用p.sendline
来添加\n
(当然如果我们手动在payload里面添加\n
也OK)。遇到read()
这类函数则使用p.sendline
和p.send
都可以.
那如果我们都使用p.sendline
不就好了吗?
NO,这并不好,比如我们遇到read()
并且希望发送一些字符串比如"flag"
,如果你用p.send("flag")
那么没错你发送的是flag
,计算机解析后是\x67\x61\x6C\x66
,而如果你用的是p.sendline("flag")
,则你发送的是flag\n
,计算机解析后是\x0a\x67\x61\x6C\x66
,这一点细节上的差距就可能导致我们的脚本无法打通,所以我们需要面对合适的函数使用合适的方法,后续我们将会继续讨论read()、scanf()、fgets()和gets()
这类函数在输入的时候具体有什么区别。
接收消息
**************************************************
p.recv(int) 利用recv来接收返回的数据,并且可以控制接受到的字节数
比如:p.recv(7) => 系统输出'hello world' => 我们会接受到'hello w'
**************************************************
p.recvline('string') 设置一个标识符,接收标识符所在的那一行
比如:p.recvline('O.o')
#系统输出:
Hello World
This is a test.
O.o This is the target line.
Goodbye.
#我们接收:
O.o This is the target line.
**************************************************
p.recvlines(N) 接收 N 行输出
**************************************************
p.recvuntil('string') 可以指定接收到某一字符串的时候停止 ,还有第二个参数 drop,drop=True(默认为false) 表示丢弃设定的停止符号
比如:p.recvuntil('or')
#系统输出:
hello world
#我们接收:
hello wor
比如:a = io.recvuntil(']', drop=True)
就是一直获取到`]`符号出现就停止,并且不接收`]`符号
**************************************************
传递到终端
# 接受信息并且在终端操作,程序拿到shell,然后就可以转接到linux终端上,让pwn手享受拿flag的乐趣
p.interactive()
构造发送地址类型
**************************************************
p64(int)
p64(0xfaceb00c) => '\x0c\xb0\xce\xfa\x00\x00\x00\x00\x00'
**************************************************
u64(str)
u64('\x0c\xb0\xce\xfa\x00\x00\x00\x00') =>0xfaceb00c
**************************************************
p32(int)
p32(0xfaceb00c) => '\x0c\xb0\xce\xfa'
**************************************************
u32(str)
u32('\x0c\xb0\xce\xfa') => 0xfaceb00c
**************************************************
p64()
这种类型用于将消息变成对应的进制流(因为原本程序里面的数据都是已经编译过的,所以打入的数据也需要是编译过的,所以需要使用p64()这类工具)
u64()
这种类型用于泄露地址
的时候将泄露的进制流变成对应的原本的样子,方便来辨认查找glibc版本
因为一般计算机都是小端程序,所以这两个函数都自带有将数据变成小端需要的样子,如果遇到大端程序可能需要额外注意
除了p32()
这种转化方式还有,flat()
,它可以将多个数据结构(如字符串、整数等)连接在一起,并将它们转换为二进制数据。通常用于构建复杂的ROP链的shellcode
。flat 函数会将数据扁平化,将它们按照顺序连接在一起,不做任何其他处理。在提供的代码中,flat
被用于构建一个包含多个元素的列表,然后将它们连接起来形成一个二进制数据。