pwntools:
(参照//www.jianshu.com/p/355e4badab50)
使用from pwn import *将所有的模块导入到当前namespace
模块:
asm : 汇编与反汇编,支持x86/x64/arm/mips/powerpc等基本上所有的主流平台
dynelf : 用于远程符号泄漏,需要提供leak方法
elf : 对elf文件进行操作
gdb : 配合gdb进行调试
memleak : 用于内存泄漏
shellcraft : shellcode的生成器
tubes : 包括tubes.sock, tubes.process, tubes.ssh, tubes.serialtube,分别适用于不同场景的PIPE
utils : 一些实用的小功能,例如CRC计算,cyclic pattern等
连接:
本地 :sh = porcess("./level0")
远程:sh = remote("127.0.0.1",10001)对于remote函数可以接url并且指定端口
关闭连接:sh.close()
数据处理:
主要是对整数进行打包:p32、p64是打包为二进制,u32、u64是解包为二进制
IO模块:
容易跟zio搞混,zio是read、write,pwn是recv、send
sh.send(data) 发送数据
sh.sendline(data) 发送一行数据,相当于在数据后面加\n
sh.recv(numb =2048, timeout = dufault) 接受数据,numb指定接收的字节,timeout指定超时
sh.recvline(keepends=True) 接受一行数据,keepends为是否保留行尾的\n
sh.recvuntil("Hello,World\n",drop=fasle) 接受数据直到我们设置的标志出现
sh.recvall() 一直接收直到EOF
sh.recvrepeat(timeout =default) 持续接受直到EOF或timeout
sh.interactive() 直接进行交互,相当于回到shell的模式,在取得shell之后使用
汇编和反汇编:
利用asm和disasm
asm需要binutils中的as工具辅助
context可用来指定cpu类型以及操作系统,如:
context.arch ='i386'
context.os ='linux'
context.endian ='little'
context.word_size =32
Shellcode生成payload:
得到payload
from pwn import*
context(os='linux',arch='amd64')
shellcode = asm(shellcraft.sh())
或者
from pwn import*
shellcode = asm(shellcraft.amd64.linux.sh())
除了直接执行sh之外,还可以进行其它的一些常用操作例如提权、反向连接
ELF文件求地址:
ELF可重定位目标文件的节:
.bss(Block Strorage Start) 储存未初始化的全局和c变量,和被初始化为0的全局和静态变量
>>> e = ELF('/bin/cat')
>>> print hex(e.address)# 文件装载的基地址
>>> print hex(e.symbols['write'])# 函数地址
>>> print hex(e.got['write'])# GOT表的地址
>>> print hex(e.plt['write'])# PLT的地址
>>> print hex(e.search('/bin/sh').next())# 字符串/bin/sh的地址
脚本常用:
context.arch ='amd64'//设置架构
context.log_level ='debug'//显示log详细信息
libc = ELF('./libc-2.24.so')//加载库文件
做pwn题时的一些调试技巧:参考(https://blog.csdn.net/zszcr/article/details/79834303)
脚本调试:
1、context.log_level="debug"
脚本在执行时就会输出debug的信息,你可以通过观察这些信息查找哪步出错了
2、pwnlib.gdb.attach(p)
在发送payload前加入这条语句,同时加上pause() 时脚本暂停
然后就会弹出来一个开启着gdb的终端,你先在gdb中设置好断点
然后再运行脚本的那个终端按一下回车继续运行脚本,程序就会运行到断点,你就可以查看相应的寄存器,或者是栈的信息
格式:payload = .....
pwnlib.gdb.attach(p)
pause()
p.sendline(payload)
p.interactive()
Egg hunting:
使用情况:可用缓冲区太短装不了所有的shellcode。
Egg hunting这种技巧可以被归为“分级shellcode”,它主要可以支持你用一小段特制的shellcode来找到你的实际的(更大的)shellcode(我们的‘鸡蛋‘),原理就是通过在内存中搜索我们的最终shellcode。换句话说,一段短代码先执行,然后再去寻找真正的shellcode并执行。
条件:
1. 你必须能跳到(jmp,call,push/ret)并且执行“一些”shellcode。可用缓冲区空间可以相对较小,因为它只需要包含一些所谓的“鸡蛋猎人”。
2. 最终的shellcode必须在一个可用的存储空间中(堆、栈)
3. 你必须要用一个独特的记号标记最终的shellcode。最初的shellcode(短小的“鸡蛋猎人”)会遍历内存空间,寻找这个小标记。找到它后,用一个jmp或call 指令跳转到小标记后面的代码中执行。这就意味着你需要在鸡蛋猎人代码中定义这个标记,并且把标记写到实际shellcode的前面。
搜索内存是相当需要处理器资源的,并且会花一段时间。因此:
1.搜寻内存时,很快CPU就被占满了。
2.执行shellcode前会先花上一段时间。
所涉及的汇编代码:
or dx,0xfff;获取页尾地址
inc edx;计数器
push edx;
push byte +0x02;NtAccessCheckAndAuditAlarm的调用号;或者用0x43调用NtDisPlayString()
pop eax ;0x02或0x43装入eax,这样就能当做参数调用系统函数了
int 0x2e ;告诉内核我想用之前的寄存器调用系统函数
cmp al,0x5 ;检查是否触发非法访问;0xc0000005==ACCESS_VIOLATION
pop edx ;返回edx
je xxxx ;74 EF 跳回开头dx 0xfff处
mov eax,0x50905090;标签(鸡蛋)
mov edi,edx; edi用作指针
scasd ;对比状态位
jnz xxxxxx ;75EA跳回inc ebx,检查找到没
scasd ;当找到鸡蛋后
jnz xxxxx ;跳回inc edx ;仅当第一个鸡蛋被找到
jmp edi ;edi指向shellcode的开头