pwntools的使用技巧

  1. process
  2. attach
  3. debug里的技巧
  4. shellcraft

process

一般本地进行交互的时候,有这么几种写法:

# 正常的调用
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())

当然即使你所有环境齐全,ldlibc都有了,但是如果你没有配置过一些特殊的魔改gdb环境的话,有的时候依然会调试错误,你在gdb里的行动可能会非常困难,pwndbg里面的heap,got等指令有时候会失效,所以本地调试的时候,建议只使用第一种交互方式

p = process('./elf')

那么只能建议多装几个环境了,一般来说ubuntu1604 + ubuntu1804总归是够用了,当然一些魔改的gdb如果你用的习惯也是很好用的。

而且切记只要你写了p = process(),那么不管接下来你怎么构造利用,怎么写代码,都不要忘记最后一定要有一句:

p.interactive()

这里还有一个本地可以获取进程基地址的tips,因为程序如果开启了PIE,基地址就不再是0x400000 or 0x8048000了,而获取基地址每次都在gdbvmmap看也太慢了,所以可以用这种方式:

p = process('./elf')
proc_base = p.libs()[elf_path]

这里的elf_path一定要是绝对路径的字符串,也就是从根目录开始的那种,pwd查看一下再加上二进制文件的文件名就好啦,比如:

"/home/t1an5t/ctf/pwn1/elf"            √
"./elf"                                X

后来写了一种更舒服的方法:

proc_base = p.libs()[p.cwd + p.argv[0].strip('.')]

libc的加载基地址也可以类似方式得到:

libc_base = p.libs()[libc_path]

由于只使用第一种方法的进程交互,所以libc一定是本地的,那么一般通过ldd ./elf_name就能找到路径了。


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断下来。然后想继续就重复这个流程就行了。(如果手抖在程序运行终端ctrl+C了,那么请重新开始

这里推荐一种配置终端的方式,爽到飞起:

先下载tmux,一款非常好用的终端分屏程序。

然后配置tmux的识别鼠标模式,因为每次想切换终端需要ctrl+b and ctrl+方向键的操作很多人会不习惯,包括我。

具体的配置的方法:vim ~/.tmux.conf 然后输入set-g option -g mouse on,保存退出。

然后如果是其他终端输入tmux进入tmux终端 ,tmux终端的话不用这一步。

然后再ctrl+B进入tmux终端,然后shift+:(就是L右边那个键) 就可以进入tmux终端的命令模式

这个时候输入内容,最下行会有一个冒号显示,然后输入source ~/.tmux.conf 配置完毕

如果还不成功,那就去问百度把

有了上述的配置后,在你的exp文件前需要加上这么一句:

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

然后在tmux终端里运行你的exp.py,运行到gdb.attach()时就可以分左右两屏,左边跑程序,右边跑gdb了,然后鼠标点点点就可以切换屏幕。然后在gdb那边输入q命令就可以退出调试。

有的时候会突然发现无法输入内容,这时候对应屏幕右上角有一个 [1/1024] 类似的显示,这个时候在对应屏幕摁q就可以解决。


debug里的技巧

主要针对上述的debug()函数里面的一些技巧:

def debug(cmd=""):
    gdb.attach(p, cmd)
    pause()

cmd里可以下任何gdb的命令,一般都是下一些断点命令,还有设置一些变量来方便查看内存。

下断点命令时,用上述process中的获取程序基地址的方式,然后就可以对开了PIE的程序下断点了。

一般构造方式:

cmd = "b *%d\n" %(proc_base+breakaddr)

breakaddr为想要下断点的地址,从IDA里可以很容易找到,IDA里的可是16进制,不要忘了0x前缀。

设置某些变量时,一般都会用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)

shellcraft

一般在exp的开头指定本次的osarch会好一点,比如这样:

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()这种组合。

其他

其他的暂时我也没怎么用到过。

。。。待续


connect 1037178204@qq.com

文章标题:pwntools的使用技巧

本文作者:t1an5t

发布时间:2019-06-19, 15:26:27

最后更新:2020-02-05, 20:49:40

原始链接:http://yoursite.com/pwntools%E7%9A%84%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录