Handle Pwn Libc and Host a Pwn
TyeYeah Lv4

This blog introduces tools to deal with libc problems in pwn and to host pwn challenges.

Pwn Libc Part

The libc version matters in Linux pwn challenges. Using different libc means having different gadgets, different heap mechanisms and maybe totally differrnt program behaviors.

Ubuntu 16.04 gets libc 2.23, Ubuntu 18.04 gets libc 2.27, the newer versions gets libc 2.29, libc 2.30 and even libc 2.31. This is always the reason why some newcomers cannot test the exploit scripts on their own machine, or they may not even run program successfully. All due to the wrong libc using.

This part provides you with some libc related tools.

LibcSearcher

It is a tool on github, helps determine which libc the remote server is using (according to function addresses you leaked, like some online searching sites)

To install

1
2
3
$ git clone https://github.com/lieanu/LibcSearcher.git
$ cd LibcSearcher
$ python setup.py develop

Usage

1
2
3
4
5
6
7
8
9
from LibcSearcher import *

# the 2rd arg should be address you leaked
obj = LibcSearcher("fgets", 0X7ff39014bd90)
# if it's not unique, it will prompt you to choose manually, or u can try `add_condition(leaked_func, leaked_address)`

obj.dump("system") # offset of 'system()' in libc
obj.dump("str_bin_sh") # offset of '/bin/sh' string in libc
obj.dump("__libc_start_main_ret")

With this you can not only know the remote libc

pwn_debug

It’s like an advanced pwntools, with the ability to run different libcon one host machine.

Start with installation

1
2
3
4
$ git clone https://github.com/ray-cp/pwn_debug.git
$ cd pwn_debug
$ sudo python setup.py install
# or python setup.py install --user

Then download and compile glibc with debug symbols

1
2
3
4
5
6
# compile specific version
# ./build.sh $(version)
$ ./build.sh 2.23

# compile all versions in './build.sh'
$ ./build.sh

They are all located in /glibc/*

1
2
$ ls /glibc/
$ source x64 x86

And the normal usage is divided into 3 steps:

  1. Import and declare object
  2. Set the mode parameters, three mode in total: debug, local and remote.
  3. Run the mode and interactive like using pwntools
    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
    from pwn_debug import *

    ## step 1
    pdbg=pwn_debug("binary") # specific the binary filename
    pdbg.context.terminal=['tmux', 'splitw', '-h'] # set terminal

    ## step 2
    pdbg.local("libc.so.6") # local libc file
    pdbg.debug("2.23") # libc in /glibc/*
    pdbg.remote('host','port') # remote address


    ## step 3
    #p=pdbg.run("local")
    #p=pdbg.run("debug")
    p=pdbg.run("remote") # choose mode

    pdbg.bp([0x9aa]) # easy to set breakpoint, regardless of PIE

    # the followings are pwntools functions
    elf=pdbg.elf
    print hex(elf.got['printf'])
    print hex(elf.plt['printf'])

    libc=pdbg.libc
    print libc.symbols['system']
    p.interactive()
    More usages on github wiki, btw this is for Python2, Python3 users need to modify print formats and some import parts to make it work.

glibc-all-in-one and libc-database

glibc-all-in-one is just for debugging different version libc with symbols (it can assist pwn_debug), and libc-database is for ctfers to get the version of remote server responding to leaked information (the engine of LibcSearcher).

  • glibc-all-in-one

You can update the libc list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ./update_list
[+] Common list has been save to "list"
[+] Old-release list has been save to "old_list"

$ cat old_list
2.21-0ubuntu4.3_amd64
2.21-0ubuntu4.3_i386
...
2.30-0ubuntu2_amd64
2.30-0ubuntu2_i386

$ cat list
2.23-0ubuntu11.3_amd64
2.23-0ubuntu11.3_i386
...
2.33-0ubuntu9_amd64
2.33-0ubuntu9_i386

use download for packages in the list; use download_old for packages in the old_list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ glibc-all-in-one ./download 2.23-0ubuntu10_i386
Getting 2.23-0ubuntu10_i386
-> Location: https://mirror.tuna.tsinghua.edu.cn/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_i386.deb
-> Downloading libc binary package
-> Extracting libc binary package
-> Package saved to libs/2.23-0ubuntu10_i386
-> Location: https://mirror.tuna.tsinghua.edu.cn/ubuntu/pool/main/g/glibc/libc6-dbg_2.23-0ubuntu10_i386.deb
-> Downloading libc debug package
-> Extracting libc debug package
-> Package saved to libs/2.23-0ubuntu10_i386/dbg
$ ls libs/2.23-0ubuntu10_i386
. .. .debug ld-2.23.so libc-2.23.so libpthread.so.0 ......
$ ls libs/2.23-0ubuntu10_i386/.debug
ld-2.23.so libc-2.23.so ......

Need additional libc packages, use extract

1
2
3
4
$ ./extract
Usage: ./extract deb output
$ ./extract ~/libc6_2.26-0ubuntu2_i386.deb /tmp/test
$ ./extract ~/libc6-dbg_2.26-0ubuntu2_i386.deb /tmp/test_dbg

Then build as

1
2
3
4
5
$  ./build
Usage: ./build version arch
Supported version:2.19, 2.23-2.29
Supported arch: i686, amd64
$ ./build 2.29 i686
  • libc-database

First install some dependencies:

1
2
3
4
5
6
$ apt-get update
$ apt-get install -y \
binutils file \
wget \
rpm2cpio cpio \
zstd jq

The online version is available and you can build one using libc-database/searchengine.
Fetch the desired libc categories and extract the symbol offsets, and update database:

1
2
3
$ ./get  # List categories
$ ./get ubuntu debian # Download Ubuntu's and Debian's libc, old default behavior
$ ./get all # Download all categories. Can take a while!

Add a custom libc:

1
$ ./add /usr/lib/libc-2.21.so

Find the libc you want using leaking addresses. Only the last 12 bits are checked, because randomization usually works on page size level.

1
2
$ ./find printf 260 puts f30
archive-glibc (libc6_2.19-10ubuntu2_i386)

Dump some useful offsets

1
2
3
4
5
6
$ ./dump libc6_2.19-0ubuntu6.6_i386
offset___libc_start_main_ret = 0x19a83
offset_system = 0x00040190
offset_dup2 = 0x000db590
offset_recv = 0x000ed2d0
offset_str_bin_sh = 0x160a24

And check whether a library is already in the database

1
2
$ ./identify /usr/lib/libc.so.6
local-f706181f06104ef6c7008c066290ea47aa4a82c5

patchelf

PatchELF is a simple utility for modifying existing ELF executables and libraries. It helps program run on own library path RPATH and cunstom libc like pwn_debug, and so on.

Installation

1
2
3
4
5
6
7
8
9
# apt 
$ sudo apt install patchelf

# or compile by yourself
$ ./bootstrap.sh
$ ./configure
$ make
$ make check
$ sudo make install

Choose the dynamic loader (“ELF interpreter”) of executables

1
$ patchelf --set-interpreter /lib/my-ld-linux.so.2 my-program

Change (or shrink) the RPATH of executables and libraries

1
2
3
4
5
6
# set `RPATH`
$ patchelf --set-rpath /opt/my-libs/lib:/other-libs my-program
# shrink `RPATH`: get rid of useless `RPATH`
$ patchelf --shrink-rpath my-program
# cooperate with `--allowed-rpath-prefixes`
$ patchelf --shrink-rpath --allowed-rpath-prefixes /usr/lib:/foo/lib my-program

More on README.md

Host Pwn Challenges

Most of the time we debug locally due too the speed and other facters, but the exploit scripts sometimes goes wrong when testing on remote dur to details in interaction.

Here introduces several ways to host the pwn challenges, not only for CTF competition organizers but facilitate CTFers to test remote on local.

One Command

In real challenge we are given an ip and a port, therefore the easiest way is to do port forwarding using socat:

1
$ socat tcp-l:8080,fork exec:/root/pwn_to_run,reuseaddr

Then you can nc to interact.

Add nohup, even combine with screen/& will helps.

1
$ nohup socat tcp-l:8080,fork exec:/root/pwn_to_run,reuseaddr

ctf_xinetd

A docker repository for deploying CTF challenges

xinetd - the extended Internet services daemon. It starts programs that provide Internet services.
xinetd is the only daemon process started and it listens on all service ports for the services listed in its configuration file.
ctf_xinetd uses docker to run xinetd and host the pwn challenge.

The brief README.md comes


  • Configuration

Put files to floder bin. They’ll be copied to /home/ctf. Update the flag at the same time.

Edit ctf.xinetd. replace ./helloworld to your command.

You can also edit Dockerfile, ctf.xinetd, start.sh to custom your environment.

  • Build
1
$ docker build -t "helloworld" .

DO NOT use bin as challenge’s name

  • Run
1
$ docker run -d -p "0.0.0.0:pub_port:9999" -h "helloworld" --name="helloworld" $ helloworld

pub_port is the port you want to expose to the public network.

  • Capture traffic

If you want to capture challenge traffic, just run tcpdump on the host. Here is an example.

1
$ tcpdump -w helloworld.pcap -i eth0 port pub_port

pwn_deploy_chroot

A project for deploying ctf pwn challenge use chroot

Since ctf_xinetd needs to modify flag, ctf.xinetd, and only deploys one challenge a time, another convenient tool pwn_deploy_chroot comes.

pwn_deploy_chroot is able to deploy multiple challenges in one docker machine, and generate flag & configurations automatically.
Replacing /bin/sh in chroot with statically linked catflag gives users no chance to use sh fork bombs. Ports starts from 10000, +1 for each additional program.

Come the brief README.md


  • Before
1
2
3
4
# Install the latest version docker
$ curl -s https://get.docker.com/ | sh
# Install docker compose
$ apt install docker-compose
  • How to use
  1. Put your pwn program to ./bin (Note that the filename should not contain special characters.)
  2. python initialize.py
  3. docker-compose up –build -d # please run as root

You can edit config.py to decide whether to replace /bin/sh with catflag

1
2
3
4
5
6
# Whether to replace /bin/sh

## replace
REPLACE_BINSH = True
## not replace(default)
REPLACE_BINSH = False
  • Attention

The flag will be generated by the initialize.py and it store in flags.txt

The port information of the pwn program is also inside the flags.txt.

  • Update

2018.09.17 version v1

2018.09.23 version v2:Use the catflag program instead of /bin/sh, which is more secure

Powered by Hexo & Theme Keep
Total words 135.7k