PWN出题小记

记录一下 PWN 出题的源码、环境部署。

PWN题环境部署

一把梭

图方便的话可以使用 pwn_deploy_chroot 这个项目。

如何安全快速地部署多道ctf pwn比赛题目

自己编写dockerfile

也可以自己写 dockerfile 来拉取镜像。

将题目名命名为 pwn,与 Dockerfile、ctf.xinetd、start.sh 三个文件放在同一目录下,下面提供相应配置文件的模板。

Dockerfile

FROM ubuntu:16.04
# 此处可以修改版本 直接修改版本号即可改变拉取的镜像版本 这里会直接到dockerhub拉取相应的镜像
# 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

RUN useradd -m ctf

WORKDIR /home/ctf

RUN cp -R /lib* /home/ctf && \
    cp -R /usr/lib* /home/ctf
# 使用 ubuntu 20.04 及以上版本的 libc 使用下面指令
# RUN cp -R /usr/lib* /home/ctf

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 ./pwn /home/ctf/
RUN chown -R root:ctf /home/ctf && \
    chmod -R 750 /home/ctf

CMD ["/start.sh"]

EXPOSE 9999

ctf.xinetd

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
    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
}

start.sh

#!/bin/sh
# Add your startup script
export FLAG="flag{c6f3396244adadd3c53c49cf13ca864e}"
echo $FLAG > /home/ctf/flag
chown root:ctf /home/ctf/flag
chmod 740 /home/ctf/flag
# 清环境变量
export FLAG=
# DO NOT DELETE
/etc/init.d/xinetd start;
sleep infinity;

如果需要设置动态 flag 的话,这里提供一种实现思路,首先肯定是需要靶场进行配合的,在拉镜像生成容器的时候,平台生成并记录一个随机数,传递随机数到容器的环境变量,然后通过环境变量写容器里的 flag 文件。

要设置 random 值为 flag,需要删除上面的 start.sh 中对 FLAG 赋值的 export FLAG="xx",并修改起容器的命令。

$ sudo docker run -itd --name [CONTAINER NAME] -p [PORT]:9999 -e FLAG=random [IMAGE ID]

写好这三个文件后,在当前文件夹路径下执行相关命令,拉镜像,起容器。

$ sudo docker build -t <image_name>:<tag> .			#拉取题目镜像
$ sudo docker images								#获取镜像id
$ sudo docker run -id --name [CONTAINER NAME] -p [PORT]:9999 [IMAGE ID]	#设置使用IMAGE ID镜像、生成的容器的容器名为CONTAINER NAME、将容器的9999端口映射主机的PORT端口

$ sudo docker ps -a									#查看所有容器名
$ sudo docker ps									#查看正在运行的容器名
$ sudo docker rm -f [CONTAINER NAME]				#删容器
$ sudo docker images								#获取镜像id
$ sudo docker rmi [IMAGE ID]						#删镜像
$ sudo docker start [CONTAINER ID]					#重启容器

load&save image

打包镜像,加载镜像,起容器。

「PS」
一、请自定义命令行里的 [httpd]。
二、注意 -t 会指定镜像名和标签,这样生成的 tar 包将会对镜像名有所限制。如果指定镜像名来 build 一个题并 save 为 tar 文件,后续若要对题目做修改,build 时需要指定不同的镜像名,或者在 load 新 tar 文件之前,先把名字冲突了的镜像删掉。

$ sudo docker build -t httpd:pwn .
$ sudo docker ps -a
$ sudo docker save -o httpd.tar httpd:pwn
$ sudo chmod 777 httpd.tar

$ sudo docker load -i httpd.tar
$ sudo docker images
$ sudo docker run -id --name httpd -p [PORT]:9999 [IMAGE ID]

测试docker环境

进入对应容器的 shell。

$ sudo docker exec -it [CONTAINER NAME] /bin/bash

docker-compose

很显然,通过上述方法来生成容器的话会有些繁琐,docker-compose 这个工具就十分方便,它是用于定义和运行多容器 Docker 应用程序的工具。通过 docker-compose,可以使用 yml 文件来配置应用程序需要的所有服务。然后,使用一个简短的命令,就可以从 yml 文件配置中创建并启动所有服务。docker-compose 默认的配置文件为 docker-compose.yml,其用 YAML 语言编写。

YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和 YAML 语法非常接近)。

docker-compose.yml 文件如下,也是与上述文件放在同一文件夹下。image_name 是生成的镜像名,PORT 是容器的外部端口。

version: "2"
services:
  pwn:
    build: .
    image: <image_name>
    restart: unless-stopped
    ports:
      - "[PORT]:9999"

然后启动服务。

$ sudo docker-compose up -d

若是直接安装 docker-compose,可能会遇到 docker-composedocker 版本不适配的问题,报错如下。

TypeError: kwargs_from_env() got an unexpected keyword argument 'ssl_version'

分别重新安装指定版本。

$ sudo pip uninstall docker-compose
$ sudo pip install pyyaml==5.3.1
$ sudo pip install docker-compose
$ sudo pip uninstall docker
$ sudo pip install docker==6.1.3

PWN出题

编译时设置 elf 文件的保护机制:

NX:-z execstack / -z noexecstack (关闭 / 开启) 栈上数据不可执行。

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

PIE:-no-pie / -pie (关闭 / 开启) 地址随机化。

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

常见报错:

/usr/bin/ld: /tmp/ccMFw2CH.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC

解决方法:在 gcc 编译时多添加一个参数 -fPIC

放些基础题的源码。

ret2text。

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

void init()
{
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
}

void func(void)
{
    char buf[40];
    read(0, buf, 60);
    return 0;
}

int main(void)
{
    init();
    puts("welcome to ctf!");
    func();
    return 0;
}

void backdoor(void)
{
    system("/bin/sh");
}

shellcode。

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

// char code[] = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";

void init()
{
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
}

int main()
{
    init();
    char code[100];
    puts("execute: ");
    read(0, code, 0x20);
    (*(void (*)())code)();
    return 0;
}

关于堆的菜单题。

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

#define PAPER_CNT (32)

struct paper_mgr
{
	char paper_time[48];
	char paper_content[32];
};

struct paper_mgr **array;

void backdoor()
{
	puts("Aha! Rock on!");
	system("/bin/sh");
}

void init()
{
	setvbuf(stdin, 0LL, 2, 0LL);
	setvbuf(stdout, 0LL, 2, 0LL);
	alarm(60); // 1 minute timeout
	array = (struct paper_mgr **)malloc(PAPER_CNT * sizeof(struct paper_mgr *));
	memset(array, 0, (PAPER_CNT * sizeof(struct paper_mgr *)));
}

void banner()
{
	printf("Welcome.\n");
	printf("\n");
}

void menu()
{
	puts("You have following choices:\n [1]: add a paper\n [2]: show a paper\n [3]: edit a paper\n [4]: finish a paper\n [5]: exit\nYour chocie:");
}

void get_input_custom(char *ptr, int len)
{
	if (!len)
		return;
	read(0, ptr, len);
}

void add_paper()
{
	int i;
	struct paper_mgr *paper_ptr;
	for (i = 0; i < PAPER_CNT; i++)
		if (!array[i])
			break;
	if (i == PAPER_CNT)
	{
		puts("paper manager is full :(");
		return;
	}

	paper_ptr = malloc(sizeof(struct paper_mgr));

	printf("creating paper with index-%d\n", i + 1);
	puts("please input the paper time");
	get_input_custom(paper_ptr->paper_time, 48);
	puts("please input the paper content");
	get_input_custom(paper_ptr->paper_content, 1024);
	puts("done");
	array[i] = paper_ptr;
}

void finish_paper()
{
	int index;
	puts("please input the paper index");
	scanf("%d", &index);
	index = index - 1;

	if (0 <= index && index < PAPER_CNT)
	{
		if (array[index])
		{
			free(array[index]);
			puts("done");
			return;
		}
	}
	puts("invalid paper index");
}

void show_paper()
{
	int index;
	puts("please input the paper index");
	scanf("%d", &index);
	index = index - 1;

	if (0 <= index && index < PAPER_CNT)
	{
		if (array[index])
		{
			printf("paper time: %s\n", array[index]->paper_time);
			printf("paper content: %s\n", array[index]->paper_content);
			puts("done");
			return;
		}
	}
	puts("invalid paper index");
}

void edit_paper()
{
	int index;
	puts("please input the paper index");
	scanf("%d", &index);
	index = index - 1;

	if (0 <= index && index < PAPER_CNT)
	{
		if (array[index])
		{
			struct paper_mgr *paper_ptr = array[index];
			puts("please input the new paper time");
			get_input_custom(paper_ptr->paper_time, 48);
			puts("please input the new paper content");
			get_input_custom(paper_ptr->paper_content, 1024);
			puts("done");
			return;
		}
	}
	puts("invalid paper index");
}

int main(int argc, char *argv[])
{
	int choice = 0;
	init();
	banner();
	while (1)
	{
		menu();
		scanf("%d", &choice);
		switch (choice)
		{
		case 1:
			add_paper();
			break;
		case 2:
			show_paper();
			break;
		case 3:
			edit_paper();
			break;
		case 4:
			finish_paper();
			break;
		case 5:
			puts("Bye!");
			exit(0);
		default:
			puts("Wrong!");
			break;
		}
	}
}

fmt、栈迁移。

SCUCTF 2020新生赛 PWN部分出题笔记-Pwn

这篇文章有介绍如何给 pwn 题布置沙箱。

Seccomp从0到1

posted @ 2024-04-11 10:51  ve1kcon  阅读(822)  评论(4编辑  收藏  举报