pwn-server icon indicating copy to clipboard operation
pwn-server copied to clipboard

A tool for easy manage a pwn server

pwn-server

这是一个自动化部署pwn题的工具

有如下特性:

1.自动化部署pwn

2.所有的pwn题都在不同的容器中,pwn-server将会自动化的创建容器

3.只占用一个端口,会根据不同的token将连接分发到不同的容器

4.支持一题多flag

5.自动记录pwn题流量

6.自动标记出读取了flag的流量

7.防fork bomb

8.支持多镜像,既不同容器可以指定不同的docker镜像

依赖

fanotify

pip3 install git+https://github.com/google/python-fanotify.git --user

使用

第一次使用时需要build基础容器

build基础容器:

docker build --tag cnss/pwn ./docker/pwndocker

启动pwn-server

sudo python3 pwnserver.py

目前并没有封装为服务,无守护程序

注:关闭容器后容器将会被自动删除

由于使用了fanotify,所以必须要有root权限

请务必保证pwn目录下的文件是可执行的!!!

自动化部署pwn

与其它脚本不同,用pwn-server部署pwn题只需要将pwn的二进制文件丢入pwn文件夹,pwn-server会自动的识别pwn题

pwn题目录结构

仓库中默认有2个题目在目录pwn下

pwn目录结构:

pwn
├── note
│   ├── RNote3
│   ├── startdocker.sh
│   └── startpwn.sh
└── pwn1
    ├── printf
    ├── startdocker.sh
    └── startpwn.sh

其中包含了2个pwn题 分别为note和pwn1

startdocker.sh默认为

/usr/sbin/xinetd -dontfork

这是docker运行时执行的命令

startpwn.sh为xinetd会执行的命令,对于pwn1而言就是

stdbuf -i 0 -o 0 -e 0 ./printf

多flag支持

对于每一个connect,在连接上后会要求输入token,之后交由get_pwn_data.py处理

若是要支持多flag则可做以下设置

def get_pwn_data(token):
    '''参数为token 返回该token对应的题目名和flag以及image_tag'''
    if token == 'note_01':
    	return ('note', "cnss{it_is_note_01}", "cnss/pwn")
    elif token == 'note_02':
        return ('note', 'cnss{it_is_note_02}', "cnss/pwn")

    if token == 'pwn1_01':
    	return ('pwn1', "cnss{it_is_pwn1}", "cnss/pwn")

    return('', '', '')

token=note_01

[*] Switching to interactive mode
[DEBUG] Received 0x51 bytes:
    "*** Error in `./RNote3': munmap_chunk(): invalid pointer: 0x00007fd7f0bd7afd ***\n"
*** Error in `./RNote3': munmap_chunk(): invalid pointer: 0x00007fd7f0bd7afd ***
$ ls -al ../
[DEBUG] Sent 0xb bytes:
    'ls -al ../\n'
[DEBUG] Received 0x154 bytes:
    'total 25\n'
    'drwxr-xr-x 1 pwn  pwn  4096 Aug 15 05:56 .\n'
    'drwxr-xr-x 1 root root 4096 Aug 15 03:33 ..\n'
    '-rw-r--r-- 1 pwn  pwn   220 Aug 31  2015 .bash_logout\n'
    '-rw-r--r-- 1 pwn  pwn  3771 Aug 31  2015 .bashrc\n'
    'drwxrwxrwx 1 root root    0 Aug 15 04:58 bin\n'
    '-rwxrwxrwx 1 root root   19 Aug 15 05:56 flag\n'
    '-rw-r--r-- 1 pwn  pwn   655 May 16  2017 .profile\n'
total 25
drwxr-xr-x 1 pwn  pwn  4096 Aug 15 05:56 .
drwxr-xr-x 1 root root 4096 Aug 15 03:33 ..
-rw-r--r-- 1 pwn  pwn   220 Aug 31  2015 .bash_logout
-rw-r--r-- 1 pwn  pwn  3771 Aug 31  2015 .bashrc
drwxrwxrwx 1 root root    0 Aug 15 04:58 bin
-rwxrwxrwx 1 root root   19 Aug 15 05:56 flag
-rw-r--r-- 1 pwn  pwn   655 May 16  2017 .profile
$ cat ../flag
[DEBUG] Sent 0xc bytes:
    'cat ../flag\n'
[DEBUG] Received 0x13 bytes:
    'cnss{it_is_note_01}'
cnss{it_is_note_01}$  

token=note_02

[*] Switching to interactive mode
[DEBUG] Received 0x51 bytes:
    "*** Error in `./RNote3': munmap_chunk(): invalid pointer: 0x00007fad2858cafd ***\n"
*** Error in `./RNote3': munmap_chunk(): invalid pointer: 0x00007fad2858cafd ***
$ cat ../flag
[DEBUG] Sent 0xc bytes:
    'cat ../flag\n'
[DEBUG] Received 0x13 bytes:
    'cnss{it_is_note_02}'
cnss{it_is_note_02}$  

docker 容器列表

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
335ac415ef15        7d472f349e2d        "/bin/sh -c /ctf/pwn…"   4 minutes ago       Up 4 minutes        1337/tcp            note_02
92de3f097e93        7d472f349e2d        "/bin/sh -c /ctf/pwn…"   7 minutes ago       Up 7 minutes        1337/tcp            note_01

当然,可以自定义该函数与ctf平台进行交互,从而做到反作弊

多镜像支持

考虑到不同题目可能要求的运行环境不同,pwnserver支持不同题目使用不同的镜像,只需要在get_pwn_data中设置即可

关于容器

对于每个不同的token,都会启动不同的容器,也就是说同一个题会有多个容器。

但是对于相同的token则不会启动多个容器

关于fork bomb

每个容器都有资源限制,限制了pid的数量

正常情况下父进程被发送SIGPIPE后子进程也会被杀死,所以在socket断开后fork炸弹一般会失效

当然,例外情况也是有的,详情见BUG

关于log

对于流量将会被自动记录在log目录下,若是一个连接获取了flag,那条log的文件名中将会包含flag

例如:

plusls@pwn:~/pwn/pwn-server/log$ ls -al
total 44
drwxrwxr-x 2 plusls plusls  4096 Aug 18 01:34 .
drwxrwxr-x 9 plusls plusls  4096 Aug 18 01:27 ..
-rw-r--r-- 1 root   root   12949 Aug 18 01:34 log.note.note_02.1534527196.572956.log
-rw-r--r-- 1 root   root   12237 Aug 18 01:34 log.note.note_02.1534527217.5434923-flag.log
-rw-r--r-- 1 root   root    1025 Aug 18 01:31 log.sh.sh.1534527057.2515137.log
-rw-r--r-- 1 root   root     507 Aug 18 01:31 log.sh.sh.1534527099.9255965-flag.log

判断一个连接是否成功拿到flag使用了fainotify,想了解实现可以阅读源码

config.json

pwn-server使用config.json来配置

{
    // 监听的地址
    "address": {
        "ip": "0.0.0.0",
        "port": 8888
    },
    // 尚未使用
    "sqlserver": "",
    // 放置pwn题的位置
    "pwn_dir": "./pwn",
    // 存放log的位置
    "log_dir": "./log",
    // 该目录下存放着xinetd的配置文件
    "pwmdocker_dir": "./docker/pwndocker",
    // 数据目录 目前只用来存放flag
    "data_dir": "./data"
}

TO DO

~~1.自动化管理容器~~

~~2.标记出成功getflag的log~~

3.细化容器权限控制

BUG

目前已知在某些情况下socket断开后程序仍会继续运行...

socket断开后程序结束的原因是SIGPIPE,当往一个写端关闭的管道或socket连接中连续写入数据时会引发SIGPIPE信号,从而导致程序结束

若是程序无任何写操作并进入死循环,则会出现冗余进程

例如

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
    setbuf(stdout, NULL);
    setbuf(stdin, NULL);
    setbuf(stderr, NULL);

    char s[0x300];
    while (1) {
        memset(s, 0, 0x300);
        printf("Please input somthing:");
        read(0, s, 0x300);
        printf("Your input is:");
        printf(s);
    }
    return 0;
}

printf被改为system后程序就没有任何写操作了,因此不会触发SIGPIPE,从而不会被结束