이 글을 읽기 전에 시스템콜과 쉘의 개념이 헷갈린다면 여기를 읽어주세요
익스플로잇과 쉘코드
첫 번째 악용하다그것이 무엇인지 알아보자!
해킹 익스플로잇에는 다음이 포함됩니다.
“적 시스템에 대한 공격”말한다
그래서 오늘의 컨셉은 쉘 코드무엇인가요
쉘코드는 익스플로잇을 위해 작성된 기계 코드오전.
셸을 획득하는 데 자주 사용되기 때문에 일반적으로 셸 코드라고 합니다.
립 위치를 우리 쉘코드로 옮기면 다른 시스템에서 우리 쉘코드를 실행하게 됩니다!
!
우리의 목표는 립 위치를 조정하는 것이 아닐까요? (그냥 내 생각)
하지만 쉘코드는 결국 어셈블리어와 기계어로 작성되기 때문에 공격 대상 시스템의 컴퓨터 구조와 운영체제에 따라 달라지게 됩니다!
!
그래서 실력 향상을 위해 직접 쉘 코드를 작성하는 과정을 거쳐야 할 것 같습니다.
운체 등에 따라 구조 같은 것이 있습니까? 바뀔테니 직접 쉘코드를 작성하는 연습을 해보세요 ㅎㅎ
훈련
orw 쉘코드는 Open, Read 및 Write를 실행하는 쉘코드입니다.
오늘은 /tmp/flag orws라는 파일을 생성하는 간단한 쉘코드를 작성하겠습니다.
syscall은 위에 첨부되어 있습니다.
링크에서 자세히 설명했는데 그냥 커널에게 어떤 내부 동작을 하라고 알려주는 명령어 정도로 생각하시면 됩니다.
위의 표는 열기, 읽기, 쓰기 작업을 수행할 때 시스템 호출 유형에 따라 레지스터에 인수를 입력하는 방법을 보여줍니다.
이것을 코드에 대한 참조로 사용하겠습니다.
첫째, 의사 코드는 C 형식으로 생성되었습니다.
char buf(30); //길이 30의 버퍼 생성. 여기에 문자열을 저장할 것임.
int fd=open("/tmp/flag",O_RONLY,NULL);
read(fd,buf,0x30); //buf에서 30만큼 읽을 것
write(1,buf,0x30); //buf를 0x30만큼 1(stdout)에 쓸것
위의 코드를 어셈블리 언어로 변환해 봅시다.
(Char buf(30)는 별도로 작성하지 않고 아래 orw에 따라 내부적으로 추가합니다.
무슨 말인지 모르겠다면 그대로 따라하시면 됩니다.
)
int fd=open(“/tmp/flag”,O_RONLY,NULL);
이 코드는 O_RONLY(읽기 전용) 권한으로 “/tmp/flag” 파일을 엽니다.
C에서 open 함수는 인수로 사용합니다.
파일 경로, 열기 목적, 권한인수로 받습니다.
그러므로 파일 경로를 저장소로 이동한 후 rax=0x02, rdi=0(O_RDONLY), rsi=NULL요인으로 입력하십시오.
이것을 어셈블리 코드로 작성하면 다음과 같습니다.
push 0x616c662f706d742f67 ; 함수 경로 /tmp/flag를 push
rax=0x02 ;open해줘
mov rdi,rsp ;rsp가 0x616c662f706d742f67 가리키고 있으므로
xor rsi, rsi ; rsi를 0(O_RONLY)로 세팅
syscall
* Open 기능을 설명하는 좋은 글 -> IT 개발자 노트 :: open(2) – 읽기 또는 쓰기용 파일 열기(it-note.kr)
읽기(fd,buf,0x30);
읽기 기능 fd(파일 설명자), 읽기 위치, 길이인수로 받습니다.
open 함수의 반환 값은 rax에 저장되어 있으므로(시스템 호출 자체의 반환 값은 원래 rax에 들어갑니다!
!
) fd는 rax에 저장됩니다.
그리고 buf는 0x30으로 설정되는데, 이는 스택에 최대 30까지 버퍼 크기를 할당함으로써 달성할 수 있습니다.
마지막 0x30은 읽을 양을 나타냅니다.
mov rdi, rax ;rax에 open의 return값 fd가 들어있으므로 걔를 rdi에 대입
mov rax, 0x00 ;0x00은 read
mov rsi, rsp
sub rsp, 0x30 ;rsp위로 0x30위로 rsi 설정해줘서 거기부터쓰게함
;그런데 rsp위에 중요한 정보 있었으면 어떡하지...? 궁금....
mov rdx, 0x30 ;0x30만큼 읽어줘
syscall
쓰기(1,buf,0x30);
함수를 인수로 쓰기 쓸 곳, 쓸 내용의 위치, 쓸 내용의 길이인수로 받습니다.
그리고 쓴 바이트 수를 반환합니다.
첫 번째 인수는 open, 또는 0(표준 입력), 1(표준 출력) 또는 2(표준 오류)에서 얻은 파일 설명자일 수 있습니다.
우리는 stdout으로 갈 것이기 때문에 rdi에 1을 할당합시다.
그리고 buf의 경우 읽을 때 사용하는 것과 동일한 버퍼가 사용됩니다.
길이도 똑같습니다.
mov rax, 0x01; write 해줘
mov rdi, 1; fd=stdout
syscall
* 쓰기 기능에 대한 참고 사항 -> 쓰기(C 시스템 호출) – 코드 위키(wikidot.com)
최종 코드 orw.S
;Name: orw.S
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)
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)
mov rdi, 1 ; rdi = 1 ; fd = stdout
mov rax, 0x1 ; rax = 1 ; syscall_write
syscall ; write(fd, buf, 0x30)
다음 게시물에서는 gdb를 사용하여 코드를 컴파일, 실행 및 디버깅합니다.
참조