pwn出题小记

参考文章:

Docker部署小记 | 幻境

奇奇怪怪的教程 | Yxing

web和pwn题的简单动态flag实现_gzctf-CSDN博客

所使用的工具:docker、CTF-Xined

pwn题编译

1
2
3
4
5
6
7
gcc -o pwn pwn.c

-static #指定静态链接

#默认动态链接
#指定动态链接文件
gcc main.c -o main /usr/lib/x86_64-linux-gnu/libc.so.6

保护

1
2
3
4
5
6
7
NX:-z execstack / -z noexecstack (关闭 / 开启)    不让执行栈上的数据,于是JMP ESP就不能用了

Canary:-fno-stack-protector /-fstack-protector / -fstack-protector-all (关闭 / 开启 / 全开启) 栈里插入cookie信息

PIE:-no-pie / -pie (关闭 / 开启) 地址随机化,另外打开后会有get_pc_thunk

RELRO:-z norelro / -z lazy / -z now (关闭 / 部分开启 / 完全开启) 对GOT表具有写权限

docker的准备

因为虚拟机换源后仍然无法pull ubuntu22.04镜像,所以选择在主机上配置,主机上我是配了Linux系统的

docker换源

准备的是阿里云的加速源,这里每个人的源都不同,需要自行去阿里云官网查询:

容器镜像服务 ACR 控制台

1

打开docker设置

1

将刚获取到的源放入

1
2
3
"registry-mirrors": [
"https://xxxxx.mirror.aliyuncs.com"
]

点击apply应用

使用docker info查看是否换源成功,成功的话能看到刚刚换源上去的地址

正式出题

先开启主机上的Linux终端,在终端中输入wsl开启ubuntu环境方便我们测试容器可行性

接下来下载CTF-Xined

1
git clone https://github.com/Eadom/ctf_xinetd.git

扒下来后有这样几个文件

1

docker-compose.yml是我自己加的

其中bin文件夹放编译好的pwn文件和flag文件

我们主要修改ctf.xinetd、dockerfil、start.sh和bin目录下的flag

ctf.xinetd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
service ctf
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = root
type = UNLISTED
port = 9999
bind = 0.0.0.0
server = /usr/sbin/chroot
# replace helloworld to your program
server_args = --userspec=1000:1000 /home/ctf ./pwn
banner_fail = /etc/banner_fail
# safety options
per_source = 10 # the maximum instances of this service per source IP address
rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use
#rlimit_as = 1024M # the Address Space resource limit for the service
#access_times = 2:00-9:00 12:00-24:00
}

这里的端口指定了9999,平台上题目的端口也要是9999,不然不能正常连接环境

该文件修改了./onechance改为了pwn因为在bin目录下我的题目源文件叫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
FROM ubuntu:22.04

RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \
apt-get update && apt-get -y dist-upgrade && \
apt-get install -y lib32z1 xinetd libc6-i386

RUN useradd -m ctf

WORKDIR /home/ctf

RUN cp -R /lib* /home/ctf

RUN rm -rf /home/ctf/lib32 && mkdir -p /home/ctf/lib32
RUN rm -rf /home/ctf/lib && mkdir -p /home/ctf/lib

RUN cp /lib32/libc.so.6 /home/ctf/lib32/
RUN cp /lib/ld-linux.so.2 /home/ctf/lib/

RUN mkdir /home/ctf/dev && \
mknod /home/ctf/dev/null c 1 3 && \
mknod /home/ctf/dev/zero c 1 5 && \
mknod /home/ctf/dev/random c 1 8 && \
mknod /home/ctf/dev/urandom c 1 9 && \
chmod 666 /home/ctf/dev/*

RUN mkdir /home/ctf/bin && \
cp /bin/sh /home/ctf/bin && \
cp /bin/ls /home/ctf/bin && \
cp /bin/cat /home/ctf/bin

COPY ./ctf.xinetd /etc/xinetd.d/ctf
COPY ./start.sh /start.sh
RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail

RUN chmod +x /start.sh

COPY ./bin/ /home/ctf/
RUN chown -R root:ctf /home/ctf && \
chmod -R 750 /home/ctf && \
chmod 740 /home/ctf/flag

CMD ["/start.sh"]

EXPOSE 9999

注意修改ubuntu版本号,这段是支持32位程序的,目前还没考虑指定libc的情况

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
FROM ubuntu:22.04

# 替换源并安装必要依赖
RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \
apt-get update && apt-get -y dist-upgrade && \
apt-get install -y xinetd

RUN useradd -m ctf

WORKDIR /home/ctf

# 核心修正:先创建64位库的目标目录结构
RUN mkdir -p /home/ctf/lib/x86_64-linux-gnu/ # 创建libc等库的目录
RUN mkdir -p /home/ctf/lib64/ # 创建64位链接器目录

# 再复制64位系统库
RUN cp -R /lib/x86_64-linux-gnu/* /home/ctf/lib/x86_64-linux-gnu/
RUN cp -R /lib64/* /home/ctf/lib64/

# 确保/dev设备文件正确
RUN mkdir /home/ctf/dev && \
mknod /home/ctf/dev/null c 1 3 && \
mknod /home/ctf/dev/zero c 1 5 && \
mknod /home/ctf/dev/random c 1 8 && \
mknod /home/ctf/dev/urandom c 1 9 && \
chmod 666 /home/ctf/dev/*

# 复制基础命令
RUN mkdir /home/ctf/bin && \
cp /bin/sh /home/ctf/bin && \
cp /bin/ls /home/ctf/bin && \
cp /bin/cat /home/ctf/bin

# 配置xinetd和启动脚本
COPY ./ctf.xinetd /etc/xinetd.d/ctf
COPY ./start.sh /start.sh
RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail

RUN chmod +x /start.sh

# 复制pwn程序并设置权限
COPY ./bin/ /home/ctf/
RUN chown -R root:ctf /home/ctf && \
chmod -R 750 /home/ctf && \
chmod 740 /home/ctf/flag

CMD ["/start.sh"]

EXPOSE 9999

这段是64位程序的

dockerfile可以理解为是用来构建镜像的,目前的问题是容器的地址随机化关不掉,还得再研究

chroot只支持lscat cd三个操作,该dockerfile主要进行的操作就是将bin目录放到/home/ctf/目录下,并给他们可执行权限,然后添加了libc库文件,在最后以./start.sh文件为启动文件指定端口为9999

目前只能丢给人机写

start.sh

1
2
3
4
5
6
#!/bin/sh

sed -i "s/cdusec{pwntestflag}/$GZCTF_FLAG/" /home/ctf/flag
export GZCTF_FLAG=""
/etc/init.d/xinetd start;
sleep infinity;

注意我用的是GZ平台所以环境变量是$GZCTF_FLAG

flag

1
cdusec{pwntestflag}

注意flag要和start.sh中的一致,不然替换不上

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
version: '3'

services:
pwn:
build: ./
image: pwn #这里的image写自己创建的镜像名
ports:
- "60001:9999"
pids_limit: 1024
# cpus: 0.5
restart: unless-stopped
# privileged: true

学姐搓的自动部署文件?但是没搞懂怎么用,应该不放到当前目录也是可以成功创建容器的

部署流程

因为直接构建容器不下载ubuntu镜像到本地老是获取超时,所以选择先将镜像扒下来到本地,我们构建的时候就会直接拉本地的了

1
docker pull ubuntu:22.04

创建docker镜像

1
docker build -t mintlovecate/pwn-11 .

创建失败就可以开始调整dockerfile了,一般是libc库有问题,我还没跑出一个32位和64位都通用的dockerfile

运行该镜像

将本地9999端口转8888端口运行镜像,镜像名为mintlovecate/pwn-11容器名为pwn_test,flag环境变量为-e GZCTF_FLAG="flag{test123}"

1
docker run -d -p 127.0.0.1:8888:9999 -e GZCTF_FLAG="flag{test123}" --name pwn_test mintlovecate/pwn-11

开始测试

首先看nc连接是否有回显

若nc无回显则是端口问题

nc有回显但是没有运行pwn文件:那么是chroot(沙箱)环境下pwn文件无法运行

报错如下:1

也可以在容器中排查

1
2
3
4
5
6
7
8
9
10
11
#开启容器
docker exec -it pwn_test /bin/bash

#在chroot下运行pwn
chroot /home/ctf ./pwn

#地址随机化检查
cat /proc/sys/kernel/randomize_va_space

#退出shell
exit

这种情况一般是dockerfile中pwn文件的libc文件缺失导致的

解决之后在容器中尝试cat flag

如果cat之后无回显,那么大概率是flag文件为空

使用ls -l flag测试flag文件是否为空

1

若红圈处为0则证明flag文件未写入,开始排查,flag文件是否为cdusec{pwntestflag} start.sh文件中是否有

1
2
sed -i "s/cdusec{pwntestflag}/$GZCTF_FLAG/" /home/ctf/flag
export GZCTF_FLAG=""

检查环境变量是否是GZCTF_FLAG

若检查无误仍然显示flag为空,看看运行时环境变量是否指定了GZCTF_FLAG,这时候部署到平台上应该是有了,只是本地没有加环境变量

这时候可以把容器推送到docker hub上了,可以用命令推也可以手动推 。

测试完后记得删除现在正在运行的容器,或已作废的镜像

1
2
3
4
#删除容器
docker rm -f pwn_test
#删除镜像
docker rmi mintlovecate/pwn-11

做题平台设置

在gz平台上,我们首先选择动态容器,接着放附件,放镜像源

1

如果这里拉取镜像失败的话就用xshell 连服务器直接在服务器上pull

申请一个测试容器如果显示下图那么证明动态flag设置成功了

1

(图片是牢出来后手机拍的有点模糊)

然后就成功了,后续还得研究如何指定libc,应该在dockerfile中修改了

docker的各种命令

概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker build -t mintlovecate/pwn-11 .

docker run -d -p 127.0.0.1:8888:9999 -e GZCTF_FLAG="flag{test123}" --name pwn_test mintlovecate/pwn-11

docker exec -it pwn_test /bin/bash

chroot /home/ctf ./pwn

exit

docker rm -f pwn_test

docker rmi mintlovecate/pwn-11

cat /proc/sys/kernel/randomize_va_space

一、镜像管理命令

命令 功能描述 示例
docker images 列出本地所有镜像 docker images(查看所有镜像)docker images ubuntu(筛选 ubuntu 镜像)
docker pull <镜像名:标签> 从远程仓库拉取镜像 docker pull ubuntu:22.04(拉取 Ubuntu 22.04)
docker build -t <镜像名:标签> 基于 Dockerfile 构建镜像 docker build -t myapp:v1 .(当前目录构建,标签 v1)
docker rmi <镜像ID/名称> 删除本地镜像 docker rmi 8c79c1a7142d(通过 ID 删除)docker rmi myapp:v1(通过名称删除)
docker tag <原镜像> <新镜像名:标签> 为镜像打标签(重命名) docker tag ubuntu:22.04 myubuntu:latest
docker push <镜像名:标签> 推送镜像到远程仓库(需登录) docker push username/myapp:v1
docker inspect <镜像名/ID> 查看镜像详细信息(配置、层等) docker inspect ubuntu:22.04

二、容器操作命令

命令 功能描述 示例
docker run [选项] <镜像名> [命令] 创建并启动容器 基础启动:docker run -it ubuntu:22.04 /bin/bash(交互式进入 bash)后台运行:docker run -d --name mycontainer nginx(命名为 mycontainer,后台启动 nginx)端口映射:docker run -p 8080:80 nginx(本地 8080 映射容器 80)
docker ps 列出运行中的容器 docker ps(默认显示运行中)docker ps -a(显示所有容器,包括停止的)
docker start <容器名/ID> 启动已停止的容器 docker start mycontainer
docker stop <容器名/ID> 停止运行中的容器 docker stop mycontainer
docker restart <容器名/ID> 重启容器 docker restart mycontainer
docker rm <容器名/ID> 删除容器(需先停止,或加-f强制删除) docker rm mycontainer(删除停止的)docker rm -f mycontainer(强制删除运行中的)
docker exec [选项] <容器名/ID> <命令> 进入运行中的容器执行命令 交互式进入:docker exec -it mycontainer /bin/bash(常用!)执行单条命令:docker exec mycontainer ls /root
docker logs <容器名/ID> 查看容器日志 docker logs mycontainer(查看全部)docker logs -f mycontainer(实时跟踪日志)
docker inspect <容器名/ID> 查看容器详细信息(IP、配置等) docker inspect mycontainer
docker cp <本地路径> <容器名:路径>``docker cp <容器名:路径> <本地路径> 本地与容器间复制文件 本地→容器:docker cp ./flag.txt mycontainer:/home/容器→本地:docker cp mycontainer:/etc/passwd ./

三、网络管理命令

命令 功能描述 示例
docker network ls 列出所有 Docker 网络 docker network ls
docker network create <网络名> 创建自定义网络(默认 bridge 模式) docker network create mynet
docker network connect <网络名> <容器名> 将容器连接到指定网络 docker network connect mynet mycontainer
docker network disconnect <网络名> <容器名> 断开容器与网络的连接 docker network disconnect mynet mycontainer
docker network rm <网络名> 删除网络 docker network rm mynet

四、数据卷命令(持久化存储)

命令 功能描述 示例
docker volume ls 列出所有数据卷 docker volume ls
docker volume create <卷名> 创建数据卷 docker volume create myvol
docker volume inspect <卷名> 查看数据卷详情(路径等) docker inspect myvol
docker run -v <卷名:容器路径> ... 挂载数据卷到容器 docker run -d -v myvol:/data nginx(容器 /data 映射到 myvol)
docker volume rm <卷名> 删除数据卷 docker volume rm myvol

五、其他常用命令

命令 功能描述 示例
docker info 查看 Docker 系统信息(版本、镜像数、容器数等) docker info
docker version 查看 Docker 客户端和服务器版本 docker version
docker login / docker logout 登录 / 退出 Docker 远程仓库(如 Docker Hub) docker login(输入用户名密码)
docker system df 查看 Docker 磁盘使用情况(镜像、容器、卷占用) docker system df
docker system prune 清理无用资源(停止的容器、未使用的 docker system prune -a(加-a删除所有未使用镜像)