Giter Site home page Giter Site logo

pwninit's Introduction

Checks Status Deploy Status

pwninit

A tool for automating starting binary exploit challenges

Features

  • Set challenge binary to be executable
  • Download a linker (ld-linux.so.*) that can segfaultlessly load the provided libc
  • Download debug symbols and unstrip the libc
  • Patch the binary with patchelf to use the correct RPATH and interpreter for the provided libc
  • Fill in a template pwntools solve script

Usage

Short version

Run pwninit

Long version

Run pwninit in a directory with the relevant files and it will detect which ones are the binary, libc, and linker. If the detection is wrong, you can specify the locations with --bin, --libc, and --ld.

Custom solve.py template

If you don't like the default template, you can use your own. Just specify --template-path <path>. Check template.py for the template format. The names of the exe, libc, and ld bindings can be customized with --template-bin-name, --template-libc-name, and --template-ld-name.

Persisting custom solve.py

You can make pwninit load your custom template automatically by adding an alias to your ~/.bashrc.

Example
alias pwninit='pwninit --template-path ~/.config/pwninit-template.py --template-bin-name e'

Install

Arch Linux

Install pwninit or pwninit-bin from the AUR.

Download

You can download statically-linked musl binaries from the releases page.

Using cargo

Run

cargo install pwninit

This places the binary in ~/.cargo/bin.

Note that openssl, liblzma, and pkg-config are required for the build.

Example

$ ls
hunter  libc.so.6  readme

$ pwninit
bin: ./hunter
libc: ./libc.so.6

setting ./hunter executable
fetching linker
https://launchpad.net/ubuntu/+archive/primary/+files//libc6_2.23-0ubuntu10_i386.deb
unstripping libc
https://launchpad.net/ubuntu/+archive/primary/+files//libc6-dbg_2.23-0ubuntu10_i386.deb
setting ./ld-2.23.so executable
copying ./hunter to ./hunter_patched
running patchelf on ./hunter_patched
writing solve.py stub

$ ls
hunter	hunter_patched	ld-2.23.so  libc.so.6  readme  solve.py

solve.py:

#!/usr/bin/env python3

from pwn import *

exe = ELF("./hunter_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-2.23.so")

context.binary = exe


def conn():
    if args.LOCAL:
        r = process([exe.path])
        if args.DEBUG:
            gdb.attach(r)
    else:
        r = remote("addr", 1337)

    return r


def main():
    r = conn()

    # good luck pwning :)

    r.interactive()


if __name__ == "__main__":
    main()

pwninit's People

Contributors

anthraxx avatar dependabot-preview[bot] avatar dependabot[bot] avatar dp1 avatar fuyu0425 avatar green-avocado avatar io12 avatar k4mp3t avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

pwninit's Issues

Patching rpath fails for

Hey @io12 . I've just grabbed the 3.0 release and noticed that the patchelf step attempts to set the rpath to an empty string if a pathless filename is used for the libc arg, e.g. --libc my_libc.so. That's because libc.parent() -> "" here:

if let Some(lib_dir) = opts.libc.as_ref().and_then(|libc| libc.parent()) {

having this default to "." would be helpful, I'm just working around it by specifying --libc ./my_libc.so which seems to work fine. It might also be better to absolutify that path so running the binary works from other directories though - equivalent to running --libc "${PWD}/my_libc.so".

Thanks for the tool, btw!

Support more libc versions

Currently only the glibc versions in the Ubuntu repos are supported (2.24, 2.27, etc aren't). I'm not sure where to get old glibc packages. Maybe launchpad.net?

c++ executables exploitation support

C++ executables usually require libstdc++ shared object file.
Having version mismatch btw libc and libstdc++ will usually cause an error on startup:

$ LD_PRELOAD=./libc.so.6 ./ld-2.35.so ./exe
./libc.so.6: version `GLIBC_2.36' not found (required by /lib/x86_64-linux-gnu/libstdc++.so.6)

While libc usually is provided by chal's author, the libstdc++ usually is not. Using local in-system libstdc++ (in /usr/lib...) often causes version mismatch, which produces the error above. The task is to find the corresponding libstdc++ for given libc and download it.

Right now pwninit does not searches for libstdc++ when dealing with c++ chals. Gonna add this feature

  • Add -cpp flag to mark chal as C++ and not C. So pwninit -cpp should start C++ initialization
  • When -cpp download the corresponding libstdc++ based on detected ld.so or libc.so.6 version.

The example is roppenheimer chal from recent ductf-2023

error: failed to run custom build command for `rust-lzma v0.4.0`

I'm trying to install pwninit on a Debian box on AWS but am getting this error:

error: failed to run custom build command for `rust-lzma v0.4.0`

Caused by:
  process didn't exit successfully: `/tmp/cargo-installP91vdS/release/build/rust-lzma-879711f934ca58da/build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at 'Could not find liblzma using pkg-config', /home/aaron/.cargo/registry/src/github.com-1ecc6299db9ec823/rust-lzma-0.4.0/build.rs:10:3
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

warning: build failed, waiting for other jobs to finish...
error: failed to compile `pwninit v2.1.0`, intermediate artifacts can be found at `/tmp/cargo-installP91vdS`

Caused by:
  build failed

I don't do anything rust related and I have never used cargo. Could you point me in the right direction?

Add kernel-pwning initialization

Currently it only supports userland-pwning chals. Kernel-pwning also need such a bootstrap.

  • Add CLI parameter "-ker" to switch to kernel-pwn initialization
  • Autodetection of bzImage file
  • Extract vmlinux from bzImage, +tests
  • "Unstrip" vmlinux like vmlinux-to-elf did, +tests
  • Add templates for compress.sh/decompress.sh to work with cpio-compressed initramfs. Example [here]
    (https://lkmidas.github.io/posts/20210123-linux-kernel-pwn-part-1/)
  • It is important for users to have vmlinux-to-elf and extract-vmlinux. Post-cargo installation script vs provide instructions in README.md.

Probably can add dependency on vmlinux-to-elf repo?

Unable to install on Debian 11 with Cargo 1.46.0

When I run cargo install pwninit I get

Downloads > cargo install pwninit
    Updating crates.io index
  Downloaded pwninit v3.3.1
error: failed to parse manifest at `/home/anon/.cargo/registry/src/github.com-1ecc6299db9ec823/pwninit-3.3.1/Cargo.toml`

Caused by:
  failed to parse the `edition` key

Caused by:
  this version of Cargo is older than the `2021` edition, and only supports `2015` and `2018` editions.

the output of cargo -vV is

 Downloads > cargo -vV
cargo 1.46.0
release: 1.46.0

and system information is

OS: Debian GNU/Linux 11 (bullseye) x86_64 
Kernel: 5.10.0-28-amd64

[eglibc support] warning: failed finding version string

I am trying to use pwninit --libc ./libc.so.6 --bin ./c to fetch ld but am getting this error:

bin: ./c
libc: ./libc.so.6

warning: failed finding version string

Is there an argument I can pass to be more verbose (so that I can get some debug information for you)? I am running pwninit 2.1.1.

Config file

Something like ~/.config/pwninit/config.toml and ~/.config/pwninit/template.py

  • Should be created with a command, not by default
  • Should disallow setting --bin, --lib, and --ld (because those aren't config)

Specify Dependencies

I think you should specify dependencies the tool needs before running the cargo command:
patchelf
elfutils

Otherwise it the tool gives warnings about them

Addition of a config option to run analysis or other scripts upon completion of pwninit

Problem

During a CTF you might have tools you want to run, such as cwe_checker, checksec, seccomp rules, find cryptographic constants, find vulnerable functions, etc. You might even want to run custom scripts with information about the binary.

Currently pwninit doesn't support any of this out of the box. Nor does pwninit currently support a configuration file, instead taking just command line arguments to configure it on run.

Proposed Approach

I propose we borrow the functionality of spwn which is a project meant to expand upon pwninit.

One solution could be to have pwninit generate a config file similar to that of spwns default configs to mimic the ability for individuals to set up a ctf toolchain without the added difficulty of implementing some of the other features spwn has built into it like automatically using cwe_checker (and without adding more dependencies to the pwninit project).

I propose the following:

  1. Create a new Rust file called pwn_config.rs, that would create the config files in ~/.config/pwninit or some other similar location.

  2. Create a new Rust file called command_runner.rs that does the following:

    • Provide functionality similar to solvepy.rs but instead of formatting it into pwntools' ELF({stuff}), it would attempt to execute the python script/shell command provided with a struct similar to the following python struct. source
     class FileManager:
         # Three `Binary` objects
         self.binary
         self.libc    # Can be None
         self.loader  # Can be None
         # libc and loader have their own type that are a subclasses of `Binary`
         self.other_binaries  # list of relative paths
    
    class Binary:
        self.name  # relative path to the original binary
        self.debug_name  # relative path to the debug binary, if there is none it is equal to `self.name`
        self.pwnfile  # pwn.ELF object
    • Has the following functions run_command, pre_analysis, post_analysis where:
      • run_command: simply exec's the given command to the shell
      • pre_analysis: Would be used before pwninit fetches and patches the binary, and could take commands that work on {binary}, but not commands that operate on {patched_binary}.
      • post_analysis: Would be used after pwninit has completed running, and would be ab.e to take commands that work on both {binary} and {patched_binary}. Post analysis commands should be used if you want to actually run the binary during your analysis.
      • The syntax for the command is up for debate, currently spwn uses the following format [command, timeout], however a tuple of (command, timeout) could be used in Rust or something similar.
    • Another usage for these commands could be to open your decompiler of choice using the given binary. spwn currently has dedicated decompiler commands for IDA free and others. This could be useful behavior to mimic, however it probably isn't necessary with commands implemented.
    • An example of what the analysis would look like would be something like this customanalyzer.
  3. Update pwninit.rs to use the pre_analysis and post_analysis functions with the given config commands. As an example of how this would be implemented:

/// Run `pwninit` with specified options
pub fn run(opts: Opts) -> Result {
    // .. snip config fetching stuff ..
   command_runner::pre_analysis(config); // <--------------------- #1

    // Detect unspecified files
    let opts = opts.find_if_unspec().context(FindSnafu)?;

    // Print detected files
    opts.print();
    println!();

    set_bin_exec(&opts).context(SetBinExecSnafu)?;
    maybe_visit_libc(&opts);

    // Redo detection in case the ld was downloaded
    let opts = opts.find_if_unspec().context(FindSnafu)?;

    set_ld_exec(&opts).context(SetLdExecSnafu)?;

    if !opts.no_patch_bin {
        patch_bin::patch_bin(&opts).context(PatchBinSnafu)?;
    }

    if !opts.no_template {
        solvepy::write_stub(&opts).context(SolvepySnafu)?;
    }

    command_runner::post_analysis(config); // <----------------- #2

    Ok(())
}

Alternatives

  • Using all of these utilities separately would achieve the same result, it would just be more time consuming.

f-strings break templates

pwninit templates substitute in stuff like this: {bindings}, {bin_name}, {proc_args}
if you have an f string in your template, for example f"{1}", pwninit fails with this error

error: failed making template solve script: error filling in solve script template: KeyError(Invalid key: 1)

i guess it searches for all {...} pattern matches and tries substituting them in, and crashes if it cant

a fix could be making pwninit warn but not error when it can't substitute stuff in, or hardcode what is substituted, like just bindings bin name or proc args, or letting people use {{...}} or \{...\} which will just turn into {...} after the solve script is created

Integrate patchelf

Hello,
I recently found this package randomly from AUR. Great Work!
This tool is very useful and automates what I do manually, especially the unstrip libc part.

Do you consider integrating things like patchelf?
Although pwntools can works fine, if we can use patchelf to set rpath and interpreter, we can even debug it naturally in gdb!

The following is the snippet I do manually everytime.

patchelf --set-interpreter ./ld-2.27.so ./target
patchelf --set-rpath `pwd` ./target # for libc.so.6

Thanks for such a great tool again!

single patchelf command corrupts symbols

binary/libc: Archive.zip
OS: Manjaro
patchelf: 0.18.0

Gdb can't find any symbols anymore after running pwninit:

$ pwninit --bin rpsls --libc libc.so.6 --no-template
bin: rpsls
libc: libc.so.6
ld: ./ld-2.31.so

unstripping libc
https://launchpad.net/ubuntu/+archive/primary/+files//libc6-dbg_2.31-0ubuntu9.2_amd64.deb
eu-unstrip: cannot find matching section for [16] '.text'
eu-unstrip: cannot find matching section for [17] '__libc_freeres_fn'
eu-unstrip: cannot find matching section for [18] '.rodata'
eu-unstrip: cannot find matching section for [19] '.stapsdt.base'
eu-unstrip: cannot find matching section for [20] '.interp'
eu-unstrip: cannot find matching section for [21] '.eh_frame_hdr'
eu-unstrip: cannot find matching section for [22] '.eh_frame'
eu-unstrip: cannot find matching section for [23] '.gcc_except_table'
eu-unstrip: cannot find matching section for [24] '.tdata'
eu-unstrip: cannot find matching section for [25] '.tbss'
eu-unstrip: cannot find matching section for [26] '.init_array'
eu-unstrip: cannot find matching section for [27] '.data.rel.ro'
eu-unstrip: cannot find matching section for [28] '.dynamic'
eu-unstrip: cannot find matching section for [29] '.got'
eu-unstrip: cannot find matching section for [30] '.got.plt'
eu-unstrip: cannot find matching section for [31] '.data'
eu-unstrip: cannot find matching section for [32] '__libc_subfreeres'
eu-unstrip: cannot find matching section for [33] '__libc_IO_vtables'
eu-unstrip: cannot find matching section for [34] '__libc_atexit'
eu-unstrip: cannot find matching section for [35] '.bss'
eu-unstrip: cannot find matching section for [36] '__libc_freeres_ptrs'
warning: failed unstripping libc: eu-unstrip exited with failure: exit status: 1
copying rpsls to rpsls_patched
running patchelf on rpsls_patched

$ ldd rpsls_patched
        linux-vdso.so.1 (0x00007ffff7fc8000)
        libc.so.6 => ./libc.so.6 (0x00007ffff7dc9000)
        ./ld-2.31.so => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fca000)

$ gdb rpsls_patched
GNU gdb (GDB) 13.1
...
gef➤  b main
Function "main" not defined.

Same behavior when manually running patchelf with both args:

$ cp rpsls rpsls_patched
$ patchelf --set-interpreter ./ld-2.31.so --set-rpath . rpsls_patched
$ gdb rpsls_patched
GNU gdb (GDB) 13.1
...
gef➤  b main
Function "main" not defined.

Only with 2 separate commands it works as expected:

$ cp rpsls rpsls_patched
$ patchelf --set-interpreter ./ld-2.31.so rpsls_patched
$ patchelf --set-rpath . rpsls_patched

$ ldd rpsls_patched
        linux-vdso.so.1 (0x00007ffff7fc8000)
        libc.so.6 => ./libc.so.6 (0x00007ffff7dc9000)
        ./ld-2.31.so => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fca000)

$ gdb
GNU gdb (GDB) 13.1
...
gef➤  b main
Breakpoint 1 at 0x153a

Improve reproducability of stack-based exploits

Apparently, there might be cases where explicitly calling ./ld affects the contents of the stack in a way that breaks certain exploits. The way to fix this would be to patch the binary to use the custom linker path instead of the system one (or use fake chroot magic?). Either way, it might make sense to use the same method to avoid needing an explicit LD_PRELOAD=....

DEBUG check in template doesn't work?

I don't know if it's a bug in pwntools or what but the check if args.DEBUG: in the standard template doesn't work for me. It seems like launching solve.py with python3 solve.py LOCAL DEBUG only changes the log_level to debug.

change name of solve.py

This isn't an issue, but I didn't know where else to ask this, so forgive me. I wanted to change the name of solve.py to exploit.py, everything else being the same, is there any way I can do this?

Compilation fails after commit 661f1620e3a261d4b32fc5987acc322d59fcc874

I tried to compile from source, but it seems that the updated reqwest version breaks the code. When compiling the previous commit everything works fine, but from 661f162 onwards I get these errors:

error[E0599]: no method named `context` found for opaque type `impl Future` in the current scope
  --> src/libc_deb.rs:73:34
   |
73 |     let resp = reqwest::get(url).context(DownloadError)?;
   |                                  ^^^^^^^ method not found in `impl Future`

error[E0277]: the trait bound `Response: std::io::Read` is not satisfied
  --> src/libc_deb.rs:99:36
   |
99 |     let mut deb = ar::Archive::new(deb_bytes);
   |                                    ^^^^^^^^^ the trait `std::io::Read` is not implemented for `Response`
   |
   = note: required by `ar::Archive::<R>::new`

error[E0277]: the trait bound `Response: std::io::Read` is not satisfied
   --> src/libc_deb.rs:99:19
    |
99  |     let mut deb = ar::Archive::new(deb_bytes);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::io::Read` is not implemented for `Response`
    | 
   ::: /home/dario/.cargo/registry/src/github.com-1ecc6299db9ec823/ar-0.8.0/src/lib.rs:437:23
    |
437 | pub struct Archive<R: Read> {
    |                       ---- required by this bound in `ar::Archive`

error[E0599]: the method `next_entry` exists for struct `ar::Archive<Response>`, but its trait bounds were not satisfied
   --> src/libc_deb.rs:102:37
    |
102 |     while let Some(Ok(entry)) = deb.next_entry() {
    |                                     ^^^^^^^^^^ method cannot be called on `ar::Archive<Response>` due to unsatisfied trait bounds
    | 
   ::: /home/dario/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwest-0.10.8/src/async_impl/response.rs:24:1
    |
24  | pub struct Response {
    | ------------------- doesn't satisfy `Response: std::io::Read`
    |
    = note: the following trait bounds were not satisfied:
            `Response: std::io::Read`

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `pwninit`

To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error: build failed

Changing reqwest back to 0.9.22 makes everything work with the latest commit as well

Problem when installing ( compiling phase )

Hello,
I am always having this error when installing pwninit, is it from caused by the last update ? can you help me.
Thank you
Compiling pwninit v3.0.1
error[E0277]: a value of type std::ffi::OsString cannot be built from an iterator over elements of type &_
--> /root/.cargo/registry/src/github.com-1ecc6299db9ec823/pwninit-3.0.1/src/patch_bin.rs:112:10

Capture

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.