使用docker调试和部署pwn题

WriteUp 2个月前 admin
31 0 0
主要内容如下:

1.提供了使用docker构建的不同glibc版本的轻量级调试环境,涵盖glibc-2.27/2.31/2.35/2.38等主流版本。

2.提供了11种基于docker环境的pwn题部署模板。

3.给出了相关的Dockerfile用于定制化环境。


关于docker的基础概念不做过多的介绍。可以到Docker : Accelerated, Containerized Application Development官方网站上获取更多的信息。熟悉并使用docker的常用命令并不需要太多的时间。


在使用docker指令或者docker-compose的指令时,尽量到官方手册上查询有关指令的详情内容。


一般来说,使用docker-compose来运行多个容器,也可以集成编译镜像、运行实例等流程。





调试环境


如果需要一个完整的、经常使用的主力调试环境,我推荐使用skysider/pwndocker: A docker environment for pwn in ctf (https://github.com/skysider/pwndocker),工具非常完整,可以直接拉取镜像。


本文所分享的,是临时使用的、快速的、轻量级调试环境。镜像只包含必要的程序,镜像包相对较小,下载更快。


对于不同的版本,特别是传统的glibc的环境,有时候使用patchelf改变二进制文件的so无法得到与远程环境一样的内存布局,并且会缺乏调试符号。而对于部署在debian/fedora等其他操作系统上的程序,受到的影响会更大。因此,使用docker搭建一个一模一样的调试环境很有必要。


目前,我针对常用的环境制作了轻量级镜像,并发布在roderickchan/debug_pwn_env Tags | Docker Hub(https://hub.docker.com/r/roderickchan/debug_pwn_env/tags)

使用docker调试和部署pwn题


每个镜像都给出了Ubuntu的版本、其使用的glibc libc.so.6的具体版本(包括小版本)和镜像编译时间。


直接使用docker pull roderickchan/debug_pwn_env:22.04-2.35-0ubuntu3.1-20230213这样的命令拉取镜像即可。


拉取镜像后,运行命令如下:


docker run -it --rm -v host_path:container_path -p host_port:container_port --cap-add=SYS_PTRACE IMAGE_ID # auto update

docker run -it -rm -v host_path:container_path -p host_port:container_port --cap-add=SYS_PTRACE IMAGE_ID /bin/bash # do not update

docker run -it --rm -v host_path:container_path -p host_port:container_port --privileged IMAGE_ID # privileged enabled and auto update


可以映射宿主机端口和容器端口,映射文件或者目录。如果不带命令,容器会自动更新一些pwn相关的仓库和包,如果带上命令,就会执行指定的命令。


事实上这个镜像的Dockerfile如下:


ARG BUILD_VERSION

FROM ubuntu:$BUILD_VERSION

ARG DEBIAN_FRONTEND=noninteractive
ARG HUB_DOMAIN=github.com
ARG NORMAL_USER_NAME=ctf

ENV TZ=Etc/UTC
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

WORKDIR /root

RUN apt-get update && apt-get -y dist-upgrade && apt-get install -y --fix-missing python3 python3-pip python3-dev lib32z1
xinetd curl gcc gdb gdbserver g++ git libssl-dev libffi-dev build-essential tmux
vim iputils-ping gdb-multiarch
file net-tools socat ruby ruby-dev locales autoconf automake libtool make &&
gem install one_gadget &&
gem install seccomp-tools &&
sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen

# 先执行容易失败的操作
RUN git clone https://${HUB_DOMAIN}/pwndbg/pwndbg &&
cd ./pwndbg &&
./setup.sh

RUN git clone https://${HUB_DOMAIN}/NixOS/patchelf.git &&
cd ./patchelf &&
./bootstrap.sh &&
./configure &&
make &&
make install

RUN git clone https://${HUB_DOMAIN}/hugsy/gef.git &&
git clone https://${HUB_DOMAIN}/RoderickChan/Pwngdb.git &&
git clone https://${HUB_DOMAIN}/Gallopsled/pwntools &&
(mv /usr/lib/python3.11/EXTERNALLY-MANAGED /usr/lib/python3.11/EXTERNALLY-MANAGED.old || true) &&
pip3 install --upgrade --editable ./pwntools &&
git clone https://${HUB_DOMAIN}/RoderickChan/pwncli.git &&
pip3 install --upgrade --editable ./pwncli


COPY ./gdb-gef /bin
COPY ./gdb-pwndbg /bin
COPY ./update.sh /bin
COPY ./test-this-container.sh /bin
COPY ./.tmux.conf ./
COPY ./.gdbinit ./
COPY ./flag /
COPY ./flag /flag.txt

RUN chmod +x /bin/gdb-gef /bin/gdb-pwndbg /bin/update.sh /bin/test-this-container.sh &&
echo "root:root" | chpasswd &&
python3 -m pip install --upgrade pip &&
pip3 install ropper capstone z3-solver qiling lief

# normal user
RUN useradd ${NORMAL_USER_NAME} -d /home/${NORMAL_USER_NAME} -m -s /bin/bash -u 1001 &&
echo "${NORMAL_USER_NAME}:${NORMAL_USER_NAME}" | chpasswd &&
cp -r /root/pwndbg /home/${NORMAL_USER_NAME} &&
cp -r /root/gef /home/${NORMAL_USER_NAME} &&
cp -r /root/pwntools /home/${NORMAL_USER_NAME} &&
cp -r /root/Pwngdb /home/${NORMAL_USER_NAME} &&
cp -r /root/pwncli /home/${NORMAL_USER_NAME} &&

cp /root/.tmux.conf /home/${NORMAL_USER_NAME} &&
cp /root/.gdbinit /home/${NORMAL_USER_NAME} &&
cp /flag /home/${NORMAL_USER_NAME} &&
cp /flag.txt /home/${NORMAL_USER_NAME} &&
chown -R ${NORMAL_USER_NAME}:${NORMAL_USER_NAME} /home/${NORMAL_USER_NAME}

USER ${NORMAL_USER_NAME}:${NORMAL_USER_NAME}

WORKDIR /home/${NORMAL_USER_NAME}

RUN pip3 install --upgrade --editable ./pwntools &&
pip3 install --upgrade --editable ./pwncli


# switch to root and install zsh
USER root:root
RUN apt-get install -y sudo zsh &&
echo "${NORMAL_USER_NAME} ALL=(ALL) NOPASSWD : ALL" | tee /etc/sudoers.d/ctfsudo

# switch 2 normal user
USER ${NORMAL_USER_NAME}:${NORMAL_USER_NAME}
WORKDIR /home/${NORMAL_USER_NAME}


# install zsh
RUN curl -fsSL -O https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh &&
chmod +x ./install.sh &&
sed -i -e 's/read[[:space:]]*-r[[:space:]]*opt/opt=n/g' ./install.sh &&
./install.sh &&
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting &&
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions

COPY ./.zshrc ./

# expose some ports
EXPOSE 20 21 22 80 443 23946 10001 10002 10003 10004 10005

CMD ["/bin/update.sh"]


内置了自使用的tmux配置文件和gdb-gef/gdb-pwndbg启动脚本以及最新的pwncli库。完整文件位于GitHub – RoderickChan/docker_pwn_env: Debug pwn using docker image(https://github.com/RoderickChan/docker_pwn_env),可根据需求更改和自定义配置文件。


使用docker调试和部署pwn题


如果是debian系统,只需要安装一些基础的包即可:


#!/bin/bash

set -ex

apt update && apt install -y tmux gdb gdbserver wget rpm file binutils socat python3 python3-pip procps

# 修改tmux配置
cat > ~/.tmux.conf << "EOF"
set -g prefix C-a #
unbind C-b # C-b即Ctrl+b键,unbind意味着解除绑定
bind C-a send-prefix # 绑定Ctrl+a为新的指令前缀

# 从tmux v1.6版起,支持设置第二个指令前缀
set-option -g prefix2 ` # 设置一个不常用的`键作为指令前缀,按键更快些
#set-option -g mouse on # 开启鼠标支持
# 修改分屏快捷键
unbind '"'
bind - splitw -v -c '#{pane_current_path}' # 垂直方向新增面板,默认进入当前目录
unbind %
bind | splitw -h -c '#{pane_current_path}' # 水平方向新增面板,默认进入当前目录

# 设置面板大小调整快捷键
bind j resize-pane -D 10
bind k resize-pane -U 10
bind h resize-pane -L 10
bind l resize-pane -R 10
EOF

# 安装pwntools和pwncli
pip3 install pwntools pwncli
bash -c "$(wget https://gef.blah.cat/sh -O -)"


这里选用gef插件,因为gef插件的依赖少,安装速度快,功能比peda强大。


如果是fedora系统,安装软件包列表如下:


#!/bin/bash

dnf install -y tmux gdb gdb-gdbserver wget which file binutils socat python3 python3-pip procps

cat > ~/.tmux.conf << "EOF"
set -g prefix C-a #
unbind C-b # C-b即Ctrl+b键,unbind意味着解除绑定
bind C-a send-prefix # 绑定Ctrl+a为新的指令前缀

# 从tmux v1.6版起,支持设置第二个指令前缀
set-option -g prefix2 ` # 设置一个不常用的`键作为指令前缀,按键更快些
#set-option -g mouse on # 开启鼠标支持
# 修改分屏快捷键
unbind '"'
bind - splitw -v -c '#{pane_current_path}' # 垂直方向新增面板,默认进入当前目录
unbind %
bind | splitw -h -c '#{pane_current_path}' # 水平方向新增面板,默认进入当前目录

# 设置面板大小调整快捷键
bind j resize-pane -D 10
bind k resize-pane -U 10
bind h resize-pane -L 10
bind l resize-pane -R 10
EOF

pip3 install pwntools pwncli
bash -c "$(wget https://gef.blah.cat/sh -O -)"


和上面基本是一样的,不过有些包的名字不一样。


使用示例如下,使用最新的版本,普通用户的用户名为ctf(我的主力机器为Ubuntu 20.04,但是该题目运行环境需要Ubuntu 22.04):


使用docker调试和部署pwn题


这是另一个示例,使用之前的版本,普通用户名为roderick


使用docker调试和部署pwn题





出题模板


出题模板可以参考GitHub – RoderickChan/deploy_pwn_template: Templates for deploying pwn challenge in ctf(https://github.com/RoderickChan/deploy_pwn_template)


使用docker调试和部署pwn题


这里列出了11中出题模板文件,只需要按照不同的模板文件把二进制文件替换一下,设置flag等操作即可。


不同的模板各有优缺点。大部分的模板都支持从环境变量中动态获取flag和设置sha256proof of work


◆如果想要编译后体积小的镜像,优先采用alpine系列模板,不过要结合patchelf来部署glibc的题。

◆如果想限制系统调用,可以使用kctf+nsjail或者red.pwn.jail。

◆如果想限制运行目录,可以采用ubuntu+xinetd+chroot。

◆……


总有一款适合你。


ubuntu+xinetd+chroot为例,把题目源码放置在src目录下,修改编译文件和Dockerfile中的编译环境。


如果需要动态flag,就把docker-compose.yamlFLAG环境变量设置为真正的flag,如果不需要,删除FLAG环境变量即可。


如果需要pow,就把docker-compose.yamlENABLE_POW环境变量设置为1,否则设置为0或者删除均可。


如下图,使用pow时会验证sha256,可有效地避免爆破。


使用docker调试和部署pwn题





使用技巧


这里给出一些我使用docker的调试技巧。


1.使用docker commit将容器保存为镜像。


2.使用docker save/load保存和传输体积较小的镜像。


3.使用docker cp xxx:/lib/libc.so.6 .拷贝环境使用的libc.so.6。


4.运行容器的时候至少映射一个端口,以备不时之需,比如我喜欢映射23946端口,以使用IDA进行调试。


5.有些调试环境只需要安装一个gdbserver即可,然后启动容器的时候映射一下端口,在宿主机上使用gdb file -ex 'target remote 127.0.0.1:port'挂上去调试。这样既可以保证环境和远程一样,又能够使用本地的编辑器、文件,还不需要为镜像安装太多的软件包。


6.碰到需要输入特殊字符的场景,可以把所有给程序的输入保存到一个文件里面,然后使用gdb ./pwn < input启动,这个时候最好关闭aslr,避免地址空间不一样。


7.有时候忘记了映射端口,可以参考docker修改、增加和删除已创建容器映射端口 – orcl – 博客园 (https://www.cnblogs.com/orcl-2018/p/15450219.html),按步骤进行就好。


8.有时候忘记了映射目录,可以自己写一个python脚本,检查相关文件是否改动,如果改动,就执行docker cp命令拷贝。


9.使用有名管道等通信文件和tmux,甚至可以把容器和本地调试环境无缝衔接。具体的做法就是:容器和宿主机映射同一个有名管道文件;容器内使用gdbserver :1234 ./pwn < pipe启动;宿主机gdb远程链接,然后往管道文件写内容进行交互。如果同时想用脚本去调试,可以将第二步改为:./pwn > pipe1 < pipe2,然后宿主机读pipe1获取输出,写pipe2给程序输入。




使用docker调试和部署pwn题


看雪ID:roderick01

https://bbs.kanxue.com/user-home-956675.htm

*本文为看雪论坛优秀文章,由 roderick01 原创,转载请注明来自看雪社区

使用docker调试和部署pwn题

# 往期推荐

1、区块链智能合约逆向-合约创建-调用执行流程分析

2、在Windows平台使用VS2022的MSVC编译LLVM16

3、神挡杀神——揭开世界第一手游保护nProtect的神秘面纱

4、为什么在ASLR机制下DLL文件在不同进程中加载的基址相同

5、2022QWB final RDP

6、华为杯研究生国赛 adv_lua


使用docker调试和部署pwn题


使用docker调试和部署pwn题

球分享

使用docker调试和部署pwn题

球点赞

使用docker调试和部署pwn题

球在看

原文始发于微信公众号(看雪学苑):使用docker调试和部署pwn题

版权声明:admin 发表于 2024年1月6日 下午6:00。
转载请注明:使用docker调试和部署pwn题 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...