glibc
下载:https://ftp.gnu.org/gnu/libc/
本地更换libc环境
准备工作
下载工具
patchelf:git clone https://github.com/NixOS/patchelf
glibc-all-in_one:git 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是一个CTF框架和漏洞利用开发库,用Python开发,旨在让使用者简单快速的编写exploit。
用法汇总
导入与环境设置
1 2 3 4 5 6 7
| from pwn import *
context(os='linux', arch='i386', log_level='debug') context(os='linux', arch='amd64', log_level='debug')
|
连接
1 2 3 4 5 6
| conn = remote('127.0.0.1', 8888)
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
| payload = p32(0xdeadbeef) payload = p64(0xdeadbeef)
payload = flat([0xaaaa, 0xbbbb, 0xcccc])
|
发送和接收数据
1 2 3 4 5 6 7 8
| conn.send(data) conn.sendline(data) conn.recv(numb = 2048, timeout = default) conn.recvline(keepends=True) conn.recvuntil("Hello,World\n",drop=false) conn.recvall() conn.recvrepeat(timeout = default) conn.interactive()
|
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'])) print(hex(e.plt['write']))
|
汇编与反汇编
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())
|
调用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) cyclic_find(0x61616161) 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
| 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
| 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
| 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
|
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'zsh', '-c'] context.log_level = 'info'
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)
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)
binary = ELF('./once', checksec=False) host = ('182.92.108.71', 30103)
rs('remote')
irt()
|
当使用了 prctl 系统调用的 PR_SET_SECCOMP 功能禁用了一些系统调用,直接用 seccomp-tools 可以看禁用的系统调用。
seccomp-tools dump ./pwn