보안전문가로 향하는 길
[시스템 해킹] ORW(open-read-write), execve, objdump 본문
이번에는 셸 코드와 익스플로잇에 대해서 알아보자
셸코드(Shellcode)는 익스플로잇을 위해 제작된 어셈블리 코드 조각을 말한다.
먼저 ORW 셸 코드를 알아보도록 하자
orw 셸코드는 파일을 열고, 읽은 뒤 화면에 출력해주는 셸코드이다.
알아보고자 하는 셸 코드를 c언어로 작성하면 아래와 같은 코드이다
flag라는 파일을 읽기 모드로 열어서 화면에 출력하는 코드다.
위 코드를 어셈블리어로 만들어 보기 전에 알아야 할 사전지식이 있다.syscall에 대해서 알아야 한다. syscall은 커널에 접근하기 위한 인터페이스이다.
syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
| read | 0x00 | unsigned int fd | char *buf | size_t count |
| write | 0x01 | unsigned int fd | const char *buf | size_t count |
| open | 0x02 | const char *filename | int flags | umode_t mode |
어셈블러에서 syscall함수를 호출할 때 rdi, rsi, rdx에 있는 값을 이용한다.
즉 rax에 0x00이 있어야 read를 실행 할 수 있고
rax에 0x01가 있어야 write를 실행할 수 있고
rax에 0x02가 있어야 open을 실행할 수 있다.
int open(const char *filepath, int flag, mode_t mode);
먼저 open부터 살펴보도록 하자. 3가지의 매개변수를 갖는다.
첫번째는 파일의 경로, 두번째는 파일을 열 때 사용할 옵션, 세번째는 O_CREAT옵션을 사용할 때 사용한다.
C/C++ open 함수 - 파일 생성 / 읽기 / 쓰기
Open 함수 기능 파일을 열거나 생성 후 열어주는 함수 함수원형 #include #include #include int open(const char *filepath, int flag); int open(const char *filepath, int flag, mode_t mode); 매개변수 const char *filepath 열고자 하
bubble-dev.tistory.com
flag의 매개변수로 0은 read를 1은 write를 2를 read-write를 의미한다.
open
syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
| open | 0x02 | const char *filename | int flags | umode_t mode |
위 코드를 살펴보도록 하자
첫번째로 해야 할 일은 /tmp/flag라는 문자열을 메모리에 위치시켜야 한다.
이를 위해 0x616c662f706d742f67(/tmp/flag) 를 push해야 한다.
스택에는 8바이트 단위로 push를 할 수 있기 때문에 0x67을 push한 후 나머지를 push한다
8바이트를 맞추기 위해 0x67을 push후 0x616c662f706d742f67(/tmp/flag) 를 push했다
rdi를 rsp로 바꿔줌으로 첫번째 매개변수에 파일의 경로를 주었다.
읽기모드를 열기 위해 rsi를 0으로 초기화 해주고 세번째 인자는 null을 주기위해 0으로 초기화한다.
read
C언어 파일 읽기 함수 read()
C read() 파일 읽기 함수 open() 함수로 열기한 파일의 내용을 읽기 합니다. 헤더: unistd.h 형태: ssize_t read (int fd, void *buf, size_t nbytes) 인수: int fd 파일 디스크립터 void *buf 파일을 읽어 들일 버퍼 size_t nb
badayak.com
syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
| read | 0x00 | unsigned int fd | char *buf | size_t count |
위 코드를 살펴보도록 하자
rdi에 파일의 이름을 줘야 한다. 위에서 syscall을 호출함으로 rax에 fd의 반환값이 저장되어 있다.따라서 rdi에 rax의 값을 넘겨주었다.총 0x30만큼의 내용을 읽을 것이기 때문에 rsi에 rsp의 주소를 복사 후rsi - 0x30을 해줘서 스택의 공간을 만든다.(만약 무슨 소리인지 모른다면 어셈블리어와 컴퓨터시스템에 대해 배우고오자)
rdx는 null을 주기위해 0으로 초기와 하고
read를 실행하기 위해서 rax를 0으로 만들어 준다.
💡 fd란?
| 파일 서술자(File Descriptor, fd)는 유닉스 계열의 운영체제에서 파일에 접근하는 소프트웨어에 제공하는 가상의 접근 제어자입니다. 프로세스마다 고유의 서술자 테이블을 갖고 있으며, 그 안에 여러 파일 서술자를 저장합니다. 서술자 각각은 번호로 구별되는데, 일반적으로 0번은 일반 입력(Standard Input, STDIN), 1번은 일반 출력(Standard Output, STDOUT), 2번은 일반 오류(Standard Error, STDERR)에 할당되어 있으며, 이들은 프로세스를 터미널과 연결해줍니다. 그래서 우리는 키보드 입력을 통해 프로세스에 입력을 전달하고, 출력을 터미널로 받아볼 수 있습니다. 프로세스가 생성된 이후, 위의 open같은 함수를 통해 어떤 파일과 프로세스를 연결하려고 하면, 기본으로 할당된 2번 이후의 번호를 새로운 fd에 차례로 할당해줍니다. 그러면 프로세스는 그 fd를 이용하여 파일에 접근할 수 있습니다. |
write
C언어 write 함수 파일 쓰기
C함수 파일 쓰기 write() open() 함수로 열기를 한 파일에 쓰기를 합니다. open()함수는 fcntl.h 에 정의 되어 있지만 write(), read(), close()는 unistd.h에 정의 되어 있습니다. 헤더: unistd.h 형태: ssize_t write (int
badayak.com
write (int fd, const void *buf, size_t n)
첫번째 매개변수 : 파일, 두번째 매개변수 : 버버, 세번째 매개변수 : 쓰기할 바이트 갯수
syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
| write | 0x01 | unsigned int fd | const char *buf | size_t count |
위 코드를 살펴보도록 하자
write에서 첫번째 매개변수로 1을 준다면 모니터에 출력을 한다.rsi와 rdx는 위에서 이미 지정해 놓았다.write를 사용하기 위해서 rax를 1로만 만들어 주면 된다.
전체코드
어셈블리 파일을 컴파일
어셈블리 코드를 컴파일 하는 법은 여러가지 이지만 여기에서는 셸코드를 실행할 수 있는
스켈레톤 코드를 c언어로 작성해서 사용한다.
위 스켈레톤을 사용해서 코드를 작성
아래 명령어로 파일을 만들어 준다.
echo 'flag{this_is_open_read_write_shellcode!}' > /tmp/flag
gcc -o test test.c -masm=intel
./test

Execve 셸 코드
C언어 다른 프로그램 실행 함수execve()
C함수 다른 프로그램 실행 execve() 다른 프로그램을 실행하고 자신은 종료합니다. execle() 이나 execve() 처럼 exec 함수 중 e 로 끝나는 함수는 환경변수를 지정할 수 있습니다. 헤더: unistd.h 형태: int ex
badayak.com
execve(2) - 프로그램 실행.
execve(2) #include int execve(const char *filename, char *const argv[], char *const envp[]); 실행가능한 파일인 filename의 실행코드를 현재 프로세스에 적재하여 기존의 실행코드와 교체하여 새로운 기능으로 실행합
www.it-note.kr
execve 셸코드는 임의의 프로그램을 실행하는 셸코드이다. 이를 이용하면 서버의 셸을 획득할 수 있다.
보통 웬만한 유닉스(리눅스) 계열에는 셸을 실행시키는 함수가 있다.
해당 함수를 실행시키는 법을 알아보자
execve(“/bin/sh”, null, null)
syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
| execve | 0x3b | const char *filename | const char *const *argv | const char *const *envp |
어셈블리어 코드
c언어 코드
gcc -o execve execve.c -masm=intel
./execve

objdump
objdump를 이용해서 byte code의 형태로 추출
nasm -f elf shellcode.asm
objdump -d shellcode.o

objcopy --dump-section .text=shellcode.bin shellcode.o
xxd shellcode.bin

'드림핵 > 시스템 해킹' 카테고리의 다른 글
| checksec -- 파일에 어떤 보안기법이 있는지 확인 하는 명령어 (0) | 2024.01.03 |
|---|---|
| 스택 카라리 알아보기, 생성방법 및 우회 (2) | 2024.01.03 |
| 셸코드 모음 (0) | 2024.01.02 |
| pwntools - 해킹 오픈 api (0) | 2024.01.01 |
| 디버깅 - gdb(pwngdb) 사용방법 (1) | 2023.12.30 |