Pwnning is an art.
Switch branches/tags
Nothing to show
Clone or download
Latest commit 8a9acb9 Nov 15, 2018
Type Name Latest commit message Commit time
Failed to load latest commit information.
PwnContext add check for the name of remote_libc Nov 12, 2018
.gitignore privilege bug fix. Nov 8, 2018 update Nov 14, 2018 readme update Nov 15, 2018 some fix Nov 15, 2018 some fix Nov 15, 2018


Pwnning is an art.

welpwn is designed to make pwnning an art, freeing you from dozens of meaningless jobs.


  • Automatically get those magic values for you.
    • libc address
    • heap address
    • stack address
    • program address (with PIE)
    • canary
  • Support multi glibc debugging.
    • 2.19, 2.23-2.27
    • both 32bit and 64bit
  • Debug enhancement (support PIE).
    • symbols
    • breakpoints
  • Misc
    • libc-database
    • one_gadget
  • Heap ? Well, no support for heap analysis. But I have a gif for you. HeapInspect


There are two ways for you to use welpwn.

Via Install has been added since version 0.9.3. If you do not frequently update welpwn, and have no need for teamwork with this tool, this is the recommended way.

git clone && cd welpwn && python install

Via Path

This is recommended for developers and those who need to share their exploit for teamwork.

git clone && cd welpwn && python

Then you probably get something like this.

# paste these codes into your
# # reserve this link :)
import sys
from PwnContext.core import *



Let's make a fresh start.

>>> from PwnContext.core import *
>>> ctx.binary = '/bin/sh'
[*] '/bin/sh'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled
>>> ctx.start()
[x] Starting local process '/bin/sh'
[+] Starting local process '/bin/sh': pid 27377
>>> ctx.sendline('whoami')
>>> print(ctx.recv())

Let's continue with remote target. Run this in the shell at first nc -lvp 1234 -e /bin/sh.

>>> ctx.remote = ('localhost', 1234)
>>> ctx.start('remote')
[*] Stopped process '/bin/sh' (pid 27377)
[x] Opening connection to localhost on port 1234
[x] Opening connection to localhost on port 1234: Trying ::1
[x] Opening connection to localhost on port 1234: Trying
[+] Opening connection to localhost on port 1234: Done
>>> ctx.sendline('whoami')
>>> print(ctx.recv())

If you are not comfortable with ctx. You can use this.

>>> p = ctx.start()
[*] Closed connection to localhost port 1234
[x] Starting local process '/bin/sh'
[+] Starting local process '/bin/sh': pid 27703
>>> print(type(p))
<class 'pwnlib.tubes.process.process'>

So, it seems rather a wrapper of process and remote in pwntools. What's special ?

>>> print(hex(ctx.bases.libc))
>>> print(hex(ctx.bases.heap))
>>> print(hex(ctx.bases.prog))
>>> print(hex(ctx.canary))


Multi glibc support

Make glibc loading on the fly. Mom will never worry about your glibc.

This feature is designed to handle the challenge that uses different glibc.

If the glibc has a different version from your system's, it will get segfault while loading.

Try this.

from PwnContext.core import *

# note that my system libc version is 2.27
TEST_BIN = '/bin/cat'
TEST_LIB = '/tmp/welpwn/PwnContext/libs/libc-2.23/64bit/'

ctx.binary = TEST_BIN
ctx.remote_libc = TEST_LIB
ctx.debug_remote_libc = False # this is by default

# use original libc
# result: /lib/x86_64-linux-gnu/
assert ctx.recv() == 'test\n' # check if correct

# use original libc
ctx.debug_remote_libc = True
# result: /tmp/welpwn/PwnContext/libs/libc-2.23/64bit/
assert ctx.recv() == 'test\n' # check if correct

You may have noticed that there is a ctx.libc in the script. Let me explain what it does.

If you are debugging the local binary, ctx.libc will return exactly the ELF object of the libc which is loaded by the binary. (system libc or remote libc)

If you are exploiting remote target, it will return the ELF of ctx.remote_libc.


Still debug again and again with challenges which need brute force ? Try this.

Note that ctx.bases.heap is available only after the process called malloc.

from PwnContext.core import *

ctx.binary = '/bin/sh'
while True:
    libc_base = ctx.bases.libc
    if (libc_base & 0xf000) == 0x2000: break
print 'now we got libc base:', hex(ctx.bases.libc)
# result: now we got libc base: 0x7f680e782000

GDB support

Find it awful to remember those addresses ? Get tired of debugging PIE enabled program ?

Try this.

from PwnContext.core import *

ctx.binary = '/bin/cat'
ctx.symbols = {'sym1':0x1234, 'sym2':0x5678}
ctx.breakpoints = [0x1234, 0x5678]

After the script run ctx.debug, gdb will show up. (same as gdb.attach(p)).

Then check this in gdb.

pwndbg> p/x $sym1
$1 = 0x5647464d4234
pwndbg> p/x $sym2
$2 = 0x5647464d8678
pwndbg> bl
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005647464d4234
2       breakpoint     keep y   0x00005647464d8678

Well, that's because I set gdbscript before debug.

This is a sample which gonna save you a great lot time.

set $sym2=0x5647464d8678
set $sym1=0x5647464d4234
b *0x5647464d4234
b *0x5647464d8678



Install one_gadget at first.

gem install one_gadget
from PwnContext.core import *
print one_gadgets('/lib/x86_64-linux-gnu/')
print 'now we run it will use cache to speed up'
print one_gadgets('/lib/x86_64-linux-gnu/')


[+] dump one_gadgets from /lib/x86_64-linux-gnu/ : [265195, 265279, 891189]
[265195, 265279, 891189]
now we run it will use cache to speed up
[+] using cached gadgets /root/.one_gadgets/7fb8b29b6dafb0ffe252eba2b54c5781bc6f3e99
[265195, 265279, 891189]


Clone libc-database.

git clone && cd libc-database && ./get && echo `pwd` > ~/.libcdb_path
from PwnContext.core import *
print libc_search({'printf':0x6b0})



Update Log

2018/11/13 Version 0.9.3

  • add

2018/11/7 Version 0.9.2

  • tests
  • readme
  • symbols and breakpoints
  • pep8
  • docs

2018/11/6 Version 0.9.0

This will be a release a few days later.

  • reconstruct the framework


  • docs
  • finish some features
  • finish readme

2018/9/7 Version 0.8.0

  • add experimental offline pwn framework, check PwnContext/
  • not well tested, please issue any wanted feature or bug

2018/5/25 Version 0.7.1

  • update README
  • add libc-database support
  • add instruction_log (check example babyheap for usage end result)

2018/5/22 Version 0.7.0

  • move some auxiliary functions in to
  • add one_gadget supported
  • add runable example (babyheap)