【OPS.0x01】为 Docker 连接 Wayland 图形环境

本文最后更新于:2024年2月23日 早上

这一日,Docker 再入桌面 GUI 境界

0x00. 一切开始之前

如果你只是寻求 docker 接通 Wayland 跑图形界面的解决方案,不想看笔者吐槽的各种批话,请直接跳过这一小节 :)

如果你想要的是 docker 接通显卡的解决方案,那你走错路了,这个方案在隔壁

笔者大一刚开始学 Pwn 的时候就因环境问题而感到苦手,上一题的环境可能是 Ubuntu 16.04 带个 glibc-2.23 只有 fastbin,下一题可能又变成 Ubuntu 18.04 带个 glibc-2.27 又多一个 tcache ,然后笔者本地环境又是 Ubuntu 20.04 带个 glibc-2.31 的同时 tcache 又多带一个 key,对于那时候以 glibc heap 利用作为主流的 Pwn 题而言若是本地 libc 版本不同则根本没有办法很好地调试好打远程

通过 LD_PRELOAD 参数在程序执行前 预先加载 libc 或许某些程度上是一个可行的办法,至少 libc 大版本相同的情况下载入不同的小版本基本上是没问题的,但是由于不同系统环境中 ld 版本不同的缘故,对于跨 ld 版本加载不同版本的 libc 则又可能直接 segmentation fault 了

虽然 Linux 的用户环境并不似 Windows 那样有着强壮的二进制前向兼容性,但是用户环境依托于内核环境、依托于内核向用户态暴露的接口——系统调用,而这并不是会轻易发生变动以及兼容性破坏的一个东西,由此,通过重新开辟一个对应的新的用户环境的方式—— 即形如 Docker 这样的操作系统层上的虚拟化方案,我们便能非常简单地搭建不同的 Pwn 题所对应的原始环境

当然,这里就不得不提某些连 libc 都不给的是人是鬼全靠猜的比赛的含金量了

但是 Docker 一直有一个问题就是默认是没有图形环境的,这意味着我们没法直接在 Docker 当中运行 GUI 程序,对于需要复杂堆风水等多次调试的 Pwn 题目而言则没办法像直接运行在主机上那样直接通过 gdb.attach() 弹一个伪终端窗口出来:

感觉不如 lldb

TMUX 为我们提供了一个比较好的 docker 中的多终端解决方案,当你在 tmux 中运行 exploit 执行到 gdb.attach() 这样需要一个新终端的命令时,tmux 可以无缝分割出一个新的窗口,通过额外指定 pwntools 中的 context.terminal 环境变量可以控制新窗口的分割位置:

是新终端吗?如新

但是 tmux 的翻页手感终究是差点意思,不像图形化界面那样可以很方便地用滚轮滚来滚去,同时基于 GUI 窗口的跨行文本复制也会跨多个 tmux 窗口,打破了不同窗口的隔离性:

更重要的一点是笔者当时非常 sb 认为 tmux 不能翻页,后面才知道 ctrl+b 后按下 [ 就能通过 PgUp 和 PgDn 翻页]

笔者当年的解决方案是直接开多个虚拟机,但用起来总感觉差点意思不说,还占用了大量的物理磁盘空间——笔者本科阶段前两年所用的电脑是机身只搭载了一块 512GB SSD 的 surface book 2——为什么这里要特别讲一下这个机型并不是因为笔者想要炫富,虽然说从价格而言 surface 系统产品和 MacBook 系列产品一样同属价格极高性价比极低的“高端产品”,但笔者当时是在某海鲜市场买的可能大于二手的机子,所以其实没有花太多钱,但和 MacBook 一样的是 surface 全系列产品没有办法自行更换包括内存与硬盘在内的任何部件,而即便是在海鲜市场寻宝的情况下笔者也买不起更大容量的版本,再加上笔者当时除了是个计算机科学爱好者以及网络空间安全专业本科生的身份以外还是半个画师加十六分之一个平面设计师(当然现在已经不是本科生了),装个 visual studio 加个 matlab 加上 jetbrains 全家桶再装个 adobe 半家桶(主要就用 PS 和 AI 还有个剪视频的叫啥👴已经记不得了)就已经把硬盘填得满满当当的了,各种杂七杂八的资料照片音乐视频啥的又要吃掉少说几十 GB,这个时候再塞几个平均十几 GB 的各种虚拟机(主力虚拟机所占用的硬盘空间超过100GB)无疑更是让本就不富裕的 512GB SSD 雪上加霜

而笔者为什么选择买这样一台中看不中用的电脑是因为在初中的时候笔者看一本不知道叫啥的电子硬件相关的杂志中看到 surface book屏幕键盘可分离、独显放置在键盘中 的这样一个前所未有的解决方案感到十分惊人加非常的帅气,于是就一直心心念念想要买一台这样的本子,高中时期笔者换过三台不同的平板电脑二合一产品(当然,都是从某海鲜市场淘来的,且基本上是卖了上一台才买下一台),其中第一台是 surface 3 而第三台是 surface pro 3,当时的主要用途是拿来日常刷刷 OI 、写点小说 (那么这里就不得不简单帮忙推广一下某不知名墨姓作家所写的《从零开始的 CTFer 生活》 这部小说了虽然一直在咕咕咕) 玩一些轻量游戏,体感其实还行

当然现在微软的 surface 系列产品已经能够初步地自行更换硬盘,虽然是充满槽点的 2230 规格,但是和隔壁某水果品牌相比已经好得不得了了

但是各项性能又被隔壁秒成渣渣了,微软你在干什么啊微软(恼)

但其实仔细一想这不应该是Intel的锅么,wintel联盟依托答辩了👊

以及 surface 系统产品一直有一个噱头就是有个触控笔可以画画,也就是自带数位屏,这也是这个产品一直以来最吸引笔者的一点,虽然微软最初给大家呈上来的只是一坨闻着香吃着臭的答辩,但笔者一直希望随着产品不断迭代,微软能够把这个功能给真正做好,就算达不到 wacom 上万块钱的数位屏相的高度至少也要和国产的一千出头的数位屏掰个手腕的程度,可惜哪怕是一直到今天最新的 surface pro 9 这一代产品,其绘画手感依旧比不过几千块钱的 iPad ,甚至比不过200块出头的国产数位板(当然笔者只买了 surface pro 8 没有买 surface pro 9 因为微软非常 SB 地把 3.5mm 耳机接口这个天顶星科技给砍掉了并且性能和续航和 8 代相比并没有什么提升简而言之又是挤牙膏的一代,但笔者有去微软线下门店亲自尝试过最新一代,绘画手感依旧一坨答辩)

虽然 surface book 2 无法更换硬盘的特性让笔者非常痛苦,但仔细想来还是老罗的那句名言——又不是不能用,硬盘空间想办法腾一腾多开好几个虚拟机其实没什么不好,毕竟省去了折腾 docker 的麻烦就一万个值,于是笔者日渐习惯每天在不同虚拟机之间跳来跳去

你知道我要说什么.png

不过这个问题并没有折磨笔者太长的时间,在玩了将近一个学期的用户态 Pwn 学了各种不同的 house 之后笔者感觉 glibc pwn 在技术上基本上已经玩不出什么新的有意思的花样了,于是选择了 all in Linux kernel pwn,只需要用 QEMU 去跑虚拟机,gdb 可以直接连上 QEMU 的端口进行调试,也不依赖于某些特定的 glibc 环境(虽然后面玩虚拟机逃逸又用到了不过这是后话了),于是当初搭建的几个用户态 Pwn 环境的虚拟机就慢慢用不到了, 现在已经被笔者逐一打包扔到备份硬盘当中

而在 2023 年的今天,笔者又想趁着闲暇时间再业余小玩一下用户态的 Pwn(比赛大概率不一定会专门打了,最多就看到 corCTF 这样的优质比赛会去做做他们的内核题,一些比较有意思的的用户态 Pwn 题最多可能赛后会复现之类的),那么环境以及各种 libc(包括 glibc 、tclibc、musl,👴其实没想明白 musl 为啥会在 CTF 里流行起来,是没活了🐎)又重新变为笔者需要面对的问题之一,而彼时同时跑好几个虚拟机的解决方案未免太过于小丑🤡,因此笔者决定找到一个能够在 docker 当中执行 gdb.attach() 时直接在宿主机中弹出一个窗口的办法

写了这么多没用的批话,突然感觉这一篇其实应该放到 PIECES 分类 而非 OPS ,但仔细想想其实笔者真正要讲的核心内容其实是 docker 的一个小知识,所以还是放 OPS 分类下或许会更加合适一些

0x01. 为 Docker 接入 Wayland 环境

笔者所用的图形服务为 Wayland,因此本篇不会讲 X11 该怎么配置(毕竟已经有很多讲这个的文章了,,以及都什么年代了还在用传统图形服务

配置的办法其实很简单,我们只需要在启动容器时额外添加一些参数即可,下面是一个简单的例子:

1
2
3
4
5
6
7
8
9
$ docker build -t pwnenv_ubuntu20 .
$ docker run -d -p "2222:22" \
--name=pwnenv_ubuntu20 \
-e XDG_RUNTIME_DIR=/tmp \
-e DISPLAY=$DISPLAY \
-e WAYLAND_DISPLAY=$WAYLAND_DISPLAY \
-v $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY:/tmp/$WAYLAND_DISPLAY \
-e QT_QPA_PLATFORM=wayland \
pwnenv_ubuntu20

别问👴这些参数是什么意思,自己查嗷

启动之后容器其实就完成对 Wayland 服务的接入了,这里我们简单写一个 QT 小程序看看实力:

libEGL 的报错懒得管了,反正日常使用没啥影响

现在我们的 docker 就已经成功接入 Host 侧的图形服务了:)

0x02. 让 gdb.attach() 弹出一个新的图形窗口

这个其实也很简单,笔者用的是 KDE 桌面,所以先在容器里装一个 konsole ,如果你用的是 Gnome 则可以装个 gnome-terminal

1
$ sudo apt install konsole

之后在 pwntools 调用 gdb 之前将全局变量 context.terminal 的值设为如下:

1
context.terminal = ['konsole', '-e', 'sh', '-c']

之后就能像调试本地原生进程那样在调试 docker 里的进程的时候弹出一个新的 gdb 图形窗口了 :)

yattaze

0xFF. What’s more…

最后给出一个笔者自用的开箱即用的 docker pwn 环境的 Dockerfile,有需要的可以自取:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
FROM ubuntu:20.04

ARG DEBIAN_FRONTEND=noninteractive

# pre-install softwares
RUN apt-get -y update && \
apt-get install -y lib32z1 apt-transport-https python3 python3-pip git \
libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev \
vim nano netcat openssh-server unzip make wget bison flex build-essential \
curl qemu qemu-system-x86 gcc gdb clang lldb tmux konsole

# enable ssh login
RUN rm -f /etc/service/sshd/down
RUN sed -ri 's/^#?PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config &&\
sed -ri 's/#UseDNS\ no/UseDNS\ no/g' /etc/ssh/sshd_config && \
sed -ri "s/StrictModes yes/StrictModes no/g" /etc/ssh/sshd_config && \
sed -ri "s/UsePAM yes/UsePAM no/g" /etc/ssh/sshd_config

# enable login with password
RUN echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config

# set username and password
RUN groupadd arttnba3 && \
useradd -g arttnba3 arttnba3 -m -s /bin/bash && \
echo "arttnba3:123456" | chpasswd && \
echo "root:root123456" | chpasswd

# enable ssh key login
#RUN mkdir /home/arttnba3/.ssh && \
# echo "Your ssh key" > /home/arttnba3/.ssh/authorized_keys

# keep container running
RUN echo "#!/bin/sh\nservice ssh restart\nsleep infinity" > /root/start.sh
RUN chmod +x /root/start.sh

# enable sudo
RUN apt-get install -y sudo && \
usermod -aG sudo arttnba3

# pwn-related tools
RUN python3 -m pip config set global.index-url http://pypi.tuna.tsinghua.edu.cn/simple && \
python3 -m pip config set global.trusted-host pypi.tuna.tsinghua.edu.cn && \
python3 -m pip install -U pip && \
python3 -m pip install --no-cache-dir \
pwntools \
ropgadget \
z3-solver \
smmap2 \
apscheduler \
ropper \
unicorn \
keystone-engine \
capstone \
angr \
pebble \
r2pipe

RUN git clone https://github.com/pwndbg/pwndbg && \
cd pwndbg && chmod +x setup.sh && ./setup.sh

CMD ["/root/start.sh"]

EXPOSE 22


【OPS.0x01】为 Docker 连接 Wayland 图形环境
https://arttnba3.github.io/2023/10/25/OPS-0X01-DOCKER_WAYLAND_GUI/
作者
arttnba3
发布于
2023年10月25日
许可协议