참고※참고 자료는 드림핵을 이용하였습니다.
※드림핵의 로드맵대로 공부할 예정입니다.
※스스로를 위해 정리한 참고 자료입니다.
원격 Linux 서버에 연결하는 가장 일반적인 방식은 SSH 명령어를 사용하는 것입니다.
SSH (Secure Shell, Secure Socket Shell)는 원격 서버(컴퓨터)에 연결할 수 있도록 해 주는 암호화된 네트워크 프로토콜입니다.
암호화를 통해 호스트와 클라이언트가 안전하게 통신할 수 있습니다.
클라이언트가 원격 서버의 터미널에 접속하여 명령어를 입력하면, 호스트가 명령 실행 결과를 클라이언트에게 전달합니다.
- 워게임이나 CTF 문제 서버에 접속하기 위해 가끔 SSH 명령어를 사용합니다. 이번 강의에서는 SSH 명령어 사용법에 대해 알아보겠습니다.
Linux와 macOS에서는 터미널에서 ssh 명령어를 기본적으로 사용할 수 있습니다.
앞서 Virtual Machine 강의에서 구축한 Ubuntu Linux 환경을 사용한다면 따로 설치할 것이 없으므로 다음 단계로 넘어가시면 됩니다.
Windows에서는 open SSH 클라이언트를 사용해야 합니다.
Windows 10 기준으로 open SSH 클라이언트는 다음과 같이 설치합니다.
설정 - 앱 - 선택적 기능 클릭
선택적 기능 추가 (기능 보기) - OpenSSH 클라이언트 설치
터미널에서 ssh 명령어를 입력하여 아래와 같이 뜨는지 확인
PS C:\Users\user> ssh
usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]
[-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
[-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]
[-i identity_file] [-J [user@]host[:port]] [-L address]
[-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
[-Q query_option] [-R address] [-S ctl_path] [-W host:port]
[-w local_tun[:remote_tun]] destination [command]
ssh 명령은 다음과 같이 작성합니다.
ssh user@HOST -p PORT -i [개인 키 파일 경로]
첫 번째는 패스워드로 인증하는 방법입니다. ssh user@HOST 명령을 실행하면 패스워드를 입력한 뒤 원격 서버에 접속할 수 있습니다.
하지만 패스워드를 충분히 긴 길이로 설정하지 않으면 브루트 포스 공격을 통해 패스워드를 유추할 수 있다는 위험이 있어 권장되지 않습니다.
더 안전한 방법은 원격 서버에 공개 키를 저장하고, 클라이언트가 사용할 개인 키를 지정하여 알맞은 사용자인지 검증하는 것입니다.
공개 키-개인 키 쌍은 ssh-keygen 명령으로 생성할 수 있습니다.
이번 강의는 ssh로 원격 서버에 접속해 보는 것이 목적이므로, 암호화와 검증에 대한 자세한 내용은 다루지 않겠습니다.
개인 키 파일을 이용하여 원격 서버에 접속할 때 -i 옵션을 이용한다는 것만 알아두어도 충분합니다.
Host: host3.dreamhack.games
Port: 11051/tcp → 31337/tcp
ssh with
id: bguser
pw: bgpw
터미널을 열고 ssh bguser@host3.dreamhack.games -p 11051 명령을 수행한 뒤, 패스워드로 bgpw를 입력하여 문제 서버에 접속합니다.
Host: host3.dreamhack.games
Port: 11051/tcp → 31337/tcp
ssh with id: bguser
터미널을 열고 ssh bguser@host3.dreamhack.games -p 11051 -i [다운받은 개인 키 파일 경로] 명령을 수행하면 문제 서버에 접속합니다.
이제 ssh로 워게임 문제 서버에 직접 접속해 보겠습니다.
Exercise: SSH는 ssh로 원격 서버에 접속해서 플래그 파일을 읽는 것이 목표인 문제입니다.
접속을 위한 사용자와 패스워드 정보는 다음과 같습니다.
Host와 Port는 접속 정보 보기를 클릭하여 알 수 있습니다.ssh with id: chall password: dhbgssh
- 문제 서버에 다음과 같이 접속하여 플래그를 출력합니다. 직접 명령어를 작성하고 접속해 본 뒤 정답을 확인하시기 바랍니다.
user@user-VirtualBox:~$ ssh chall@host2.dreamhack.games -p 20411 The authenticity of host '[host2.dreamhack.games]:20411 ([158.247.221.81]:20411)' can't be established. ED25519 key fingerprint is SHA256:xhAn3bi+kzaaA1iGfdWEqxlXIHfGg8F0iJuyKHulTXw. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '[host2.dreamhack.games]:20411' (ED25519) to the list of known hosts. chall@host2.dreamhack.games's password: (dhbgssh 입력) Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 4.19.234 x86_64) ... (생략) chall@localhost:~$ ls bin flag chall@localhost:~$ cat flag DH{h3110_6e9inn3rs!} chall@localhost:~$
CTF나 워게임을 풀어 보았다면 도커(Docker)를 본 적이 있을 것입니다.
도커가 무엇인지 몰랐다면, 열심히 검색하고 도커 문서를 읽다가 지쳐 그래서 도커가 대체 뭐지?라고 생각했을 수 있습니다.
도커를 이미 알고 있더라도 해킹과 도커는 무슨 관련이 있는지, 해킹 공부를 할 때 도커를 꼭 알아야 하는지 의문을 가질 수도 있습니다.이번 강의에서는 도커가 무엇이고, 앞으로의 해킹 공부에 왜 필요한지 알아보겠습니다.
그리고 도커 관련 명령어와 사용 방법을 배운 뒤, 도커를 사용하여 직접 워게임에 접속하는 실습을 진행합니다.
도커(Docker)는 컨테이너를 만들고, 실행하고, 배포할 수 있는 가상화 프랫폼입니다.
도커의 컨테이너란, 가상의 환경이 구축되어 있는 하나의 박스를 말합니다.
VirtualBox 등의 가상 머신으로 하나의 운영체제 위에 다른 운영체제 환경을 구축하는 것과 유사하지만, 도커 컨테이너는 새로운 운영체제 환경을 구축할 필요 없이 하나의 분리된 프로세스처럼 작동하여 더 가볍습니다.
쉽게 말하면 특정한 환경을 구성하기 위해 만들어진 가상의 공간입니다.
도커 이미지는 도커 컨테이너의 전 단계로, 컨테이너를 생성하고 실행하기 위한 모든 것을 포함합니다.
예를 들어 컨테이너 생성에 필요한 파일, 환경, 변수, 명령어 등과 파일 시스템이 있습니다.
자신만의 이미지를 만들거나 다른 사람이 만든 이미지를 사용할 수도 있습니다.이미지를 생성항려면 Dockerfile을 작성하고 이미지를 빌드해야 합니다.
Dockerfile은 이미지를 빌드하는 데 단계적으로 필요한 명령어가 있는 파일입니다.
도커 이미지에는 태그(Tag)를 붙일 수 있습니다.
태크를 붙이는 것은 하나의 이미지에 여러 개의 별명을 붙여 주는 것과 같습니다.
주로 이미지의 버전을 지정하기 위해 사용합니다.
도커 컨테이너는 도커 이미지로부터 만들어진 실행 가능한 인스턴스입니다.
다르게 말하면, 실행 중인 이미지를 컨테이너라고 합니다.
컨테이너는 도커 이미지와 사용자가 컨테이너를 시작할 때 작성하는 옵션에 의해 정의됩니다.
컨테이너를 실행하는 동안은 분리된 파일 시스템을 사용합니다.
도커 레지스트리는 도커 이미지를 저장하는 저장소입니다.
도커의 공식 레지스트리로 Dokcer Hub가 있습니다.
누구나 레지스트리에 도커 이미지를 올리고, 존재하는 도커 이미지를 가져올 수 있습니다.
CTF 혹은 워게임 문제에 종종 Dockerfile이 제공되는 경우가 있습니다.
이 경우, 제공되는 Dockerfile을 빌드하고 컨테이너를 실행하면 문제 환경을 똑같이 재현하여 풀 수 있습니다.문제에서 Dockerfile을 제공하여 도커를 사용할 수 있도록 하는 이유는 무엇일까요?
- 도커가 없다면 VirtualBox와 같은 가상 머신을 설치하여 직접 문제 환경을 구축해야 합니다.
- VM을 사용하면 설치하는 데 시간이 오래 걸리고 과정도 복잡합니다.
- 문제마다 환경을 구성해야 할 수도 있는데, 가상 머신은 용량이 매우 크고 무거워서 동시에 여러 개를 실행하기 어렵습니다.
- 뿐만 아니라, 풀이자가 직접 가상 환경을 구축하면 출제자가 의도한 환경에서 벗어날 수도 있습니다.
- 특히 시스템 해킹의 경우 정교한 공격을 위해 똑같은 환경에서 바이너리를 디버깅하고 익스플로잇하는 것이 중요합니다.
- 도커 컨테이너는 가상 환경 구축의 많은 부분을 생략하고 가볍게 실행되어, 매우 간편하게 문제 환경을 재현합니다.
- 또한, 문제 환경과 거의 동일한 환경을 제공하여 문제 풀이에 방해가 되는 요소를 줄입니다.
- CTF의 경우 같은 서버에서 여러 문제를 제공하면 공격 과정에서 다른 문제에 영향을 미치거나, 출제자가 의도하지 않은 공격을 수행할 수도 있습니다.
- 도커는 문제가 각각 분리된 환경에 있는 것처럼 제공하여 이를 막습니다.
Dockerfile을 이용하여 이미지를 생성합니다.
- docker build [옵션] <경로>
- docker build -t <이미지명:태그> <경로>
-t 옵션으로 이미지의 이름과 태그를 지정할 수 있습니다.
태그를 작성하지 않을 경우 'latest'로 지정됩니다.➡️ docker build . : 현재 디렉토리에 있는 Dockerfile로 이미지 생성
➡️ docker build -t my-image . : 현재 디렉토리에 있는 Dockerfile로 ‘my-image:latest’ 이미지 생성
도커 이미지 목록을 출력합니다.
아래는 미리 준비된 Dockerfile을 이용해서 docker build 명령어로 이미지를 빌드 후, docker images 명령어로 출력하는 모습입니다.user@user-VirtualBox:~/Desktop/ex-docker$ docker build . [+] Building 27.1s (17/17) FINISHED => [internal] load build definition from Dockerfile 0.0s => ...생략... => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:08a83756c396b9ca5d83735153cf0176056fb23c3eb3c 0.0s user@user-VirtualBox:~/Desktop/ex-docker$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> 9d201c4de2b6 2 minutes ago 122MB user@user-VirtualBox:~/Desktop/ex-docker$ docker build -t my-image:1 . [+] Building 0.8s (17/17) FINISHED => ...생략... => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:9d201c4de2b62519383058265e31669b167c422502643 0.0s => => naming to docker.io/library/my-image:1 0.0s user@user-VirtualBox:~/Desktop/ex-docker$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE my-image 1 9d201c4de2b6 4 minutes ago 122MB
도커 이미지로 컨테이너를 생성하고 실행합니다.
- docker run [옵션] <이미지명|ID> [명령어]
- docker run -p <호스트 PORT>:<컨테이너 PORT><이미지명|ID>
-P 옵션은 도커 컨테이너의 포트와 호스트의 포트를 매핑합니다.
컨테이너에서 리슨하고 있는 포트를 호스트의 특정 포트로 접속할 수 있도록 합니다.
- docker run -it <이미지명|ID> <명령어>
-it 옵션으로 컨테이너에서 bash 셸을 사용할 수 있습니다.
-i (--interactive)는 표준 입력을 활성화하여 사용자가 명령어를 입력할 수 있도록 하고, -t (--tty)는 가상 터미널(tty)을 사용할 수 있도록 합니다.
➡️ docker run -it my-image:1 /bin/bash : my-image:1 이미지로 컨테이너를 생성하고 실행하여 bash 셸 열기user@user-VirtualBox:~/Desktop/ex-docker$ docker run -it my-image:1 /bin/bash chall@852bb2be037c:~$
실행 중인 컨테이너 목록을 출력합니다.
- docker ps -a
-a 옵션은 종료된 컨테이너까지 모두 출력합니다.
- 컨테이너를 실행한 상태로 다른 터미널 창을 열어 docker ps를 입력하면 아래와 같이 실행 중인 컨테이너 목록이 출력됩니다.
- 컨테이너 안에서 exit 명령어를 실행하여 컨테이너를 종료한 후 docker ps를 입력하면 컨테이너가 출력되지 않고, docker ps -a를 입력하면 종료된 컨테이너까지 출력됩니다.
docker run 대신, 컨테이너 생성과 실행을 따로 명령할 수도 있습니다.
도커 이미지로 컨테이너를 생성합니다.
- docker create [옵션] <이미지명|ID> [명령어]
중단된 컨테이너를 시작합니다.
- docker start [옵션] <컨테이너명|ID>
user@user-VirtualBox:~$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE my-image 1 9d201c4de2b6 2 hours ago 122MB user@user-VirtualBox:~$ docker create my-image:1 eb01688504dc201d9f3a03bf3a27d84cdeafca4b2110766d52fc7551a7d9d05a user@user-VirtualBox:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES eb01688504dc my-image:1 "/bin/sh -c 'socat -…" 4 seconds ago Created priceless_meninsky user@user-VirtualBox:~$ docker start eb01688504dc eb01688504dc user@user-VirtualBox:~$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES eb01688504dc my-image:1 "/bin/sh -c 'socat -…" 19 seconds ago Up 4 seconds 2222/tcp priceless_meninsky
실행 중인 컨테이너에 접속하여 명령을 수행합니다.
- docker exec [옵션] <컨테이너명|ID> [명령어]
docker run과 유사하게 -it 옵션으로 bash 셸을 실행할 수 있습니다.
➡️ docker exec -it <컨테이너명|ID> /bin/bash : 실행 중인 컨테이너에서 bash 셸 열기1 user@user-VirtualBox:~$ docker exec -it eb01688504dc /bin/bash 2 chall@eb01688504dc:~$
실행 중인 컨테이너를 중단합니다.
- docker stop [옵션] <컨테이너명|ID>
레지스트리(Docker Hub)에 존재하는 도커 이미지르 다운받습니다.
- docker pull [옵션] <이미지명>
➡️ docker pull ubuntu:18.04 : Docker hub에서 ubuntu:18.04 이미지를 다운받습니다.user@user-VirtualBox:~$ docker pull ubuntu:18.04 18.04: Pulling from library/ubuntu 0c5227665c11: Pull complete Digest: sha256:8aa9c2798215f99544d1ce7439ea9c3a6dfd82de607da1cec3a8a2fae005931b Status: Downloaded newer image for ubuntu:18.04 docker.io/library/ubuntu:18.04
도커 컨테이너를 삭제합니다.
- docker rm [옵션] <컨테이너명|ID>
도커 이미지를 삭제합니다.
- docker rmi [옵션] <이미지명|ID>
도커 이미지 혹은 컨테이너의 자세한 정보를 출력합니다.
- docker inspect [옵션] <이미지 혹은 컨테이너명|ID>
user@user-VirtualBox:~$ docker inspect ubuntu:18.04 [ { "Id": "sha256:3941d3b032a8168d53508410a67baad120a563df67a7959565a30a1cb2114731", "RepoTags": [ "ubuntu:18.04" ], "RepoDigests": [ "ubuntu@sha256:8aa9c2798215f99544d1ce7439ea9c3a6dfd82de607da1cec3a8a2fae005931b" ], "Parent": "", "Comment": "", "Created": "2023-03-08T03:22:44.73196058Z", "Container": "ee3fcc8c88d3f3129f1236850de28a7eba0da7c548a7b23a6495905ebcf255ea", "ContainerConfig": { "Hostname": "ee3fcc8c88d3", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "CMD [\"/bin/bash\"]" ], "Image": "sha256:b64649bc9d1a48300ec5a929146aa3c5ca80046f74c33aa5de65a7046f5177a6", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": { "org.opencontainers.image.ref.name": "ubuntu", "org.opencontainers.image.version": "18.04" } }, "DockerVersion": "20.10.12", "Author": "", "Config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/bash" ], "Image": "sha256:b64649bc9d1a48300ec5a929146aa3c5ca80046f74c33aa5de65a7046f5177a6", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": { "org.opencontainers.image.ref.name": "ubuntu", "org.opencontainers.image.version": "18.04" } }, "Architecture": "amd64", "Os": "linux", "Size": 63146040, "VirtualSize": 63146040, "GraphDriver": { "Data": { "MergedDir": "/var/lib/docker/overlay2/0fe24c66cfaad338ccd55946d7734702a3575513fb2e697b409d3194c95c7e62/merged", "UpperDir": "/var/lib/docker/overlay2/0fe24c66cfaad338ccd55946d7734702a3575513fb2e697b409d3194c95c7e62/diff", "WorkDir": "/var/lib/docker/overlay2/0fe24c66cfaad338ccd55946d7734702a3575513fb2e697b409d3194c95c7e62/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:b7e0fa7bfe7f9796f1268cca2e65a8bfb1e010277652cee9a9c9d077a83db3c4" ] }, "Metadata": { "LastTagTime": "0001-01-01T00:00:00Z" } } ]