pwntools

pwntools是一个CTF框架和漏洞利用开发库,用Python开发,旨在让使用者简单快速的编写exploit。

用法汇总

导入与环境设置

1
2
3
4
5
6
7
from pwn import *   #导入包

#os设置系统为linux系统
#arch设置架构为32/64位
#log_level设置日志输出的等级为debug(会将完整的io过程都打印下来,方便出现问题时排查错误)
context(os='linux', arch='i386', log_level='debug')
context(os='linux', arch='amd64', log_level='debug')

连接

1
2
3
4
5
6
# 第一种连接方式,通过ip和port去连接
conn = remote('127.0.0.1', 8888)
# 第二种连接方式,通过ssh连接
shell = ssh(host='192.168.14.144', user='root', port=2222, password='123456')
# 第三种连接方式,打开程序,开启进程
p = process('./reverse0')

打包函数

p32/p64: 打包一个整数,分别打包为32位或64位
u32/u64: 解包一个字符串,得到整数

1
2
3
# 比如将0xdeadbeef进行32位的打包,将会得到'\xef\xbe\xad\xde'(小端序)
payload = p32(0xdeadbeef) #pack 32 bits number
payload = p64(0xdeadbeef) #pack 64 bits number

发送和接收数据

1
2
3
4
5
6
7
8
conn.send(data) #发送数据
conn.sendline(data) #发送一行数据,相当于在数据后面加\n
conn.recv(numb = 2048, timeout = default) #接收数据,numb制定接收的字节,timeout指定超时
conn.recvline(keepends=True) #接受一行数据,keepends为是否保留行尾的\n
conn.recvuntil("Hello,World\n",drop=false) #接受数据直到我们设置的标志出现
conn.recvall() #一直接收直到 EOF
conn.recvrepeat(timeout = default) #持续接受直到EOF或timeout
conn.interactive() #直接进行交互,相当于回到shell的模式,在取得shell之后使用

ELF模块

用于获取ELF文件的信息,首先使用ELF()获取这个文件的句柄,然后使用这个句柄调用函数,和IO模块很相似。

1
2
3
4
5
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表的地址

汇编与反汇编

1
2
asm('mov eax, 0')   #汇编
disasm('\xb8\x0b\x00\x00\x00') #反汇编

生成shellcode

先通过context设置架构,然后生成shellcode。

1
2
context(arch='i386', os='linux')  #设置环境
shellcode = asm(shellcraft.sh()) #打印出汇编后的shellcode

调用gdb调试

在python文件中直接设置断点,当运行到该位置之后就会断下。

1
2
3
from pwn import *
p = process('./c')
gdb.attach(p)

Cyclic Pattern

使用pwntools生成一个pattern,pattern指一个字符串,可以通过其中的一部分数据去定位到其在一个字符串中的位置。

如,在栈溢出的时候,首先构造cyclic(0x100),或者更长长度的pattern,进行输入,输入后pc的值变为0x61616161,那么再通过cyclic_find(0x61616161)就可以得到从哪一个字节开始会控制PC寄存器了,避免了很多没必要的计算。

1
2
3
cyclic(0x100) # 生成一个0x100大小的pattern,即一个特殊的字符串
cyclic_find(0x61616161) # 找到该数据在pattern中的位置
cyclic_find('aaaa') # 查找位置也可以使用字符串去定位

常用脚本

  • SHA / MD5 认证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from pwn import *
    from parse import *
    from pwnlib.util.iters import bruteforce
    import string
    from hashlib import sha256

    def brute_force(prefix,s):
    return bruteforce(lambda x:sha256(x+prefix).hexdigest()==s,string.ascii_letters+string.digits,length=4,method='fixed')

    r = remote("34.74.30.191",1337)
    data = r.recvline()
    prefix, s = parse("sha256(XXXX+{}) == {}",data)
    r.recvline()
    r.sendline(brute_force(prefix,s))

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#coding=utf8
#python2

from pwn import *

context.terminal = ['gnome-terminal', '-x', 'zsh', '-c']
context.log_level = 'info'
# functions for quick script
s = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
r = lambda numb=4096, timeout=2:p.recv(numb, timeout=timeout)
rl = lambda :p.recvline()
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
irt = lambda :p.interactive()
dbg = lambda gs='', **kwargs :gdb.attach(p, gdbscript=gs, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
leak = lambda name, addr :log.success('{} = {:#x}'.format(name, addr))

def rs(arg=''):
global p
if arg == 'remote':
p = remote(*host)
else:
p = binary.process()

libc = ELF('./libc-2.27.so', checksec=False)
binary = ELF('./once', checksec=False)
host = ('182.92.108.71', 30103)

#rs()
rs('remote')