記錄一下 PWN 出題的原始碼、環境部署。
PWN題環境部署
圖方便的話可以使用 pwn_deploy_chroot
這個專案。
如何安全快速地部署多道ctf pwn比賽題目
也可以自己寫 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] #重啟容器
進入對應容器的 shell。
$ sudo docker exec -it [CONTAINER NAME] /bin/bash
很顯然,透過上述方法來生成容器的話會有些繁瑣,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"
然後啟動服務。
$ docker-compose up -d
PWN出題
編譯時設定 elf 檔案的保護機制:
NX:-z execstack / -z noexecstack
(關閉 / 開啟) 棧上資料不可執行。
Canary:-fno-stack-protector /-fstack-protector / -fstack-protector-all
(關閉 / 開啟 / 全開啟) 棧裡插入 canary。
PIE:-no-pie / -pie
(關閉 / 開啟) 地址隨機化。
RELRO:-z norelro / -z lazy / -z now
(關閉 / 部分開啟 / 完全開啟) 對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