pwntools
pwntools的使用
该板块会持续更新,学一点写一点
学习资料来源于:1
导入pwntools库
1 | from pwn import* |
设置基本信息
1 | context(os='linux',arch='AMD64',log_level='debug') |
os
是靶机的操作系统类型
arch
是题目的架构,一般是AMD64(64位)或i386(32位)
log_level
是指日志输出等级,设置为debug可以在脚本运行时输出我们具体发送了什么信息,靶机反馈了什么信息。
连接远程靶机
1 | io = remote("ip",port)#前者为靶机地址,后者为端口号 |
1 | io = ssh(host='ip',user='用户名',port=端口号,password='密码') |
本地调试
1 | io = process("./pwn") |
发送信息
1 | p.send(payload) # 直接发送payload |
接收信息
1 | p.recv(int) #利用recv来接收返回的数据,并且可以控制接受到的字节数 |
常用接收地址:
64位:
1 | write_addrs=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) |
32位:
1 | puts_addrs=u32(io.recvuntil(b'\xf7')[-4:]) |
开启交互
1 | p.interactive() |
构造发送地址类型
p64/p32/u64/u32
这类函数的作用
1 | p64(int) |
p64()
这种类型用于将消息变成对应的进制流(因为原本程序里面的数据都是已经编译过的,所以打入的数据也需要是编译过的,所以需要使用p64()这类工具)u64()
这种类型用于泄露地址的时候将泄露的进制流变成对应的原本的样子,方便来辨认查找glibc版本
因为一般计算机都是小端程序,所以这两个函数都自带有将数据变成小端需要的样子,如果遇到大端程序可能需要额外注意
除了p32()
这种转化方式还有,flat()
,它可以将多个数据结构(如字符串、整数等)连接在一起,并将它们转换为二进制数据。通常用于构建复杂的ROP链的shellcode
。flat 函数会将数据扁平化,将它们按照顺序连接在一起,不做任何其他处理。在提供的代码中,flat
被用于构建一个包含多个元素的列表,然后将它们连接起来形成一个二进制数据。
1 | payload = flat([0x12345678, 'AAAA', 0xdeadbeef], word_size=4/8) |
生成指定大小字符串
1 | cyclic(100) |
汇编于反汇编
pwntools提供了两个工具:asm
函数可以将汇编代码转为对应的二进制disasm
函数则相反可以将二进制转化为汇编代码
1 | 'mov eax, 0') #汇编 asm( |
生成shellcode后门
注意对于64位程序使用该函数前需指定架构,默认架构为32位
1 | context(os='linux', arch='i386') |
纯净版shellcode
1 | from pwn import * |
专门收集shellcode的网站:https://www.exploit-db.com/shellcodes/43550
已编译好的shellcode:
64位linux的24Byte(字节)的shellcode
1 | shellcode_x64 ="\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05" |
64位linux的23Byte的shellcode
1 | shellcode_x64 ="\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05" |
很多时候我们需要自己手动编写shellcode来绕过一些检测
运行时使用gdb调试(还需要进一步学习)
gdb.attach
1 | gdb.attach(p, gdbscript=""" b main; commands; silent printf "Breakpoint hit\n"; continue; end """) |
一般gdb.attach(p)
可以和pause()
函数连用,可以确保在gdb完全打开之前脚本不运行pause()
函数用于暂停脚本的运行,直到用户输入任意数据
ELF模块
我们可以通过这个模块来快速获取pwn文件的got表地址
以及plt表地址
用于获取ELF文件的信息
,首先使用ELF()
获取这个文件的句柄,然后使用这个句柄调用函数,和IO模块很相似。
下面演示了:获取基地址、获取函数地址(基于符号)、获取函数got地址、获取函数plt地址,和LibcSearcher库联动使用
1 | elf = ELF('./pwn') |