├── screenshot.png ├── scripts ├── init.sh └── db.sql ├── start.sh ├── conf └── database.yml ├── contrib ├── config ├── vimrc └── tmux.conf ├── .github └── workflows │ └── docker-image.yml ├── Dockerfile └── README.md /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phocean/dockerfile-msf/HEAD/screenshot.png -------------------------------------------------------------------------------- /scripts/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | rm -f /var/run/postgresql/*.pid 5 | /etc/init.d/postgresql start 6 | 7 | /bin/bash 8 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker build --no-cache -t phocean/msf . 3 | docker run --rm -i -t -p 9990-9999:9990-9999 -v ~/.msf4:/root/.msf4 -v /tmp/msf:/tmp/data --name msf phocean/msf -------------------------------------------------------------------------------- /conf/database.yml: -------------------------------------------------------------------------------- 1 | production: 2 | adapter: postgresql 3 | database: msfdb 4 | username: msf 5 | password: MyS3cr$t 6 | host: 127.0.0.1 7 | port: 5432 8 | pool: 75 9 | timeout: 5 10 | -------------------------------------------------------------------------------- /contrib/config: -------------------------------------------------------------------------------- 1 | [framework/core] 2 | SessionLogging=True 3 | PROMPT=%grn%T %red%L %yel[S:%red%S%yel,J:%red%J%yel]%whi 4 | LogLevel=5 5 | TimestampOutput=true 6 | ConsoleLogging=true 7 | ExitOnSession=False 8 | PromptTimeFormat=%H:%M:%S 9 | 10 | [framework/ui/console] 11 | -------------------------------------------------------------------------------- /scripts/db.sql: -------------------------------------------------------------------------------- 1 | update pg_database set datallowconn = TRUE where datname = 'template0'; 2 | \c template0 3 | update pg_database set datistemplate = FALSE where datname = 'template1'; 4 | drop database template1; 5 | create database template1 with template = template0 encoding = 'UTF8'; 6 | update pg_database set datistemplate = TRUE where datname = 'template1'; 7 | \c template1 8 | update pg_database set datallowconn = FALSE where datname = 'template0'; 9 | create user msf; 10 | alter user msf with encrypted password 'MyS3cr$t'; 11 | alter user msf CREATEDB; 12 | \q 13 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | schedule: 5 | - cron: '39 18 * * *' 6 | push: 7 | branches: [ master ] 8 | pull_request: 9 | branches: [ "master" ] 10 | 11 | jobs: 12 | 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | 19 | - name: Check Out Repo 20 | uses: actions/checkout@v4 21 | 22 | - name: Login to Docker Hub 23 | uses: docker/login-action@v3 24 | with: 25 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 26 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 27 | 28 | - name: Set up Docker Buildx 29 | id: buildx 30 | uses: docker/setup-buildx-action@v3 31 | 32 | - name: Build and push 33 | id: docker_build 34 | uses: docker/build-push-action@v3 35 | with: 36 | context: ./ 37 | file: ./Dockerfile 38 | push: true 39 | tags: ${{ secrets.DOCKER_HUB_USERNAME }}/msf:latest 40 | 41 | - name: Image digest 42 | run: echo ${{ steps.docker_build.outputs.digest }} 43 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:23.10 2 | 3 | MAINTAINER Phocean 4 | 5 | ARG DEBIAN_FRONTEND=noninteractive 6 | 7 | # PosgreSQL DB 8 | COPY ./scripts/db.sql /tmp/ 9 | 10 | # Startup script 11 | COPY ./scripts/init.sh /usr/local/bin/init.sh 12 | 13 | # conf 14 | COPY ./contrib/tmux.conf /root/.tmux.conf 15 | COPY ./contrib/vimrc /root/.vimrc 16 | 17 | WORKDIR /opt/ 18 | 19 | # Installation 20 | RUN apt-get -qq update \ 21 | && apt-get -yq install --no-install-recommends build-essential patch ruby-bundler ruby-dev zlib1g-dev liblzma-dev git autoconf build-essential libpcap-dev libpq-dev libsqlite3-dev \ 22 | postgresql postgresql-contrib postgresql-client dialog apt-utils \ 23 | ruby nmap nasm tmux vim \ 24 | && echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections \ 25 | && git clone https://github.com/rapid7/metasploit-framework.git \ 26 | && cd metasploit-framework \ 27 | && git fetch --tags \ 28 | && latestTag=$(git describe --tags `git rev-list --tags --max-count=1`) \ 29 | && git checkout $latestTag \ 30 | && rm Gemfile.lock \ 31 | && bundle install \ 32 | && /etc/init.d/postgresql start && chmod 666 /tmp/db.sql && su - postgres -c "psql -f /tmp/db.sql" \ 33 | && apt-get -y remove --purge build-essential patch ruby-dev zlib1g-dev liblzma-dev git autoconf build-essential libpcap-dev libpq-dev libsqlite3-dev dialog apt-utils \ 34 | && apt-get -y autoremove \ 35 | && apt-get -y clean \ 36 | && rm -rf /var/lib/apt/lists/* 37 | 38 | # DB config 39 | COPY ./conf/database.yml /opt/metasploit-framework/config/ 40 | 41 | # Configuration and sharing folders 42 | VOLUME /root/.msf4/ 43 | VOLUME /tmp/data/ 44 | 45 | # Locales for tmux 46 | ENV LANG C.UTF-8 47 | WORKDIR /opt/metasploit-framework/ 48 | 49 | CMD "init.sh" 50 | -------------------------------------------------------------------------------- /contrib/vimrc: -------------------------------------------------------------------------------- 1 | filetype plugin indent on 2 | syntax on 3 | 4 | if has("autocmd") 5 | au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal! g`\"" | endif 6 | endif 7 | 8 | "set laststatus=2 " Always show status line 9 | "set statusline=%f\ " Show filename 10 | "set statusline+=%h%w%m%r\ " Show flags 11 | "set statusline+=%= " Align right 12 | "set statusline+=%(%l,%c%V\ %=\ %P%) " Show ruler 13 | 14 | set tabstop=4 15 | set shiftwidth=4 16 | set expandtab 17 | set autoindent 18 | "set mouse=a 19 | 20 | set undofile "turn on the feature 21 | set undodir=$HOME/.vim/undo "directory where the undo files will be stored 22 | set noswapfile 23 | 24 | call plug#begin('~/.vim/plugged') 25 | Plug 'vim-airline/vim-airline' 26 | Plug 'vim-airline/vim-airline-themes' 27 | Plug 'nordtheme/vim' 28 | call plug#end() 29 | 30 | if !exists('g:airline_symbols') 31 | let g:airline_symbols = {} 32 | endif 33 | 34 | let g:airline_theme='minimalist' 35 | " powerline symbols 36 | let g:airline_left_sep = '' 37 | let g:airline_left_alt_sep = '' 38 | let g:airline_right_sep = '' 39 | let g:airline_right_alt_sep = '' 40 | let g:airline_symbols.branch = '' 41 | let g:airline_symbols.readonly = '' 42 | let g:airline_symbols.linenr = '☰ ' 43 | let g:airline_symbols.maxlinenr = ' ' 44 | let g:airline_symbols.dirty='⚡' 45 | " remove the filetype part 46 | let g:airline_section_x='' 47 | let g:airline_section_warning='' 48 | " " remove separators for empty sections 49 | let g:airline_skip_empty_sections = 1 50 | au User AirlineAfterInit :let g:airline_section_z = airline#section#create(['%l:%L']) 51 | 52 | let &t_SI = "\e[6 q" 53 | let &t_EI = "\e[2 q" 54 | 55 | " reset the cursor on start (for older versions of vim, usually not required) 56 | augroup myCmds 57 | au! 58 | autocmd VimEnter * silent !echo -ne "\e[2 q" 59 | augroup END 60 | -------------------------------------------------------------------------------- /contrib/tmux.conf: -------------------------------------------------------------------------------- 1 | # TMUX config - Kitty 2 | # File: .tmux.conf 3 | 4 | set-option -sa terminal-overrides ",xterm*:Tc" 5 | 6 | # Make it use C-a, similar to screen.. 7 | unbind C-b 8 | unbind l 9 | set -g prefix C-a 10 | #bind-key C-a last-window 11 | bind-key a send-prefix 12 | 13 | # Start windows and panes at 1, not 0 14 | set -g base-index 1 15 | set -g pane-base-index 1 16 | set-window-option -g pane-base-index 1 17 | set-option -g renumber-windows on 18 | 19 | # Set Copy mode to escape key 20 | bind-key -T copy-mode 'Enter' send -X copy-pipe-and-cancel "reattach-to-user-namespace pbcopy" 21 | 22 | # Use Vi mode 23 | set-window-option -g mode-keys vi 24 | # keybindings 25 | bind-key -T copy-mode-vi v send-keys -X begin-selection 26 | bind-key -T copy-mode-vi C-v send-keys -X rectangle-toggle 27 | bind-key -T copy-mode-vi y send-keys -X copy-selection-and-cancel 28 | 29 | # More straight forward key bindings for splitting 30 | unbind % 31 | bind l split-window -h -c "#{pane_current_path}" 32 | unbind '"' 33 | bind - split-window -v -c "#{pane_current_path}" 34 | 35 | # switch panes using Alt-arrow without prefix 36 | bind -n M-Left select-pane -L 37 | bind -n M-Right select-pane -R 38 | bind -n M-Up select-pane -U 39 | bind -n M-Down select-pane -D 40 | 41 | # Move panes 42 | bind -n M-PageUp swap-pane -U 43 | bind -n M-PageDown swap-pane -D 44 | bind -n M-z resize-pane -Z 45 | 46 | # Resize panes 47 | bind -n S-Down resize-pane -D 48 | bind -n S-Up resize-pane -U 49 | bind -n S-Left resize-pane -L 50 | bind -n S-Right resize-pane -R 51 | bind -n M-S-x kill-window 52 | 53 | bind x kill-pane 54 | 55 | # Here is a jewel of a bind which does the task of flipping the 56 | # orientation of the current pane with the pane before it (in the 57 | # ordering) -- I had a SO question on this and nobody answered. 58 | bind -n S-PageUp move-pane -t '.-' 59 | bind S-PageUp move-pane -t '.-' 60 | bind -n S-PageDown move-pane -h -t '.-' 61 | bind S-PageDown move-pane -h -t '.-' 62 | 63 | # Session 64 | bind X confirm-before kill-session 65 | 66 | # No delay for escape key press 67 | set -sg escape-time 0 68 | 69 | set -g default-terminal "screen-256color" 70 | set -g history-limit 50000 71 | 72 | #setw -g mouse on 73 | 74 | # Terminal emulator window title 75 | set-option -g set-titles on 76 | set-option -g set-titles-string '#S:#I.#P #W' 77 | 78 | # THEME 79 | set -g window-status-separator '' 80 | set -g status-bg colour237 81 | set -g status-fg white 82 | set -g status-justify centre 83 | set -g window-status-format ' #I:#W#F ' 84 | set -g status-interval 1 85 | set -g status-left-length 30 86 | set -g status-left '#[bg=colour120]#[fg=black] (#S) ' 87 | set -g status-right '#[bg=colour120]#[fg=black] #(cut -d " " -f 1-3 /proc/loadavg) ' 88 | 89 | # Pane number indicator 90 | set -g display-panes-colour colour148 91 | set -g display-panes-active-colour colour245 92 | 93 | # Clock mode 94 | set -g clock-mode-colour colour100 95 | set -g clock-mode-style 24 96 | 97 | # Enable mouse control (clickable windows, panes, resizable panes) 98 | set -g mouse on 99 | bind-key -T copy-mode MouseDragEnd1Pane send -X copy-pipe-and-cancel "pbcopy" 100 | 101 | # Notifying if other windows has activities 102 | #set-option -g visual-activity on 103 | #set-window-option -g monitor-activity on 104 | 105 | 106 | # Add a key for opening new windows to remote hosts. 107 | bind-key h command-prompt -p "SSH to host:" "if-shell '[ -n \"%%\" ]' 'new-window -n \"%1\" \"/usr/bin/ssh %1\"'" 108 | 109 | # Reload 110 | bind-key r source-file ~/.tmux.conf \; display-message "reloaded" 111 | 112 | set -g @plugin 'tmux-plugins/tpm' 113 | set -g @plugin 'tmux-plugins/tmux-sensible' 114 | set -g @plugin 'christoomey/vim-tmux-navigator' 115 | set -g @plugin 'tmux-plugins/tmux-yank' 116 | set -g @plugin 'ofirgall/tmux-window-name' 117 | set -g @plugin 'tmux-plugins/tmux-resurrect' 118 | 119 | set -g default-terminal "screen-256color" 120 | 121 | set -g @plugin 'o0th/tmux-nova' 122 | set -g @nova-nerdfonts true 123 | #set -g @nova-nerdfonts-left  124 | #set -g @nova-nerdfonts-right  125 | set -g @nova-pane-active-border-style "#87C0D0" 126 | set -g @nova-pane-border-style "#282a36" 127 | set -g @nova-status-style-bg "#3A3A3A" 128 | set -g @nova-status-style-fg "#d8dee9" 129 | set -g @nova-status-style-active-bg "#87C0D0" 130 | set -g @nova-status-style-active-fg "#2e3540" 131 | set -g @nova-status-style-double-bg "#4C576A" 132 | set -g @nova-pane "#I#{?pane_in_mode,  #{pane_mode},}  #W" 133 | set -g @nova-segment-mode "#{?client_prefix,󰘳,}" 134 | set -g @nova-segment-mode-colors "#81A1C1 #ffffff" 135 | set -g @nova-segment-whoami "#(whoami)@#h" 136 | set -g @nova-segment-whoami-colors "#81A1C1 #ffffff" 137 | set -g @nova-rows 0 138 | set -g @nova-segments-0-left "mode" 139 | set -g @nova-segments-0-right "whoami" 140 | 141 | #set -g @plugin 'wfxr/tmux-power' 142 | set -g @tmux_power_theme '#59C2FE' 143 | set -g @tmux_power_time_format '%H:%m' 144 | set -g @tmux_power_session_icon '' 145 | 146 | # Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf) 147 | run '~/.tmux/plugins/tpm/tpm' 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Metasploit Framework dockerfile 2 | 3 | # Purpose 4 | 5 | This Dockerfile builds a Debian-based Docker container with Metasploit-Framework installed. 6 | 7 | **A quick and easy way to deploy Metasploit on any box, including Linux, MacOS or Windows!** 8 | 9 | ![phocean/msf](https://raw.githubusercontent.com/phocean/dockerfile-debian-metasploit/master/screenshot.png) 10 | 11 | MSF is started automatically with: 12 | 13 | - all dependencies installed, 14 | - automatic updates at startup, 15 | - a connection with the local Postgres database, 16 | - volumes, to share data and get access to your custom Metasploit scripts. 17 | 18 | It also includes: 19 | 20 | - tmux, to use multiple windows (msfconsole, shell, etc.) inside the container; 21 | - nmap, the famous network scanner (along with ncat); 22 | - nasm, to support custom encoders; 23 | - a configuration file to get an improved prompt in Metasploit, with timestamping and sessions/jobs status. 24 | 25 | # Use 26 | 27 | The image is built every day by Docker Hub, upon the Dockerfile of my github repository. 28 | 29 | The image is based on Ubuntu LTS and the nighly builds Metasploit repository. 30 | 31 | Also note that the image is signed (trusted content) so that the integrity of each layer in the image is checked. 32 | 33 | You can get the image with this simple command: 34 | 35 | ```bash 36 | docker pull phocean/msf 37 | ``` 38 | 39 | This command will download the image and you should have it available in your local image repository: 40 | 41 | ```bash 42 | docker images 43 | ``` 44 | 45 | 46 | # Run 47 | 48 | Now, you can enjoy a neat msf prompt with this command: 49 | 50 | ```bash 51 | docker run --rm -i -t -p 9990-9999:9990-9999 -v $HOME/.msf4:/root/.msf4 -v /tmp/msf:/tmp/data --name msf phocean/msf 52 | ``` 53 | 54 | From there, you can start *msfconsole*, *tmux* or any other Metasploit tool (*msfvenom*, *pattern_offset.rb*, etc.). 55 | 56 | Explanations: 57 | 58 | - We map the port range from 9990 to 9999 to our host, to catch reverse shells back. 59 | - We mount the local *.msf4* folder, where you can set your prompt and put custom scripts and modules, to */root/.msf4* inside the container (if you want to make some changes at runtime, beware to do it from your host, not from within the container). 60 | - Similarly, we mount a */tmp/data folder* to exchange data (a dump from a successful exploit, for instance). 61 | 62 | Of course, it is up to you to adjust it to your taste or need. 63 | 64 | You can also give it full access to the host network: 65 | 66 | > Note that this can be **risky** as all services on your host, including those that listen on localhost, would be reachable from within the container, in case it is compromise. 67 | 68 | ```bash 69 | docker run --rm -it --net=host --hostname msf -v $HOME/.msf4:/root/.msf4 -v /tmp/msf:/tmp/data --name msf phocean/msf 70 | ``` 71 | 72 | When you need extra terminals besides, use an alias such as: 73 | 74 | ```bash 75 | docker exec -it msf /bin/bash 76 | ``` 77 | 78 | At any time, you can exit, which only stops (suspend) the container. 79 | 80 | Because of the *--rm*, the container itself is *not persistent*. 81 | 82 | Persistency is however partially made throught the mounting of your local folders (history, scripts). 83 | So if you want to restart a session, just re-run the docker. 84 | 85 | I find it more convenient, but if, for some reason, you prefer to keep the container, just remove the *--rm* and then you can restart the stopped container anytime: 86 | 87 | ```bash 88 | docker restart msf 89 | docker attach msf 90 | # Later: 91 | docker rm msf 92 | ``` 93 | 94 | # Keep it up-to-date 95 | 96 | The image is built daily from Docker Hub, so for example I use a crontab entry to keep it up-to-date: 97 | 98 | ``` 99 | */5 * * * * root docker pull phocean/msf 100 | ``` 101 | 102 | Alternatively, you can keep all your images (not only phocean/msf) with such a command: 103 | 104 | ``` 105 | /usr/bin/docker images | awk '(NR>1) && ($2!~/none/) {print $1":"$2}' | xargs -L1 docker pull 2>&1 > /dev/null 106 | ``` 107 | 108 | Finally, you can choose to build it manually with the steps described below. 109 | 110 | # Build 111 | 112 | If for some reason, including trust, you prefer to build the container, just use this command: 113 | 114 | ```bash 115 | docker build -t phocean/msf . 116 | ``` 117 | 118 | Alternatively, you can use the provided `start.sh` script. 119 | 120 | Docker will download the Debian image and then execute the installation steps. 121 | 122 | > Be patient, the process can be quite long the first time. 123 | 124 | Note that you may want to: 125 | 126 | - copy the *contrib/config* file to the *~/.msf4* folder to get a nice prompt. 127 | - customize the *conf/tmux* file, if you plan to use this tool. 128 | 129 | > The configuration of Tmux maps the keyboard as in Screen (CTRL-A). It also makes a few cosmetic changes to the status bar. 130 | 131 | > Note that you could adjust the init script to automatically launch Tmux with a msf window and a bash one, for instance. I don't make it the default, because I don't want to bother people who don't need/want Tmux.* 132 | 133 | # Contributors 134 | 135 | Thanks to contributors that helped this project: 136 | 137 | * [Lenny Zeltser](https://github.com/lennyzeltser) 138 | * [llyus](https://github.com/pierrickv) 139 | * [r0mdau](https://github.com/r0mdau) 140 | --------------------------------------------------------------------------------