Don't read this now.
I have prepared a template for you.
python start.py --template
Then you get this.
#https://github.com/matrix1001/welpwn
from PwnContext import *
try:
from IPython import embed as ipy
except ImportError:
print ('IPython not installed.')
if __name__ == '__main__':
# context.terminal = ['tmux', 'splitw', '-h'] # uncomment this if you use tmux
context.log_level = 'debug'
# functions for quick script
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
st = lambda delim,data :ctx.sendthen(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
slt = lambda delim,data :ctx.sendlinethen(str(delim), str(data))
r = lambda numb=4096 :ctx.recv(numb)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
leak = lambda address, count=0 :ctx.leak(address, count)
dbg = lambda *args, **kwargs :ctx.debug(*args, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
ctx.binary = './pwn'
ctx.remote_libc = './libc.so'
ctx.remote = ('1.1.1.1', 1111)
ctx.debug_remote_libc = False # True for debugging remote libc, false for local.
rs()
# rs('remote') # uncomment this for exploiting remote target
libc = ctx.libc # ELF object of the corresponding libc.
# ipy() # if you have ipython, you can use this to check variables.
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())
root
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 127.0.0.1
[+] Opening connection to localhost on port 1234: Done
>>> ctx.sendline('whoami')
>>> print(ctx.recv())
root
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))
0x7fd1b6b5d000
>>> print(hex(ctx.bases.heap))
0x556e998be000
>>> print(hex(ctx.bases.prog))
0x556e99249000
>>> print(hex(ctx.canary))
0xea507930dd6a9800
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/libc.so.6'
ctx.binary = TEST_BIN
ctx.remote_libc = TEST_LIB
ctx.debug_remote_libc = False # this is by default
# use original libc
ctx.start()
print(ctx.libc.path)
# result: /lib/x86_64-linux-gnu/libc-2.27.so
ctx.sendline('test')
assert ctx.recv() == 'test\n' # check if correct
# use original libc
ctx.debug_remote_libc = True
ctx.start()
print(ctx.libc.path)
# result: /tmp/welpwn/PwnContext/libs/libc-2.23/64bit/libc.so.6
ctx.sendline('test')
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
.
Note
LD_PRELOAD
and LD_LIBRARY_PATH
are auto patched to ensure system
is called successfully. This might affect other environmental variables. To disable this patch, you need to set ctx.auto_patch_env
to False
.
Check ctx.patch_env
, ctx.start
for more detail.
new feature
Now support debugging glibc with symbol file. First you must download glibc binary and its symbol file.
However, I've made one for you. glibc-all-in-one
Follow these.
from PwnContext import *
ctx.binary = '/bin/sh'
# download first
ctx.custom_lib_dir = '/path/to/glibc-all-in-one/libs/2.23-0ubuntu3_amd64'
# also available to use `remote_libc`
# ctx.remote_libc = '/path/to/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so'
ctx.debug_remote_libc = True
# now we start. libc.so should be loaded automatically.
ctx.start()
# now we debug.
ctx.debug()
Check this.
pwndbg> p &main_arena
$3 = (malloc_state *) 0x7fdb15055b20
pwndbg> vmmap
......
0x7fdb14c92000 0x7fdb14e52000 r-xp 1c0000 0 /path/to/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6
......
0x7fdb1505b000 0x7fdb15081000 r-xp 26000 0 /tmp/ld.so.2
......
Well, some of you may prefer to debug glibc with source code, then you need to compile glibc on your own. Just use custom_lib_dir
and enjoy it.
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:
ctx.start()
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
Find it awful to remember those addresses ? Get tired of debugging PIE
enabled program ?
Now support heap
and libc
symbols. Just use a name start with libc_
or heap_
.
Try this.
from PwnContext.core import *
ctx.binary = '/bin/cat'
ctx.symbols = {'sym1':0x1234, 'sym2':0x5678}
ctx.breakpoints = [0x1234, 0x5678]
ctx.start()
ctx.debug()
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/libc.so.6')
print 'now we run it again.it will use cache to speed up'
print one_gadgets('/lib/x86_64-linux-gnu/libc.so.6')
result:
[+] dump one_gadgets from /lib/x86_64-linux-gnu/libc.so.6 : [265195, 265279, 891189]
[265195, 265279, 891189]
now we run it again.it will use cache to speed up
[+] using cached gadgets /root/.one_gadgets/7fb8b29b6dafb0ffe252eba2b54c5781bc6f3e99
[265195, 265279, 891189]
Clone libc-database
.
git clone https://github.com/niklasb/libc-database && cd libc-database && echo `pwd` > ~/.libcdb_path && ./get
from PwnContext.core import *
print libc_search({'printf':0x6b0})
result:
ELF('/tmp/libc-database/db/libc6_2.19-0ubuntu6_amd64.so')
- add support for glibc-all-in-one
- add support for custom libs
- add
patch_env
- add
auto_patch_env
- add fix for
change_ld
- add
addr_generator
(inutils.misc
)
- add template
- add support for libc/heap symbols
- add
setup.py
- tests
- readme
- symbols and breakpoints
- pep8
- docs
This will be a release a few days later.
- reconstruct the framework
TODO:
- docs
- finish some features
- finish readme
- add experimental offline pwn framework, check PwnContext/offpwn.py
- not well tested, please issue any wanted feature or bug
- update README
- add libc-database support
- add instruction_log (check example babyheap for usage end result)
- move some auxiliary functions in PwnContext.py to auxiliary.py
- add one_gadget supported
- add runable example (babyheap)