시스템 보안 · 취약점 › A. Linux 서버 보안 설정 · 14/50편 (전체 014/450)
학습 단계: 2단계 · 설정 및 점검
실습 표기: 이 글의 명령어·출력·로그는 로컬 VMware 테스트 VM(Rocky Linux 9 / Ubuntu 22.04) 기준의 「실습 예시」이며, IP·계정·호스트명은 가상의 값입니다.
리스닝 포트 점검의 목적은 "몇 번 포트가 열려 있나"가 아니라 "어떤 프로세스가, 어느 인터페이스에, 왜 열었나" 를 설명하는 것입니다. 같은 22번이라도 127.0.0.1:22와 0.0.0.0:22는 노출 범위가 완전히 다릅니다.
ss -tulpn 한 줄
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1022,fd=3))
│ │ │ │
프로토콜 바인딩 주소:포트 │ 프로세스(이름, PID)
0.0.0.0 = 모든 IPv4 인터페이스
127.0.0.1 = 로컬 전용
[::] = 모든 IPv6 (설정에 따라 IPv4도 수신)
0.0.0.0:8080)는 의도치 않은 노출입니다.| 명령 | 용도 |
|---|---|
sudo ss -tulpnH | TCP/UDP 리스닝 + 프로세스(헤더 없이) |
sudo ss -tlpn 'sport = :8081' | 특정 포트 필터 |
sudo lsof -i :8081 -P -n | 포트를 연 프로세스·사용자 |
readlink /proc/PID/exe | 프로세스의 실제 실행 파일 |
rpm -qf 경로 / dpkg -S 경로 | 실행 파일이 어느 패키지 소속인지 |
# 정규화: 프로토콜 + 주소:포트 + 프로세스명 (PID·fd 제외 → 재시작해도 비교 가능)
sudo ss -tulpnH | awk '{match($0,/\("([^"]+)"/,m); print $1, $5, m[1]}' | sort -u > /root/baseline/listen_now.txt
# 기준선과 비교 (> 는 새로 생긴 포트)
diff /root/baseline/listen_base.txt /root/baseline/listen_now.txt
# 신규 포트 출처 추적
PID=$(sudo ss -tlpnH 'sport = :8081' | grep -oP 'pid=\K[0-9]+' | head -1)
sudo readlink /proc/$PID/exe
sudo ls -l /proc/$PID/cwd
rpm -qf "$(sudo readlink /proc/$PID/exe)" # Ubuntu: dpkg -S
awk의 match(..., m) 배열 문법은 GNU awk(gawk) 기능입니다. Ubuntu 기본 mawk에서는 gawk 설치가 필요합니다.
$ cat /root/baseline/listen_now.txt
tcp 0.0.0.0:22 sshd
tcp 0.0.0.0:80 httpd
tcp 127.0.0.1:25 master
udp 127.0.0.1:323 chronyd
$ diff listen_base.txt listen_now.txt
$
메일(25)·시간 동기화(323)처럼 로컬 전용 바인딩은 외부 노출이 없으므로 위험도가 낮습니다.
$ diff listen_base.txt listen_now.txt
> tcp 0.0.0.0:8081 python3
$ sudo readlink /proc/7120/exe
/usr/bin/python3.9
$ sudo ls -l /proc/7120/cwd
lrwxrwxrwx. 1 apache apache 0 Oct 1 08:20 /proc/7120/cwd -> /tmp/.cache
$ rpm -qf /usr/bin/python3.9
python3-3.9.18-3.el9.x86_64
/tmp/.cache(숨김), 실행 사용자 apache포트 생성 시점은 auditd의 bind/listen 시스템 콜 감시나, 프로세스 실행 기록으로 추정합니다(가상의 예시 로그).
type=SYSCALL msg=audit(1759717200.330:1402): arch=c000003e syscall=59 success=yes ppid=6950 pid=7120 auid=4294967295 uid=48 comm="python3" exe="/usr/bin/python3.9" key="exec_tmp"
type=CWD msg=audit(1759717200.330:1402): cwd="/tmp/.cache"
type=EXECVE msg=audit(1759717200.330:1402): argc=4 a0="python3" a1="-m" a2="http.server" a3="8081"
| 필드 | 해석 |
|---|---|
uid=48 | Rocky의 apache 계정 UID |
auid=4294967295 | 로그인 세션이 아닌 데몬 하위에서 실행 → 웹 프로세스 경유 가능성 |
a1="-m" a2="http.server" | 파이썬 내장 웹서버 모듈로 8081 리스닝 |
ppid=6950 | 부모 프로세스 → ps -o pid,cmd -p 6950으로 httpd 여부 확인 |
apache, www-data)이 연 비표준 포트는 높은 우선순위로 분류합니다.<!-- 002편의 full_command 수집(alias: listen-baseline)과 연계 -->
<rule id="100210" level="10">
<if_sid>530</if_sid>
<match>ossec: output: 'listen-baseline'</match>
<check_diff />
<description>리스닝 포트 목록 변경</description>
</rule>
530은 Wazuh에서 명령 출력 로그를 받는 기본 룰이고, <check_diff />는 이전 출력과 달라졌을 때만 Alert를 만듭니다.
/proc/PID 정보(cmdline, environ, maps), 실행 파일 해시, 네트워크 연결 목록을 먼저 수집합니다.| 구분 | 핵심 내용 |
|---|---|
| 바인딩 | 0.0.0.0(전체) vs 127.0.0.1(로컬) 노출 범위 차이 |
| 정규화 | 프로토콜+주소:포트+프로세스명 (PID 제외) |
| 판정 4요소 | 프로세스 · 사용자 · cwd · 부모 프로세스 |
| 함정 | 정상 바이너리(python3 등)로 연 비인가 리스너 |
| 면접 포인트 | "포트 번호가 아니라 누가 왜 열었는지 설명" |
다음 편 015. Linux 서버 보안 — firewalld zone 설계와 점검 에서는 네트워크 접근을 통제하는 firewalld zone 설계와 점검을 다룹니다.
이전 편: 013. Linux 서버 보안 — 불필요한 서비스 식별과 비활성화
📚 시리즈 전체 보기: 시스템 보안 · 취약점