pwn工具

glibc

下载:https://ftp.gnu.org/gnu/libc/

本地更换libc环境

准备工作

下载工具

patchelfgit clone https://github.com/NixOS/patchelf

glibc-all-in_onegit clone https://github.com/matrix1001/glibc-all-in-one

查看题目libc版本

老版本:strings libc.so.6 | grep 'Ubuntu GLIBC'

新版本:./libc.so.6

下载指定libc

进入下载好的 glibc-all-in-one 文件夹,命令:

1
2
3
4
5
6
7
8
9
./update_list            #更新最新版本的glibc
cat list #查看可下载的glibc
./download 2.31-0ubuntu9.5_amd64 #找到需要的glibc版本,下载到本地

cat old_list #打开老版本glibc列表(如果在list中没有找到需要的glibc版本)
./download_old 2.26-0ubuntu2.1_amd64 #找到需要的老版本glibc,下载到本地

#下载路径:/glibc-all-in-one/libs
#如果在old_list和list里都没有找到合适的版本,可以选择相近的版本的ld文件

利用patchelf修改题目环境

patchelf --set-interpreter [~/glibc-all-in-one/libs/2.26-0ubuntu2.1_amd64/ld-2.26.so] --set-rpath [~/glibc-all-in-one/libs/2.26-0ubuntu2.1_amd64] [./gundam]

三个[ ]分别对应 ld文件地址glibc文件夹题目文件。(--set-rpath 后面的地址有时候需要加上 “ / ” )

patchelf --set-rpath 的作用是 在ELF文件的 Dynamic section 的中添加一个 RUNPATH 段里面储存着动态链接库的绝对路径,即设置程序的动态链接库。

patchelf --set-interpreter 的作用是 在ELF文件的 interp section 设置 interpreter 项的值为 ld加载器绝对路径,即设置程序的动态链接库加载器。

例:

1
2
3
4
patchelf --replace-needed ./libc.so.6 ~/pwn/libc.so.6 ./vuln
patchelf --set-interpreter ~/pwn/ld-2.31.so ./vuln
ldd vuln
./vuln

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
4
5
6
# 比如将0xdeadbeef进行32位的打包,将会得到'\xef\xbe\xad\xde'(小端序)
payload = p32(0xdeadbeef) #pack 32 bits number
payload = p64(0xdeadbeef) #pack 64 bits number

#将多个变量转换为二进制字符串
payload = flat([0xaaaa, 0xbbbb, 0xcccc])

发送和接收数据

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') # 查找位置也可以使用字符串去定位

打印日志

1
success("addr => " + hex(addr))

常用脚本

SHA / MD5 认证(Proof of Work)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#python3 脚本1
from pwn import *
from hashlib import sha256
import string
from pwnlib.util.iters import mbruteforce

r = remote('34.74.30.191',1337)

table = string.ascii_letters+string.digits
def PoW():
r.recvuntil(b'XXXX+')
suffix = r.recv(16).decode()
r.recvuntil(b'== ')
cipher = r.recvline().strip().decode()
proof = mbruteforce(lambda x: sha256((x + suffix).encode()).hexdigest() ==
cipher, table, length=4, method='fixed')
r.sendlineafter(b'XXXX: ', proof.encode())

PoW()
r.interactive()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#python2 脚本
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
#python3 脚本2
from hashlib import sha256
from itertools import product

data = r.recvline().decode()
print(data)

suffix = re.findall(r'\(XXX\+(.*?)\)', data)[0]
digest = re.findall(r'== (.*?)\n', data)[0]
print(f"suffix: {suffix} \ndigest: {digest}")

print('Calculating hash...')
dic = string.ascii_letters + string.digits
for i in product(dic, repeat=3):
prefix = ''.join(i)
guess = prefix + suffix
if sha256(guess.encode()).hexdigest() == digest:
print(f"result: {guess}\n")
break
r.sendlineafter(b'[+] Plz Tell Me XXX :',prefix.encode())

伪随机数同步

1
2
3
4
5
from ctypes import *
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')

libc.srand(libc.time(0))
r = libc.rand()

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *

sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
sda = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
ru = lambda x:p.recvuntil(x)
rv = lambda x:p.recv(x)
io = lambda :p.interactive()
ps = lambda :pause()

p = remote('ok', 20002)
context.log_level = 'debug'
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
36
37
#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(argv=arg, raw=True)

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

#rs()
rs('remote')

irt()

seccomp-tools

当使用了 prctl 系统调用的 PR_SET_SECCOMP 功能禁用了一些系统调用,直接用 seccomp-tools 可以看禁用的系统调用。

seccomp-tools dump ./pwn